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(); |