Commit d6779b25 authored by Simon Zünd's avatar Simon Zünd Committed by Commit Bot

[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/1424863Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59019}
parent 9042f2e3
...@@ -74,12 +74,13 @@ class JsonStringifier { ...@@ -74,12 +74,13 @@ class JsonStringifier {
return SerializeDouble(object->value()); 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 SerializeJSArray(Handle<JSArray> object, Handle<Object> key);
V8_INLINE Result SerializeJSObject(Handle<JSObject> object); 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 SerializeJSReceiverSlow(Handle<JSReceiver> object);
Result SerializeArrayLikeSlow(Handle<JSReceiver> object, uint32_t start, Result SerializeArrayLikeSlow(Handle<JSReceiver> object, uint32_t start,
uint32_t length); uint32_t length);
...@@ -105,7 +106,7 @@ class JsonStringifier { ...@@ -105,7 +106,7 @@ class JsonStringifier {
Handle<JSReceiver> CurrentHolder(Handle<Object> value, Handle<JSReceiver> CurrentHolder(Handle<Object> value,
Handle<Object> inital_holder); Handle<Object> inital_holder);
Result StackPush(Handle<Object> object); Result StackPush(Handle<Object> object, Handle<Object> key);
void StackPop(); void StackPop();
Factory* factory() { return isolate_->factory(); } Factory* factory() { return isolate_->factory(); }
...@@ -113,12 +114,14 @@ class JsonStringifier { ...@@ -113,12 +114,14 @@ class JsonStringifier {
Isolate* isolate_; Isolate* isolate_;
IncrementalStringBuilder builder_; IncrementalStringBuilder builder_;
Handle<String> tojson_string_; Handle<String> tojson_string_;
Handle<JSArray> stack_;
Handle<FixedArray> property_list_; Handle<FixedArray> property_list_;
Handle<JSReceiver> replacer_function_; Handle<JSReceiver> replacer_function_;
uc16* gap_; uc16* gap_;
int indent_; int indent_;
using KeyObject = std::pair<Handle<Object>, Handle<Object>>;
std::vector<KeyObject> stack_;
static const int kJsonEscapeTableEntrySize = 8; static const int kJsonEscapeTableEntrySize = 8;
static const char* const JsonEscapeTable; static const char* const JsonEscapeTable;
}; };
...@@ -198,9 +201,12 @@ const char* const JsonStringifier::JsonEscapeTable = ...@@ -198,9 +201,12 @@ const char* const JsonStringifier::JsonEscapeTable =
"\xFC\0 \xFD\0 \xFE\0 \xFF\0 "; "\xFC\0 \xFD\0 \xFE\0 \xFF\0 ";
JsonStringifier::JsonStringifier(Isolate* isolate) 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(); tojson_string_ = factory()->toJSON_string();
stack_ = factory()->NewJSArray(8);
} }
MaybeHandle<Object> JsonStringifier::Stringify(Handle<Object> object, MaybeHandle<Object> JsonStringifier::Stringify(Handle<Object> object,
...@@ -345,33 +351,30 @@ MaybeHandle<Object> JsonStringifier::ApplyReplacerFunction( ...@@ -345,33 +351,30 @@ MaybeHandle<Object> JsonStringifier::ApplyReplacerFunction(
Handle<JSReceiver> JsonStringifier::CurrentHolder( Handle<JSReceiver> JsonStringifier::CurrentHolder(
Handle<Object> value, Handle<Object> initial_holder) { Handle<Object> value, Handle<Object> initial_holder) {
int length = Smi::ToInt(stack_->length()); if (stack_.empty()) {
if (length == 0) {
Handle<JSObject> holder = Handle<JSObject> holder =
factory()->NewJSObject(isolate_->object_function()); factory()->NewJSObject(isolate_->object_function());
JSObject::AddProperty(isolate_, holder, factory()->empty_string(), JSObject::AddProperty(isolate_, holder, factory()->empty_string(),
initial_holder, NONE); initial_holder, NONE);
return holder; return holder;
} else { } else {
FixedArray elements = FixedArray::cast(stack_->elements()); return Handle<JSReceiver>(JSReceiver::cast(*stack_.back().second),
return Handle<JSReceiver>(JSReceiver::cast(elements->get(length - 1)),
isolate_); isolate_);
} }
} }
JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object) { JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object,
Handle<Object> key) {
StackLimitCheck check(isolate_); StackLimitCheck check(isolate_);
if (check.HasOverflowed()) { if (check.HasOverflowed()) {
isolate_->StackOverflow(); isolate_->StackOverflow();
return EXCEPTION; return EXCEPTION;
} }
int length = Smi::ToInt(stack_->length());
{ {
DisallowHeapAllocation no_allocation; DisallowHeapAllocation no_allocation;
FixedArray elements = FixedArray::cast(stack_->elements()); for (const KeyObject& key_object : stack_) {
for (int i = 0; i < length; i++) { if (*key_object.second == *object) {
if (elements->get(i) == *object) {
AllowHeapAllocation allow_to_return_error; AllowHeapAllocation allow_to_return_error;
Handle<Object> error = Handle<Object> error =
factory()->NewTypeError(MessageTemplate::kCircularStructure); factory()->NewTypeError(MessageTemplate::kCircularStructure);
...@@ -380,15 +383,11 @@ JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object) { ...@@ -380,15 +383,11 @@ JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object) {
} }
} }
} }
JSArray::SetLength(stack_, length + 1); stack_.emplace_back(key, object);
FixedArray::cast(stack_->elements())->set(length, *object);
return SUCCESS; return SUCCESS;
} }
void JsonStringifier::StackPop() { void JsonStringifier::StackPop() { stack_.pop_back(); }
int length = Smi::ToInt(stack_->length());
stack_->set_length(Smi::FromInt(length - 1));
}
template <bool deferred_string_key> template <bool deferred_string_key>
JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object, JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
...@@ -443,10 +442,10 @@ JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object, ...@@ -443,10 +442,10 @@ JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
} }
case JS_ARRAY_TYPE: case JS_ARRAY_TYPE:
if (deferred_string_key) SerializeDeferredKey(comma, key); if (deferred_string_key) SerializeDeferredKey(comma, key);
return SerializeJSArray(Handle<JSArray>::cast(object)); return SerializeJSArray(Handle<JSArray>::cast(object), key);
case JS_VALUE_TYPE: case JS_VALUE_TYPE:
if (deferred_string_key) SerializeDeferredKey(comma, key); if (deferred_string_key) SerializeDeferredKey(comma, key);
return SerializeJSValue(Handle<JSValue>::cast(object)); return SerializeJSValue(Handle<JSValue>::cast(object), key);
case SYMBOL_TYPE: case SYMBOL_TYPE:
return UNCHANGED; return UNCHANGED;
default: default:
...@@ -460,9 +459,9 @@ JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object, ...@@ -460,9 +459,9 @@ JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
// Go to slow path for global proxy and objects requiring access checks. // Go to slow path for global proxy and objects requiring access checks.
if (deferred_string_key) SerializeDeferredKey(comma, key); if (deferred_string_key) SerializeDeferredKey(comma, key);
if (object->IsJSProxy()) { 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, ...@@ -470,7 +469,7 @@ JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
} }
JsonStringifier::Result JsonStringifier::SerializeJSValue( JsonStringifier::Result JsonStringifier::SerializeJSValue(
Handle<JSValue> object) { Handle<JSValue> object, Handle<Object> key) {
Object raw = object->value(); Object raw = object->value();
if (raw->IsString()) { if (raw->IsString()) {
Handle<Object> value; Handle<Object> value;
...@@ -491,7 +490,7 @@ JsonStringifier::Result JsonStringifier::SerializeJSValue( ...@@ -491,7 +490,7 @@ JsonStringifier::Result JsonStringifier::SerializeJSValue(
builder_.AppendCString(raw->IsTrue(isolate_) ? "true" : "false"); builder_.AppendCString(raw->IsTrue(isolate_) ? "true" : "false");
} else { } else {
// ES6 24.3.2.1 step 10.c, serialize as an ordinary JSObject. // ES6 24.3.2.1 step 10.c, serialize as an ordinary JSObject.
return SerializeJSObject(object); return SerializeJSObject(object, key);
} }
return SUCCESS; return SUCCESS;
} }
...@@ -517,9 +516,9 @@ JsonStringifier::Result JsonStringifier::SerializeDouble(double number) { ...@@ -517,9 +516,9 @@ JsonStringifier::Result JsonStringifier::SerializeDouble(double number) {
} }
JsonStringifier::Result JsonStringifier::SerializeJSArray( JsonStringifier::Result JsonStringifier::SerializeJSArray(
Handle<JSArray> object) { Handle<JSArray> object, Handle<Object> key) {
HandleScope handle_scope(isolate_); HandleScope handle_scope(isolate_);
Result stack_push = StackPush(object); Result stack_push = StackPush(object, key);
if (stack_push != SUCCESS) return stack_push; if (stack_push != SUCCESS) return stack_push;
uint32_t length = 0; uint32_t length = 0;
CHECK(object->length()->ToArrayLength(&length)); CHECK(object->length()->ToArrayLength(&length));
...@@ -632,9 +631,9 @@ JsonStringifier::Result JsonStringifier::SerializeArrayLikeSlow( ...@@ -632,9 +631,9 @@ JsonStringifier::Result JsonStringifier::SerializeArrayLikeSlow(
} }
JsonStringifier::Result JsonStringifier::SerializeJSObject( JsonStringifier::Result JsonStringifier::SerializeJSObject(
Handle<JSObject> object) { Handle<JSObject> object, Handle<Object> key) {
HandleScope handle_scope(isolate_); HandleScope handle_scope(isolate_);
Result stack_push = StackPush(object); Result stack_push = StackPush(object, key);
if (stack_push != SUCCESS) return stack_push; if (stack_push != SUCCESS) return stack_push;
if (property_list_.is_null() && if (property_list_.is_null() &&
...@@ -711,9 +710,9 @@ JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow( ...@@ -711,9 +710,9 @@ JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow(
} }
JsonStringifier::Result JsonStringifier::SerializeJSProxy( JsonStringifier::Result JsonStringifier::SerializeJSProxy(
Handle<JSProxy> object) { Handle<JSProxy> object, Handle<Object> key) {
HandleScope scope(isolate_); HandleScope scope(isolate_);
Result stack_push = StackPush(object); Result stack_push = StackPush(object, key);
if (stack_push != SUCCESS) return stack_push; if (stack_push != SUCCESS) return stack_push;
Maybe<bool> is_array = Object::IsArray(object); Maybe<bool> is_array = Object::IsArray(object);
if (is_array.IsNothing()) return EXCEPTION; if (is_array.IsNothing()) return EXCEPTION;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment