// Copyright 2015 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/objects/objects.h"

#include <algorithm>
#include <cmath>
#include <optional>
#include <sstream>
#include <vector>

#include "src/api/api-arguments-inl.h"
#include "src/api/api-natives.h"
#include "src/ast/scopes.h"
#include "src/base/bits.h"
#include "src/base/logging.h"
#include "src/base/overflowing-math.h"
#include "src/builtins/accessors.h"
#include "src/builtins/builtins.h"
#include "src/codegen/source-position-table.h"
#include "src/common/globals.h"
#include "src/common/message-template.h"
#include "src/debug/debug.h"
#include "src/execution/execution.h"
#include "src/execution/frames-inl.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/isolate-utils-inl.h"
#include "src/execution/isolate-utils.h"
#include "src/execution/microtask-queue.h"
#include "src/execution/protectors-inl.h"
#include "src/heap/factory-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/heap/read-only-heap.h"
#include "src/logging/counters.h"
#include "src/logging/log.h"
#include "src/objects/allocation-site-inl.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/arguments-inl.h"
#include "src/objects/bigint.h"
#include "src/objects/call-site-info-inl.h"
#include "src/objects/cell-inl.h"
#include "src/objects/code-inl.h"
#include "src/objects/compilation-cache-table-inl.h"
#include "src/objects/debug-objects-inl.h"
#include "src/objects/dictionary.h"
#include "src/objects/elements.h"
#include "src/objects/embedder-data-array-inl.h"
#include "src/objects/field-index-inl.h"
#include "src/objects/field-index.h"
#include "src/objects/field-type.h"
#include "src/objects/free-space-inl.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/heap-object-inl.h"
#include "src/objects/instance-type.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-collection-inl.h"
#include "src/objects/js-disposable-stack-inl.h"
#include "src/objects/js-generator-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/js-weak-refs-inl.h"
#include "src/objects/keys.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/lookup-inl.h"
#include "src/objects/map-inl.h"
#include "src/objects/map.h"
#include "src/objects/megadom-handler-inl.h"
#include "src/objects/microtask-inl.h"
#include "src/objects/module-inl.h"
#include "src/objects/objects-body-descriptors-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/promise-inl.h"
#include "src/objects/promise.h"
#include "src/objects/property-descriptor-object-inl.h"
#include "src/objects/property-descriptor.h"
#include "src/objects/property-details.h"
#include "src/objects/slots-atomic-inl.h"
#include "src/objects/string-set-inl.h"
#include "src/objects/struct-inl.h"
#include "src/objects/template-objects-inl.h"
#include "src/objects/transitions-inl.h"
#include "src/parsing/preparse-data.h"
#include "src/regexp/regexp.h"
#include "src/roots/roots.h"
#include "src/snapshot/deserializer.h"
#include "src/strings/string-builder-inl.h"
#include "src/strings/string-stream.h"
#include "src/strings/unicode-inl.h"
#include "src/utils/identity-map.h"
#include "src/utils/ostreams.h"
#include "src/utils/utils-inl.h"

#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-objects.h"
#endif  // V8_ENABLE_WEBASSEMBLY

#ifdef V8_INTL_SUPPORT
#include "src/objects/js-break-iterator.h"
#include "src/objects/js-collator.h"
#include "src/objects/js-date-time-format.h"
#include "src/objects/js-list-format.h"
#include "src/objects/js-locale.h"
#include "src/objects/js-number-format.h"
#include "src/objects/js-plural-rules.h"
#include "src/objects/js-relative-time-format.h"
#include "src/objects/js-segment-iterator.h"
#include "src/objects/js-segmenter.h"
#include "src/objects/js-segments.h"
#endif  // V8_INTL_SUPPORT

namespace v8::internal {

ShouldThrow GetShouldThrow(Isolate* isolate, Maybe<ShouldThrow> should_throw) {
  if (should_throw.IsJust()) return should_throw.FromJust();

  LanguageMode mode = isolate->context()->scope_info()->language_mode();
  if (mode == LanguageMode::kStrict) return kThrowOnError;

  for (StackFrameIterator it(isolate, isolate->thread_local_top()); !it.done();
       it.Advance()) {
    if (!it.frame()->is_javascript()) continue;

    // Get the language mode from closure.
    JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(it.frame());
    std::vector<Tagged<SharedFunctionInfo>> functions;
    js_frame->GetFunctions(&functions);
    LanguageMode closure_language_mode = functions.back()->language_mode();
    if (closure_language_mode > mode) {
      mode = closure_language_mode;
    }
    break;
  }

  return is_sloppy(mode) ? kDontThrow : kThrowOnError;
}

bool ComparisonResultToBool(Operation op, ComparisonResult result) {
  switch (op) {
    case Operation::kLessThan:
      return result == ComparisonResult::kLessThan;
    case Operation::kLessThanOrEqual:
      return result == ComparisonResult::kLessThan ||
             result == ComparisonResult::kEqual;
    case Operation::kGreaterThan:
      return result == ComparisonResult::kGreaterThan;
    case Operation::kGreaterThanOrEqual:
      return result == ComparisonResult::kGreaterThan ||
             result == ComparisonResult::kEqual;
    default:
      break;
  }
  UNREACHABLE();
}

std::string ToString(InstanceType instance_type) {
  if (InstanceTypeChecker::IsJSApiObject(instance_type)) {
    std::stringstream ss;
    ss << "[api object] "
       << static_cast<int16_t>(instance_type) -
              i::Internals::kFirstJSApiObjectType;
    return ss.str();
  }
  switch (instance_type) {
#define WRITE_TYPE(TYPE) \
  case TYPE:             \
    return #TYPE;
    INSTANCE_TYPE_LIST(WRITE_TYPE)
#undef WRITE_TYPE
  }

  std::stringstream ss;
  ss << "[unknown instance type " << static_cast<int16_t>(instance_type) << "]";
  return ss.str();
}

std::ostream& operator<<(std::ostream& os, InstanceType instance_type) {
  return os << ToString(instance_type);
}

std::ostream& operator<<(std::ostream& os, PropertyCellType type) {
  switch (type) {
    case PropertyCellType::kUndefined:
      return os << "Undefined";
    case PropertyCellType::kConstant:
      return os << "Constant";
    case PropertyCellType::kConstantType:
      return os << "ConstantType";
    case PropertyCellType::kMutable:
      return os << "Mutable";
    case PropertyCellType::kInTransition:
      return os << "InTransition";
  }
  UNREACHABLE();
}

// static
DirectHandle<FieldType> Object::OptimalType(Tagged<Object> obj,
                                            Isolate* isolate,
                                            Representation representation) {
  if (representation.IsNone()) return FieldType::None(isolate);
  if (v8_flags.track_field_types) {
    if (representation.IsHeapObject() && IsHeapObject(obj)) {
      // We can track only JavaScript objects with stable maps.
      DirectHandle<Map> map(Cast<HeapObject>(obj)->map(), isolate);
      if (map->is_stable() && IsJSReceiverMap(*map)) {
        return FieldType::Class(map, isolate);
      }
    }
  }
  return FieldType::Any(isolate);
}

Handle<UnionOf<JSAny, Hole>> Object::NewStorageFor(
    Isolate* isolate, Handle<UnionOf<JSAny, Hole>> object,
    Representation representation) {
  if (!representation.IsDouble()) return object;
  Handle<HeapNumber> result = isolate->factory()->NewHeapNumberWithHoleNaN();
  if (IsUninitializedHole(*object, isolate)) {
    result->set_value_as_bits(kHoleNanInt64);
  } else if (IsHeapNumber(*object)) {
    // Ensure that all bits of the double value are preserved.
    result->set_value_as_bits(Cast<HeapNumber>(*object)->value_as_bits());
  } else {
    result->set_value(Cast<Smi>(*object).value());
  }
  return result;
}

template <AllocationType allocation_type, typename IsolateT>
Handle<JSAny> Object::WrapForRead(IsolateT* isolate, Handle<JSAny> object,
                                  Representation representation) {
  DCHECK(!IsUninitializedHole(*object, isolate));
  if (!representation.IsDouble()) {
    DCHECK(Object::FitsRepresentation(*object, representation));
    return object;
  }
  return isolate->factory()->template NewHeapNumberFromBits<allocation_type>(
      Cast<HeapNumber>(*object)->value_as_bits());
}

template Handle<JSAny> Object::WrapForRead<AllocationType::kYoung>(
    Isolate* isolate, Handle<JSAny> object, Representation representation);
template Handle<JSAny> Object::WrapForRead<AllocationType::kOld>(
    LocalIsolate* isolate, Handle<JSAny> object, Representation representation);

MaybeHandle<JSReceiver> Object::ToObjectImpl(Isolate* isolate,
                                             DirectHandle<Object> object,
                                             const char* method_name) {
  DCHECK(!IsJSReceiver(*object));  // Use ToObject() for fast path.
  DirectHandle<Context> native_context = isolate->native_context();
  DirectHandle<JSFunction> constructor;
  if (IsSmi(*object)) {
    constructor = direct_handle(native_context->number_function(), isolate);
  } else {
    int constructor_function_index =
        Cast<HeapObject>(object)->map()->GetConstructorFunctionIndex();
    if (constructor_function_index == Map::kNoConstructorFunctionIndex) {
      if (method_name != nullptr) {
        THROW_NEW_ERROR(
            isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
                                  isolate->factory()->NewStringFromAsciiChecked(
                                      method_name)));
      }
      THROW_NEW_ERROR(isolate,
                      NewTypeError(MessageTemplate::kUndefinedOrNullToObject));
    }
    constructor = direct_handle(
        Cast<JSFunction>(native_context->GetNoCell(constructor_function_index)),
        isolate);
  }
  Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
  Cast<JSPrimitiveWrapper>(result)->set_value(Cast<JSAny>(*object));
  return result;
}

// ES6 section 9.2.1.2, OrdinaryCallBindThis for sloppy callee.
// static
MaybeDirectHandle<JSReceiver> Object::ConvertReceiver(
    Isolate* isolate, DirectHandle<Object> object) {
  if (IsJSReceiver(*object)) return Cast<JSReceiver>(object);
  if (IsNullOrUndefined(*object, isolate)) {
    return isolate->global_proxy();
  }
  return Object::ToObject(isolate, object);
}

// static

template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Number>::MaybeType Object::ConvertToNumber(
    Isolate* isolate, HandleType<Object> input) {
  while (true) {
    if (IsNumber(*input)) {
      return Cast<Number>(input);
    }
    if (IsString(*input)) {
      return String::ToNumber(isolate, Cast<String>(input));
    }
    if (IsOddball(*input)) {
      return Oddball::ToNumber(isolate, Cast<Oddball>(input));
    }
    if (IsSymbol(*input)) {
      THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber));
    }
    if (IsBigInt(*input)) {
      THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kBigIntToNumber));
    }
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, input,
        JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(input),
                                ToPrimitiveHint::kNumber));
  }
}

template V8_EXPORT_PRIVATE MaybeDirectHandle<Number> Object::ConvertToNumber(
    Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Number> Object::ConvertToNumber(
    Isolate* isolate, IndirectHandle<Object> input);

// static
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Numeric>::MaybeType Object::ConvertToNumeric(
    Isolate* isolate, HandleType<Object> input) {
  while (true) {
    if (IsNumber(*input)) {
      return Cast<Number>(input);
    }
    if (IsString(*input)) {
      return String::ToNumber(isolate, Cast<String>(input));
    }
    if (IsOddball(*input)) {
      return Oddball::ToNumber(isolate, Cast<Oddball>(input));
    }
    if (IsSymbol(*input)) {
      THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber));
    }
    if (IsBigInt(*input)) {
      return Cast<BigInt>(input);
    }
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, input,
        JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(input),
                                ToPrimitiveHint::kNumber));
  }
}

template V8_EXPORT_PRIVATE MaybeDirectHandle<Numeric> Object::ConvertToNumeric(
    Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Numeric>
Object::ConvertToNumeric(Isolate* isolate, IndirectHandle<Object> input);

// static
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Number>::MaybeType Object::ConvertToInteger(
    Isolate* isolate, HandleType<Object> input) {
  ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ConvertToNumber(isolate, input));
  if (IsSmi(*input)) return Cast<Smi>(input);
  return isolate->factory()->NewNumber(
      DoubleToInteger(Cast<HeapNumber>(*input)->value()));
}

template V8_EXPORT_PRIVATE MaybeDirectHandle<Number> Object::ConvertToInteger(
    Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Number> Object::ConvertToInteger(
    Isolate* isolate, IndirectHandle<Object> input);

// static
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Number>::MaybeType Object::ConvertToInt32(
    Isolate* isolate, HandleType<Object> input) {
  ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ConvertToNumber(isolate, input));
  if (IsSmi(*input)) return Cast<Smi>(input);
  return isolate->factory()->NewNumberFromInt(
      DoubleToInt32(Cast<HeapNumber>(*input)->value()));
}

template MaybeDirectHandle<Number> Object::ConvertToInt32(
    Isolate* isolate, DirectHandle<Object> input);
template MaybeIndirectHandle<Number> Object::ConvertToInt32(
    Isolate* isolate, IndirectHandle<Object> input);

// static
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Number>::MaybeType Object::ConvertToUint32(
    Isolate* isolate, HandleType<Object> input) {
  ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ConvertToNumber(isolate, input));
  if (IsSmi(*input))
    return typename HandleType<Number>::MaybeType(
        Smi::ToUint32Smi(Cast<Smi>(*input)), isolate);
  return isolate->factory()->NewNumberFromUint(
      DoubleToUint32(Cast<HeapNumber>(*input)->value()));
}

template MaybeDirectHandle<Number> Object::ConvertToUint32(
    Isolate* isolate, DirectHandle<Object> input);
template MaybeIndirectHandle<Number> Object::ConvertToUint32(
    Isolate* isolate, IndirectHandle<Object> input);

// static
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Name>::MaybeType Object::ConvertToName(
    Isolate* isolate, HandleType<Object> input) {
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, input,
      Object::ToPrimitive(isolate, input, ToPrimitiveHint::kString));
  if (IsName(*input)) return Cast<Name>(input);
  return ToString(isolate, input);
}

template V8_EXPORT_PRIVATE MaybeDirectHandle<Name> Object::ConvertToName(
    Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Name> Object::ConvertToName(
    Isolate* isolate, IndirectHandle<Object> input);

// ES6 7.1.14
// static
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Object>::MaybeType Object::ConvertToPropertyKey(
    Isolate* isolate, HandleType<Object> value) {
  // 1. Let key be ToPrimitive(argument, hint String).
  typename HandleType<Object>::MaybeType maybe_key =
      Object::ToPrimitive(isolate, value, ToPrimitiveHint::kString);
  // 2. ReturnIfAbrupt(key).
  HandleType<Object> key;
  if (!maybe_key.ToHandle(&key)) return key;
  // 3. If Type(key) is Symbol, then return key.
  if (IsSymbol(*key)) return key;
  // 4. Return ToString(key).
  // Extending spec'ed behavior, we'd be happy to return an element index.
  if (IsSmi(*key)) return key;
  if (IsHeapNumber(*key)) {
    uint32_t uint_value;
    if (Object::ToArrayLength(*value, &uint_value) &&
        uint_value <= static_cast<uint32_t>(Smi::kMaxValue)) {
      return HandleType<Object>(Smi::FromInt(static_cast<int>(uint_value)),
                                isolate);
    }
  }
  return Object::ToString(isolate, key);
}

template V8_EXPORT_PRIVATE MaybeDirectHandle<Object>
Object::ConvertToPropertyKey(Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Object>
Object::ConvertToPropertyKey(Isolate* isolate, IndirectHandle<Object> input);

// static
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<String>::MaybeType Object::ConvertToString(
    Isolate* isolate, HandleType<Object> input) {
  while (true) {
    if (IsOddball(*input)) {
      HandleType<String> result(Cast<Oddball>(input)->to_string(), isolate);
      return result;
    }
    if (IsNumber(*input)) {
      return isolate->factory()->NumberToString(input);
    }
    if (IsSymbol(*input)) {
      THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToString));
    }
    if (IsBigInt(*input)) {
      return BigInt::ToString(isolate, Cast<BigInt>(input));
    }
#if V8_ENABLE_WEBASSEMBLY
    // We generally don't let the WasmNull escape into the JavaScript world,
    // but some builtins may encounter it when called directly from Wasm code.
    if (IsWasmNull(*input)) {
      return isolate->factory()->null_string();
    }
#endif
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, input,
        JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(input),
                                ToPrimitiveHint::kString));
    // The previous isString() check happened in Object::ToString and thus we
    // put it at the end of the loop in this helper.
    if (IsString(*input)) {
      return Cast<String>(input);
    }
  }
}

template V8_EXPORT_PRIVATE MaybeDirectHandle<String> Object::ConvertToString(
    Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<String> Object::ConvertToString(
    Isolate* isolate, IndirectHandle<Object> input);

namespace {

bool IsErrorObject(Isolate* isolate, DirectHandle<Object> object) {
  if (!IsJSObject(*object)) return false;
  return ErrorUtils::HasErrorStackSymbolOwnProperty(isolate,
                                                    Cast<JSObject>(object));
}

DirectHandle<String> AsStringOrEmpty(Isolate* isolate,
                                     DirectHandle<Object> object) {
  return IsString(*object) ? Cast<String>(object)
                           : isolate->factory()->empty_string();
}

DirectHandle<String> NoSideEffectsErrorToString(
    Isolate* isolate, DirectHandle<JSReceiver> error) {
  DirectHandle<Name> name_key = isolate->factory()->name_string();
  DirectHandle<Object> name =
      JSReceiver::GetDataProperty(isolate, error, name_key);
  DirectHandle<String> name_str = AsStringOrEmpty(isolate, name);

  DirectHandle<Name> msg_key = isolate->factory()->message_string();
  DirectHandle<Object> msg =
      JSReceiver::GetDataProperty(isolate, error, msg_key);
  DirectHandle<String> msg_str = AsStringOrEmpty(isolate, msg);

  if (name_str->length() == 0) return msg_str;
  if (msg_str->length() == 0) return name_str;

  constexpr const char error_suffix[] = "<a very large string>";
  constexpr uint32_t error_suffix_size = sizeof(error_suffix);
  uint32_t suffix_size = std::min(error_suffix_size, msg_str->length());

  IncrementalStringBuilder builder(isolate);
  if (name_str->length() + suffix_size + 2 /* ": " */ > String::kMaxLength) {
    constexpr const char connector[] = "... : ";
    int connector_size = sizeof(connector);
    DirectHandle<String> truncated_name =
        isolate->factory()->NewProperSubString(
            name_str, 0,
            name_str->length() - error_suffix_size - connector_size);
    builder.AppendString(truncated_name);
    builder.AppendCStringLiteral(connector);
    builder.AppendCStringLiteral(error_suffix);
  } else {
    builder.AppendString(name_str);
    builder.AppendCStringLiteral(": ");
    if (builder.Length() + msg_str->length() <= String::kMaxLength) {
      builder.AppendString(msg_str);
    } else {
      builder.AppendCStringLiteral(error_suffix);
    }
  }

  return builder.Finish().ToHandleChecked();
}

}  // namespace

// static
MaybeDirectHandle<String> Object::NoSideEffectsToMaybeString(
    Isolate* isolate, DirectHandle<Object> input) {
  DisallowJavascriptExecution no_js(isolate);

  if (IsAnyHole(*input, isolate)) {
    ReadOnlyRoots roots(isolate);
#define HOLE_CASE(CamelName, snake_name, _)                           \
  if (Is##CamelName(*input)) {                                        \
    return isolate->factory()->NewStringFromAsciiChecked(#CamelName); \
  }
    HOLE_LIST(HOLE_CASE)
#undef HOLE_CASE
    UNREACHABLE();
  } else if (IsString(*input) || IsNumber(*input) || IsOddball(*input)) {
    return Object::ToString(isolate, input).ToHandleChecked();
  } else if (IsJSProxy(*input)) {
    DirectHandle<Object> currInput = input;
    do {
      Tagged<HeapObject> target = Cast<JSProxy>(currInput)->target(isolate);
      currInput = direct_handle(target, isolate);
    } while (IsJSProxy(*currInput));
    return NoSideEffectsToString(isolate, currInput);
  } else if (IsBigInt(*input)) {
    return BigInt::NoSideEffectsToString(isolate, Cast<BigInt>(input));
  } else if (IsJSFunctionOrBoundFunctionOrWrappedFunction(*input)) {
    // -- F u n c t i o n
    DirectHandle<String> fun_str;
    if (IsJSBoundFunction(*input)) {
      fun_str =
          JSBoundFunction::ToString(isolate, Cast<JSBoundFunction>(input));
    } else if (IsJSWrappedFunction(*input)) {
      fun_str =
          JSWrappedFunction::ToString(isolate, Cast<JSWrappedFunction>(input));
    } else {
      DCHECK(IsJSFunction(*input));
      fun_str = JSFunction::ToString(isolate, Cast<JSFunction>(input));
    }

    if (fun_str->length() > 128) {
      IncrementalStringBuilder builder(isolate);
      builder.AppendString(isolate->factory()->NewSubString(fun_str, 0, 111));
      builder.AppendCStringLiteral("...<omitted>...");
      builder.AppendString(isolate->factory()->NewSubString(
          fun_str, fun_str->length() - 2, fun_str->length()));

      return builder.Finish().ToHandleChecked();
    }
    return fun_str;
  } else if (IsSymbol(*input)) {
    // -- S y m b o l
    DirectHandle<Symbol> symbol = Cast<Symbol>(input);

    if (symbol->is_private_name()) {
      return DirectHandle<String>(Cast<String>(symbol->description()), isolate);
    }

    IncrementalStringBuilder builder(isolate);
    builder.AppendCStringLiteral("Symbol(");
    if (IsString(symbol->description())) {
      DirectHandle<String> description(Cast<String>(symbol->description()),
                                       isolate);
      if (description->length() > 128) {
        builder.AppendString(
            isolate->factory()->NewSubString(description, 0, 56));
        builder.AppendCStringLiteral("...<omitted>...");
        builder.AppendString(isolate->factory()->NewSubString(
            description, description->length() - 56, description->length()));
      } else {
        builder.AppendString(description);
      }
    }
    builder.AppendCharacter(')');

    return builder.Finish().ToHandleChecked();
  } else if (IsJSReceiver(*input)) {
    // -- J S R e c e i v e r
    DirectHandle<JSReceiver> receiver = Cast<JSReceiver>(input);
    DirectHandle<Object> to_string = JSReceiver::GetDataProperty(
        isolate, receiver, isolate->factory()->toString_string());

    if (IsErrorObject(isolate, input) ||
        *to_string == *isolate->error_to_string()) {
      // When internally formatting error objects, use a side-effects-free
      // version of Error.prototype.toString independent of the actually
      // installed toString method.
      return NoSideEffectsErrorToString(isolate, receiver);
    } else if (*to_string == *isolate->object_to_string()) {
      DirectHandle<Object> ctor = JSReceiver::GetDataProperty(
          isolate, receiver, isolate->factory()->constructor_string());
      if (IsJSFunctionOrBoundFunctionOrWrappedFunction(*ctor)) {
        DirectHandle<String> ctor_name;
        if (IsJSBoundFunction(*ctor)) {
          ctor_name =
              JSBoundFunction::GetName(isolate, Cast<JSBoundFunction>(ctor))
                  .ToHandleChecked();
        } else if (IsJSFunction(*ctor)) {
          ctor_name = JSFunction::GetName(isolate, Cast<JSFunction>(ctor));
        } else if (IsJSWrappedFunction(*ctor)) {
          ctor_name =
              JSWrappedFunction::GetName(isolate, Cast<JSWrappedFunction>(ctor))
                  .ToHandleChecked();
        } else {
          UNREACHABLE();
        }

        if (ctor_name->length() != 0) {
          IncrementalStringBuilder builder(isolate);
          builder.AppendCStringLiteral("#<");
          builder.AppendString(ctor_name);
          builder.AppendCharacter('>');

          return builder.Finish().ToHandleChecked();
        }
      }
    }
  }
  return {};
}

// static
DirectHandle<String> Object::NoSideEffectsToString(Isolate* isolate,
                                                   DirectHandle<Object> input) {
  DisallowJavascriptExecution no_js(isolate);

  // Try to convert input to a meaningful string.
  MaybeDirectHandle<String> maybe_string =
      NoSideEffectsToMaybeString(isolate, input);
  DirectHandle<String> string_handle;
  if (maybe_string.ToHandle(&string_handle)) {
    return string_handle;
  }

  // At this point, input is either none of the above or a JSReceiver.

  DirectHandle<JSReceiver> receiver;
  if (IsJSReceiver(*input)) {
    receiver = Cast<JSReceiver>(input);
  } else {
    // This is the only case where Object::ToObject throws.
    DCHECK(!IsSmi(*input));
    int constructor_function_index =
        Cast<HeapObject>(input)->map()->GetConstructorFunctionIndex();
    if (constructor_function_index == Map::kNoConstructorFunctionIndex) {
      return isolate->factory()->NewStringFromAsciiChecked("[object Unknown]");
    }

    receiver = Object::ToObjectImpl(isolate, input).ToHandleChecked();
  }

  DirectHandle<String> builtin_tag =
      direct_handle(receiver->class_name(), isolate);
  DirectHandle<Object> tag_obj = JSReceiver::GetDataProperty(
      isolate, receiver, isolate->factory()->to_string_tag_symbol());
  DirectHandle<String> tag =
      IsString(*tag_obj) ? Cast<String>(tag_obj) : builtin_tag;

  IncrementalStringBuilder builder(isolate);
  builder.AppendCStringLiteral("[object ");
  builder.AppendString(tag);
  builder.AppendCharacter(']');

  return builder.Finish().ToHandleChecked();
}

// static
MaybeHandle<Number> Object::ConvertToLength(Isolate* isolate,
                                            DirectHandle<Object> input) {
  ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input));
  if (IsSmi(*input)) {
    int value = std::max(Smi::ToInt(*input), 0);
    return handle(Smi::FromInt(value), isolate);
  }
  double len = DoubleToInteger(Cast<HeapNumber>(*input)->value());
  if (len <= 0.0) {
    return handle(Smi::zero(), isolate);
  } else if (len >= kMaxSafeInteger) {
    len = kMaxSafeInteger;
  }
  return isolate->factory()->NewNumber(len);
}

// static
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Number>::MaybeType Object::ConvertToIndex(
    Isolate* isolate, HandleType<Object> input, MessageTemplate error_index) {
  if (IsUndefined(*input, isolate))
    return HandleType<Number>(Smi::zero(), isolate);
  ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input));
  if (IsSmi(*input) && Smi::ToInt(*input) >= 0) return Cast<Smi>(input);
  double len = DoubleToInteger(Object::NumberValue(Cast<Number>(*input)));
  HandleType<Number> js_len = isolate->factory()->NewNumber(len);
  if (len < 0.0 || len > kMaxSafeInteger) {
    THROW_NEW_ERROR(isolate, NewRangeError(error_index, js_len));
  }
  return js_len;
}

template V8_EXPORT_PRIVATE MaybeDirectHandle<Number> Object::ConvertToIndex(
    Isolate* isolate, DirectHandle<Object> input, MessageTemplate error_index);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Number> Object::ConvertToIndex(
    Isolate* isolate, IndirectHandle<Object> input,
    MessageTemplate error_index);

template <typename IsolateT>
// static
bool Object::BooleanValue(Tagged<Object> obj, IsolateT* isolate) {
  if (IsSmi(obj)) return Smi::ToInt(obj) != 0;
  DCHECK(IsHeapObject(obj));
#ifdef V8_ENABLE_WEBASSEMBLY
  DCHECK(!IsWasmNull(obj));
#endif
  if (IsBoolean(obj)) return IsTrue(obj, isolate);
  if (IsNullOrUndefined(obj, isolate)) return false;
  if (IsUndetectable(obj)) return false;  // Undetectable object is false.
  if (IsString(obj)) return Cast<String>(obj)->length() != 0;
  if (IsHeapNumber(obj)) return DoubleToBoolean(Cast<HeapNumber>(obj)->value());
  if (IsBigInt(obj)) return Cast<BigInt>(obj)->ToBoolean();
  return true;
}
template bool Object::BooleanValue(Tagged<Object>, Isolate*);
template bool Object::BooleanValue(Tagged<Object>, LocalIsolate*);

// static
Tagged<Object> Object::ToBoolean(Tagged<Object> obj, Isolate* isolate) {
  if (IsBoolean(obj)) return obj;
  return isolate->heap()->ToBoolean(Object::BooleanValue(obj, isolate));
}

namespace {

// TODO(bmeurer): Maybe we should introduce a marker interface Number,
// where we put all these methods at some point?
ComparisonResult StrictNumberCompare(double x, double y) {
  if (std::isnan(x) || std::isnan(y)) {
    return ComparisonResult::kUndefined;
  } else if (x < y) {
    return ComparisonResult::kLessThan;
  } else if (x > y) {
    return ComparisonResult::kGreaterThan;
  } else {
    return ComparisonResult::kEqual;
  }
}

// See Number case of ES6#sec-strict-equality-comparison
// Returns false if x or y is NaN, treats -0.0 as equal to 0.0.
bool StrictNumberEquals(double x, double y) {
  // Must check explicitly for NaN's on Windows, but -0 works fine.
  if (std::isnan(x) || std::isnan(y)) return false;
  return x == y;
}

bool StrictNumberEquals(const Tagged<Number> x, const Tagged<Number> y) {
  return StrictNumberEquals(Object::NumberValue(x), Object::NumberValue(y));
}

bool StrictNumberEquals(DirectHandle<Number> x, DirectHandle<Number> y) {
  return StrictNumberEquals(*x, *y);
}

ComparisonResult Reverse(ComparisonResult result) {
  if (result == ComparisonResult::kLessThan) {
    return ComparisonResult::kGreaterThan;
  }
  if (result == ComparisonResult::kGreaterThan) {
    return ComparisonResult::kLessThan;
  }
  return result;
}

}  // anonymous namespace

// static
Maybe<ComparisonResult> Object::Compare(Isolate* isolate,
                                        DirectHandle<Object> x,
                                        DirectHandle<Object> y) {
  // ES6 section 7.2.11 Abstract Relational Comparison step 3 and 4.
  if (!Object::ToPrimitive(isolate, x, ToPrimitiveHint::kNumber).ToHandle(&x) ||
      !Object::ToPrimitive(isolate, y, ToPrimitiveHint::kNumber).ToHandle(&y)) {
    return Nothing<ComparisonResult>();
  }
  if (IsString(*x) && IsString(*y)) {
    // ES6 section 7.2.11 Abstract Relational Comparison step 5.
    return Just(String::Compare(isolate, Cast<String>(x), Cast<String>(y)));
  }
  if (IsBigInt(*x) && IsString(*y)) {
    return BigInt::CompareToString(isolate, Cast<BigInt>(x), Cast<String>(y));
  }
  if (IsString(*x) && IsBigInt(*y)) {
    Maybe<ComparisonResult> maybe_result =
        BigInt::CompareToString(isolate, Cast<BigInt>(y), Cast<String>(x));
    ComparisonResult result;
    if (maybe_result.To(&result)) {
      return Just(Reverse(result));
    } else {
      return Nothing<ComparisonResult>();
    }
  }
  // ES6 section 7.2.11 Abstract Relational Comparison step 6.
  if (!Object::ToNumeric(isolate, x).ToHandle(&x) ||
      !Object::ToNumeric(isolate, y).ToHandle(&y)) {
    return Nothing<ComparisonResult>();
  }

  bool x_is_number = IsNumber(*x);
  bool y_is_number = IsNumber(*y);
  if (x_is_number && y_is_number) {
    return Just(
        StrictNumberCompare(Object::NumberValue(*x), Object::NumberValue(*y)));
  } else if (!x_is_number && !y_is_number) {
    return Just(BigInt::CompareToBigInt(Cast<BigInt>(x), Cast<BigInt>(y)));
  } else if (x_is_number) {
    return Just(Reverse(BigInt::CompareToNumber(Cast<BigInt>(y), x)));
  } else {
    return Just(BigInt::CompareToNumber(Cast<BigInt>(x), y));
  }
}

Maybe<bool> Object::Equals(Isolate* isolate, DirectHandle<Object> x,
                           DirectHandle<Object> y) {
  // This is the generic version of Abstract Equality Comparison. Must be in
  // sync with CodeStubAssembler::Equal.
  while (true) {
    if (IsNumber(*x)) {
      if (IsNumber(*y)) {
        return Just(StrictNumberEquals(Cast<Number>(*x), Cast<Number>(*y)));
      } else if (IsBoolean(*y)) {
        return Just(StrictNumberEquals(Cast<Number>(*x),
                                       Cast<Oddball>(y)->to_number()));
      } else if (IsString(*y)) {
        return Just(StrictNumberEquals(
            Cast<Number>(x), String::ToNumber(isolate, Cast<String>(y))));
      } else if (IsBigInt(*y)) {
        return Just(BigInt::EqualToNumber(Cast<BigInt>(y), Cast<Number>(x)));
      } else if (IsJSReceiver(*y)) {
        if (!JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(y))
                 .ToHandle(&y)) {
          return Nothing<bool>();
        }
      } else {
        return Just(false);
      }
    } else if (IsString(*x)) {
      if (IsString(*y)) {
        return Just(String::Equals(isolate, Cast<String>(x), Cast<String>(y)));
      } else if (IsNumber(*y)) {
        x = String::ToNumber(isolate, Cast<String>(x));
        return Just(StrictNumberEquals(Cast<Number>(*x), Cast<Number>(*y)));
      } else if (IsBoolean(*y)) {
        x = String::ToNumber(isolate, Cast<String>(x));
        return Just(StrictNumberEquals(Cast<Number>(*x),
                                       Cast<Oddball>(y)->to_number()));
      } else if (IsBigInt(*y)) {
        return BigInt::EqualToString(isolate, Cast<BigInt>(y), Cast<String>(x));
      } else if (IsJSReceiver(*y)) {
        if (!JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(y))
                 .ToHandle(&y)) {
          return Nothing<bool>();
        }
      } else {
        return Just(false);
      }
    } else if (IsBoolean(*x)) {
      if (IsOddball(*y)) {
        return Just(x.is_identical_to(y));
      } else if (IsNumber(*y)) {
        return Just(StrictNumberEquals(Cast<Oddball>(x)->to_number(),
                                       Cast<Number>(*y)));
      } else if (IsString(*y)) {
        y = String::ToNumber(isolate, Cast<String>(y));
        return Just(StrictNumberEquals(Cast<Oddball>(x)->to_number(),
                                       Cast<Number>(*y)));
      } else if (IsBigInt(*y)) {
        x = Oddball::ToNumber(isolate, Cast<Oddball>(x));
        return Just(BigInt::EqualToNumber(Cast<BigInt>(y), x));
      } else if (IsJSReceiver(*y)) {
        if (!JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(y))
                 .ToHandle(&y)) {
          return Nothing<bool>();
        }
        x = Oddball::ToNumber(isolate, Cast<Oddball>(x));
      } else {
        return Just(false);
      }
    } else if (IsSymbol(*x)) {
      if (IsSymbol(*y)) {
        return Just(x.is_identical_to(y));
      } else if (IsJSReceiver(*y)) {
        if (!JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(y))
                 .ToHandle(&y)) {
          return Nothing<bool>();
        }
      } else {
        return Just(false);
      }
    } else if (IsBigInt(*x)) {
      if (IsBigInt(*y)) {
        return Just(BigInt::EqualToBigInt(Cast<BigInt>(*x), Cast<BigInt>(*y)));
      }
      return Equals(isolate, y, x);
    } else if (IsJSReceiver(*x)) {
      if (IsJSReceiver(*y)) {
        return Just(x.is_identical_to(y));
      } else if (IsUndetectable(*y)) {
        return Just(IsUndetectable(*x));
      } else if (IsBoolean(*y)) {
        y = Oddball::ToNumber(isolate, Cast<Oddball>(y));
      } else if (!JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(x))
                      .ToHandle(&x)) {
        return Nothing<bool>();
      }
    } else {
      return Just(IsUndetectable(*x) && IsUndetectable(*y));
    }
  }
}

// static
bool Object::StrictEquals(Tagged<Object> obj, Tagged<Object> that) {
  if (IsNumber(obj)) {
    if (!IsNumber(that)) return false;
    return StrictNumberEquals(Cast<Number>(obj), Cast<Number>(that));
  } else if (IsString(obj)) {
    if (!IsString(that)) return false;
    return Cast<String>(obj)->Equals(Cast<String>(that));
  } else if (IsBigInt(obj)) {
    if (!IsBigInt(that)) return false;
    return BigInt::EqualToBigInt(Cast<BigInt>(obj), Cast<BigInt>(that));
  }
  return obj == that;
}

// static
Handle<String> Object::TypeOf(Isolate* isolate, DirectHandle<Object> object) {
  if (IsNumber(*object)) return isolate->factory()->number_string();
  if (IsOddball(*object))
    return handle(Cast<Oddball>(*object)->type_of(), isolate);
  if (IsUndetectable(*object)) {
    return isolate->factory()->undefined_string();
  }
  if (IsString(*object)) return isolate->factory()->string_string();
  if (IsSymbol(*object)) return isolate->factory()->symbol_string();
  if (IsBigInt(*object)) return isolate->factory()->bigint_string();
  if (IsCallable(*object)) return isolate->factory()->function_string();
  return isolate->factory()->object_string();
}

// static
MaybeDirectHandle<Object> Object::Add(Isolate* isolate, Handle<Object> lhs,
                                      Handle<Object> rhs) {
  if (IsNumber(*lhs) && IsNumber(*rhs)) {
    return isolate->factory()->NewNumber(
        Object::NumberValue(Cast<Number>(*lhs)) +
        Object::NumberValue(Cast<Number>(*rhs)));
  } else if (IsString(*lhs) && IsString(*rhs)) {
    return isolate->factory()->NewConsString(Cast<String>(lhs),
                                             Cast<String>(rhs));
  }
  ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToPrimitive(isolate, lhs));
  ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToPrimitive(isolate, rhs));
  if (IsString(*lhs) || IsString(*rhs)) {
    ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToString(isolate, rhs));
    ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToString(isolate, lhs));
    return isolate->factory()->NewConsString(Cast<String>(lhs),
                                             Cast<String>(rhs));
  }
  DirectHandle<Number> lhs_number;
  DirectHandle<Number> rhs_number;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs_number,
                             Object::ToNumber(isolate, rhs));
  ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs_number,
                             Object::ToNumber(isolate, lhs));
  return isolate->factory()->NewNumber(Object::NumberValue(*lhs_number) +
                                       Object::NumberValue(*rhs_number));
}

// static
MaybeHandle<Object> Object::OrdinaryHasInstance(Isolate* isolate,
                                                DirectHandle<JSAny> callable,
                                                DirectHandle<JSAny> object) {
  // The {callable} must have a [[Call]] internal method.
  if (!IsCallable(*callable)) return isolate->factory()->false_value();

  // Check if {callable} is a bound function, and if so retrieve its
  // [[BoundTargetFunction]] and use that instead of {callable}.
  if (IsJSBoundFunction(*callable)) {
    // Since there is a mutual recursion here, we might run out of stack
    // space for long chains of bound functions.
    STACK_CHECK(isolate, MaybeHandle<Object>());
    DirectHandle<JSCallable> bound_callable(
        Cast<JSBoundFunction>(callable)->bound_target_function(), isolate);
    return Object::InstanceOf(isolate, object, bound_callable);
  }

  // If {object} is not a receiver, return false.
  if (!IsJSReceiver(*object)) return isolate->factory()->false_value();

  // Get the "prototype" of {callable}; raise an error if it's not a receiver.
  DirectHandle<Object> prototype;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, prototype,
      Object::GetProperty(isolate, callable,
                          isolate->factory()->prototype_string()));
  if (!IsJSReceiver(*prototype)) {
    THROW_NEW_ERROR(
        isolate,
        NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype));
  }

  // Return whether or not {prototype} is in the prototype chain of {object}.
  Maybe<bool> result = JSReceiver::HasInPrototypeChain(
      isolate, Cast<JSReceiver>(object), prototype);
  if (result.IsNothing()) return MaybeHandle<Object>();
  return isolate->factory()->ToBoolean(result.FromJust());
}

// static
MaybeHandle<Object> Object::InstanceOf(Isolate* isolate,
                                       DirectHandle<JSAny> object,
                                       DirectHandle<JSAny> callable) {
  // The {callable} must be a receiver.
  if (!IsJSReceiver(*callable)) {
    THROW_NEW_ERROR(isolate,
                    NewTypeError(MessageTemplate::kNonObjectInInstanceOfCheck));
  }

  // Lookup the @@hasInstance method on {callable}.
  DirectHandle<Object> inst_of_handler;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, inst_of_handler,
      Object::GetMethod(isolate, Cast<JSReceiver>(callable),
                        isolate->factory()->has_instance_symbol()));
  if (!IsUndefined(*inst_of_handler, isolate)) {
    // Call the {inst_of_handler} on the {callable}.
    DirectHandle<Object> result;
    DirectHandle<Object> args[] = {object};
    ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
                               Execution::Call(isolate, inst_of_handler,
                                               callable, base::VectorOf(args)));
    return isolate->factory()->ToBoolean(
        Object::BooleanValue(*result, isolate));
  }

  // The {callable} must have a [[Call]] internal method.
  if (!IsCallable(*callable)) {
    THROW_NEW_ERROR(
        isolate, NewTypeError(MessageTemplate::kNonCallableInInstanceOfCheck));
  }

  // Fall back to OrdinaryHasInstance with {callable} and {object}.
  Handle<Object> result;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, result, Object::OrdinaryHasInstance(isolate, callable, object));
  return result;
}

// static
MaybeDirectHandle<Object> Object::GetMethod(Isolate* isolate,
                                            DirectHandle<JSReceiver> receiver,
                                            DirectHandle<Name> name) {
  Handle<Object> func;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, func,
                             JSReceiver::GetProperty(isolate, receiver, name));
  if (IsNullOrUndefined(*func, isolate)) {
    return isolate->factory()->undefined_value();
  }
  if (!IsCallable(*func)) {
    THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kPropertyNotFunction,
                                          func, name, receiver));
  }
  return func;
}

namespace {

MaybeDirectHandle<FixedArray> CreateListFromArrayLikeFastPath(
    Isolate* isolate, DirectHandle<Object> object, ElementTypes element_types) {
  if (element_types == ElementTypes::kAll) {
    if (IsJSArray(*object)) {
      DirectHandle<JSArray> array = Cast<JSArray>(object);
      uint32_t length;
      if (!array->HasArrayPrototype(isolate) ||
          !Object::ToUint32(array->length(), &length) ||
          !array->HasFastElements() ||
          !JSObject::PrototypeHasNoElements(isolate, *array)) {
        return MaybeDirectHandle<FixedArray>();
      }
      return array->GetElementsAccessor()->CreateListFromArrayLike(
          isolate, array, length);
    } else if (IsJSTypedArray(*object)) {
      DirectHandle<JSTypedArray> array = Cast<JSTypedArray>(object);
      size_t length = array->GetLength();
      if (array->IsDetachedOrOutOfBounds() ||
          length > static_cast<size_t>(FixedArray::kMaxLength)) {
        return MaybeDirectHandle<FixedArray>();
      }
      static_assert(FixedArray::kMaxLength <=
                    std::numeric_limits<uint32_t>::max());
      return array->GetElementsAccessor()->CreateListFromArrayLike(
          isolate, array, static_cast<uint32_t>(length));
    }
  }
  return MaybeDirectHandle<FixedArray>();
}

}  // namespace

// static
MaybeDirectHandle<FixedArray> Object::CreateListFromArrayLike(
    Isolate* isolate, DirectHandle<Object> object, ElementTypes element_types) {
  // Fast-path for JSArray and JSTypedArray.
  MaybeDirectHandle<FixedArray> fast_result =
      CreateListFromArrayLikeFastPath(isolate, object, element_types);
  if (!fast_result.is_null()) return fast_result;
  // 1. ReturnIfAbrupt(object).
  // 2. (default elementTypes -- not applicable.)
  // 3. If Type(obj) is not Object, throw a TypeError exception.
  if (!IsJSReceiver(*object)) {
    THROW_NEW_ERROR(isolate,
                    NewTypeError(MessageTemplate::kCalledOnNonObject,
                                 isolate->factory()->NewStringFromAsciiChecked(
                                     "CreateListFromArrayLike")));
  }

  // 4. Let len be ? ToLength(? Get(obj, "length")).
  DirectHandle<JSReceiver> receiver = Cast<JSReceiver>(object);
  DirectHandle<Object> raw_length_number;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number,
                             Object::GetLengthFromArrayLike(isolate, receiver));
  uint32_t len;
  if (!Object::ToUint32(*raw_length_number, &len) ||
      len > static_cast<uint32_t>(FixedArray::kMaxLength)) {
    THROW_NEW_ERROR(isolate,
                    NewRangeError(MessageTemplate::kInvalidArrayLength));
  }
  // 5. Let list be an empty List.
  DirectHandle<FixedArray> list = isolate->factory()->NewFixedArray(len);
  // 6. Let index be 0.
  // 7. Repeat while index < len:
  for (uint32_t index = 0; index < len; ++index) {
    // 7a. Let indexName be ToString(index).
    // 7b. Let next be ? Get(obj, indexName).
    DirectHandle<Object> next;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, next, JSReceiver::GetElement(isolate, receiver, index));
    switch (element_types) {
      case ElementTypes::kAll:
        // Nothing to do.
        break;
      case ElementTypes::kStringAndSymbol: {
        // 7c. If Type(next) is not an element of elementTypes, throw a
        //     TypeError exception.
        if (!IsName(*next)) {
          THROW_NEW_ERROR(
              isolate, NewTypeError(MessageTemplate::kNotPropertyName, next));
        }
        // 7d. Append next as the last element of list.
        // Internalize on the fly so we can use pointer identity later.
        next = isolate->factory()->InternalizeName(Cast<Name>(next));
        break;
      }
    }
    list->set(index, *next);
    // 7e. Set index to index + 1. (See loop header.)
  }
  // 8. Return list.
  return list;
}

// static
MaybeDirectHandle<Object> Object::GetLengthFromArrayLike(
    Isolate* isolate, DirectHandle<JSReceiver> object) {
  DirectHandle<Object> val;
  DirectHandle<Name> key = isolate->factory()->length_string();
  ASSIGN_RETURN_ON_EXCEPTION(isolate, val,
                             JSReceiver::GetProperty(isolate, object, key));
  return Object::ToLength(isolate, val);
}

// static
MaybeHandle<Object> Object::GetProperty(LookupIterator* it,
                                        bool is_global_reference) {
  for (;; it->Next()) {
    switch (it->state()) {
      case LookupIterator::TRANSITION:
        UNREACHABLE();
      case LookupIterator::JSPROXY: {
        bool was_found;
        DirectHandle<JSAny> receiver = it->GetReceiver();
        // In case of global IC, the receiver is the global object. Replace by
        // the global proxy.
        if (IsJSGlobalObject(*receiver)) {
          receiver = direct_handle(
              Cast<JSGlobalObject>(*receiver)->global_proxy(), it->isolate());
        }
        if (is_global_reference) {
          Maybe<bool> maybe = JSProxy::HasProperty(
              it->isolate(), it->GetHolder<JSProxy>(), it->GetName());
          if (maybe.IsNothing()) return {};
          if (!maybe.FromJust()) {
            it->NotFound();
            return it->isolate()->factory()->undefined_value();
          }
        }
        MaybeHandle<JSAny> result =
            JSProxy::GetProperty(it->isolate(), it->GetHolder<JSProxy>(),
                                 it->GetName(), receiver, &was_found);
        if (!was_found && !is_global_reference) it->NotFound();
        return result;
      }
      case LookupIterator::WASM_OBJECT:
        continue;  // Continue to the prototype, if present.
      case LookupIterator::INTERCEPTOR: {
        bool done;
        Handle<JSAny> result;
        ASSIGN_RETURN_ON_EXCEPTION(
            it->isolate(), result,
            JSObject::GetPropertyWithInterceptor(it, &done));
        if (done) return result;
        continue;
      }
      case LookupIterator::ACCESS_CHECK:
        if (it->HasAccess()) continue;
        return JSObject::GetPropertyWithFailedAccessCheck(it);
      case LookupIterator::ACCESSOR:
        return GetPropertyWithAccessor(it);
      case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
        return it->isolate()->factory()->undefined_value();
      case LookupIterator::DATA:
        return it->GetDataValue();
      case LookupIterator::STRING_LOOKUP_START_OBJECT:
        return it->GetStringPropertyValue();
      case LookupIterator::NOT_FOUND:
        if (it->IsPrivateName()) {
          auto private_symbol = Cast<Symbol>(it->name());
          DirectHandle<String> name_string(
              Cast<String>(private_symbol->description()), it->isolate());
          if (private_symbol->is_private_brand()) {
            DirectHandle<String> class_name =
                name_string->length() == 0
                    ? it->isolate()->factory()->anonymous_string()
                    : name_string;
            THROW_NEW_ERROR(
                it->isolate(),
                NewTypeError(MessageTemplate::kInvalidPrivateBrandInstance,
                             class_name));
          }
          THROW_NEW_ERROR(
              it->isolate(),
              NewTypeError(MessageTemplate::kInvalidPrivateMemberRead,
                           name_string));
        }

        return it->isolate()->factory()->undefined_value();
    }
    UNREACHABLE();
  }
}

// static
MaybeHandle<JSAny> JSProxy::GetProperty(Isolate* isolate,
                                        DirectHandle<JSProxy> proxy,
                                        DirectHandle<Name> name,
                                        DirectHandle<JSAny> receiver,
                                        bool* was_found) {
  *was_found = true;

  DCHECK(!name->IsPrivate());
  STACK_CHECK(isolate, kNullMaybeHandle);
  DirectHandle<Name> trap_name = isolate->factory()->get_string();
  // 1. Assert: IsPropertyKey(P) is true.
  // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
  DirectHandle<UnionOf<JSReceiver, Null>> handler(proxy->handler(), isolate);
  // 3. If handler is null, throw a TypeError exception.
  // 4. Assert: Type(handler) is Object.
  if (proxy->IsRevoked()) {
    THROW_NEW_ERROR(isolate,
                    NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
  }
  // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
  DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
  // 6. Let trap be ? GetMethod(handler, "get").
  DirectHandle<Object> trap;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap,
      Object::GetMethod(isolate, Cast<JSReceiver>(handler), trap_name));
  // 7. If trap is undefined, then
  if (IsUndefined(*trap, isolate)) {
    // 7.a Return target.[[Get]](P, Receiver).
    PropertyKey key(isolate, name);
    LookupIterator it(isolate, receiver, key, target);
    MaybeHandle<JSAny> result = Cast<JSAny>(Object::GetProperty(&it));
    *was_found = it.IsFound();
    return result;
  }
  // 8. Let trapResult be ? Call(trap, handler, «target, P, Receiver»).
  Handle<Object> trap_result;
  DirectHandle<Object> args[] = {target, name, receiver};
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap_result,
      Execution::Call(isolate, trap, handler, base::VectorOf(args)));

  MaybeHandle<JSAny> result =
      JSProxy::CheckGetSetTrapResult(isolate, name, target, trap_result, kGet);
  if (result.is_null()) {
    return result;
  }

  // 11. Return trap_result
  return Cast<JSAny>(trap_result);
}

// static
MaybeHandle<JSAny> JSProxy::CheckGetSetTrapResult(
    Isolate* isolate, DirectHandle<Name> name, DirectHandle<JSReceiver> target,
    DirectHandle<Object> trap_result, AccessKind access_kind) {
  // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
  PropertyDescriptor target_desc;
  Maybe<bool> target_found =
      JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
  MAYBE_RETURN_NULL(target_found);
  // 10. If targetDesc is not undefined, then
  if (target_found.FromJust()) {
    // 10.a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is
    //       false and targetDesc.[[Writable]] is false, then
    // 10.a.i. If SameValue(trapResult, targetDesc.[[Value]]) is false,
    //        throw a TypeError exception.
    bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) &&
                        !target_desc.configurable() &&
                        !target_desc.writable() &&
                        !Object::SameValue(*trap_result, *target_desc.value());
    if (inconsistent) {
      if (access_kind == kGet) {
        THROW_NEW_ERROR(
            isolate, NewTypeError(MessageTemplate::kProxyGetNonConfigurableData,
                                  name, target_desc.value(), trap_result));
      } else {
        isolate->Throw(*isolate->factory()->NewTypeError(
            MessageTemplate::kProxySetFrozenData, name));
        return {};
      }
    }
    // 10.b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]]
    //       is false and targetDesc.[[Get]] is undefined, then
    // 10.b.i. If trapResult is not undefined, throw a TypeError exception.
    if (access_kind == kGet) {
      inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
                     !target_desc.configurable() &&
                     IsUndefined(*target_desc.get(), isolate) &&
                     !IsUndefined(*trap_result, isolate);
    } else {
      inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
                     !target_desc.configurable() &&
                     IsUndefined(*target_desc.set(), isolate);
    }
    if (inconsistent) {
      if (access_kind == kGet) {
        THROW_NEW_ERROR(
            isolate,
            NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor,
                         name, trap_result));
      } else {
        isolate->Throw(*isolate->factory()->NewTypeError(
            MessageTemplate::kProxySetFrozenAccessor, name));
        return {};
      }
    }
  }
  return isolate->factory()->undefined_value();
}

// static
bool Object::ToInt32(Tagged<Object> obj, int32_t* value) {
  if (IsSmi(obj)) {
    *value = Smi::ToInt(obj);
    return true;
  }
  if (IsHeapNumber(obj)) {
    double num = Cast<HeapNumber>(obj)->value();
    // Check range before conversion to avoid undefined behavior.
    if (num >= kMinInt && num <= kMaxInt && FastI2D(FastD2I(num)) == num) {
      *value = FastD2I(num);
      return true;
    }
  }
  return false;
}

// ES6 9.5.1
// static
MaybeDirectHandle<JSPrototype> JSProxy::GetPrototype(
    DirectHandle<JSProxy> proxy) {
  Isolate* isolate = Isolate::Current();
  DirectHandle<String> trap_name = isolate->factory()->getPrototypeOf_string();

  STACK_CHECK(isolate, {});

  // 1. Let handler be the value of the [[ProxyHandler]] internal slot.
  // 2. If handler is null, throw a TypeError exception.
  // 3. Assert: Type(handler) is Object.
  // 4. Let target be the value of the [[ProxyTarget]] internal slot.
  if (proxy->IsRevoked()) {
    THROW_NEW_ERROR(isolate,
                    NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
  }
  DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
  DirectHandle<JSReceiver> handler(Cast<JSReceiver>(proxy->handler()), isolate);

  // 5. Let trap be ? GetMethod(handler, "getPrototypeOf").
  DirectHandle<Object> trap;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
                             Object::GetMethod(isolate, handler, trap_name));
  // 6. If trap is undefined, then return target.[[GetPrototypeOf]]().
  if (IsUndefined(*trap, isolate)) {
    return JSReceiver::GetPrototype(isolate, target);
  }
  // 7. Let handlerProto be ? Call(trap, handler, «target»).
  DirectHandle<Object> args[] = {target};
  DirectHandle<Object> handler_proto_result;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, handler_proto_result,
      Execution::Call(isolate, trap, handler, base::VectorOf(args)));
  // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError.
  DirectHandle<JSPrototype> handler_proto;
  if (!TryCast(handler_proto_result, &handler_proto)) {
    THROW_NEW_ERROR(isolate,
                    NewTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid));
  }
  // 9. Let extensibleTarget be ? IsExtensible(target).
  Maybe<bool> is_extensible = JSReceiver::IsExtensible(isolate, target);
  MAYBE_RETURN(is_extensible, {});
  // 10. If extensibleTarget is true, return handlerProto.
  if (is_extensible.FromJust()) return handler_proto;
  // 11. Let targetProto be ? target.[[GetPrototypeOf]]().
  DirectHandle<JSPrototype> target_proto;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto,
                             JSReceiver::GetPrototype(isolate, target));
  // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError.
  if (!Object::SameValue(*handler_proto, *target_proto)) {
    THROW_NEW_ERROR(
        isolate,
        NewTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible));
  }
  // 13. Return handlerProto.
  return handler_proto;
}

MaybeHandle<JSAny> Object::GetPropertyWithAccessor(LookupIterator* it) {
  Isolate* isolate = it->isolate();
  DirectHandle<Object> structure = it->GetAccessors();
  DirectHandle<JSAny> receiver = it->GetReceiver();
  // In case of global IC, the receiver is the global object. Replace by the
  // global proxy.
  if (IsJSGlobalObject(*receiver)) {
    receiver =
        direct_handle(Cast<JSGlobalObject>(*receiver)->global_proxy(), isolate);
  }

  // We should never get here to initialize a const with the hole value since a
  // const declaration would conflict with the getter.
  DCHECK(!IsForeign(*structure));

  // API style callbacks.
  if (IsAccessorInfo(*structure)) {
    DirectHandle<Name> name = it->GetName();
    auto info = Cast<AccessorInfo>(structure);

    if (!info->has_getter(isolate)) {
      return isolate->factory()->undefined_value();
    }

    if (info->is_sloppy() && !IsJSReceiver(*receiver)) {
      ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
                                 Object::ConvertReceiver(isolate, receiver));
    }

    PropertyCallbackArguments args(isolate, *receiver, it->GetHolderForApi(),
                                   Just(kDontThrow));
    DirectHandle<JSAny> result = args.CallAccessorGetter(info, name);
    RETURN_EXCEPTION_IF_EXCEPTION(isolate);
    Handle<JSAny> reboxed_result(*result, isolate);
    if (info->replace_on_access() && IsJSReceiver(*receiver)) {
      RETURN_ON_EXCEPTION(isolate, Accessors::ReplaceAccessorWithDataProperty(
                                       isolate, args.holder(), name, result));
    }
    return reboxed_result;
  }

  auto accessor_pair = Cast<AccessorPair>(structure);
  // AccessorPair with 'cached' private property.
  if (it->TryLookupCachedProperty(accessor_pair)) {
    return Cast<JSAny>(Object::GetProperty(it));
  }

  // Regular accessor.
  DirectHandle<Object> getter(accessor_pair->getter(), isolate);
  if (IsFunctionTemplateInfo(*getter)) {
    DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
    SaveAndSwitchContext save(isolate, holder->GetCreationContext().value());
    return Cast<JSAny>(Builtins::InvokeApiFunction(
        isolate, false, Cast<FunctionTemplateInfo>(getter), receiver, {},
        isolate->factory()->undefined_value()));
  } else if (IsCallable(*getter)) {
    // TODO(rossberg): nicer would be to cast to some JSCallable here...
    return Object::GetPropertyWithDefinedGetter(receiver,
                                                Cast<JSReceiver>(getter));
  }
  // Getter is not a function.
  return isolate->factory()->undefined_value();
}

Maybe<bool> Object::SetPropertyWithAccessor(LookupIterator* it,
                                            DirectHandle<Object> value,
                                            Maybe<ShouldThrow> should_throw) {
  Isolate* isolate = it->isolate();
  DirectHandle<Object> structure = it->GetAccessors();
  DirectHandle<JSAny> receiver = it->GetReceiver();
  // In case of global IC, the receiver is the global object. Replace by the
  // global proxy.
  if (IsJSGlobalObject(*receiver)) {
    receiver =
        direct_handle(Cast<JSGlobalObject>(*receiver)->global_proxy(), isolate);
  }

  // We should never get here to initialize a const with the hole value since a
  // const declaration would conflict with the setter.
  DCHECK(!IsForeign(*structure));

  // API style callbacks.
  if (IsAccessorInfo(*structure)) {
    DirectHandle<Name> name = it->GetName();
    auto info = Cast<AccessorInfo>(structure);

    if (!info->has_setter(isolate)) {
      // TODO(verwaest): We should not get here anymore once all AccessorInfos
      // are marked as special_data_property. They cannot both be writable and
      // not have a setter.
      return Just(true);
    }

    if (info->is_sloppy() && !IsJSReceiver(*receiver)) {
      ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
                                 Object::ConvertReceiver(isolate, receiver));
    }

    PropertyCallbackArguments args(isolate, *receiver, it->GetHolderForApi(),
                                   should_throw);
    bool result = args.CallAccessorSetter(info, name, value);
    RETURN_VALUE_IF_EXCEPTION(isolate, Nothing<bool>());
    if (!result) {
      // Make sure TypeError is thrown if necessary in case the callback
      // failed to set the property.
      RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                     NewTypeError(MessageTemplate::kStrictCannotSetProperty,
                                  it->GetName(), receiver));
    }
    return Just(result);
  }

  // Regular accessor.
  DirectHandle<Object> setter(Cast<AccessorPair>(*structure)->setter(),
                              isolate);
  if (IsFunctionTemplateInfo(*setter)) {
    DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
    SaveAndSwitchContext save(isolate, holder->GetCreationContext().value());
    DirectHandle<Object> args[] = {value};
    RETURN_ON_EXCEPTION_VALUE(
        isolate,
        Builtins::InvokeApiFunction(
            isolate, false, Cast<FunctionTemplateInfo>(setter), receiver,
            base::VectorOf(args), isolate->factory()->undefined_value()),
        Nothing<bool>());
    return Just(true);
  } else if (IsCallable(*setter)) {
    // TODO(rossberg): nicer would be to cast to some JSCallable here...
    return SetPropertyWithDefinedSetter(receiver, Cast<JSReceiver>(setter),
                                        value, should_throw);
  }

  RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                 NewTypeError(MessageTemplate::kNoSetterInCallback,
                              it->GetName(), it->GetHolder<JSObject>()));
}

MaybeHandle<JSAny> Object::GetPropertyWithDefinedGetter(
    DirectHandle<JSAny> receiver, DirectHandle<JSReceiver> getter) {
  Isolate* isolate = Isolate::Current();

  // Platforms with simulators like arm/arm64 expose a funny issue. If the
  // simulator has a separate JS stack pointer from the C++ stack pointer, it
  // can miss C++ stack overflows in the stack guard at the start of JavaScript
  // functions. It would be very expensive to check the C++ stack pointer at
  // that location. The best solution seems to be to break the impasse by
  // adding checks at possible recursion points. What's more, we don't put
  // this stack check behind the USE_SIMULATOR define in order to keep
  // behavior the same between hardware and simulators.
  StackLimitCheck check(isolate);
  if (check.JsHasOverflowed()) {
    isolate->StackOverflow();
    return kNullMaybeHandle;
  }

  return Cast<JSAny>(Execution::Call(isolate, getter, receiver, {}));
}

Maybe<bool> Object::SetPropertyWithDefinedSetter(
    DirectHandle<JSAny> receiver, DirectHandle<JSReceiver> setter,
    DirectHandle<Object> value, Maybe<ShouldThrow> should_throw) {
  Isolate* isolate = Isolate::Current();

  DirectHandle<Object> args[] = {value};
  RETURN_ON_EXCEPTION_VALUE(
      isolate, Execution::Call(isolate, setter, receiver, base::VectorOf(args)),
      Nothing<bool>());
  return Just(true);
}

// static
Tagged<Map> Object::GetPrototypeChainRootMap(Tagged<Object> obj,
                                             Isolate* isolate) {
  DisallowGarbageCollection no_alloc;
  if (IsSmi(obj)) {
    Tagged<Context> native_context = isolate->context()->native_context();
    return native_context->number_function()->initial_map();
  }

  const Tagged<HeapObject> heap_object = Cast<HeapObject>(obj);
  return heap_object->map()->GetPrototypeChainRootMap(isolate);
}

// static
Tagged<Smi> Object::GetOrCreateHash(Tagged<Object> obj, Isolate* isolate) {
  DisallowGarbageCollection no_gc;
  Tagged<Object> hash = Object::GetSimpleHash(obj);
  if (IsSmi(hash)) return Cast<Smi>(hash);

  DCHECK(IsJSReceiver(obj));
  return Cast<JSReceiver>(obj)->GetOrCreateIdentityHash(isolate);
}

// static
bool Object::SameValue(Tagged<Object> obj, Tagged<Object> other) {
  if (other == obj) return true;

  if (IsNumber(obj) && IsNumber(other)) {
    return SameNumberValue(Object::NumberValue(Cast<Number>(obj)),
                           Object::NumberValue(Cast<Number>(other)));
  }
  if (IsString(obj) && IsString(other)) {
    return Cast<String>(obj)->Equals(Cast<String>(other));
  }
  if (IsBigInt(obj) && IsBigInt(other)) {
    return BigInt::EqualToBigInt(Cast<BigInt>(obj), Cast<BigInt>(other));
  }
  return false;
}

// static
bool Object::SameValueZero(Tagged<Object> obj, Tagged<Object> other) {
  if (other == obj) return true;

  if (IsNumber(obj) && IsNumber(other)) {
    double this_value = Object::NumberValue(Cast<Number>(obj));
    double other_value = Object::NumberValue(Cast<Number>(other));
    // +0 == -0 is true
    return this_value == other_value ||
           (std::isnan(this_value) && std::isnan(other_value));
  }
  if (IsString(obj) && IsString(other)) {
    return Cast<String>(obj)->Equals(Cast<String>(other));
  }
  if (IsBigInt(obj) && IsBigInt(other)) {
    return BigInt::EqualToBigInt(Cast<BigInt>(obj), Cast<BigInt>(other));
  }
  return false;
}

MaybeDirectHandle<Object> Object::ArraySpeciesConstructor(
    Isolate* isolate, DirectHandle<JSAny> original_array) {
  DirectHandle<Object> default_species = isolate->array_function();
  if (!v8_flags.builtin_subclassing) return default_species;
  if (IsJSArray(*original_array) &&
      Cast<JSArray>(original_array)->HasArrayPrototype(isolate) &&
      Protectors::IsArraySpeciesLookupChainIntact(isolate)) {
    return default_species;
  }
  DirectHandle<Object> constructor = isolate->factory()->undefined_value();
  Maybe<bool> is_array = IsArray(original_array);
  MAYBE_RETURN_NULL(is_array);
  if (is_array.FromJust()) {
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, constructor,
        Object::GetProperty(isolate, original_array,
                            isolate->factory()->constructor_string()));
    if (IsConstructor(*constructor)) {
      DirectHandle<NativeContext> constructor_context;
      ASSIGN_RETURN_ON_EXCEPTION(
          isolate, constructor_context,
          JSReceiver::GetFunctionRealm(Cast<JSReceiver>(constructor)));
      if (*constructor_context != *isolate->native_context() &&
          *constructor == constructor_context->array_function()) {
        constructor = isolate->factory()->undefined_value();
      }
    }
    if (IsJSReceiver(*constructor)) {
      ASSIGN_RETURN_ON_EXCEPTION(
          isolate, constructor,
          JSReceiver::GetProperty(isolate, Cast<JSReceiver>(constructor),
                                  isolate->factory()->species_symbol()));
      if (IsNull(*constructor, isolate)) {
        constructor = isolate->factory()->undefined_value();
      }
    }
  }
  if (IsUndefined(*constructor, isolate)) {
    return default_species;
  } else {
    if (!IsConstructor(*constructor)) {
      THROW_NEW_ERROR(isolate,
                      NewTypeError(MessageTemplate::kSpeciesNotConstructor));
    }
    return constructor;
  }
}

// ES6 section 7.3.20 SpeciesConstructor ( O, defaultConstructor )
V8_WARN_UNUSED_RESULT MaybeDirectHandle<Object> Object::SpeciesConstructor(
    Isolate* isolate, DirectHandle<JSReceiver> recv,
    DirectHandle<JSFunction> default_ctor) {
  DirectHandle<Object> ctor_obj;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, ctor_obj,
      JSObject::GetProperty(isolate, recv,
                            isolate->factory()->constructor_string()));

  if (IsUndefined(*ctor_obj, isolate)) return default_ctor;

  if (!IsJSReceiver(*ctor_obj)) {
    THROW_NEW_ERROR(isolate,
                    NewTypeError(MessageTemplate::kConstructorNotReceiver));
  }

  DirectHandle<JSReceiver> ctor = Cast<JSReceiver>(ctor_obj);

  DirectHandle<Object> species;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, species,
      JSObject::GetProperty(isolate, ctor,
                            isolate->factory()->species_symbol()));

  if (IsNullOrUndefined(*species, isolate)) {
    return default_ctor;
  }

  if (IsConstructor(*species)) return species;

  THROW_NEW_ERROR(isolate,
                  NewTypeError(MessageTemplate::kSpeciesNotConstructor));
}

// static
bool Object::IterationHasObservableEffects(Tagged<Object> obj) {
  DisallowGarbageCollection no_gc;
  // Check that this object is an array.
  if (!IsJSArray(obj)) return true;
  Tagged<JSArray> array = Cast<JSArray>(obj);

  // Check that we have the original ArrayPrototype.
  Tagged<Object> array_proto = array->map()->prototype();
  if (!IsJSObject(array_proto)) return true;
  Tagged<NativeContext> native_context = array->GetCreationContext().value();
  auto initial_array_prototype = native_context->initial_array_prototype();
  if (initial_array_prototype != array_proto) return true;

  Isolate* isolate = Isolate::Current();
  // Check that the ArrayPrototype hasn't been modified in a way that would
  // affect iteration.
  if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return true;

  // For FastPacked kinds, iteration will have the same effect as simply
  // accessing each property in order.
  ElementsKind array_kind = array->GetElementsKind();
  if (IsFastPackedElementsKind(array_kind)) return false;

  // For FastHoley kinds, an element access on a hole would cause a lookup on
  // the prototype. This could have different results if the prototype has been
  // changed.
  if (IsHoleyElementsKind(array_kind) &&
      Protectors::IsNoElementsIntact(isolate)) {
    return false;
  }
  return true;
}

// static
bool Object::IsCodeLike(Tagged<Object> obj, Isolate* isolate) {
  DisallowGarbageCollection no_gc;
  return IsJSReceiver(obj) && Cast<JSReceiver>(obj)->IsCodeLike(isolate);
}

void ShortPrint(Tagged<Object> obj, FILE* out) {
  OFStream os(out);
  os << Brief(obj);
}

void ShortPrint(Tagged<Object> obj, StringStream* accumulator) {
  std::ostringstream os;
  os << Brief(obj);
  accumulator->Add(os.str().c_str());
}

void ShortPrint(Tagged<Object> obj, std::ostream& os) { os << Brief(obj); }

std::ostream& operator<<(std::ostream& os, Tagged<Object> obj) {
  ShortPrint(obj, os);
  return os;
}

std::ostream& operator<<(std::ostream& os, Object::Conversion kind) {
  switch (kind) {
    case Object::Conversion::kToNumber:
      return os << "ToNumber";
    case Object::Conversion::kToNumeric:
      return os << "ToNumeric";
  }
  UNREACHABLE();
}

std::ostream& operator<<(std::ostream& os, const Brief& v) {
  Tagged<MaybeObject> maybe_object(v.value);
  Tagged<Smi> smi;
  Tagged<HeapObject> heap_object;
  if (maybe_object.ToSmi(&smi)) {
    Smi::SmiPrint(smi, os);
  } else if (maybe_object.IsCleared()) {
    os << "[cleared]";
  } else if (maybe_object.GetHeapObjectIfWeak(&heap_object)) {
    os << "[weak] ";
    heap_object->HeapObjectShortPrint(os);
  } else if (maybe_object.GetHeapObjectIfStrong(&heap_object)) {
    heap_object->HeapObjectShortPrint(os);
  } else {
    UNREACHABLE();
  }
  return os;
}

// static
void Smi::SmiPrint(Tagged<Smi> smi, std::ostream& os) { os << smi.value(); }

void Struct::BriefPrintDetails(std::ostream& os) {}

void StructLayout::BriefPrintDetails(std::ostream& os) {}

void Tuple2::BriefPrintDetails(std::ostream& os) {
  os << " " << Brief(value1()) << ", " << Brief(value2());
}

void MegaDomHandler::BriefPrintDetails(std::ostream& os) {
  os << " " << Brief(accessor(kAcquireLoad)) << ", " << Brief(context());
}

void ClassPositions::BriefPrintDetails(std::ostream& os) {
  os << " " << start() << ", " << end();
}

void CallableTask::BriefPrintDetails(std::ostream& os) {
  os << " callable=" << Brief(callable());
}

int HeapObjectLayout::SizeFromMap(Tagged<Map> map) const {
  return Tagged<HeapObject>(this)->SizeFromMap(map);
}

SafeHeapObjectSize HeapObjectLayout::SafeSizeFromMap(Tagged<Map> map) const {
  return Tagged<HeapObject>(this)->SafeSizeFromMap(map);
}

int HeapObject::SizeFromMap(Tagged<Map> map) const {
  int instance_size = map->instance_size();
  if (instance_size != kVariableSizeSentinel) return instance_size;
  // Only inline the most frequent cases.
  InstanceType instance_type = map->instance_type();
  if (base::IsInRange(instance_type, FIRST_FIXED_ARRAY_TYPE,
                      LAST_FIXED_ARRAY_TYPE)) {
    return UncheckedCast<FixedArray>(*this)->AllocatedSize();
  }
#define CASE(TypeCamelCase, TYPE_UPPER_CASE)                     \
  if (instance_type == TYPE_UPPER_CASE##_TYPE) {                 \
    return UncheckedCast<TypeCamelCase>(*this)->AllocatedSize(); \
  }
  SIMPLE_HEAP_OBJECT_LIST2(CASE)
#undef CASE
  if (instance_type == SLOPPY_ARGUMENTS_ELEMENTS_TYPE) {
    return UncheckedCast<SloppyArgumentsElements>(*this)->AllocatedSize();
  }
  if (base::IsInRange(instance_type, FIRST_CONTEXT_TYPE, LAST_CONTEXT_TYPE)) {
    if (instance_type == NATIVE_CONTEXT_TYPE) return NativeContext::kSize;
    return Context::SizeFor(UncheckedCast<Context>(*this)->length());
  }
  if (instance_type == SEQ_ONE_BYTE_STRING_TYPE ||
      instance_type == INTERNALIZED_ONE_BYTE_STRING_TYPE ||
      instance_type == SHARED_SEQ_ONE_BYTE_STRING_TYPE) {
    // Strings may get concurrently truncated, hence we have to access its
    // length synchronized.
    return SeqOneByteString::SizeFor(
        UncheckedCast<SeqOneByteString>(*this)->length(kAcquireLoad));
  }
  if (instance_type == BYTECODE_ARRAY_TYPE) {
    return BytecodeArray::SizeFor(
        UncheckedCast<BytecodeArray>(*this)->length(kAcquireLoad));
  }
  if (instance_type == FREE_SPACE_TYPE) {
    return UncheckedCast<FreeSpace>(*this)->size(kRelaxedLoad);
  }
  if (instance_type == SEQ_TWO_BYTE_STRING_TYPE ||
      instance_type == INTERNALIZED_TWO_BYTE_STRING_TYPE ||
      instance_type == SHARED_SEQ_TWO_BYTE_STRING_TYPE) {
    // Strings may get concurrently truncated, hence we have to access its
    // length synchronized.
    return SeqTwoByteString::SizeFor(
        UncheckedCast<SeqTwoByteString>(*this)->length(kAcquireLoad));
  }
  if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) {
    return UncheckedCast<FixedDoubleArray>(*this)->AllocatedSize();
  }
  if (instance_type == TRUSTED_FIXED_ARRAY_TYPE) {
    return UncheckedCast<TrustedFixedArray>(*this)->AllocatedSize();
  }
  if (instance_type == PROTECTED_FIXED_ARRAY_TYPE) {
    return UncheckedCast<ProtectedFixedArray>(*this)->AllocatedSize();
  }
  if (instance_type == PROTECTED_WEAK_FIXED_ARRAY_TYPE) {
    return UncheckedCast<ProtectedWeakFixedArray>(*this)->AllocatedSize();
  }
  if (instance_type == TRUSTED_WEAK_FIXED_ARRAY_TYPE) {
    return UncheckedCast<TrustedWeakFixedArray>(*this)->AllocatedSize();
  }
  if (instance_type == TRUSTED_BYTE_ARRAY_TYPE) {
    return UncheckedCast<TrustedByteArray>(*this)->AllocatedSize();
  }
  if (instance_type == FEEDBACK_METADATA_TYPE) {
    return UncheckedCast<FeedbackMetadata>(*this)->AllocatedSize();
  }
  if (base::IsInRange(instance_type, FIRST_DESCRIPTOR_ARRAY_TYPE,
                      LAST_DESCRIPTOR_ARRAY_TYPE)) {
    return DescriptorArray::SizeFor(
        UncheckedCast<DescriptorArray>(*this)->number_of_all_descriptors());
  }
  if (base::IsInRange(instance_type, FIRST_WEAK_FIXED_ARRAY_TYPE,
                      LAST_WEAK_FIXED_ARRAY_TYPE)) {
    return UncheckedCast<WeakFixedArray>(*this)->AllocatedSize();
  }
  if (instance_type == WEAK_ARRAY_LIST_TYPE) {
    return UncheckedCast<WeakArrayList>(*this)->AllocatedSize();
  }
  if (instance_type == SMALL_ORDERED_HASH_SET_TYPE) {
    return SmallOrderedHashSet::SizeFor(
        UncheckedCast<SmallOrderedHashSet>(*this)->Capacity());
  }
  if (instance_type == SMALL_ORDERED_HASH_MAP_TYPE) {
    return SmallOrderedHashMap::SizeFor(
        UncheckedCast<SmallOrderedHashMap>(*this)->Capacity());
  }
  if (instance_type == SMALL_ORDERED_NAME_DICTIONARY_TYPE) {
    return SmallOrderedNameDictionary::SizeFor(
        UncheckedCast<SmallOrderedNameDictionary>(*this)->Capacity());
  }
  if (instance_type == SWISS_NAME_DICTIONARY_TYPE) {
    return SwissNameDictionary::SizeFor(
        UncheckedCast<SwissNameDictionary>(*this)->Capacity());
  }
  if (instance_type == PROPERTY_ARRAY_TYPE) {
    return PropertyArray::SizeFor(
        UncheckedCast<PropertyArray>(*this)->length(kAcquireLoad));
  }
  if (instance_type == FEEDBACK_VECTOR_TYPE) {
    return FeedbackVector::SizeFor(
        UncheckedCast<FeedbackVector>(*this)->length());
  }
  if (instance_type == BIGINT_TYPE) {
    return BigInt::SizeFor(UncheckedCast<BigInt>(*this)->length());
  }
  if (instance_type == PREPARSE_DATA_TYPE) {
    Tagged<PreparseData> data = UncheckedCast<PreparseData>(*this);
    return PreparseData::SizeFor(data->data_length(), data->children_length());
  }
#define MAKE_TORQUE_SIZE_FOR(TYPE, TypeName)                \
  if (instance_type == TYPE) {                              \
    return UncheckedCast<TypeName>(*this)->AllocatedSize(); \
  }
  TORQUE_INSTANCE_TYPE_TO_BODY_DESCRIPTOR_LIST(MAKE_TORQUE_SIZE_FOR)
#undef MAKE_TORQUE_SIZE_FOR

  if (instance_type == INSTRUCTION_STREAM_TYPE) {
    return UncheckedCast<InstructionStream>(*this)->Size();
  }
  if (instance_type == COVERAGE_INFO_TYPE) {
    return CoverageInfo::SizeFor(
        UncheckedCast<CoverageInfo>(*this)->slot_count());
  }
#if V8_ENABLE_WEBASSEMBLY
  if (instance_type == WASM_TYPE_INFO_TYPE) {
    return WasmTypeInfo::SizeFor(
        UncheckedCast<WasmTypeInfo>(*this)->supertypes_length());
  }
  if (instance_type == WASM_STRUCT_TYPE) {
    return WasmStruct::GcSafeSize(map);
  }
  if (instance_type == WASM_ARRAY_TYPE) {
    return WasmArray::SizeFor(map, UncheckedCast<WasmArray>(*this)->length());
  }
  if (instance_type == WASM_NULL_TYPE) {
    return WasmNull::kSize;
  }
  if (instance_type == WASM_DISPATCH_TABLE_TYPE) {
    return WasmDispatchTable::SizeFor(
        UncheckedCast<WasmDispatchTable>(*this)->capacity());
  }
  if (instance_type == WASM_DISPATCH_TABLE_FOR_IMPORTS_TYPE) {
    return WasmDispatchTableForImports::SizeFor(
        UncheckedCast<WasmDispatchTableForImports>(*this)->length());
  }
#endif  // V8_ENABLE_WEBASSEMBLY
  if (instance_type == DOUBLE_STRING_CACHE_TYPE) {
    return DoubleStringCache::SizeFor(
        UncheckedCast<DoubleStringCache>(*this)->capacity());
  }
  if (instance_type == EMBEDDER_DATA_ARRAY_TYPE) {
    return EmbedderDataArray::SizeFor(
        UncheckedCast<EmbedderDataArray>(*this)->length());
  }
  if (instance_type == HOLE_TYPE) {
    return sizeof(Hole);
  }
  UNREACHABLE();
}

SafeHeapObjectSize HeapObject::SafeSizeFromMap(Tagged<Map> map) const {
  const int unsafe_size = SizeFromMap(map);
  // The uint32_t cast may convert a negative number into any index within 4G.
  // Subsequently converting this size to other unsigned sizes is safe as
  // there's just extensions with zeros.
  return SafeHeapObjectSize(static_cast<uint32_t>(unsafe_size));
}

bool HeapObject::NeedsRehashing(PtrComprCageBase cage_base) const {
  return NeedsRehashing(map(cage_base)->instance_type());
}

bool HeapObject::NeedsRehashing(InstanceType instance_type) const {
  if (V8_EXTERNAL_CODE_SPACE_BOOL) {
    // Use map() only when it's guaranteed that it's not an InstructionStream
    // object.
    DCHECK_IMPLIES(instance_type != INSTRUCTION_STREAM_TYPE,
                   instance_type == map()->instance_type());
  } else {
    DCHECK_EQ(instance_type, map()->instance_type());
  }
  switch (instance_type) {
    case DESCRIPTOR_ARRAY_TYPE:
    case STRONG_DESCRIPTOR_ARRAY_TYPE:
      return Cast<DescriptorArray>(*this)->number_of_descriptors() > 1;
    case TRANSITION_ARRAY_TYPE:
      return Cast<TransitionArray>(*this)->number_of_transitions() > 1;
    case ORDERED_HASH_MAP_TYPE:
    case ORDERED_HASH_SET_TYPE:
      return false;  // We'll rehash from the JSMap or JSSet referencing them.
    case NAME_DICTIONARY_TYPE:
    case NAME_TO_INDEX_HASH_TABLE_TYPE:
    case REGISTERED_SYMBOL_TABLE_TYPE:
    case GLOBAL_DICTIONARY_TYPE:
    case NUMBER_DICTIONARY_TYPE:
    case SIMPLE_NUMBER_DICTIONARY_TYPE:
    case HASH_TABLE_TYPE:
    case SMALL_ORDERED_HASH_MAP_TYPE:
    case SMALL_ORDERED_HASH_SET_TYPE:
    case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
    case SWISS_NAME_DICTIONARY_TYPE:
    case JS_MAP_TYPE:
    case JS_SET_TYPE:
      return true;
    default:
      return false;
  }
  UNREACHABLE();
}

bool HeapObject::CanBeRehashed(PtrComprCageBase cage_base) const {
  DCHECK(NeedsRehashing(cage_base));
  switch (map(cage_base)->instance_type()) {
    case JS_MAP_TYPE:
    case JS_SET_TYPE:
      return true;
    case ORDERED_HASH_MAP_TYPE:
    case ORDERED_HASH_SET_TYPE:
      UNREACHABLE();  // We'll rehash from the JSMap or JSSet referencing them.
    case ORDERED_NAME_DICTIONARY_TYPE:
      return false;
    case NAME_DICTIONARY_TYPE:
    case NAME_TO_INDEX_HASH_TABLE_TYPE:
    case REGISTERED_SYMBOL_TABLE_TYPE:
    case GLOBAL_DICTIONARY_TYPE:
    case NUMBER_DICTIONARY_TYPE:
    case SIMPLE_NUMBER_DICTIONARY_TYPE:
    case SWISS_NAME_DICTIONARY_TYPE:
      return true;
    case DESCRIPTOR_ARRAY_TYPE:
    case STRONG_DESCRIPTOR_ARRAY_TYPE:
      return true;
    case TRANSITION_ARRAY_TYPE:
      return true;
    case SMALL_ORDERED_HASH_MAP_TYPE:
      return Cast<SmallOrderedHashMap>(*this)->NumberOfElements() == 0;
    case SMALL_ORDERED_HASH_SET_TYPE:
      return Cast<SmallOrderedHashMap>(*this)->NumberOfElements() == 0;
    case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
      return Cast<SmallOrderedNameDictionary>(*this)->NumberOfElements() == 0;
    default:
      return false;
  }
  UNREACHABLE();
}

template <typename IsolateT>
void HeapObject::RehashBasedOnMap(IsolateT* isolate) {
  switch (map(isolate)->instance_type()) {
    case HASH_TABLE_TYPE:
      Cast<ObjectHashTable>(*this)->Rehash(isolate);
      break;
    case NAME_DICTIONARY_TYPE:
      Cast<NameDictionary>(*this)->Rehash(isolate);
      break;
    case NAME_TO_INDEX_HASH_TABLE_TYPE:
      Cast<NameToIndexHashTable>(*this)->Rehash(isolate);
      break;
    case REGISTERED_SYMBOL_TABLE_TYPE:
      Cast<RegisteredSymbolTable>(*this)->Rehash(isolate);
      break;
    case SWISS_NAME_DICTIONARY_TYPE:
      Cast<SwissNameDictionary>(*this)->Rehash(isolate);
      break;
    case GLOBAL_DICTIONARY_TYPE:
      Cast<GlobalDictionary>(*this)->Rehash(isolate);
      break;
    case NUMBER_DICTIONARY_TYPE:
      Cast<NumberDictionary>(*this)->Rehash(isolate);
      break;
    case SIMPLE_NAME_DICTIONARY_TYPE:
      Cast<SimpleNameDictionary>(*this)->Rehash(isolate);
      break;
    case SIMPLE_NUMBER_DICTIONARY_TYPE:
      Cast<SimpleNumberDictionary>(*this)->Rehash(isolate);
      break;
    case DESCRIPTOR_ARRAY_TYPE:
    case STRONG_DESCRIPTOR_ARRAY_TYPE:
      DCHECK_LE(1, Cast<DescriptorArray>(*this)->number_of_descriptors());
      Cast<DescriptorArray>(*this)->Sort();
      break;
    case TRANSITION_ARRAY_TYPE:
      Cast<TransitionArray>(*this)->Sort();
      break;
    case SMALL_ORDERED_HASH_MAP_TYPE:
      DCHECK_EQ(0, Cast<SmallOrderedHashMap>(*this)->NumberOfElements());
      break;
    case SMALL_ORDERED_HASH_SET_TYPE:
      DCHECK_EQ(0, Cast<SmallOrderedHashSet>(*this)->NumberOfElements());
      break;
    case ORDERED_HASH_MAP_TYPE:
    case ORDERED_HASH_SET_TYPE:
      UNREACHABLE();  // We'll rehash from the JSMap or JSSet referencing them.
    case JS_MAP_TYPE: {
      Cast<JSMap>(*this)->Rehash(isolate->AsIsolate());
      break;
    }
    case JS_SET_TYPE: {
      Cast<JSSet>(*this)->Rehash(isolate->AsIsolate());
      break;
    }
    case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
      DCHECK_EQ(0, Cast<SmallOrderedNameDictionary>(*this)->NumberOfElements());
      break;
    case INTERNALIZED_ONE_BYTE_STRING_TYPE:
    case INTERNALIZED_TWO_BYTE_STRING_TYPE:
      // Rare case, rehash read-only space strings before they are sealed.
      DCHECK(ReadOnlyHeap::Contains(*this));
      Cast<String>(*this)->EnsureHash();
      break;
    default:
      // TODO(ishell): remove once b/326043780 is no longer an issue.
      isolate->AsIsolate()->PushParamsAndDie(
          reinterpret_cast<void*>(ptr()), reinterpret_cast<void*>(map().ptr()),
          reinterpret_cast<void*>(
              static_cast<uintptr_t>(map()->instance_type())));
      UNREACHABLE();
  }
}
template void HeapObject::RehashBasedOnMap(Isolate* isolate);
template void HeapObject::RehashBasedOnMap(LocalIsolate* isolate);

void DescriptorArray::GeneralizeAllFields(bool clear_constness) {
  int length = number_of_descriptors();
  for (InternalIndex i : InternalIndex::Range(length)) {
    PropertyDetails details = GetDetails(i);
    details = details.CopyWithRepresentation(Representation::Tagged());
    if (details.location() == PropertyLocation::kField) {
      // Since constness is not propagated across proto transitions we must
      // clear the flag here.
      if (clear_constness) {
        details = details.CopyWithConstness(PropertyConstness::kMutable);
      }
      DCHECK_EQ(PropertyKind::kData, details.kind());
      SetValue(i, FieldType::Any());
    }
    SetDetails(i, details);
  }
}

MaybeHandle<Object> Object::SetProperty(Isolate* isolate,
                                        DirectHandle<JSAny> object,
                                        DirectHandle<Name> name,
                                        DirectHandle<Object> value,
                                        StoreOrigin store_origin,
                                        Maybe<ShouldThrow> should_throw) {
  LookupIterator it(isolate, object, name);
  MAYBE_RETURN_NULL(SetProperty(&it, value, store_origin, should_throw));
  return indirect_handle(value, isolate);
}

Maybe<bool> Object::SetPropertyInternal(LookupIterator* it,
                                        DirectHandle<Object> value,
                                        Maybe<ShouldThrow> should_throw,
                                        StoreOrigin store_origin, bool* found) {
  it->UpdateProtector();
  DCHECK(it->IsFound());

  // Make sure that the top context does not change when doing callbacks or
  // interceptor calls.
  AssertNoContextChange ncc(it->isolate());

  for (;; it->Next()) {
    switch (it->state()) {
      case LookupIterator::ACCESS_CHECK:
        if (it->HasAccess()) continue;
        // Check whether it makes sense to reuse the lookup iterator. Here it
        // might still call into setters up the prototype chain.
        return JSObject::SetPropertyWithFailedAccessCheck(it, value,
                                                          should_throw);

      case LookupIterator::JSPROXY: {
        DirectHandle<JSAny> receiver = it->GetReceiver();
        // In case of global IC, the receiver is the global object. Replace by
        // the global proxy.
        if (IsJSGlobalObject(*receiver)) {
          receiver = direct_handle(
              Cast<JSGlobalObject>(*receiver)->global_proxy(), it->isolate());
        }
        return JSProxy::SetProperty(it->GetHolder<JSProxy>(), it->GetName(),
                                    value, receiver, should_throw);
      }

      case LookupIterator::WASM_OBJECT:
        continue;  // Continue to the prototype, if present.

      case LookupIterator::INTERCEPTOR: {
        if (it->HolderIsReceiverOrHiddenPrototype()) {
          InterceptorResult result;
          if (!JSObject::SetPropertyWithInterceptor(it, should_throw, value)
                   .To(&result)) {
            // An exception was thrown in the interceptor. Propagate.
            return Nothing<bool>();
          }
          switch (result) {
            case InterceptorResult::kFalse: {
              // Throw TypeError if necessary in case the callback failed
              // to set the property.
              Isolate* isolate = it->isolate();
              RETURN_FAILURE(
                  isolate, GetShouldThrow(isolate, should_throw),
                  NewTypeError(MessageTemplate::kStrictCannotSetProperty,
                               it->GetName(), it->GetReceiver()));
            }
            case InterceptorResult::kTrue:
              return Just(true);
            case InterceptorResult::kNotIntercepted:
              // Proceed lookup.
              break;
          }
          // Assuming that the callback has side effects, we use
          // Object::SetSuperProperty() which works properly regardless on
          // whether the property was present on the receiver or not when
          // storing to the receiver.
          // Proceed lookup from the next state.
          it->Next();
        } else {
          Maybe<PropertyAttributes> maybe_attributes =
              JSObject::GetPropertyAttributesWithInterceptor(it);
          if (maybe_attributes.IsNothing()) return Nothing<bool>();
          if ((maybe_attributes.FromJust() & READ_ONLY) != 0) {
            return WriteToReadOnlyProperty(it, value, should_throw);
          }
          // At this point we might have called interceptor's query or getter
          // callback. Assuming that the callbacks have side effects, we use
          // Object::SetSuperProperty() which works properly regardless on
          // whether the property was present on the receiver or not when
          // storing to the receiver.
          if (maybe_attributes.FromJust() == ABSENT) {
            // Proceed lookup from the next state.
            it->Next();
          } else {
            // Finish lookup in order to make Object::SetSuperProperty() store
            // property to the receiver.
            it->NotFound();
          }
        }
        return Object::SetSuperProperty(it, value, store_origin, should_throw);
      }

      case LookupIterator::ACCESSOR: {
        if (it->IsReadOnly()) {
          return WriteToReadOnlyProperty(it, value, should_throw);
        }
        DirectHandle<Object> accessors = it->GetAccessors();
        if (IsAccessorInfo(*accessors) &&
            !it->HolderIsReceiverOrHiddenPrototype()) {
          *found = false;
          return Nothing<bool>();
        }
        return SetPropertyWithAccessor(it, value, should_throw);
      }
      case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND: {
        // IntegerIndexedElementSet converts value to a Number/BigInt prior to
        // the bounds check. The bounds check has already happened here, but
        // perform the possibly effectful ToNumber (or ToBigInt) operation
        // anyways.
        DirectHandle<JSTypedArray> holder = it->GetHolder<JSTypedArray>();

        // The index found case uses HolderIsReceiverOrHiddenPrototype()
        // in below "case LookupIterator::DATA" to check
        // "i. If SameValue(O, Receiver) is true..." of the spec.
        // https://tc39.es/ecma262/#sec-typedarray-set
        if (it->HolderIsReceiver()) {
          DirectHandle<Object> converted_value;

          if (holder->type() == kExternalBigInt64Array ||
              holder->type() == kExternalBigUint64Array) {
            ASSIGN_RETURN_ON_EXCEPTION(
                it->isolate(), converted_value,
                BigInt::FromObject(it->isolate(), value));
          } else {
            ASSIGN_RETURN_ON_EXCEPTION(it->isolate(), converted_value,
                                       Object::ToNumber(it->isolate(), value));
          }
          // For RAB/GSABs, the above conversion might grow the buffer so that
          // the index is no longer out of bounds. Redo the bounds check and try
          // again.
          it->RecheckTypedArrayBounds();
          value = converted_value;
        }
        if (it->state() != LookupIterator::DATA) {
          // Still out of bounds.
          DCHECK_EQ(it->state(), LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND);

          // FIXME: Throw a TypeError if the holder is detached here
          // (IntegerIndexedElementSet step 5).

          // TODO(verwaest): Per spec, we should return false here (steps 6-9
          // in IntegerIndexedElementSet), resulting in an exception being
          // thrown on OOB accesses in strict code. Historically, v8 has not
          // done made this change due to uncertainty about web compat.
          // (v8:4901)
          return Just(true);
        }
        [[fallthrough]];
      }

      case LookupIterator::DATA:
        if (it->IsReadOnly()) {
          return WriteToReadOnlyProperty(it, value, should_throw);
        }
        if (it->HolderIsReceiverOrHiddenPrototype()) {
          return SetDataProperty(it, value);
        }
        [[fallthrough]];
      case LookupIterator::NOT_FOUND:
      case LookupIterator::TRANSITION:
        *found = false;
        return Nothing<bool>();

      case LookupIterator::STRING_LOOKUP_START_OBJECT:
        return WriteToReadOnlyProperty(it, value, should_throw);
    }
    UNREACHABLE();
  }
}

bool Object::CheckContextualStoreToJSGlobalObject(
    LookupIterator* it, Maybe<ShouldThrow> should_throw) {
  Isolate* isolate = it->isolate();

  if (IsJSGlobalObject(*it->GetReceiver(), isolate) &&
      (GetShouldThrow(isolate, should_throw) == ShouldThrow::kThrowOnError)) {
    if (it->state() == LookupIterator::TRANSITION) {
      // The property cell that we have created is garbage because we are going
      // to throw now instead of putting it into the global dictionary. However,
      // the cell might already have been stored into the feedback vector, so
      // we must invalidate it nevertheless.
      it->transition_cell()->ClearAndInvalidate(isolate);
    }
    isolate->Throw(*isolate->factory()->NewReferenceError(
        MessageTemplate::kNotDefined, it->GetName()));
    return false;
  }
  return true;
}

Maybe<bool> Object::SetProperty(LookupIterator* it, DirectHandle<Object> value,
                                StoreOrigin store_origin,
                                Maybe<ShouldThrow> should_throw) {
  if (it->IsFound()) {
    bool found = true;
    Maybe<bool> result =
        SetPropertyInternal(it, value, should_throw, store_origin, &found);
    if (found) return result;
  }

  if (!CheckContextualStoreToJSGlobalObject(it, should_throw)) {
    return Nothing<bool>();
  }
  return AddDataProperty(it, value, NONE, should_throw, store_origin);
}

Maybe<bool> Object::SetSuperProperty(LookupIterator* it,
                                     DirectHandle<Object> value,
                                     StoreOrigin store_origin,
                                     Maybe<ShouldThrow> should_throw) {
  Isolate* isolate = it->isolate();

  if (it->IsFound()) {
    bool found = true;
    Maybe<bool> result =
        SetPropertyInternal(it, value, should_throw, store_origin, &found);
    if (found) return result;
  }

  it->UpdateProtector();

  // The property either doesn't exist on the holder or exists there as a data
  // property.

  if (!IsJSReceiver(*it->GetReceiver())) {
    return WriteToReadOnlyProperty(it, value, should_throw);
  }
  DirectHandle<JSReceiver> receiver = Cast<JSReceiver>(it->GetReceiver());

  // Note, the callers rely on the fact that this code is redoing the full own
  // lookup from scratch.
  LookupIterator own_lookup(isolate, receiver, it->GetKey(),
                            LookupIterator::OWN);
  for (;; own_lookup.Next()) {
    switch (own_lookup.state()) {
      case LookupIterator::ACCESS_CHECK:
        if (!own_lookup.HasAccess()) {
          return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value,
                                                            should_throw);
        }
        continue;

      case LookupIterator::ACCESSOR:
        if (IsAccessorInfo(*own_lookup.GetAccessors())) {
          if (own_lookup.IsReadOnly()) {
            return WriteToReadOnlyProperty(&own_lookup, value, should_throw);
          }
          return Object::SetPropertyWithAccessor(&own_lookup, value,
                                                 should_throw);
        }
        [[fallthrough]];
      case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
        return RedefineIncompatibleProperty(isolate, it->GetName(), value,
                                            should_throw);

      case LookupIterator::STRING_LOOKUP_START_OBJECT:
        UNREACHABLE();

      case LookupIterator::DATA: {
        if (own_lookup.IsReadOnly()) {
          return WriteToReadOnlyProperty(&own_lookup, value, should_throw);
        }
        return SetDataProperty(&own_lookup, value);
      }

      case LookupIterator::INTERCEPTOR:
      case LookupIterator::JSPROXY: {
        PropertyDescriptor desc;
        Maybe<bool> owned =
            JSReceiver::GetOwnPropertyDescriptor(&own_lookup, &desc);
        MAYBE_RETURN(owned, Nothing<bool>());
        if (!owned.FromJust()) {
          // |own_lookup| might become outdated at this point anyway.
          // TODO(leszeks): Remove this restart since we don't really use the
          // lookup iterator after this.
          own_lookup.Restart();
          if (!CheckContextualStoreToJSGlobalObject(&own_lookup,
                                                    should_throw)) {
            return Nothing<bool>();
          }
          return JSReceiver::CreateDataProperty(isolate, receiver, it->GetKey(),
                                                value, should_throw);
        }
        if (PropertyDescriptor::IsAccessorDescriptor(&desc) ||
            !desc.writable()) {
          return RedefineIncompatibleProperty(isolate, it->GetName(), value,
                                              should_throw);
        }

        PropertyDescriptor value_desc;
        value_desc.set_value(Cast<JSAny>(value));
        return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(),
                                             &value_desc, should_throw);
      }

      case LookupIterator::NOT_FOUND:
        if (!CheckContextualStoreToJSGlobalObject(&own_lookup, should_throw)) {
          return Nothing<bool>();
        }
        return AddDataProperty(&own_lookup, value, NONE, should_throw,
                               store_origin);

      case LookupIterator::WASM_OBJECT:
        RETURN_FAILURE(it->isolate(), kThrowOnError,
                       NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));

      case LookupIterator::TRANSITION:
        UNREACHABLE();
    }
    UNREACHABLE();
  }
}

Maybe<bool> Object::CannotCreateProperty(Isolate* isolate,
                                         DirectHandle<JSAny> receiver,
                                         DirectHandle<Object> name,
                                         Maybe<ShouldThrow> should_throw) {
  RETURN_FAILURE(
      isolate, GetShouldThrow(isolate, should_throw),
      NewTypeError(MessageTemplate::kStrictCannotCreateProperty, name,
                   Object::TypeOf(isolate, receiver), receiver));
}

Maybe<bool> Object::WriteToReadOnlyProperty(
    LookupIterator* it, DirectHandle<Object> value,
    Maybe<ShouldThrow> maybe_should_throw) {
  ShouldThrow should_throw = GetShouldThrow(it->isolate(), maybe_should_throw);
  if (it->IsFound() &&
      it->state() != LookupIterator::STRING_LOOKUP_START_OBJECT &&
      !it->HolderIsReceiver()) {
    // "Override mistake" attempted, record a use count to track this per
    // v8:8175
    v8::Isolate::UseCounterFeature feature =
        should_throw == kThrowOnError
            ? v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeStrict
            : v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeSloppy;
    it->isolate()->CountUsage(feature);
  }
  return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(),
                                 it->GetName(), value, should_throw);
}

Maybe<bool> Object::WriteToReadOnlyProperty(Isolate* isolate,
                                            DirectHandle<JSAny> receiver,
                                            DirectHandle<Object> name,
                                            DirectHandle<Object> value,
                                            ShouldThrow should_throw) {
  RETURN_FAILURE(isolate, should_throw,
                 NewTypeError(MessageTemplate::kStrictReadOnlyProperty, name,
                              Object::TypeOf(isolate, receiver), receiver));
}

Maybe<bool> Object::RedefineIncompatibleProperty(
    Isolate* isolate, DirectHandle<Object> name, DirectHandle<Object> value,
    Maybe<ShouldThrow> should_throw) {
  RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                 NewTypeError(MessageTemplate::kRedefineDisallowed, name));
}

Maybe<bool> Object::SetDataProperty(LookupIterator* it,
                                    DirectHandle<Object> value) {
  Isolate* isolate = it->isolate();
  DCHECK_IMPLIES(IsJSProxy(*it->GetReceiver(), isolate),
                 it->GetName()->IsPrivateName());
  DCHECK_IMPLIES(!it->IsElement() && it->GetName()->IsPrivateName(),
                 it->state() == LookupIterator::DATA);
  DirectHandle<JSReceiver> receiver = Cast<JSReceiver>(it->GetReceiver());

  // Store on the holder which may be hidden behind the receiver.
  DCHECK(it->HolderIsReceiverOrHiddenPrototype());

  DirectHandle<Object> to_assign = value;
  // Convert the incoming value to a number for storing into typed arrays.
  if (it->IsElement() && IsJSObject(*receiver, isolate) &&
      Cast<JSObject>(*receiver)->HasTypedArrayOrRabGsabTypedArrayElements(
          isolate)) {
    auto receiver_ta = Cast<JSTypedArray>(receiver);
    ElementsKind elements_kind = Cast<JSObject>(*receiver)->GetElementsKind();
    if (IsBigIntTypedArrayElementsKind(elements_kind)) {
      ASSIGN_RETURN_ON_EXCEPTION(isolate, to_assign,
                                 BigInt::FromObject(isolate, value));
      if (V8_UNLIKELY(receiver_ta->IsDetachedOrOutOfBounds() ||
                      it->index() >= receiver_ta->GetLength())) {
        return Just(true);
      }
    } else if (!IsNumber(*value) && !IsUndefined(*value, isolate)) {
      ASSIGN_RETURN_ON_EXCEPTION(isolate, to_assign,
                                 Object::ToNumber(isolate, value));
      if (V8_UNLIKELY(receiver_ta->IsDetachedOrOutOfBounds() ||
                      it->index() >= receiver_ta->GetLength())) {
        return Just(true);
      }
    }
  }

  DCHECK(!IsWasmObject(*receiver, isolate));
  if (V8_UNLIKELY(IsJSSharedStruct(*receiver, isolate) ||
                  IsJSSharedArray(*receiver, isolate))) {
    // Shared structs can only point to primitives or shared values.
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, to_assign, Object::Share(isolate, to_assign, kThrowOnError));
    it->WriteDataValue(to_assign, false);
  } else {
    // Possibly migrate to the most up-to-date map that will be able to store
    // |value| under it->name().
    it->PrepareForDataProperty(to_assign);

    // Write the property value.
    it->WriteDataValue(to_assign, false);
  }

#if VERIFY_HEAP
  if (v8_flags.verify_heap) {
    receiver->HeapObjectVerify(isolate);
  }
#endif
  return Just(true);
}

Maybe<bool> Object::AddDataProperty(LookupIterator* it,
                                    DirectHandle<Object> value,
                                    PropertyAttributes attributes,
                                    Maybe<ShouldThrow> should_throw,
                                    StoreOrigin store_origin,
                                    EnforceDefineSemantics semantics) {
  if (!IsJSReceiver(*it->GetReceiver())) {
    return CannotCreateProperty(it->isolate(), it->GetReceiver(), it->GetName(),
                                should_throw);
  }

  // Private symbols should be installed on JSProxy using
  // JSProxy::SetPrivateSymbol.
  if (IsJSProxy(*it->GetReceiver()) && it->GetName()->IsPrivate() &&
      !it->GetName()->IsPrivateName()) {
    RETURN_FAILURE(it->isolate(), GetShouldThrow(it->isolate(), should_throw),
                   NewTypeError(MessageTemplate::kProxyPrivate));
  }

  DCHECK_NE(LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND, it->state());

  DirectHandle<JSReceiver> receiver = it->GetStoreTarget<JSReceiver>();
  DCHECK_IMPLIES(IsJSProxy(*receiver), it->GetName()->IsPrivateName());
  DCHECK_IMPLIES(IsJSProxy(*receiver),
                 it->state() == LookupIterator::NOT_FOUND);

  // If the receiver is a JSGlobalProxy, store on the prototype (JSGlobalObject)
  // instead. If the prototype is Null, the proxy is detached.
  if (IsJSGlobalProxy(*receiver)) return Just(true);

  Isolate* isolate = it->isolate();

  if (it->ExtendingNonExtensible(receiver)) {
    if (IsWasmObject(*receiver)) {
      RETURN_FAILURE(it->isolate(), kThrowOnError,
                     NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
    }
    bool is_shared_object = IsAlwaysSharedSpaceJSObject(*receiver);
    RETURN_FAILURE(
        isolate, GetShouldThrow(it->isolate(), should_throw),
        NewTypeError(
            semantics == EnforceDefineSemantics::kDefine
                ? (is_shared_object
                       ? MessageTemplate::kDefineDisallowedFixedLayout
                       : MessageTemplate::kDefineDisallowed)
                : (is_shared_object ? MessageTemplate::kObjectFixedLayout
                                    : MessageTemplate::kObjectNotExtensible),
            it->GetName()));
  }

  if (it->IsElement(*receiver)) {
    if (IsJSArray(*receiver)) {
      DirectHandle<JSArray> array = Cast<JSArray>(receiver);
      if (JSArray::WouldChangeReadOnlyLength(array, it->array_index())) {
        RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                       NewTypeError(MessageTemplate::kStrictReadOnlyProperty,
                                    isolate->factory()->length_string(),
                                    Object::TypeOf(isolate, array), array));
      }
    }

    DirectHandle<JSObject> receiver_obj = Cast<JSObject>(receiver);
    MAYBE_RETURN(JSObject::AddDataElement(isolate, receiver_obj,
                                          it->array_index(), value, attributes),
                 Nothing<bool>());
    JSObject::ValidateElements(isolate, *receiver_obj);
    return Just(true);
  }

  return Object::TransitionAndWriteDataProperty(it, value, attributes,
                                                should_throw, store_origin);
}

// static
Maybe<bool> Object::TransitionAndWriteDataProperty(
    LookupIterator* it, DirectHandle<Object> value,
    PropertyAttributes attributes, Maybe<ShouldThrow> should_throw,
    StoreOrigin store_origin) {
  DirectHandle<JSReceiver> receiver = it->GetStoreTarget<JSReceiver>();
  it->UpdateProtector();
  // Migrate to the most up-to-date map that will be able to store |value|
  // under it->name() with |attributes|.
  it->PrepareTransitionToDataProperty(receiver, value, attributes,
                                      store_origin);
  DCHECK_EQ(LookupIterator::TRANSITION, it->state());

  // Apply the transitions -- this can fail if there are too many properties.
  Maybe<bool> transitioned =
      it->ApplyTransitionToDataProperty(receiver, should_throw);
  if (!transitioned.FromMaybe(false)) return transitioned;

  // Write the property value.
  it->WriteDataValue(value, true);

#if VERIFY_HEAP
  if (v8_flags.verify_heap) {
    receiver->HeapObjectVerify(it->isolate());
  }
#endif

  return Just(true);
}

// static
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Object>::MaybeType Object::ShareSlow(
    Isolate* isolate, HandleType<HeapObject> value,
    ShouldThrow throw_if_cannot_be_shared) {
  // Use Object::Share() if value might already be shared.
  DCHECK(!IsShared(*value));

  SharedObjectSafePublishGuard publish_guard;

  if (IsString(*value)) {
    return String::Share(isolate, Cast<String>(value));
  }

  if (IsHeapNumber(*value)) {
    uint64_t bits = Cast<HeapNumber>(*value)->value_as_bits();
    return isolate->factory()
        ->NewHeapNumberFromBits<AllocationType::kSharedOld>(bits);
  }

  if (throw_if_cannot_be_shared == kThrowOnError) {
    THROW_NEW_ERROR(isolate,
                    NewTypeError(MessageTemplate::kCannotBeShared, value));
  }
  return {};
}

template V8_EXPORT_PRIVATE MaybeDirectHandle<Object> Object::ShareSlow(
    Isolate* isolate, DirectHandle<HeapObject> value,
    ShouldThrow throw_if_cannot_be_shared);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Object> Object::ShareSlow(
    Isolate* isolate, IndirectHandle<HeapObject> value,
    ShouldThrow throw_if_cannot_be_shared);

namespace {

template <class T>
int AppendUniqueCallbacks(Isolate* isolate, DirectHandle<ArrayList> callbacks,
                          DirectHandle<typename T::Array> array,
                          int valid_descriptors) {
  int nof_callbacks = callbacks->length();

  // Fill in new callback descriptors.  Process the callbacks from
  // back to front so that the last callback with a given name takes
  // precedence over previously added callbacks with that name.
  for (int i = nof_callbacks - 1; i >= 0; i--) {
    DirectHandle<AccessorInfo> entry(Cast<AccessorInfo>(callbacks->get(i)),
                                     isolate);
    DirectHandle<Name> key(Cast<Name>(entry->name()), isolate);
    DCHECK(IsUniqueName(*key));
    // Check if a descriptor with this name already exists before writing.
    if (!T::Contains(key, entry, valid_descriptors, array)) {
      T::Insert(key, entry, valid_descriptors, array);
      valid_descriptors++;
    }
  }

  return valid_descriptors;
}

struct FixedArrayAppender {
  using Array = FixedArray;
  static bool Contains(DirectHandle<Name> key, DirectHandle<AccessorInfo> entry,
                       int valid_descriptors, DirectHandle<FixedArray> array) {
    for (int i = 0; i < valid_descriptors; i++) {
      if (*key == Cast<AccessorInfo>(array->get(i))->name()) return true;
    }
    return false;
  }
  static void Insert(DirectHandle<Name> key, DirectHandle<AccessorInfo> entry,
                     int valid_descriptors, DirectHandle<FixedArray> array) {
    DisallowGarbageCollection no_gc;
    array->set(valid_descriptors, *entry);
  }
};

}  // namespace

int AccessorInfo::AppendUnique(Isolate* isolate,
                               DirectHandle<Object> descriptors,
                               DirectHandle<FixedArray> array,
                               int valid_descriptors) {
  auto callbacks = Cast<ArrayList>(descriptors);
  DCHECK_GE(array->length(), callbacks->length() + valid_descriptors);
  return AppendUniqueCallbacks<FixedArrayAppender>(isolate, callbacks, array,
                                                   valid_descriptors);
}

void JSProxy::Revoke(DirectHandle<JSProxy> proxy) {
  Isolate* isolate = Isolate::Current();
  // If this fails then some Proxy allocation code path that created
  // revocable Proxies didn't set the bit correctly.
  CHECK(JSProxy::IsRevocableBit::decode(proxy->flags()));
  // ES#sec-proxy-revocation-functions
  if (!proxy->IsRevoked()) {
    // 5. Set p.[[ProxyTarget]] to null.
    proxy->set_target(ReadOnlyRoots(isolate).null_value());
    // 6. Set p.[[ProxyHandler]] to null.
    proxy->set_handler(ReadOnlyRoots(isolate).null_value());
  }
  DCHECK(proxy->IsRevoked());
}

// static
Maybe<bool> JSProxy::IsArray(DirectHandle<JSProxy> proxy) {
  Isolate* isolate = Isolate::Current();
  DirectHandle<JSReceiver> object = Cast<JSReceiver>(proxy);
  for (int i = 0; i < JSProxy::kMaxIterationLimit; i++) {
    proxy = Cast<JSProxy>(object);
    if (proxy->IsRevoked()) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyRevoked,
          isolate->factory()->NewStringFromAsciiChecked("IsArray")));
      return Nothing<bool>();
    }
    object = direct_handle(Cast<JSReceiver>(proxy->target()), isolate);
    if (IsJSArray(*object)) return Just(true);
    if (!IsJSProxy(*object)) return Just(false);
  }

  // Too deep recursion, throw a RangeError.
  isolate->StackOverflow();
  return Nothing<bool>();
}

Maybe<bool> JSProxy::HasProperty(Isolate* isolate, DirectHandle<JSProxy> proxy,
                                 DirectHandle<Name> name) {
  DCHECK(!name->IsPrivate());
  STACK_CHECK(isolate, Nothing<bool>());
  // 1. (Assert)
  // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
  DirectHandle<Object> handler(proxy->handler(), isolate);
  // 3. If handler is null, throw a TypeError exception.
  // 4. Assert: Type(handler) is Object.
  if (proxy->IsRevoked()) {
    isolate->Throw(*isolate->factory()->NewTypeError(
        MessageTemplate::kProxyRevoked, isolate->factory()->has_string()));
    return Nothing<bool>();
  }
  // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
  DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
  // 6. Let trap be ? GetMethod(handler, "has").
  DirectHandle<Object> trap;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap,
      Object::GetMethod(isolate, Cast<JSReceiver>(handler),
                        isolate->factory()->has_string()));
  // 7. If trap is undefined, then
  if (IsUndefined(*trap, isolate)) {
    // 7a. Return target.[[HasProperty]](P).
    return JSReceiver::HasProperty(isolate, target, name);
  }
  // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, P»)).
  DirectHandle<Object> trap_result_obj;
  DirectHandle<Object> args[] = {target, name};
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap_result_obj,
      Execution::Call(isolate, trap, handler, base::VectorOf(args)));
  bool boolean_trap_result = Object::BooleanValue(*trap_result_obj, isolate);
  // 9. If booleanTrapResult is false, then:
  if (!boolean_trap_result) {
    MAYBE_RETURN(JSProxy::CheckHasTrap(isolate, name, target), Nothing<bool>());
  }
  // 10. Return booleanTrapResult.
  return Just(boolean_trap_result);
}

Maybe<bool> JSProxy::CheckHasTrap(Isolate* isolate, DirectHandle<Name> name,
                                  DirectHandle<JSReceiver> target) {
  // 9a. Let targetDesc be ? target.[[GetOwnProperty]](P).
  PropertyDescriptor target_desc;
  Maybe<bool> target_found =
      JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
  MAYBE_RETURN(target_found, Nothing<bool>());
  // 9b. If targetDesc is not undefined, then:
  if (target_found.FromJust()) {
    // 9b i. If targetDesc.[[Configurable]] is false, throw a TypeError
    //       exception.
    if (!target_desc.configurable()) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyHasNonConfigurable, name));
      return Nothing<bool>();
    }
    // 9b ii. Let extensibleTarget be ? IsExtensible(target).
    Maybe<bool> extensible_target = JSReceiver::IsExtensible(isolate, target);
    MAYBE_RETURN(extensible_target, Nothing<bool>());
    // 9b iii. If extensibleTarget is false, throw a TypeError exception.
    if (!extensible_target.FromJust()) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyHasNonExtensible, name));
      return Nothing<bool>();
    }
  }
  return Just(true);
}

Maybe<bool> JSProxy::SetProperty(DirectHandle<JSProxy> proxy,
                                 DirectHandle<Name> name,
                                 DirectHandle<Object> value,
                                 DirectHandle<JSAny> receiver,
                                 Maybe<ShouldThrow> should_throw) {
  DCHECK(!name->IsPrivate());
  Isolate* isolate = Isolate::Current();
  STACK_CHECK(isolate, Nothing<bool>());
  Factory* factory = isolate->factory();
  DirectHandle<String> trap_name = factory->set_string();

  if (proxy->IsRevoked()) {
    isolate->Throw(
        *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
    return Nothing<bool>();
  }
  DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
  DirectHandle<JSReceiver> handler(Cast<JSReceiver>(proxy->handler()), isolate);

  DirectHandle<Object> trap;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
                             Object::GetMethod(isolate, handler, trap_name));
  if (IsUndefined(*trap, isolate)) {
    PropertyKey key(isolate, name);
    LookupIterator it(isolate, receiver, key, target);

    return Object::SetSuperProperty(&it, value, StoreOrigin::kMaybeKeyed,
                                    should_throw);
  }

  DirectHandle<Object> trap_result;
  DirectHandle<Object> args[] = {target, name, value, receiver};
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap_result,
      Execution::Call(isolate, trap, handler, base::VectorOf(args)));
  if (!Object::BooleanValue(*trap_result, isolate)) {
    RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                   NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor,
                                trap_name, name));
  }

  MaybeDirectHandle<Object> result =
      JSProxy::CheckGetSetTrapResult(isolate, name, target, value, kSet);

  if (result.is_null()) {
    return Nothing<bool>();
  }
  return Just(true);
}

Maybe<bool> JSProxy::DeletePropertyOrElement(DirectHandle<JSProxy> proxy,
                                             DirectHandle<Name> name,
                                             LanguageMode language_mode) {
  DCHECK(!name->IsPrivate());
  ShouldThrow should_throw =
      is_sloppy(language_mode) ? kDontThrow : kThrowOnError;
  Isolate* isolate = Isolate::Current();
  STACK_CHECK(isolate, Nothing<bool>());
  Factory* factory = isolate->factory();
  DirectHandle<String> trap_name = factory->deleteProperty_string();

  if (proxy->IsRevoked()) {
    isolate->Throw(
        *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
    return Nothing<bool>();
  }
  DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
  DirectHandle<JSReceiver> handler(Cast<JSReceiver>(proxy->handler()), isolate);

  DirectHandle<Object> trap;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
                             Object::GetMethod(isolate, handler, trap_name));
  if (IsUndefined(*trap, isolate)) {
    return JSReceiver::DeletePropertyOrElement(isolate, target, name,
                                               language_mode);
  }

  DirectHandle<Object> trap_result;
  DirectHandle<Object> args[] = {target, name};
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap_result,
      Execution::Call(isolate, trap, handler, base::VectorOf(args)));
  if (!Object::BooleanValue(*trap_result, isolate)) {
    RETURN_FAILURE(isolate, should_throw,
                   NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor,
                                trap_name, name));
  }

  // Enforce the invariant.
  return JSProxy::CheckDeleteTrap(isolate, name, target);
}

Maybe<bool> JSProxy::CheckDeleteTrap(Isolate* isolate, DirectHandle<Name> name,
                                     DirectHandle<JSReceiver> target) {
  // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
  PropertyDescriptor target_desc;
  Maybe<bool> target_found =
      JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
  MAYBE_RETURN(target_found, Nothing<bool>());
  // 11. If targetDesc is undefined, return true.
  if (target_found.FromJust()) {
    // 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
    if (!target_desc.configurable()) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyDeletePropertyNonConfigurable, name));
      return Nothing<bool>();
    }
    // 13. Let extensibleTarget be ? IsExtensible(target).
    Maybe<bool> extensible_target = JSReceiver::IsExtensible(isolate, target);
    MAYBE_RETURN(extensible_target, Nothing<bool>());
    // 14. If extensibleTarget is false, throw a TypeError exception.
    if (!extensible_target.FromJust()) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyDeletePropertyNonExtensible, name));
      return Nothing<bool>();
    }
  }
  return Just(true);
}

// static
MaybeDirectHandle<JSProxy> JSProxy::New(Isolate* isolate,
                                        DirectHandle<Object> target,
                                        DirectHandle<Object> handler,
                                        bool revocable) {
  if (!IsJSReceiver(*target)) {
    THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject));
  }
  if (!IsJSReceiver(*handler)) {
    THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject));
  }
  return isolate->factory()->NewJSProxy(Cast<JSReceiver>(target),
                                        Cast<JSReceiver>(handler), revocable);
}

Maybe<PropertyAttributes> JSProxy::GetPropertyAttributes(LookupIterator* it) {
  PropertyDescriptor desc;
  Maybe<bool> found = JSProxy::GetOwnPropertyDescriptor(
      it->isolate(), it->GetHolder<JSProxy>(), it->GetName(), &desc);
  MAYBE_RETURN(found, Nothing<PropertyAttributes>());
  if (!found.FromJust()) return Just(ABSENT);
  return Just(desc.ToAttributes());
}

// TODO(jkummerow): Consider unification with FastAsArrayLength() in
// accessors.cc.
bool PropertyKeyToArrayLength(DirectHandle<Object> value, uint32_t* length) {
  DCHECK(IsNumber(*value) || IsName(*value));
  if (Object::ToArrayLength(*value, length)) return true;
  if (IsString(*value)) return Cast<String>(*value)->AsArrayIndex(length);
  return false;
}

bool PropertyKeyToArrayIndex(DirectHandle<Object> index_obj, uint32_t* output) {
  return PropertyKeyToArrayLength(index_obj, output) && *output != kMaxUInt32;
}

// ES6 9.4.2.1
// static
Maybe<bool> JSArray::DefineOwnProperty(Isolate* isolate,
                                       DirectHandle<JSArray> o,
                                       DirectHandle<Object> name,
                                       PropertyDescriptor* desc,
                                       Maybe<ShouldThrow> should_throw) {
  if (IsName(*name)) {
    name = isolate->factory()->InternalizeName(Cast<Name>(name));
  }

  // 1. Assert: IsPropertyKey(P) is true. ("P" is |name|.)
  // 2. If P is "length", then:
  if (*name == ReadOnlyRoots(isolate).length_string()) {
    // 2a. Return ArraySetLength(A, Desc).
    return ArraySetLength(isolate, o, desc, should_throw);
  }
  // 3. Else if P is an array index, then:
  uint32_t index = 0;
  if (PropertyKeyToArrayIndex(name, &index)) {
    // 3a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
    PropertyDescriptor old_len_desc;
    Maybe<bool> success = GetOwnPropertyDescriptor(
        isolate, o, isolate->factory()->length_string(), &old_len_desc);
    // 3b. (Assert)
    DCHECK(success.FromJust());
    USE(success);
    // 3c. Let oldLen be oldLenDesc.[[Value]].
    uint32_t old_len = 0;
    CHECK(Object::ToArrayLength(*old_len_desc.value(), &old_len));
    // 3d. Let index be ToUint32(P).
    // (Already done above.)
    // 3e. (Assert)
    // 3f. If index >= oldLen and oldLenDesc.[[Writable]] is false,
    //     return false.
    if (index >= old_len && old_len_desc.has_writable() &&
        !old_len_desc.writable()) {
      RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                     NewTypeError(MessageTemplate::kDefineDisallowed, name));
    }
    // 3g. Let succeeded be OrdinaryDefineOwnProperty(A, P, Desc).
    Maybe<bool> succeeded =
        OrdinaryDefineOwnProperty(isolate, o, name, desc, should_throw);
    // 3h. Assert: succeeded is not an abrupt completion.
    //     In our case, if should_throw == kThrowOnError, it can be!
    // 3i. If succeeded is false, return false.
    if (succeeded.IsNothing() || !succeeded.FromJust()) return succeeded;
    // 3j. If index >= oldLen, then:
    if (index >= old_len) {
      // 3j i. Set oldLenDesc.[[Value]] to index + 1.
      old_len_desc.set_value(isolate->factory()->NewNumberFromUint(index + 1));
      // 3j ii. Let succeeded be
      //        OrdinaryDefineOwnProperty(A, "length", oldLenDesc).
      succeeded = OrdinaryDefineOwnProperty(isolate, o,
                                            isolate->factory()->length_string(),
                                            &old_len_desc, should_throw);
      // 3j iii. Assert: succeeded is true.
      DCHECK(succeeded.FromJust());
      USE(succeeded);
    }
    // 3k. Return true.
    return Just(true);
  }

  // 4. Return OrdinaryDefineOwnProperty(A, P, Desc).
  return OrdinaryDefineOwnProperty(isolate, o, name, desc, should_throw);
}

// Part of ES6 9.4.2.4 ArraySetLength.
// static
bool JSArray::AnythingToArrayLength(Isolate* isolate,
                                    DirectHandle<Object> length_object,
                                    uint32_t* output) {
  // Fast path: check numbers and strings that can be converted directly
  // and unobservably.
  if (Object::ToArrayLength(*length_object, output)) return true;
  if (IsString(*length_object) &&
      Cast<String>(length_object)->AsArrayIndex(output)) {
    return true;
  }
  // Slow path: follow steps in ES6 9.4.2.4 "ArraySetLength".
  // 3. Let newLen be ToUint32(Desc.[[Value]]).
  DirectHandle<Number> uint32_v;
  if (!Object::ToUint32(isolate, length_object).ToHandle(&uint32_v)) {
    // 4. ReturnIfAbrupt(newLen).
    return false;
  }
  // 5. Let numberLen be ToNumber(Desc.[[Value]]).
  DirectHandle<Number> number_v;
  if (!Object::ToNumber(isolate, length_object).ToHandle(&number_v)) {
    // 6. ReturnIfAbrupt(newLen).
    return false;
  }
  // 7. If newLen != numberLen, throw a RangeError exception.
  if (Object::NumberValue(*uint32_v) != Object::NumberValue(*number_v)) {
    DirectHandle<Object> exception =
        isolate->factory()->NewRangeError(MessageTemplate::kInvalidArrayLength);
    isolate->Throw(*exception);
    return false;
  }
  CHECK(Object::ToArrayLength(*uint32_v, output));
  return true;
}

// ES6 9.4.2.4
// static
Maybe<bool> JSArray::ArraySetLength(Isolate* isolate, DirectHandle<JSArray> a,
                                    PropertyDescriptor* desc,
                                    Maybe<ShouldThrow> should_throw) {
  // 1. If the [[Value]] field of Desc is absent, then
  if (!desc->has_value()) {
    // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
    return OrdinaryDefineOwnProperty(
        isolate, a, isolate->factory()->length_string(), desc, should_throw);
  }
  // 2. Let newLenDesc be a copy of Desc.
  // (Actual copying is not necessary.)
  PropertyDescriptor* new_len_desc = desc;
  // 3. - 7. Convert Desc.[[Value]] to newLen.
  uint32_t new_len = 0;
  if (!AnythingToArrayLength(isolate, desc->value(), &new_len)) {
    DCHECK(isolate->has_exception());
    return Nothing<bool>();
  }
  // 8. Set newLenDesc.[[Value]] to newLen.
  // (Done below, if needed.)
  // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
  PropertyDescriptor old_len_desc;
  Maybe<bool> success = GetOwnPropertyDescriptor(
      isolate, a, isolate->factory()->length_string(), &old_len_desc);
  // 10. (Assert)
  DCHECK(success.FromJust());
  USE(success);
  // 11. Let oldLen be oldLenDesc.[[Value]].
  uint32_t old_len = 0;
  CHECK(Object::ToArrayLength(*old_len_desc.value(), &old_len));
  // 12. If newLen >= oldLen, then
  if (new_len >= old_len) {
    // 8. Set newLenDesc.[[Value]] to newLen.
    // 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
    new_len_desc->set_value(isolate->factory()->NewNumberFromUint(new_len));
    return OrdinaryDefineOwnProperty(isolate, a,
                                     isolate->factory()->length_string(),
                                     new_len_desc, should_throw);
  }
  // 13. If oldLenDesc.[[Writable]] is false, return false.
  if (!old_len_desc.writable() ||
      // Also handle the {configurable: true} and enumerable changes
      // since we later use JSArray::SetLength instead of
      // OrdinaryDefineOwnProperty to change the length,
      // and it doesn't have access to the descriptor anymore.
      new_len_desc->configurable() ||
      (new_len_desc->has_enumerable() &&
       (old_len_desc.enumerable() != new_len_desc->enumerable()))) {
    RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                   NewTypeError(MessageTemplate::kRedefineDisallowed,
                                isolate->factory()->length_string()));
  }
  // 14. If newLenDesc.[[Writable]] is absent or has the value true,
  // let newWritable be true.
  bool new_writable = false;
  if (!new_len_desc->has_writable() || new_len_desc->writable()) {
    new_writable = true;
  } else {
    // 15. Else,
    // 15a. Need to defer setting the [[Writable]] attribute to false in case
    //      any elements cannot be deleted.
    // 15b. Let newWritable be false. (It's initialized as "false" anyway.)
    // 15c. Set newLenDesc.[[Writable]] to true.
    // (Not needed.)
  }
  // Most of steps 16 through 19 is implemented by JSArray::SetLength.
  MAYBE_RETURN(JSArray::SetLength(isolate, a, new_len), Nothing<bool>());
  // Steps 19d-ii, 20.
  if (!new_writable) {
    PropertyDescriptor readonly;
    readonly.set_writable(false);
    success = OrdinaryDefineOwnProperty(isolate, a,
                                        isolate->factory()->length_string(),
                                        &readonly, should_throw);
    DCHECK(success.FromJust());
    USE(success);
  }
  uint32_t actual_new_len = 0;
  CHECK(Object::ToArrayLength(a->length(), &actual_new_len));
  // Steps 19d-v, 21. Return false if there were non-deletable elements.
  bool result = actual_new_len == new_len;
  if (!result) {
    RETURN_FAILURE(
        isolate, GetShouldThrow(isolate, should_throw),
        NewTypeError(MessageTemplate::kStrictCannotDeleteProperty,
                     isolate->factory()->NewNumberFromUint(actual_new_len - 1),
                     a));
  }
  return Just(result);
}

// ES6 9.5.6
// static
Maybe<bool> JSProxy::DefineOwnProperty(Isolate* isolate,
                                       DirectHandle<JSProxy> proxy,
                                       DirectHandle<Object> key,
                                       PropertyDescriptor* desc,
                                       Maybe<ShouldThrow> should_throw) {
  STACK_CHECK(isolate, Nothing<bool>());
  if (IsSymbol(*key) && Cast<Symbol>(key)->IsPrivate()) {
    DCHECK(!Cast<Symbol>(key)->IsPrivateName());
    return JSProxy::SetPrivateSymbol(isolate, proxy, Cast<Symbol>(key), desc,
                                     should_throw);
  }
  DirectHandle<String> trap_name = isolate->factory()->defineProperty_string();
  // 1. Assert: IsPropertyKey(P) is true.
  DCHECK(IsName(*key) || IsNumber(*key));
  // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
  DirectHandle<Object> handler(proxy->handler(), isolate);
  // 3. If handler is null, throw a TypeError exception.
  // 4. Assert: Type(handler) is Object.
  if (proxy->IsRevoked()) {
    isolate->Throw(*isolate->factory()->NewTypeError(
        MessageTemplate::kProxyRevoked, trap_name));
    return Nothing<bool>();
  }
  // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
  DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
  // 6. Let trap be ? GetMethod(handler, "defineProperty").
  DirectHandle<Object> trap;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap,
      Object::GetMethod(isolate, Cast<JSReceiver>(handler), trap_name));
  // 7. If trap is undefined, then:
  if (IsUndefined(*trap, isolate)) {
    // 7a. Return target.[[DefineOwnProperty]](P, Desc).
    return JSReceiver::DefineOwnProperty(isolate, target, key, desc,
                                         should_throw);
  }
  // 8. Let descObj be FromPropertyDescriptor(Desc).
  DirectHandle<Object> desc_obj = desc->ToObject(isolate);
  // 9. Let booleanTrapResult be
  //    ToBoolean(? Call(trap, handler, «target, P, descObj»)).
  DirectHandle<Name> property_name =
      IsName(*key) ? Cast<Name>(key)
                   : Cast<Name>(isolate->factory()->NumberToString(key));
  // Do not leak private property names.
  DCHECK(!property_name->IsPrivate());
  DirectHandle<Object> trap_result_obj;
  DirectHandle<Object> args[] = {target, property_name, desc_obj};
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap_result_obj,
      Execution::Call(isolate, trap, handler, base::VectorOf(args)));
  // 10. If booleanTrapResult is false, return false.
  if (!Object::BooleanValue(*trap_result_obj, isolate)) {
    RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                   NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor,
                                trap_name, property_name));
  }
  // 11. Let targetDesc be ? target.[[GetOwnProperty]](P).
  PropertyDescriptor target_desc;
  Maybe<bool> target_found =
      JSReceiver::GetOwnPropertyDescriptor(isolate, target, key, &target_desc);
  MAYBE_RETURN(target_found, Nothing<bool>());
  // 12. Let extensibleTarget be ? IsExtensible(target).
  Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(isolate, target);
  MAYBE_RETURN(maybe_extensible, Nothing<bool>());
  bool extensible_target = maybe_extensible.FromJust();
  // 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]]
  //     is false, then:
  // 13a. Let settingConfigFalse be true.
  // 14. Else let settingConfigFalse be false.
  bool setting_config_false = desc->has_configurable() && !desc->configurable();
  // 15. If targetDesc is undefined, then
  if (!target_found.FromJust()) {
    // 15a. If extensibleTarget is false, throw a TypeError exception.
    if (!extensible_target) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyDefinePropertyNonExtensible, property_name));
      return Nothing<bool>();
    }
    // 15b. If settingConfigFalse is true, throw a TypeError exception.
    if (setting_config_false) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name));
      return Nothing<bool>();
    }
  } else {
    // 16. Else targetDesc is not undefined,
    // 16a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc,
    //      targetDesc) is false, throw a TypeError exception.
    Maybe<bool> valid = IsCompatiblePropertyDescriptor(
        isolate, extensible_target, desc, &target_desc, property_name,
        Just(kDontThrow));
    MAYBE_RETURN(valid, Nothing<bool>());
    if (!valid.FromJust()) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyDefinePropertyIncompatible, property_name));
      return Nothing<bool>();
    }
    // 16b. If settingConfigFalse is true and targetDesc.[[Configurable]] is
    //      true, throw a TypeError exception.
    if (setting_config_false && target_desc.configurable()) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name));
      return Nothing<bool>();
    }
    // 16c. If IsDataDescriptor(targetDesc) is true,
    // targetDesc.[[Configurable]] is
    //       false, and targetDesc.[[Writable]] is true, then
    if (PropertyDescriptor::IsDataDescriptor(&target_desc) &&
        !target_desc.configurable() && target_desc.writable()) {
      // 16c i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false,
      // throw a TypeError exception.
      if (desc->has_writable() && !desc->writable()) {
        isolate->Throw(*isolate->factory()->NewTypeError(
            MessageTemplate::kProxyDefinePropertyNonConfigurableWritable,
            property_name));
        return Nothing<bool>();
      }
    }
  }
  // 17. Return true.
  return Just(true);
}

// static
Maybe<bool> JSProxy::SetPrivateSymbol(Isolate* isolate,
                                      DirectHandle<JSProxy> proxy,
                                      DirectHandle<Symbol> private_name,
                                      PropertyDescriptor* desc,
                                      Maybe<ShouldThrow> should_throw) {
  // Despite the generic name, this can only add private data properties.
  if (!PropertyDescriptor::IsDataDescriptor(desc) ||
      desc->ToAttributes() != DONT_ENUM) {
    RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                   NewTypeError(MessageTemplate::kProxyPrivate));
  }
  DCHECK(proxy->map()->is_dictionary_map());
  DirectHandle<Object> value =
      desc->has_value() ? desc->value()
                        : Cast<Object>(isolate->factory()->undefined_value());

  LookupIterator it(isolate, proxy, private_name, proxy);

  if (it.IsFound()) {
    DCHECK_EQ(LookupIterator::DATA, it.state());
    DCHECK_EQ(DONT_ENUM, it.property_attributes());
    // We are not tracking constness for private symbols added to JSProxy
    // objects.
    DCHECK_EQ(PropertyConstness::kMutable, it.property_details().constness());
    it.WriteDataValue(value, false);
    return Just(true);
  }

  PropertyDetails details(PropertyKind::kData, DONT_ENUM,
                          PropertyConstness::kMutable);
  if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
    DirectHandle<SwissNameDictionary> dict(proxy->property_dictionary_swiss(),
                                           isolate);
    DirectHandle<SwissNameDictionary> result =
        SwissNameDictionary::Add(isolate, dict, private_name, value, details);
    if (!dict.is_identical_to(result)) proxy->SetProperties(*result);
  } else {
    DirectHandle<NameDictionary> dict(proxy->property_dictionary(), isolate);
    DirectHandle<NameDictionary> result;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, result,
        NameDictionary::Add(isolate, dict, private_name, value, details));
    if (!dict.is_identical_to(result)) proxy->SetProperties(*result);
  }
  return Just(true);
}

// ES6 9.5.5
// static
Maybe<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate,
                                              DirectHandle<JSProxy> proxy,
                                              DirectHandle<Name> name,
                                              PropertyDescriptor* desc) {
  DCHECK(!name->IsPrivate());
  STACK_CHECK(isolate, Nothing<bool>());

  DirectHandle<String> trap_name =
      isolate->factory()->getOwnPropertyDescriptor_string();
  // 1. (Assert)
  // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
  DirectHandle<Object> handler(proxy->handler(), isolate);
  // 3. If handler is null, throw a TypeError exception.
  // 4. Assert: Type(handler) is Object.
  if (proxy->IsRevoked()) {
    isolate->Throw(*isolate->factory()->NewTypeError(
        MessageTemplate::kProxyRevoked, trap_name));
    return Nothing<bool>();
  }
  // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
  DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
  // 6. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor").
  DirectHandle<Object> trap;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap,
      Object::GetMethod(isolate, Cast<JSReceiver>(handler), trap_name));
  // 7. If trap is undefined, then
  if (IsUndefined(*trap, isolate)) {
    // 7a. Return target.[[GetOwnProperty]](P).
    return JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, desc);
  }
  // 8. Let trapResultObj be ? Call(trap, handler, «target, P»).
  Handle<JSAny> trap_result_obj;
  DirectHandle<Object> args[] = {target, name};
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap_result_obj,
      Cast<JSAny>(
          Execution::Call(isolate, trap, handler, base::VectorOf(args))));
  // 9. If Type(trapResultObj) is neither Object nor Undefined, throw a
  //    TypeError exception.
  if (!IsJSReceiver(*trap_result_obj) &&
      !IsUndefined(*trap_result_obj, isolate)) {
    isolate->Throw(*isolate->factory()->NewTypeError(
        MessageTemplate::kProxyGetOwnPropertyDescriptorInvalid, name));
    return Nothing<bool>();
  }
  // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
  PropertyDescriptor target_desc;
  Maybe<bool> found =
      JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
  MAYBE_RETURN(found, Nothing<bool>());
  // 11. If trapResultObj is undefined, then
  if (IsUndefined(*trap_result_obj, isolate)) {
    // 11a. If targetDesc is undefined, return undefined.
    if (!found.FromJust()) return Just(false);
    // 11b. If targetDesc.[[Configurable]] is false, throw a TypeError
    //      exception.
    if (!target_desc.configurable()) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyGetOwnPropertyDescriptorUndefined, name));
      return Nothing<bool>();
    }
    // 11c. Let extensibleTarget be ? IsExtensible(target).
    Maybe<bool> extensible_target = JSReceiver::IsExtensible(isolate, target);
    MAYBE_RETURN(extensible_target, Nothing<bool>());
    // 11d. (Assert)
    // 11e. If extensibleTarget is false, throw a TypeError exception.
    if (!extensible_target.FromJust()) {
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyGetOwnPropertyDescriptorNonExtensible, name));
      return Nothing<bool>();
    }
    // 11f. Return undefined.
    return Just(false);
  }
  // 12. Let extensibleTarget be ? IsExtensible(target).
  Maybe<bool> extensible_target = JSReceiver::IsExtensible(isolate, target);
  MAYBE_RETURN(extensible_target, Nothing<bool>());
  // 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj).
  if (!PropertyDescriptor::ToPropertyDescriptor(isolate, trap_result_obj,
                                                desc)) {
    DCHECK(isolate->has_exception());
    return Nothing<bool>();
  }
  // 14. Call CompletePropertyDescriptor(resultDesc).
  PropertyDescriptor::CompletePropertyDescriptor(isolate, desc);
  // 15. Let valid be IsCompatiblePropertyDescriptor (extensibleTarget,
  //     resultDesc, targetDesc).
  Maybe<bool> valid = IsCompatiblePropertyDescriptor(
      isolate, extensible_target.FromJust(), desc, &target_desc, name,
      Just(kDontThrow));
  MAYBE_RETURN(valid, Nothing<bool>());
  // 16. If valid is false, throw a TypeError exception.
  if (!valid.FromJust()) {
    isolate->Throw(*isolate->factory()->NewTypeError(
        MessageTemplate::kProxyGetOwnPropertyDescriptorIncompatible, name));
    return Nothing<bool>();
  }
  // 17. If resultDesc.[[Configurable]] is false, then
  if (!desc->configurable()) {
    // 17a. If targetDesc is undefined or targetDesc.[[Configurable]] is true:
    if (target_desc.is_empty() || target_desc.configurable()) {
      // 17a i. Throw a TypeError exception.
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kProxyGetOwnPropertyDescriptorNonConfigurable,
          name));
      return Nothing<bool>();
    }
    // 17b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]]
    // is false, then
    if (desc->has_writable() && !desc->writable()) {
      // 17b i. If targetDesc.[[Writable]] is true, throw a TypeError exception.
      if (target_desc.writable()) {
        isolate->Throw(*isolate->factory()->NewTypeError(
            MessageTemplate::
                kProxyGetOwnPropertyDescriptorNonConfigurableWritable,
            name));
        return Nothing<bool>();
      }
    }
  }
  // 18. Return resultDesc.
  return Just(true);
}

Maybe<bool> JSProxy::PreventExtensions(DirectHandle<JSProxy> proxy,
                                       ShouldThrow should_throw) {
  Isolate* isolate = Isolate::Current();
  STACK_CHECK(isolate, Nothing<bool>());
  Factory* factory = isolate->factory();
  DirectHandle<String> trap_name = factory->preventExtensions_string();

  if (proxy->IsRevoked()) {
    isolate->Throw(
        *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
    return Nothing<bool>();
  }
  DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
  DirectHandle<JSReceiver> handler(Cast<JSReceiver>(proxy->handler()), isolate);

  DirectHandle<Object> trap;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
                             Object::GetMethod(isolate, handler, trap_name));
  if (IsUndefined(*trap, isolate)) {
    return JSReceiver::PreventExtensions(isolate, target, should_throw);
  }

  DirectHandle<Object> trap_result;
  DirectHandle<Object> args[] = {target};
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap_result,
      Execution::Call(isolate, trap, handler, base::VectorOf(args)));
  if (!Object::BooleanValue(*trap_result, isolate)) {
    RETURN_FAILURE(
        isolate, should_throw,
        NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name));
  }

  // Enforce the invariant.
  Maybe<bool> target_result = JSReceiver::IsExtensible(isolate, target);
  MAYBE_RETURN(target_result, Nothing<bool>());
  if (target_result.FromJust()) {
    isolate->Throw(*factory->NewTypeError(
        MessageTemplate::kProxyPreventExtensionsExtensible));
    return Nothing<bool>();
  }
  return Just(true);
}

Maybe<bool> JSProxy::IsExtensible(DirectHandle<JSProxy> proxy) {
  Isolate* isolate = Isolate::Current();
  STACK_CHECK(isolate, Nothing<bool>());
  Factory* factory = isolate->factory();
  DirectHandle<String> trap_name = factory->isExtensible_string();

  if (proxy->IsRevoked()) {
    isolate->Throw(
        *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
    return Nothing<bool>();
  }
  DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
  DirectHandle<JSReceiver> handler(Cast<JSReceiver>(proxy->handler()), isolate);

  DirectHandle<Object> trap;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
                             Object::GetMethod(isolate, handler, trap_name));
  if (IsUndefined(*trap, isolate)) {
    return JSReceiver::IsExtensible(isolate, target);
  }

  DirectHandle<Object> trap_result;
  DirectHandle<Object> args[] = {target};
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap_result,
      Execution::Call(isolate, trap, handler, base::VectorOf(args)));

  // Enforce the invariant.
  Maybe<bool> target_result = JSReceiver::IsExtensible(isolate, target);
  MAYBE_RETURN(target_result, Nothing<bool>());
  if (target_result.FromJust() != Object::BooleanValue(*trap_result, isolate)) {
    isolate->Throw(
        *factory->NewTypeError(MessageTemplate::kProxyIsExtensibleInconsistent,
                               factory->ToBoolean(target_result.FromJust())));
    return Nothing<bool>();
  }
  return target_result;
}

DirectHandle<DescriptorArray> DescriptorArray::CopyUpTo(
    Isolate* isolate, DirectHandle<DescriptorArray> desc, int enumeration_index,
    int slack) {
  return DescriptorArray::CopyUpToAddAttributes(isolate, desc,
                                                enumeration_index, NONE, slack);
}

DirectHandle<DescriptorArray> DescriptorArray::CopyUpToAddAttributes(
    Isolate* isolate, DirectHandle<DescriptorArray> source_handle,
    int enumeration_index, PropertyAttributes attributes, int slack) {
  if (enumeration_index + slack == 0) {
    return isolate->factory()->empty_descriptor_array();
  }

  int size = enumeration_index;
  DirectHandle<DescriptorArray> copy_handle =
      DescriptorArray::Allocate(isolate, size, slack);

  DisallowGarbageCollection no_gc;
  Tagged<DescriptorArray> source = *source_handle;
  Tagged<DescriptorArray> copy = *copy_handle;

  if (attributes != NONE) {
    for (InternalIndex i : InternalIndex::Range(size)) {
      Tagged<MaybeObject> value_or_field_type = source->GetValue(i);
      Tagged<Name> key = source->GetKey(i);
      PropertyDetails details = source->GetDetails(i);
      // Bulk attribute changes never affect private properties.
      if (!key->IsPrivate()) {
        int mask = DONT_DELETE | DONT_ENUM;
        // READ_ONLY is an invalid attribute for JS setters/getters.
        Tagged<HeapObject> heap_object;
        if (details.kind() != PropertyKind::kAccessor ||
            !(value_or_field_type.GetHeapObjectIfStrong(&heap_object) &&
              IsAccessorPair(heap_object))) {
          mask |= READ_ONLY;
        }
        details = details.CopyAddAttributes(
            static_cast<PropertyAttributes>(attributes & mask));
      }
      copy->Set(i, key, value_or_field_type, details);
    }
  } else {
    for (InternalIndex i : InternalIndex::Range(size)) {
      copy->CopyFrom(i, source);
    }
  }

  if (source->number_of_descriptors() != enumeration_index) copy->Sort();

  return copy_handle;
}

bool DescriptorArray::IsEqualUpTo(Tagged<DescriptorArray> desc,
                                  int nof_descriptors) {
  for (InternalIndex i : InternalIndex::Range(nof_descriptors)) {
    if (GetKey(i) != desc->GetKey(i) || GetValue(i) != desc->GetValue(i)) {
      return false;
    }
    PropertyDetails details = GetDetails(i);
    PropertyDetails other_details = desc->GetDetails(i);
    if (details.kind() != other_details.kind() ||
        details.location() != other_details.location() ||
        !details.representation().Equals(other_details.representation())) {
      return false;
    }
  }
  return true;
}

// static
Handle<WeakArrayList> PrototypeUsers::Add(Isolate* isolate,
                                          Handle<WeakArrayList> array,
                                          DirectHandle<Map> value,
                                          int* assigned_index) {
  int length = array->length();
  if (length == 0) {
    // Uninitialized WeakArrayList; need to initialize empty_slot_index.
    array = WeakArrayList::EnsureSpace(isolate, array, kFirstIndex + 1);
    set_empty_slot_index(*array, kNoEmptySlotsMarker);
    array->Set(kFirstIndex, MakeWeak(*value));
    array->set_length(kFirstIndex + 1);
    if (assigned_index != nullptr) *assigned_index = kFirstIndex;
    return array;
  }

  // If the array has unfilled space at the end, use it.
  if (!array->IsFull()) {
    array->Set(length, MakeWeak(*value));
    array->set_length(length + 1);
    if (assigned_index != nullptr) *assigned_index = length;
    return array;
  }

  // If there are empty slots, use one of them.
  int empty_slot = Smi::ToInt(empty_slot_index(*array));

  if (empty_slot == kNoEmptySlotsMarker) {
    // GCs might have cleared some references, rescan the array for empty slots.
    PrototypeUsers::ScanForEmptySlots(*array);
    empty_slot = Smi::ToInt(empty_slot_index(*array));
  }

  if (empty_slot != kNoEmptySlotsMarker) {
    DCHECK_GE(empty_slot, kFirstIndex);
    CHECK_LT(empty_slot, array->length());
    int next_empty_slot = array->Get(empty_slot).ToSmi().value();

    array->Set(empty_slot, MakeWeak(*value));
    if (assigned_index != nullptr) *assigned_index = empty_slot;

    set_empty_slot_index(*array, next_empty_slot);
    return array;
  } else {
    DCHECK_EQ(empty_slot, kNoEmptySlotsMarker);
  }

  // Array full and no empty slots. Grow the array.
  array = WeakArrayList::EnsureSpace(isolate, array, length + 1);
  array->Set(length, MakeWeak(*value));
  array->set_length(length + 1);
  if (assigned_index != nullptr) *assigned_index = length;
  return array;
}

// static
void PrototypeUsers::ScanForEmptySlots(Tagged<WeakArrayList> array) {
  for (int i = kFirstIndex; i < array->length(); i++) {
    if (array->Get(i).IsCleared()) {
      PrototypeUsers::MarkSlotEmpty(array, i);
    }
  }
}

Tagged<WeakArrayList> PrototypeUsers::Compact(DirectHandle<WeakArrayList> array,
                                              Heap* heap,
                                              CompactionCallback callback,
                                              AllocationType allocation) {
  if (array->length() == 0) {
    return *array;
  }
  int new_length = kFirstIndex + array->CountLiveWeakReferences();
  if (new_length == array->length()) {
    return *array;
  }

  DirectHandle<WeakArrayList> new_array = WeakArrayList::EnsureSpace(
      heap->isolate(),
      handle(ReadOnlyRoots(heap).empty_weak_array_list(), heap->isolate()),
      new_length, allocation);
  // Allocation might have caused GC and turned some of the elements into
  // cleared weak heap objects. Count the number of live objects again.
  int copy_to = kFirstIndex;
  for (int i = kFirstIndex; i < array->length(); i++) {
    Tagged<MaybeObject> element = array->Get(i);
    Tagged<HeapObject> value;
    if (element.GetHeapObjectIfWeak(&value)) {
      callback(value, i, copy_to);
      new_array->Set(copy_to++, element);
    } else {
      DCHECK(element.IsCleared() || element.IsSmi());
    }
  }
  new_array->set_length(copy_to);
  set_empty_slot_index(*new_array, kNoEmptySlotsMarker);
  return *new_array;
}

template <typename IsolateT>
Handle<DescriptorArray> DescriptorArray::Allocate(IsolateT* isolate,
                                                  int nof_descriptors,
                                                  int slack,
                                                  AllocationType allocation) {
  return nof_descriptors + slack == 0
             ? isolate->factory()->empty_descriptor_array()
             : isolate->factory()->NewDescriptorArray(nof_descriptors, slack,
                                                      allocation);
}
template Handle<DescriptorArray> DescriptorArray::Allocate(
    Isolate* isolate, int nof_descriptors, int slack,
    AllocationType allocation);
template Handle<DescriptorArray> DescriptorArray::Allocate(
    LocalIsolate* isolate, int nof_descriptors, int slack,
    AllocationType allocation);

void DescriptorArray::Initialize(Tagged<EnumCache> empty_enum_cache,
                                 Tagged<HeapObject> undefined_value,
                                 int nof_descriptors, int slack,
                                 uint32_t raw_gc_state) {
  DCHECK_GE(nof_descriptors, 0);
  DCHECK_GE(slack, 0);
  DCHECK_LE(nof_descriptors + slack, kMaxNumberOfDescriptors);
  set_number_of_all_descriptors(nof_descriptors + slack);
  set_number_of_descriptors(nof_descriptors);
  set_raw_gc_state(raw_gc_state, kRelaxedStore);
  set_enum_cache(empty_enum_cache, SKIP_WRITE_BARRIER);
  set_flags(FastIterableBits::encode(FastIterableState::kUnknown),
            kRelaxedStore);
  MemsetTagged(GetDescriptorSlot(0), undefined_value,
               number_of_all_descriptors() * kEntrySize);
}

void DescriptorArray::ClearEnumCache() {
  set_enum_cache(GetReadOnlyRoots().empty_enum_cache(), SKIP_WRITE_BARRIER);
}

void DescriptorArray::Replace(InternalIndex index, Descriptor* descriptor) {
  descriptor->SetSortedKeyIndex(GetSortedKeyIndex(index.as_int()));
  Set(index, descriptor);
  // Resetting the fast iterable state is bottlenecked in SetKey().
  DCHECK_NE(fast_iterable(), FastIterableState::kJsonFast);
}

// static
void DescriptorArray::InitializeOrChangeEnumCache(
    DirectHandle<DescriptorArray> descriptors, Isolate* isolate,
    DirectHandle<FixedArray> keys, DirectHandle<FixedArray> indices,
    AllocationType allocation_if_initialize) {
  Tagged<EnumCache> enum_cache = descriptors->enum_cache();
  if (enum_cache == ReadOnlyRoots(isolate).empty_enum_cache()) {
    enum_cache = *isolate->factory()->NewEnumCache(keys, indices,
                                                   allocation_if_initialize);
    descriptors->set_enum_cache(enum_cache);
  } else {
    enum_cache->set_keys(*keys);
    enum_cache->set_indices(*indices);
  }
}

void DescriptorArray::CopyFrom(InternalIndex index,
                               Tagged<DescriptorArray> src) {
  PropertyDetails details = src->GetDetails(index);
  Set(index, src->GetKey(index), src->GetValue(index), details);
}

void DescriptorArray::SortImpl(const int len) {
  // In-place heap sort.
  DCHECK_EQ(len, number_of_descriptors());
  // Reset sorting since the descriptor array might contain invalid pointers.
  for (int i = 0; i < len; ++i) SetSortedKey(i, i);
  // Bottom-up max-heap construction.
  // Index of the last node with children.
  int max_parent_index = (len / 2) - 1;
  for (int i = max_parent_index; i >= 0; --i) {
    int parent_index = i;
    const uint32_t parent_hash = GetSortedKey(i)->hash();
    while (parent_index <= max_parent_index) {
      int child_index = 2 * parent_index + 1;
      uint32_t child_hash = GetSortedKey(child_index)->hash();
      if (child_index + 1 < len) {
        uint32_t right_child_hash = GetSortedKey(child_index + 1)->hash();
        if (right_child_hash > child_hash) {
          child_index++;
          child_hash = right_child_hash;
        }
      }
      if (child_hash <= parent_hash) break;
      SwapSortedKeys(parent_index, child_index);
      // Now element at child_index could be < its children.
      parent_index = child_index;  // parent_hash remains correct.
    }
  }

  // Extract elements and create sorted array.
  for (int i = len - 1; i > 0; --i) {
    // Put max element at the back of the array.
    SwapSortedKeys(0, i);
    // Shift down the new top element.
    int parent_index = 0;
    const uint32_t parent_hash = GetSortedKey(parent_index)->hash();
    max_parent_index = (i / 2) - 1;
    while (parent_index <= max_parent_index) {
      int child_index = parent_index * 2 + 1;
      uint32_t child_hash = GetSortedKey(child_index)->hash();
      if (child_index + 1 < i) {
        uint32_t right_child_hash = GetSortedKey(child_index + 1)->hash();
        if (right_child_hash > child_hash) {
          child_index++;
          child_hash = right_child_hash;
        }
      }
      if (child_hash <= parent_hash) break;
      SwapSortedKeys(parent_index, child_index);
      parent_index = child_index;
    }
  }
  DCHECK(IsSortedNoDuplicates());
}

void DescriptorArray::CheckNameCollisionDuringInsertion(Descriptor* desc,
                                                        uint32_t desc_hash,
                                                        int insertion_index) {
  DCHECK_GE(insertion_index, 0);
  DCHECK_LE(insertion_index, number_of_all_descriptors());

  if (insertion_index <= 0) return;

  for (int i = insertion_index; i > 0; --i) {
    Tagged<Name> current_key = GetSortedKey(i - 1);
    if (current_key->hash() != desc_hash) return;
    CHECK(current_key != *desc->GetKey());
  }
}

DirectHandle<AccessorPair> AccessorPair::Copy(Isolate* isolate,
                                              DirectHandle<AccessorPair> pair) {
  DirectHandle<AccessorPair> copy = isolate->factory()->NewAccessorPair();
  DisallowGarbageCollection no_gc;
  Tagged<AccessorPair> raw_src = *pair;
  Tagged<AccessorPair> raw_copy = *copy;
  raw_copy->set_getter(raw_src->getter());
  raw_copy->set_setter(raw_src->setter());
  return copy;
}

Handle<JSAny> AccessorPair::GetComponent(
    Isolate* isolate, DirectHandle<NativeContext> native_context,
    DirectHandle<AccessorPair> accessor_pair, AccessorComponent component) {
  Handle<Object> accessor(accessor_pair->get(component), isolate);
  if (IsFunctionTemplateInfo(*accessor)) {
    // TODO(v8:5962): pass the right name here: "get "/"set " + prop.
    Handle<JSFunction> function =
        ApiNatives::InstantiateFunction(isolate, native_context,
                                        Cast<FunctionTemplateInfo>(accessor))
            .ToHandleChecked();
    accessor_pair->set(component, *function, kReleaseStore);
    return function;
  }
  if (IsNull(*accessor, isolate)) {
    return isolate->factory()->undefined_value();
  }
  return Cast<JSAny>(accessor);
}

#ifdef DEBUG
bool DescriptorArray::IsEqualTo(Tagged<DescriptorArray> other) {
  if (number_of_all_descriptors() != other->number_of_all_descriptors()) {
    return false;
  }
  for (InternalIndex i : InternalIndex::Range(number_of_descriptors())) {
    if (GetKey(i) != other->GetKey(i)) return false;
    if (GetDetails(i).AsSmi() != other->GetDetails(i).AsSmi()) return false;
    if (GetValue(i) != other->GetValue(i)) return false;
  }
  return true;
}
#endif

// static
MaybeDirectHandle<String> Name::ToFunctionName(Isolate* isolate,
                                               DirectHandle<Name> name) {
  if (IsString(*name)) return Cast<String>(name);
  // ES6 section 9.2.11 SetFunctionName, step 4.
  DirectHandle<Object> description(Cast<Symbol>(name)->description(), isolate);
  if (IsUndefined(*description, isolate)) {
    return isolate->factory()->empty_string();
  }
  IncrementalStringBuilder builder(isolate);
  builder.AppendCharacter('[');
  builder.AppendString(Cast<String>(description));
  builder.AppendCharacter(']');
  return builder.Finish();
}

// static
MaybeDirectHandle<String> Name::ToFunctionName(Isolate* isolate,
                                               DirectHandle<Name> name,
                                               DirectHandle<String> prefix) {
  DirectHandle<String> name_string;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, name_string,
                             ToFunctionName(isolate, name));
  IncrementalStringBuilder builder(isolate);
  builder.AppendString(prefix);
  builder.AppendCharacter(' ');
  builder.AppendString(name_string);
  return builder.Finish();
}

void Relocatable::PostGarbageCollectionProcessing(Isolate* isolate) {
  Relocatable* current = isolate->relocatable_top();
  while (current != nullptr) {
    current->PostGarbageCollection();
    current = current->prev_;
  }
}

// Reserve space for statics needing saving and restoring.
int Relocatable::ArchiveSpacePerThread() { return sizeof(Relocatable*); }

// Archive statics that are thread-local.
char* Relocatable::ArchiveState(Isolate* isolate, char* to) {
  *reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top();
  isolate->set_relocatable_top(nullptr);
  return to + ArchiveSpacePerThread();
}

// Restore statics that are thread-local.
char* Relocatable::RestoreState(Isolate* isolate, char* from) {
  isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from));
  return from + ArchiveSpacePerThread();
}

char* Relocatable::Iterate(RootVisitor* v, char* thread_storage) {
  Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
  Iterate(v, top);
  return thread_storage + ArchiveSpacePerThread();
}

void Relocatable::Iterate(Isolate* isolate, RootVisitor* v) {
  Iterate(v, isolate->relocatable_top());
}

void Relocatable::Iterate(RootVisitor* v, Relocatable* top) {
  Relocatable* current = top;
  while (current != nullptr) {
    current->IterateInstance(v);
    current = current->prev_;
  }
}

START_PROHIBIT_SIGN_CONVERSION()

namespace {

template <typename sinkchar>
void WriteChunkListToFlat(Tagged<FixedArray> chunk_list_head,
                          uint32_t last_chunk_length, Tagged<String> separator,
                          sinkchar* sink, uint32_t sink_length) {
  DisallowGarbageCollection no_gc;
#ifdef DEBUG
  sinkchar* sink_end = sink + sink_length;
#endif

  const uint32_t separator_length = separator->length();
  const bool use_one_byte_separator_fast_path =
      separator_length == 1 && sizeof(sinkchar) == 1 &&
      StringShape(separator).IsSequentialOneByte();
  uint8_t separator_one_char;
  if (use_one_byte_separator_fast_path) {
    CHECK(StringShape(separator).IsSequentialOneByte());
    CHECK_EQ(separator->length(), 1);
    separator_one_char = Cast<SeqOneByteString>(separator)->GetChars(no_gc)[0];
  }

  uint32_t num_separators = 0;
  uint32_t repeat_last = 0;

  Tagged<FixedArray> chunk = chunk_list_head;
  Tagged<Object> last_element = Smi::zero();
#ifdef DEBUG
  Tagged<FixedArray> prev_chunk = GetReadOnlyRoots().empty_fixed_array();
#endif
  while (true) {
    Tagged<Object> maybe_next_chunk = chunk->get(0);
    bool is_last_chunk = IsUndefined(maybe_next_chunk);
    uint32_t length = is_last_chunk ? last_chunk_length : chunk->ulength();
    CHECK_GT(length, 0);
    CHECK_LE(length, chunk->length());

    for (uint32_t i = 1; i < length; i++) {
      Tagged<Object> element = chunk->get(i);
      const bool element_is_special = IsSmi(element);

      // If element is a positive Smi, it represents the number of separators to
      // write. If it is a negative Smi, it represents the number of times the
      // last string is repeated.
      if (V8_UNLIKELY(element_is_special)) {
        int count;
        CHECK(Object::ToInt32(element, &count));
        if (count > 0) {
          num_separators = static_cast<uint32_t>(count);
          //  Verify that Smis (number of separators) only occur when necessary:
          //    1) at the beginning
          //    2) at the end
          //    3) when the number of separators > 1
          //      - It is assumed that consecutive Strings will have one
          //      separator,
          //        so there is no need for a Smi.
          DCHECK(i == 1 || i == length - 1 || num_separators > 1);
        } else {
          repeat_last = static_cast<uint32_t>(-count);
          // Repeat is only possible when the previous element is not special.
          DCHECK_GE(i, 1);
          DCHECK(IsString(last_element));
          DCHECK_IMPLIES(i == 1, prev_chunk->get(prev_chunk->ulength() - 1) ==
                                     last_element);
          DCHECK_IMPLIES(i > 1, chunk->get(i - 1) == last_element);
        }
      }

      // Write separator(s) if necessary.
      if (num_separators > 0 && separator_length > 0) {
        // TODO(pwong): Consider doubling strategy employed by
        // runtime-strings.cc
        //              WriteRepeatToFlat().
        // Fast path for single character, single byte separators.
        if (use_one_byte_separator_fast_path) {
          DCHECK_LE(sink + num_separators, sink_end);
          memset(sink, separator_one_char, num_separators);
          DCHECK_EQ(separator_length, 1);
          sink += num_separators;
        } else {
          for (uint32_t j = 0; j < num_separators; j++) {
            DCHECK_LE(sink + separator_length, sink_end);
            String::WriteToFlat(separator, sink, 0, separator_length);
            sink += separator_length;
          }
        }
        num_separators = 0;
      }

      // Repeat the last written string |repeat_last| times (including
      // separators).
      if (V8_UNLIKELY(repeat_last > 0)) {
        uint32_t string_length = Cast<String>(last_element)->length();
        // The implemented logic requires that string length is > 0. Empty
        // strings are handled by repeating the separator (positive smi in the
        // fixed array) already.
        DCHECK_GT(string_length, 0);
        uint32_t length_with_sep = string_length + separator_length;
        // Only copy separators between elements, not at the start or beginning.
        sinkchar* copy_end =
            sink + (length_with_sep * repeat_last) - separator_length;
        uint32_t copy_length = length_with_sep;
        while (sink < copy_end - copy_length) {
          DCHECK_LE(sink + copy_length, sink_end);
          memcpy(sink, sink - copy_length, copy_length * sizeof(sinkchar));
          sink += copy_length;
          copy_length *= 2;
        }
        uint32_t remaining = static_cast<uint32_t>(copy_end - sink);
        if (remaining > 0) {
          DCHECK_LE(sink + remaining, sink_end);
          memcpy(sink, sink - remaining - separator_length,
                 remaining * sizeof(sinkchar));
          sink += remaining;
        }
        repeat_last = 0;
        num_separators = 1;
      }

      if (V8_LIKELY(!element_is_special)) {
        DCHECK(IsString(element));
        Tagged<String> string = Cast<String>(element);
        const uint32_t string_length = string->length();

        DCHECK(string_length == 0 || sink < sink_end);
        String::WriteToFlat(string, sink, 0, string_length);
        sink += string_length;

        // Next string element, needs at least one separator preceding it.
        num_separators = 1;
      }
      last_element = element;
    }
    // Move on to the next chunk.
    if (is_last_chunk) break;
#ifdef DEBUG
    prev_chunk = chunk;
#endif
    chunk = Cast<FixedArray>(maybe_next_chunk);
  }

  // Verify we have written to the end of the sink.
  DCHECK_EQ(sink, sink_end);
}

}  // namespace

// static
Address JSArray::ArrayJoinConcatToSequentialString(
    Isolate* isolate, Address raw_chunk_list_head,
    uintptr_t raw_last_chunk_length, Address raw_separator, Address raw_dest) {
  DisallowGarbageCollection no_gc;
  DisallowJavascriptExecution no_js(isolate);
  Tagged<FixedArray> chunk_list_head =
      Cast<FixedArray>(Tagged<Object>(raw_chunk_list_head));
  Tagged<String> separator = Cast<String>(Tagged<Object>(raw_separator));
  Tagged<String> dest = Cast<String>(Tagged<Object>(raw_dest));
  DCHECK(IsFixedArray(chunk_list_head));
  DCHECK(StringShape(dest).IsSequentialOneByte() ||
         StringShape(dest).IsSequentialTwoByte());

  uint32_t last_chunk_length = static_cast<uint32_t>(raw_last_chunk_length);
  if (StringShape(dest).IsSequentialOneByte()) {
    WriteChunkListToFlat(chunk_list_head, last_chunk_length, separator,
                         Cast<SeqOneByteString>(dest)->GetChars(no_gc),
                         dest->length());
  } else {
    DCHECK(StringShape(dest).IsSequentialTwoByte());
    WriteChunkListToFlat(chunk_list_head, last_chunk_length, separator,
                         Cast<SeqTwoByteString>(dest)->GetChars(no_gc),
                         dest->length());
  }
  return dest.ptr();
}

END_PROHIBIT_SIGN_CONVERSION()

void Oddball::Initialize(Isolate* isolate, DirectHandle<Oddball> oddball,
                         const char* to_string, DirectHandle<Number> to_number,
                         const char* type_of, uint8_t kind) {
  DirectHandle<String> internalized_to_string =
      isolate->factory()->InternalizeUtf8String(to_string);
  DirectHandle<String> internalized_type_of =
      isolate->factory()->InternalizeUtf8String(type_of);
  if (IsHeapNumber(*to_number)) {
    oddball->set_to_number_raw_as_bits(
        Cast<HeapNumber>(to_number)->value_as_bits());
  } else {
    oddball->set_to_number_raw(Object::NumberValue(*to_number));
  }
  oddball->set_to_number(*to_number);
  oddball->set_to_string(*internalized_to_string);
  oddball->set_type_of(*internalized_type_of);
  oddball->set_kind(kind);
}



// static
void JSArray::Initialize(Isolate* isolate, DirectHandle<JSArray> array,
                         int capacity, int length) {
  DCHECK_GE(capacity, 0);
  isolate->factory()->NewJSArrayStorage(
      array, length, capacity,
      ArrayStorageAllocationMode::INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE);
}

Maybe<bool> JSArray::SetLength(Isolate* isolate, DirectHandle<JSArray> array,
                               uint32_t new_length) {
  if (array->SetLengthWouldNormalize(new_length)) {
    JSObject::NormalizeElements(isolate, array);
  }
  return array->GetElementsAccessor()->SetLength(isolate, array, new_length);
}

// ES6: 9.5.2 [[SetPrototypeOf]] (V)
// static
Maybe<bool> JSProxy::SetPrototype(Isolate* isolate, DirectHandle<JSProxy> proxy,
                                  DirectHandle<Object> value,
                                  bool from_javascript,
                                  ShouldThrow should_throw) {
  STACK_CHECK(isolate, Nothing<bool>());
  DirectHandle<Name> trap_name = isolate->factory()->setPrototypeOf_string();
  // 1. Assert: Either Type(V) is Object or Type(V) is Null.
  DCHECK(IsJSReceiver(*value) || IsNull(*value, isolate));
  // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
  DirectHandle<Object> handler(proxy->handler(), isolate);
  // 3. If handler is null, throw a TypeError exception.
  // 4. Assert: Type(handler) is Object.
  if (proxy->IsRevoked()) {
    isolate->Throw(*isolate->factory()->NewTypeError(
        MessageTemplate::kProxyRevoked, trap_name));
    return Nothing<bool>();
  }
  // 5. Let target be the value of the [[ProxyTarget]] internal slot.
  DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
  // 6. Let trap be ? GetMethod(handler, "getPrototypeOf").
  DirectHandle<Object> trap;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap,
      Object::GetMethod(isolate, Cast<JSReceiver>(handler), trap_name));
  // 7. If trap is undefined, then return target.[[SetPrototypeOf]]().
  if (IsUndefined(*trap, isolate)) {
    return JSReceiver::SetPrototype(isolate, target, value, from_javascript,
                                    should_throw);
  }
  // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, V»)).
  DirectHandle<Object> args[] = {target, value};
  DirectHandle<Object> trap_result;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, trap_result,
      Execution::Call(isolate, trap, handler, base::VectorOf(args)));
  bool bool_trap_result = Object::BooleanValue(*trap_result, isolate);
  // 9. If booleanTrapResult is false, return false.
  if (!bool_trap_result) {
    RETURN_FAILURE(
        isolate, should_throw,
        NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name));
  }
  // 10. Let extensibleTarget be ? IsExtensible(target).
  Maybe<bool> is_extensible = JSReceiver::IsExtensible(isolate, target);
  if (is_extensible.IsNothing()) return Nothing<bool>();
  // 11. If extensibleTarget is true, return true.
  if (is_extensible.FromJust()) {
    if (bool_trap_result) return Just(true);
    RETURN_FAILURE(
        isolate, should_throw,
        NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name));
  }
  // 12. Let targetProto be ? target.[[GetPrototypeOf]]().
  DirectHandle<Object> target_proto;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto,
                             JSReceiver::GetPrototype(isolate, target));
  // 13. If SameValue(V, targetProto) is false, throw a TypeError exception.
  if (bool_trap_result && !Object::SameValue(*value, *target_proto)) {
    isolate->Throw(*isolate->factory()->NewTypeError(
        MessageTemplate::kProxySetPrototypeOfNonExtensible));
    return Nothing<bool>();
  }
  // 14. Return true.
  return Just(true);
}

bool JSArray::SetLengthWouldNormalize(uint32_t new_length) {
  if (!HasFastElements()) return false;
  uint32_t capacity = static_cast<uint32_t>(elements()->length());
  uint32_t new_capacity;
  return JSArray::SetLengthWouldNormalize(Isolate::Current()->heap(),
                                          new_length) &&
         ShouldConvertToSlowElements(*this, capacity, new_length - 1,
                                     &new_capacity);
}

void AllocationSite::ResetPretenureDecision() {
  set_pretenure_decision(kUndecided);
  set_memento_found_count(0);
  set_memento_create_count(0);
}

AllocationType AllocationSite::GetAllocationType() const {
  PretenureDecision mode = pretenure_decision();
  // Zombie objects "decide" to be untenured.
  return mode == kTenure ? AllocationType::kOld : AllocationType::kYoung;
}

bool AllocationSite::IsNested() {
  DCHECK(v8_flags.trace_track_allocation_sites);
  Tagged<Object> current = Isolate::Current()->heap()->allocation_sites_list();
  while (IsAllocationSite(current)) {
    Tagged<AllocationSiteWithWeakNext> current_site =
        Cast<AllocationSiteWithWeakNext>(current);
    if (current_site->nested_site() == this) {
      return true;
    }
    current = current_site->weak_next();
  }
  return false;
}

bool AllocationSite::ShouldTrack(ElementsKind from, ElementsKind to) {
  if (!V8_ALLOCATION_SITE_TRACKING_BOOL) return false;
  return IsMoreGeneralElementsKindTransition(from, to);
}

const char* AllocationSite::PretenureDecisionName(PretenureDecision decision) {
  switch (decision) {
    case kUndecided:
      return "undecided";
    case kDontTenure:
      return "don't tenure";
    case kMaybeTenure:
      return "maybe tenure";
    case kTenure:
      return "tenure";
    case kZombie:
      return "zombie";
    default:
      UNREACHABLE();
  }
}

bool JSArray::WouldChangeReadOnlyLength(DirectHandle<JSArray> array,
                                        uint32_t index) {
  uint32_t length = 0;
  CHECK(Object::ToArrayLength(array->length(), &length));
  if (length <= index) return HasReadOnlyLength(array);
  return false;
}

const char* Symbol::PrivateSymbolToName() const {
  ReadOnlyRoots roots = GetReadOnlyRoots();
#define SYMBOL_CHECK_AND_PRINT(_, name) \
  if (this == roots.name()) return #name;
  PRIVATE_SYMBOL_LIST_GENERATOR(SYMBOL_CHECK_AND_PRINT, /* not used */)
#undef SYMBOL_CHECK_AND_PRINT
  return "UNKNOWN";
}

v8::Promise::PromiseState JSPromise::status() const {
  int value = flags() & StatusBits::kMask;
  DCHECK(value == 0 || value == 1 || value == 2);
  return static_cast<v8::Promise::PromiseState>(value);
}

void JSPromise::set_status(Promise::PromiseState status) {
  int value = flags() & ~StatusBits::kMask;
  set_flags(value | status);
}

// static
const char* JSPromise::Status(v8::Promise::PromiseState status) {
  switch (status) {
    case v8::Promise::kFulfilled:
      return "fulfilled";
    case v8::Promise::kPending:
      return "pending";
    case v8::Promise::kRejected:
      return "rejected";
  }
  UNREACHABLE();
}

// static
Handle<Object> JSPromise::Fulfill(DirectHandle<JSPromise> promise,
                                  DirectHandle<Object> value) {
  Isolate* const isolate = Isolate::Current();

#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
  if (isolate->HasContextPromiseHooks()) {
    isolate->raw_native_context()->RunPromiseHook(
        PromiseHookType::kResolve, indirect_handle(promise, isolate),
        isolate->factory()->undefined_value());
  }
#endif

  // 1. Assert: The value of promise.[[PromiseState]] is "pending".
  CHECK_EQ(Promise::kPending, promise->status());

  // 2. Let reactions be promise.[[PromiseFulfillReactions]].
  DirectHandle<Object> reactions(promise->reactions(), isolate);

  // 3. Set promise.[[PromiseResult]] to value.
  // 4. Set promise.[[PromiseFulfillReactions]] to undefined.
  // 5. Set promise.[[PromiseRejectReactions]] to undefined.
  promise->set_reactions_or_result(Cast<JSAny>(*value));

  // 6. Set promise.[[PromiseState]] to "fulfilled".
  promise->set_status(Promise::kFulfilled);

  // 7. Return TriggerPromiseReactions(reactions, value).
  return TriggerPromiseReactions(isolate, reactions, value,
                                 PromiseReaction::kFulfill);
}

static void MoveMessageToPromise(Isolate* isolate,
                                 DirectHandle<JSPromise> promise) {
  if (!isolate->has_pending_message()) return;

  if (isolate->debug()->is_active()) {
    DirectHandle<Object> message(isolate->pending_message(), isolate);
    DirectHandle<Symbol> key =
        isolate->factory()->promise_debug_message_symbol();
    Object::SetProperty(isolate, promise, key, message,
                        StoreOrigin::kMaybeKeyed,
                        Just(ShouldThrow::kThrowOnError))
        .Assert();
  }

  // The message object for a rejected promise was only stored for this purpose.
  // Clear it, otherwise we might leak memory.
  isolate->clear_pending_message();
}

// static
Handle<Object> JSPromise::Reject(DirectHandle<JSPromise> promise,
                                 DirectHandle<Object> reason,
                                 bool debug_event) {
  Isolate* const isolate = Isolate::Current();
  DCHECK(
      !reinterpret_cast<v8::Isolate*>(isolate)->GetCurrentContext().IsEmpty());

  MoveMessageToPromise(isolate, promise);

  if (debug_event) isolate->debug()->OnPromiseReject(promise, reason);
  isolate->RunAllPromiseHooks(PromiseHookType::kResolve, promise,
                              isolate->factory()->undefined_value());

  // 1. Assert: The value of promise.[[PromiseState]] is "pending".
  CHECK_EQ(Promise::kPending, promise->status());

  // 2. Let reactions be promise.[[PromiseRejectReactions]].
  DirectHandle<Object> reactions(promise->reactions(), isolate);

  // 3. Set promise.[[PromiseResult]] to reason.
  // 4. Set promise.[[PromiseFulfillReactions]] to undefined.
  // 5. Set promise.[[PromiseRejectReactions]] to undefined.
  promise->set_reactions_or_result(Cast<JSAny>(*reason));

  // 6. Set promise.[[PromiseState]] to "rejected".
  promise->set_status(Promise::kRejected);

  // 7. If promise.[[PromiseIsHandled]] is false, perform
  //    HostPromiseRejectionTracker(promise, "reject").
  if (!promise->has_handler()) {
    isolate->ReportPromiseReject(promise, reason, kPromiseRejectWithNoHandler);
  }

  // 8. Return TriggerPromiseReactions(reactions, reason).
  return TriggerPromiseReactions(isolate, reactions, reason,
                                 PromiseReaction::kReject);
}

// https://tc39.es/ecma262/#sec-promise-resolve-functions
// static
MaybeHandle<Object> JSPromise::Resolve(DirectHandle<JSPromise> promise,
                                       DirectHandle<Object> resolution_obj) {
  Isolate* const isolate = Isolate::Current();
  DCHECK(
      !reinterpret_cast<v8::Isolate*>(isolate)->GetCurrentContext().IsEmpty());

  isolate->RunPromiseHook(PromiseHookType::kResolve, promise,
                          isolate->factory()->undefined_value());

  // 7. If SameValue(resolution, promise) is true, then
  if (promise.is_identical_to(resolution_obj)) {
    // a. Let selfResolutionError be a newly created TypeError object.
    DirectHandle<Object> self_resolution_error =
        isolate->factory()->NewTypeError(MessageTemplate::kPromiseCyclic,
                                         resolution_obj);
    // b. Return RejectPromise(promise, selfResolutionError).
    return Reject(promise, self_resolution_error);
  }

  // 8. If Type(resolution) is not Object, then
  DirectHandle<JSReceiver> resolution_recv;
  if (!TryCast<JSReceiver>(resolution_obj, &resolution_recv)) {
    // a. Return FulfillPromise(promise, resolution).
    return Fulfill(promise, resolution_obj);
  }

  // 9. Let then be Get(resolution, "then").
  MaybeDirectHandle<Object> then;

  // Make sure a lookup of "then" on any JSPromise whose [[Prototype]] is the
  // initial %PromisePrototype% yields the initial method. In addition this
  // protector also guards the negative lookup of "then" on the intrinsic
  // %ObjectPrototype%, meaning that such lookups are guaranteed to yield
  // undefined without triggering any side-effects.
  if (IsJSPromise(*resolution_recv) &&
      resolution_recv->map()->prototype()->map()->instance_type() ==
          JS_PROMISE_PROTOTYPE_TYPE &&
      Protectors::IsPromiseThenLookupChainIntact(isolate)) {
    // We can skip the "then" lookup on {resolution} if its [[Prototype]]
    // is the (initial) Promise.prototype and the Promise#then protector
    // is intact, as that guards the lookup path for the "then" property
    // on JSPromise instances which have the (initial) %PromisePrototype%.
    DirectHandle<NativeContext> resolution_proto_context =
        Cast<JSReceiver>(resolution_recv->map()->prototype())
            ->GetCreationContext(isolate)
            .ToHandleChecked();
    then =
        DirectHandle<Object>(resolution_proto_context->promise_then(), isolate);
  } else {
    then = JSReceiver::GetProperty(isolate, resolution_recv,
                                   isolate->factory()->then_string());
  }

  // 10. If then is an abrupt completion, then
  DirectHandle<Object> then_action;
  if (!then.ToHandle(&then_action)) {
    // The "then" lookup can cause termination.
    if (!isolate->is_catchable_by_javascript(isolate->exception())) {
      return kNullMaybeHandle;
    }

    // a. Return RejectPromise(promise, then.[[Value]]).
    DirectHandle<Object> reason(isolate->exception(), isolate);
    isolate->clear_exception();
    return Reject(promise, reason, false);
  }

  // 11. Let thenAction be then.[[Value]].
  // 12. If IsCallable(thenAction) is false, then
  if (!IsCallable(*then_action)) {
    // a. Return FulfillPromise(promise, resolution).
    return Fulfill(promise, resolution_recv);
  }

  // 13. Let job be NewPromiseResolveThenableJob(promise, resolution,
  //                                             thenAction).
  DirectHandle<NativeContext> then_context;
  if (!JSReceiver::GetContextForMicrotask(Cast<JSReceiver>(then_action))
           .ToHandle(&then_context)) {
    then_context = isolate->native_context();
  }

  DirectHandle<PromiseResolveThenableJobTask> task =
      isolate->factory()->NewPromiseResolveThenableJobTask(
          promise, resolution_recv, Cast<JSReceiver>(then_action),
          then_context);
  if (isolate->debug()->is_active() && IsJSPromise(*resolution_recv)) {
    // Mark the dependency of the new {promise} on the {resolution}.
    Object::SetProperty(isolate, resolution_recv,
                        isolate->factory()->promise_handled_by_symbol(),
                        promise)
        .Check();
  }
  MicrotaskQueue* microtask_queue = then_context->microtask_queue();
  if (microtask_queue) microtask_queue->EnqueueMicrotask(*task);

  // 15. Return undefined.
  return isolate->factory()->undefined_value();
}

// static
Handle<Object> JSPromise::TriggerPromiseReactions(
    Isolate* isolate, DirectHandle<Object> reactions,
    DirectHandle<Object> argument, PromiseReaction::Type type) {
  CHECK(IsSmi(*reactions) || IsPromiseReaction(*reactions));

  // We need to reverse the {reactions} here, since we record them
  // on the JSPromise in the reverse order.
  {
    DisallowGarbageCollection no_gc;
    Tagged<UnionOf<Smi, PromiseReaction>> current =
        Cast<UnionOf<Smi, PromiseReaction>>(*reactions);
    Tagged<UnionOf<Smi, PromiseReaction>> reversed = Smi::zero();
    while (!IsSmi(current)) {
      Tagged<UnionOf<Smi, PromiseReaction>> next =
          Cast<PromiseReaction>(current)->next();
      Cast<PromiseReaction>(current)->set_next(reversed);
      reversed = current;
      current = next;
    }
    reactions = direct_handle(reversed, isolate);
  }

  // Morph the {reactions} into PromiseReactionJobTasks
  // and push them onto the microtask queue.
  while (!IsSmi(*reactions)) {
    auto task = Cast<HeapObject>(reactions);
    auto reaction = Cast<PromiseReaction>(task);
    reactions = direct_handle(reaction->next(), isolate);

    // According to HTML, we use the context of the appropriate handler as the
    // context of the microtask. See step 3 of HTML's EnqueueJob:
    // https://html.spec.whatwg.org/C/#enqueuejob(queuename,-job,-arguments)
    DirectHandle<NativeContext> handler_context;

    DirectHandle<UnionOf<Undefined, JSCallable>> primary_handler;
    DirectHandle<UnionOf<Undefined, JSCallable>> secondary_handler;
    if (type == PromiseReaction::kFulfill) {
      primary_handler = direct_handle(reaction->fulfill_handler(), isolate);
      secondary_handler = direct_handle(reaction->reject_handler(), isolate);
    } else {
      primary_handler = direct_handle(reaction->reject_handler(), isolate);
      secondary_handler = direct_handle(reaction->fulfill_handler(), isolate);
    }

    bool has_handler_context = false;
    if (IsJSReceiver(*primary_handler)) {
      has_handler_context =
          JSReceiver::GetContextForMicrotask(Cast<JSReceiver>(primary_handler))
              .ToHandle(&handler_context);
    }
    if (!has_handler_context && IsJSReceiver(*secondary_handler)) {
      has_handler_context = JSReceiver::GetContextForMicrotask(
                                Cast<JSReceiver>(secondary_handler))
                                .ToHandle(&handler_context);
    }
    if (!has_handler_context) handler_context = isolate->native_context();

    static_assert(static_cast<int>(sizeof(PromiseReaction)) ==
                  static_cast<int>(sizeof(PromiseReactionJobTask)));
    if (type == PromiseReaction::kFulfill) {
      task->set_map(
          isolate,
          ReadOnlyRoots(isolate).promise_fulfill_reaction_job_task_map(),
          kReleaseStore);
      Cast<PromiseFulfillReactionJobTask>(task)->set_argument(*argument);
      Cast<PromiseFulfillReactionJobTask>(task)->set_context(*handler_context);
      static_assert(
          static_cast<int>(offsetof(PromiseReaction, fulfill_handler_)) ==
          static_cast<int>(offsetof(PromiseFulfillReactionJobTask, handler_)));
      static_assert(
          static_cast<int>(offsetof(PromiseReaction, promise_or_capability_)) ==
          static_cast<int>(
              offsetof(PromiseFulfillReactionJobTask, promise_or_capability_)));
#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
      static_assert(
          static_cast<int>(offsetof(PromiseReaction,
                                    continuation_preserved_embedder_data_)) ==
          static_cast<int>(offsetof(PromiseFulfillReactionJobTask,
                                    continuation_preserved_embedder_data_)));
#endif  // V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
    } else {
      DisallowGarbageCollection no_gc;
      task->set_map(
          isolate,
          ReadOnlyRoots(isolate).promise_reject_reaction_job_task_map(),
          kReleaseStore);
      Cast<PromiseRejectReactionJobTask>(task)->set_argument(*argument);
      Cast<PromiseRejectReactionJobTask>(task)->set_context(*handler_context);
      Cast<PromiseRejectReactionJobTask>(task)->set_handler(*primary_handler);
      static_assert(
          static_cast<int>(offsetof(PromiseReaction, promise_or_capability_)) ==
          static_cast<int>(
              offsetof(PromiseRejectReactionJobTask, promise_or_capability_)));
#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
      static_assert(
          static_cast<int>(offsetof(PromiseReaction,
                                    continuation_preserved_embedder_data_)) ==
          static_cast<int>(offsetof(PromiseRejectReactionJobTask,
                                    continuation_preserved_embedder_data_)));
#endif  // V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
    }

    MicrotaskQueue* microtask_queue = handler_context->microtask_queue();
    if (microtask_queue) {
      microtask_queue->EnqueueMicrotask(*Cast<PromiseReactionJobTask>(task));
    }
  }

  return isolate->factory()->undefined_value();
}

#ifdef V8_LOWER_LIMITS_MODE
const uint32_t EphemeronHashTableShape::kHashBits = 10;
#else
const uint32_t EphemeronHashTableShape::kHashBits =
    PropertyArray::HashField::kSize;
#endif  // V8_LOWER_LIMITS_MODE

template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::IteratePrefix(ObjectVisitor* v) {
  BodyDescriptorBase::IteratePointers(this, 0, kElementsStartOffset, v);
}

template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::IterateElements(ObjectVisitor* v) {
  BodyDescriptorBase::IteratePointers(this, kElementsStartOffset,
                                      SizeFor(length()), v);
}

template <typename Derived, typename Shape>
template <typename IsolateT>
Handle<Derived> HashTable<Derived, Shape>::New(
    IsolateT* isolate, uint32_t at_least_space_for, AllocationType allocation,
    MinimumCapacity capacity_option) {
  Handle<Derived> result;
  if (TryNew(isolate, at_least_space_for, allocation, capacity_option)
          .To(&result)) {
    return result;
  }
  isolate->FatalProcessOutOfHeapMemory("invalid table size");
  UNREACHABLE();
}

template <typename Derived, typename Shape>
template <typename IsolateT>
MaybeHandle<Derived> HashTable<Derived, Shape>::TryNew(
    IsolateT* isolate, uint32_t at_least_space_for, AllocationType allocation,
    MinimumCapacity capacity_option) {
  DCHECK_LE(0, at_least_space_for);

  uint32_t capacity;
  if (capacity_option == USE_CUSTOM_MINIMUM_CAPACITY) {
    DCHECK(base::bits::IsPowerOfTwo(at_least_space_for));
    capacity = at_least_space_for;
    if (capacity > HashTable::kMaxCapacity) {
      return kNullMaybeHandle;
    }
  } else {
    // Max |at_least_space_for| value that's about to make the table capacity
    // exceed the HashTable::kMaxCapacity.
    const uint32_t kMaxAtLeastSpaceFor = HashTable::kMaxCapacity * 2 / 3;
    static_assert(ComputeCapacity(kMaxAtLeastSpaceFor) <=
                  HashTable::kMaxCapacity);
    static_assert(ComputeCapacity(kMaxAtLeastSpaceFor + 1) >=
                  HashTable::kMaxCapacity);
    static_assert(ComputeCapacity(kMaxAtLeastSpaceFor + 4) >
                  HashTable::kMaxCapacity);
    if (at_least_space_for > kMaxAtLeastSpaceFor) {
      return kNullMaybeHandle;
    }
    capacity = ComputeCapacity(at_least_space_for);
    DCHECK_LE(capacity, HashTable::kMaxCapacity);
  }
  return NewInternal(isolate, capacity, allocation);
}

template <typename Derived, typename Shape>
template <typename IsolateT>
Handle<Derived> HashTable<Derived, Shape>::NewInternal(
    IsolateT* isolate, uint32_t capacity, AllocationType allocation) {
  auto* factory = isolate->factory();
  int length = EntryToIndex(InternalIndex(capacity));
  Handle<FixedArray> array = factory->NewFixedArrayWithMap(
      Derived::GetMap(isolate->roots_table()), length, allocation);
  Handle<Derived> table = Cast<Derived>(array);
  DisallowGarbageCollection no_gc;
  Tagged<Derived> raw_table = *table;
  raw_table->SetNumberOfElements(0);
  raw_table->SetNumberOfDeletedElements(0);
  raw_table->SetCapacity(capacity);
  return table;
}

template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::Rehash(PtrComprCageBase cage_base,
                                       Tagged<Derived> new_table) {
  DisallowGarbageCollection no_gc;
  WriteBarrierModeScope mode = new_table->GetWriteBarrierMode(no_gc);

  DCHECK_LT(NumberOfElements(), new_table->Capacity());

  // Copy prefix to new array.
  for (int i = kPrefixStartIndex; i < kElementsStartIndex; i++) {
    new_table->set(i, get(i), *mode);
  }

  // Rehash the elements.
  ReadOnlyRoots roots = GetReadOnlyRoots();
  for (InternalIndex i : this->IterateEntries()) {
    uint32_t from_index = EntryToIndex(i);
    Tagged<Object> k = this->get(from_index);
    if (!IsKey(roots, k)) continue;
    uint32_t hash = TodoShape::HashForObject(roots, k);
    uint32_t insertion_index =
        EntryToIndex(new_table->FindInsertionEntry(cage_base, roots, hash));
    new_table->set_key(insertion_index, get(from_index), *mode);
    for (int j = 1; j < TodoShape::kEntrySize; j++) {
      new_table->set(insertion_index + j, get(from_index + j), *mode);
    }
  }
  new_table->SetNumberOfElements(NumberOfElements());
  new_table->SetNumberOfDeletedElements(0);
}

template <typename Derived, typename Shape>
InternalIndex HashTable<Derived, Shape>::EntryForProbe(ReadOnlyRoots roots,
                                                       Tagged<Object> k,
                                                       int probe,
                                                       InternalIndex expected) {
  uint32_t hash = TodoShape::HashForObject(roots, k);
  uint32_t capacity = this->Capacity();
  InternalIndex entry = FirstProbe(hash, capacity);
  for (int i = 1; i < probe; i++) {
    if (entry == expected) return expected;
    entry = NextProbe(entry, i, capacity);
  }
  return entry;
}

template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::Swap(InternalIndex entry1, InternalIndex entry2,
                                     WriteBarrierMode mode) {
  int index1 = EntryToIndex(entry1);
  int index2 = EntryToIndex(entry2);
  Tagged<Object> temp[TodoShape::kEntrySize];
  Derived* self = static_cast<Derived*>(this);
  for (int j = 0; j < TodoShape::kEntrySize; j++) {
    temp[j] = get(index1 + j);
  }
  self->set_key(index1, get(index2), mode);
  for (int j = 1; j < TodoShape::kEntrySize; j++) {
    set(index1 + j, get(index2 + j), mode);
  }
  self->set_key(index2, temp[0], mode);
  for (int j = 1; j < TodoShape::kEntrySize; j++) {
    set(index2 + j, temp[j], mode);
  }
}

template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::Rehash(PtrComprCageBase cage_base) {
  DisallowGarbageCollection no_gc;
  WriteBarrierModeScope mode = GetWriteBarrierMode(no_gc);
  ReadOnlyRoots roots = EarlyGetReadOnlyRoots();
  uint32_t capacity = Capacity();
  bool done = false;
  for (int probe = 1; !done; probe++) {
    // All elements at entries given by one of the first _probe_ probes
    // are placed correctly. Other elements might need to be moved.
    done = true;
    for (InternalIndex current(0); current.raw_value() < capacity;
         /* {current} is advanced manually below, when appropriate.*/) {
      Tagged<Object> current_key = KeyAt(cage_base, current);
      if (!IsKey(roots, current_key)) {
        ++current;  // Advance to next entry.
        continue;
      }
      InternalIndex target = EntryForProbe(roots, current_key, probe, current);
      if (current == target) {
        ++current;  // Advance to next entry.
        continue;
      }
      Tagged<Object> target_key = KeyAt(cage_base, target);
      if (!IsKey(roots, target_key) ||
          EntryForProbe(roots, target_key, probe, target) != target) {
        // Put the current element into the correct position.
        Swap(current, target, *mode);
        // The other element will be processed on the next iteration,
        // so don't advance {current} here!
      } else {
        // The place for the current element is occupied. Leave the element
        // for the next probe.
        done = false;
        ++current;  // Advance to next entry.
      }
    }
  }
  // Wipe deleted entries.
  Tagged<Object> the_hole = roots.the_hole_value();
  Tagged<HeapObject> undefined = roots.undefined_value();
  Derived* self = static_cast<Derived*>(this);
  for (InternalIndex current : InternalIndex::Range(capacity)) {
    if (KeyAt(cage_base, current) == the_hole) {
      self->set_key(EntryToIndex(current) + kEntryKeyIndex, undefined,
                    SKIP_WRITE_BARRIER);
    }
  }
  SetNumberOfDeletedElements(0);
}

template <typename Derived, typename Shape>
template <typename IsolateT, template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived> HashTable<Derived, Shape>::EnsureCapacity(
    IsolateT* isolate, HandleType<Derived> table, int n,
    AllocationType allocation) {
  if (table->HasSufficientCapacityToAdd(n)) return table;

  int capacity = table->Capacity();
  int new_nof = table->NumberOfElements() + n;

  bool should_pretenure = allocation == AllocationType::kOld ||
                          ((capacity > kMinCapacityForPretenure) &&
                           !HeapLayout::InYoungGeneration(*table));
  HandleType<Derived> new_table = HashTable::New(
      isolate, new_nof,
      should_pretenure ? AllocationType::kOld : AllocationType::kYoung);

  table->Rehash(isolate, *new_table);
  return new_table;
}

template <typename Derived, typename Shape>
bool HashTable<Derived, Shape>::HasSufficientCapacityToAdd(
    int number_of_additional_elements) {
  return HasSufficientCapacityToAdd(Capacity(), NumberOfElements(),
                                    NumberOfDeletedElements(),
                                    number_of_additional_elements);
}

// static
template <typename Derived, typename Shape>
bool HashTable<Derived, Shape>::HasSufficientCapacityToAdd(
    int capacity, int number_of_elements, int number_of_deleted_elements,
    int number_of_additional_elements) {
  int nof = number_of_elements + number_of_additional_elements;
  // Return true if:
  //   50% is still free after adding number_of_additional_elements elements and
  //   at most 50% of the free elements are deleted elements.
  if ((nof < capacity) &&
      ((number_of_deleted_elements <= (capacity - nof) / 2))) {
    int needed_free = nof / 2;
    if (nof + needed_free <= capacity) return true;
  }
  return false;
}

// static
template <typename Derived, typename Shape>
uint32_t HashTable<Derived, Shape>::ComputeCapacityWithShrink(
    uint32_t current_capacity, uint32_t at_least_room_for) {
  // Shrink to fit the number of elements if only a quarter of the
  // capacity is filled with elements.
  if (at_least_room_for > (current_capacity / 4)) return current_capacity;
  // Recalculate the smaller capacity actually needed.
  uint32_t new_capacity = ComputeCapacity(at_least_room_for);
  DCHECK_GE(new_capacity, at_least_room_for);
  // Don't go lower than room for {kMinShrinkCapacity} elements.
  if (new_capacity < Derived::kMinShrinkCapacity) return current_capacity;
  return new_capacity;
}

// static
template <typename Derived, typename Shape>
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived> HashTable<Derived, Shape>::Shrink(Isolate* isolate,
                                                      HandleType<Derived> table,
                                                      int additional_capacity) {
  uint32_t new_capacity = ComputeCapacityWithShrink(
      table->Capacity(), table->NumberOfElements() + additional_capacity);
  if (new_capacity == table->Capacity()) return table;
  DCHECK_GE(new_capacity, Derived::kMinShrinkCapacity);

  bool pretenure = (new_capacity > kMinCapacityForPretenure) &&
                   !HeapLayout::InYoungGeneration(*table);
  HandleType<Derived> new_table =
      HashTable::New(isolate, new_capacity,
                     pretenure ? AllocationType::kOld : AllocationType::kYoung,
                     USE_CUSTOM_MINIMUM_CAPACITY);

  table->Rehash(isolate, *new_table);
  return new_table;
}

template <typename Derived, typename Shape>
InternalIndex HashTable<Derived, Shape>::FindInsertionEntry(
    PtrComprCageBase cage_base, ReadOnlyRoots roots, uint32_t hash) {
  uint32_t capacity = Capacity();
  uint32_t count = 1;
  // EnsureCapacity will guarantee the hash table is never full.
  for (InternalIndex entry = FirstProbe(hash, capacity);;
       entry = NextProbe(entry, count++, capacity)) {
    if (!IsKey(roots, KeyAt(cage_base, entry))) return entry;
  }
}

std::optional<Tagged<PropertyCell>>
GlobalDictionary::TryFindPropertyCellForConcurrentLookupIterator(
    Isolate* isolate, DirectHandle<Name> name, RelaxedLoadTag tag) {
  // This reimplements HashTable::FindEntry for use in a concurrent setting.
  // 1) Atomic loads.
  // 2) IsPendingAllocation checks.
  // 3) Return the PropertyCell value instead of the InternalIndex to avoid a
  //   repeated load (unsafe with concurrent modifications).

  DisallowGarbageCollection no_gc;
  PtrComprCageBase cage_base{isolate};
  ReadOnlyRoots roots(isolate);
  const int32_t hash = TodoShape::Hash(roots, name);
  const uint32_t capacity = Capacity();
  uint32_t count = 1;
  Tagged<Object> undefined = roots.undefined_value();
  Tagged<Object> the_hole = roots.the_hole_value();
  // EnsureCapacity will guarantee the hash table is never full.
  for (InternalIndex entry = FirstProbe(hash, capacity);;
       entry = NextProbe(entry, count++, capacity)) {
    Tagged<Object> element = KeyAt(cage_base, entry, kRelaxedLoad);
    if (isolate->heap()->IsPendingAllocation(element)) return {};
    if (element == undefined) return {};
    if (TodoShape::kMatchNeedsHoleCheck && element == the_hole) continue;
    if (!TodoShape::IsMatch(name, element)) continue;
    CHECK(IsPropertyCell(element, cage_base));
    return Cast<PropertyCell>(element);
  }
}

Handle<StringSet> StringSet::New(Isolate* isolate) {
  return HashTable::New(isolate, 0);
}

Handle<StringSet> StringSet::Add(Isolate* isolate, Handle<StringSet> stringset,
                                 DirectHandle<String> name) {
  if (!stringset->Has(isolate, name)) {
    stringset = EnsureCapacity(isolate, stringset);
    uint32_t hash = TodoShape::Hash(ReadOnlyRoots(isolate), *name);
    InternalIndex entry = stringset->FindInsertionEntry(isolate, hash);
    stringset->set(EntryToIndex(entry), *name);
    stringset->ElementAdded();
  }
  return stringset;
}

bool StringSet::Has(Isolate* isolate, DirectHandle<String> name) {
  return FindEntry(isolate, *name).is_found();
}

Handle<RegisteredSymbolTable> RegisteredSymbolTable::Add(
    Isolate* isolate, Handle<RegisteredSymbolTable> table,
    DirectHandle<String> key, DirectHandle<Symbol> symbol) {
  // Validate that the key is absent.
  SLOW_DCHECK(table->FindEntry(isolate, key).is_not_found());

  table = EnsureCapacity(isolate, table);
  uint32_t hash = TodoShape::Hash(ReadOnlyRoots(isolate), key);
  InternalIndex entry = table->FindInsertionEntry(isolate, hash);
  table->set(EntryToIndex(entry), *key);
  table->set(EntryToValueIndex(entry), *symbol);
  table->ElementAdded();
  return table;
}

template <typename Derived, typename Shape>
template <typename IsolateT>
Handle<Derived> BaseNameDictionary<Derived, Shape>::New(
    IsolateT* isolate, int at_least_space_for, AllocationType allocation,
    MinimumCapacity capacity_option) {
  DCHECK_LE(0, at_least_space_for);
  Handle<Derived> dict = Dictionary<Derived, Shape>::New(
      isolate, at_least_space_for, allocation, capacity_option);
  dict->SetHash(PropertyArray::kNoHashSentinel);
  dict->set_next_enumeration_index(PropertyDetails::kInitialIndex);
  return dict;
}

template <typename IsolateT>
Handle<NameDictionary> NameDictionary::New(IsolateT* isolate,
                                           int at_least_space_for,
                                           AllocationType allocation,
                                           MinimumCapacity capacity_option) {
  Handle<NameDictionary> dict =
      BaseNameDictionary<NameDictionary, NameDictionaryShape>::New(
          isolate, at_least_space_for, allocation, capacity_option);
  dict->set_flags(kFlagsDefault);
  return dict;
}

template <typename Derived, typename Shape>
int BaseNameDictionary<Derived, Shape>::NextEnumerationIndex(
    Isolate* isolate, DirectHandle<Derived> dictionary) {
  int index = dictionary->next_enumeration_index();
  // Check whether the next enumeration index is valid.
  if (!PropertyDetails::IsValidIndex(index)) {
    // If not, we generate new indices for the properties.
    DirectHandle<FixedArray> iteration_order =
        IterationIndices(isolate, dictionary);
    int length = iteration_order->length();
    DCHECK_LE(length, dictionary->NumberOfElements());

    // Iterate over the dictionary using the enumeration order and update
    // the dictionary with new enumeration indices.
    for (int i = 0; i < length; i++) {
      InternalIndex internal_index(Smi::ToInt(iteration_order->get(i)));
      DCHECK(dictionary->IsKey(GetReadOnlyRoots(),
                               dictionary->KeyAt(isolate, internal_index)));

      int enum_index = PropertyDetails::kInitialIndex + i;

      PropertyDetails details = dictionary->DetailsAt(internal_index);
      PropertyDetails new_details = details.set_index(enum_index);
      dictionary->DetailsAtPut(internal_index, new_details);
    }

    index = PropertyDetails::kInitialIndex + length;
  }

  // Don't update the next enumeration index here, since we might be looking at
  // an immutable empty dictionary.
  return index;
}

template <typename Derived, typename Shape>
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived> Dictionary<Derived, Shape>::DeleteEntry(
    Isolate* isolate, HandleType<Derived> dictionary, InternalIndex entry) {
  DCHECK(TodoShape::kEntrySize != 3 ||
         dictionary->DetailsAt(entry).IsConfigurable());
  dictionary->ClearEntry(entry);
  dictionary->ElementRemoved();
  return Shrink(isolate, dictionary);
}

template <typename Derived, typename Shape>
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
auto Dictionary<Derived, Shape>::AtPut(Isolate* isolate,
                                       HandleType<Derived> dictionary, Key key,
                                       DirectHandle<Object> value,
                                       PropertyDetails details) {
  using AtPutReturnType =
      decltype(Derived::Add(isolate, dictionary, key, value, details));

  InternalIndex entry = dictionary->FindEntry(isolate, key);

  // If the entry is present set the value;
  if (entry.is_not_found()) {
    return Derived::Add(isolate, dictionary, key, value, details);
  }

  // We don't need to copy over the enumeration index.
  dictionary->ValueAtPut(entry, *value);
  if (TodoShape::kEntrySize == 3) dictionary->DetailsAtPut(entry, details);
  return AtPutReturnType(dictionary);
}

template <typename Derived, typename Shape>
void Dictionary<Derived, Shape>::UncheckedAtPut(
    Isolate* isolate, DirectHandle<Derived> dictionary, Key key,
    DirectHandle<Object> value, PropertyDetails details) {
  InternalIndex entry = dictionary->FindEntry(isolate, key);

  // If the entry is present set the value;
  if (entry.is_not_found()) {
    Derived::UncheckedAdd(isolate, dictionary, key, value, details);
  } else {
    // We don't need to copy over the enumeration index.
    dictionary->ValueAtPut(entry, *value);
    if (TodoShape::kEntrySize == 3) dictionary->DetailsAtPut(entry, details);
  }
}

template <typename Derived, typename Shape>
template <typename IsolateT, template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived>
BaseNameDictionary<Derived, Shape>::AddNoUpdateNextEnumerationIndex(
    IsolateT* isolate, HandleType<Derived> dictionary, Key key,
    DirectHandle<Object> value, PropertyDetails details,
    InternalIndex* entry_out) {
  // Insert element at empty or deleted entry.
  return Dictionary<Derived, Shape>::Add(isolate, dictionary, key, value,
                                         details, entry_out);
}

template <typename Derived, typename Shape>
template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived>::MaybeType BaseNameDictionary<Derived, Shape>::Add(
    Isolate* isolate, HandleType<Derived> dictionary, Key key,
    DirectHandle<Object> value, PropertyDetails details,
    InternalIndex* entry_out) {
  // Insert element at empty or deleted entry
  DCHECK_EQ(0, details.dictionary_index());
  // Assign an enumeration index to the property and update
  // SetNextEnumerationIndex.
  int index = Derived::NextEnumerationIndex(isolate, dictionary);
  if (!PropertyDetails::CanSetIndex(index)) {
    THROW_NEW_ERROR(isolate,
                    NewRangeError(MessageTemplate::kTooManyProperties));
  }
  details = details.set_index(index);
  dictionary = AddNoUpdateNextEnumerationIndex(isolate, dictionary, key, value,
                                               details, entry_out);
  // Update enumeration index here in order to avoid potential modification of
  // the canonical empty dictionary which lives in read only space.
  dictionary->set_next_enumeration_index(index + 1);
  return dictionary;
}

template <typename Derived, typename Shape>
template <typename IsolateT, template <typename> typename HandleType,
          AllocationType key_allocation>
  requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived> Dictionary<Derived, Shape>::Add(
    IsolateT* isolate, HandleType<Derived> dictionary, Key key,
    DirectHandle<Object> value, PropertyDetails details,
    InternalIndex* entry_out) {
  ReadOnlyRoots roots(isolate);
  uint32_t hash = TodoShape::Hash(roots, key);
  // Validate that the key is absent.
  SLOW_DCHECK(dictionary->FindEntry(isolate, key).is_not_found());
  // Check whether the dictionary should be extended.
  dictionary = Derived::EnsureCapacity(isolate, dictionary);

  // Compute the key object.
  DirectHandle<Object> k =
      TodoShape::template AsHandle<key_allocation>(isolate, key);

  InternalIndex entry = dictionary->FindInsertionEntry(isolate, roots, hash);
  dictionary->SetEntry(entry, *k, *value, details);
  DCHECK(IsNumber(dictionary->KeyAt(isolate, entry)) ||
         IsUniqueName(TodoShape::Unwrap(dictionary->KeyAt(isolate, entry))));
  dictionary->ElementAdded();
  if (entry_out) *entry_out = entry;
  return dictionary;
}

template <typename Derived, typename Shape>
template <typename IsolateT, template <typename> typename HandleType,
          AllocationType key_allocation>
  requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
void Dictionary<Derived, Shape>::UncheckedAdd(IsolateT* isolate,
                                              HandleType<Derived> dictionary,
                                              Key key,
                                              DirectHandle<Object> value,
                                              PropertyDetails details) {
  ReadOnlyRoots roots(isolate);
  uint32_t hash = TodoShape::Hash(roots, key);
  // Validate that the key is absent and we capacity is sufficient.
  SLOW_DCHECK(dictionary->FindEntry(isolate, key).is_not_found());
  DCHECK(dictionary->HasSufficientCapacityToAdd(1));

  // Compute the key object.
  DirectHandle<Object> k =
      TodoShape::template AsHandle<key_allocation>(isolate, key);

  InternalIndex entry = dictionary->FindInsertionEntry(isolate, roots, hash);
  dictionary->SetEntry(entry, *k, *value, details);
  DCHECK(IsNumber(dictionary->KeyAt(isolate, entry)) ||
         IsUniqueName(TodoShape::Unwrap(dictionary->KeyAt(isolate, entry))));
}

template <typename Derived, typename Shape>
Handle<Derived> Dictionary<Derived, Shape>::ShallowCopy(
    Isolate* isolate, DirectHandle<Derived> dictionary,
    AllocationType allocation) {
  return Cast<Derived>(isolate->factory()->CopyFixedArrayWithMap(
      dictionary, Derived::GetMap(isolate->roots_table()), allocation));
}

// static
Handle<SimpleNumberDictionary> SimpleNumberDictionary::Set(
    Isolate* isolate, Handle<SimpleNumberDictionary> dictionary, uint32_t key,
    DirectHandle<Object> value) {
  return AtPut(isolate, dictionary, key, value, PropertyDetails::Empty());
}

// static
Handle<SimpleNameDictionary> SimpleNameDictionary::Set(
    Isolate* isolate, Handle<SimpleNameDictionary> dictionary,
    DirectHandle<Name> key, DirectHandle<Object> value) {
  return AtPut(isolate, dictionary, key, value, PropertyDetails::Empty());
}

void NumberDictionary::UpdateMaxNumberKey(
    uint32_t key, DirectHandle<JSObject> dictionary_holder) {
  DisallowGarbageCollection no_gc;
  // If the dictionary requires slow elements an element has already
  // been added at a high index.
  if (requires_slow_elements()) return;
  // Check if this index is high enough that we should require slow
  // elements.
  if (key > kRequiresSlowElementsLimit) {
    if (!dictionary_holder.is_null()) {
      dictionary_holder->RequireSlowElements(this);
    }
    set_requires_slow_elements();
    return;
  }
  // Update max key value.
  Tagged<Object> max_index_object = get(kMaxNumberKeyIndex);
  if (!IsSmi(max_index_object) || max_number_key() < key) {
    FixedArray::set(kMaxNumberKeyIndex,
                    Smi::FromInt(key << kRequiresSlowElementsTagSize));
  }
}

template <template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<NumberDictionary>,
                                 DirectHandle<NumberDictionary>>)
HandleType<NumberDictionary> NumberDictionary::Set(
    Isolate* isolate, HandleType<NumberDictionary> dictionary, uint32_t key,
    DirectHandle<Object> value, DirectHandle<JSObject> dictionary_holder,
    PropertyDetails details) {
  // We could call Set with empty dictionaries. UpdateMaxNumberKey doesn't
  // expect empty dictionaries so make sure to call AtPut that correctly handles
  // them by creating new dictionary when required.
  HandleType<NumberDictionary> new_dictionary =
      AtPut(isolate, dictionary, key, value, details);
  new_dictionary->UpdateMaxNumberKey(key, dictionary_holder);
  return new_dictionary;
}

template DirectHandle<NumberDictionary> NumberDictionary::Set(
    Isolate* isolate, DirectHandle<NumberDictionary> dictionary, uint32_t key,
    DirectHandle<Object> value, DirectHandle<JSObject> dictionary_holder,
    PropertyDetails details);
template IndirectHandle<NumberDictionary> NumberDictionary::Set(
    Isolate* isolate, IndirectHandle<NumberDictionary> dictionary, uint32_t key,
    DirectHandle<Object> value, DirectHandle<JSObject> dictionary_holder,
    PropertyDetails details);

// static
void NumberDictionary::UncheckedSet(Isolate* isolate,
                                    DirectHandle<NumberDictionary> dictionary,
                                    uint32_t key, DirectHandle<Object> value) {
  UncheckedAtPut(isolate, dictionary, key, value, PropertyDetails::Empty());
}

void NumberDictionary::CopyValuesTo(Tagged<FixedArray> elements) {
  ReadOnlyRoots roots = GetReadOnlyRoots();
  int pos = 0;
  DisallowGarbageCollection no_gc;
  WriteBarrierModeScope mode = elements->GetWriteBarrierMode(no_gc);
  for (InternalIndex i : this->IterateEntries()) {
    Tagged<Object> k;
    if (this->ToKey(roots, i, &k)) {
      elements->set(pos++, this->ValueAt(i), *mode);
    }
  }
  DCHECK_EQ(pos, elements->length());
}

template <typename Derived, typename Shape>
int Dictionary<Derived, Shape>::NumberOfEnumerableProperties() {
  ReadOnlyRoots roots = GetReadOnlyRoots();
  int result = 0;
  for (InternalIndex i : this->IterateEntries()) {
    Tagged<Object> k;
    if (!this->ToKey(roots, i, &k)) continue;
    if (Object::FilterKey(k, ENUMERABLE_STRINGS)) continue;
    PropertyDetails details = this->DetailsAt(i);
    PropertyAttributes attr = details.attributes();
    if ((int{attr} & ONLY_ENUMERABLE) == 0) result++;
  }
  return result;
}

template <typename Derived, typename Shape>
DirectHandle<FixedArray> BaseNameDictionary<Derived, Shape>::IterationIndices(
    Isolate* isolate, DirectHandle<Derived> dictionary) {
  DirectHandle<FixedArray> array =
      isolate->factory()->NewFixedArray(dictionary->NumberOfElements());
  ReadOnlyRoots roots(isolate);
  int array_size = 0;
  {
    DisallowGarbageCollection no_gc;
    Tagged<Derived> raw_dictionary = *dictionary;
    for (InternalIndex i : dictionary->IterateEntries()) {
      Tagged<Object> k;
      if (!raw_dictionary->ToKey(roots, i, &k)) continue;
      array->set(array_size++, Smi::FromInt(i.as_int()));
    }

    // The global dictionary doesn't track its deletion count, so we may iterate
    // fewer entries than the count of elements claimed by the dictionary.
    if (std::is_same_v<Derived, GlobalDictionary>) {
      DCHECK_LE(array_size, dictionary->NumberOfElements());
    } else {
      DCHECK_EQ(array_size, dictionary->NumberOfElements());
    }

    EnumIndexComparator<Derived> cmp(raw_dictionary);
    // Use AtomicSlot wrapper to ensure that std::sort uses atomic load and
    // store operations that are safe for concurrent marking.
    AtomicSlot start(array->RawFieldOfFirstElement());
    std::sort(start, start + array_size, cmp);
  }
  return FixedArray::RightTrimOrEmpty(isolate, array, array_size);
}

// Backwards lookup (slow).
template <typename Derived, typename Shape>
Tagged<Object> Dictionary<Derived, Shape>::SlowReverseLookup(
    Tagged<Object> value) {
  Tagged<Derived> dictionary = Cast<Derived>(this);
  ReadOnlyRoots roots = GetReadOnlyRoots();
  for (InternalIndex i : dictionary->IterateEntries()) {
    Tagged<Object> k;
    if (!dictionary->ToKey(roots, i, &k)) continue;
    Tagged<Object> e = dictionary->ValueAt(i);
    if (e == value) return k;
  }
  return roots.undefined_value();
}

template <typename Derived, typename Shape>
void ObjectHashTableBase<Derived, Shape>::FillEntriesWithHoles(
    DirectHandle<Derived> table) {
  auto roots = GetReadOnlyRoots();
  int length = table->length();
  for (int i = Derived::EntryToIndex(InternalIndex(0)); i < length; i++) {
    table->set_the_hole(roots, i);
  }
}

template <typename Derived, typename Shape>
Tagged<Object> ObjectHashTableBase<Derived, Shape>::Lookup(
    PtrComprCageBase cage_base, DirectHandle<Object> key, int32_t hash) {
  DisallowGarbageCollection no_gc;
  ReadOnlyRoots roots = GetReadOnlyRoots();
  DCHECK(this->IsKey(roots, *key));

  InternalIndex entry = this->FindEntry(cage_base, roots, key, hash);
  if (entry.is_not_found()) return roots.the_hole_value();
  return this->get(Derived::EntryToIndex(entry) + 1);
}

// The implementation should be in sync with
// CodeStubAssembler::NameToIndexHashTableLookup.
int NameToIndexHashTable::Lookup(Tagged<Name> key) {
  DisallowGarbageCollection no_gc;
  PtrComprCageBase cage_base = GetPtrComprCageBase(this);
  ReadOnlyRoots roots = GetReadOnlyRoots();

  InternalIndex entry = this->FindEntry(cage_base, roots, key, key->hash());
  if (entry.is_not_found()) return -1;
  return Cast<Smi>(this->get(EntryToValueIndex(entry))).value();
}

template <typename Derived, typename Shape>
Tagged<Object> ObjectHashTableBase<Derived, Shape>::Lookup(
    DirectHandle<Object> key) {
  DisallowGarbageCollection no_gc;

  PtrComprCageBase cage_base = GetPtrComprCageBase(this);
  ReadOnlyRoots roots = GetReadOnlyRoots();
  DCHECK(this->IsKey(roots, *key));

  // If the object does not have an identity hash, it was never used as a key.
  Tagged<Object> hash = Object::GetHash(*key);
  if (IsUndefined(hash, roots)) {
    return roots.the_hole_value();
  }
  return Lookup(cage_base, key, Smi::ToInt(hash));
}

template <typename Derived, typename Shape>
Tagged<Object> ObjectHashTableBase<Derived, Shape>::Lookup(
    DirectHandle<Object> key, int32_t hash) {
  return Lookup(GetPtrComprCageBase(this), key, hash);
}

template <typename Derived, typename Shape>
Tagged<Object> ObjectHashTableBase<Derived, Shape>::ValueAt(
    InternalIndex entry) {
  return this->get(EntryToValueIndex(entry));
}

Tagged<Object> RegisteredSymbolTable::ValueAt(InternalIndex entry) {
  return this->get(EntryToValueIndex(entry));
}

Tagged<Object> NameToIndexHashTable::ValueAt(InternalIndex entry) {
  return this->get(EntryToValueIndex(entry));
}

int NameToIndexHashTable::IndexAt(InternalIndex entry) {
  Tagged<Object> value = ValueAt(entry);
  if (IsSmi(value)) {
    int index = Smi::ToInt(value);
    DCHECK_LE(0, index);
    return index;
  }
  return -1;
}

template <typename Derived, typename Shape>
Handle<Derived> ObjectHashTableBase<Derived, Shape>::Put(
    Isolate* isolate, Handle<Derived> table, DirectHandle<Object> key,
    DirectHandle<Object> value) {
  DCHECK(table->IsKey(ReadOnlyRoots(isolate), *key));
  DCHECK(!IsTheHole(*value, ReadOnlyRoots(isolate)));

  // Make sure the key object has an identity hash code.
  int32_t hash = Object::GetOrCreateHash(*key, isolate).value();

  return ObjectHashTableBase<Derived, Shape>::Put(isolate, table, key, value,
                                                  hash);
}

namespace {

template <typename T, template <typename> typename HandleType>
void RehashObjectHashTableAndGCIfNeeded(Isolate* isolate, HandleType<T> table) {
  // Rehash if more than 33% of the entries are deleted entries.
  // TODO(verwaest): Consider to shrink the fixed array in place.
  if ((table->NumberOfDeletedElements() << 1) > table->NumberOfElements()) {
    table->Rehash(isolate);
  }
  // If we're out of luck, we didn't get a GC recently, and so rehashing
  // isn't enough to avoid a crash.
  if (!table->HasSufficientCapacityToAdd(1)) {
    uint32_t nof = table->NumberOfElements() + 1;
    uint32_t capacity = T::ComputeCapacity(nof);
    if (capacity > T::kMaxCapacity) {
      for (size_t i = 0; i < 2; ++i) {
        isolate->heap()->CollectAllGarbage(
            GCFlag::kNoFlags, GarbageCollectionReason::kFullHashtable);
      }
      table->Rehash(isolate);
    }
  }
}

}  // namespace

template <typename Derived, typename Shape>
Handle<Derived> ObjectHashTableBase<Derived, Shape>::Put(
    Isolate* isolate, Handle<Derived> table, DirectHandle<Object> key,
    DirectHandle<Object> value, int32_t hash) {
  ReadOnlyRoots roots(isolate);
  DCHECK(table->IsKey(roots, *key));
  DCHECK(!IsTheHole(*value, roots));

  InternalIndex entry = table->FindEntry(isolate, roots, key, hash);

  // Key is already in table, just overwrite value.
  if (entry.is_found()) {
    table->set(Derived::EntryToValueIndex(entry), *value);
    return table;
  }

  RehashObjectHashTableAndGCIfNeeded(isolate, table);

  // Check whether the hash table should be extended.
  table = Derived::EnsureCapacity(isolate, table);
  table->AddEntry(table->FindInsertionEntry(isolate, hash), *key, *value);
  return table;
}

template <typename Derived, typename Shape>
Handle<Derived> ObjectHashTableBase<Derived, Shape>::Remove(
    Isolate* isolate, Handle<Derived> table, DirectHandle<Object> key,
    bool* was_present) {
  DCHECK(table->IsKey(GetReadOnlyRoots(), *key));

  Tagged<Object> hash = Object::GetHash(*key);
  if (IsUndefined(hash)) {
    *was_present = false;
    return table;
  }

  return Remove(isolate, table, key, was_present, Smi::ToInt(hash));
}

template <typename Derived, typename Shape>
Handle<Derived> ObjectHashTableBase<Derived, Shape>::Remove(
    Isolate* isolate, Handle<Derived> table, DirectHandle<Object> key,
    bool* was_present, int32_t hash) {
  ReadOnlyRoots roots = GetReadOnlyRoots();
  DCHECK(table->IsKey(roots, *key));

  InternalIndex entry = table->FindEntry(isolate, roots, key, hash);
  if (entry.is_not_found()) {
    *was_present = false;
    return table;
  }

  *was_present = true;
  table->RemoveEntry(entry);
  return Derived::Shrink(isolate, table);
}

template <typename Derived, typename Shape>
void ObjectHashTableBase<Derived, Shape>::AddEntry(InternalIndex entry,
                                                   Tagged<Object> key,
                                                   Tagged<Object> value) {
  Derived* self = static_cast<Derived*>(this);
  self->set_key(Derived::EntryToIndex(entry), key);
  self->set(Derived::EntryToValueIndex(entry), value);
  self->ElementAdded();
}

template <typename Derived, typename Shape>
void ObjectHashTableBase<Derived, Shape>::RemoveEntry(InternalIndex entry) {
  auto roots = GetReadOnlyRoots();
  this->set_the_hole(roots, Derived::EntryToIndex(entry));
  this->set_the_hole(roots, Derived::EntryToValueIndex(entry));
  this->ElementRemoved();
}

template <typename Derived, int N>
std::array<Tagged<Object>, N> ObjectMultiHashTableBase<Derived, N>::Lookup(
    DirectHandle<Object> key) {
  return Lookup(GetPtrComprCageBase(this), key);
}

template <typename Derived, int N>
std::array<Tagged<Object>, N> ObjectMultiHashTableBase<Derived, N>::Lookup(
    PtrComprCageBase cage_base, DirectHandle<Object> key) {
  DisallowGarbageCollection no_gc;

  ReadOnlyRoots roots = GetReadOnlyRoots();
  DCHECK(this->IsKey(roots, *key));

  Tagged<Object> hash_obj = Object::GetHash(*key);
  if (IsUndefined(hash_obj, roots)) {
    return {roots.the_hole_value(), roots.the_hole_value()};
  }
  int32_t hash = Smi::ToInt(hash_obj);

  InternalIndex entry = this->FindEntry(cage_base, roots, key, hash);
  if (entry.is_not_found()) {
    return {roots.the_hole_value(), roots.the_hole_value()};
  }

  int start_index = this->EntryToIndex(entry) +
                    ObjectMultiHashTableShape<N>::kEntryValueIndex;
  std::array<Tagged<Object>, N> values;
  for (int i = 0; i < N; i++) {
    values[i] = this->get(start_index + i);
    DCHECK(!IsTheHole(values[i]));
  }
  return values;
}

// static
template <typename Derived, int N>
Handle<Derived> ObjectMultiHashTableBase<Derived, N>::Put(
    Isolate* isolate, Handle<Derived> table, DirectHandle<Object> key,
    const std::array<DirectHandle<Object>, N>& values) {
  ReadOnlyRoots roots(isolate);
  DCHECK(table->IsKey(roots, *key));

  int32_t hash = Object::GetOrCreateHash(*key, isolate).value();
  InternalIndex entry = table->FindEntry(isolate, roots, key, hash);

  // Overwrite values if entry is found.
  if (entry.is_found()) {
    table->SetEntryValues(entry, values);
    return table;
  }

  RehashObjectHashTableAndGCIfNeeded(isolate, table);

  // Check whether the hash table should be extended.
  table = Derived::EnsureCapacity(isolate, table);
  entry = table->FindInsertionEntry(isolate, hash);
  table->set(Derived::EntryToIndex(entry), *key);
  table->SetEntryValues(entry, values);
  return table;
}

template <typename Derived, int N>
void ObjectMultiHashTableBase<Derived, N>::SetEntryValues(
    InternalIndex entry, const std::array<DirectHandle<Object>, N>& values) {
  int start_index = EntryToValueIndexStart(entry);
  for (int i = 0; i < N; i++) {
    this->set(start_index + i, *values[i]);
  }
}

Handle<ObjectHashSet> ObjectHashSet::Add(Isolate* isolate,
                                         Handle<ObjectHashSet> set,
                                         DirectHandle<Object> key) {
  int32_t hash = Object::GetOrCreateHash(*key, isolate).value();
  if (!set->Has(isolate, key, hash)) {
    set = EnsureCapacity(isolate, set);
    InternalIndex entry = set->FindInsertionEntry(isolate, hash);
    set->set(EntryToIndex(entry), *key);
    set->ElementAdded();
  }
  return set;
}

void JSSet::Initialize(DirectHandle<JSSet> set, Isolate* isolate) {
  DirectHandle<OrderedHashSet> table = isolate->factory()->NewOrderedHashSet();
  set->set_table(*table);
}

void JSSet::Clear(Isolate* isolate, DirectHandle<JSSet> set) {
  Handle<OrderedHashSet> table(Cast<OrderedHashSet>(set->table()), isolate);
  table = OrderedHashSet::Clear(isolate, table);
  set->set_table(*table);
}

void JSSet::Rehash(Isolate* isolate) {
  Handle<OrderedHashSet> table_handle(Cast<OrderedHashSet>(table()), isolate);
  DirectHandle<OrderedHashSet> new_table =
      OrderedHashSet::Rehash(isolate, table_handle).ToHandleChecked();
  set_table(*new_table);
}

void JSMap::Initialize(DirectHandle<JSMap> map, Isolate* isolate) {
  DirectHandle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
  map->set_table(*table);
}

void JSMap::Clear(Isolate* isolate, DirectHandle<JSMap> map) {
  Handle<OrderedHashMap> table(Cast<OrderedHashMap>(map->table()), isolate);
  table = OrderedHashMap::Clear(isolate, table);
  map->set_table(*table);
}

void JSMap::Rehash(Isolate* isolate) {
  Handle<OrderedHashMap> table_handle(Cast<OrderedHashMap>(table()), isolate);
  DirectHandle<OrderedHashMap> new_table =
      OrderedHashMap::Rehash(isolate, table_handle).ToHandleChecked();
  set_table(*new_table);
}

void JSWeakCollection::Initialize(
    DirectHandle<JSWeakCollection> weak_collection, Isolate* isolate) {
  DirectHandle<EphemeronHashTable> table = EphemeronHashTable::New(isolate, 0);
  weak_collection->set_table(*table);
}

void JSWeakCollection::Set(DirectHandle<JSWeakCollection> weak_collection,
                           DirectHandle<Object> key, DirectHandle<Object> value,
                           int32_t hash) {
  DCHECK(IsJSReceiver(*key) || IsSymbol(*key));
  Handle<EphemeronHashTable> table(
      Cast<EphemeronHashTable>(weak_collection->table()), Isolate::Current());
  DCHECK(table->IsKey(GetReadOnlyRoots(), *key));
  DirectHandle<EphemeronHashTable> new_table =
      EphemeronHashTable::Put(Isolate::Current(), table, key, value, hash);
  weak_collection->set_table(*new_table);
  if (*table != *new_table) {
    // Zap the old table since we didn't record slots for its elements.
    EphemeronHashTable::FillEntriesWithHoles(table);
  }
}

bool JSWeakCollection::Delete(DirectHandle<JSWeakCollection> weak_collection,
                              DirectHandle<Object> key, int32_t hash) {
  DCHECK(IsJSReceiver(*key) || IsSymbol(*key));
  Handle<EphemeronHashTable> table(
      Cast<EphemeronHashTable>(weak_collection->table()), Isolate::Current());
  DCHECK(table->IsKey(GetReadOnlyRoots(), *key));
  bool was_present = false;
  DirectHandle<EphemeronHashTable> new_table = EphemeronHashTable::Remove(
      Isolate::Current(), table, key, &was_present, hash);
  weak_collection->set_table(*new_table);
  if (*table != *new_table) {
    // Zap the old table since we didn't record slots for its elements.
    EphemeronHashTable::FillEntriesWithHoles(table);
  }
  return was_present;
}

DirectHandle<JSArray> JSWeakCollection::GetEntries(
    DirectHandle<JSWeakCollection> holder, uint32_t max_entries) {
  Isolate* isolate = Isolate::Current();
  DirectHandle<EphemeronHashTable> table(
      Cast<EphemeronHashTable>(holder->table()), isolate);
  if (max_entries == 0 || max_entries > table->NumberOfElements()) {
    max_entries = table->NumberOfElements();
  }
  int values_per_entry = IsJSWeakMap(*holder) ? 2 : 1;
  DirectHandle<FixedArray> entries =
      isolate->factory()->NewFixedArray(max_entries * values_per_entry);
  // Recompute max_values because GC could have removed elements from the table.
  if (max_entries > table->NumberOfElements()) {
    max_entries = table->NumberOfElements();
  }

  {
    DisallowGarbageCollection no_gc;
    ReadOnlyRoots roots = ReadOnlyRoots(isolate);
    uint32_t count = 0;
    for (uint32_t i = 0;
         count / values_per_entry < max_entries && i < table->Capacity(); i++) {
      Tagged<Object> key;
      if (table->ToKey(roots, InternalIndex(i), &key)) {
        entries->set(count++, key);
        if (values_per_entry > 1) {
          Tagged<Object> value = table->Lookup(direct_handle(key, isolate));
          entries->set(count++, value);
        }
      }
    }
    DCHECK_EQ(max_entries * values_per_entry, count);
  }
  return isolate->factory()->NewJSArrayWithElements(entries);
}

void JSDisposableStackBase::InitializeJSDisposableStackBase(
    Isolate* isolate, DirectHandle<JSDisposableStackBase> disposable_stack) {
  DirectHandle<FixedArray> array = isolate->factory()->NewFixedArray(0);
  disposable_stack->set_stack(*array);
  disposable_stack->set_needs_await(false);
  disposable_stack->set_has_awaited(false);
  disposable_stack->set_suppressed_error_created(false);
  disposable_stack->set_length(0);
  disposable_stack->set_state(DisposableStackState::kPending);
  disposable_stack->set_error(*(isolate->factory()->uninitialized_value()));
  disposable_stack->set_error_message(
      *(isolate->factory()->uninitialized_value()));
}

void PropertyCell::ClearAndInvalidate(Isolate* isolate) {
  DCHECK(!IsPropertyCellHole(value(), isolate));
  PropertyDetails details = property_details();
  details = details.set_cell_type(PropertyCellType::kConstant);
  Transition(details, isolate->factory()->property_cell_hole_value());
  DependentCode::DeoptimizeDependencyGroups(
      isolate, *this, DependentCode::kPropertyCellChangedGroup);
}

// static
Handle<PropertyCell> PropertyCell::InvalidateAndReplaceEntry(
    Isolate* isolate, DirectHandle<GlobalDictionary> dictionary,
    InternalIndex entry, PropertyDetails new_details,
    DirectHandle<Object> new_value) {
  DirectHandle<PropertyCell> cell(dictionary->CellAt(entry), isolate);
  DirectHandle<Name> name(cell->name(), isolate);
  DCHECK(cell->property_details().IsConfigurable());
  DCHECK(!IsAnyHole(cell->value()));

  // Swap with a new property cell.
  Handle<PropertyCell> new_cell =
      isolate->factory()->NewPropertyCell(name, new_details, new_value);
  dictionary->ValueAtPut(entry, *new_cell);

  cell->ClearAndInvalidate(isolate);
  return new_cell;
}

static bool RemainsConstantType(Tagged<PropertyCell> cell,
                                Tagged<Object> value) {
  DisallowGarbageCollection no_gc;
  // TODO(dcarney): double->smi and smi->double transition from kConstant
  if (IsSmi(cell->value()) && IsSmi(value)) {
    return true;
  } else if (IsHeapObject(cell->value()) && IsHeapObject(value)) {
    Tagged<Map> map = Cast<HeapObject>(value)->map();
    return Cast<HeapObject>(cell->value())->map() == map && map->is_stable();
  }
  return false;
}

// static
PropertyCellType PropertyCell::InitialType(Isolate* isolate,
                                           Tagged<Object> value) {
  return IsUndefined(value, isolate) ? PropertyCellType::kUndefined
                                     : PropertyCellType::kConstant;
}

// static
PropertyCellType PropertyCell::UpdatedType(Isolate* isolate,
                                           Tagged<PropertyCell> cell,
                                           Tagged<Object> value,
                                           PropertyDetails details) {
  DisallowGarbageCollection no_gc;
  DCHECK(!IsAnyHole(value));
  DCHECK(!IsAnyHole(cell->value()));
  switch (details.cell_type()) {
    case PropertyCellType::kUndefined:
      return PropertyCellType::kConstant;
    case PropertyCellType::kConstant:
      if (value == cell->value()) return PropertyCellType::kConstant;
      [[fallthrough]];
    case PropertyCellType::kConstantType:
      if (RemainsConstantType(cell, value)) {
        return PropertyCellType::kConstantType;
      }
      [[fallthrough]];
    case PropertyCellType::kMutable:
      return PropertyCellType::kMutable;
    case PropertyCellType::kInTransition:
      UNREACHABLE();
  }
  UNREACHABLE();
}

Handle<PropertyCell> PropertyCell::PrepareForAndSetValue(
    Isolate* isolate, DirectHandle<GlobalDictionary> dictionary,
    InternalIndex entry, DirectHandle<Object> value, PropertyDetails details) {
  DCHECK(!IsAnyHole(*value));
  Tagged<PropertyCell> raw_cell = dictionary->CellAt(entry);
  CHECK(!IsAnyHole(raw_cell->value()));
  const PropertyDetails original_details = raw_cell->property_details();
  // Data accesses could be cached in ics or optimized code.
  bool invalidate = original_details.kind() == PropertyKind::kData &&
                    details.kind() == PropertyKind::kAccessor;
  int index = original_details.dictionary_index();
  DCHECK_LT(0, index);
  details = details.set_index(index);

  PropertyCellType new_type =
      UpdatedType(isolate, raw_cell, *value, original_details);
  details = details.set_cell_type(new_type);

  Handle<PropertyCell> cell(raw_cell, isolate);

  if (invalidate) {
    cell = PropertyCell::InvalidateAndReplaceEntry(isolate, dictionary, entry,
                                                   details, value);
  } else {
    cell->Transition(details, value);
    // Deopt when transitioning from a constant type or when making a writable
    // property read-only. Making a read-only property writable again is not
    // interesting because Turbofan does not currently rely on read-only unless
    // the property is also configurable, in which case it will stay read-only
    // forever.
    if (original_details.cell_type() != new_type ||
        (!original_details.IsReadOnly() && details.IsReadOnly())) {
      DependentCode::DeoptimizeDependencyGroups(
          isolate, *cell, DependentCode::kPropertyCellChangedGroup);
    }
  }
  return cell;
}

// static
void PropertyCell::InvalidateProtector(Isolate* isolate) {
  if (value() != Smi::FromInt(Protectors::kProtectorInvalid)) {
    DCHECK_EQ(value(), Smi::FromInt(Protectors::kProtectorValid));
    set_value(Smi::FromInt(Protectors::kProtectorInvalid), kReleaseStore,
              SKIP_WRITE_BARRIER);
    DependentCode::DeoptimizeDependencyGroups(
        isolate, *this, DependentCode::kPropertyCellChangedGroup);
  }
}

// static
bool PropertyCell::CheckDataIsCompatible(PropertyDetails details,
                                         Tagged<Object> value) {
  DisallowGarbageCollection no_gc;
  PropertyCellType cell_type = details.cell_type();
  CHECK_NE(cell_type, PropertyCellType::kInTransition);
  if (IsPropertyCellHole(value)) {
    CHECK_EQ(cell_type, PropertyCellType::kConstant);
  } else {
    CHECK_EQ(IsAccessorInfo(value) || IsAccessorPair(value),
             details.kind() == PropertyKind::kAccessor);
    DCHECK_IMPLIES(cell_type == PropertyCellType::kUndefined,
                   IsUndefined(value));
  }
  return true;
}

#ifdef DEBUG
bool PropertyCell::CanTransitionTo(PropertyDetails new_details,
                                   Tagged<Object> new_value) const {
  // Extending the implementation of PropertyCells with additional states
  // and/or transitions likely requires changes to PropertyCellData::Serialize.
  DisallowGarbageCollection no_gc;
  DCHECK(CheckDataIsCompatible(new_details, new_value));
  switch (property_details().cell_type()) {
    case PropertyCellType::kUndefined:
      return new_details.cell_type() != PropertyCellType::kUndefined;
    case PropertyCellType::kConstant:
      return !IsPropertyCellHole(value()) &&
             new_details.cell_type() != PropertyCellType::kUndefined;
    case PropertyCellType::kConstantType:
      return new_details.cell_type() == PropertyCellType::kConstantType ||
             new_details.cell_type() == PropertyCellType::kMutable ||
             (new_details.cell_type() == PropertyCellType::kConstant &&
              IsPropertyCellHole(new_value));
    case PropertyCellType::kMutable:
      return new_details.cell_type() == PropertyCellType::kMutable ||
             (new_details.cell_type() == PropertyCellType::kConstant &&
              IsPropertyCellHole(new_value));
    case PropertyCellType::kInTransition:
      UNREACHABLE();
  }
  UNREACHABLE();
}
#endif  // DEBUG

int JSGeneratorObject::code_offset() const {
  DCHECK(IsSmi(input_or_debug_pos()));
  int code_offset = Smi::ToInt(input_or_debug_pos());

  // The stored bytecode offset is relative to a different base than what
  // is used in the source position table, hence the subtraction.
  code_offset -= BytecodeArray::kHeaderSize - kHeapObjectTag;
  return code_offset;
}

int JSGeneratorObject::source_position() const {
  CHECK(is_suspended());
  DCHECK(function()->shared()->HasBytecodeArray());
  Isolate* isolate = Isolate::Current();
  DCHECK(function()
             ->shared()
             ->GetBytecodeArray(isolate)
             ->HasSourcePositionTable());
  Tagged<BytecodeArray> bytecode =
      function()->shared()->GetBytecodeArray(isolate);
  return bytecode->SourcePosition(code_offset());
}

// static
Tagged<AccessCheckInfo> AccessCheckInfo::Get(Isolate* isolate,
                                             DirectHandle<JSObject> receiver) {
  DisallowGarbageCollection no_gc;
  DCHECK(receiver->map()->is_access_check_needed());
  Tagged<Object> maybe_constructor = receiver->map()->GetConstructor();
  if (IsFunctionTemplateInfo(maybe_constructor)) {
    Tagged<Object> data_obj =
        Cast<FunctionTemplateInfo>(maybe_constructor)->GetAccessCheckInfo();
    if (IsUndefined(data_obj, isolate)) return {};
    return Cast<AccessCheckInfo>(data_obj);
  }
  // Might happen for a detached context.
  if (!IsJSFunction(maybe_constructor)) return {};
  Tagged<JSFunction> constructor = Cast<JSFunction>(maybe_constructor);
  // Might happen for the debug context.
  if (!constructor->shared()->IsApiFunction()) return {};

  Tagged<Object> data_obj =
      constructor->shared()->api_func_data()->GetAccessCheckInfo();
  if (IsUndefined(data_obj, isolate)) return {};

  return Cast<AccessCheckInfo>(data_obj);
}

Address Smi::LexicographicCompare(Isolate* isolate, Tagged<Smi> x,
                                  Tagged<Smi> y) {
  DisallowGarbageCollection no_gc;
  DisallowJavascriptExecution no_js(isolate);

  int x_value = Smi::ToInt(x);
  int y_value = Smi::ToInt(y);

  // If the integers are equal so are the string representations.
  if (x_value == y_value) return Smi::FromInt(0).ptr();

  // If one of the integers is zero the normal integer order is the
  // same as the lexicographic order of the string representations.
  if (x_value == 0 || y_value == 0) {
    return Smi::FromInt(x_value < y_value ? -1 : 1).ptr();
  }

  // If only one of the integers is negative the negative number is
  // smallest because the char code of '-' is less than the char code
  // of any digit.  Otherwise, we make both values positive.

  // Use unsigned values otherwise the logic is incorrect for -MIN_INT on
  // architectures using 32-bit Smis.
  uint32_t x_scaled = x_value;
  uint32_t y_scaled = y_value;
  if (x_value < 0) {
    if (y_value >= 0) {
      return Smi::FromInt(-1).ptr();
    } else {
      y_scaled = base::NegateWithWraparound(y_value);
    }
    x_scaled = base::NegateWithWraparound(x_value);
  } else if (y_value < 0) {
    return Smi::FromInt(1).ptr();
  }

  // clang-format off
  static const uint32_t kPowersOf10[] = {
      1,                 10,                100,         1000,
      10 * 1000,         100 * 1000,        1000 * 1000, 10 * 1000 * 1000,
      100 * 1000 * 1000, 1000 * 1000 * 1000};
  // clang-format on

  // If the integers have the same number of decimal digits they can be
  // compared directly as the numeric order is the same as the
  // lexicographic order.  If one integer has fewer digits, it is scaled
  // by some power of 10 to have the same number of digits as the longer
  // integer.  If the scaled integers are equal it means the shorter
  // integer comes first in the lexicographic order.

  // From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
  int x_log2 = 31 - base::bits::CountLeadingZeros(x_scaled);
  int x_log10 = ((x_log2 + 1) * 1233) >> 12;
  x_log10 -= x_scaled < kPowersOf10[x_log10];

  int y_log2 = 31 - base::bits::CountLeadingZeros(y_scaled);
  int y_log10 = ((y_log2 + 1) * 1233) >> 12;
  y_log10 -= y_scaled < kPowersOf10[y_log10];

  int tie = 0;

  if (x_log10 < y_log10) {
    // X has fewer digits.  We would like to simply scale up X but that
    // might overflow, e.g when comparing 9 with 1_000_000_000, 9 would
    // be scaled up to 9_000_000_000. So we scale up by the next
    // smallest power and scale down Y to drop one digit. It is OK to
    // drop one digit from the longer integer since the final digit is
    // past the length of the shorter integer.
    x_scaled *= kPowersOf10[y_log10 - x_log10 - 1];
    y_scaled /= 10;
    tie = -1;
  } else if (y_log10 < x_log10) {
    y_scaled *= kPowersOf10[x_log10 - y_log10 - 1];
    x_scaled /= 10;
    tie = 1;
  }

  if (x_scaled < y_scaled) return Smi::FromInt(-1).ptr();
  if (x_scaled > y_scaled) return Smi::FromInt(1).ptr();
  return Smi::FromInt(tie).ptr();
}

// static
bool MapWord::IsMapOrForwarded(Tagged<Map> map) {
  MapWord map_word = map->map_word(kRelaxedLoad);
  if (map_word.IsForwardingAddress()) {
    // During GC we can't access forwarded maps without synchronization.
    return true;
  }
  // The meta map might be moved away by GC too but we can read instance
  // type from both old and new location as it can't change.
  return InstanceTypeChecker::IsMap(map_word.ToMap()->instance_type());
}

// Force instantiation of template instances class.
// Please note this list is compiler dependent.
// Keep this at the end of this file

#define EXTERN_DEFINE_HASH_TABLE(DERIVED, SHAPE)                             \
  template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)                   \
      HashTable<DERIVED, SHAPE>;                                             \
                                                                             \
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) MaybeHandle<DERIVED>    \
  HashTable<DERIVED, SHAPE>::TryNew(Isolate*, uint32_t, AllocationType,      \
                                    MinimumCapacity);                        \
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) MaybeHandle<DERIVED>    \
  HashTable<DERIVED, SHAPE>::TryNew(LocalIsolate*, uint32_t, AllocationType, \
                                    MinimumCapacity);                        \
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED>         \
  HashTable<DERIVED, SHAPE>::New(Isolate*, uint32_t, AllocationType,         \
                                 MinimumCapacity);                           \
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED>         \
  HashTable<DERIVED, SHAPE>::New(LocalIsolate*, uint32_t, AllocationType,    \
                                 MinimumCapacity);                           \
                                                                             \
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED>         \
  HashTable<DERIVED, SHAPE>::EnsureCapacity(Isolate*, Handle<DERIVED>, int,  \
                                            AllocationType);                 \
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED>         \
  HashTable<DERIVED, SHAPE>::EnsureCapacity(LocalIsolate*, Handle<DERIVED>,  \
                                            int, AllocationType);            \
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) DirectHandle<DERIVED>   \
  HashTable<DERIVED, SHAPE>::EnsureCapacity(Isolate*, DirectHandle<DERIVED>, \
                                            int, AllocationType);            \
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) DirectHandle<DERIVED>   \
  HashTable<DERIVED, SHAPE>::EnsureCapacity(                                 \
      LocalIsolate*, DirectHandle<DERIVED>, int, AllocationType);            \
                                                                             \
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED>         \
  HashTable<DERIVED, SHAPE>::Shrink(Isolate*, Handle<DERIVED>, int);         \
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) DirectHandle<DERIVED>   \
  HashTable<DERIVED, SHAPE>::Shrink(Isolate*, DirectHandle<DERIVED>, int);

#define EXTERN_DEFINE_OBJECT_BASE_HASH_TABLE(DERIVED, SHAPE) \
  EXTERN_DEFINE_HASH_TABLE(DERIVED, SHAPE)                   \
  template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)   \
      ObjectHashTableBase<DERIVED, SHAPE>;

#define EXTERN_DEFINE_MULTI_OBJECT_BASE_HASH_TABLE(DERIVED, N)    \
  EXTERN_DEFINE_HASH_TABLE(DERIVED, ObjectMultiHashTableShape<N>) \
  template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)        \
      ObjectMultiHashTableBase<DERIVED, N>;

#define EXTERN_DEFINE_DICTIONARY(DERIVED, SHAPE)                               \
  EXTERN_DEFINE_HASH_TABLE(DERIVED, SHAPE)                                     \
  template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)                     \
      Dictionary<DERIVED, SHAPE>;                                              \
                                                                               \
  template V8_EXPORT_PRIVATE DirectHandle<DERIVED>                             \
  Dictionary<DERIVED, SHAPE>::Add(Isolate* isolate, DirectHandle<DERIVED>,     \
                                  Key, DirectHandle<Object>, PropertyDetails,  \
                                  InternalIndex*);                             \
  template V8_EXPORT_PRIVATE DirectHandle<DERIVED>                             \
  Dictionary<DERIVED, SHAPE>::Add(                                             \
      LocalIsolate* isolate, DirectHandle<DERIVED>, Key, DirectHandle<Object>, \
      PropertyDetails, InternalIndex*);                                        \
  template V8_EXPORT_PRIVATE IndirectHandle<DERIVED>                           \
  Dictionary<DERIVED, SHAPE>::Add(Isolate* isolate, IndirectHandle<DERIVED>,   \
                                  Key, DirectHandle<Object>, PropertyDetails,  \
                                  InternalIndex*);                             \
  template V8_EXPORT_PRIVATE IndirectHandle<DERIVED>                           \
  Dictionary<DERIVED, SHAPE>::Add(                                             \
      LocalIsolate* isolate, IndirectHandle<DERIVED>, Key,                     \
      DirectHandle<Object>, PropertyDetails, InternalIndex*);                  \
  template V8_EXPORT_PRIVATE DirectHandle<DERIVED>                             \
  Dictionary<DERIVED, SHAPE>::DeleteEntry(                                     \
      Isolate* isolate, DirectHandle<DERIVED>, InternalIndex);                 \
  template V8_EXPORT_PRIVATE IndirectHandle<DERIVED>                           \
  Dictionary<DERIVED, SHAPE>::DeleteEntry(                                     \
      Isolate* isolate, IndirectHandle<DERIVED>, InternalIndex);               \
  template V8_EXPORT_PRIVATE DirectHandle<DERIVED>                             \
  Dictionary<DERIVED, SHAPE>::Shrink(Isolate* isolate, DirectHandle<DERIVED>); \
  template V8_EXPORT_PRIVATE IndirectHandle<DERIVED>                           \
  Dictionary<DERIVED, SHAPE>::Shrink(Isolate* isolate,                         \
                                     IndirectHandle<DERIVED>);

#define EXTERN_DEFINE_BASE_NAME_DICTIONARY(DERIVED, SHAPE)                     \
  EXTERN_DEFINE_DICTIONARY(DERIVED, SHAPE)                                     \
  template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)                     \
      BaseNameDictionary<DERIVED, SHAPE>;                                      \
                                                                               \
  template V8_EXPORT_PRIVATE Handle<DERIVED>                                   \
  BaseNameDictionary<DERIVED, SHAPE>::New(Isolate*, int, AllocationType,       \
                                          MinimumCapacity);                    \
  template V8_EXPORT_PRIVATE Handle<DERIVED>                                   \
  BaseNameDictionary<DERIVED, SHAPE>::New(LocalIsolate*, int, AllocationType,  \
                                          MinimumCapacity);                    \
                                                                               \
  template V8_EXPORT_PRIVATE MaybeDirectHandle<DERIVED>                        \
  BaseNameDictionary<DERIVED, SHAPE>::Add(                                     \
      Isolate* isolate, DirectHandle<DERIVED>, Key, DirectHandle<Object>,      \
      PropertyDetails, InternalIndex*);                                        \
  template V8_EXPORT_PRIVATE MaybeIndirectHandle<DERIVED>                      \
  BaseNameDictionary<DERIVED, SHAPE>::Add(                                     \
      Isolate* isolate, IndirectHandle<DERIVED>, Key, DirectHandle<Object>,    \
      PropertyDetails, InternalIndex*);                                        \
                                                                               \
  template DirectHandle<DERIVED>                                               \
  BaseNameDictionary<DERIVED, SHAPE>::AddNoUpdateNextEnumerationIndex(         \
      Isolate* isolate, DirectHandle<DERIVED>, Key, DirectHandle<Object>,      \
      PropertyDetails, InternalIndex*);                                        \
  template DirectHandle<DERIVED>                                               \
  BaseNameDictionary<DERIVED, SHAPE>::AddNoUpdateNextEnumerationIndex(         \
      LocalIsolate* isolate, DirectHandle<DERIVED>, Key, DirectHandle<Object>, \
      PropertyDetails, InternalIndex*);                                        \
  template IndirectHandle<DERIVED>                                             \
  BaseNameDictionary<DERIVED, SHAPE>::AddNoUpdateNextEnumerationIndex(         \
      Isolate* isolate, IndirectHandle<DERIVED>, Key, DirectHandle<Object>,    \
      PropertyDetails, InternalIndex*);                                        \
  template IndirectHandle<DERIVED>                                             \
  BaseNameDictionary<DERIVED, SHAPE>::AddNoUpdateNextEnumerationIndex(         \
      LocalIsolate* isolate, IndirectHandle<DERIVED>, Key,                     \
      DirectHandle<Object>, PropertyDetails, InternalIndex*);

EXTERN_DEFINE_HASH_TABLE(StringSet, StringSetShape)
EXTERN_DEFINE_HASH_TABLE(CompilationCacheTable, CompilationCacheShape)
EXTERN_DEFINE_HASH_TABLE(ObjectHashSet, ObjectHashSetShape)
EXTERN_DEFINE_HASH_TABLE(NameToIndexHashTable, NameToIndexShape)
EXTERN_DEFINE_HASH_TABLE(RegisteredSymbolTable, RegisteredSymbolTableShape)

EXTERN_DEFINE_OBJECT_BASE_HASH_TABLE(ObjectHashTable, ObjectHashTableShape)
EXTERN_DEFINE_OBJECT_BASE_HASH_TABLE(EphemeronHashTable,
                                     EphemeronHashTableShape)

EXTERN_DEFINE_MULTI_OBJECT_BASE_HASH_TABLE(ObjectTwoHashTable, 2)

EXTERN_DEFINE_DICTIONARY(SimpleNumberDictionary, SimpleNumberDictionaryShape)
EXTERN_DEFINE_DICTIONARY(NumberDictionary, NumberDictionaryShape)
EXTERN_DEFINE_DICTIONARY(SimpleNameDictionary, SimpleNameDictionaryShape)

template V8_EXPORT_PRIVATE void
Dictionary<NumberDictionary, NumberDictionaryShape>::UncheckedAdd<
    Isolate, Handle, AllocationType::kSharedOld>(Isolate*,
                                                 Handle<NumberDictionary>,
                                                 uint32_t, DirectHandle<Object>,
                                                 PropertyDetails);
template V8_EXPORT_PRIVATE void
Dictionary<NumberDictionary, NumberDictionaryShape>::UncheckedAdd<
    Isolate, DirectHandle, AllocationType::kSharedOld>(
    Isolate*, DirectHandle<NumberDictionary>, uint32_t, DirectHandle<Object>,
    PropertyDetails);

EXTERN_DEFINE_BASE_NAME_DICTIONARY(NameDictionary, NameDictionaryShape)
template V8_EXPORT_PRIVATE Handle<NameDictionary> NameDictionary::New(
    Isolate*, int, AllocationType, MinimumCapacity);
template V8_EXPORT_PRIVATE Handle<NameDictionary> NameDictionary::New(
    LocalIsolate*, int, AllocationType, MinimumCapacity);

EXTERN_DEFINE_BASE_NAME_DICTIONARY(GlobalDictionary, GlobalDictionaryShape)

#undef EXTERN_DEFINE_HASH_TABLE
#undef EXTERN_DEFINE_OBJECT_BASE_HASH_TABLE
#undef EXTERN_DEFINE_DICTIONARY
#undef EXTERN_DEFINE_BASE_NAME_DICTIONARY

}  // namespace v8::internal