Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(485)

Unified Diff: src/json-stringifier.h

Issue 11315009: Handle edge cases in basic JSON.stringify. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: rebased Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/json.js ('k') | test/mjsunit/json2.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/json-stringifier.h
diff --git a/src/json-stringifier.h b/src/json-stringifier.h
index 2235ed5e8a8c563ba9a57644e44cd86cc7f64a9c..1b4ab9a0faa9f8a9e803c85575f994359dadc4da 100644
--- a/src/json-stringifier.h
+++ b/src/json-stringifier.h
@@ -46,7 +46,7 @@ class BasicJsonStringifier BASE_EMBEDDED {
static const int kMaxPartLength = 16 * 1024;
static const int kPartLengthGrowthFactor = 2;
- enum Result { UNCHANGED, SUCCESS, BAILOUT, CIRCULAR, STACK_OVERFLOW };
+ enum Result { UNCHANGED, SUCCESS, EXCEPTION, CIRCULAR, STACK_OVERFLOW };
template <bool is_ascii> void Extend();
@@ -79,27 +79,36 @@ class BasicJsonStringifier BASE_EMBEDDED {
Handle<Object> GetProperty(Handle<JSObject> object,
Handle<String> key);
- bool MayHaveToJsonFunction(Handle<JSObject> object);
+ Handle<Object> ApplyToJsonFunction(Handle<Object> object,
+ Handle<Object> key);
- INLINE(Result Serialize(Handle<Object> object)) {
- return Serialize_<false>(object);
+ // Entry point to serialize the object.
+ INLINE(Result SerializeObject(Handle<Object> obj)) {
+ return Serialize_<false>(obj, false, isolate_->factory()->empty_string());
}
- INLINE(Result SerializeDeferred(Handle<Object> object,
- bool deferred_comma,
- Handle<String> deferred_key)) {
+ // Serialize an array element.
+ // The index may serve as argument for the toJSON function.
+ INLINE(Result SerializeElement(Handle<Object> object, int i)) {
+ return Serialize_<false>(object, false, Handle<Object>(Smi::FromInt(i)));
+ }
+
+ // Serialize a object property.
+ // The key may or may not be serialized depending on the property.
+ // The key may also serve as argument for the toJSON function.
+ INLINE(Result SerializeProperty(Handle<Object> object,
+ bool deferred_comma,
+ Handle<String> deferred_key)) {
ASSERT(!deferred_key.is_null());
return Serialize_<true>(object, deferred_comma, deferred_key);
}
- template <bool deferred_key>
- Result Serialize_(Handle<Object> object,
- bool comma = false,
- Handle<String> key = Handle<String>::null());
+ template <bool deferred_string_key>
+ Result Serialize_(Handle<Object> object, bool comma, Handle<Object> key);
- void SerializeDeferredKey(bool deferred_comma, Handle<String> deferred_key) {
+ void SerializeDeferredKey(bool deferred_comma, Handle<Object> deferred_key) {
if (deferred_comma) Append(',');
- SerializeString(deferred_key);
+ SerializeString(Handle<String>::cast(deferred_key));
Append(':');
}
@@ -110,8 +119,12 @@ class BasicJsonStringifier BASE_EMBEDDED {
return SerializeDouble(object->value());
}
- INLINE(Result SerializeArray(Handle<JSArray> object));
- INLINE(Result SerializeObject(Handle<JSObject> object));
+ Result SerializeJSValue(Handle<JSValue> object);
+
+ INLINE(Result SerializeJSArray(Handle<JSArray> object));
+ INLINE(Result SerializeJSObject(Handle<JSObject> object));
+
+ Result SerializeJSArraySlow(Handle<JSArray> object, int length);
void SerializeString(Handle<String> object);
@@ -207,19 +220,19 @@ BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate)
MaybeObject* BasicJsonStringifier::Stringify(Handle<Object> object) {
- switch (Serialize(object)) {
+ switch (SerializeObject(object)) {
+ case UNCHANGED:
+ return isolate_->heap()->undefined_value();
case SUCCESS:
ShrinkCurrentPart();
return *isolate_->factory()->NewConsString(accumulator(), current_part_);
- case UNCHANGED:
- return isolate_->heap()->undefined_value();
case CIRCULAR:
return isolate_->Throw(*isolate_->factory()->NewTypeError(
"circular_structure", HandleVector<Object>(NULL, 0)));
case STACK_OVERFLOW:
return isolate_->StackOverflow();
default:
- return Smi::FromInt(0);
+ return Failure::Exception();
}
}
@@ -261,36 +274,34 @@ Handle<Object> BasicJsonStringifier::GetProperty(Handle<JSObject> object,
}
case CONSTANT_FUNCTION:
return Handle<Object>(lookup.GetConstantFunction());
- case CALLBACKS:
- case HANDLER:
- case INTERCEPTOR:
- return Handle<Object>::null();
- case TRANSITION:
- case NONEXISTENT:
- UNREACHABLE();
- break;
+ default: {
+ PropertyAttributes attr;
+ return Object::GetProperty(object, object, &lookup, key, &attr);
+ }
}
return Handle<Object>::null();
}
-bool BasicJsonStringifier::MayHaveToJsonFunction(Handle<JSObject> object) {
+Handle<Object> BasicJsonStringifier::ApplyToJsonFunction(
+ Handle<Object> object, Handle<Object> key) {
LookupResult lookup(isolate_);
- object->LookupRealNamedProperty(*tojson_symbol_, &lookup);
- if (!lookup.IsProperty()) return false;
- Object* value;
- switch (lookup.type()) {
- case NORMAL:
- value = lookup.holder()->GetNormalizedProperty(&lookup);
- break;
- case FIELD:
- value = lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
- break;
- default:
- return true;
- }
- ASSERT(!value->IsTheHole());
- return value->IsSpecFunction();
+ JSObject::cast(*object)->LookupRealNamedProperty(*tojson_symbol_, &lookup);
+ if (!lookup.IsProperty()) return object;
+ PropertyAttributes attr;
+ Handle<Object> fun =
+ Object::GetProperty(object, object, &lookup, tojson_symbol_, &attr);
+ if (!fun->IsJSFunction()) return object;
+
+ // Call toJSON function.
+ if (key->IsSmi()) key = isolate_->factory()->NumberToString(key);
+ Handle<Object> argv[] = { key };
+ bool has_exception = false;
+ HandleScope scope(isolate_);
+ object = Execution::Call(fun, object, 1, argv, &has_exception);
+ // Return empty handle to signal an exception.
+ if (has_exception) return Handle<Object>::null();
+ return scope.CloseAndEscape(object);
}
@@ -319,51 +330,49 @@ void BasicJsonStringifier::StackPop() {
}
-template <bool deferred_key>
+template <bool deferred_string_key>
BasicJsonStringifier::Result BasicJsonStringifier::Serialize_(
- Handle<Object> object, bool comma, Handle<String> key) {
+ Handle<Object> object, bool comma, Handle<Object> key) {
if (object->IsJSObject()) {
- // We don't deal with custom toJSON functions.
- if (MayHaveToJsonFunction(Handle<JSObject>::cast(object))) return BAILOUT;
-
- if (object->IsJSFunction()) {
- return UNCHANGED;
- } else if (object->IsJSArray()) {
- if (deferred_key) SerializeDeferredKey(comma, key);
- return SerializeArray(Handle<JSArray>::cast(object));
+ object = ApplyToJsonFunction(object, key);
+ if (object.is_null()) return EXCEPTION;
+ }
+
+ if (object->IsJSObject()) {
+ if (object->IsJSFunction()) return UNCHANGED;
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ if (object->IsJSArray()) {
+ return SerializeJSArray(Handle<JSArray>::cast(object));
} else if (object->IsJSValue()) {
- // JSValue with a custom prototype.
- if (object->GetPrototype()->IsJSReceiver()) return BAILOUT;
- // Unpack value wrapper and fall through.
- object = Handle<Object>(JSValue::cast(*object)->value());
+ return SerializeJSValue(Handle<JSValue>::cast(object));
} else {
- if (deferred_key) SerializeDeferredKey(comma, key);
- return SerializeObject(Handle<JSObject>::cast(object));
+ return SerializeJSObject(Handle<JSObject>::cast(object));
}
}
+ // Handle non-JSObject.
if (object->IsString()) {
- if (deferred_key) SerializeDeferredKey(comma, key);
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
SerializeString(Handle<String>::cast(object));
return SUCCESS;
} else if (object->IsSmi()) {
- if (deferred_key) SerializeDeferredKey(comma, key);
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
return SerializeSmi(Smi::cast(*object));
} else if (object->IsHeapNumber()) {
- if (deferred_key) SerializeDeferredKey(comma, key);
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
} else if (object->IsOddball()) {
switch (Oddball::cast(*object)->kind()) {
case Oddball::kFalse:
- if (deferred_key) SerializeDeferredKey(comma, key);
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
Append("false");
return SUCCESS;
case Oddball::kTrue:
- if (deferred_key) SerializeDeferredKey(comma, key);
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
Append("true");
return SUCCESS;
case Oddball::kNull:
- if (deferred_key) SerializeDeferredKey(comma, key);
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
Append("null");
return SUCCESS;
}
@@ -373,6 +382,29 @@ BasicJsonStringifier::Result BasicJsonStringifier::Serialize_(
}
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSValue(
+ Handle<JSValue> object) {
+ bool has_exception = false;
+ String* class_name = object->class_name();
+ if (class_name == isolate_->heap()->String_symbol()) {
+ Handle<Object> value = Execution::ToString(object, &has_exception);
+ if (has_exception) return EXCEPTION;
+ SerializeString(Handle<String>::cast(value));
+ } else if (class_name == isolate_->heap()->Number_symbol()) {
+ Handle<Object> value = Execution::ToNumber(object, &has_exception);
+ if (has_exception) return EXCEPTION;
+ if (value->IsSmi()) return SerializeSmi(Smi::cast(*value));
+ SerializeHeapNumber(Handle<HeapNumber>::cast(value));
+ } else {
+ ASSERT(class_name == isolate_->heap()->Boolean_symbol());
+ Object* value = JSValue::cast(*object)->value();
+ ASSERT(value->IsBoolean());
+ Append(value->IsTrue() ? "true" : "false");
+ }
+ return SUCCESS;
+}
+
+
BasicJsonStringifier::Result BasicJsonStringifier::SerializeSmi(Smi* object) {
static const int kBufferSize = 100;
char chars[kBufferSize];
@@ -396,7 +428,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeDouble(
}
-BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray(
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArray(
Handle<JSArray> object) {
HandleScope handle_scope(isolate_);
Result stack_push = StackPush(object);
@@ -405,8 +437,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray(
Append('[');
switch (object->GetElementsKind()) {
case FAST_SMI_ELEMENTS: {
- Handle<FixedArray> elements = Handle<FixedArray>(
- FixedArray::cast(object->elements()));
+ Handle<FixedArray> elements(FixedArray::cast(object->elements()));
for (int i = 0; i < length; i++) {
if (i > 0) Append(',');
SerializeSmi(Smi::cast(elements->get(i)));
@@ -414,7 +445,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray(
break;
}
case FAST_DOUBLE_ELEMENTS: {
- Handle<FixedDoubleArray> elements = Handle<FixedDoubleArray>(
+ Handle<FixedDoubleArray> elements(
FixedDoubleArray::cast(object->elements()));
for (int i = 0; i < length; i++) {
if (i > 0) Append(',');
@@ -423,11 +454,10 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray(
break;
}
case FAST_ELEMENTS: {
- Handle<FixedArray> elements = Handle<FixedArray>(
- FixedArray::cast(object->elements()));
+ Handle<FixedArray> elements(FixedArray::cast(object->elements()));
for (int i = 0; i < length; i++) {
if (i > 0) Append(',');
- Result result = Serialize(Handle<Object>(elements->get(i)));
+ Result result = SerializeElement(Handle<Object>(elements->get(i)), i);
if (result == SUCCESS) continue;
if (result == UNCHANGED) {
Append("null");
@@ -437,8 +467,14 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray(
}
break;
}
- default:
- return BAILOUT;
+ // TODO(yangguo): The FAST_HOLEY_* cases could be handled in a faster way.
+ // They resemble the non-holey cases except that a prototype chain lookup
+ // is necessary for holes.
+ default: {
+ Result result = SerializeJSArraySlow(object, length);
+ if (result != SUCCESS) return result;
+ break;
+ }
}
Append(']');
StackPop();
@@ -447,16 +483,40 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray(
}
-BasicJsonStringifier::Result BasicJsonStringifier::SerializeObject(
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArraySlow(
+ Handle<JSArray> object, int length) {
+ for (int i = 0; i < length; i++) {
+ if (i > 0) Append(',');
+ Handle<Object> element = Object::GetElement(object, i);
+ if (element->IsUndefined()) {
+ Append("null");
+ } else {
+ Result result = SerializeElement(element, i);
+ if (result == SUCCESS) continue;
+ if (result == UNCHANGED) {
+ Append("null");
+ } else {
+ return result;
+ }
+ }
+ }
+ return SUCCESS;
+}
+
+
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
Handle<JSObject> object) {
HandleScope handle_scope(isolate_);
Result stack_push = StackPush(object);
if (stack_push != SUCCESS) return stack_push;
- if (object->IsJSGlobalProxy()) return BAILOUT;
- bool threw = false;
+ if (object->IsJSGlobalProxy()) {
+ object = Handle<JSObject>(JSObject::cast(object->GetPrototype()));
+ ASSERT(object->IsGlobalObject());
+ }
+ bool has_exception = false;
Handle<FixedArray> contents =
- GetKeysInFixedArrayFor(object, LOCAL_ONLY, &threw);
- if (threw) return BAILOUT;
+ GetKeysInFixedArrayFor(object, LOCAL_ONLY, &has_exception);
+ if (has_exception) return EXCEPTION;
Append('{');
bool comma = false;
for (int i = 0; i < contents->length(); i++) {
@@ -478,10 +538,10 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeObject(
property = GetProperty(object, key_handle);
}
}
- if (property.is_null()) return BAILOUT;
- Result result = SerializeDeferred(property, comma, key_handle);
+ if (property.is_null()) return EXCEPTION;
+ Result result = SerializeProperty(property, comma, key_handle);
if (!comma && result == SUCCESS) comma = true;
- if (result >= BAILOUT) return result;
+ if (result >= EXCEPTION) return result;
}
Append('}');
StackPop();
« no previous file with comments | « src/json.js ('k') | test/mjsunit/json2.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698