// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/json/json-parser.h" #include "src/debug/debug.h" #include "src/hash-seed-inl.h" #include "src/message-template.h" #include "src/numbers/conversions.h" #include "src/objects-inl.h" #include "src/objects/field-type.h" #include "src/objects/hash-table-inl.h" #include "src/objects/property-descriptor.h" #include "src/strings/char-predicates-inl.h" #include "src/strings/string-hasher.h" namespace v8 { namespace internal { namespace { constexpr JsonToken GetOneCharJsonToken(uint8_t c) { // clang-format off return c == '"' ? JsonToken::STRING : IsDecimalDigit(c) ? JsonToken::NUMBER : c == '-' ? JsonToken::NUMBER : c == '[' ? JsonToken::LBRACK : c == '{' ? JsonToken::LBRACE : c == ']' ? JsonToken::RBRACK : c == '}' ? JsonToken::RBRACE : c == 't' ? JsonToken::TRUE_LITERAL : c == 'f' ? JsonToken::FALSE_LITERAL : c == 'n' ? JsonToken::NULL_LITERAL : c == ' ' ? JsonToken::WHITESPACE : c == '\t' ? JsonToken::WHITESPACE : c == '\r' ? JsonToken::WHITESPACE : c == '\n' ? JsonToken::WHITESPACE : c == ':' ? JsonToken::COLON : c == ',' ? JsonToken::COMMA : JsonToken::ILLEGAL; // clang-format on } // Table of one-character tokens, by character (0x00..0xFF only). static const constexpr JsonToken one_char_json_tokens[256] = { #define CALL_GET_SCAN_FLAGS(N) GetOneCharJsonToken(N), INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS) #undef CALL_GET_SCAN_FLAGS #define CALL_GET_SCAN_FLAGS(N) GetOneCharJsonToken(128 + N), INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS) #undef CALL_GET_SCAN_FLAGS }; enum class EscapeKind : uint8_t { kIllegal, kSelf, kBackspace, kTab, kNewLine, kFormFeed, kCarriageReturn, kUnicode }; using EscapeKindField = BitField8; using MayTerminateStringField = BitField8; using NumberPartField = BitField8; constexpr bool MayTerminateJsonString(uint8_t flags) { return MayTerminateStringField::decode(flags); } constexpr EscapeKind GetEscapeKind(uint8_t flags) { return EscapeKindField::decode(flags); } constexpr bool IsNumberPart(uint8_t flags) { return NumberPartField::decode(flags); } constexpr uint8_t GetJsonScanFlags(uint8_t c) { // clang-format off return (c == 'b' ? EscapeKindField::encode(EscapeKind::kBackspace) : c == 't' ? EscapeKindField::encode(EscapeKind::kTab) : c == 'n' ? EscapeKindField::encode(EscapeKind::kNewLine) : c == 'f' ? EscapeKindField::encode(EscapeKind::kFormFeed) : c == 'r' ? EscapeKindField::encode(EscapeKind::kCarriageReturn) : c == 'u' ? EscapeKindField::encode(EscapeKind::kUnicode) : c == '"' ? EscapeKindField::encode(EscapeKind::kSelf) : c == '\\' ? EscapeKindField::encode(EscapeKind::kSelf) : c == '/' ? EscapeKindField::encode(EscapeKind::kSelf) : EscapeKindField::encode(EscapeKind::kIllegal)) | (c < 0x20 ? MayTerminateStringField::encode(true) : c == '"' ? MayTerminateStringField::encode(true) : c == '\\' ? MayTerminateStringField::encode(true) : MayTerminateStringField::encode(false)) | NumberPartField::encode(c == '.' || c == 'e' || c == 'E' || IsDecimalDigit(c) || c == '-' || c == '+'); // clang-format on } // Table of one-character scan flags, by character (0x00..0xFF only). static const constexpr uint8_t character_json_scan_flags[256] = { #define CALL_GET_SCAN_FLAGS(N) GetJsonScanFlags(N), INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS) #undef CALL_GET_SCAN_FLAGS #define CALL_GET_SCAN_FLAGS(N) GetJsonScanFlags(128 + N), INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS) #undef CALL_GET_SCAN_FLAGS }; } // namespace 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(isolate, 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(isolate_, 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) { STACK_CHECK(isolate_, false); Handle result; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate_, result, InternalizeJsonProperty(holder, name), false); Maybe change_result = Nothing(); if (result->IsUndefined(isolate_)) { change_result = JSReceiver::DeletePropertyOrElement(holder, name, LanguageMode::kSloppy); } 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, Just(kDontThrow)); } MAYBE_RETURN(change_result, false); return true; } template JsonParser::JsonParser(Isolate* isolate, Handle source) : isolate_(isolate), hash_seed_(HashSeed(isolate)), object_constructor_(isolate_->object_function()), original_source_(source) { size_t start = 0; size_t length = source->length(); if (source->IsSlicedString()) { SlicedString string = SlicedString::cast(*source); start = string.offset(); String parent = string.parent(); if (parent.IsThinString()) parent = ThinString::cast(parent).actual(); source_ = handle(parent, isolate); } else { source_ = String::Flatten(isolate, source); } if (StringShape(*source_).IsExternal()) { chars_ = static_cast(SeqExternalString::cast(*source_)->GetChars()); chars_may_relocate_ = false; } else { DisallowHeapAllocation no_gc; isolate->heap()->AddGCEpilogueCallback(UpdatePointersCallback, v8::kGCTypeAll, this); chars_ = SeqString::cast(*source_)->GetChars(no_gc); chars_may_relocate_ = true; } cursor_ = chars_ + start; end_ = cursor_ + length; } template void JsonParser::ReportUnexpectedToken(JsonToken token) { // Some exception (for example stack overflow) is already pending. if (isolate_->has_pending_exception()) return; // Parse failed. Current character is the unexpected token. Factory* factory = this->factory(); MessageTemplate message; int offset = original_source_->IsSlicedString() ? SlicedString::cast(*original_source_).offset() : 0; int pos = position() - offset; Handle arg1 = Handle(Smi::FromInt(pos), isolate()); Handle arg2; switch (token) { case JsonToken::EOS: message = MessageTemplate::kJsonParseUnexpectedEOS; break; case JsonToken::NUMBER: message = MessageTemplate::kJsonParseUnexpectedTokenNumber; break; case JsonToken::STRING: message = MessageTemplate::kJsonParseUnexpectedTokenString; break; default: message = MessageTemplate::kJsonParseUnexpectedToken; arg2 = arg1; arg1 = factory->LookupSingleCharacterStringFromCode(*cursor_); break; } Handle