[json] Replace JSArray with std::vector to track circular structures
This CL prepares JSON#stringify for improved error messages when serializing circular structures. To this end, we also push the key/index, in addition to the object itself, onto the stack that keeps track of circular structures. The stack itself is changed from a JSArray to a std::vector. R=yangguo@chromium.org Bug: v8:6513, v8:8698 Change-Id: I6dc4cb3be75a4514281411c654337f37c8798e55 Reviewed-on: https://chromium-review.googlesource.com/c/1424863 Reviewed-by: Yang Guo <yangguo@chromium.org> Commit-Queue: Simon Zünd <szuend@chromium.org> Cr-Commit-Position: refs/heads/master@{#59019}
This commit is contained in:
parent
9042f2e333
commit
d6779b2522
@ -74,12 +74,13 @@ class JsonStringifier {
|
||||
return SerializeDouble(object->value());
|
||||
}
|
||||
|
||||
Result SerializeJSValue(Handle<JSValue> object);
|
||||
Result SerializeJSValue(Handle<JSValue> object, Handle<Object> key);
|
||||
|
||||
V8_INLINE Result SerializeJSArray(Handle<JSArray> object);
|
||||
V8_INLINE Result SerializeJSObject(Handle<JSObject> object);
|
||||
V8_INLINE Result SerializeJSArray(Handle<JSArray> object, Handle<Object> key);
|
||||
V8_INLINE Result SerializeJSObject(Handle<JSObject> object,
|
||||
Handle<Object> key);
|
||||
|
||||
Result SerializeJSProxy(Handle<JSProxy> object);
|
||||
Result SerializeJSProxy(Handle<JSProxy> object, Handle<Object> key);
|
||||
Result SerializeJSReceiverSlow(Handle<JSReceiver> object);
|
||||
Result SerializeArrayLikeSlow(Handle<JSReceiver> object, uint32_t start,
|
||||
uint32_t length);
|
||||
@ -105,7 +106,7 @@ class JsonStringifier {
|
||||
Handle<JSReceiver> CurrentHolder(Handle<Object> value,
|
||||
Handle<Object> inital_holder);
|
||||
|
||||
Result StackPush(Handle<Object> object);
|
||||
Result StackPush(Handle<Object> object, Handle<Object> key);
|
||||
void StackPop();
|
||||
|
||||
Factory* factory() { return isolate_->factory(); }
|
||||
@ -113,12 +114,14 @@ class JsonStringifier {
|
||||
Isolate* isolate_;
|
||||
IncrementalStringBuilder builder_;
|
||||
Handle<String> tojson_string_;
|
||||
Handle<JSArray> stack_;
|
||||
Handle<FixedArray> property_list_;
|
||||
Handle<JSReceiver> replacer_function_;
|
||||
uc16* gap_;
|
||||
int indent_;
|
||||
|
||||
using KeyObject = std::pair<Handle<Object>, Handle<Object>>;
|
||||
std::vector<KeyObject> stack_;
|
||||
|
||||
static const int kJsonEscapeTableEntrySize = 8;
|
||||
static const char* const JsonEscapeTable;
|
||||
};
|
||||
@ -198,9 +201,12 @@ const char* const JsonStringifier::JsonEscapeTable =
|
||||
"\xFC\0 \xFD\0 \xFE\0 \xFF\0 ";
|
||||
|
||||
JsonStringifier::JsonStringifier(Isolate* isolate)
|
||||
: isolate_(isolate), builder_(isolate), gap_(nullptr), indent_(0) {
|
||||
: isolate_(isolate),
|
||||
builder_(isolate),
|
||||
gap_(nullptr),
|
||||
indent_(0),
|
||||
stack_() {
|
||||
tojson_string_ = factory()->toJSON_string();
|
||||
stack_ = factory()->NewJSArray(8);
|
||||
}
|
||||
|
||||
MaybeHandle<Object> JsonStringifier::Stringify(Handle<Object> object,
|
||||
@ -345,33 +351,30 @@ MaybeHandle<Object> JsonStringifier::ApplyReplacerFunction(
|
||||
|
||||
Handle<JSReceiver> JsonStringifier::CurrentHolder(
|
||||
Handle<Object> value, Handle<Object> initial_holder) {
|
||||
int length = Smi::ToInt(stack_->length());
|
||||
if (length == 0) {
|
||||
if (stack_.empty()) {
|
||||
Handle<JSObject> holder =
|
||||
factory()->NewJSObject(isolate_->object_function());
|
||||
JSObject::AddProperty(isolate_, holder, factory()->empty_string(),
|
||||
initial_holder, NONE);
|
||||
return holder;
|
||||
} else {
|
||||
FixedArray elements = FixedArray::cast(stack_->elements());
|
||||
return Handle<JSReceiver>(JSReceiver::cast(elements->get(length - 1)),
|
||||
return Handle<JSReceiver>(JSReceiver::cast(*stack_.back().second),
|
||||
isolate_);
|
||||
}
|
||||
}
|
||||
|
||||
JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object) {
|
||||
JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object,
|
||||
Handle<Object> key) {
|
||||
StackLimitCheck check(isolate_);
|
||||
if (check.HasOverflowed()) {
|
||||
isolate_->StackOverflow();
|
||||
return EXCEPTION;
|
||||
}
|
||||
|
||||
int length = Smi::ToInt(stack_->length());
|
||||
{
|
||||
DisallowHeapAllocation no_allocation;
|
||||
FixedArray elements = FixedArray::cast(stack_->elements());
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (elements->get(i) == *object) {
|
||||
for (const KeyObject& key_object : stack_) {
|
||||
if (*key_object.second == *object) {
|
||||
AllowHeapAllocation allow_to_return_error;
|
||||
Handle<Object> error =
|
||||
factory()->NewTypeError(MessageTemplate::kCircularStructure);
|
||||
@ -380,15 +383,11 @@ JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object) {
|
||||
}
|
||||
}
|
||||
}
|
||||
JSArray::SetLength(stack_, length + 1);
|
||||
FixedArray::cast(stack_->elements())->set(length, *object);
|
||||
stack_.emplace_back(key, object);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
void JsonStringifier::StackPop() {
|
||||
int length = Smi::ToInt(stack_->length());
|
||||
stack_->set_length(Smi::FromInt(length - 1));
|
||||
}
|
||||
void JsonStringifier::StackPop() { stack_.pop_back(); }
|
||||
|
||||
template <bool deferred_string_key>
|
||||
JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
|
||||
@ -443,10 +442,10 @@ JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
|
||||
}
|
||||
case JS_ARRAY_TYPE:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
return SerializeJSArray(Handle<JSArray>::cast(object));
|
||||
return SerializeJSArray(Handle<JSArray>::cast(object), key);
|
||||
case JS_VALUE_TYPE:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
return SerializeJSValue(Handle<JSValue>::cast(object));
|
||||
return SerializeJSValue(Handle<JSValue>::cast(object), key);
|
||||
case SYMBOL_TYPE:
|
||||
return UNCHANGED;
|
||||
default:
|
||||
@ -460,9 +459,9 @@ JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
|
||||
// Go to slow path for global proxy and objects requiring access checks.
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
if (object->IsJSProxy()) {
|
||||
return SerializeJSProxy(Handle<JSProxy>::cast(object));
|
||||
return SerializeJSProxy(Handle<JSProxy>::cast(object), key);
|
||||
}
|
||||
return SerializeJSObject(Handle<JSObject>::cast(object));
|
||||
return SerializeJSObject(Handle<JSObject>::cast(object), key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -470,7 +469,7 @@ JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
|
||||
}
|
||||
|
||||
JsonStringifier::Result JsonStringifier::SerializeJSValue(
|
||||
Handle<JSValue> object) {
|
||||
Handle<JSValue> object, Handle<Object> key) {
|
||||
Object raw = object->value();
|
||||
if (raw->IsString()) {
|
||||
Handle<Object> value;
|
||||
@ -491,7 +490,7 @@ JsonStringifier::Result JsonStringifier::SerializeJSValue(
|
||||
builder_.AppendCString(raw->IsTrue(isolate_) ? "true" : "false");
|
||||
} else {
|
||||
// ES6 24.3.2.1 step 10.c, serialize as an ordinary JSObject.
|
||||
return SerializeJSObject(object);
|
||||
return SerializeJSObject(object, key);
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
@ -517,9 +516,9 @@ JsonStringifier::Result JsonStringifier::SerializeDouble(double number) {
|
||||
}
|
||||
|
||||
JsonStringifier::Result JsonStringifier::SerializeJSArray(
|
||||
Handle<JSArray> object) {
|
||||
Handle<JSArray> object, Handle<Object> key) {
|
||||
HandleScope handle_scope(isolate_);
|
||||
Result stack_push = StackPush(object);
|
||||
Result stack_push = StackPush(object, key);
|
||||
if (stack_push != SUCCESS) return stack_push;
|
||||
uint32_t length = 0;
|
||||
CHECK(object->length()->ToArrayLength(&length));
|
||||
@ -632,9 +631,9 @@ JsonStringifier::Result JsonStringifier::SerializeArrayLikeSlow(
|
||||
}
|
||||
|
||||
JsonStringifier::Result JsonStringifier::SerializeJSObject(
|
||||
Handle<JSObject> object) {
|
||||
Handle<JSObject> object, Handle<Object> key) {
|
||||
HandleScope handle_scope(isolate_);
|
||||
Result stack_push = StackPush(object);
|
||||
Result stack_push = StackPush(object, key);
|
||||
if (stack_push != SUCCESS) return stack_push;
|
||||
|
||||
if (property_list_.is_null() &&
|
||||
@ -711,9 +710,9 @@ JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow(
|
||||
}
|
||||
|
||||
JsonStringifier::Result JsonStringifier::SerializeJSProxy(
|
||||
Handle<JSProxy> object) {
|
||||
Handle<JSProxy> object, Handle<Object> key) {
|
||||
HandleScope scope(isolate_);
|
||||
Result stack_push = StackPush(object);
|
||||
Result stack_push = StackPush(object, key);
|
||||
if (stack_push != SUCCESS) return stack_push;
|
||||
Maybe<bool> is_array = Object::IsArray(object);
|
||||
if (is_array.IsNothing()) return EXCEPTION;
|
||||
|
Loading…
Reference in New Issue
Block a user