diff --git a/src/api.cc b/src/api.cc index 9ffb4dad20..d6336f2846 100644 --- a/src/api.cc +++ b/src/api.cc @@ -2742,9 +2742,10 @@ MaybeLocal JSON::Parse(Isolate* v8_isolate, Local json_string) { PREPARE_FOR_EXECUTION_WITH_ISOLATE(isolate, JSON, Parse, Value); i::Handle string = Utils::OpenHandle(*json_string); i::Handle source = i::String::Flatten(string); + i::Handle undefined = isolate->factory()->undefined_value(); auto maybe = source->IsSeqOneByteString() - ? i::JsonParser::Parse(source) - : i::JsonParser::Parse(source); + ? i::JsonParser::Parse(isolate, source, undefined) + : i::JsonParser::Parse(isolate, source, undefined); Local result; has_pending_exception = !ToLocal(maybe, &result); RETURN_ON_FAILED_EXECUTION(Value); @@ -2756,9 +2757,10 @@ MaybeLocal JSON::Parse(Local context, PREPARE_FOR_EXECUTION(context, JSON, Parse, Value); i::Handle string = Utils::OpenHandle(*json_string); i::Handle source = i::String::Flatten(string); + i::Handle undefined = isolate->factory()->undefined_value(); auto maybe = source->IsSeqOneByteString() - ? i::JsonParser::Parse(source) - : i::JsonParser::Parse(source); + ? i::JsonParser::Parse(isolate, source, undefined) + : i::JsonParser::Parse(isolate, source, undefined); Local result; has_pending_exception = !ToLocal(maybe, &result); RETURN_ON_FAILED_EXECUTION(Value); diff --git a/src/js/json.js b/src/js/json.js index a7b6920008..16f55c5cd8 100644 --- a/src/js/json.js +++ b/src/js/json.js @@ -19,50 +19,8 @@ var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); // ------------------------------------------------------------------- -function CreateDataProperty(o, p, v) { - var desc = {value: v, enumerable: true, writable: true, configurable: true}; - return %reflect_define_property(o, p, desc); -} - - -function InternalizeJSONProperty(holder, name, reviver) { - var val = holder[name]; - if (IS_RECEIVER(val)) { - if (%is_arraylike(val)) { - var length = TO_LENGTH(val.length); - for (var i = 0; i < length; i++) { - var newElement = - InternalizeJSONProperty(val, %_NumberToString(i), reviver); - if (IS_UNDEFINED(newElement)) { - %reflect_delete_property(val, i); - } else { - CreateDataProperty(val, i, newElement); - } - } - } else { - var keys = %object_keys(val); - for (var i = 0; i < keys.length; i++) { - var p = keys[i]; - var newElement = InternalizeJSONProperty(val, p, reviver); - if (IS_UNDEFINED(newElement)) { - %reflect_delete_property(val, p); - } else { - CreateDataProperty(val, p, newElement); - } - } - } - } - return %_Call(reviver, holder, name, val); -} - - function JSONParse(text, reviver) { - var unfiltered = %ParseJson(text); - if (IS_CALLABLE(reviver)) { - return InternalizeJSONProperty({'': unfiltered}, '', reviver); - } else { - return unfiltered; - } + return %ParseJson(text, reviver); } // ------------------------------------------------------------------- diff --git a/src/json-parser.cc b/src/json-parser.cc index fc8b17943b..e1a342150c 100644 --- a/src/json-parser.cc +++ b/src/json-parser.cc @@ -13,16 +13,96 @@ #include "src/objects-inl.h" #include "src/parsing/scanner.h" #include "src/parsing/token.h" +#include "src/property-descriptor.h" #include "src/transitions.h" namespace v8 { namespace internal { +MaybeHandle JsonParseInternalizer::Internalize(Isolate* isolate, + Handle object, + Handle reviver) { + DCHECK(reviver->IsCallable()); + JsonParseInternalizer internalizer(isolate, + Handle::cast(reviver)); + Handle holder = + isolate->factory()->NewJSObject(isolate->object_function()); + Handle name = isolate->factory()->empty_string(); + JSObject::AddProperty(holder, name, object, NONE); + return internalizer.InternalizeJsonProperty(holder, name); +} + +MaybeHandle JsonParseInternalizer::InternalizeJsonProperty( + Handle holder, Handle name) { + HandleScope outer_scope(isolate_); + Handle value; + ASSIGN_RETURN_ON_EXCEPTION( + isolate_, value, Object::GetPropertyOrElement(holder, name), Object); + if (value->IsJSReceiver()) { + Handle object = Handle::cast(value); + Maybe is_array = Object::IsArray(object); + if (is_array.IsNothing()) return MaybeHandle(); + if (is_array.FromJust()) { + Handle length_object; + ASSIGN_RETURN_ON_EXCEPTION( + isolate_, length_object, + Object::GetLengthFromArrayLike(isolate_, object), Object); + double length = length_object->Number(); + for (double i = 0; i < length; i++) { + HandleScope inner_scope(isolate_); + Handle index = isolate_->factory()->NewNumber(i); + Handle name = isolate_->factory()->NumberToString(index); + if (!RecurseAndApply(object, name)) return MaybeHandle(); + } + } else { + Handle contents; + ASSIGN_RETURN_ON_EXCEPTION( + isolate_, contents, + KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, + ENUMERABLE_STRINGS, + GetKeysConversion::kConvertToString), + Object); + for (int i = 0; i < contents->length(); i++) { + HandleScope inner_scope(isolate_); + Handle name(String::cast(contents->get(i)), isolate_); + if (!RecurseAndApply(object, name)) return MaybeHandle(); + } + } + } + Handle argv[] = {name, value}; + Handle result; + ASSIGN_RETURN_ON_EXCEPTION( + isolate_, result, Execution::Call(isolate_, reviver_, holder, 2, argv), + Object); + return outer_scope.CloseAndEscape(result); +} + +bool JsonParseInternalizer::RecurseAndApply(Handle holder, + Handle name) { + Handle result; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate_, result, InternalizeJsonProperty(holder, name), false); + Maybe change_result = Nothing(); + if (result->IsUndefined()) { + change_result = JSReceiver::DeletePropertyOrElement(holder, name, SLOPPY); + } else { + PropertyDescriptor desc; + desc.set_value(result); + desc.set_configurable(true); + desc.set_enumerable(true); + desc.set_writable(true); + change_result = JSReceiver::DefineOwnProperty(isolate_, holder, name, &desc, + Object::DONT_THROW); + } + MAYBE_RETURN(change_result, false); + return true; +} + template -JsonParser::JsonParser(Handle source) +JsonParser::JsonParser(Isolate* isolate, Handle source) : source_(source), source_length_(source->length()), - isolate_(source->map()->GetHeap()->isolate()), + isolate_(isolate), factory_(isolate_->factory()), zone_(isolate_->allocator()), object_constructor_(isolate_->native_context()->object_function(), @@ -90,6 +170,9 @@ MaybeHandle JsonParser::ParseJson() { return result; } +MaybeHandle InternalizeJsonProperty(Handle holder, + Handle key); + template void JsonParser::Advance() { position_++; diff --git a/src/json-parser.h b/src/json-parser.h index abbb99c93a..2d08fefda9 100644 --- a/src/json-parser.h +++ b/src/json-parser.h @@ -13,19 +13,45 @@ namespace internal { enum ParseElementResult { kElementFound, kElementNotFound, kNullHandle }; +class JsonParseInternalizer BASE_EMBEDDED { + public: + static MaybeHandle Internalize(Isolate* isolate, + Handle object, + Handle reviver); + + private: + JsonParseInternalizer(Isolate* isolate, Handle reviver) + : isolate_(isolate), reviver_(reviver) {} + + MaybeHandle InternalizeJsonProperty(Handle holder, + Handle key); + + bool RecurseAndApply(Handle holder, Handle name); + + Isolate* isolate_; + Handle reviver_; +}; // A simple json parser. template class JsonParser BASE_EMBEDDED { public: - MUST_USE_RESULT static MaybeHandle Parse(Handle source) { - return JsonParser(source).ParseJson(); + MUST_USE_RESULT static MaybeHandle Parse(Isolate* isolate, + Handle source, + Handle reviver) { + Handle result; + ASSIGN_RETURN_ON_EXCEPTION(isolate, result, + JsonParser(isolate, source).ParseJson(), Object); + if (reviver->IsCallable()) { + return JsonParseInternalizer::Internalize(isolate, result, reviver); + } + return result; } static const int kEndOfString = -1; private: - explicit JsonParser(Handle source); + JsonParser(Isolate* isolate, Handle source); // Parse a string containing a single JSON value. MaybeHandle ParseJson(); diff --git a/src/json-stringifier.cc b/src/json-stringifier.cc index 192c01202f..6293b0c459 100644 --- a/src/json-stringifier.cc +++ b/src/json-stringifier.cc @@ -534,33 +534,20 @@ JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow( ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate_, contents, KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, - ENUMERABLE_STRINGS), + ENUMERABLE_STRINGS, + GetKeysConversion::kConvertToString), EXCEPTION); } builder_.AppendCharacter('{'); Indent(); bool comma = false; for (int i = 0; i < contents->length(); i++) { - Object* key = contents->get(i); - Handle key_handle; - MaybeHandle maybe_property; - if (key->IsString()) { - key_handle = Handle(String::cast(key), isolate_); - maybe_property = Object::GetPropertyOrElement(object, key_handle); - } else { - DCHECK(key->IsNumber()); - key_handle = factory()->NumberToString(Handle(key, isolate_)); - if (key->IsSmi()) { - maybe_property = - JSReceiver::GetElement(isolate_, object, Smi::cast(key)->value()); - } else { - maybe_property = Object::GetPropertyOrElement(object, key_handle); - } - } + Handle key(String::cast(contents->get(i)), isolate_); Handle property; - ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, property, maybe_property, + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, property, + Object::GetPropertyOrElement(object, key), EXCEPTION); - Result result = SerializeProperty(property, comma, key_handle); + Result result = SerializeProperty(property, comma, key); if (!comma && result == SUCCESS) comma = true; if (result == EXCEPTION) return result; } @@ -572,6 +559,7 @@ JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow( JsonStringifier::Result JsonStringifier::SerializeJSProxy( Handle object) { + HandleScope scope(isolate_); Result stack_push = StackPush(object); if (stack_push != SUCCESS) return stack_push; Maybe is_array = Object::IsArray(object); diff --git a/src/runtime/runtime-json.cc b/src/runtime/runtime-json.cc index 2280d85ce2..cba8e11f78 100644 --- a/src/runtime/runtime-json.cc +++ b/src/runtime/runtime-json.cc @@ -16,16 +16,18 @@ namespace internal { RUNTIME_FUNCTION(Runtime_ParseJson) { HandleScope scope(isolate); - DCHECK_EQ(1, args.length()); + DCHECK_EQ(2, args.length()); CONVERT_ARG_HANDLE_CHECKED(Object, object, 0); + CONVERT_ARG_HANDLE_CHECKED(Object, reviver, 1); Handle source; ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, source, Object::ToString(isolate, object)); source = String::Flatten(source); // Optimized fast case where we only have Latin1 characters. - RETURN_RESULT_OR_FAILURE(isolate, source->IsSeqOneByteString() - ? JsonParser::Parse(source) - : JsonParser::Parse(source)); + RETURN_RESULT_OR_FAILURE( + isolate, source->IsSeqOneByteString() + ? JsonParser::Parse(isolate, source, reviver) + : JsonParser::Parse(isolate, source, reviver)); } } // namespace internal diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index b18fbe0129..3b861168f7 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -327,8 +327,7 @@ namespace internal { F(OrdinaryHasInstance, 2, 1) \ F(IsWasmObject, 1, 1) -#define FOR_EACH_INTRINSIC_JSON(F) \ - F(ParseJson, 1, 1) +#define FOR_EACH_INTRINSIC_JSON(F) F(ParseJson, 2, 1) #define FOR_EACH_INTRINSIC_LITERALS(F) \ F(CreateRegExpLiteral, 4, 1) \