current commit
261007.21 · deps: update V8 to 15.5.35.20
// 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 <optional>

#include "hwy/highway.h"
#include "include/v8config.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/base/small-vector.h"
#include "src/base/strings.h"
#include "src/builtins/builtins.h"
#include "src/common/assert-scope.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/heap/factory.h"
#include "src/numbers/conversions.h"
#include "src/numbers/hash-seed-inl.h"
#include "src/objects/elements-kind.h"
#include "src/objects/field-type.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/heap-object.h"
#include "src/objects/js-data-object-builder-inl.h"
#include "src/objects/object-conversions-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/property-descriptor.h"
#include "src/objects/property-details.h"
#include "src/objects/string-inl.h"
#include "src/objects/string-table.h"
#include "src/roots/roots.h"
#include "src/strings/char-predicates-inl.h"
#include "src/strings/string-hasher.h"
#include "src/utils/boxed-float.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
};

#define EXPECT_RETURN_ON_ERROR(token, msg, ret) \
  if (V8_UNLIKELY(!Expect<token>(msg))) {       \
    return ret;                                 \
  }
#define EXPECT_NEXT_RETURN_ON_ERROR(token, msg, ret) \
  if (V8_UNLIKELY(!ExpectNext<token>(msg))) {        \
    return ret;                                      \
  }

}  // namespace

MaybeHandle<Object> JsonParseInternalizer::Internalize(
    Isolate* isolate, DirectHandle<Object> result, Handle<Object> reviver,
    Handle<String> source, MaybeHandle<Object> val_node,
    bool pass_context_argument) {
  DCHECK(IsCallable(*reviver));
  JsonParseInternalizer internalizer(isolate, Cast<JSReceiver>(reviver),
                                     source);
  DirectHandle<JSObject> holder =
      isolate->factory()->NewJSObject(isolate->object_function());
  DirectHandle<String> name = isolate->factory()->empty_string();
  JSObject::AddProperty(isolate, holder, name, result, NONE);
  if (pass_context_argument) {
    // Three-argument reviver, so we add a context argument to each
    // callback to the reviver, and that context object will normally
    // have a source property.
    return internalizer.InternalizeJsonProperty<kWithSource>(holder, name,
                                                             val_node, result);
  } else {
    // Faster two-argument reviver.
    return internalizer.InternalizeJsonProperty<kWithoutContext>(
        holder, name, val_node, DirectHandle<Object>());
  }
}

template <JsonParseInternalizer::ReviverMode initial_reviver_mode>
MaybeHandle<Object> JsonParseInternalizer::InternalizeJsonProperty(
    DirectHandle<JSReceiver> holder, DirectHandle<String> name,
    MaybeHandle<Object> val_node, DirectHandle<Object> snapshot) {
  DCHECK_EQ(val_node.is_null(), snapshot.is_null());
  DCHECK_NE(initial_reviver_mode == kWithSource, val_node.is_null());
  DCHECK(IsCallable(*reviver_));
  HandleScope outer_scope(isolate_);
  Handle<Object> value;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate_, value, Object::GetPropertyOrElement(isolate_, holder, name));

  // When reviver_mode == kWithSource, the source text is passed
  // to the reviver if the reviver has not mucked with the originally parsed
  // value.
  ReviverMode reviver_mode;
  if (initial_reviver_mode == kWithSource &&
      !Object::SameValue(*value, *snapshot)) {
    reviver_mode = NoSource(initial_reviver_mode);
  } else {
    reviver_mode = initial_reviver_mode;
  }

  if (IsJSReceiver(*value)) {
    // Value is non-primitive, so we may have to recurse deeper.
    Handle<JSReceiver> object = Cast<JSReceiver>(value);
    Maybe<bool> is_array = Object::IsArray(object);
    if (is_array.IsNothing()) return MaybeHandle<Object>();
    if (is_array.FromJust()) {
      DirectHandle<Object> length_object;
      ASSIGN_RETURN_ON_EXCEPTION(
          isolate_, length_object,
          Object::GetLengthFromArrayLike(isolate_, object));
      double length = Object::NumberValue(*length_object);
      if (reviver_mode == kWithSource) {
        auto val_nodes_and_snapshots =
            Cast<FixedArray>(val_node.ToHandleChecked());
        uint32_t snapshot_length =
            val_nodes_and_snapshots->ulength().value() / 2;
        for (uint32_t i = 0; i < length; i++) {
          HandleScope inner_scope(isolate_);
          DirectHandle<Object> index = isolate_->factory()->NewNumber(i);
          Handle<String> index_name =
              isolate_->factory()->NumberToString(index);
          // Even if the array pointer snapshot matched, it's possible the
          // array had new elements added that are not in the snapshotted
          // elements.
          const bool rv =
              i < snapshot_length
                  ? RecurseAndApply<kWithSource>(
                        object, index_name,
                        handle(val_nodes_and_snapshots->get(i * 2), isolate_),
                        handle(val_nodes_and_snapshots->get(i * 2 + 1),
                               isolate_))
                  : RecurseAndApply<kWithoutSource>(
                        object, index_name, Handle<Object>(), Handle<Object>());
          if (!rv) {
            return MaybeHandle<Object>();
          }
        }
      } else {
        DCHECK(reviver_mode == kWithoutSource ||
               reviver_mode == kWithoutContext);
        for (int i = 0; i < length; i++) {
          HandleScope inner_scope(isolate_);
          DirectHandle<Object> index = isolate_->factory()->NewNumber(i);
          Handle<String> index_name =
              isolate_->factory()->NumberToString(index);
          if (!RecurseAndApply<NoSource(initial_reviver_mode)>(
                  object, index_name, Handle<Object>(), Handle<Object>())) {
            return MaybeHandle<Object>();
          }
        }
      }
    } else {
      DCHECK(!is_array.FromJust());  // It's a non-primitive, non-array.
      DirectHandle<FixedArray> contents;
      ASSIGN_RETURN_ON_EXCEPTION(
          isolate_, contents,
          KeyAccumulator::GetKeys(isolate_, object, KeyCollectionMode::kOwnOnly,
                                  ENUMERABLE_STRINGS,
                                  GetKeysConversion::kConvertToString));
      if (reviver_mode == kWithSource) {
        auto val_nodes_and_snapshots =
            Cast<ObjectTwoHashTable>(val_node.ToHandleChecked());
        uint32_t contents_len = contents->ulength().value();
        for (uint32_t i = 0; i < contents_len; i++) {
          HandleScope inner_scope(isolate_);
          Handle<String> key_name(Cast<String>(contents->get(i)), isolate_);
          auto property_val_node_and_snapshot =
              val_nodes_and_snapshots->Lookup(key_name);
          Handle<Object> property_val_node(property_val_node_and_snapshot[0],
                                           isolate_);
          Handle<Object> property_snapshot(property_val_node_and_snapshot[1],
                                           isolate_);
          // Even if the object pointer snapshot matched, it's possible the
          // object had new properties added that are not in the snapshotted
          // contents.
          const bool rv =
              !IsTheHole(*property_snapshot)
                  ? RecurseAndApply<kWithSource>(
                        object, key_name, property_val_node, property_snapshot)
                  : RecurseAndApply<kWithoutSource>(
                        object, key_name, Handle<Object>(), Handle<Object>());
          if (!rv) {
            return MaybeHandle<Object>();
          }
        }
      } else {
        DCHECK(reviver_mode == kWithoutSource ||
               reviver_mode == kWithoutContext);
        uint32_t contents_len = contents->ulength().value();
        for (uint32_t i = 0; i < contents_len; i++) {
          HandleScope inner_scope(isolate_);
          Handle<String> key_name(Cast<String>(contents->get(i)), isolate_);
          if (!RecurseAndApply<NoSource(initial_reviver_mode)>(
                  object, key_name, Handle<Object>(), Handle<Object>())) {
            return MaybeHandle<Object>();
          }
        }
      }
    }
  }
  // Done recursing.

  if (reviver_mode == kWithoutContext) {
    DirectHandle<Object> args[] = {name, value};
    Handle<Object> result;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate_, result,
        Execution::Call(isolate_, reviver_, holder, base::VectorOf(args)));
    return outer_scope.CloseAndEscape(result);
  }

  DirectHandle<JSObject> context =
      isolate_->factory()->NewJSObject(isolate_->object_function());
  if (reviver_mode == kWithSource) {
    auto val = val_node.ToHandleChecked();
    if (IsString(*val)) {
      JSReceiver::CreateDataProperty(isolate_, context,
                                     isolate_->factory()->source_string(), val,
                                     Just(kThrowOnError))
          .Check();
    }
  }
  DirectHandle<Object> args[] = {name, value, context};
  Handle<Object> result;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate_, result,
      Execution::Call(isolate_, reviver_, holder, base::VectorOf(args)));
  return outer_scope.CloseAndEscape(result);
}

template <JsonParseInternalizer::ReviverMode reviver_mode>
bool JsonParseInternalizer::RecurseAndApply(Handle<JSReceiver> holder,
                                            Handle<String> name,
                                            Handle<Object> val_node,
                                            Handle<Object> snapshot) {
  STACK_CHECK(isolate_, false);
  DCHECK(IsCallable(*reviver_));
  DirectHandle<Object> result;
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate_, result,
      InternalizeJsonProperty<reviver_mode>(holder, name, val_node, snapshot),
      false);
  Maybe<bool> change_result = Nothing<bool>();
  if (IsUndefined(*result)) {
    change_result = JSReceiver::DeletePropertyOrElement(isolate_, holder, name,
                                                        LanguageMode::kSloppy);
  } else {
    PropertyDescriptor desc;
    desc.set_value(Cast<JSAny>(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,
                             std::optional<ScriptDetails> script_details)
    : isolate_(isolate),
      object_constructor_(isolate_->object_function()),
      original_source_(source),
      script_details_(script_details),
      parsed_val_node_(),
      remaining_heuristic_internalizations_(
          v8_flags.json_parse_max_heuristically_internalized_strings) {
  size_t start = 0;
  size_t length = source->length();
  if (IsSlicedString(*source)) {
    Tagged<SlicedString> string = Cast<SlicedString>(*source);
    start = string->offset();
    Tagged<String> parent = string->parent();
    if (IsThinString(parent)) parent = Cast<ThinString>(parent)->actual();
    source_ = handle(parent, isolate);
  } else {
    source_ = String::Flatten(isolate, source);
  }

  // JsonParser with --shared-string-table is currently not supported on worker
  // threads due to known issues (see crbug.com/486551890).
  // We still allow fuzzers to explore this path further.
  DCHECK_IMPLIES(v8_flags.shared_string_table && !v8_flags.fuzzing,
                 isolate->is_shared_space_isolate());

  if (StringShape(*source_).IsExternal()) {
    chars_ =
        static_cast<const Char*>(Cast<SeqExternalString>(*source_)->GetChars());
    chars_may_relocate_ = false;
  } else {
    DisallowGarbageCollection no_gc;
    isolate->main_thread_local_heap()->AddGCEpilogueCallback(
        UpdatePointersCallback, this);
    // Keeping the `GetChars()` result is safe because we update the pointer in
    // the GCEpilogueCallback.
    START_IGNORE_LIFETIME_SAFETY_WARNINGS();
    chars_ = Cast<SeqString>(*source_)->GetChars(no_gc);
    END_IGNORE_LIFETIME_SAFETY_WARNINGS();
    chars_may_relocate_ = true;
  }
  cursor_ = chars_ + start;
  end_ = cursor_ + length;
}

template <typename Char>
bool JsonParser<Char>::IsSpecialString() {
  // The special cases are undefined, NaN, Infinity, and {} being passed to the
  // parse method
  int offset = IsSlicedString(*original_source_)
                   ? Cast<SlicedString>(*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 <typename Char>
MessageTemplate JsonParser<Char>::GetErrorMessageWithEllipses(
    DirectHandle<Object>& arg, DirectHandle<Object>& 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 characters 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 ellipses.
      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
      // ellipses.
      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 <typename Char>
MessageTemplate JsonParser<Char>::LookUpErrorMessageForJsonToken(
    JsonToken token, DirectHandle<Object>& arg, DirectHandle<Object>& 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 <typename Char>
void JsonParser<Char>::CalculateFileLocation(DirectHandle<Object>& line,
                                             DirectHandle<Object>& column) {
  // JSON allows only \r and \n as line terminators.
  // (See https://www.json.org/json-en.html - "whitespace")
  int line_number = 1;
  const Char* start =
      chars_ + (IsSlicedString(*original_source_)
                    ? Cast<SlicedString>(*original_source_)->offset()
                    : 0);
  const Char* last_line_break = start;
  const Char* cursor = start;
  const Char* end = cursor_;  // cursor_ points to the position of the error.
  for (; cursor < end; ++cursor) {
    if (*cursor == '\r' && cursor < end - 1 && cursor[1] == '\n') {
      // \r\n counts as a single line terminator, as of
      // https://tc39.es/ecma262/#sec-line-terminators. JSON itself does not
      // have a notion of lines or line terminators.
      ++cursor;
    }
    if (*cursor == '\r' || *cursor == '\n') {
      ++line_number;
      last_line_break = cursor + 1;
    }
  }
  int column_number = 1 + static_cast<int>(cursor - last_line_break);
  line = direct_handle(Smi::FromInt(line_number), isolate());
  column = direct_handle(Smi::FromInt(column_number), isolate());
}

template <typename Char>
void JsonParser<Char>::ReportUnexpectedToken(
    JsonToken token, std::optional<MessageTemplate> errorMessage) {
  // Some exception (for example stack overflow) was already thrown.
  if (isolate_->has_exception()) return;

  // Parse failed. Current character is the unexpected token.
  Factory* factory = this->factory();
  int offset = IsSlicedString(*original_source_)
                   ? Cast<SlicedString>(*original_source_)->offset()
                   : 0;
  int pos = position() - offset;
  DirectHandle<Object> arg(Smi::FromInt(pos), isolate());
  DirectHandle<Object> arg2;
  DirectHandle<Object> arg3;
  CalculateFileLocation(arg2, arg3);

  MessageTemplate message =
      errorMessage ? errorMessage.value()
                   : LookUpErrorMessageForJsonToken(token, arg, arg2, pos);

  Handle<Script> script(factory->NewScript(original_source_));
  DCHECK_IMPLIES(isolate_->NeedsSourcePositions(), script->has_line_ends());
  if (script_details_.has_value()) {
    const ScriptDetails& details = script_details_.value();
    script->set_origin_options(details.origin_options);
    {
      DisallowGarbageCollection no_gc;
      SetScriptFieldsFromDetails(isolate_, *script, details, &no_gc);
    }
  } else {
    DebuggableStackFrameIterator it(isolate_);
    if (!it.done() && it.is_javascript()) {
      FrameSummary summary = it.GetTopValidFrame();
      script->set_eval_from_shared(summary.AsJavaScript().function()->shared());
      if (IsScript(*summary.script())) {
        script->set_origin_options(
            Cast<Script>(*summary.script())->origin_options());
      }
    }
  }

  // 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, arg, arg2, arg3),
                     &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(base::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 (chars_may_relocate_) {
    // Check that the string shape hasn't changed. Otherwise our GC hooks are
    // broken.
    Cast<SeqString>(*source_);
    isolate()->main_thread_local_heap()->RemoveGCEpilogueCallback(
        UpdatePointersCallback, this);
  } else {
    // Check that the string shape hasn't changed. Otherwise our GC hooks are
    // broken.
    Cast<SeqExternalString>(*source_);
  }
}

template <typename Char>
MaybeHandle<Object> JsonParser<Char>::ParseJson(bool should_track_json_source) {
  Handle<Object> result;
  if (V8_UNLIKELY(should_track_json_source)) {
    ASSIGN_RETURN_ON_EXCEPTION(isolate(), result, ParseJsonValue<true>());
  } else {
    ASSIGN_RETURN_ON_EXCEPTION(isolate(), result, ParseJsonValueRecursive());
  }

  EXPECT_NEXT_RETURN_ON_ERROR(
      JsonToken::EOS,
      MessageTemplate::kJsonParseUnexpectedNonWhiteSpaceCharacter, {});
  if (isolate_->has_exception()) {
    return MaybeHandle<Object>();
  }
  return result;
}

MaybeDirectHandle<Object> InternalizeJsonProperty(Handle<JSObject> holder,
                                                  Handle<String> key);

namespace {
template <typename Char>
JsonToken GetTokenForCharacter(Char c) {
  return V8_LIKELY(c <= unibrow::Latin1::kMaxChar) ? one_char_json_tokens[c]
                                                   : JsonToken::ILLEGAL;
}
}  // namespace

template <typename Char>
JsonToken JsonParser<Char>::peek() const {
  // Check that the next token was scanned before using peek().
  DCHECK_IMPLIES(!is_at_end(),
                 GetTokenForCharacter(CurrentCharacter()) == next_);
  return next_;
}

template <typename Char>
void JsonParser<Char>::GetNextNonWhitespaceToken() {
  JsonToken local_next = JsonToken::EOS;

  cursor_ = std::find_if(cursor_, end_, [&](Char c) {
    JsonToken current = GetTokenForCharacter(c);
    bool result = current != JsonToken::WHITESPACE;
    if (V8_LIKELY(result)) local_next = current;
    return result;
  });

  next_ = local_next;
}

template <typename Char>
base::uc32 JsonParser<Char>::ScanUnicodeCharacter() {
  base::uc32 value = 0;
  for (int i = 0; i < 4; i++) {
    int digit = base::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) {
  {
    DisallowGarbageCollection no_gc;
    const Char* start = cursor_;
    base::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 !IsDecimalDigit(c) || !TryAddArrayIndexChar(&index, c);
          });

          if (CurrentCharacter() == '"') {
            advance();
            // Record element information.
            cont->elements++;
            cont->max_index = std::max(cont->max_index, index);
            return JsonString(index);
          }

          if (CurrentCharacter() == '\\' && NextCharacter() == 'u') {
            base::uc32 c = ScanUnicodeCharacter();
            if (IsDecimalDigit(c) && TryAddArrayIndexChar(&index, c)) continue;
          }

          break;
        }
      }
    }
    // Reset cursor_ to start if the key is not an index.
    cursor_ = start;
  }
  return ScanJsonString(true);
}

class NamedPropertyValueIterator {
 public:
  NamedPropertyValueIterator(const JsonProperty* it, const JsonProperty* end)
      : it_(it), end_(end) {
    DCHECK_LE(it_, end_);
    DCHECK_IMPLIES(it_ != end_, !it_->string.is_index());
  }

  Tagged<Object> GetNext() {
    DCHECK_LT(it_, end_);
    Tagged<Object> value = *it_->value;
    do {
      it_++;
    } while (it_ != end_ && it_->string.is_index());
    return value;
  }

 private:
  // We need to store both the current iterator and the iterator end, since we
  // don't want to iterate past the end on operator++ if the last property is an
  // index property.
  const JsonProperty* it_;
  const JsonProperty* end_;
};

template <typename Char>
class JsonParser<Char>::NamedPropertyIterator {
 public:
  static constexpr bool kSupportsRawKeys = true;
  static constexpr bool kMayHaveDuplicateKeys = true;

  NamedPropertyIterator(JsonParser<Char>& parser, const JsonProperty* it,
                        const JsonProperty* end)
      : parser_(parser), it_(it), end_(end) {
    DCHECK_LE(it_, end_);
    while (it_ != end_ && it_->string.is_index()) {
      it_++;
    }
    start_ = it_;
  }

  void Advance() {
    DCHECK_LT(it_, end_);
    do {
      it_++;
    } while (it_ != end_ && it_->string.is_index());
  }

  bool Done() const {
    DCHECK_LE(it_, end_);
    return it_ == end_;
  }

  base::Vector<const Char> GetKeyChars() {
    return parser_.GetKeyChars(it_->string);
  }
  Handle<InternalizedString> GetKey(Handle<String> expected_key_hint) {
    return Cast<InternalizedString>(
        parser_.MakeString(it_->string, expected_key_hint));
  }
  Handle<Object> GetValue(bool will_revisit_value) {
    // Revisiting values is free, so we don't need to cache the value anywhere.
    return it_->value;
  }
  NamedPropertyValueIterator RevisitValues() {
    return NamedPropertyValueIterator(start_, end_);
  }

 private:
  JsonParser<Char>& parser_;

  const JsonProperty* start_;
  const JsonProperty* it_;
  const JsonProperty* end_;
};

template <typename Char>
template <bool should_track_json_source>
Handle<JSObject> JsonParser<Char>::BuildJsonObject(const JsonContinuation& cont,
                                                   DirectHandle<Map> feedback) {
  bool feedback_was_deprecated = false;
  if (!feedback.is_null() && feedback->is_deprecated()) {
    feedback = Map::Update(isolate_, feedback);
    feedback_was_deprecated = true;
  }
  size_t start = cont.index();
  DCHECK_LE(start, property_stack_.size());
  int length = static_cast<int>(property_stack_.size() - start);
  int named_length = length - cont.elements;
  DCHECK_LE(0, named_length);

  // Fast path for homogeneous object arrays:
  // If ParseJsonObjectProperties<FastIterableState::kJsonFast> matched all
  // property keys against the feedback map in exact sequence without any
  // trailing properties (fast_keys_matched == true) and the object contains no
  // indexed array elements (cont.elements == 0), we can bypass
  // JSDataObjectBuilder entirely.
  //
  // Note:
  // 1. If the original feedback map was deprecated, we must skip the fast path
  //    because fast_keys_matched was verified against the old map and the
  //    updated map may have a different descriptor layout or be a dictionary
  //    map.
  // 2. IsFastElementsKind(feedback->elements_kind()) is required because we
  //    initialize object->set_elements(empty_fixed_array()). An empty fixed
  //    array is a valid backing store for any fast elements kind when element
  //    capacity is 0, but is invalid for dictionary elements maps.
  if (cont.fast_keys_matched && cont.elements == 0 && !feedback.is_null() &&
      !feedback_was_deprecated &&
      IsFastElementsKind(feedback->elements_kind())) {
    // fast_keys_matched guarantees exact descriptor count alignment.
    DCHECK_EQ(length, feedback->NumberOfOwnDescriptors());
    bool fast_path_valid = true;
    DirectHandle<DescriptorArray> descriptors(feedback->instance_descriptors(),
                                              isolate_);

    // Verify that every parsed property value satisfies the representation and
    // field type expected by the feedback map's descriptors.
    uint32_t extra_heap_numbers_needed = 0;
    for (int i = 0; i < length; i++) {
      InternalIndex descriptor_index(i);
      PropertyDetails details = descriptors->GetDetails(descriptor_index);
      // Fast iterable keys (kJsonFast) are guaranteed to be in-object data
      // fields.
      DCHECK_EQ(details.kind(), PropertyKind::kData);
      DCHECK_EQ(details.location(), PropertyLocation::kField);
      Tagged<Object> value = *property_stack_[start + i].value;
      Representation rep = details.representation();
      if (!Object::FitsRepresentation(value, rep)) {
        fast_path_valid = false;
        break;
      }
      if (rep.IsHeapObject() &&
          !FieldType::NowContains(descriptors->GetFieldType(descriptor_index),
                                  value)) {
        fast_path_valid = false;
        break;
      }
      if (rep.IsDouble()) {
        if (IsSmi(value) || should_track_json_source) {
          extra_heap_numbers_needed++;
        }
      }
    }

    if (fast_path_valid) {
      // Create a folded mutable HeapNumber allocation area before allocating
      // the object -- this ensures that there is no allocation between the
      // object allocation and its initial fields being initialized, where the
      // verifier would see invalid double field state.
      FoldedMutableHeapNumberAllocation hn_allocation(
          isolate_, extra_heap_numbers_needed);

      Handle<JSObject> object = factory()->NewJSObjectFromMap(
          feedback, AllocationType::kYoung,
          DirectHandle<AllocationSite>::null(),
          NewJSObjectType::kNoEmbedderFieldsAndNoApiWrapper);

      DisallowGarbageCollection no_gc;
      Tagged<JSObject> raw_object = *object;
      ReadOnlyRoots roots(isolate_);

      raw_object->set_elements(roots.empty_fixed_array());

      FoldedMutableHeapNumberAllocator hn_allocator(isolate_, &hn_allocation,
                                                    no_gc);
      int current_property_offset = raw_object->GetInObjectPropertyOffset(0);
      for (int i = 0; i < length; i++) {
        Tagged<Object> value = *property_stack_[start + i].value;
        PropertyDetails details = descriptors->GetDetails(InternalIndex(i));
        if (details.representation().IsDouble()) {
          if (IsSmi(value)) {
            value = hn_allocator.AllocateNext(
                roots, Float64::FromMaybeNaN(Smi::ToInt(value)));
          } else if constexpr (should_track_json_source) {
            value = hn_allocator.AllocateNext(
                roots, Float64::FromMaybeNaN(Object::NumberValue(value)));
          }
        }
        FieldIndex index = FieldIndex::ForInObjectOffset(
            current_property_offset, FieldIndex::kTagged);
        raw_object->RawFastInobjectPropertyAtPut(index, value,
                                                 SKIP_WRITE_BARRIER);
        current_property_offset += kTaggedSize;
      }
      return object;
    }
  }

  Handle<FixedArrayBase> elements;
  ElementsKind elements_kind = HOLEY_ELEMENTS;

  // 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();
        DirectHandle<Object> value = property.value;
        NumberDictionary::UncheckedSet(isolate_, elms, index, value);
      }
      elms->SetInitialNumberOfElements(cont.elements);
      elms->UpdateMaxNumberKey(cont.max_index, Handle<JSObject>::null());
      elements_kind = DICTIONARY_ELEMENTS;
      elements = elms;
    } else {
      Handle<FixedArray> elms = factory()->NewFixedArrayWithHoles(
          cont.max_index + 1, AllocationType::kYoung);
      DisallowGarbageCollection no_gc;
      Tagged<FixedArray> raw_elements = *elms;

      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();
        DirectHandle<Object> value = property.value;
        // FixedArray is the most recent young allocation, so no write barrier
        // required.
        raw_elements->set(static_cast<int>(index), *value, SKIP_WRITE_BARRIER);
      }
      elements = elms;
    }
  } else {
    elements = factory()->empty_fixed_array();
  }

  // When tracking JSON source with a reviver, do not use mutable HeapNumbers.
  // In this mode, values are snapshotted at the beginning because the source is
  // only passed to the reviver if the reviver does not muck with the original
  // value. Mutable HeapNumbers would make the snapshot incorrect.
  JSDataObjectBuilder js_data_object_builder(
      isolate_, elements_kind, named_length, feedback,
      should_track_json_source
          ? JSDataObjectBuilder::kNormalHeapNumbers
          : JSDataObjectBuilder::kHeapNumbersGuaranteedUniquelyOwned);

  NamedPropertyIterator it(*this, property_stack_.begin() + start,
                           property_stack_.end());

  return js_data_object_builder.BuildFromIterator(it, elements);
}

template <typename Char>
Handle<Object> JsonParser<Char>::BuildJsonArray(size_t start) {
  const uint32_t length = static_cast<uint32_t>(element_stack_.size() - start);

  ElementsKind kind = PACKED_SMI_ELEMENTS;
  for (size_t i = start; i < element_stack_.size(); i++) {
    Tagged<Object> value = *element_stack_[i];
    if (IsHeapObject(value)) {
      if (IsHeapNumber(Cast<HeapObject>(value))) {
        kind = PACKED_DOUBLE_ELEMENTS;
      } else {
        kind = PACKED_ELEMENTS;
        break;
      }
    }
  }

  HandleScope inner_scope(isolate());
  Handle<FixedArrayBase> elements =
      (kind == PACKED_DOUBLE_ELEMENTS)
          ? FixedDoubleArray::New(
                isolate(), length,
                [this, start](uint32_t i) {
                  return Object::NumberValue(*element_stack_[start + i]);
                })
          : FixedArray::New(isolate(), length, [this, start](uint32_t i) {
              return *element_stack_[start + i];
            });
  return inner_scope.CloseAndEscape(
      factory()->NewJSArrayWithElements(elements, kind, length));
}

// Parse rawJSON value.
template <typename Char>
bool JsonParser<Char>::ParseRawJson() {
  if (is_at_end()) {
    isolate_->Throw(*isolate_->factory()->NewSyntaxError(
        MessageTemplate::kInvalidRawJsonValue));
    return false;
  }
  next_ = GetTokenForCharacter(*cursor_);
  switch (peek()) {
    case JsonToken::STRING:
      Consume(JsonToken::STRING);
      ScanJsonString(false);
      break;

    case JsonToken::NUMBER:
      ParseJsonNumber();
      break;

    case JsonToken::TRUE_LITERAL:
      ScanLiteral("true");
      break;

    case JsonToken::FALSE_LITERAL:
      ScanLiteral("false");
      break;

    case JsonToken::NULL_LITERAL:
      ScanLiteral("null");
      break;

    default:
      ReportUnexpectedCharacter(CurrentCharacter());
      return false;
  }
  if (isolate_->has_exception()) return false;
  if (cursor_ != end_) {
    isolate_->Throw(*isolate_->factory()->NewSyntaxError(
        MessageTemplate::kInvalidRawJsonValue));
    return false;
  }
  return true;
}

template <typename Char>
V8_INLINE MaybeHandle<Object> JsonParser<Char>::ParseJsonValueRecursive(
    Handle<Map> feedback) {
  GetNextNonWhitespaceToken();
  switch (peek()) {
    case JsonToken::NUMBER:
      return ParseJsonNumber();
    case JsonToken::STRING:
      Consume(JsonToken::STRING);
      return MakeString(ScanJsonString(false));

    case JsonToken::TRUE_LITERAL:
      ScanLiteral("true");
      return factory()->true_value();
    case JsonToken::FALSE_LITERAL:
      ScanLiteral("false");
      return factory()->false_value();
    case JsonToken::NULL_LITERAL:
      ScanLiteral("null");
      return factory()->null_value();

    case JsonToken::LBRACE:
      return ParseJsonObject(feedback);
    case JsonToken::LBRACK:
      return ParseJsonArray();

    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.
      return MaybeHandle<Object>();

    case JsonToken::WHITESPACE:
      UNREACHABLE();
  }
  UNREACHABLE();
}

template <typename Char>
bool JsonParser<Char>::FastKeyMatch(const uint8_t* key_chars,
                                    uint32_t key_length) {
  return key_length < remaining_chars() && *(cursor_ + key_length) == '"' &&
         CompareCharsEqual(key_chars, cursor_, key_length);
}

template <typename Char>
bool JsonParser<Char>::FastKeyMatch(const uint8_t* key_chars,
                                    uint32_t key_length,
                                    JsonString scanned_key) {
  return key_length == scanned_key.length() &&
         CompareCharsEqual(key_chars, chars_ + scanned_key.start(), key_length);
}

template <typename Char>
bool JsonParser<Char>::ParseJsonPropertyValue(const JsonString& key) {
  EXPECT_NEXT_RETURN_ON_ERROR(
      JsonToken::COLON,
      MessageTemplate::kJsonParseExpectedColonAfterPropertyName, false);
  Handle<Object> value;
  if (V8_UNLIKELY(!ParseJsonValueRecursive().ToHandle(&value))) return false;
  property_stack_.emplace_back(key, value);
  return true;
}

namespace {

const uint8_t* GetFastKeyChars(
    Isolate* isolate, Tagged<String> key, Tagged<Map> map,
    const DisallowGarbageCollection& no_gc V8_LIFETIME_BOUND) {
  DCHECK(InstanceTypeChecker::IsOneByteString(map));
#if V8_STATIC_ROOTS_BOOL
  ReadOnlyRoots roots(isolate);
  if (map == roots.internalized_one_byte_string_map()) {
    return Cast<SeqOneByteString>(key)->GetChars(no_gc);
  } else {
    DCHECK(map == roots.external_internalized_one_byte_string_map() ||
           map == roots.uncached_external_internalized_one_byte_string_map());
    return Cast<ExternalOneByteString>(key)->GetChars();
  }
#else
  InstanceType instance_type = map->instance_type();
  switch (instance_type) {
    case INTERNALIZED_ONE_BYTE_STRING_TYPE:
      return Cast<SeqOneByteString>(key)->GetChars(no_gc);
    case EXTERNAL_INTERNALIZED_ONE_BYTE_STRING_TYPE:
    case UNCACHED_EXTERNAL_INTERNALIZED_ONE_BYTE_STRING_TYPE:
      return Cast<ExternalOneByteString>(key)->GetChars();
    default:
      UNREACHABLE();
  }
#endif
}

}  // namespace

template <typename Char>
template <DescriptorArray::FastIterableState fast_iterable_state>
bool JsonParser<Char>::ParseJsonObjectProperties(
    JsonContinuation* cont, MessageTemplate first_token_msg,
    Handle<DescriptorArray> descriptors, uint16_t nof_descriptors) {
  using FastIterableState = DescriptorArray::FastIterableState;
  if constexpr (fast_iterable_state == FastIterableState::kJsonSlow) {
    do {
      EXPECT_NEXT_RETURN_ON_ERROR(JsonToken::STRING, first_token_msg, false);
      first_token_msg =
          MessageTemplate::kJsonParseExpectedDoubleQuotedPropertyName;
      JsonString key = ScanJsonPropertyKey(cont);
      if (V8_UNLIKELY(!ParseJsonPropertyValue(key))) return false;
    } while (Check<JsonToken::COMMA>());
  } else {
    DCHECK_GT(nof_descriptors, 0);
    InternalIndex idx{0};
    InternalIndex descriptors_end = InternalIndex{nof_descriptors};
    do {
      EXPECT_NEXT_RETURN_ON_ERROR(JsonToken::STRING, first_token_msg, false);
      first_token_msg =
          MessageTemplate::kJsonParseExpectedDoubleQuotedPropertyName;
      bool key_match;
      if constexpr (fast_iterable_state == FastIterableState::kJsonFast) {
        uint32_t key_length;
        {
          DisallowGarbageCollection no_gc;
          Tagged<String> expected_key = Cast<String>(descriptors->GetKey(idx));
          Tagged<Map> key_map = expected_key->map();
          // Fast iterable keys are guaranteed to be 1-byte.
          const uint8_t* expected_chars =
              GetFastKeyChars(isolate_, expected_key, key_map, no_gc);
          key_length = expected_key->length();
          key_match = FastKeyMatch(expected_chars, key_length);
        }
        if (V8_LIKELY(key_match)) {
          JsonString key =
              JsonString(position(), key_length, false, true, false);
          ++idx;
          cursor_ += key_length + 1 /* double quote */;
          if (V8_UNLIKELY(!ParseJsonPropertyValue(key))) return false;
        } else {
          JsonString key = ScanJsonPropertyKey(cont);
          if (!key.is_index()) {
            // Feedback doesn't match. Finish processing the current property
            // and continue in slow-path if we have more properties.
            if (V8_UNLIKELY(!ParseJsonPropertyValue(key))) return false;
            if (Check<JsonToken::COMMA>()) {
              return ParseJsonObjectProperties<FastIterableState::kJsonSlow>(
                  cont, first_token_msg, {}, nof_descriptors);
            }
            return true;
          }
          if (V8_UNLIKELY(!ParseJsonPropertyValue(key))) return false;
        }
      } else {
        DCHECK_EQ(fast_iterable_state, FastIterableState::kUnknown);
        JsonString key = ScanJsonPropertyKey(cont);
        // Indices don't participate in fast iterable key checks.
        if (key.is_index()) {
          if (V8_UNLIKELY(!ParseJsonPropertyValue(key))) return false;
          continue;
        }
        DCHECK_LT(idx, InternalIndex(descriptors->number_of_descriptors()));
        // Check if the key is fast iterable.
        // Some of the checks below are not relevant for the parser, but are
        // requirements for fast iterable keys in general (e.g. for
        // JSON.stringify).
        Tagged<Name> property_name = descriptors->GetKey(idx);
        bool is_slow = key.has_escape();
        // Check that the property is enumerable and located in field.
        PropertyDetails details = descriptors->GetDetails(idx);
        if (V8_UNLIKELY(details.IsDontEnum() ||
                        details.location() != PropertyLocation::kField)) {
          is_slow = true;
        }
        // Symbol property keys are slow.
        if (V8_UNLIKELY(IsSymbol(property_name))) {
          is_slow = true;
        }

        key_match = false;
        if (V8_LIKELY(!is_slow)) {
          DisallowGarbageCollection no_gc;
          // Property key is known to be fast so far, so it is guaranteed to
          // be a string.
          Tagged<String> expected_key = Cast<String>(property_name);
          Tagged<Map> key_map = expected_key->map();
          if (InstanceTypeChecker::IsTwoByteString(key_map)) {
            // Two-byte keys are slow.
            is_slow = true;
          } else {
            const uint8_t* expected_chars =
                GetFastKeyChars(isolate_, expected_key, key_map, no_gc);
            const uint32_t key_length = expected_key->length();
            key_match = FastKeyMatch(expected_chars, key_length, key);
          }
        }

        if (V8_UNLIKELY(is_slow)) {
          // The key is not fast iterable. Mark it as slow in the descriptor
          // array.
          descriptors->set_fast_iterable(FastIterableState::kJsonSlow);
        }

        // Finish parsing the property.
        if (V8_UNLIKELY(!ParseJsonPropertyValue(key))) return false;

        // If key is not fast iterable or doesn't match the feedback, we
        // continue on the slow-path.
        if (V8_UNLIKELY(is_slow || !key_match)) {
          if (Check<JsonToken::COMMA>()) {
            return ParseJsonObjectProperties<FastIterableState::kJsonSlow>(
                cont, first_token_msg, {}, nof_descriptors);
          }
          // No more properties to scan, we are done.
          return true;
        }
        ++idx;
      }
    } while (idx < descriptors_end && Check<JsonToken::COMMA>());
    // Additional, unknown properties. Scan them slow.
    if (Check<JsonToken::COMMA>()) {
      return ParseJsonObjectProperties<FastIterableState::kJsonSlow>(
          cont, first_token_msg, descriptors, nof_descriptors);
    }
    if constexpr (fast_iterable_state == FastIterableState::kJsonFast) {
      // If we matched every descriptor in the feedback map and there are no
      // trailing properties, signal to BuildJsonObject that keys matched the
      // map's layout in exact sequence.
      if (idx == descriptors_end) {
        cont->fast_keys_matched = true;
      }
    } else if constexpr (fast_iterable_state == FastIterableState::kUnknown) {
      // All properties are fast iterable. Update DescriptorArray flags.
      if (idx == InternalIndex(descriptors->number_of_descriptors())) {
        descriptors->set_fast_iterable_if(FastIterableState::kJsonFast,
                                          FastIterableState::kUnknown);
      }
    }
  }

  return true;
}

template <typename Char>
MaybeHandle<Object> JsonParser<Char>::ParseJsonObject(Handle<Map> feedback) {
  {
    StackLimitCheck check(isolate_);
    if (V8_UNLIKELY(check.HasOverflowed())) {
      return ParseJsonValue<false>();
    }
  }

  Consume(JsonToken::LBRACE);
  if (Check<JsonToken::RBRACE>()) {
    return factory()->NewJSObject(object_constructor_);
  }

  JsonContinuation cont(isolate_, JsonContinuation::kObjectProperty,
                        property_stack_.size());
  bool success;
  using FastIterableState = DescriptorArray::FastIterableState;
  const MessageTemplate first_token_msg =
      MessageTemplate::kJsonParseExpectedPropNameOrRBrace;
  if (!feedback.is_null()) {
    Handle<DescriptorArray> descriptors =
        handle(feedback->instance_descriptors(), isolate_);
    uint16_t nof_descriptors = feedback->NumberOfOwnDescriptors();
    if (nof_descriptors == 0) {
      // Empty descriptor array is technically fast iterable, but the fast
      // iteration expects at least 1 property.
      success = ParseJsonObjectProperties<FastIterableState::kJsonSlow>(
          &cont, first_token_msg, {}, nof_descriptors);
    } else {
      switch (descriptors->fast_iterable()) {
        case FastIterableState::kJsonFast:
          success = ParseJsonObjectProperties<FastIterableState::kJsonFast>(
              &cont, first_token_msg, descriptors, nof_descriptors);
          break;
        case FastIterableState::kJsonSlow:
          success = ParseJsonObjectProperties<FastIterableState::kJsonSlow>(
              &cont, first_token_msg, {}, nof_descriptors);
          break;
        case FastIterableState::kUnknown:
          success = ParseJsonObjectProperties<FastIterableState::kUnknown>(
              &cont, first_token_msg, descriptors, nof_descriptors);
          break;
      }
    }
  } else {
    success = ParseJsonObjectProperties<FastIterableState::kJsonSlow>(
        &cont, first_token_msg, {}, 0 /* unused */);
  }
  if (V8_UNLIKELY(!success)) {
    return {};
  }

  EXPECT_RETURN_ON_ERROR(JsonToken::RBRACE,
                         MessageTemplate::kJsonParseExpectedCommaOrRBrace, {});
  Handle<Object> result = BuildJsonObject<false>(cont, feedback);
  property_stack_.resize(cont.index());
  return cont.scope.CloseAndEscape(result);
}

template <typename Char>
MaybeHandle<Object> JsonParser<Char>::ParseJsonArray() {
  {
    StackLimitCheck check(isolate_);
    if (V8_UNLIKELY(check.HasOverflowed())) {
      return ParseJsonValue<false>();
    }
  }

  Consume(JsonToken::LBRACK);
  if (Check<JsonToken::RBRACK>()) {
    return factory()->NewJSArray(0, PACKED_SMI_ELEMENTS);
  }

  HandleScope handle_scope(isolate_);
  size_t start = element_stack_.size();

  // Avoid allocating HeapNumbers until we really need to.
  bool saw_double = false;
  bool success = false;
  DCHECK_EQ(double_elements_.size(), 0);
  DCHECK_EQ(smi_elements_.size(), 0);
  // GetNextNonWhitespaceToken was performed by Check(JsonToken::RBRACK).
  while (peek() == JsonToken::NUMBER) {
    double current_double;
    int current_smi;
    if (ParseJsonNumberAsDoubleOrSmi(&current_double, &current_smi)) {
      saw_double = true;
      double_elements_.push_back(current_double);
    } else {
      if (saw_double) {
        double_elements_.push_back(current_smi);
      } else {
        smi_elements_.push_back(current_smi);
      }
    }
    if (Check<JsonToken::COMMA>()) {
      GetNextNonWhitespaceToken();
      continue;
    } else {
      EXPECT_RETURN_ON_ERROR(JsonToken::RBRACK,
                             MessageTemplate::kJsonParseExpectedCommaOrRBrack,
                             {});
      success = true;
      break;
    }
  }

  if (success) {
    // We managed to parse the whole array either as Smis or doubles. Empty
    // arrays are handled above, so here we will have some elements.
    DCHECK(smi_elements_.size() != 0 || double_elements_.size() != 0);
    int length =
        static_cast<int>(smi_elements_.size() + double_elements_.size());
    Handle<JSArray> array;
    if (!saw_double) {
      array = factory()->NewJSArray(PACKED_SMI_ELEMENTS, length, length);
      DisallowGarbageCollection no_gc;
      Tagged<FixedArray> elements = Cast<FixedArray>(array->elements());
      for (int i = 0; i < length; i++) {
        elements->set(i, Smi::FromInt(smi_elements_[i]));
      }
    } else {
      array = factory()->NewJSArray(PACKED_DOUBLE_ELEMENTS, length, length);
      DisallowGarbageCollection no_gc;
      Tagged<FixedDoubleArray> elements =
          Cast<FixedDoubleArray>(array->elements());
      int i = 0;
      for (int element : smi_elements_) {
        elements->set(i++, element);
      }
      for (double element : double_elements_) {
        elements->set(i++, element);
      }
    }
    smi_elements_.resize(0);
    double_elements_.resize(0);
    return handle_scope.CloseAndEscape(array);
  }
  // Otherwise, we fell out of the while loop above because the next element
  // is not a number. Move the smi_elements_ and double_elements_ to
  // element_stack_ and continue parsing the next element.
  for (int element : smi_elements_) {
    element_stack_.emplace_back(handle(Smi::FromInt(element), isolate_));
  }
  smi_elements_.resize(0);
  for (double element : double_elements_) {
    element_stack_.emplace_back(factory()->NewNumber(element));
  }
  double_elements_.resize(0);

  Handle<Object> value;
  if (V8_UNLIKELY(!ParseJsonValueRecursive().ToHandle(&value))) return {};
  element_stack_.emplace_back(value);
  while (Check<JsonToken::COMMA>()) {
    Handle<Map> feedback;
    if (IsJSObject(*value)) {
      Tagged<Map> maybe_feedback = Cast<JSObject>(*value)->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 (V8_UNLIKELY(!ParseJsonValueRecursive(feedback).ToHandle(&value))) {
      return {};
    }
    element_stack_.emplace_back(value);
  }

  EXPECT_RETURN_ON_ERROR(JsonToken::RBRACK,
                         MessageTemplate::kJsonParseExpectedCommaOrRBrack, {});
  Handle<Object> result = BuildJsonArray(start);
  element_stack_.resize(start);
  return handle_scope.CloseAndEscape(result);
}

// Parse any JSON value.
template <typename Char>
template <bool should_track_json_source>
MaybeHandle<Object> JsonParser<Char>::ParseJsonValue() {
  std::vector<JsonContinuation> cont_stack;

  cont_stack.reserve(16);

  JsonContinuation cont(isolate_, JsonContinuation::kReturn, 0);

  Handle<Object> value;

  // When should_track_json_source is true, we use val_node to record current
  // JSON value's parse node.
  //
  // For primitive values, the val_node is the source string of the JSON value.
  //
  // For JSObject values, the val_node is an ObjectHashTable in which the key is
  // the property name and the first value is the property value's parse
  // node. The order in which properties are defined may be different from the
  // order in which properties are enumerated when calling
  // InternalizeJSONProperty for the JSObject value. E.g., the JSON source
  // string is '{"a": 1, "1": 2}', and the properties enumerate order is ["1",
  // "a"]. Moreover, properties may be defined repeatedly in the JSON string.
  // E.g., the JSON string is '{"a": 1, "a": 1}', and the properties enumerate
  // order is ["a"]. So we cannot use the FixedArray to record the properties's
  // parse node by the order in which properties are defined and we use a
  // ObjectHashTable here to record the property name and the property's parse
  // node. We then look up the property's parse node by the property name when
  // calling InternalizeJSONProperty. The second value associated with the key
  // is the property value's snapshot.
  //
  // For JSArray values, the val_node is a FixedArray containing the parse nodes
  // and snapshots of the elements.
  //
  // For information about snapshotting, see below.
  Handle<Object> val_node;
  // Record the start position and end position for the primitive values.
  uint32_t start_position;

  // Workaround for -Wunused-but-set-variable on old gcc versions (version < 8).
  USE(start_position);

  // element_val_node_stack is used to track all the elements's
  // parse nodes. And we use this to construct the JSArray's
  // parse node and value snapshot.
  SmallVector<Handle<Object>> element_val_node_stack;
  // property_val_node_stack is used to track all the property
  // value's parse nodes. And we use this to construct the
  // JSObject's parse node and value snapshot.
  SmallVector<Handle<Object>> property_val_node_stack;
  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) {
      GetNextNonWhitespaceToken();
      // The switch is immediately followed by 'break' so we can use 'break' to
      // break out of the loop, and 'continue' to continue the loop.

      if constexpr (should_track_json_source) {
        start_position = position();
      }
      switch (peek()) {
        case JsonToken::STRING:
          Consume(JsonToken::STRING);
          value = MakeString(ScanJsonString(false));
          if constexpr (should_track_json_source) {
            uint32_t end_position = position();
            val_node = isolate_->factory()->NewSubString(
                source_, start_position, end_position);
          }
          break;

        case JsonToken::NUMBER:
          value = ParseJsonNumber();
          if constexpr (should_track_json_source) {
            uint32_t end_position = position();
            val_node = isolate_->factory()->NewSubString(
                source_, start_position, end_position);
          }
          break;

        case JsonToken::LBRACE: {
          Consume(JsonToken::LBRACE);
          if (Check<JsonToken::RBRACE>()) {
            // TODO(verwaest): Directly use the map instead.
            value = factory()->NewJSObject(object_constructor_);
            if constexpr (should_track_json_source) {
              val_node = ObjectTwoHashTable::New(isolate_, 0);
            }
            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.
          // GetNextNonWhitespaceToken was already performed by
          // Check(JsonToken::RBRACE).
          EXPECT_RETURN_ON_ERROR(
              JsonToken::STRING,
              MessageTemplate::kJsonParseExpectedPropNameOrRBrace, {});
          property_stack_.emplace_back(ScanJsonPropertyKey(&cont));
          if constexpr (should_track_json_source) {
            property_val_node_stack.emplace_back(Handle<Object>());
          }

          EXPECT_NEXT_RETURN_ON_ERROR(
              JsonToken::COLON,
              MessageTemplate::kJsonParseExpectedColonAfterPropertyName, {});

          // 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);
            if constexpr (should_track_json_source) {
              val_node = factory()->NewFixedArray(0);
            }
            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();
          if constexpr (should_track_json_source) {
            val_node = isolate_->factory()->true_string();
          }
          break;

        case JsonToken::FALSE_LITERAL:
          ScanLiteral("false");
          value = factory()->false_value();
          if constexpr (should_track_json_source) {
            val_node = isolate_->factory()->false_string();
          }
          break;

        case JsonToken::NULL_LITERAL:
          ScanLiteral("null");
          value = factory()->null_value();
          if constexpr (should_track_json_source) {
            val_node = isolate_->factory()->null_string();
          }
          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:
          if constexpr (should_track_json_source) {
            DCHECK(!val_node.is_null());
            Tagged<Object> raw_value = *value;
            parsed_val_node_ = cont.scope.CloseAndEscape(val_node);
            return cont.scope.CloseAndEscape(handle(raw_value, isolate_));
          } else {
            return cont.scope.CloseAndEscape(value);
          }

        case JsonContinuation::kObjectProperty: {
          // Store the previous property value into its property info.
          property_stack_.back().value = value;
          if constexpr (should_track_json_source) {
            property_val_node_stack.back() = val_node;
          }

          if (V8_LIKELY(Check<JsonToken::COMMA>())) {
            // Parse the property key.
            EXPECT_NEXT_RETURN_ON_ERROR(
                JsonToken::STRING,
                MessageTemplate::kJsonParseExpectedDoubleQuotedPropertyName,
                {});

            property_stack_.emplace_back(ScanJsonPropertyKey(&cont));
            if constexpr (should_track_json_source) {
              property_val_node_stack.emplace_back(Handle<Object>());
            }
            EXPECT_NEXT_RETURN_ON_ERROR(
                JsonToken::COLON,
                MessageTemplate::kJsonParseExpectedColonAfterPropertyName, {});

            // 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() &&
              IsJSObject(*element_stack_.back())) {
            Tagged<Map> maybe_feedback =
                Cast<JSObject>(*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_);
            }
          }
          value = BuildJsonObject<should_track_json_source>(cont, feedback);
          EXPECT_RETURN_ON_ERROR(
              JsonToken::RBRACE,
              MessageTemplate::kJsonParseExpectedCommaOrRBrace, {});
          // Return the object.
          if constexpr (should_track_json_source) {
            size_t start = cont.index();
            int num_properties =
                static_cast<int>(property_stack_.size() - start);
            Handle<ObjectTwoHashTable> table =
                ObjectTwoHashTable::New(isolate(), num_properties);
            for (int i = 0; i < num_properties; i++) {
              const JsonProperty& property = property_stack_[start + i];
              DirectHandle<Object> property_val_node =
                  property_val_node_stack[start + i];
              DirectHandle<Object> property_snapshot = property.value;
              DirectHandle<String> key;
              if (property.string.is_index()) {
                key = factory()->Uint32ToString(property.string.index());
              } else {
                key = MakeString(property.string);
              }
              table = ObjectTwoHashTable::Put(
                  isolate(), table, key,
                  {property_val_node, property_snapshot});
            }
            property_val_node_stack.resize(cont.index());
            DisallowGarbageCollection no_gc;
            Tagged<ObjectTwoHashTable> raw_table = *table;
            value = cont.scope.CloseAndEscape(value);
            val_node = cont.scope.CloseAndEscape(handle(raw_table, isolate_));
          } else {
            value = cont.scope.CloseAndEscape(value);
          }
          property_stack_.resize(cont.index());

          // 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);
          if constexpr (should_track_json_source) {
            element_val_node_stack.emplace_back(val_node);
          }
          // Break to start producing the subsequent element value.
          if (V8_LIKELY(Check<JsonToken::COMMA>())) break;

          value = BuildJsonArray(cont.index());
          EXPECT_RETURN_ON_ERROR(
              JsonToken::RBRACK,
              MessageTemplate::kJsonParseExpectedCommaOrRBrack, {});
          // Return the array.
          if constexpr (should_track_json_source) {
            size_t start = cont.index();
            int num_elements = static_cast<int>(element_stack_.size() - start);
            DirectHandle<FixedArray> val_node_and_snapshot_array =
                factory()->NewFixedArray(num_elements * 2);
            DisallowGarbageCollection no_gc;
            Tagged<FixedArray> raw_val_node_and_snapshot_array =
                *val_node_and_snapshot_array;
            for (int i = 0; i < num_elements; i++) {
              raw_val_node_and_snapshot_array->set(
                  i * 2, *element_val_node_stack[start + i]);
              raw_val_node_and_snapshot_array->set(i * 2 + 1,
                                                   *element_stack_[start + i]);
            }
            element_val_node_stack.resize(cont.index());
            value = cont.scope.CloseAndEscape(value);
            val_node = cont.scope.CloseAndEscape(
                handle(raw_val_node_and_snapshot_array, isolate_));
          } else {
            value = cont.scope.CloseAndEscape(value);
          }
          element_stack_.resize(cont.index());
          // 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 double_number;
  int smi_number;
  if (ParseJsonNumberAsDoubleOrSmi(&double_number, &smi_number)) {
    return factory()->NewHeapNumber(double_number);
  }
  return handle(Smi::FromInt(smi_number), isolate_);
}

template <typename Char>
bool JsonParser<Char>::ParseJsonNumberAsDoubleOrSmi(double* result_double,
                                                    int* result_smi) {
  int sign = 1;

  {
    const Char* start = cursor_;
    DisallowGarbageCollection no_gc;

    base::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))) {
          AllowGarbageCollection allow_before_exception;
          ReportUnexpectedToken(JsonToken::NUMBER);
          *result_smi = 0;
          return false;
        }
      } else if (sign > 0) {
        *result_smi = 0;
        return false;
      }
    } else {
      const Char* smi_start = cursor_;
      static_assert(Smi::IsValid(-999999999));
      static_assert(Smi::IsValid(999999999));
      const int kMaxSmiLength = 9;
      int32_t i = 0;
      const Char* stop = cursor_ + kMaxSmiLength;
      if (stop > end_) stop = end_;
      while (cursor_ < stop && IsDecimalDigit(*cursor_)) {
        i = (i * 10) + ((*cursor_) - '0');
        cursor_++;
      }
      if (V8_UNLIKELY(smi_start == cursor_)) {
        AllowGarbageCollection allow_before_exception;
        ReportUnexpectedToken(
            JsonToken::ILLEGAL,
            MessageTemplate::kJsonParseNoNumberAfterMinusSign);
        *result_smi = 0;
        return false;
      }
      c = CurrentCharacter();
      if (!base::IsInRange(c, 0,
                           static_cast<int32_t>(unibrow::Latin1::kMaxChar)) ||
          !IsNumberPart(character_json_scan_flags[c])) {
        // Smi.
        // TODO(verwaest): Cache?
        *result_smi = i * sign;
        return false;
      }
      AdvanceToNonDecimal();
    }

    if (CurrentCharacter() == '.') {
      c = NextCharacter();
      if (!IsDecimalDigit(c)) {
        AllowGarbageCollection allow_before_exception;
        ReportUnexpectedToken(
            JsonToken::ILLEGAL,
            MessageTemplate::kJsonParseUnterminatedFractionalNumber);
        *result_smi = 0;
        return false;
      }
      AdvanceToNonDecimal();
    }

    if (AsciiAlphaToLower(CurrentCharacter()) == 'e') {
      c = NextCharacter();
      if (c == '-' || c == '+') c = NextCharacter();
      if (!IsDecimalDigit(c)) {
        AllowGarbageCollection allow_before_exception;
        ReportUnexpectedToken(
            JsonToken::ILLEGAL,
            MessageTemplate::kJsonParseExponentPartMissingNumber);
        *result_smi = 0;
        return false;
      }
      AdvanceToNonDecimal();
    }

    base::Vector<const Char> chars(start, cursor_ - start);
    *result_double =
        StringToDouble(chars,
                       NO_CONVERSION_FLAG,  // Hex, octal or trailing junk.
                       std::numeric_limits<double>::quiet_NaN());
    DCHECK(!std::isnan(*result_double));

    // The result might still be a smi even if it has a decimal part.
    return !DoubleToSmiInteger(*result_double, result_smi);
  }
}

namespace {

template <typename Char>
bool Matches(base::Vector<const Char> chars, DirectHandle<String> string) {
  DCHECK(!string.is_null());
  return string->IsEqualTo(chars);
}

}  // 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;
  {
    DisallowGarbageCollection 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;

    base::Vector<const SinkChar> data(dest, string.length());
    if (!hint.is_null() && Matches(data, hint)) return hint;
  }

  DCHECK_EQ(intermediate->length(), string.length());
  return factory()->InternalizeString(intermediate);
}

template <typename Char>
Handle<String> JsonParser<Char>::MakeString(const JsonString& string,
                                            Handle<String> hint) {
  if (string.length() == 0) return factory()->empty_string();
  if (string.length() == 1) {
    uint16_t first_char;
    if (!string.has_escape()) {
      first_char = chars_[string.start()];
    } else {
      DecodeString(&first_char, string.start(), 1);
    }
    return factory()->LookupSingleCharacterStringFromCode(first_char);
  }

  if (!string.has_escape()) {
    bool should_internalize = string.internalize();
    // For non-escaped strings, try to internalize the following candidates:
    // 1. Property keys (string.internalize() is true in that case).
    // 2. Short one-byte strings. If we have a budget for heuristic,
    //    we can consider inserting them. Otherwise, at least try to reuse
    //    existing internalized strings without inserting new ones.
    constexpr int kMaxInternalizedStringValueLength = 10;
    if (!should_internalize && sizeof(Char) == 1 &&
        string.length() < kMaxInternalizedStringValueLength) {
      if (remaining_heuristic_internalizations_ > 0) {
        --remaining_heuristic_internalizations_;
        should_internalize = true;
      } else {
        if (chars_may_relocate_) {
          SeqSubStringKey<SeqOneByteString> key(
              isolate_, Cast<SeqOneByteString>(source_), string.start(),
              string.length(), string.needs_conversion());
          auto existing =
              isolate_->string_table()->TryLookupKey(isolate_, &key);
          if (existing) return handle(*existing.value(), isolate_);
        } else {
          base::Vector<const Char> chars(chars_ + string.start(),
                                         string.length());
          SequentialStringKey<Char> key(chars, HashSeed(isolate_),
                                        string.needs_conversion());
          auto existing =
              isolate_->string_table()->TryLookupKey(isolate_, &key);
          if (existing) return handle(*existing.value(), isolate_);
        }
      }
    }
    if (should_internalize) {
      if (!hint.is_null()) {
        base::Vector<const Char> data(chars_ + string.start(), string.length());
        if (Matches(data, hint)) return hint;
      }
      if (chars_may_relocate_) {
        return factory()->InternalizeSubString(Cast<SeqString>(source_),
                                               string.start(), string.length(),
                                               string.needs_conversion());
      }
      base::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, uint32_t start,
                                    uint32_t length) {
  const Char* cursor = chars_ + start;
  while (length > 0) {
    // Copy everything until the first escape character
    const Char* backslash_pos = std::find(cursor, cursor + length, '\\');
    size_t to_copy = backslash_pos - cursor;
    std::copy_n(cursor, to_copy, sink);
    length -= to_copy;
    cursor += to_copy;
    sink += to_copy;

    if (length == 0) return;

    cursor++;

    switch (GetEscapeKind(character_json_scan_flags[*cursor])) {
      case EscapeKind::kSelf:
        *sink++ = *cursor;
        length--;
        break;

      case EscapeKind::kBackspace:
        *sink++ = '\x08';
        length--;
        break;

      case EscapeKind::kTab:
        *sink++ = '\x09';
        length--;
        break;

      case EscapeKind::kNewLine:
        *sink++ = '\x0A';
        length--;
        break;

      case EscapeKind::kFormFeed:
        *sink++ = '\x0C';
        length--;
        break;

      case EscapeKind::kCarriageReturn:
        *sink++ = '\x0D';
        length--;
        break;

      case EscapeKind::kUnicode: {
        base::uc32 value = 0;
        for (int i = 0; i < 4; i++) {
          value = value * 16 + base::HexValue(*++cursor);
        }
        if (value <=
            static_cast<base::uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
          *sink++ = value;
          length--;
        } else {
          SBXCHECK_GE(length, 2);
          *sink++ = unibrow::Utf16::LeadSurrogate(value);
          *sink++ = unibrow::Utf16::TrailSurrogate(value);
          length -= 2;
        }
        break;
      }

      case EscapeKind::kIllegal:
        UNREACHABLE();
    }
    cursor++;
  }
}

template <typename Char>
JsonString JsonParser<Char>::ScanJsonString(bool needs_internalization) {
  namespace hw = hwy::HWY_NAMESPACE;

  DisallowGarbageCollection no_gc;
  uint32_t start = position();
  uint32_t offset = start;
  bool has_escape = false;
  base::uc32 bits = 0;

  // SIMD constants for one-byte string scanning, hoisted out of the loop.
  // Only used in the sizeof(Char)==1 path; dead-store-eliminated for two-byte.
  [[maybe_unused]] hw::FixedTag<uint8_t, 16> tag;
  [[maybe_unused]] const size_t stride = hw::Lanes(tag);
  [[maybe_unused]] const auto mask_0x20 = hw::Set(tag, 0x20);
  [[maybe_unused]] const auto mask_quote = hw::Set(tag, '"');
  [[maybe_unused]] const auto mask_backslash = hw::Set(tag, '\\');

  while (true) {
    if constexpr (sizeof(Char) == 1) {
      // SIMD fast path: scan 16 bytes at a time looking for characters that
      // may terminate a JSON string: '"', '\', or control characters (< 0x20).
      for (; cursor_ + (stride - 1) < end_; cursor_ += stride) {
        const auto input =
            hw::LoadU(tag, reinterpret_cast<const uint8_t*>(cursor_));
        // TODO(floitsch): use operators for the comparisons when they are
        // available on RISC-V.
        const auto result =
            hw::Or(hw::Or(hw::Lt(input, mask_0x20), hw::Eq(input, mask_quote)),
                   hw::Eq(input, mask_backslash));
        if (V8_LIKELY(hw::AllFalse(tag, result))) continue;
        cursor_ += hw::FindKnownFirstTrue(tag, result);
        break;
      }
      // Scalar fallback for remaining tail bytes after the last full SIMD
      // block. When the SIMD loop found a match, *cursor_ is already a
      // special character and this immediately breaks.
      for (; cursor_ < end_; ++cursor_) {
        if (MayTerminateJsonString(character_json_scan_flags[*cursor_])) break;
      }
    } else {
      cursor_ = std::find_if(cursor_, end_, [&bits](Char c) {
        if (V8_UNLIKELY(c > unibrow::Latin1::kMaxChar)) {
          bits |= c;
          return false;
        }
        return MayTerminateJsonString(character_json_scan_flags[c]);
      });
    }

    if (V8_UNLIKELY(is_at_end())) {
      AllowGarbageCollection allow_before_exception;
      ReportUnexpectedToken(JsonToken::ILLEGAL,
                            MessageTemplate::kJsonParseUnterminatedString);
      break;
    }

    if (*cursor_ == '"') {
      uint32_t end = position();
      advance();
      uint32_t 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;
      base::uc32 c = NextCharacter();
      if (V8_UNLIKELY(!base::IsInRange(
              c, 0, static_cast<int32_t>(unibrow::Latin1::kMaxChar)))) {
        AllowGarbageCollection 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: {
          base::uc32 value = ScanUnicodeCharacter();
          if (value == kInvalidUnicodeCharacter) {
            AllowGarbageCollection allow_before_exception;
            ReportUnexpectedToken(JsonToken::ILLEGAL,
                                  MessageTemplate::kJsonParseBadUnicodeEscape);
            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<base::uc32>(
                                     unibrow::Utf16::kMaxNonSurrogateCharCode));
          break;
        }

        case EscapeKind::kIllegal:
          AllowGarbageCollection allow_before_exception;
          ReportUnexpectedToken(JsonToken::ILLEGAL,
                                MessageTemplate::kJsonParseBadEscapedCharacter);
          return JsonString();
      }

      advance();
      continue;
    }

    DCHECK_LT(*cursor_, 0x20);
    AllowGarbageCollection allow_before_exception;
    ReportUnexpectedToken(JsonToken::ILLEGAL,
                          MessageTemplate::kJsonParseBadControlCharacter);
    break;
  }

  return JsonString();
}

template <typename Char>
MaybeHandle<Object> JsonParser<Char>::Parse(
    Isolate* isolate, Handle<String> source, Handle<Object> reviver,
    std::optional<ScriptDetails> script_details) {
  HighAllocationThroughputScope high_throughput_scope(V8::GetCurrentPlatform());
  bool collect_source_strings = IsCallable(*reviver);
  if (IsJSFunction(*reviver)) {
    Handle<SharedFunctionInfo> reviver_sfi(Cast<JSFunction>(*reviver)->shared(),
                                           isolate);
    if (reviver_sfi->CanOnlyAccessFixedFormalParameters() &&
        reviver_sfi->internal_formal_parameter_count_without_receiver() < 3) {
      // The reviver function has only one or two arguments, so we don't need
      // to collect the information on the source for each primitive value.
      //
      // Note that on the first run, we don't get here because the optimizers
      // have not set enough information on the shared function info.  So we
      // will do the slow JSON parsing initially until the compiler has
      // optimized more.
      collect_source_strings = false;
    }
  }
  Handle<Object> result;
  MaybeHandle<Object> val_node;
  {
    JsonParser parser(isolate, source, script_details);
    ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
                               parser.ParseJson(collect_source_strings));
    val_node = parser.parsed_val_node_;
    if (v8_flags.trace_json_parse_internalization) {
      PrintF(
          "[JSON.parse internalization] source length=%d "
          "remaining budget for heuristic-based string internalization=%u\n",
          source->length(), parser.remaining_heuristic_internalizations_);
    }
  }
  if (IsCallable(*reviver)) {
    return JsonParseInternalizer::Internalize(isolate, result, reviver, source,
                                              val_node, collect_source_strings);
  }
  return result;
}

// Explicit instantiation.
template class JsonParser<uint8_t>;
template class JsonParser<uint16_t>;

}  // namespace internal
}  // namespace v8