[json] handle proxies in BasicJsonSerializer.
R=cbruni@chromium.org Review-Url: https://codereview.chromium.org/1994183002 Cr-Commit-Position: refs/heads/master@{#36409}
This commit is contained in:
parent
b71f1cc2f4
commit
a19404f04a
@ -1088,12 +1088,8 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver,
|
||||
length = static_cast<uint32_t>(array->length()->Number());
|
||||
} else {
|
||||
Handle<Object> val;
|
||||
Handle<Object> key = isolate->factory()->length_string();
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate, val, Runtime::GetObjectProperty(isolate, receiver, key),
|
||||
false);
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, val,
|
||||
Object::ToLength(isolate, val), false);
|
||||
isolate, val, Object::GetLengthFromArrayLike(isolate, receiver), false);
|
||||
// TODO(caitp): Support larger element indexes (up to 2^53-1).
|
||||
if (!val->ToUint32(&length)) {
|
||||
length = 0;
|
||||
|
@ -201,9 +201,7 @@ function JSONSerialize(key, holder, replacer, stack, indent, gap) {
|
||||
|
||||
|
||||
function JSONStringify(value, replacer, space) {
|
||||
if (arguments.length === 1 && !IS_PROXY(value)) {
|
||||
return %BasicJSONStringify(value, "");
|
||||
}
|
||||
if (arguments.length === 1) return %BasicJSONStringify(value, "");
|
||||
if (!IS_CALLABLE(replacer) && %is_arraylike(replacer)) {
|
||||
var property_list = new InternalArray();
|
||||
var seen_properties = new GlobalSet();
|
||||
@ -248,7 +246,7 @@ function JSONStringify(value, replacer, space) {
|
||||
} else {
|
||||
gap = "";
|
||||
}
|
||||
if (!IS_CALLABLE(replacer) && !property_list && !IS_PROXY(value)) {
|
||||
if (!IS_CALLABLE(replacer) && !property_list) {
|
||||
return %BasicJSONStringify(value, gap);
|
||||
}
|
||||
return JSONSerialize('', {'': value}, replacer, new Stack(), "", gap);
|
||||
|
@ -85,8 +85,10 @@ class BasicJsonStringifier BASE_EMBEDDED {
|
||||
INLINE(Result SerializeJSArray(Handle<JSArray> object));
|
||||
INLINE(Result SerializeJSObject(Handle<JSObject> object));
|
||||
|
||||
Result SerializeJSArraySlow(Handle<JSArray> object, uint32_t start,
|
||||
uint32_t length);
|
||||
Result SerializeJSProxy(Handle<JSProxy> object);
|
||||
Result SerializeJSReceiverSlow(Handle<JSReceiver> object);
|
||||
Result SerializeArrayLikeSlow(Handle<JSReceiver> object, uint32_t start,
|
||||
uint32_t length);
|
||||
|
||||
void SerializeString(Handle<String> object);
|
||||
|
||||
@ -326,7 +328,7 @@ void BasicJsonStringifier::StackPop() {
|
||||
template <bool deferred_string_key>
|
||||
BasicJsonStringifier::Result BasicJsonStringifier::Serialize_(
|
||||
Handle<Object> object, bool comma, Handle<Object> key) {
|
||||
if (object->IsJSObject()) {
|
||||
if (object->IsJSReceiver()) {
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate_, object,
|
||||
ApplyToJsonFunction(object, key),
|
||||
@ -372,11 +374,14 @@ BasicJsonStringifier::Result BasicJsonStringifier::Serialize_(
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
SerializeString(Handle<String>::cast(object));
|
||||
return SUCCESS;
|
||||
} else if (object->IsJSObject()) {
|
||||
} else if (object->IsJSReceiver()) {
|
||||
if (object->IsCallable()) return UNCHANGED;
|
||||
// Go to slow path for global proxy and objects requiring access checks.
|
||||
if (object->IsAccessCheckNeeded() || object->IsJSGlobalProxy()) break;
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
if (object->IsJSProxy()) {
|
||||
return SerializeJSProxy(Handle<JSProxy>::cast(object));
|
||||
}
|
||||
return SerializeJSObject(Handle<JSObject>::cast(object));
|
||||
}
|
||||
}
|
||||
@ -494,7 +499,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArray(
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (object->length() != *old_length ||
|
||||
object->GetElementsKind() != FAST_ELEMENTS) {
|
||||
Result result = SerializeJSArraySlow(object, i, length);
|
||||
Result result = SerializeArrayLikeSlow(object, i, length);
|
||||
if (result != SUCCESS) return result;
|
||||
break;
|
||||
}
|
||||
@ -516,7 +521,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArray(
|
||||
// The FAST_HOLEY_* cases could be handled in a faster way. They resemble
|
||||
// the non-holey cases except that a lookup is necessary for holes.
|
||||
default: {
|
||||
Result result = SerializeJSArraySlow(object, 0, length);
|
||||
Result result = SerializeArrayLikeSlow(object, 0, length);
|
||||
if (result != SUCCESS) return result;
|
||||
break;
|
||||
}
|
||||
@ -528,9 +533,8 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArray(
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArraySlow(
|
||||
Handle<JSArray> object, uint32_t start, uint32_t length) {
|
||||
BasicJsonStringifier::Result BasicJsonStringifier::SerializeArrayLikeSlow(
|
||||
Handle<JSReceiver> object, uint32_t start, uint32_t length) {
|
||||
for (uint32_t i = start; i < length; i++) {
|
||||
Separator(i == 0);
|
||||
Handle<Object> element;
|
||||
@ -552,7 +556,6 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArraySlow(
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
|
||||
Handle<JSObject> object) {
|
||||
HandleScope handle_scope(isolate_);
|
||||
@ -560,15 +563,17 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
|
||||
if (stack_push != SUCCESS) return stack_push;
|
||||
DCHECK(!object->IsJSGlobalProxy() && !object->IsJSGlobalObject());
|
||||
|
||||
builder_.AppendCharacter('{');
|
||||
Indent();
|
||||
bool comma = false;
|
||||
|
||||
if (object->HasFastProperties() &&
|
||||
!object->HasIndexedInterceptor() &&
|
||||
!object->HasNamedInterceptor() &&
|
||||
object->elements()->length() == 0) {
|
||||
Handle<Map> map(object->map());
|
||||
if (object->map()->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER &&
|
||||
object->HasFastProperties() &&
|
||||
Handle<JSObject>::cast(object)->elements()->length() == 0) {
|
||||
DCHECK(object->IsJSObject());
|
||||
Handle<JSObject> js_obj = Handle<JSObject>::cast(object);
|
||||
DCHECK(!js_obj->HasIndexedInterceptor());
|
||||
DCHECK(!js_obj->HasNamedInterceptor());
|
||||
Handle<Map> map(js_obj->map());
|
||||
builder_.AppendCharacter('{');
|
||||
Indent();
|
||||
bool comma = false;
|
||||
for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
|
||||
Handle<Name> name(map->instance_descriptors()->GetKey(i), isolate_);
|
||||
// TODO(rossberg): Should this throw?
|
||||
@ -577,60 +582,104 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
|
||||
PropertyDetails details = map->instance_descriptors()->GetDetails(i);
|
||||
if (details.IsDontEnum()) continue;
|
||||
Handle<Object> property;
|
||||
if (details.type() == DATA && *map == object->map()) {
|
||||
if (details.type() == DATA && *map == js_obj->map()) {
|
||||
FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
|
||||
Isolate* isolate = object->GetIsolate();
|
||||
if (object->IsUnboxedDoubleField(field_index)) {
|
||||
double value = object->RawFastDoublePropertyAt(field_index);
|
||||
property = isolate->factory()->NewHeapNumber(value);
|
||||
|
||||
if (js_obj->IsUnboxedDoubleField(field_index)) {
|
||||
double value = js_obj->RawFastDoublePropertyAt(field_index);
|
||||
property = isolate_->factory()->NewHeapNumber(value);
|
||||
} else {
|
||||
property = handle(object->RawFastPropertyAt(field_index), isolate);
|
||||
property = handle(js_obj->RawFastPropertyAt(field_index), isolate_);
|
||||
}
|
||||
} else {
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate_, property,
|
||||
Object::GetPropertyOrElement(object, key),
|
||||
isolate_, property, Object::GetPropertyOrElement(js_obj, key),
|
||||
EXCEPTION);
|
||||
}
|
||||
Result result = SerializeProperty(property, comma, key);
|
||||
if (!comma && result == SUCCESS) comma = true;
|
||||
if (result == EXCEPTION) return result;
|
||||
}
|
||||
Unindent();
|
||||
if (comma) NewLine();
|
||||
builder_.AppendCharacter('}');
|
||||
} else {
|
||||
Handle<FixedArray> contents;
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate_, contents,
|
||||
JSReceiver::GetKeys(object, OWN_ONLY, ENUMERABLE_STRINGS), EXCEPTION);
|
||||
Result result = SerializeJSReceiverSlow(object);
|
||||
if (result != SUCCESS) return result;
|
||||
}
|
||||
StackPop();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
for (int i = 0; i < contents->length(); i++) {
|
||||
Object* key = contents->get(i);
|
||||
Handle<String> key_handle;
|
||||
MaybeHandle<Object> maybe_property;
|
||||
if (key->IsString()) {
|
||||
key_handle = Handle<String>(String::cast(key), isolate_);
|
||||
maybe_property = Object::GetPropertyOrElement(object, key_handle);
|
||||
BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSReceiverSlow(
|
||||
Handle<JSReceiver> object) {
|
||||
Handle<FixedArray> contents;
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate_, contents,
|
||||
JSReceiver::GetKeys(object, OWN_ONLY, ENUMERABLE_STRINGS), EXCEPTION);
|
||||
|
||||
builder_.AppendCharacter('{');
|
||||
Indent();
|
||||
bool comma = false;
|
||||
for (int i = 0; i < contents->length(); i++) {
|
||||
Object* key = contents->get(i);
|
||||
Handle<String> key_handle;
|
||||
MaybeHandle<Object> maybe_property;
|
||||
if (key->IsString()) {
|
||||
key_handle = Handle<String>(String::cast(key), isolate_);
|
||||
maybe_property = Object::GetPropertyOrElement(object, key_handle);
|
||||
} else {
|
||||
DCHECK(key->IsNumber());
|
||||
key_handle = factory()->NumberToString(Handle<Object>(key, isolate_));
|
||||
if (key->IsSmi()) {
|
||||
maybe_property =
|
||||
JSReceiver::GetElement(isolate_, object, Smi::cast(key)->value());
|
||||
} else {
|
||||
DCHECK(key->IsNumber());
|
||||
key_handle = factory()->NumberToString(Handle<Object>(key, isolate_));
|
||||
if (key->IsSmi()) {
|
||||
maybe_property =
|
||||
JSReceiver::GetElement(isolate_, object, Smi::cast(key)->value());
|
||||
} else {
|
||||
maybe_property = Object::GetPropertyOrElement(object, key_handle);
|
||||
}
|
||||
maybe_property = Object::GetPropertyOrElement(object, key_handle);
|
||||
}
|
||||
Handle<Object> property;
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate_, property, maybe_property, EXCEPTION);
|
||||
Result result = SerializeProperty(property, comma, key_handle);
|
||||
if (!comma && result == SUCCESS) comma = true;
|
||||
if (result == EXCEPTION) return result;
|
||||
}
|
||||
Handle<Object> property;
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, property, maybe_property,
|
||||
EXCEPTION);
|
||||
Result result = SerializeProperty(property, comma, key_handle);
|
||||
if (!comma && result == SUCCESS) comma = true;
|
||||
if (result == EXCEPTION) return result;
|
||||
}
|
||||
Unindent();
|
||||
if (comma) NewLine();
|
||||
builder_.AppendCharacter('}');
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSProxy(
|
||||
Handle<JSProxy> object) {
|
||||
Result stack_push = StackPush(object);
|
||||
if (stack_push != SUCCESS) return stack_push;
|
||||
Maybe<bool> is_array = Object::IsArray(object);
|
||||
if (is_array.IsNothing()) return EXCEPTION;
|
||||
if (is_array.FromJust()) {
|
||||
Handle<Object> length_object;
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate_, length_object,
|
||||
Object::GetLengthFromArrayLike(isolate_, object), EXCEPTION);
|
||||
uint32_t length;
|
||||
if (!length_object->ToUint32(&length)) {
|
||||
// Technically, we need to be able to handle lengths outside the
|
||||
// uint32_t range. However, we would run into string size overflow
|
||||
// if we tried to stringify such an array.
|
||||
isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError());
|
||||
return EXCEPTION;
|
||||
}
|
||||
builder_.AppendCharacter('[');
|
||||
Indent();
|
||||
Result result = SerializeArrayLikeSlow(object, 0, length);
|
||||
if (result != SUCCESS) return result;
|
||||
Unindent();
|
||||
if (length > 0) NewLine();
|
||||
builder_.AppendCharacter(']');
|
||||
} else {
|
||||
Result result = SerializeJSReceiverSlow(object);
|
||||
if (result != SUCCESS) return result;
|
||||
}
|
||||
StackPop();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
@ -719,14 +719,9 @@ MaybeHandle<FixedArray> Object::CreateListFromArrayLike(
|
||||
}
|
||||
// 4. Let len be ? ToLength(? Get(obj, "length")).
|
||||
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
|
||||
Handle<Object> raw_length_obj;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, raw_length_obj,
|
||||
JSReceiver::GetProperty(receiver, isolate->factory()->length_string()),
|
||||
FixedArray);
|
||||
Handle<Object> raw_length_number;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number,
|
||||
Object::ToLength(isolate, raw_length_obj),
|
||||
Object::GetLengthFromArrayLike(isolate, receiver),
|
||||
FixedArray);
|
||||
uint32_t len;
|
||||
if (!raw_length_number->ToUint32(&len) ||
|
||||
@ -772,6 +767,16 @@ MaybeHandle<FixedArray> Object::CreateListFromArrayLike(
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
MaybeHandle<Object> Object::GetLengthFromArrayLike(Isolate* isolate,
|
||||
Handle<Object> object) {
|
||||
Handle<Object> val;
|
||||
Handle<Object> key = isolate->factory()->length_string();
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, val, Runtime::GetObjectProperty(isolate, object, key), Object);
|
||||
return Object::ToLength(isolate, val);
|
||||
}
|
||||
|
||||
// static
|
||||
Maybe<bool> JSReceiver::HasProperty(LookupIterator* it) {
|
||||
for (; it->IsFound(); it->Next()) {
|
||||
|
@ -1177,6 +1177,10 @@ class Object {
|
||||
MUST_USE_RESULT static MaybeHandle<FixedArray> CreateListFromArrayLike(
|
||||
Isolate* isolate, Handle<Object> object, ElementTypes element_types);
|
||||
|
||||
// Get length property and apply ToLength.
|
||||
MUST_USE_RESULT static MaybeHandle<Object> GetLengthFromArrayLike(
|
||||
Isolate* isolate, Handle<Object> object);
|
||||
|
||||
// Check whether |object| is an instance of Error or NativeError.
|
||||
static bool IsErrorObject(Isolate* isolate, Handle<Object> object);
|
||||
|
||||
|
@ -507,3 +507,56 @@ for (var i in log) assertSame(target, log[i][1]);
|
||||
assertEquals(["get", target, "length", proxy], log[0]);
|
||||
assertEquals(["get", target, "0", proxy], log[1]);
|
||||
assertEquals(["deleteProperty", target, "0"], log[2]);
|
||||
|
||||
proxy = new Proxy([], {
|
||||
get: function(target, property) {
|
||||
if (property == "length") return 7;
|
||||
return 0;
|
||||
},
|
||||
});
|
||||
assertEquals('[[0,0,0,0,0,0,0]]', JSON.stringify([proxy]));
|
||||
|
||||
proxy = new Proxy([], {
|
||||
get: function(target, property) {
|
||||
if (property == "length") return 1E40;
|
||||
return 0;
|
||||
},
|
||||
});
|
||||
assertThrows(() => JSON.stringify([proxy]), RangeError);
|
||||
|
||||
log = [];
|
||||
proxy = new Proxy({}, {
|
||||
ownKeys: function() {
|
||||
log.push("ownKeys");
|
||||
return ["0", "a", "b"];
|
||||
},
|
||||
get: function(target, property) {
|
||||
log.push("get " + property);
|
||||
return property.toUpperCase();
|
||||
},
|
||||
getOwnPropertyDescriptor: function(target, property) {
|
||||
log.push("descriptor " + property);
|
||||
return {enumerable: true, configurable: true};
|
||||
},
|
||||
isExtensible: assertUnreachable,
|
||||
has: assertUnreachable,
|
||||
getPrototypeOf: assertUnreachable,
|
||||
setPrototypeOf: assertUnreachable,
|
||||
preventExtensions: assertUnreachable,
|
||||
setPrototypeOf: assertUnreachable,
|
||||
defineProperty: assertUnreachable,
|
||||
set: assertUnreachable,
|
||||
deleteProperty: assertUnreachable,
|
||||
apply: assertUnreachable,
|
||||
construct: assertUnreachable,
|
||||
});
|
||||
|
||||
assertEquals('[{"0":"0","a":"A","b":"B"}]', JSON.stringify([proxy]));
|
||||
assertEquals(['get toJSON',
|
||||
'ownKeys',
|
||||
'descriptor 0',
|
||||
'descriptor a',
|
||||
'descriptor b',
|
||||
'get 0',
|
||||
'get a',
|
||||
'get b'], log);
|
||||
|
Loading…
Reference in New Issue
Block a user