// 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/base/strings.h" #include "src/common/globals.h" #include "src/common/message-template.h" #include "src/debug/debug.h" #include "src/execution/frames-inl.h" #include "src/numbers/conversions.h" #include "src/numbers/hash-seed-inl.h" #include "src/objects/field-type.h" #include "src/objects/hash-table-inl.h" #include "src/objects/map-updater.h" #include "src/objects/objects-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 = base::BitField8; using MayTerminateStringField = EscapeKindField::Next; using NumberPartField = MayTerminateStringField::Next; 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 index_name = isolate_->factory()->NumberToString(index); if (!RecurseAndApply(object, index_name)) return MaybeHandle(); } } else { Handle contents; ASSIGN_RETURN_ON_EXCEPTION( isolate_, contents, KeyAccumulator::GetKeys(isolate_, object, KeyCollectionMode::kOwnOnly, ENUMERABLE_STRINGS, GetKeysConversion::kConvertToString), Object); for (int i = 0; i < contents->length(); i++) { HandleScope inner_scope(isolate_); Handle key_name(String::cast(contents->get(i)), isolate_); if (!RecurseAndApply(object, key_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(); PtrComprCageBase cage_base(isolate); if (source->IsSlicedString(cage_base)) { SlicedString string = SlicedString::cast(*source); start = string.offset(); String parent = string.parent(cage_base); if (parent.IsThinString(cage_base)) parent = ThinString::cast(parent).actual(cage_base); source_ = handle(parent, isolate); } else { source_ = String::Flatten(isolate, source); } if (StringShape(*source_, cage_base).IsExternal()) { chars_ = static_cast( SeqExternalString::cast(*source_).GetChars(cage_base)); chars_may_relocate_ = false; } else { DisallowGarbageCollection no_gc; isolate->main_thread_local_heap()->AddGCEpilogueCallback( UpdatePointersCallback, this); chars_ = SeqString::cast(*source_).GetChars(no_gc); chars_may_relocate_ = true; } cursor_ = chars_ + start; end_ = cursor_ + length; } template bool JsonParser::IsSpecialString() { // The special cases are undefined, NaN, Infinity, and {} being passed to the // parse method int offset = original_source_->IsSlicedString() ? SlicedString::cast(*original_source_).offset() : 0; size_t length = original_source_->length(); #define CASES(V) \ V("[object Object]") \ V("undefined") \ V("Infinity") \ V("NaN") switch (length) { #define CASE(n) \ case arraysize(n) - 1: \ return CompareCharsEqual(chars_ + offset, n, arraysize(n) - 1); CASES(CASE) default: return false; } #undef CASE #undef CASES } template MessageTemplate JsonParser::GetErrorMessageWithEllipses( Handle& arg, Handle& arg2, int pos) { MessageTemplate message; Factory* factory = this->factory(); arg = factory->LookupSingleCharacterStringFromCode(*cursor_); int origin_source_length = original_source_->length(); // only provide context for strings with at least // kMinOriginalSourceLengthForContext charcacters in length if (origin_source_length >= kMinOriginalSourceLengthForContext) { int substring_start = 0; int substring_end = origin_source_length; if (pos < kMaxContextCharacters) { message = MessageTemplate::kJsonParseUnexpectedTokenStartStringWithContext; // Output the string followed by elipses substring_end = pos + kMaxContextCharacters; } else if (pos >= kMaxContextCharacters && pos < origin_source_length - kMaxContextCharacters) { message = MessageTemplate::kJsonParseUnexpectedTokenSurroundStringWithContext; // Add context before and after position of bad token surrounded by // elipses substring_start = pos - kMaxContextCharacters; substring_end = pos + kMaxContextCharacters; } else { message = MessageTemplate::kJsonParseUnexpectedTokenEndStringWithContext; // Add ellipses followed by some context before bad token substring_start = pos - kMaxContextCharacters; } arg2 = factory->NewSubString(original_source_, substring_start, substring_end); } else { arg2 = original_source_; // Output the entire string without ellipses but provide the token which // was unexpected message = MessageTemplate::kJsonParseUnexpectedTokenShortString; } return message; } template MessageTemplate JsonParser::LookUpErrorMessageForJsonToken( JsonToken token, Handle& arg, Handle& arg2, int pos) { MessageTemplate message; switch (token) { case JsonToken::EOS: message = MessageTemplate::kJsonParseUnexpectedEOS; break; case JsonToken::NUMBER: message = MessageTemplate::kJsonParseUnexpectedTokenNumber; break; case JsonToken::STRING: message = MessageTemplate::kJsonParseUnexpectedTokenString; break; default: // Output entire string without ellipses and don't provide the token // that was unexpected because it makes the error messages more confusing if (IsSpecialString()) { arg = original_source_; message = MessageTemplate::kJsonParseShortString; } else { message = GetErrorMessageWithEllipses(arg, arg2, pos); } } return message; } template void JsonParser::ReportUnexpectedToken( JsonToken token, base::Optional errorMessage) { // 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(); int offset = original_source_->IsSlicedString() ? SlicedString::cast(*original_source_).offset() : 0; int pos = position() - offset; Handle arg(Smi::FromInt(pos), isolate()); Handle arg2; MessageTemplate message = errorMessage ? errorMessage.value() : LookUpErrorMessageForJsonToken(token, arg, arg2, pos); Handle