|  | // 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/common/message-template.h" | 
|  | #include "src/debug/debug.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/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<EscapeKind, 0, 3>; | 
|  | using MayTerminateStringField = EscapeKindField::Next<bool, 1>; | 
|  | using NumberPartField = MayTerminateStringField::Next<bool, 1>; | 
|  |  | 
|  | 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<Object> JsonParseInternalizer::Internalize(Isolate* isolate, | 
|  | Handle<Object> object, | 
|  | Handle<Object> reviver) { | 
|  | DCHECK(reviver->IsCallable()); | 
|  | JsonParseInternalizer internalizer(isolate, | 
|  | Handle<JSReceiver>::cast(reviver)); | 
|  | Handle<JSObject> holder = | 
|  | isolate->factory()->NewJSObject(isolate->object_function()); | 
|  | Handle<String> name = isolate->factory()->empty_string(); | 
|  | JSObject::AddProperty(isolate, holder, name, object, NONE); | 
|  | return internalizer.InternalizeJsonProperty(holder, name); | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> JsonParseInternalizer::InternalizeJsonProperty( | 
|  | Handle<JSReceiver> holder, Handle<String> name) { | 
|  | HandleScope outer_scope(isolate_); | 
|  | Handle<Object> value; | 
|  | ASSIGN_RETURN_ON_EXCEPTION( | 
|  | isolate_, value, Object::GetPropertyOrElement(isolate_, holder, name), | 
|  | Object); | 
|  | if (value->IsJSReceiver()) { | 
|  | Handle<JSReceiver> object = Handle<JSReceiver>::cast(value); | 
|  | Maybe<bool> is_array = Object::IsArray(object); | 
|  | if (is_array.IsNothing()) return MaybeHandle<Object>(); | 
|  | if (is_array.FromJust()) { | 
|  | Handle<Object> 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<Object> index = isolate_->factory()->NewNumber(i); | 
|  | Handle<String> name = isolate_->factory()->NumberToString(index); | 
|  | if (!RecurseAndApply(object, name)) return MaybeHandle<Object>(); | 
|  | } | 
|  | } else { | 
|  | Handle<FixedArray> 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<String> name(String::cast(contents->get(i)), isolate_); | 
|  | if (!RecurseAndApply(object, name)) return MaybeHandle<Object>(); | 
|  | } | 
|  | } | 
|  | } | 
|  | Handle<Object> argv[] = {name, value}; | 
|  | Handle<Object> result; | 
|  | ASSIGN_RETURN_ON_EXCEPTION( | 
|  | isolate_, result, Execution::Call(isolate_, reviver_, holder, 2, argv), | 
|  | Object); | 
|  | return outer_scope.CloseAndEscape(result); | 
|  | } | 
|  |  | 
|  | bool JsonParseInternalizer::RecurseAndApply(Handle<JSReceiver> holder, | 
|  | Handle<String> name) { | 
|  | STACK_CHECK(isolate_, false); | 
|  |  | 
|  | Handle<Object> result; | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate_, result, InternalizeJsonProperty(holder, name), false); | 
|  | Maybe<bool> change_result = Nothing<bool>(); | 
|  | 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 <typename Char> | 
|  | JsonParser<Char>::JsonParser(Isolate* isolate, Handle<String> 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<const Char*>(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 <typename Char> | 
|  | void JsonParser<Char>::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<Object> arg1 = Handle<Smi>(Smi::FromInt(pos), isolate()); | 
|  | Handle<Object> 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<Script> script(factory->NewScript(original_source_)); | 
|  | if (isolate()->NeedsSourcePositionsForProfiling()) { | 
|  | Script::InitLineEnds(isolate(), script); | 
|  | } | 
|  | // We should sent compile error event because we compile JSON object in | 
|  | // separated source file. | 
|  | isolate()->debug()->OnCompileError(script); | 
|  | MessageLocation location(script, pos, pos + 1); | 
|  | isolate()->ThrowAt(factory->NewSyntaxError(message, arg1, arg2), &location); | 
|  |  | 
|  | // Move the cursor to the end so we won't be able to proceed parsing. | 
|  | cursor_ = end_; | 
|  | } | 
|  |  | 
|  | template <typename Char> | 
|  | void JsonParser<Char>::ReportUnexpectedCharacter(uc32 c) { | 
|  | JsonToken token = JsonToken::ILLEGAL; | 
|  | if (c == kEndOfString) { | 
|  | token = JsonToken::EOS; | 
|  | } else if (c <= unibrow::Latin1::kMaxChar) { | 
|  | token = one_char_json_tokens[c]; | 
|  | } | 
|  | return ReportUnexpectedToken(token); | 
|  | } | 
|  |  | 
|  | template <typename Char> | 
|  | JsonParser<Char>::~JsonParser() { | 
|  | if (StringShape(*source_).IsExternal()) { | 
|  | // Check that the string shape hasn't changed. Otherwise our GC hooks are | 
|  | // broken. | 
|  | SeqExternalString::cast(*source_); | 
|  | } else { | 
|  | // Check that the string shape hasn't changed. Otherwise our GC hooks are | 
|  | // broken. | 
|  | SeqString::cast(*source_); | 
|  | isolate()->heap()->RemoveGCEpilogueCallback(UpdatePointersCallback, this); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename Char> | 
|  | MaybeHandle<Object> JsonParser<Char>::ParseJson() { | 
|  | MaybeHandle<Object> result = ParseJsonValue(); | 
|  | if (!Check(JsonToken::EOS)) ReportUnexpectedToken(peek()); | 
|  | if (isolate_->has_pending_exception()) return MaybeHandle<Object>(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> InternalizeJsonProperty(Handle<JSObject> holder, | 
|  | Handle<String> key); | 
|  |  | 
|  | template <typename Char> | 
|  | void JsonParser<Char>::SkipWhitespace() { | 
|  | next_ = JsonToken::EOS; | 
|  |  | 
|  | cursor_ = std::find_if(cursor_, end_, [this](Char c) { | 
|  | JsonToken current = V8_LIKELY(c <= unibrow::Latin1::kMaxChar) | 
|  | ? one_char_json_tokens[c] | 
|  | : JsonToken::ILLEGAL; | 
|  | bool result = current != JsonToken::WHITESPACE; | 
|  | if (result) next_ = current; | 
|  | return result; | 
|  | }); | 
|  | } | 
|  |  | 
|  | template <typename Char> | 
|  | uc32 JsonParser<Char>::ScanUnicodeCharacter() { | 
|  | uc32 value = 0; | 
|  | for (int i = 0; i < 4; i++) { | 
|  | int digit = HexValue(NextCharacter()); | 
|  | if (V8_UNLIKELY(digit < 0)) return kInvalidUnicodeCharacter; | 
|  | value = value * 16 + digit; | 
|  | } | 
|  | return value; | 
|  | } | 
|  |  | 
|  | // Parse any JSON value. | 
|  | template <typename Char> | 
|  | JsonString JsonParser<Char>::ScanJsonPropertyKey(JsonContinuation* cont) { | 
|  | { | 
|  | DisallowHeapAllocation no_gc; | 
|  | const Char* start = cursor_; | 
|  | uc32 first = CurrentCharacter(); | 
|  | if (first == '\\' && NextCharacter() == 'u') first = ScanUnicodeCharacter(); | 
|  | if (IsDecimalDigit(first)) { | 
|  | if (first == '0') { | 
|  | if (NextCharacter() == '"') { | 
|  | advance(); | 
|  | // Record element information. | 
|  | cont->elements++; | 
|  | DCHECK_LE(0, cont->max_index); | 
|  | return JsonString(0); | 
|  | } | 
|  | } else { | 
|  | uint32_t index = first - '0'; | 
|  | while (true) { | 
|  | cursor_ = std::find_if(cursor_ + 1, end_, [&index](Char c) { | 
|  | return !TryAddArrayIndexChar(&index, c); | 
|  | }); | 
|  |  | 
|  | if (CurrentCharacter() == '"') { | 
|  | advance(); | 
|  | // Record element information. | 
|  | cont->elements++; | 
|  | cont->max_index = Max(cont->max_index, index); | 
|  | return JsonString(index); | 
|  | } | 
|  |  | 
|  | if (CurrentCharacter() == '\\' && NextCharacter() == 'u') { | 
|  | if (TryAddArrayIndexChar(&index, ScanUnicodeCharacter())) continue; | 
|  | } | 
|  |  | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | // Reset cursor_ to start if the key is not an index. | 
|  | cursor_ = start; | 
|  | } | 
|  | return ScanJsonString(true); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | Handle<Map> ParentOfDescriptorOwner(Isolate* isolate, Handle<Map> maybe_root, | 
|  | Handle<Map> source, int descriptor) { | 
|  | if (descriptor == 0) { | 
|  | DCHECK_EQ(0, maybe_root->NumberOfOwnDescriptors()); | 
|  | return maybe_root; | 
|  | } | 
|  | return handle(source->FindFieldOwner(isolate, InternalIndex(descriptor - 1)), | 
|  | isolate); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | template <typename Char> | 
|  | Handle<Object> JsonParser<Char>::BuildJsonObject( | 
|  | const JsonContinuation& cont, | 
|  | const std::vector<JsonProperty>& property_stack, Handle<Map> feedback) { | 
|  | size_t start = cont.index; | 
|  | int length = static_cast<int>(property_stack.size() - start); | 
|  | int named_length = length - cont.elements; | 
|  |  | 
|  | Handle<Map> initial_map = factory()->ObjectLiteralMapFromCache( | 
|  | isolate_->native_context(), named_length); | 
|  |  | 
|  | Handle<Map> map = initial_map; | 
|  |  | 
|  | Handle<FixedArrayBase> elements = factory()->empty_fixed_array(); | 
|  |  | 
|  | // First store the elements. | 
|  | if (cont.elements > 0) { | 
|  | // Store as dictionary elements if that would use less memory. | 
|  | if (ShouldConvertToSlowElements(cont.elements, cont.max_index + 1)) { | 
|  | Handle<NumberDictionary> elms = | 
|  | NumberDictionary::New(isolate_, cont.elements); | 
|  | for (int i = 0; i < length; i++) { | 
|  | const JsonProperty& property = property_stack[start + i]; | 
|  | if (!property.string.is_index()) continue; | 
|  | uint32_t index = property.string.index(); | 
|  | Handle<Object> value = property.value; | 
|  | elms = NumberDictionary::Set(isolate_, elms, index, value); | 
|  | } | 
|  | map = Map::AsElementsKind(isolate_, map, DICTIONARY_ELEMENTS); | 
|  | elements = elms; | 
|  | } else { | 
|  | Handle<FixedArray> elms = | 
|  | factory()->NewFixedArrayWithHoles(cont.max_index + 1); | 
|  | DisallowHeapAllocation no_gc; | 
|  | WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); | 
|  | DCHECK_EQ(HOLEY_ELEMENTS, map->elements_kind()); | 
|  |  | 
|  | for (int i = 0; i < length; i++) { | 
|  | const JsonProperty& property = property_stack[start + i]; | 
|  | if (!property.string.is_index()) continue; | 
|  | uint32_t index = property.string.index(); | 
|  | Handle<Object> value = property.value; | 
|  | elms->set(static_cast<int>(index), *value, mode); | 
|  | } | 
|  | elements = elms; | 
|  | } | 
|  | } | 
|  |  | 
|  | int feedback_descriptors = | 
|  | (feedback.is_null() || | 
|  | feedback->elements_kind() != map->elements_kind() || | 
|  | feedback->instance_size() != map->instance_size()) | 
|  | ? 0 | 
|  | : feedback->NumberOfOwnDescriptors(); | 
|  |  | 
|  | int i; | 
|  | int descriptor = 0; | 
|  | int new_mutable_double = 0; | 
|  | for (i = 0; i < length; i++) { | 
|  | const JsonProperty& property = property_stack[start + i]; | 
|  | if (property.string.is_index()) continue; | 
|  | Handle<String> expected; | 
|  | Handle<Map> target; | 
|  | InternalIndex descriptor_index(descriptor); | 
|  | if (descriptor < feedback_descriptors) { | 
|  | expected = | 
|  | handle(String::cast(feedback->instance_descriptors(kRelaxedLoad) | 
|  | .GetKey(descriptor_index)), | 
|  | isolate_); | 
|  | } else { | 
|  | DisallowHeapAllocation no_gc; | 
|  | TransitionsAccessor transitions(isolate(), *map, &no_gc); | 
|  | expected = transitions.ExpectedTransitionKey(); | 
|  | if (!expected.is_null()) { | 
|  | // Directly read out the target while reading out the key, otherwise it | 
|  | // might die while building the string below. | 
|  | target = TransitionsAccessor(isolate(), *map, &no_gc) | 
|  | .ExpectedTransitionTarget(); | 
|  | } | 
|  | } | 
|  |  | 
|  | Handle<String> key = MakeString(property.string, expected); | 
|  | if (key.is_identical_to(expected)) { | 
|  | if (descriptor < feedback_descriptors) target = feedback; | 
|  | } else { | 
|  | if (descriptor < feedback_descriptors) { | 
|  | map = ParentOfDescriptorOwner(isolate_, map, feedback, descriptor); | 
|  | feedback_descriptors = 0; | 
|  | } | 
|  | if (!TransitionsAccessor(isolate(), map) | 
|  | .FindTransitionToField(key) | 
|  | .ToHandle(&target)) { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | Handle<Object> value = property.value; | 
|  |  | 
|  | PropertyDetails details = | 
|  | target->instance_descriptors(kRelaxedLoad).GetDetails(descriptor_index); | 
|  | Representation expected_representation = details.representation(); | 
|  |  | 
|  | if (!value->FitsRepresentation(expected_representation)) { | 
|  | Representation representation = value->OptimalRepresentation(isolate()); | 
|  | representation = representation.generalize(expected_representation); | 
|  | if (!expected_representation.CanBeInPlaceChangedTo(representation)) { | 
|  | map = ParentOfDescriptorOwner(isolate_, map, target, descriptor); | 
|  | break; | 
|  | } | 
|  | Handle<FieldType> value_type = | 
|  | value->OptimalType(isolate(), representation); | 
|  | Map::GeneralizeField(isolate(), target, descriptor_index, | 
|  | details.constness(), representation, value_type); | 
|  | } else if (expected_representation.IsHeapObject() && | 
|  | !target->instance_descriptors(kRelaxedLoad) | 
|  | .GetFieldType(descriptor_index) | 
|  | .NowContains(value)) { | 
|  | Handle<FieldType> value_type = | 
|  | value->OptimalType(isolate(), expected_representation); | 
|  | Map::GeneralizeField(isolate(), target, descriptor_index, | 
|  | details.constness(), expected_representation, | 
|  | value_type); | 
|  | } else if (!FLAG_unbox_double_fields && | 
|  | expected_representation.IsDouble() && value->IsSmi()) { | 
|  | new_mutable_double++; | 
|  | } | 
|  |  | 
|  | DCHECK(target->instance_descriptors(kRelaxedLoad) | 
|  | .GetFieldType(descriptor_index) | 
|  | .NowContains(value)); | 
|  | map = target; | 
|  | descriptor++; | 
|  | } | 
|  |  | 
|  | // Fast path: Write all transitioned named properties. | 
|  | if (i == length && descriptor < feedback_descriptors) { | 
|  | map = ParentOfDescriptorOwner(isolate_, map, map, descriptor); | 
|  | } | 
|  |  | 
|  | // Preallocate all mutable heap numbers so we don't need to allocate while | 
|  | // setting up the object. Otherwise verification of that object may fail. | 
|  | Handle<ByteArray> mutable_double_buffer; | 
|  | // Allocate enough space so we can double-align the payload. | 
|  | const int kMutableDoubleSize = sizeof(double) * 2; | 
|  | STATIC_ASSERT(HeapNumber::kSize <= kMutableDoubleSize); | 
|  | if (new_mutable_double > 0) { | 
|  | mutable_double_buffer = | 
|  | factory()->NewByteArray(kMutableDoubleSize * new_mutable_double); | 
|  | } | 
|  |  | 
|  | Handle<JSObject> object = initial_map->is_dictionary_map() | 
|  | ? factory()->NewSlowJSObjectFromMap(map) | 
|  | : factory()->NewJSObjectFromMap(map); | 
|  | object->set_elements(*elements); | 
|  |  | 
|  | { | 
|  | descriptor = 0; | 
|  | DisallowHeapAllocation no_gc; | 
|  | WriteBarrierMode mode = object->GetWriteBarrierMode(no_gc); | 
|  | Address mutable_double_address = | 
|  | mutable_double_buffer.is_null() | 
|  | ? 0 | 
|  | : reinterpret_cast<Address>( | 
|  | mutable_double_buffer->GetDataStartAddress()); | 
|  | Address filler_address = mutable_double_address; | 
|  | if (kTaggedSize != kDoubleSize) { | 
|  | if (IsAligned(mutable_double_address, kDoubleAlignment)) { | 
|  | mutable_double_address += kTaggedSize; | 
|  | } else { | 
|  | filler_address += HeapNumber::kSize; | 
|  | } | 
|  | } | 
|  | for (int j = 0; j < i; j++) { | 
|  | const JsonProperty& property = property_stack[start + j]; | 
|  | if (property.string.is_index()) continue; | 
|  | InternalIndex descriptor_index(descriptor); | 
|  | PropertyDetails details = | 
|  | map->instance_descriptors(kRelaxedLoad).GetDetails(descriptor_index); | 
|  | Object value = *property.value; | 
|  | FieldIndex index = FieldIndex::ForDescriptor(*map, descriptor_index); | 
|  | descriptor++; | 
|  |  | 
|  | if (details.representation().IsDouble()) { | 
|  | if (object->IsUnboxedDoubleField(index)) { | 
|  | uint64_t bits; | 
|  | if (value.IsSmi()) { | 
|  | bits = bit_cast<uint64_t>(static_cast<double>(Smi::ToInt(value))); | 
|  | } else { | 
|  | DCHECK(value.IsHeapNumber()); | 
|  | bits = HeapNumber::cast(value).value_as_bits(); | 
|  | } | 
|  | object->RawFastDoublePropertyAsBitsAtPut(index, bits); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (value.IsSmi()) { | 
|  | if (kTaggedSize != kDoubleSize) { | 
|  | // Write alignment filler. | 
|  | HeapObject filler = HeapObject::FromAddress(filler_address); | 
|  | filler.set_map_after_allocation( | 
|  | *factory()->one_pointer_filler_map()); | 
|  | filler_address += kMutableDoubleSize; | 
|  | } | 
|  |  | 
|  | uint64_t bits = | 
|  | bit_cast<uint64_t>(static_cast<double>(Smi::ToInt(value))); | 
|  | // Allocate simple heapnumber with immortal map, with non-pointer | 
|  | // payload, so we can skip notifying object layout change. | 
|  |  | 
|  | HeapObject hn = HeapObject::FromAddress(mutable_double_address); | 
|  | hn.set_map_after_allocation(*factory()->heap_number_map()); | 
|  | HeapNumber::cast(hn).set_value_as_bits(bits); | 
|  | value = hn; | 
|  | mutable_double_address += kMutableDoubleSize; | 
|  | } else { | 
|  | DCHECK(value.IsHeapNumber()); | 
|  | HeapObject::cast(value).synchronized_set_map( | 
|  | *factory()->heap_number_map()); | 
|  | } | 
|  | } | 
|  | object->RawFastInobjectPropertyAtPut(index, value, mode); | 
|  | } | 
|  | // Make all mutable HeapNumbers alive. | 
|  | if (!mutable_double_buffer.is_null()) { | 
|  | #ifdef DEBUG | 
|  | Address end = | 
|  | reinterpret_cast<Address>(mutable_double_buffer->GetDataEndAddress()); | 
|  | if (kTaggedSize != kDoubleSize) { | 
|  | DCHECK_EQ(Min(filler_address, mutable_double_address), end); | 
|  | DCHECK_GE(filler_address, end); | 
|  | DCHECK_GE(mutable_double_address, end); | 
|  | } else { | 
|  | DCHECK_EQ(mutable_double_address, end); | 
|  | } | 
|  | #endif | 
|  | mutable_double_buffer->set_length(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Slow path: define remaining named properties. | 
|  | for (; i < length; i++) { | 
|  | HandleScope scope(isolate_); | 
|  | const JsonProperty& property = property_stack[start + i]; | 
|  | if (property.string.is_index()) continue; | 
|  | Handle<String> key = MakeString(property.string); | 
|  | #ifdef DEBUG | 
|  | uint32_t index; | 
|  | DCHECK(!key->AsArrayIndex(&index)); | 
|  | #endif | 
|  | Handle<Object> value = property.value; | 
|  | LookupIterator it(isolate_, object, key, object, LookupIterator::OWN); | 
|  | JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE).Check(); | 
|  | } | 
|  |  | 
|  | return object; | 
|  | } | 
|  |  | 
|  | template <typename Char> | 
|  | Handle<Object> JsonParser<Char>::BuildJsonArray( | 
|  | const JsonContinuation& cont, | 
|  | const std::vector<Handle<Object>>& element_stack) { | 
|  | size_t start = cont.index; | 
|  | int length = static_cast<int>(element_stack.size() - start); | 
|  |  | 
|  | ElementsKind kind = PACKED_SMI_ELEMENTS; | 
|  | for (size_t i = start; i < element_stack.size(); i++) { | 
|  | Object value = *element_stack[i]; | 
|  | if (value.IsHeapObject()) { | 
|  | if (HeapObject::cast(value).IsHeapNumber()) { | 
|  | kind = PACKED_DOUBLE_ELEMENTS; | 
|  | } else { | 
|  | kind = PACKED_ELEMENTS; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Handle<JSArray> array = factory()->NewJSArray(kind, length, length); | 
|  | if (kind == PACKED_DOUBLE_ELEMENTS) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | FixedDoubleArray elements = FixedDoubleArray::cast(array->elements()); | 
|  | for (int i = 0; i < length; i++) { | 
|  | elements.set(i, element_stack[start + i]->Number()); | 
|  | } | 
|  | } else { | 
|  | DisallowHeapAllocation no_gc; | 
|  | FixedArray elements = FixedArray::cast(array->elements()); | 
|  | WriteBarrierMode mode = kind == PACKED_SMI_ELEMENTS | 
|  | ? SKIP_WRITE_BARRIER | 
|  | : elements.GetWriteBarrierMode(no_gc); | 
|  | for (int i = 0; i < length; i++) { | 
|  | elements.set(i, *element_stack[start + i], mode); | 
|  | } | 
|  | } | 
|  | return array; | 
|  | } | 
|  |  | 
|  | // Parse any JSON value. | 
|  | template <typename Char> | 
|  | MaybeHandle<Object> JsonParser<Char>::ParseJsonValue() { | 
|  | std::vector<JsonContinuation> cont_stack; | 
|  | std::vector<JsonProperty> property_stack; | 
|  | std::vector<Handle<Object>> element_stack; | 
|  |  | 
|  | cont_stack.reserve(16); | 
|  | property_stack.reserve(16); | 
|  | element_stack.reserve(16); | 
|  |  | 
|  | JsonContinuation cont(isolate_, JsonContinuation::kReturn, 0); | 
|  |  | 
|  | Handle<Object> value; | 
|  | while (true) { | 
|  | // Produce a json value. | 
|  | // | 
|  | // Iterate until a value is produced. Starting but not immediately finishing | 
|  | // objects and arrays will cause the loop to continue until a first member | 
|  | // is completed. | 
|  | while (true) { | 
|  | SkipWhitespace(); | 
|  | // The switch is immediately followed by 'break' so we can use 'break' to | 
|  | // break out of the loop, and 'continue' to continue the loop. | 
|  | switch (peek()) { | 
|  | case JsonToken::STRING: | 
|  | Consume(JsonToken::STRING); | 
|  | value = MakeString(ScanJsonString(false)); | 
|  | break; | 
|  |  | 
|  | case JsonToken::NUMBER: | 
|  | value = ParseJsonNumber(); | 
|  | break; | 
|  |  | 
|  | case JsonToken::LBRACE: { | 
|  | Consume(JsonToken::LBRACE); | 
|  | if (Check(JsonToken::RBRACE)) { | 
|  | // TODO(verwaest): Directly use the map instead. | 
|  | value = factory()->NewJSObject(object_constructor_); | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Start parsing an object with properties. | 
|  | cont_stack.emplace_back(std::move(cont)); | 
|  | cont = JsonContinuation(isolate_, JsonContinuation::kObjectProperty, | 
|  | property_stack.size()); | 
|  |  | 
|  | // Parse the property key. | 
|  | ExpectNext(JsonToken::STRING); | 
|  | property_stack.emplace_back(ScanJsonPropertyKey(&cont)); | 
|  |  | 
|  | ExpectNext(JsonToken::COLON); | 
|  |  | 
|  | // Continue to start producing the first property value. | 
|  | continue; | 
|  | } | 
|  |  | 
|  | case JsonToken::LBRACK: | 
|  | Consume(JsonToken::LBRACK); | 
|  | if (Check(JsonToken::RBRACK)) { | 
|  | value = factory()->NewJSArray(0, PACKED_SMI_ELEMENTS); | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Start parsing an array with elements. | 
|  | cont_stack.emplace_back(std::move(cont)); | 
|  | cont = JsonContinuation(isolate_, JsonContinuation::kArrayElement, | 
|  | element_stack.size()); | 
|  |  | 
|  | // Continue to start producing the first array element. | 
|  | continue; | 
|  |  | 
|  | case JsonToken::TRUE_LITERAL: | 
|  | ScanLiteral("true"); | 
|  | value = factory()->true_value(); | 
|  | break; | 
|  |  | 
|  | case JsonToken::FALSE_LITERAL: | 
|  | ScanLiteral("false"); | 
|  | value = factory()->false_value(); | 
|  | break; | 
|  |  | 
|  | case JsonToken::NULL_LITERAL: | 
|  | ScanLiteral("null"); | 
|  | value = factory()->null_value(); | 
|  | break; | 
|  |  | 
|  | case JsonToken::COLON: | 
|  | case JsonToken::COMMA: | 
|  | case JsonToken::ILLEGAL: | 
|  | case JsonToken::RBRACE: | 
|  | case JsonToken::RBRACK: | 
|  | case JsonToken::EOS: | 
|  | ReportUnexpectedCharacter(CurrentCharacter()); | 
|  | // Pop the continuation stack to correctly tear down handle scopes. | 
|  | while (!cont_stack.empty()) { | 
|  | cont = std::move(cont_stack.back()); | 
|  | cont_stack.pop_back(); | 
|  | } | 
|  | return MaybeHandle<Object>(); | 
|  |  | 
|  | case JsonToken::WHITESPACE: | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | // Done producing a value, consume it. | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Consume a produced json value. | 
|  | // | 
|  | // Iterate as long as values are produced (arrays or object literals are | 
|  | // finished). | 
|  | while (true) { | 
|  | // The switch is immediately followed by 'break' so we can use 'break' to | 
|  | // break out of the loop, and 'continue' to continue the loop. | 
|  | switch (cont.type()) { | 
|  | case JsonContinuation::kReturn: | 
|  | return cont.scope.CloseAndEscape(value); | 
|  |  | 
|  | case JsonContinuation::kObjectProperty: { | 
|  | // Store the previous property value into its property info. | 
|  | property_stack.back().value = value; | 
|  |  | 
|  | if (V8_LIKELY(Check(JsonToken::COMMA))) { | 
|  | // Parse the property key. | 
|  | ExpectNext(JsonToken::STRING); | 
|  |  | 
|  | property_stack.emplace_back(ScanJsonPropertyKey(&cont)); | 
|  | ExpectNext(JsonToken::COLON); | 
|  |  | 
|  | // Break to start producing the subsequent property value. | 
|  | break; | 
|  | } | 
|  |  | 
|  | Handle<Map> feedback; | 
|  | if (cont_stack.size() > 0 && | 
|  | cont_stack.back().type() == JsonContinuation::kArrayElement && | 
|  | cont_stack.back().index < element_stack.size() && | 
|  | element_stack.back()->IsJSObject()) { | 
|  | Map maybe_feedback = JSObject::cast(*element_stack.back()).map(); | 
|  | // Don't consume feedback from objects with a map that's detached | 
|  | // from the transition tree. | 
|  | if (!maybe_feedback.IsDetached(isolate_)) { | 
|  | feedback = handle(maybe_feedback, isolate_); | 
|  | if (feedback->is_deprecated()) { | 
|  | feedback = Map::Update(isolate_, feedback); | 
|  | } | 
|  | } | 
|  | } | 
|  | value = BuildJsonObject(cont, property_stack, feedback); | 
|  | property_stack.resize(cont.index); | 
|  | Expect(JsonToken::RBRACE); | 
|  |  | 
|  | // Return the object. | 
|  | value = cont.scope.CloseAndEscape(value); | 
|  | // Pop the continuation. | 
|  | cont = std::move(cont_stack.back()); | 
|  | cont_stack.pop_back(); | 
|  | // Consume to produced object. | 
|  | continue; | 
|  | } | 
|  |  | 
|  | case JsonContinuation::kArrayElement: { | 
|  | // Store the previous element on the stack. | 
|  | element_stack.emplace_back(value); | 
|  | // Break to start producing the subsequent element value. | 
|  | if (V8_LIKELY(Check(JsonToken::COMMA))) break; | 
|  |  | 
|  | value = BuildJsonArray(cont, element_stack); | 
|  | element_stack.resize(cont.index); | 
|  | Expect(JsonToken::RBRACK); | 
|  |  | 
|  | // Return the array. | 
|  | value = cont.scope.CloseAndEscape(value); | 
|  | // Pop the continuation. | 
|  | cont = std::move(cont_stack.back()); | 
|  | cont_stack.pop_back(); | 
|  | // Consume the produced array. | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Done consuming a value. Produce next value. | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename Char> | 
|  | void JsonParser<Char>::AdvanceToNonDecimal() { | 
|  | cursor_ = | 
|  | std::find_if(cursor_, end_, [](Char c) { return !IsDecimalDigit(c); }); | 
|  | } | 
|  |  | 
|  | template <typename Char> | 
|  | Handle<Object> JsonParser<Char>::ParseJsonNumber() { | 
|  | double number; | 
|  | int sign = 1; | 
|  |  | 
|  | { | 
|  | const Char* start = cursor_; | 
|  | DisallowHeapAllocation no_gc; | 
|  |  | 
|  | uc32 c = *cursor_; | 
|  | if (c == '-') { | 
|  | sign = -1; | 
|  | c = NextCharacter(); | 
|  | } | 
|  |  | 
|  | if (c == '0') { | 
|  | // Prefix zero is only allowed if it's the only digit before | 
|  | // a decimal point or exponent. | 
|  | c = NextCharacter(); | 
|  | if (base::IsInRange(c, 0, | 
|  | static_cast<int32_t>(unibrow::Latin1::kMaxChar)) && | 
|  | IsNumberPart(character_json_scan_flags[c])) { | 
|  | if (V8_UNLIKELY(IsDecimalDigit(c))) { | 
|  | AllowHeapAllocation allow_before_exception; | 
|  | ReportUnexpectedToken(JsonToken::NUMBER); | 
|  | return handle(Smi::FromInt(0), isolate_); | 
|  | } | 
|  | } else if (sign > 0) { | 
|  | return handle(Smi::FromInt(0), isolate_); | 
|  | } | 
|  | } else { | 
|  | const Char* smi_start = cursor_; | 
|  | AdvanceToNonDecimal(); | 
|  | if (V8_UNLIKELY(smi_start == cursor_)) { | 
|  | AllowHeapAllocation allow_before_exception; | 
|  | ReportUnexpectedCharacter(CurrentCharacter()); | 
|  | return handle(Smi::FromInt(0), isolate_); | 
|  | } | 
|  | uc32 c = CurrentCharacter(); | 
|  | STATIC_ASSERT(Smi::IsValid(-999999999)); | 
|  | STATIC_ASSERT(Smi::IsValid(999999999)); | 
|  | const int kMaxSmiLength = 9; | 
|  | if ((cursor_ - smi_start) <= kMaxSmiLength && | 
|  | (!base::IsInRange(c, 0, | 
|  | static_cast<int32_t>(unibrow::Latin1::kMaxChar)) || | 
|  | !IsNumberPart(character_json_scan_flags[c]))) { | 
|  | // Smi. | 
|  | int32_t i = 0; | 
|  | for (; smi_start != cursor_; smi_start++) { | 
|  | DCHECK(IsDecimalDigit(*smi_start)); | 
|  | i = (i * 10) + ((*smi_start) - '0'); | 
|  | } | 
|  | // TODO(verwaest): Cache? | 
|  | return handle(Smi::FromInt(i * sign), isolate_); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (CurrentCharacter() == '.') { | 
|  | uc32 c = NextCharacter(); | 
|  | if (!IsDecimalDigit(c)) { | 
|  | AllowHeapAllocation allow_before_exception; | 
|  | ReportUnexpectedCharacter(c); | 
|  | return handle(Smi::FromInt(0), isolate_); | 
|  | } | 
|  | AdvanceToNonDecimal(); | 
|  | } | 
|  |  | 
|  | if (AsciiAlphaToLower(CurrentCharacter()) == 'e') { | 
|  | uc32 c = NextCharacter(); | 
|  | if (c == '-' || c == '+') c = NextCharacter(); | 
|  | if (!IsDecimalDigit(c)) { | 
|  | AllowHeapAllocation allow_before_exception; | 
|  | ReportUnexpectedCharacter(c); | 
|  | return handle(Smi::FromInt(0), isolate_); | 
|  | } | 
|  | AdvanceToNonDecimal(); | 
|  | } | 
|  |  | 
|  | Vector<const Char> chars(start, cursor_ - start); | 
|  | number = StringToDouble(chars, | 
|  | NO_FLAGS,  // Hex, octal or trailing junk. | 
|  | std::numeric_limits<double>::quiet_NaN()); | 
|  |  | 
|  | DCHECK(!std::isnan(number)); | 
|  | } | 
|  |  | 
|  | return factory()->NewNumber(number); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | template <typename Char> | 
|  | bool Matches(const Vector<const Char>& chars, Handle<String> string) { | 
|  | DCHECK(!string.is_null()); | 
|  |  | 
|  | if (chars.length() != string->length()) return false; | 
|  |  | 
|  | DisallowHeapAllocation no_gc; | 
|  | if (string->IsOneByteRepresentation()) { | 
|  | const uint8_t* string_data = string->GetChars<uint8_t>(no_gc); | 
|  | return CompareChars(chars.begin(), string_data, chars.length()) == 0; | 
|  | } | 
|  | const uint16_t* string_data = string->GetChars<uint16_t>(no_gc); | 
|  | return CompareChars(chars.begin(), string_data, chars.length()) == 0; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | template <typename Char> | 
|  | template <typename SinkSeqString> | 
|  | Handle<String> JsonParser<Char>::DecodeString( | 
|  | const JsonString& string, Handle<SinkSeqString> intermediate, | 
|  | Handle<String> hint) { | 
|  | using SinkChar = typename SinkSeqString::Char; | 
|  | { | 
|  | DisallowHeapAllocation no_gc; | 
|  | SinkChar* dest = intermediate->GetChars(no_gc); | 
|  | if (!string.has_escape()) { | 
|  | DCHECK(!string.internalize()); | 
|  | CopyChars(dest, chars_ + string.start(), string.length()); | 
|  | return intermediate; | 
|  | } | 
|  | DecodeString(dest, string.start(), string.length()); | 
|  |  | 
|  | if (!string.internalize()) return intermediate; | 
|  |  | 
|  | Vector<const SinkChar> data(dest, string.length()); | 
|  | if (!hint.is_null() && Matches(data, hint)) return hint; | 
|  | } | 
|  |  | 
|  | return factory()->InternalizeString(intermediate, 0, string.length()); | 
|  | } | 
|  |  | 
|  | template <typename Char> | 
|  | Handle<String> JsonParser<Char>::MakeString(const JsonString& string, | 
|  | Handle<String> hint) { | 
|  | if (string.length() == 0) return factory()->empty_string(); | 
|  |  | 
|  | if (string.internalize() && !string.has_escape()) { | 
|  | if (!hint.is_null()) { | 
|  | Vector<const Char> data(chars_ + string.start(), string.length()); | 
|  | if (Matches(data, hint)) return hint; | 
|  | } | 
|  | if (chars_may_relocate_) { | 
|  | return factory()->InternalizeString(Handle<SeqString>::cast(source_), | 
|  | string.start(), string.length(), | 
|  | string.needs_conversion()); | 
|  | } | 
|  | Vector<const Char> chars(chars_ + string.start(), string.length()); | 
|  | return factory()->InternalizeString(chars, string.needs_conversion()); | 
|  | } | 
|  |  | 
|  | if (sizeof(Char) == 1 ? V8_LIKELY(!string.needs_conversion()) | 
|  | : string.needs_conversion()) { | 
|  | Handle<SeqOneByteString> intermediate = | 
|  | factory()->NewRawOneByteString(string.length()).ToHandleChecked(); | 
|  | return DecodeString(string, intermediate, hint); | 
|  | } | 
|  |  | 
|  | Handle<SeqTwoByteString> intermediate = | 
|  | factory()->NewRawTwoByteString(string.length()).ToHandleChecked(); | 
|  | return DecodeString(string, intermediate, hint); | 
|  | } | 
|  |  | 
|  | template <typename Char> | 
|  | template <typename SinkChar> | 
|  | void JsonParser<Char>::DecodeString(SinkChar* sink, int start, int length) { | 
|  | SinkChar* sink_start = sink; | 
|  | const Char* cursor = chars_ + start; | 
|  | while (true) { | 
|  | const Char* end = cursor + length - (sink - sink_start); | 
|  | cursor = std::find_if(cursor, end, [&sink](Char c) { | 
|  | if (c == '\\') return true; | 
|  | *sink++ = c; | 
|  | return false; | 
|  | }); | 
|  |  | 
|  | if (cursor == end) return; | 
|  |  | 
|  | cursor++; | 
|  |  | 
|  | switch (GetEscapeKind(character_json_scan_flags[*cursor])) { | 
|  | case EscapeKind::kSelf: | 
|  | *sink++ = *cursor; | 
|  | break; | 
|  |  | 
|  | case EscapeKind::kBackspace: | 
|  | *sink++ = '\x08'; | 
|  | break; | 
|  |  | 
|  | case EscapeKind::kTab: | 
|  | *sink++ = '\x09'; | 
|  | break; | 
|  |  | 
|  | case EscapeKind::kNewLine: | 
|  | *sink++ = '\x0A'; | 
|  | break; | 
|  |  | 
|  | case EscapeKind::kFormFeed: | 
|  | *sink++ = '\x0C'; | 
|  | break; | 
|  |  | 
|  | case EscapeKind::kCarriageReturn: | 
|  | *sink++ = '\x0D'; | 
|  | break; | 
|  |  | 
|  | case EscapeKind::kUnicode: { | 
|  | uc32 value = 0; | 
|  | for (int i = 0; i < 4; i++) { | 
|  | value = value * 16 + HexValue(*++cursor); | 
|  | } | 
|  | if (value <= | 
|  | static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) { | 
|  | *sink++ = value; | 
|  | } else { | 
|  | *sink++ = unibrow::Utf16::LeadSurrogate(value); | 
|  | *sink++ = unibrow::Utf16::TrailSurrogate(value); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case EscapeKind::kIllegal: | 
|  | UNREACHABLE(); | 
|  | } | 
|  | cursor++; | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename Char> | 
|  | JsonString JsonParser<Char>::ScanJsonString(bool needs_internalization) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | int start = position(); | 
|  | int offset = start; | 
|  | bool has_escape = false; | 
|  | uc32 bits = 0; | 
|  |  | 
|  | while (true) { | 
|  | cursor_ = std::find_if(cursor_, end_, [&bits](Char c) { | 
|  | if (sizeof(Char) == 2 && V8_UNLIKELY(c > unibrow::Latin1::kMaxChar)) { | 
|  | bits |= c; | 
|  | return false; | 
|  | } | 
|  | return MayTerminateJsonString(character_json_scan_flags[c]); | 
|  | }); | 
|  |  | 
|  | if (V8_UNLIKELY(is_at_end())) { | 
|  | AllowHeapAllocation allow_before_exception; | 
|  | ReportUnexpectedCharacter(kEndOfString); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (*cursor_ == '"') { | 
|  | int end = position(); | 
|  | advance(); | 
|  | int length = end - offset; | 
|  | bool convert = sizeof(Char) == 1 ? bits > unibrow::Latin1::kMaxChar | 
|  | : bits <= unibrow::Latin1::kMaxChar; | 
|  | return JsonString(start, length, convert, needs_internalization, | 
|  | has_escape); | 
|  | } | 
|  |  | 
|  | if (*cursor_ == '\\') { | 
|  | has_escape = true; | 
|  | uc32 c = NextCharacter(); | 
|  | if (V8_UNLIKELY(!base::IsInRange( | 
|  | c, 0, static_cast<int32_t>(unibrow::Latin1::kMaxChar)))) { | 
|  | AllowHeapAllocation allow_before_exception; | 
|  | ReportUnexpectedCharacter(c); | 
|  | break; | 
|  | } | 
|  |  | 
|  | switch (GetEscapeKind(character_json_scan_flags[c])) { | 
|  | case EscapeKind::kSelf: | 
|  | case EscapeKind::kBackspace: | 
|  | case EscapeKind::kTab: | 
|  | case EscapeKind::kNewLine: | 
|  | case EscapeKind::kFormFeed: | 
|  | case EscapeKind::kCarriageReturn: | 
|  | offset += 1; | 
|  | break; | 
|  |  | 
|  | case EscapeKind::kUnicode: { | 
|  | uc32 value = ScanUnicodeCharacter(); | 
|  | if (value == kInvalidUnicodeCharacter) { | 
|  | AllowHeapAllocation allow_before_exception; | 
|  | ReportUnexpectedCharacter(CurrentCharacter()); | 
|  | return JsonString(); | 
|  | } | 
|  | bits |= value; | 
|  | // \uXXXX results in either 1 or 2 Utf16 characters, depending on | 
|  | // whether the decoded value requires a surrogate pair. | 
|  | offset += 5 - (value > static_cast<uc32>( | 
|  | unibrow::Utf16::kMaxNonSurrogateCharCode)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case EscapeKind::kIllegal: | 
|  | AllowHeapAllocation allow_before_exception; | 
|  | ReportUnexpectedCharacter(c); | 
|  | return JsonString(); | 
|  | } | 
|  |  | 
|  | advance(); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | DCHECK_LT(*cursor_, 0x20); | 
|  | AllowHeapAllocation allow_before_exception; | 
|  | ReportUnexpectedCharacter(*cursor_); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return JsonString(); | 
|  | } | 
|  |  | 
|  | // Explicit instantiation. | 
|  | template class JsonParser<uint8_t>; | 
|  | template class JsonParser<uint16_t>; | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace v8 |