Fix JSON.stringify wrt harmony proxies.
BUG= Review URL: https://chromiumcodereview.appspot.com/11312063 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12851 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
17fab1dfa1
commit
e26012e771
@ -48,7 +48,7 @@ class BasicJsonStringifier BASE_EMBEDDED {
|
||||
|
||||
enum Result { UNCHANGED, SUCCESS, EXCEPTION, CIRCULAR, STACK_OVERFLOW };
|
||||
|
||||
template <bool is_ascii> void Extend();
|
||||
void Extend();
|
||||
|
||||
void ChangeEncoding();
|
||||
|
||||
@ -82,9 +82,14 @@ class BasicJsonStringifier BASE_EMBEDDED {
|
||||
Handle<Object> ApplyToJsonFunction(Handle<Object> object,
|
||||
Handle<Object> key);
|
||||
|
||||
Result SerializeGeneric(Handle<Object> object,
|
||||
Handle<Object> key,
|
||||
bool deferred_comma,
|
||||
bool deferred_key);
|
||||
|
||||
// Entry point to serialize the object.
|
||||
INLINE(Result SerializeObject(Handle<Object> obj)) {
|
||||
return Serialize_<false>(obj, false, isolate_->factory()->empty_string());
|
||||
return Serialize_<false>(obj, false, factory_->empty_string());
|
||||
}
|
||||
|
||||
// Serialize an array element.
|
||||
@ -147,7 +152,7 @@ class BasicJsonStringifier BASE_EMBEDDED {
|
||||
void StackPop();
|
||||
|
||||
INLINE(Handle<String> accumulator()) {
|
||||
return Handle<String>(String::cast(accumulator_store_->value()));
|
||||
return Handle<String>(String::cast(accumulator_store_->value()), isolate_);
|
||||
}
|
||||
|
||||
INLINE(void set_accumulator(Handle<String> string)) {
|
||||
@ -155,6 +160,7 @@ class BasicJsonStringifier BASE_EMBEDDED {
|
||||
}
|
||||
|
||||
Isolate* isolate_;
|
||||
Factory* factory_;
|
||||
// We use a value wrapper for the string accumulator to keep the
|
||||
// (indirect) handle to it in the outermost handle scope.
|
||||
Handle<JSValue> accumulator_store_;
|
||||
@ -209,13 +215,13 @@ const char* const BasicJsonStringifier::JsonEscapeTable =
|
||||
|
||||
BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate)
|
||||
: isolate_(isolate), current_index_(0), is_ascii_(true) {
|
||||
factory_ = isolate_->factory();
|
||||
accumulator_store_ = Handle<JSValue>::cast(
|
||||
isolate_->factory()->ToObject(isolate_->factory()->empty_string()));
|
||||
factory_->ToObject(factory_->empty_string()));
|
||||
part_length_ = kInitialPartLength;
|
||||
current_part_ =
|
||||
isolate_->factory()->NewRawAsciiString(kInitialPartLength);
|
||||
tojson_symbol_ = isolate_->factory()->LookupAsciiSymbol("toJSON");
|
||||
stack_ = isolate_->factory()->NewJSArray(8);
|
||||
current_part_ = factory_->NewRawAsciiString(kInitialPartLength);
|
||||
tojson_symbol_ = factory_->LookupAsciiSymbol("toJSON");
|
||||
stack_ = factory_->NewJSArray(8);
|
||||
}
|
||||
|
||||
|
||||
@ -225,9 +231,9 @@ MaybeObject* BasicJsonStringifier::Stringify(Handle<Object> object) {
|
||||
return isolate_->heap()->undefined_value();
|
||||
case SUCCESS:
|
||||
ShrinkCurrentPart();
|
||||
return *isolate_->factory()->NewConsString(accumulator(), current_part_);
|
||||
return *factory_->NewConsString(accumulator(), current_part_);
|
||||
case CIRCULAR:
|
||||
return isolate_->Throw(*isolate_->factory()->NewTypeError(
|
||||
return isolate_->Throw(*factory_->NewTypeError(
|
||||
"circular_structure", HandleVector<Object>(NULL, 0)));
|
||||
case STACK_OVERFLOW:
|
||||
return isolate_->StackOverflow();
|
||||
@ -246,7 +252,7 @@ void BasicJsonStringifier::Append_(Char c) {
|
||||
SeqTwoByteString::cast(*current_part_)->SeqTwoByteStringSet(
|
||||
current_index_++, c);
|
||||
}
|
||||
if (current_index_ == part_length_) Extend<is_ascii>();
|
||||
if (current_index_ == part_length_) Extend();
|
||||
}
|
||||
|
||||
|
||||
@ -260,20 +266,20 @@ Handle<Object> BasicJsonStringifier::GetProperty(Handle<JSObject> object,
|
||||
Handle<String> key) {
|
||||
LookupResult lookup(isolate_);
|
||||
object->LocalLookupRealNamedProperty(*key, &lookup);
|
||||
if (!lookup.IsProperty()) return isolate_->factory()->undefined_value();
|
||||
if (!lookup.IsProperty()) return factory_->undefined_value();
|
||||
switch (lookup.type()) {
|
||||
case NORMAL: {
|
||||
Object* value = lookup.holder()->GetNormalizedProperty(&lookup);
|
||||
ASSERT(!value->IsTheHole());
|
||||
return Handle<Object>(value);
|
||||
return Handle<Object>(value, isolate_);
|
||||
}
|
||||
case FIELD: {
|
||||
Object* value = lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
|
||||
ASSERT(!value->IsTheHole());
|
||||
return Handle<Object>(value);
|
||||
return Handle<Object>(value, isolate_);
|
||||
}
|
||||
case CONSTANT_FUNCTION:
|
||||
return Handle<Object>(lookup.GetConstantFunction());
|
||||
return Handle<Object>(lookup.GetConstantFunction(), isolate_);
|
||||
default: {
|
||||
PropertyAttributes attr;
|
||||
return Object::GetProperty(object, object, &lookup, key, &attr);
|
||||
@ -294,7 +300,7 @@ Handle<Object> BasicJsonStringifier::ApplyToJsonFunction(
|
||||
if (!fun->IsJSFunction()) return object;
|
||||
|
||||
// Call toJSON function.
|
||||
if (key->IsSmi()) key = isolate_->factory()->NumberToString(key);
|
||||
if (key->IsSmi()) key = factory_->NumberToString(key);
|
||||
Handle<Object> argv[] = { key };
|
||||
bool has_exception = false;
|
||||
HandleScope scope(isolate_);
|
||||
@ -338,47 +344,84 @@ BasicJsonStringifier::Result BasicJsonStringifier::Serialize_(
|
||||
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()) {
|
||||
return SerializeJSValue(Handle<JSValue>::cast(object));
|
||||
} else {
|
||||
return SerializeJSObject(Handle<JSObject>::cast(object));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle non-JSObject.
|
||||
if (object->IsString()) {
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
SerializeString(Handle<String>::cast(object));
|
||||
return SUCCESS;
|
||||
} else if (object->IsSmi()) {
|
||||
if (object->IsSmi()) {
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
return SerializeSmi(Smi::cast(*object));
|
||||
} else if (object->IsHeapNumber()) {
|
||||
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_string_key) SerializeDeferredKey(comma, key);
|
||||
Append("false");
|
||||
return SUCCESS;
|
||||
case Oddball::kTrue:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
Append("true");
|
||||
return SUCCESS;
|
||||
case Oddball::kNull:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
Append("null");
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return UNCHANGED;
|
||||
switch (HeapObject::cast(*object)->map()->instance_type()) {
|
||||
case HEAP_NUMBER_TYPE:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
|
||||
case ODDBALL_TYPE:
|
||||
switch (Oddball::cast(*object)->kind()) {
|
||||
case Oddball::kFalse:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
Append("false");
|
||||
return SUCCESS;
|
||||
case Oddball::kTrue:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
Append("true");
|
||||
return SUCCESS;
|
||||
case Oddball::kNull:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
Append("null");
|
||||
return SUCCESS;
|
||||
default:
|
||||
return UNCHANGED;
|
||||
}
|
||||
case JS_ARRAY_TYPE:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
return SerializeJSArray(Handle<JSArray>::cast(object));
|
||||
case JS_VALUE_TYPE:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
return SerializeJSValue(Handle<JSValue>::cast(object));
|
||||
case JS_FUNCTION_TYPE:
|
||||
return UNCHANGED;
|
||||
default:
|
||||
if (object->IsString()) {
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
SerializeString(Handle<String>::cast(object));
|
||||
return SUCCESS;
|
||||
} else if (object->IsJSObject()) {
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
return SerializeJSObject(Handle<JSObject>::cast(object));
|
||||
} else {
|
||||
return SerializeGeneric(object, key, comma, deferred_string_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BasicJsonStringifier::Result BasicJsonStringifier::SerializeGeneric(
|
||||
Handle<Object> object,
|
||||
Handle<Object> key,
|
||||
bool deferred_comma,
|
||||
bool deferred_key) {
|
||||
Handle<JSObject> builtins(isolate_->native_context()->builtins());
|
||||
Handle<JSFunction> builtin = Handle<JSFunction>::cast(
|
||||
v8::internal::GetProperty(builtins, "JSONSerializeAdapter"));
|
||||
|
||||
Handle<Object> argv[] = { key, object };
|
||||
bool has_exception = false;
|
||||
Handle<Object> result =
|
||||
Execution::Call(builtin, object, 2, argv, &has_exception);
|
||||
if (has_exception) return EXCEPTION;
|
||||
if (result->IsUndefined()) return UNCHANGED;
|
||||
if (deferred_key) {
|
||||
if (key->IsSmi()) key = factory_->NumberToString(key);
|
||||
SerializeDeferredKey(deferred_comma, key);
|
||||
}
|
||||
|
||||
Handle<String> result_string = Handle<String>::cast(result);
|
||||
// Shrink current part, attach it to the accumulator, also attach the result
|
||||
// string to the accumulator, and allocate a new part.
|
||||
ShrinkCurrentPart(); // Shrink.
|
||||
part_length_ = kInitialPartLength; // Allocate conservatively.
|
||||
Extend(); // Attach current part and allocate new part.
|
||||
// Attach result string to the accumulator.
|
||||
set_accumulator(factory_->NewConsString(accumulator(), result_string));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@ -437,7 +480,8 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArray(
|
||||
Append('[');
|
||||
switch (object->GetElementsKind()) {
|
||||
case FAST_SMI_ELEMENTS: {
|
||||
Handle<FixedArray> elements(FixedArray::cast(object->elements()));
|
||||
Handle<FixedArray> elements(
|
||||
FixedArray::cast(object->elements()), isolate_);
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (i > 0) Append(',');
|
||||
SerializeSmi(Smi::cast(elements->get(i)));
|
||||
@ -446,7 +490,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArray(
|
||||
}
|
||||
case FAST_DOUBLE_ELEMENTS: {
|
||||
Handle<FixedDoubleArray> elements(
|
||||
FixedDoubleArray::cast(object->elements()));
|
||||
FixedDoubleArray::cast(object->elements()), isolate_);
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (i > 0) Append(',');
|
||||
SerializeDouble(elements->get_scalar(i));
|
||||
@ -454,10 +498,12 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArray(
|
||||
break;
|
||||
}
|
||||
case FAST_ELEMENTS: {
|
||||
Handle<FixedArray> elements(FixedArray::cast(object->elements()));
|
||||
Handle<FixedArray> elements(
|
||||
FixedArray::cast(object->elements()), isolate_);
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (i > 0) Append(',');
|
||||
Result result = SerializeElement(Handle<Object>(elements->get(i)), i);
|
||||
Result result =
|
||||
SerializeElement(Handle<Object>(elements->get(i), isolate_), i);
|
||||
if (result == SUCCESS) continue;
|
||||
if (result == UNCHANGED) {
|
||||
Append("null");
|
||||
@ -510,7 +556,8 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
|
||||
Result stack_push = StackPush(object);
|
||||
if (stack_push != SUCCESS) return stack_push;
|
||||
if (object->IsJSGlobalProxy()) {
|
||||
object = Handle<JSObject>(JSObject::cast(object->GetPrototype()));
|
||||
object = Handle<JSObject>(
|
||||
JSObject::cast(object->GetPrototype()), isolate_);
|
||||
ASSERT(object->IsGlobalObject());
|
||||
}
|
||||
bool has_exception = false;
|
||||
@ -524,11 +571,11 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
|
||||
Handle<String> key_handle;
|
||||
Handle<Object> property;
|
||||
if (key->IsString()) {
|
||||
key_handle = Handle<String>(String::cast(key));
|
||||
key_handle = Handle<String>(String::cast(key), isolate_);
|
||||
property = GetProperty(object, key_handle);
|
||||
} else {
|
||||
ASSERT(key->IsNumber());
|
||||
key_handle = isolate_->factory()->NumberToString(Handle<Object>(key));
|
||||
key_handle = factory_->NumberToString(Handle<Object>(key, isolate_));
|
||||
uint32_t index;
|
||||
if (key->IsSmi()) {
|
||||
property = Object::GetElement(object, Smi::cast(key)->value());
|
||||
@ -553,7 +600,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
|
||||
void BasicJsonStringifier::ShrinkCurrentPart() {
|
||||
ASSERT(current_index_ < part_length_);
|
||||
if (current_index_ == 0) {
|
||||
current_part_ = isolate_->factory()->empty_string();
|
||||
current_part_ = factory_->empty_string();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -580,19 +627,15 @@ void BasicJsonStringifier::ShrinkCurrentPart() {
|
||||
}
|
||||
|
||||
|
||||
template <bool is_ascii>
|
||||
void BasicJsonStringifier::Extend() {
|
||||
set_accumulator(
|
||||
isolate_->factory()->NewConsString(accumulator(), current_part_));
|
||||
set_accumulator(factory_->NewConsString(accumulator(), current_part_));
|
||||
if (part_length_ <= kMaxPartLength / kPartLengthGrowthFactor) {
|
||||
part_length_ *= kPartLengthGrowthFactor;
|
||||
}
|
||||
if (is_ascii) {
|
||||
current_part_ =
|
||||
isolate_->factory()->NewRawAsciiString(part_length_);
|
||||
if (is_ascii_) {
|
||||
current_part_ = factory_->NewRawAsciiString(part_length_);
|
||||
} else {
|
||||
current_part_ =
|
||||
isolate_->factory()->NewRawTwoByteString(part_length_);
|
||||
current_part_ = factory_->NewRawTwoByteString(part_length_);
|
||||
}
|
||||
current_index_ = 0;
|
||||
}
|
||||
@ -600,10 +643,8 @@ void BasicJsonStringifier::Extend() {
|
||||
|
||||
void BasicJsonStringifier::ChangeEncoding() {
|
||||
ShrinkCurrentPart();
|
||||
set_accumulator(
|
||||
isolate_->factory()->NewConsString(accumulator(), current_part_));
|
||||
current_part_ =
|
||||
isolate_->factory()->NewRawTwoByteString(part_length_);
|
||||
set_accumulator(factory_->NewConsString(accumulator(), current_part_));
|
||||
current_part_ = factory_->NewRawTwoByteString(part_length_);
|
||||
current_index_ = 0;
|
||||
is_ascii_ = false;
|
||||
}
|
||||
|
@ -215,4 +215,12 @@ function SetUpJSON() {
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
function JSONSerializeAdapter(key, object) {
|
||||
var holder = {};
|
||||
holder[key] = object;
|
||||
// No need to pass the actual holder since there is no replacer function.
|
||||
return JSONSerialize(key, holder, void 0, new InternalArray(), "", "");
|
||||
}
|
||||
|
||||
SetUpJSON();
|
||||
|
@ -9188,7 +9188,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_AllocateInNewSpace) {
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_PushIfAbsent) {
|
||||
ASSERT(args.length() == 2);
|
||||
CONVERT_ARG_CHECKED(JSArray, array, 0);
|
||||
CONVERT_ARG_CHECKED(JSObject, element, 1);
|
||||
CONVERT_ARG_CHECKED(JSReceiver, element, 1);
|
||||
RUNTIME_ASSERT(array->HasFastSmiOrObjectElements());
|
||||
int length = Smi::cast(array->length())->value();
|
||||
FixedArray* elements = FixedArray::cast(array->elements());
|
||||
|
156
test/mjsunit/harmony/proxies-json.js
Normal file
156
test/mjsunit/harmony/proxies-json.js
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --harmony
|
||||
|
||||
function testStringify(expected, object) {
|
||||
// Test fast case that bails out to slow case.
|
||||
assertEquals(expected, JSON.stringify(object));
|
||||
// Test slow case.
|
||||
assertEquals(expected, JSON.stringify(object, undefined, 0));
|
||||
}
|
||||
|
||||
// Test serializing a proxy, function proxy and objects that contain them.
|
||||
var handler1 = {
|
||||
get: function(target, name) {
|
||||
return name.toUpperCase();
|
||||
},
|
||||
enumerate: function(target) {
|
||||
return ['a', 'b', 'c'];
|
||||
},
|
||||
getOwnPropertyDescriptor: function(target, name) {
|
||||
return { enumerable: true };
|
||||
}
|
||||
}
|
||||
|
||||
var proxy1 = Proxy.create(handler1);
|
||||
testStringify('{"a":"A","b":"B","c":"C"}', proxy1);
|
||||
|
||||
var proxy_fun = Proxy.createFunction(handler1, function() { return 1; });
|
||||
testStringify(undefined, proxy_fun);
|
||||
testStringify('[1,null]', [1, proxy_fun]);
|
||||
|
||||
var parent1a = { b: proxy1 };
|
||||
testStringify('{"b":{"a":"A","b":"B","c":"C"}}', parent1a);
|
||||
|
||||
var parent1b = { a: 123, b: proxy1, c: true };
|
||||
testStringify('{"a":123,"b":{"a":"A","b":"B","c":"C"},"c":true}', parent1b);
|
||||
|
||||
var parent1c = [123, proxy1, true];
|
||||
testStringify('[123,{"a":"A","b":"B","c":"C"},true]', parent1c);
|
||||
|
||||
// Proxy with side effect.
|
||||
var handler2 = {
|
||||
get: function(target, name) {
|
||||
delete parent2.c;
|
||||
return name.toUpperCase();
|
||||
},
|
||||
enumerate: function(target) {
|
||||
return ['a', 'b', 'c'];
|
||||
},
|
||||
getOwnPropertyDescriptor: function(target, name) {
|
||||
return { enumerable: true };
|
||||
}
|
||||
}
|
||||
|
||||
var proxy2 = Proxy.create(handler2);
|
||||
var parent2 = { a: "delete", b: proxy2, c: "remove" };
|
||||
var expected2 = '{"a":"delete","b":{"a":"A","b":"B","c":"C"}}';
|
||||
assertEquals(expected2, JSON.stringify(parent2));
|
||||
parent2.c = "remove"; // Revert side effect.
|
||||
assertEquals(expected2, JSON.stringify(parent2, undefined, 0));
|
||||
|
||||
// Proxy with a get function that uses the first argument.
|
||||
var handler3 = {
|
||||
get: function(target, name) {
|
||||
if (name == 'valueOf') return function() { return "proxy" };
|
||||
return name + "(" + target + ")";
|
||||
},
|
||||
enumerate: function(target) {
|
||||
return ['a', 'b', 'c'];
|
||||
},
|
||||
getOwnPropertyDescriptor: function(target, name) {
|
||||
return { enumerable: true };
|
||||
}
|
||||
}
|
||||
|
||||
var proxy3 = Proxy.create(handler3);
|
||||
var parent3 = { x: 123, y: proxy3 }
|
||||
testStringify('{"x":123,"y":{"a":"a(proxy)","b":"b(proxy)","c":"c(proxy)"}}',
|
||||
parent3);
|
||||
|
||||
// Empty proxy.
|
||||
var handler4 = {
|
||||
get: function(target, name) {
|
||||
return 0;
|
||||
},
|
||||
enumerate: function(target) {
|
||||
return [];
|
||||
},
|
||||
getOwnPropertyDescriptor: function(target, name) {
|
||||
return { enumerable: false };
|
||||
}
|
||||
}
|
||||
|
||||
var proxy4 = Proxy.create(handler4);
|
||||
testStringify('{}', proxy4);
|
||||
testStringify('{"a":{}}', { a: proxy4 });
|
||||
|
||||
// Proxy that provides a toJSON function that uses this.
|
||||
var handler5 = {
|
||||
get: function(target, name) {
|
||||
if (name == 'z') return 97000;
|
||||
return function(key) { return key.charCodeAt(0) + this.z; };
|
||||
},
|
||||
enumerate: function(target) {
|
||||
return ['toJSON', 'z'];
|
||||
},
|
||||
getOwnPropertyDescriptor: function(target, name) {
|
||||
return { enumerable: true };
|
||||
}
|
||||
}
|
||||
|
||||
var proxy5 = Proxy.create(handler5);
|
||||
testStringify('{"a":97097}', { a: proxy5 });
|
||||
|
||||
// Proxy that provides a toJSON function that returns undefined.
|
||||
var handler6 = {
|
||||
get: function(target, name) {
|
||||
return function(key) { return undefined; };
|
||||
},
|
||||
enumerate: function(target) {
|
||||
return ['toJSON'];
|
||||
},
|
||||
getOwnPropertyDescriptor: function(target, name) {
|
||||
return { enumerable: true };
|
||||
}
|
||||
}
|
||||
|
||||
var proxy6 = Proxy.create(handler6);
|
||||
testStringify('[1,null,true]', [1, proxy6, true]);
|
||||
testStringify('{"a":1,"c":true}', {a: 1, b: proxy6, c: true});
|
||||
|
@ -109,6 +109,10 @@ assertEquals('["00","11"]', JSON.stringify(tojson_with_key_2));
|
||||
var tojson_ex = { toJSON: function(key) { throw "123" } };
|
||||
assertThrows(function() { JSON.stringify(tojson_ex); });
|
||||
|
||||
// Test toJSON with access to this.
|
||||
var obj = { toJSON: function(key) { return this.a + key; }, a: "x" };
|
||||
assertEquals('{"y":"xy"}', JSON.stringify({y: obj}));
|
||||
|
||||
// Test holes in arrays.
|
||||
var fast_smi = [1, 2, 3, 4];
|
||||
fast_smi.__proto__ = [7, 7, 7, 7];
|
||||
|
Loading…
Reference in New Issue
Block a user