text: // 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 <memory>
  #include <sstream>
  #include <vector>
  
  #include "src/api/api-arguments-inl.h"
  #include "src/api/api-natives.h"
  #include "src/api/api.h"
  #include "src/ast/ast.h"
  #include "src/ast/scopes.h"
  #include "src/base/bits.h"
  #include "src/base/debug/stack_trace.h"
  #include "src/base/overflowing-math.h"
  #include "src/base/utils/random-number-generator.h"
  #include "src/builtins/accessors.h"
  #include "src/builtins/builtins.h"
  #include "src/codegen/compiler.h"
  #include "src/common/globals.h"
  #include "src/common/message-template.h"
  #include "src/date/date.h"
  #include "src/debug/debug.h"
  #include "src/diagnostics/code-tracer.h"
  #include "src/execution/arguments.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/ic/ic.h"
  #include "src/init/bootstrapper.h"
  #include "src/logging/counters-inl.h"
  #include "src/logging/counters.h"
  #include "src/logging/log.h"
  #include "src/objects/allocation-site-inl.h"
  #include "src/objects/allocation-site-scopes.h"
  #include "src/objects/api-callbacks.h"
  #include "src/objects/arguments-inl.h"
  #include "src/objects/bigint.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/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/foreign.h"
  #include "src/objects/free-space-inl.h"
  #include "src/objects/function-kind.h"
  #include "src/objects/hash-table-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/keys.h"
  #include "src/objects/lookup-inl.h"
  #include "src/objects/map-updater.h"
  #include "src/objects/objects-body-descriptors-inl.h"
  #include "src/objects/objects-inl.h"
  #include "src/objects/property-details.h"
  #include "src/roots/roots.h"
  #include "src/snapshot/deserializer.h"
  #include "src/utils/identity-map.h"
  #ifdef V8_INTL_SUPPORT
  #include "src/objects/js-break-iterator.h"
  #include "src/objects/js-collator.h"
  #endif  // V8_INTL_SUPPORT
  #include "src/objects/js-collection-inl.h"
  #ifdef V8_INTL_SUPPORT
  #include "src/objects/js-date-time-format.h"
  #endif  // V8_INTL_SUPPORT
  #include "src/objects/js-generator-inl.h"
  #ifdef V8_INTL_SUPPORT
  #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"
  #endif  // V8_INTL_SUPPORT
  #include "src/objects/js-regexp-inl.h"
  #include "src/objects/js-regexp-string-iterator.h"
  #ifdef V8_INTL_SUPPORT
  #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
  #include "src/codegen/source-position-table.h"
  #include "src/objects/js-weak-refs-inl.h"
  #include "src/objects/literal-objects-inl.h"
  #include "src/objects/map-inl.h"
  #include "src/objects/map.h"
  #include "src/objects/microtask-inl.h"
  #include "src/objects/module-inl.h"
  #include "src/objects/promise-inl.h"
  #include "src/objects/property-descriptor-object-inl.h"
  #include "src/objects/property-descriptor.h"
  #include "src/objects/prototype.h"
  #include "src/objects/slots-atomic-inl.h"
  #include "src/objects/stack-frame-info-inl.h"
  #include "src/objects/string-comparator.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/strings/string-builder-inl.h"
  #include "src/strings/string-search.h"
  #include "src/strings/string-stream.h"
  #include "src/strings/unicode-decoder.h"
  #include "src/strings/unicode-inl.h"
  #include "src/utils/ostreams.h"
  #include "src/utils/utils-inl.h"
  #include "src/zone/zone.h"
  
  #if V8_ENABLE_WEBASSEMBLY
  #include "src/wasm/wasm-objects.h"
  #endif  // V8_ENABLE_WEBASSEMBLY
  
  namespace v8 {
  namespace 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); !it.done(); it.Advance()) {
      if (!(it.frame()->is_optimized() || it.frame()->is_unoptimized())) {
        continue;
      }
      // Get the language mode from closure.
      JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(it.frame());
      std::vector<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::ostream& operator<<(std::ostream& os, InstanceType instance_type) {
    switch (instance_type) {
  #define WRITE_TYPE(TYPE) \
    case TYPE:             \
      return os << #TYPE;
      INSTANCE_TYPE_LIST(WRITE_TYPE)
  #undef WRITE_TYPE
    }
    UNREACHABLE();
  }
  
  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";
    }
    UNREACHABLE();
  }
  
  Handle<FieldType> Object::OptimalType(Isolate* isolate,
                                        Representation representation) {
    if (representation.IsNone()) return FieldType::None(isolate);
    if (FLAG_track_field_types) {
      if (representation.IsHeapObject() && IsHeapObject()) {
        // We can track only JavaScript objects with stable maps.
        Handle<Map> map(HeapObject::cast(*this).map(), isolate);
        if (map->is_stable() && map->IsJSReceiverMap()) {
          return FieldType::Class(map, isolate);
        }
      }
    }
    return FieldType::Any(isolate);
  }
  
  Handle<Object> Object::NewStorageFor(Isolate* isolate, Handle<Object> object,
                                       Representation representation) {
    if (!representation.IsDouble()) return object;
    auto result = isolate->factory()->NewHeapNumberWithHoleNaN();
    if (object->IsUninitialized(isolate)) {
      result->set_value_as_bits(kHoleNanInt64);
    } else if (object->IsHeapNumber()) {
      // Ensure that all bits of the double value are preserved.
      result->set_value_as_bits(HeapNumber::cast(*object).value_as_bits());
    } else {
      result->set_value(object->Number());
    }
    return result;
  }
  
  Handle<Object> Object::WrapForRead(Isolate* isolate, Handle<Object> object,
                                     Representation representation) {
    DCHECK(!object->IsUninitialized(isolate));
    if (!representation.IsDouble()) {
      DCHECK(object->FitsRepresentation(representation));
      return object;
    }
    return isolate->factory()->NewHeapNumberFromBits(
        HeapNumber::cast(*object).value_as_bits());
  }
  
  MaybeHandle<JSReceiver> Object::ToObjectImpl(Isolate* isolate,
                                               Handle<Object> object,
                                               const char* method_name) {
    DCHECK(!object->IsJSReceiver());  // Use ToObject() for fast path.
    Handle<Context> native_context = isolate->native_context();
    Handle<JSFunction> constructor;
    if (object->IsSmi()) {
      constructor = handle(native_context->number_function(), isolate);
    } else {
      int constructor_function_index =
          Handle<HeapObject>::cast(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)),
              JSReceiver);
        }
        THROW_NEW_ERROR(isolate,
                        NewTypeError(MessageTemplate::kUndefinedOrNullToObject),
                        JSReceiver);
      }
      constructor = handle(
          JSFunction::cast(native_context->get(constructor_function_index)),
          isolate);
    }
    Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
    Handle<JSPrimitiveWrapper>::cast(result)->set_value(*object);
    return result;
  }
  
  // ES6 section 9.2.1.2, OrdinaryCallBindThis for sloppy callee.
  // static
  MaybeHandle<JSReceiver> Object::ConvertReceiver(Isolate* isolate,
                                                  Handle<Object> object) {
    if (object->IsJSReceiver()) return Handle<JSReceiver>::cast(object);
    if (object->IsNullOrUndefined(isolate)) {
      return isolate->global_proxy();
    }
    return Object::ToObject(isolate, object);
  }
  
  // static
  MaybeHandle<Object> Object::ConvertToNumberOrNumeric(Isolate* isolate,
                                                       Handle<Object> input,
                                                       Conversion mode) {
    while (true) {
      if (input->IsNumber()) {
        return input;
      }
      if (input->IsString()) {
        return String::ToNumber(isolate, Handle<String>::cast(input));
      }
      if (input->IsOddball()) {
        return Oddball::ToNumber(isolate, Handle<Oddball>::cast(input));
      }
      if (input->IsSymbol()) {
        THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber),
                        Object);
      }
      if (input->IsBigInt()) {
        if (mode == Conversion::kToNumeric) return input;
        DCHECK_EQ(mode, Conversion::kToNumber);
        THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kBigIntToNumber),
                        Object);
      }
      ASSIGN_RETURN_ON_EXCEPTION(
          isolate, input,
          JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input),
                                  ToPrimitiveHint::kNumber),
          Object);
    }
  }
  
  // static
  MaybeHandle<Object> Object::ConvertToInteger(Isolate* isolate,
                                               Handle<Object> input) {
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, input,
        ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object);
    if (input->IsSmi()) return input;
    return isolate->factory()->NewNumber(DoubleToInteger(input->Number()));
  }
  
  // static
  MaybeHandle<Object> Object::ConvertToInt32(Isolate* isolate,
                                             Handle<Object> input) {
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, input,
        ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object);
    if (input->IsSmi()) return input;
    return isolate->factory()->NewNumberFromInt(DoubleToInt32(input->Number()));
  }
  
  // static
  MaybeHandle<Object> Object::ConvertToUint32(Isolate* isolate,
                                              Handle<Object> input) {
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, input,
        ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object);
    if (input->IsSmi()) return handle(Smi::cast(*input).ToUint32Smi(), isolate);
    return isolate->factory()->NewNumberFromUint(DoubleToUint32(input->Number()));
  }
  
  // static
  MaybeHandle<Name> Object::ConvertToName(Isolate* isolate,
                                          Handle<Object> input) {
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, input, Object::ToPrimitive(input, ToPrimitiveHint::kString),
        Name);
    if (input->IsName()) return Handle<Name>::cast(input);
    return ToString(isolate, input);
  }
  
  // ES6 7.1.14
  // static
  MaybeHandle<Object> Object::ConvertToPropertyKey(Isolate* isolate,
                                                   Handle<Object> value) {
    // 1. Let key be ToPrimitive(argument, hint String).
    MaybeHandle<Object> maybe_key =
        Object::ToPrimitive(value, ToPrimitiveHint::kString);
    // 2. ReturnIfAbrupt(key).
    Handle<Object> key;
    if (!maybe_key.ToHandle(&key)) return key;
    // 3. If Type(key) is Symbol, then return key.
    if (key->IsSymbol()) return key;
    // 4. Return ToString(key).
    // Extending spec'ed behavior, we'd be happy to return an element index.
    if (key->IsSmi()) return key;
    if (key->IsHeapNumber()) {
      uint32_t uint_value;
      if (value->ToArrayLength(&uint_value) &&
          uint_value <= static_cast<uint32_t>(Smi::kMaxValue)) {
        return handle(Smi::FromInt(static_cast<int>(uint_value)), isolate);
      }
    }
    return Object::ToString(isolate, key);
  }
  
  // static
  MaybeHandle<String> Object::ConvertToString(Isolate* isolate,
                                              Handle<Object> input) {
    while (true) {
      if (input->IsOddball()) {
        return handle(Handle<Oddball>::cast(input)->to_string(), isolate);
      }
      if (input->IsNumber()) {
        return isolate->factory()->NumberToString(input);
      }
      if (input->IsSymbol()) {
        THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToString),
                        String);
      }
      if (input->IsBigInt()) {
        return BigInt::ToString(isolate, Handle<BigInt>::cast(input));
      }
      ASSIGN_RETURN_ON_EXCEPTION(
          isolate, input,
          JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input),
                                  ToPrimitiveHint::kString),
          String);
      // The previous isString() check happened in Object::ToString and thus we
      // put it at the end of the loop in this helper.
      if (input->IsString()) {
        return Handle<String>::cast(input);
      }
    }
  }
  
  namespace {
  
  bool IsErrorObject(Isolate* isolate, Handle<Object> object) {
    if (!object->IsJSReceiver()) return false;
    Handle<Symbol> symbol = isolate->factory()->stack_trace_symbol();
    return JSReceiver::HasOwnProperty(Handle<JSReceiver>::cast(object), symbol)
        .FromMaybe(false);
  }
  
  Handle<String> AsStringOrEmpty(Isolate* isolate, Handle<Object> object) {
    return object->IsString() ? Handle<String>::cast(object)
                              : isolate->factory()->empty_string();
  }
  
  Handle<String> NoSideEffectsErrorToString(Isolate* isolate,
                                            Handle<JSReceiver> error) {
    Handle<Name> name_key = isolate->factory()->name_string();
    Handle<Object> name = JSReceiver::GetDataProperty(error, name_key);
    Handle<String> name_str = AsStringOrEmpty(isolate, name);
  
    Handle<Name> msg_key = isolate->factory()->message_string();
    Handle<Object> msg = JSReceiver::GetDataProperty(error, msg_key);
    Handle<String> msg_str = AsStringOrEmpty(isolate, msg);
  
    if (name_str->length() == 0) return msg_str;
    if (msg_str->length() == 0) return name_str;
  
    IncrementalStringBuilder builder(isolate);
    builder.AppendString(name_str);
    builder.AppendCString(": ");
  
    if (builder.Length() + msg_str->length() <= String::kMaxLength) {
      builder.AppendString(msg_str);
    } else {
      builder.AppendCString("<a very large string>");
    }
  
    return builder.Finish().ToHandleChecked();
  }
  
  }  // namespace
  
  // static
  Handle<String> Object::NoSideEffectsToString(Isolate* isolate,
                                               Handle<Object> input) {
    DisallowJavascriptExecution no_js(isolate);
  
    if (input->IsString() || input->IsNumber() || input->IsOddball()) {
      return Object::ToString(isolate, input).ToHandleChecked();
    } else if (input->IsJSProxy()) {
      Handle<Object> currInput = input;
      do {
        HeapObject target = Handle<JSProxy>::cast(currInput)->target(isolate);
        currInput = Handle<Object>(target, isolate);
      } while (currInput->IsJSProxy());
      return NoSideEffectsToString(isolate, currInput);
    } else if (input->IsBigInt()) {
      MaybeHandle<String> maybe_string =
          BigInt::ToString(isolate, Handle<BigInt>::cast(input), 10, kDontThrow);
      Handle<String> result;
      if (maybe_string.ToHandle(&result)) return result;
      // BigInt-to-String conversion can fail on 32-bit platforms where
      // String::kMaxLength is too small to fit this BigInt.
      return isolate->factory()->NewStringFromStaticChars(
          "<a very large BigInt>");
    } else if (input->IsFunction()) {
      // -- F u n c t i o n
      Handle<String> fun_str;
      if (input->IsJSBoundFunction()) {
        fun_str = JSBoundFunction::ToString(Handle<JSBoundFunction>::cast(input));
      } else {
        DCHECK(input->IsJSFunction());
        fun_str = JSFunction::ToString(Handle<JSFunction>::cast(input));
      }
  
      if (fun_str->length() > 128) {
        IncrementalStringBuilder builder(isolate);
        builder.AppendString(isolate->factory()->NewSubString(fun_str, 0, 111));
        builder.AppendCString("...<omitted>...");
        builder.AppendString(isolate->factory()->NewSubString(
            fun_str, fun_str->length() - 2, fun_str->length()));
  
        return builder.Finish().ToHandleChecked();
      }
      return fun_str;
    } else if (input->IsSymbol()) {
      // -- S y m b o l
      Handle<Symbol> symbol = Handle<Symbol>::cast(input);
  
      if (symbol->is_private_name()) {
        return Handle<String>(String::cast(symbol->description()), isolate);
      }
  
      IncrementalStringBuilder builder(isolate);
      builder.AppendCString("Symbol(");
      if (symbol->description().IsString()) {
        builder.AppendString(
            handle(String::cast(symbol->description()), isolate));
      }
      builder.AppendCharacter(')');
  
      return builder.Finish().ToHandleChecked();
    } else if (input->IsJSReceiver()) {
      // -- J S R e c e i v e r
      Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(input);
      Handle<Object> to_string = JSReceiver::GetDataProperty(
          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,
                                          Handle<JSReceiver>::cast(input));
      } else if (*to_string == *isolate->object_to_string()) {
        Handle<Object> ctor = JSReceiver::GetDataProperty(
            receiver, isolate->factory()->constructor_string());
        if (ctor->IsFunction()) {
          Handle<String> ctor_name;
          if (ctor->IsJSBoundFunction()) {
            ctor_name = JSBoundFunction::GetName(
                            isolate, Handle<JSBoundFunction>::cast(ctor))
                            .ToHandleChecked();
          } else if (ctor->IsJSFunction()) {
            Handle<Object> ctor_name_obj =
                JSFunction::GetName(isolate, Handle<JSFunction>::cast(ctor));
            ctor_name = AsStringOrEmpty(isolate, ctor_name_obj);
          }
  
          if (ctor_name->length() != 0) {
            IncrementalStringBuilder builder(isolate);
            builder.AppendCString("#<");
            builder.AppendString(ctor_name);
            builder.AppendCString(">");
  
            return builder.Finish().ToHandleChecked();
          }
        }
      }
    }
  
    // At this point, input is either none of the above or a JSReceiver.
  
    Handle<JSReceiver> receiver;
    if (input->IsJSReceiver()) {
      receiver = Handle<JSReceiver>::cast(input);
    } else {
      // This is the only case where Object::ToObject throws.
      DCHECK(!input->IsSmi());
      int constructor_function_index =
          Handle<HeapObject>::cast(input)->map().GetConstructorFunctionIndex();
      if (constructor_function_index == Map::kNoConstructorFunctionIndex) {
        return isolate->factory()->NewStringFromAsciiChecked("[object Unknown]");
      }
  
      receiver = Object::ToObjectImpl(isolate, input).ToHandleChecked();
    }
  
    Handle<String> builtin_tag = handle(receiver->class_name(), isolate);
    Handle<Object> tag_obj = JSReceiver::GetDataProperty(
        receiver, isolate->factory()->to_string_tag_symbol());
    Handle<String> tag =
        tag_obj->IsString() ? Handle<String>::cast(tag_obj) : builtin_tag;
  
    IncrementalStringBuilder builder(isolate);
    builder.AppendCString("[object ");
    builder.AppendString(tag);
    builder.AppendCString("]");
  
    return builder.Finish().ToHandleChecked();
  }
  
  // static
  MaybeHandle<Object> Object::ConvertToLength(Isolate* isolate,
                                              Handle<Object> input) {
    ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input), Object);
    if (input->IsSmi()) {
      int value = std::max(Smi::ToInt(*input), 0);
      return handle(Smi::FromInt(value), isolate);
    }
    double len = DoubleToInteger(input->Number());
    if (len <= 0.0) {
      return handle(Smi::zero(), isolate);
    } else if (len >= kMaxSafeInteger) {
      len = kMaxSafeInteger;
    }
    return isolate->factory()->NewNumber(len);
  }
  
  // static
  MaybeHandle<Object> Object::ConvertToIndex(Isolate* isolate,
                                             Handle<Object> input,
                                             MessageTemplate error_index) {
    if (input->IsUndefined(isolate)) return handle(Smi::zero(), isolate);
    ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input), Object);
    if (input->IsSmi() && Smi::ToInt(*input) >= 0) return input;
    double len = DoubleToInteger(input->Number());
    auto js_len = isolate->factory()->NewNumber(len);
    if (len < 0.0 || len > kMaxSafeInteger) {
      THROW_NEW_ERROR(isolate, NewRangeError(error_index, js_len), Object);
    }
    return js_len;
  }
  
  bool Object::BooleanValue(Isolate* isolate) {
    if (IsSmi()) return Smi::ToInt(*this) != 0;
    DCHECK(IsHeapObject());
    if (IsBoolean()) return IsTrue(isolate);
    if (IsNullOrUndefined(isolate)) return false;
    if (IsUndetectable()) return false;  // Undetectable object is false.
    if (IsString()) return String::cast(*this).length() != 0;
    if (IsHeapNumber()) return DoubleToBoolean(HeapNumber::cast(*this).value());
    if (IsBigInt()) return BigInt::cast(*this).ToBoolean();
    return true;
  }
  
  Object Object::ToBoolean(Isolate* isolate) {
    if (IsBoolean()) return *this;
    return isolate->heap()->ToBoolean(BooleanValue(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 Object x, const Object y) {
    return StrictNumberEquals(x.Number(), y.Number());
  }
  
  bool StrictNumberEquals(Handle<Object> x, Handle<Object> 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, Handle<Object> x,
                                          Handle<Object> y) {
    // ES6 section 7.2.11 Abstract Relational Comparison step 3 and 4.
    if (!Object::ToPrimitive(x, ToPrimitiveHint::kNumber).ToHandle(&x) ||
        !Object::ToPrimitive(y, ToPrimitiveHint::kNumber).ToHandle(&y)) {
      return Nothing<ComparisonResult>();
    }
    if (x->IsString() && y->IsString()) {
      // ES6 section 7.2.11 Abstract Relational Comparison step 5.
      return Just(String::Compare(isolate, Handle<String>::cast(x),
                                  Handle<String>::cast(y)));
    }
    if (x->IsBigInt() && y->IsString()) {
      return BigInt::CompareToString(isolate, Handle<BigInt>::cast(x),
                                     Handle<String>::cast(y));
    }
    if (x->IsString() && y->IsBigInt()) {
      Maybe<ComparisonResult> maybe_result = BigInt::CompareToString(
          isolate, Handle<BigInt>::cast(y), Handle<String>::cast(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 = x->IsNumber();
    bool y_is_number = y->IsNumber();
    if (x_is_number && y_is_number) {
      return Just(StrictNumberCompare(x->Number(), y->Number()));
    } else if (!x_is_number && !y_is_number) {
      return Just(BigInt::CompareToBigInt(Handle<BigInt>::cast(x),
                                          Handle<BigInt>::cast(y)));
    } else if (x_is_number) {
      return Just(Reverse(BigInt::CompareToNumber(Handle<BigInt>::cast(y), x)));
    } else {
      return Just(BigInt::CompareToNumber(Handle<BigInt>::cast(x), y));
    }
  }
  
  // static
  Maybe<bool> Object::Equals(Isolate* isolate, Handle<Object> x,
                             Handle<Object> y) {
    // This is the generic version of Abstract Equality Comparison. Must be in
    // sync with CodeStubAssembler::Equal.
    while (true) {
      if (x->IsNumber()) {
        if (y->IsNumber()) {
          return Just(StrictNumberEquals(x, y));
        } else if (y->IsBoolean()) {
          return Just(
              StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
        } else if (y->IsString()) {
          return Just(StrictNumberEquals(
              x, String::ToNumber(isolate, Handle<String>::cast(y))));
        } else if (y->IsBigInt()) {
          return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x));
        } else if (y->IsJSReceiver()) {
          if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
                   .ToHandle(&y)) {
            return Nothing<bool>();
          }
        } else {
          return Just(false);
        }
      } else if (x->IsString()) {
        if (y->IsString()) {
          return Just(String::Equals(isolate, Handle<String>::cast(x),
                                     Handle<String>::cast(y)));
        } else if (y->IsNumber()) {
          x = String::ToNumber(isolate, Handle<String>::cast(x));
          return Just(StrictNumberEquals(x, y));
        } else if (y->IsBoolean()) {
          x = String::ToNumber(isolate, Handle<String>::cast(x));
          return Just(
              StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
        } else if (y->IsBigInt()) {
          return BigInt::EqualToString(isolate, Handle<BigInt>::cast(y),
                                       Handle<String>::cast(x));
        } else if (y->IsJSReceiver()) {
          if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
                   .ToHandle(&y)) {
            return Nothing<bool>();
          }
        } else {
          return Just(false);
        }
      } else if (x->IsBoolean()) {
        if (y->IsOddball()) {
          return Just(x.is_identical_to(y));
        } else if (y->IsNumber()) {
          return Just(
              StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
        } else if (y->IsString()) {
          y = String::ToNumber(isolate, Handle<String>::cast(y));
          return Just(
              StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
        } else if (y->IsBigInt()) {
          x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x));
          return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x));
        } else if (y->IsJSReceiver()) {
          if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
                   .ToHandle(&y)) {
            return Nothing<bool>();
          }
          x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x));
        } else {
          return Just(false);
        }
      } else if (x->IsSymbol()) {
        if (y->IsSymbol()) {
          return Just(x.is_identical_to(y));
        } else if (y->IsJSReceiver()) {
          if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
                   .ToHandle(&y)) {
            return Nothing<bool>();
          }
        } else {
          return Just(false);
        }
      } else if (x->IsBigInt()) {
        if (y->IsBigInt()) {
          return Just(BigInt::EqualToBigInt(BigInt::cast(*x), BigInt::cast(*y)));
        }
        return Equals(isolate, y, x);
      } else if (x->IsJSReceiver()) {
        if (y->IsJSReceiver()) {
          return Just(x.is_identical_to(y));
        } else if (y->IsUndetectable()) {
          return Just(x->IsUndetectable());
        } else if (y->IsBoolean()) {
          y = Oddball::ToNumber(isolate, Handle<Oddball>::cast(y));
        } else if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(x))
                        .ToHandle(&x)) {
          return Nothing<bool>();
        }
      } else {
        return Just(x->IsUndetectable() && y->IsUndetectable());
      }
    }
  }
  
  bool Object::StrictEquals(Object that) {
    if (this->IsNumber()) {
      if (!that.IsNumber()) return false;
      return StrictNumberEquals(*this, that);
    } else if (this->IsString()) {
      if (!that.IsString()) return false;
      return String::cast(*this).Equals(String::cast(that));
    } else if (this->IsBigInt()) {
      if (!that.IsBigInt()) return false;
      return BigInt::EqualToBigInt(BigInt::cast(*this), BigInt::cast(that));
    }
    return *this == that;
  }
  
  // static
  Handle<String> Object::TypeOf(Isolate* isolate, Handle<Object> object) {
    if (object->IsNumber()) return isolate->factory()->number_string();
    if (object->IsOddball())
      return handle(Oddball::cast(*object).type_of(), isolate);
    if (object->IsUndetectable()) {
      return isolate->factory()->undefined_string();
    }
    if (object->IsString()) return isolate->factory()->string_string();
    if (object->IsSymbol()) return isolate->factory()->symbol_string();
    if (object->IsBigInt()) return isolate->factory()->bigint_string();
    if (object->IsCallable()) return isolate->factory()->function_string();
    return isolate->factory()->object_string();
  }
  
  // static
  MaybeHandle<Object> Object::Add(Isolate* isolate, Handle<Object> lhs,
                                  Handle<Object> rhs) {
    if (lhs->IsNumber() && rhs->IsNumber()) {
      return isolate->factory()->NewNumber(lhs->Number() + rhs->Number());
    } else if (lhs->IsString() && rhs->IsString()) {
      return isolate->factory()->NewConsString(Handle<String>::cast(lhs),
                                               Handle<String>::cast(rhs));
    }
    ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToPrimitive(lhs), Object);
    ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToPrimitive(rhs), Object);
    if (lhs->IsString() || rhs->IsString()) {
      ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToString(isolate, rhs),
                                 Object);
      ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToString(isolate, lhs),
                                 Object);
      return isolate->factory()->NewConsString(Handle<String>::cast(lhs),
                                               Handle<String>::cast(rhs));
    }
    ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(isolate, rhs),
                               Object);
    ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(isolate, lhs),
                               Object);
    return isolate->factory()->NewNumber(lhs->Number() + rhs->Number());
  }
  
  // static
  MaybeHandle<Object> Object::OrdinaryHasInstance(Isolate* isolate,
                                                  Handle<Object> callable,
                                                  Handle<Object> object) {
    // The {callable} must have a [[Call]] internal method.
    if (!callable->IsCallable()) 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 (callable->IsJSBoundFunction()) {
      // 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>());
      Handle<Object> bound_callable(
          Handle<JSBoundFunction>::cast(callable)->bound_target_function(),
          isolate);
      return Object::InstanceOf(isolate, object, bound_callable);
    }
  
    // If {object} is not a receiver, return false.
    if (!object->IsJSReceiver()) return isolate->factory()->false_value();
  
    // Get the "prototype" of {callable}; raise an error if it's not a receiver.
    Handle<Object> prototype;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, prototype,
        Object::GetProperty(isolate, callable,
                            isolate->factory()->prototype_string()),
        Object);
    if (!prototype->IsJSReceiver()) {
      THROW_NEW_ERROR(
          isolate,
          NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype),
          Object);
    }
  
    // Return whether or not {prototype} is in the prototype chain of {object}.
    Maybe<bool> result = JSReceiver::HasInPrototypeChain(
        isolate, Handle<JSReceiver>::cast(object), prototype);
    if (result.IsNothing()) return MaybeHandle<Object>();
    return isolate->factory()->ToBoolean(result.FromJust());
  }
  
  // static
  MaybeHandle<Object> Object::InstanceOf(Isolate* isolate, Handle<Object> object,
                                         Handle<Object> callable) {
    // The {callable} must be a receiver.
    if (!callable->IsJSReceiver()) {
      THROW_NEW_ERROR(isolate,
                      NewTypeError(MessageTemplate::kNonObjectInInstanceOfCheck),
                      Object);
    }
  
    // Lookup the @@hasInstance method on {callable}.
    Handle<Object> inst_of_handler;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, inst_of_handler,
        Object::GetMethod(Handle<JSReceiver>::cast(callable),
                          isolate->factory()->has_instance_symbol()),
        Object);
    if (!inst_of_handler->IsUndefined(isolate)) {
      // Call the {inst_of_handler} on the {callable}.
      Handle<Object> result;
      ASSIGN_RETURN_ON_EXCEPTION(
          isolate, result,
          Execution::Call(isolate, inst_of_handler, callable, 1, &object),
          Object);
      return isolate->factory()->ToBoolean(result->BooleanValue(isolate));
    }
  
    // The {callable} must have a [[Call]] internal method.
    if (!callable->IsCallable()) {
      THROW_NEW_ERROR(
          isolate, NewTypeError(MessageTemplate::kNonCallableInInstanceOfCheck),
          Object);
    }
  
    // Fall back to OrdinaryHasInstance with {callable} and {object}.
    Handle<Object> result;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, result, Object::OrdinaryHasInstance(isolate, callable, object),
        Object);
    return result;
  }
  
  // static
  MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver,
                                        Handle<Name> name) {
    Handle<Object> func;
    Isolate* isolate = receiver->GetIsolate();
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, func, JSReceiver::GetProperty(isolate, receiver, name), Object);
    if (func->IsNullOrUndefined(isolate)) {
      return isolate->factory()->undefined_value();
    }
    if (!func->IsCallable()) {
      THROW_NEW_ERROR(isolate,
                      NewTypeError(MessageTemplate::kPropertyNotFunction, func,
                                   name, receiver),
                      Object);
    }
    return func;
  }
  
  namespace {
  
  MaybeHandle<FixedArray> CreateListFromArrayLikeFastPath(
      Isolate* isolate, Handle<Object> object, ElementTypes element_types) {
    if (element_types == ElementTypes::kAll) {
      if (object->IsJSArray()) {
        Handle<JSArray> array = Handle<JSArray>::cast(object);
        uint32_t length;
        if (!array->HasArrayPrototype(isolate) ||
            !array->length().ToUint32(&length) || !array->HasFastElements() ||
            !JSObject::PrototypeHasNoElements(isolate, *array)) {
          return MaybeHandle<FixedArray>();
        }
        return array->GetElementsAccessor()->CreateListFromArrayLike(
            isolate, array, length);
      } else if (object->IsJSTypedArray()) {
        Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(object);
        size_t length = array->length();
        if (array->WasDetached() ||
            length > static_cast<size_t>(FixedArray::kMaxLength)) {
          return MaybeHandle<FixedArray>();
        }
        STATIC_ASSERT(FixedArray::kMaxLength <=
                      std::numeric_limits<uint32_t>::max());
        return array->GetElementsAccessor()->CreateListFromArrayLike(
            isolate, array, static_cast<uint32_t>(length));
      }
    }
    return MaybeHandle<FixedArray>();
  }
  
  }  // namespace
  
  // static
  MaybeHandle<FixedArray> Object::CreateListFromArrayLike(
      Isolate* isolate, Handle<Object> object, ElementTypes element_types) {
    // Fast-path for JSArray and JSTypedArray.
    MaybeHandle<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 (!object->IsJSReceiver()) {
      THROW_NEW_ERROR(isolate,
                      NewTypeError(MessageTemplate::kCalledOnNonObject,
                                   isolate->factory()->NewStringFromAsciiChecked(
                                       "CreateListFromArrayLike")),
                      FixedArray);
    }
  
    // 4. Let len be ? ToLength(? Get(obj, "length")).
    Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
    Handle<Object> raw_length_number;
    ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number,
                               Object::GetLengthFromArrayLike(isolate, receiver),
                               FixedArray);
    uint32_t len;
    if (!raw_length_number->ToUint32(&len) ||
        len > static_cast<uint32_t>(FixedArray::kMaxLength)) {
      THROW_NEW_ERROR(isolate,
                      NewRangeError(MessageTemplate::kInvalidArrayLength),
                      FixedArray);
    }
    // 5. Let list be an empty List.
    Handle<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).
      Handle<Object> next;
      ASSIGN_RETURN_ON_EXCEPTION(isolate, next,
                                 JSReceiver::GetElement(isolate, receiver, index),
                                 FixedArray);
      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 (!next->IsName()) {
            THROW_NEW_ERROR(isolate,
                            NewTypeError(MessageTemplate::kNotPropertyName, next),
                            FixedArray);
          }
          // 7d. Append next as the last element of list.
          // Internalize on the fly so we can use pointer identity later.
          next = isolate->factory()->InternalizeName(Handle<Name>::cast(next));
          break;
        }
      }
      list->set(index, *next);
      // 7e. Set index to index + 1. (See loop header.)
    }
    // 8. Return list.
    return list;
  }
  
  // static
  MaybeHandle<Object> Object::GetLengthFromArrayLike(Isolate* isolate,
                                                     Handle<JSReceiver> object) {
    Handle<Object> val;
    Handle<Name> key = isolate->factory()->length_string();
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, val, JSReceiver::GetProperty(isolate, object, key), Object);
    return Object::ToLength(isolate, val);
  }
  
  // static
  MaybeHandle<Object> Object::GetProperty(LookupIterator* it,
                                          bool is_global_reference) {
    for (; it->IsFound(); it->Next()) {
      switch (it->state()) {
        case LookupIterator::NOT_FOUND:
        case LookupIterator::TRANSITION:
          UNREACHABLE();
        case LookupIterator::JSPROXY: {
          bool was_found;
          Handle<Object> receiver = it->GetReceiver();
          // In case of global IC, the receiver is the global object. Replace by
          // the global proxy.
          if (receiver->IsJSGlobalObject()) {
            receiver = handle(JSGlobalObject::cast(*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 MaybeHandle<Object>();
            if (!maybe.FromJust()) {
              it->NotFound();
              return it->isolate()->factory()->undefined_value();
            }
          }
          MaybeHandle<Object> 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::INTERCEPTOR: {
          bool done;
          Handle<Object> result;
          ASSIGN_RETURN_ON_EXCEPTION(
              it->isolate(), result,
              JSObject::GetPropertyWithInterceptor(it, &done), Object);
          if (done) return result;
          break;
        }
        case LookupIterator::ACCESS_CHECK:
          if (it->HasAccess()) break;
          return JSObject::GetPropertyWithFailedAccessCheck(it);
        case LookupIterator::ACCESSOR:
          return GetPropertyWithAccessor(it);
        case LookupIterator::INTEGER_INDEXED_EXOTIC:
          return it->isolate()->factory()->undefined_value();
        case LookupIterator::DATA:
          return it->GetDataValue();
      }
    }
  
    return it->isolate()->factory()->undefined_value();
  }
  
  // static
  MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
                                           Handle<JSProxy> proxy,
                                           Handle<Name> name,
                                           Handle<Object> receiver,
                                           bool* was_found) {
    *was_found = true;
  
    DCHECK(!name->IsPrivate());
    STACK_CHECK(isolate, MaybeHandle<Object>());
    Handle<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.
    Handle<Object> 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),
                      Object);
    }
    // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
    // 6. Let trap be ? GetMethod(handler, "get").
    Handle<Object> trap;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, trap,
        Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Object);
    // 7. If trap is undefined, then
    if (trap->IsUndefined(isolate)) {
      // 7.a Return target.[[Get]](P, Receiver).
      LookupIterator::Key key(isolate, name);
      LookupIterator it(isolate, receiver, key, target);
      MaybeHandle<Object> result = Object::GetProperty(&it);
      *was_found = it.IsFound();
      return result;
    }
    // 8. Let trapResult be ? Call(trap, handler, «target, P, Receiver»).
    Handle<Object> trap_result;
    Handle<Object> args[] = {target, name, receiver};
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, trap_result,
        Execution::Call(isolate, trap, handler, arraysize(args), args), Object);
  
    MaybeHandle<Object> result =
        JSProxy::CheckGetSetTrapResult(isolate, name, target, trap_result, kGet);
    if (result.is_null()) {
      return result;
    }
  
    // 11. Return trap_result
    return trap_result;
  }
  
  // static
  MaybeHandle<Object> JSProxy::CheckGetSetTrapResult(Isolate* isolate,
                                                     Handle<Name> name,
                                                     Handle<JSReceiver> target,
                                                     Handle<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() &&
                          !trap_result->SameValue(*target_desc.value());
      if (inconsistent) {
        if (access_kind == kGet) {
          THROW_NEW_ERROR(
              isolate,
              NewTypeError(MessageTemplate::kProxyGetNonConfigurableData, name,
                           target_desc.value(), trap_result),
              Object);
        } else {
          isolate->Throw(*isolate->factory()->NewTypeError(
              MessageTemplate::kProxySetFrozenData, name));
          return MaybeHandle<Object>();
        }
      }
      // 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() &&
                       target_desc.get()->IsUndefined(isolate) &&
                       !trap_result->IsUndefined(isolate);
      } else {
        inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
                       !target_desc.configurable() &&
                       target_desc.set()->IsUndefined(isolate);
      }
      if (inconsistent) {
        if (access_kind == kGet) {
          THROW_NEW_ERROR(
              isolate,
              NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor,
                           name, trap_result),
              Object);
        } else {
          isolate->Throw(*isolate->factory()->NewTypeError(
              MessageTemplate::kProxySetFrozenAccessor, name));
          return MaybeHandle<Object>();
        }
      }
    }
    return isolate->factory()->undefined_value();
  }
  
  bool Object::ToInt32(int32_t* value) {
    if (IsSmi()) {
      *value = Smi::ToInt(*this);
      return true;
    }
    if (IsHeapNumber()) {
      double num = HeapNumber::cast(*this).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;
  }
  
  Handle<SharedFunctionInfo> FunctionTemplateInfo::GetOrCreateSharedFunctionInfo(
      Isolate* isolate, Handle<FunctionTemplateInfo> info,
      MaybeHandle<Name> maybe_name) {
    Object current_info = info->shared_function_info();
    if (current_info.IsSharedFunctionInfo()) {
      return handle(SharedFunctionInfo::cast(current_info), isolate);
    }
    Handle<Name> name;
    Handle<String> name_string;
    if (maybe_name.ToHandle(&name) && name->IsString()) {
      name_string = Handle<String>::cast(name);
    } else if (info->class_name().IsString()) {
      name_string = handle(String::cast(info->class_name()), isolate);
    } else {
      name_string = isolate->factory()->empty_string();
    }
    FunctionKind function_kind;
    if (info->remove_prototype()) {
      function_kind = kConciseMethod;
    } else {
      function_kind = kNormalFunction;
    }
    Handle<SharedFunctionInfo> result =
        isolate->factory()->NewSharedFunctionInfoForApiFunction(name_string, info,
                                                                function_kind);
  
    result->set_length(info->length());
    result->DontAdaptArguments();
    DCHECK(result->IsApiFunction());
  
    info->set_shared_function_info(*result);
    return result;
  }
  
  bool FunctionTemplateInfo::IsTemplateFor(Map map) {
    RuntimeCallTimerScope timer(
        LocalHeap::Current() == nullptr
            ? GetIsolate()->counters()->runtime_call_stats()
            : LocalIsolate::FromHeap(LocalHeap::Current())->runtime_call_stats(),
        RuntimeCallCounterId::kIsTemplateFor);
  
    // There is a constraint on the object; check.
    if (!map.IsJSObjectMap()) return false;
    // Fetch the constructor function of the object.
    Object cons_obj = map.GetConstructor();
    Object type;
    if (cons_obj.IsJSFunction()) {
      JSFunction fun = JSFunction::cast(cons_obj);
      type = fun.shared().function_data(kAcquireLoad);
    } else if (cons_obj.IsFunctionTemplateInfo()) {
      type = FunctionTemplateInfo::cast(cons_obj);
    } else {
      return false;
    }
    // Iterate through the chain of inheriting function templates to
    // see if the required one occurs.
    while (type.IsFunctionTemplateInfo()) {
      if (type == *this) return true;
      type = FunctionTemplateInfo::cast(type).GetParentTemplate();
    }
    // Didn't find the required type in the inheritance chain.
    return false;
  }
  
  // static
  FunctionTemplateRareData FunctionTemplateInfo::AllocateFunctionTemplateRareData(
      Isolate* isolate, Handle<FunctionTemplateInfo> function_template_info) {
    DCHECK(function_template_info->rare_data(kAcquireLoad).IsUndefined(isolate));
    Handle<Struct> struct_obj = isolate->factory()->NewStruct(
        FUNCTION_TEMPLATE_RARE_DATA_TYPE, AllocationType::kOld);
    Handle<FunctionTemplateRareData> rare_data =
        i::Handle<FunctionTemplateRareData>::cast(struct_obj);
    rare_data->set_c_function(Smi(0));
    rare_data->set_c_signature(Smi(0));
    function_template_info->set_rare_data(*rare_data, kReleaseStore);
    return *rare_data;
  }
  
  // static
  Handle<TemplateList> TemplateList::New(Isolate* isolate, int size) {
    Handle<FixedArray> list =
        isolate->factory()->NewFixedArray(kLengthIndex + size);
    list->set(kLengthIndex, Smi::zero());
    return Handle<TemplateList>::cast(list);
  }
  
  // static
  Handle<TemplateList> TemplateList::Add(Isolate* isolate,
                                         Handle<TemplateList> list,
                                         Handle<i::Object> value) {
    STATIC_ASSERT(kFirstElementIndex == 1);
    int index = list->length() + 1;
    Handle<i::FixedArray> fixed_array = Handle<FixedArray>::cast(list);
    fixed_array = FixedArray::SetAndGrow(isolate, fixed_array, index, value);
    fixed_array->set(kLengthIndex, Smi::FromInt(index));
    return Handle<TemplateList>::cast(fixed_array);
  }
  
  // ES6 9.5.1
  // static
  MaybeHandle<HeapObject> JSProxy::GetPrototype(Handle<JSProxy> proxy) {
    Isolate* isolate = proxy->GetIsolate();
    Handle<String> trap_name = isolate->factory()->getPrototypeOf_string();
  
    STACK_CHECK(isolate, MaybeHandle<HeapObject>());
  
    // 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),
                      HeapObject);
    }
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
    Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
  
    // 5. Let trap be ? GetMethod(handler, "getPrototypeOf").
    Handle<Object> trap;
    ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
                               Object::GetMethod(handler, trap_name), HeapObject);
    // 6. If trap is undefined, then return target.[[GetPrototypeOf]]().
    if (trap->IsUndefined(isolate)) {
      return JSReceiver::GetPrototype(isolate, target);
    }
    // 7. Let handlerProto be ? Call(trap, handler, «target»).
    Handle<Object> argv[] = {target};
    Handle<Object> handler_proto;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, handler_proto,
        Execution::Call(isolate, trap, handler, arraysize(argv), argv),
        HeapObject);
    // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError.
    if (!(handler_proto->IsJSReceiver() || handler_proto->IsNull(isolate))) {
      THROW_NEW_ERROR(isolate,
                      NewTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid),
                      HeapObject);
    }
    // 9. Let extensibleTarget be ? IsExtensible(target).
    Maybe<bool> is_extensible = JSReceiver::IsExtensible(target);
    MAYBE_RETURN(is_extensible, MaybeHandle<HeapObject>());
    // 10. If extensibleTarget is true, return handlerProto.
    if (is_extensible.FromJust()) return Handle<HeapObject>::cast(handler_proto);
    // 11. Let targetProto be ? target.[[GetPrototypeOf]]().
    Handle<HeapObject> target_proto;
    ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto,
                               JSReceiver::GetPrototype(isolate, target),
                               HeapObject);
    // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError.
    if (!handler_proto->SameValue(*target_proto)) {
      THROW_NEW_ERROR(
          isolate,
          NewTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible),
          HeapObject);
    }
    // 13. Return handlerProto.
    return Handle<HeapObject>::cast(handler_proto);
  }
  
  MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) {
    Isolate* isolate = it->isolate();
    Handle<Object> structure = it->GetAccessors();
    Handle<Object> receiver = it->GetReceiver();
    // In case of global IC, the receiver is the global object. Replace by the
    // global proxy.
    if (receiver->IsJSGlobalObject()) {
      receiver = handle(JSGlobalObject::cast(*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(!structure->IsForeign());
  
    // API style callbacks.
    Handle<JSObject> holder = it->GetHolder<JSObject>();
    if (structure->IsAccessorInfo()) {
      Handle<Name> name = it->GetName();
      Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure);
      if (!info->IsCompatibleReceiver(*receiver)) {
        THROW_NEW_ERROR(isolate,
                        NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
                                     name, receiver),
                        Object);
      }
  
      if (!info->has_getter()) return isolate->factory()->undefined_value();
  
      if (info->is_sloppy() && !receiver->IsJSReceiver()) {
        ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
                                   Object::ConvertReceiver(isolate, receiver),
                                   Object);
      }
  
      PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder,
                                     Just(kDontThrow));
      Handle<Object> result = args.CallAccessorGetter(info, name);
      RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
      if (result.is_null()) return isolate->factory()->undefined_value();
      Handle<Object> reboxed_result = handle(*result, isolate);
      if (info->replace_on_access() && receiver->IsJSReceiver()) {
        RETURN_ON_EXCEPTION(isolate,
                            Accessors::ReplaceAccessorWithDataProperty(
                                isolate, receiver, holder, name, result),
                            Object);
      }
      return reboxed_result;
    }
  
    Handle<AccessorPair> accessor_pair = Handle<AccessorPair>::cast(structure);
    // AccessorPair with 'cached' private property.
    if (it->TryLookupCachedProperty(accessor_pair)) {
      return Object::GetProperty(it);
    }
  
    // Regular accessor.
    Handle<Object> getter(accessor_pair->getter(), isolate);
    if (getter->IsFunctionTemplateInfo()) {
      SaveAndSwitchContext save(isolate,
                                *holder->GetCreationContext().ToHandleChecked());
      return Builtins::InvokeApiFunction(
          isolate, false, Handle<FunctionTemplateInfo>::cast(getter), receiver, 0,
          nullptr, isolate->factory()->undefined_value());
    } else if (getter->IsCallable()) {
      // TODO(rossberg): nicer would be to cast to some JSCallable here...
      return Object::GetPropertyWithDefinedGetter(
          receiver, Handle<JSReceiver>::cast(getter));
    }
    // Getter is not a function.
    return isolate->factory()->undefined_value();
  }
  
  // static
  Address AccessorInfo::redirect(Address address, AccessorComponent component) {
    ApiFunction fun(address);
    DCHECK_EQ(ACCESSOR_GETTER, component);
    ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL;
    return ExternalReference::Create(&fun, type).address();
  }
  
  Address AccessorInfo::redirected_getter() const {
    Address accessor = v8::ToCData<Address>(getter());
    if (accessor == kNullAddress) return kNullAddress;
    return redirect(accessor, ACCESSOR_GETTER);
  }
  
  Address CallHandlerInfo::redirected_callback() const {
    Address address = v8::ToCData<Address>(callback());
    ApiFunction fun(address);
    ExternalReference::Type type = ExternalReference::DIRECT_API_CALL;
    return ExternalReference::Create(&fun, type).address();
  }
  
  bool AccessorInfo::IsCompatibleReceiverMap(Handle<AccessorInfo> info,
                                             Handle<Map> map) {
    if (!info->HasExpectedReceiverType()) return true;
    if (!map->IsJSObjectMap()) return false;
    return FunctionTemplateInfo::cast(info->expected_receiver_type())
        .IsTemplateFor(*map);
  }
  
  Maybe<bool> Object::SetPropertyWithAccessor(
      LookupIterator* it, Handle<Object> value,
      Maybe<ShouldThrow> maybe_should_throw) {
    Isolate* isolate = it->isolate();
    Handle<Object> structure = it->GetAccessors();
    Handle<Object> receiver = it->GetReceiver();
    // In case of global IC, the receiver is the global object. Replace by the
    // global proxy.
    if (receiver->IsJSGlobalObject()) {
      receiver = handle(JSGlobalObject::cast(*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(!structure->IsForeign());
  
    // API style callbacks.
    Handle<JSObject> holder = it->GetHolder<JSObject>();
    if (structure->IsAccessorInfo()) {
      Handle<Name> name = it->GetName();
      Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure);
      if (!info->IsCompatibleReceiver(*receiver)) {
        isolate->Throw(*isolate->factory()->NewTypeError(
            MessageTemplate::kIncompatibleMethodReceiver, name, receiver));
        return Nothing<bool>();
      }
  
      if (!info->has_setter()) {
        // 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() && !receiver->IsJSReceiver()) {
        ASSIGN_RETURN_ON_EXCEPTION_VALUE(
            isolate, receiver, Object::ConvertReceiver(isolate, receiver),
            Nothing<bool>());
      }
  
      // The actual type of setter callback is either
      // v8::AccessorNameSetterCallback or
      // i::Accesors::AccessorNameBooleanSetterCallback, depending on whether the
      // AccessorInfo was created by the API or internally (see accessors.cc).
      // Here we handle both cases using GenericNamedPropertySetterCallback and
      // its Call method.
      PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder,
                                     maybe_should_throw);
      Handle<Object> result = args.CallAccessorSetter(info, name, value);
      // In the case of AccessorNameSetterCallback, we know that the result value
      // cannot have been set, so the result of Call will be null.  In the case of
      // AccessorNameBooleanSetterCallback, the result will either be null
      // (signalling an exception) or a boolean Oddball.
      RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
      if (result.is_null()) return Just(true);
      DCHECK(result->BooleanValue(isolate) ||
             GetShouldThrow(isolate, maybe_should_throw) == kDontThrow);
      return Just(result->BooleanValue(isolate));
    }
  
    // Regular accessor.
    Handle<Object> setter(AccessorPair::cast(*structure).setter(), isolate);
    if (setter->IsFunctionTemplateInfo()) {
      SaveAndSwitchContext save(isolate,
                                *holder->GetCreationContext().ToHandleChecked());
      Handle<Object> argv[] = {value};
      RETURN_ON_EXCEPTION_VALUE(
          isolate,
          Builtins::InvokeApiFunction(isolate, false,
                                      Handle<FunctionTemplateInfo>::cast(setter),
                                      receiver, arraysize(argv), argv,
                                      isolate->factory()->undefined_value()),
          Nothing<bool>());
      return Just(true);
    } else if (setter->IsCallable()) {
      // TODO(rossberg): nicer would be to cast to some JSCallable here...
      return SetPropertyWithDefinedSetter(
          receiver, Handle<JSReceiver>::cast(setter), value, maybe_should_throw);
    }
  
    RETURN_FAILURE(isolate, GetShouldThrow(isolate, maybe_should_throw),
                   NewTypeError(MessageTemplate::kNoSetterInCallback,
                                it->GetName(), it->GetHolder<JSObject>()));
  }
  
  MaybeHandle<Object> Object::GetPropertyWithDefinedGetter(
      Handle<Object> receiver, Handle<JSReceiver> getter) {
    Isolate* isolate = getter->GetIsolate();
  
    // 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 MaybeHandle<Object>();
    }
  
    return Execution::Call(isolate, getter, receiver, 0, nullptr);
  }
  
  Maybe<bool> Object::SetPropertyWithDefinedSetter(
      Handle<Object> receiver, Handle<JSReceiver> setter, Handle<Object> value,
      Maybe<ShouldThrow> should_throw) {
    Isolate* isolate = setter->GetIsolate();
  
    Handle<Object> argv[] = {value};
    RETURN_ON_EXCEPTION_VALUE(
        isolate,
        Execution::Call(isolate, setter, receiver, arraysize(argv), argv),
        Nothing<bool>());
    return Just(true);
  }
  
  Map Object::GetPrototypeChainRootMap(Isolate* isolate) const {
    DisallowGarbageCollection no_alloc;
    if (IsSmi()) {
      Context native_context = isolate->context().native_context();
      return native_context.number_function().initial_map();
    }
  
    const HeapObject heap_object = HeapObject::cast(*this);
    return heap_object.map().GetPrototypeChainRootMap(isolate);
  }
  
  Smi Object::GetOrCreateHash(Isolate* isolate) {
    DisallowGarbageCollection no_gc;
    Object hash = Object::GetSimpleHash(*this);
    if (hash.IsSmi()) return Smi::cast(hash);
  
    DCHECK(IsJSReceiver());
    return JSReceiver::cast(*this).GetOrCreateIdentityHash(isolate);
  }
  
  bool Object::SameValue(Object other) {
    if (other == *this) return true;
  
    if (IsNumber() && other.IsNumber()) {
      return SameNumberValue(Number(), other.Number());
    }
    if (IsString() && other.IsString()) {
      return String::cast(*this).Equals(String::cast(other));
    }
    if (IsBigInt() && other.IsBigInt()) {
      return BigInt::EqualToBigInt(BigInt::cast(*this), BigInt::cast(other));
    }
    return false;
  }
  
  bool Object::SameValueZero(Object other) {
    if (other == *this) return true;
  
    if (IsNumber() && other.IsNumber()) {
      double this_value = Number();
      double other_value = other.Number();
      // +0 == -0 is true
      return this_value == other_value ||
             (std::isnan(this_value) && std::isnan(other_value));
    }
    if (IsString() && other.IsString()) {
      return String::cast(*this).Equals(String::cast(other));
    }
    if (IsBigInt() && other.IsBigInt()) {
      return BigInt::EqualToBigInt(BigInt::cast(*this), BigInt::cast(other));
    }
    return false;
  }
  
  MaybeHandle<Object> Object::ArraySpeciesConstructor(
      Isolate* isolate, Handle<Object> original_array) {
    Handle<Object> default_species = isolate->array_function();
    if (!FLAG_builtin_subclassing) return default_species;
    if (original_array->IsJSArray() &&
        Handle<JSArray>::cast(original_array)->HasArrayPrototype(isolate) &&
        Protectors::IsArraySpeciesLookupChainIntact(isolate)) {
      return default_species;
    }
    Handle<Object> constructor = isolate->factory()->undefined_value();
    Maybe<bool> is_array = Object::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()),
          Object);
      if (constructor->IsConstructor()) {
        Handle<Context> constructor_context;
        ASSIGN_RETURN_ON_EXCEPTION(
            isolate, constructor_context,
            JSReceiver::GetFunctionRealm(Handle<JSReceiver>::cast(constructor)),
            Object);
        if (*constructor_context != *isolate->native_context() &&
            *constructor == constructor_context->array_function()) {
          constructor = isolate->factory()->undefined_value();
        }
      }
      if (constructor->IsJSReceiver()) {
        ASSIGN_RETURN_ON_EXCEPTION(
            isolate, constructor,
            JSReceiver::GetProperty(isolate,
                                    Handle<JSReceiver>::cast(constructor),
                                    isolate->factory()->species_symbol()),
            Object);
        if (constructor->IsNull(isolate)) {
          constructor = isolate->factory()->undefined_value();
        }
      }
    }
    if (constructor->IsUndefined(isolate)) {
      return default_species;
    } else {
      if (!constructor->IsConstructor()) {
        THROW_NEW_ERROR(isolate,
                        NewTypeError(MessageTemplate::kSpeciesNotConstructor),
                        Object);
      }
      return constructor;
    }
  }
  
  // ES6 section 7.3.20 SpeciesConstructor ( O, defaultConstructor )
  V8_WARN_UNUSED_RESULT MaybeHandle<Object> Object::SpeciesConstructor(
      Isolate* isolate, Handle<JSReceiver> recv,
      Handle<JSFunction> default_ctor) {
    Handle<Object> ctor_obj;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, ctor_obj,
        JSObject::GetProperty(isolate, recv,
                              isolate->factory()->constructor_string()),
        Object);
  
    if (ctor_obj->IsUndefined(isolate)) return default_ctor;
  
    if (!ctor_obj->IsJSReceiver()) {
      THROW_NEW_ERROR(isolate,
                      NewTypeError(MessageTemplate::kConstructorNotReceiver),
                      Object);
    }
  
    Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj);
  
    Handle<Object> species;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, species,
        JSObject::GetProperty(isolate, ctor,
                              isolate->factory()->species_symbol()),
        Object);
  
    if (species->IsNullOrUndefined(isolate)) {
      return default_ctor;
    }
  
    if (species->IsConstructor()) return species;
  
    THROW_NEW_ERROR(
        isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object);
  }
  
  bool Object::IterationHasObservableEffects() {
    // Check that this object is an array.
    if (!IsJSArray()) return true;
    JSArray array = JSArray::cast(*this);
    Isolate* isolate = array.GetIsolate();
  
  #ifdef V8_ENABLE_FORCE_SLOW_PATH
    if (isolate->force_slow_path()) return true;
  #endif
  
    // Check that we have the original ArrayPrototype.
    if (!array.map().prototype().IsJSObject()) return true;
    JSObject array_proto = JSObject::cast(array.map().prototype());
    if (!isolate->is_initial_array_prototype(array_proto)) return true;
  
    // 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;
  }
  
  bool Object::IsCodeLike(Isolate* isolate) const {
    DisallowGarbageCollection no_gc;
    return IsJSReceiver() && JSReceiver::cast(*this).IsCodeLike(isolate);
  }
  
  void Object::ShortPrint(FILE* out) const {
    OFStream os(out);
    os << Brief(*this);
  }
  
  void Object::ShortPrint(StringStream* accumulator) const {
    std::ostringstream os;
    os << Brief(*this);
    accumulator->Add(os.str().c_str());
  }
  
  void Object::ShortPrint(std::ostream& os) const { os << Brief(*this); }
  
  std::ostream& operator<<(std::ostream& os, const Object& obj) {
    obj.ShortPrint(os);
    return os;
  }
  
  std::ostream& operator<<(std::ostream& os, const Brief& v) {
    MaybeObject maybe_object(v.value);
    Smi smi;
    HeapObject heap_object;
    if (maybe_object->ToSmi(&smi)) {
      smi.SmiPrint(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;
  }
  
  void Smi::SmiPrint(std::ostream& os) const {  // NOLINT
    os << value();
  }
  
  void HeapObject::HeapObjectShortPrint(std::ostream& os) {  // NOLINT
    os << AsHex::Address(this->ptr()) << " ";
  
    if (IsString()) {
      HeapStringAllocator allocator;
      StringStream accumulator(&allocator);
      String::cast(*this).StringShortPrint(&accumulator);
      os << accumulator.ToCString().get();
      return;
    }
    if (IsJSObject()) {
      HeapStringAllocator allocator;
      StringStream accumulator(&allocator);
      JSObject::cast(*this).JSObjectShortPrint(&accumulator);
      os << accumulator.ToCString().get();
      return;
    }
    switch (map().instance_type()) {
      case MAP_TYPE: {
        os << "<Map";
        Map mapInstance = Map::cast(*this);
        if (mapInstance.IsJSObjectMap()) {
          os << "(" << ElementsKindToString(mapInstance.elements_kind()) << ")";
        } else if (mapInstance.instance_size() != kVariableSizeSentinel) {
          os << "[" << mapInstance.instance_size() << "]";
        }
        os << ">";
      } break;
      case AWAIT_CONTEXT_TYPE: {
        os << "<AwaitContext generator= ";
        HeapStringAllocator allocator;
        StringStream accumulator(&allocator);
        Context::cast(*this).extension().ShortPrint(&accumulator);
        os << accumulator.ToCString().get();
        os << '>';
        break;
      }
      case BLOCK_CONTEXT_TYPE:
        os << "<BlockContext[" << Context::cast(*this).length() << "]>";
        break;
      case CATCH_CONTEXT_TYPE:
        os << "<CatchContext[" << Context::cast(*this).length() << "]>";
        break;
      case DEBUG_EVALUATE_CONTEXT_TYPE:
        os << "<DebugEvaluateContext[" << Context::cast(*this).length() << "]>";
        break;
      case EVAL_CONTEXT_TYPE:
        os << "<EvalContext[" << Context::cast(*this).length() << "]>";
        break;
      case FUNCTION_CONTEXT_TYPE:
        os << "<FunctionContext[" << Context::cast(*this).length() << "]>";
        break;
      case MODULE_CONTEXT_TYPE:
        os << "<ModuleContext[" << Context::cast(*this).length() << "]>";
        break;
      case NATIVE_CONTEXT_TYPE:
        os << "<NativeContext[" << Context::cast(*this).length() << "]>";
        break;
      case SCRIPT_CONTEXT_TYPE:
        os << "<ScriptContext[" << Context::cast(*this).length() << "]>";
        break;
      case WITH_CONTEXT_TYPE:
        os << "<WithContext[" << Context::cast(*this).length() << "]>";
        break;
      case SCRIPT_CONTEXT_TABLE_TYPE:
        os << "<ScriptContextTable[" << FixedArray::cast(*this).length() << "]>";
        break;
      case HASH_TABLE_TYPE:
        os << "<HashTable[" << FixedArray::cast(*this).length() << "]>";
        break;
      case ORDERED_HASH_MAP_TYPE:
        os << "<OrderedHashMap[" << FixedArray::cast(*this).length() << "]>";
        break;
      case ORDERED_HASH_SET_TYPE:
        os << "<OrderedHashSet[" << FixedArray::cast(*this).length() << "]>";
        break;
      case ORDERED_NAME_DICTIONARY_TYPE:
        os << "<OrderedNameDictionary[" << FixedArray::cast(*this).length()
           << "]>";
        break;
      case NAME_DICTIONARY_TYPE:
        os << "<NameDictionary[" << FixedArray::cast(*this).length() << "]>";
        break;
      case SWISS_NAME_DICTIONARY_TYPE:
        os << "<SwissNameDictionary["
           << SwissNameDictionary::cast(*this).Capacity() << "]>";
        break;
      case GLOBAL_DICTIONARY_TYPE:
        os << "<GlobalDictionary[" << FixedArray::cast(*this).length() << "]>";
        break;
      case NUMBER_DICTIONARY_TYPE:
        os << "<NumberDictionary[" << FixedArray::cast(*this).length() << "]>";
        break;
      case SIMPLE_NUMBER_DICTIONARY_TYPE:
        os << "<SimpleNumberDictionary[" << FixedArray::cast(*this).length()
           << "]>";
        break;
      case FIXED_ARRAY_TYPE:
        os << "<FixedArray[" << FixedArray::cast(*this).length() << "]>";
        break;
      case OBJECT_BOILERPLATE_DESCRIPTION_TYPE:
        os << "<ObjectBoilerplateDescription[" << FixedArray::cast(*this).length()
           << "]>";
        break;
      case FIXED_DOUBLE_ARRAY_TYPE:
        os << "<FixedDoubleArray[" << FixedDoubleArray::cast(*this).length()
           << "]>";
        break;
      case BYTE_ARRAY_TYPE:
        os << "<ByteArray[" << ByteArray::cast(*this).length() << "]>";
        break;
      case BYTECODE_ARRAY_TYPE:
        os << "<BytecodeArray[" << BytecodeArray::cast(*this).length() << "]>";
        break;
      case DESCRIPTOR_ARRAY_TYPE:
        os << "<DescriptorArray["
           << DescriptorArray::cast(*this).number_of_descriptors() << "]>";
        break;
      case TRANSITION_ARRAY_TYPE:
        os << "<TransitionArray[" << TransitionArray::cast(*this).length()
           << "]>";
        break;
      case PROPERTY_ARRAY_TYPE:
        os << "<PropertyArray[" << PropertyArray::cast(*this).length() << "]>";
        break;
      case FEEDBACK_CELL_TYPE: {
        {
          ReadOnlyRoots roots = GetReadOnlyRoots();
          os << "<FeedbackCell[";
          if (map() == roots.no_closures_cell_map()) {
            os << "no feedback";
          } else if (map() == roots.no_closures_cell_map()) {
            os << "no closures";
          } else if (map() == roots.one_closure_cell_map()) {
            os << "one closure";
          } else if (map() == roots.many_closures_cell_map()) {
            os << "many closures";
          } else {
            os << "!!!INVALID MAP!!!";
          }
          os << "]>";
        }
        break;
      }
      case CLOSURE_FEEDBACK_CELL_ARRAY_TYPE:
        os << "<ClosureFeedbackCellArray["
           << ClosureFeedbackCellArray::cast(*this).length() << "]>";
        break;
      case FEEDBACK_VECTOR_TYPE:
        os << "<FeedbackVector[" << FeedbackVector::cast(*this).length() << "]>";
        break;
      case FREE_SPACE_TYPE:
        os << "<FreeSpace[" << FreeSpace::cast(*this).size() << "]>";
        break;
  
      case PREPARSE_DATA_TYPE: {
        PreparseData data = PreparseData::cast(*this);
        os << "<PreparseData[data=" << data.data_length()
           << " children=" << data.children_length() << "]>";
        break;
      }
  
      case UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE: {
        UncompiledDataWithoutPreparseData data =
            UncompiledDataWithoutPreparseData::cast(*this);
        os << "<UncompiledDataWithoutPreparseData (" << data.start_position()
           << ", " << data.end_position() << ")]>";
        break;
      }
  
      case UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE: {
        UncompiledDataWithPreparseData data =
            UncompiledDataWithPreparseData::cast(*this);
        os << "<UncompiledDataWithPreparseData (" << data.start_position() << ", "
           << data.end_position() << ") preparsed=" << Brief(data.preparse_data())
           << ">";
        break;
      }
  
      case SHARED_FUNCTION_INFO_TYPE: {
        SharedFunctionInfo shared = SharedFunctionInfo::cast(*this);
        std::unique_ptr<char[]> debug_name = shared.DebugNameCStr();
        if (debug_name[0] != '\0') {
          os << "<SharedFunctionInfo " << debug_name.get() << ">";
        } else {
          os << "<SharedFunctionInfo>";
        }
        break;
      }
      case JS_MESSAGE_OBJECT_TYPE:
        os << "<JSMessageObject>";
        break;
  #define MAKE_STRUCT_CASE(TYPE, Name, name)   \
    case TYPE:                                 \
      os << "<" #Name;                         \
      Name::cast(*this).BriefPrintDetails(os); \
      os << ">";                               \
      break;
        STRUCT_LIST(MAKE_STRUCT_CASE)
  #undef MAKE_STRUCT_CASE
      case ALLOCATION_SITE_TYPE: {
        os << "<AllocationSite";
        AllocationSite::cast(*this).BriefPrintDetails(os);
        os << ">";
        break;
      }
      case SCOPE_INFO_TYPE: {
        ScopeInfo scope = ScopeInfo::cast(*this);
        os << "<ScopeInfo";
        if (!scope.IsEmpty()) os << " " << scope.scope_type();
        os << ">";
        break;
      }
      case CODE_TYPE: {
        Code code = Code::cast(*this);
        os << "<Code " << CodeKindToString(code.kind());
        if (code.is_builtin()) {
          os << " " << Builtins::name(code.builtin_index());
        }
        os << ">";
        break;
      }
      case ODDBALL_TYPE: {
        if (IsUndefined()) {
          os << "<undefined>";
        } else if (IsTheHole()) {
          os << "<the_hole>";
        } else if (IsNull()) {
          os << "<null>";
        } else if (IsTrue()) {
          os << "<true>";
        } else if (IsFalse()) {
          os << "<false>";
        } else {
          os << "<Odd Oddball: ";
          os << Oddball::cast(*this).to_string().ToCString().get();
          os << ">";
        }
        break;
      }
      case SYMBOL_TYPE: {
        Symbol symbol = Symbol::cast(*this);
        symbol.SymbolShortPrint(os);
        break;
      }
      case HEAP_NUMBER_TYPE: {
        os << "<HeapNumber ";
        HeapNumber::cast(*this).HeapNumberShortPrint(os);
        os << ">";
        break;
      }
      case BIGINT_TYPE: {
        os << "<BigInt ";
        BigInt::cast(*this).BigIntShortPrint(os);
        os << ">";
        break;
      }
      case JS_PROXY_TYPE:
        os << "<JSProxy>";
        break;
      case FOREIGN_TYPE:
        os << "<Foreign>";
        break;
      case CELL_TYPE: {
        os << "<Cell value= ";
        HeapStringAllocator allocator;
        StringStream accumulator(&allocator);
        Cell::cast(*this).value().ShortPrint(&accumulator);
        os << accumulator.ToCString().get();
        os << '>';
        break;
      }
      case PROPERTY_CELL_TYPE: {
        PropertyCell cell = PropertyCell::cast(*this);
        os << "<PropertyCell name=";
        cell.name().ShortPrint(os);
        os << " value=";
        HeapStringAllocator allocator;
        StringStream accumulator(&allocator);
        cell.value(kAcquireLoad).ShortPrint(&accumulator);
        os << accumulator.ToCString().get();
        os << '>';
        break;
      }
      case CALL_HANDLER_INFO_TYPE: {
        CallHandlerInfo info = CallHandlerInfo::cast(*this);
        os << "<CallHandlerInfo ";
        os << "callback= " << Brief(info.callback());
        os << ", js_callback= " << Brief(info.js_callback());
        os << ", data= " << Brief(info.data());
        if (info.IsSideEffectFreeCallHandlerInfo()) {
          os << ", side_effect_free= true>";
        } else {
          os << ", side_effect_free= false>";
        }
        break;
      }
      default:
        os << "<Other heap object (" << map().instance_type() << ")>";
        break;
    }
  }
  
  void Struct::BriefPrintDetails(std::ostream& os) {}
  
  void Tuple2::BriefPrintDetails(std::ostream& os) {
    os << " " << Brief(value1()) << ", " << Brief(value2());
  }
  
  void ClassPositions::BriefPrintDetails(std::ostream& os) {
    os << " " << start() << ", " << end();
  }
  
  void CallableTask::BriefPrintDetails(std::ostream& os) {
    os << " callable=" << Brief(callable());
  }
  
  void HeapObject::Iterate(ObjectVisitor* v) { IterateFast<ObjectVisitor>(v); }
  
  void HeapObject::IterateBody(ObjectVisitor* v) {
    Map m = map();
    IterateBodyFast<ObjectVisitor>(m, SizeFromMap(m), v);
  }
  
  void HeapObject::IterateBody(Map map, int object_size, ObjectVisitor* v) {
    IterateBodyFast<ObjectVisitor>(map, object_size, v);
  }
  
  struct CallIsValidSlot {
    template <typename BodyDescriptor>
    static bool apply(Map map, HeapObject obj, int offset, int) {
      return BodyDescriptor::IsValidSlot(map, obj, offset);
    }
  };
  
  bool HeapObject::IsValidSlot(Map map, int offset) {
    DCHECK_NE(0, offset);
    return BodyDescriptorApply<CallIsValidSlot, bool>(map.instance_type(), map,
                                                      *this, offset, 0);
  }
  
  int HeapObject::SizeFromMap(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 FixedArray::SizeFor(
          FixedArray::unchecked_cast(*this).synchronized_length());
    }
    if (base::IsInRange(instance_type, FIRST_CONTEXT_TYPE, LAST_CONTEXT_TYPE)) {
      if (instance_type == NATIVE_CONTEXT_TYPE) return NativeContext::kSize;
      return Context::SizeFor(Context::unchecked_cast(*this).length());
    }
    if (instance_type == ONE_BYTE_STRING_TYPE ||
        instance_type == ONE_BYTE_INTERNALIZED_STRING_TYPE) {
      // Strings may get concurrently truncated, hence we have to access its
      // length synchronized.
      return SeqOneByteString::SizeFor(
          SeqOneByteString::unchecked_cast(*this).synchronized_length());
    }
    if (instance_type == BYTE_ARRAY_TYPE) {
      return ByteArray::SizeFor(
          ByteArray::unchecked_cast(*this).synchronized_length());
    }
    if (instance_type == BYTECODE_ARRAY_TYPE) {
      return BytecodeArray::SizeFor(
          BytecodeArray::unchecked_cast(*this).synchronized_length());
    }
    if (instance_type == FREE_SPACE_TYPE) {
      return FreeSpace::unchecked_cast(*this).relaxed_read_size();
    }
    if (instance_type == STRING_TYPE ||
        instance_type == INTERNALIZED_STRING_TYPE) {
      // Strings may get concurrently truncated, hence we have to access its
      // length synchronized.
      return SeqTwoByteString::SizeFor(
          SeqTwoByteString::unchecked_cast(*this).synchronized_length());
    }
    if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) {
      return FixedDoubleArray::SizeFor(
          FixedDoubleArray::unchecked_cast(*this).synchronized_length());
    }
    if (instance_type == FEEDBACK_METADATA_TYPE) {
      return FeedbackMetadata::SizeFor(
          FeedbackMetadata::unchecked_cast(*this).synchronized_slot_count());
    }
    if (base::IsInRange(instance_type, FIRST_DESCRIPTOR_ARRAY_TYPE,
                        LAST_DESCRIPTOR_ARRAY_TYPE)) {
      return DescriptorArray::SizeFor(
          DescriptorArray::unchecked_cast(*this).number_of_all_descriptors());
    }
    if (base::IsInRange(instance_type, FIRST_WEAK_FIXED_ARRAY_TYPE,
                        LAST_WEAK_FIXED_ARRAY_TYPE)) {
      return WeakFixedArray::SizeFor(
          WeakFixedArray::unchecked_cast(*this).synchronized_length());
    }
    if (instance_type == WEAK_ARRAY_LIST_TYPE) {
      return WeakArrayList::SizeForCapacity(
          WeakArrayList::unchecked_cast(*this).synchronized_capacity());
    }
    if (instance_type == SMALL_ORDERED_HASH_SET_TYPE) {
      return SmallOrderedHashSet::SizeFor(
          SmallOrderedHashSet::unchecked_cast(*this).Capacity());
    }
    if (instance_type == SMALL_ORDERED_HASH_MAP_TYPE) {
      return SmallOrderedHashMap::SizeFor(
          SmallOrderedHashMap::unchecked_cast(*this).Capacity());
    }
    if (instance_type == SMALL_ORDERED_NAME_DICTIONARY_TYPE) {
      return SmallOrderedNameDictionary::SizeFor(
          SmallOrderedNameDictionary::unchecked_cast(*this).Capacity());
    }
    if (instance_type == SWISS_NAME_DICTIONARY_TYPE) {
      return SwissNameDictionary::SizeFor(
          SwissNameDictionary::unchecked_cast(*this).Capacity());
    }
    if (instance_type == PROPERTY_ARRAY_TYPE) {
      return PropertyArray::SizeFor(
          PropertyArray::cast(*this).synchronized_length());
    }
    if (instance_type == FEEDBACK_VECTOR_TYPE) {
      return FeedbackVector::SizeFor(
          FeedbackVector::unchecked_cast(*this).length());
    }
    if (instance_type == BIGINT_TYPE) {
      return BigInt::SizeFor(BigInt::unchecked_cast(*this).length());
    }
    if (instance_type == PREPARSE_DATA_TYPE) {
      PreparseData data = PreparseData::unchecked_cast(*this);
      return PreparseData::SizeFor(data.data_length(), data.children_length());
    }
  #define MAKE_TORQUE_SIZE_FOR(TYPE, TypeName)                \
    if (instance_type == TYPE) {                              \
      return TypeName::unchecked_cast(*this).AllocatedSize(); \
    }
    TORQUE_INSTANCE_TYPE_TO_BODY_DESCRIPTOR_LIST(MAKE_TORQUE_SIZE_FOR)
  #undef MAKE_TORQUE_SIZE_FOR
  
    if (instance_type == CODE_TYPE) {
      return Code::unchecked_cast(*this).CodeSize();
    }
    if (instance_type == COVERAGE_INFO_TYPE) {
      return CoverageInfo::SizeFor(
          CoverageInfo::unchecked_cast(*this).slot_count());
    }
  #if V8_ENABLE_WEBASSEMBLY
    if (instance_type == WASM_ARRAY_TYPE) {
      return WasmArray::GcSafeSizeFor(map, WasmArray::cast(*this).length());
    }
  #endif  // V8_ENABLE_WEBASSEMBLY
    DCHECK_EQ(instance_type, EMBEDDER_DATA_ARRAY_TYPE);
    return EmbedderDataArray::SizeFor(
        EmbedderDataArray::unchecked_cast(*this).length());
  }
  
  bool HeapObject::NeedsRehashing() const {
    return NeedsRehashing(map().instance_type());
  }
  
  bool HeapObject::NeedsRehashing(InstanceType instance_type) const {
    DCHECK_EQ(instance_type, map().instance_type());
    switch (instance_type) {
      case DESCRIPTOR_ARRAY_TYPE:
      case STRONG_DESCRIPTOR_ARRAY_TYPE:
        return DescriptorArray::cast(*this).number_of_descriptors() > 1;
      case TRANSITION_ARRAY_TYPE:
        return TransitionArray::cast(*this).number_of_entries() > 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 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;
    }
  }
  
  bool HeapObject::CanBeRehashed() const {
    DCHECK(NeedsRehashing());
    switch (map().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 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 SmallOrderedHashMap::cast(*this).NumberOfElements() == 0;
      case SMALL_ORDERED_HASH_SET_TYPE:
        return SmallOrderedHashMap::cast(*this).NumberOfElements() == 0;
      case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
        return SmallOrderedNameDictionary::cast(*this).NumberOfElements() == 0;
      default:
        return false;
    }
    return false;
  }
  
  void HeapObject::RehashBasedOnMap(Isolate* isolate) {
    switch (map().instance_type()) {
      case HASH_TABLE_TYPE:
        UNREACHABLE();
      case NAME_DICTIONARY_TYPE:
        NameDictionary::cast(*this).Rehash(isolate);
        break;
      case SWISS_NAME_DICTIONARY_TYPE:
        SwissNameDictionary::cast(*this).Rehash(isolate);
        break;
      case GLOBAL_DICTIONARY_TYPE:
        GlobalDictionary::cast(*this).Rehash(isolate);
        break;
      case NUMBER_DICTIONARY_TYPE:
        NumberDictionary::cast(*this).Rehash(isolate);
        break;
      case SIMPLE_NUMBER_DICTIONARY_TYPE:
        SimpleNumberDictionary::cast(*this).Rehash(isolate);
        break;
      case DESCRIPTOR_ARRAY_TYPE:
        DCHECK_LE(1, DescriptorArray::cast(*this).number_of_descriptors());
        DescriptorArray::cast(*this).Sort();
        break;
      case TRANSITION_ARRAY_TYPE:
        TransitionArray::cast(*this).Sort();
        break;
      case SMALL_ORDERED_HASH_MAP_TYPE:
        DCHECK_EQ(0, SmallOrderedHashMap::cast(*this).NumberOfElements());
        break;
      case SMALL_ORDERED_HASH_SET_TYPE:
        DCHECK_EQ(0, SmallOrderedHashSet::cast(*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: {
        JSMap::cast(*this).Rehash(isolate);
        break;
      }
      case JS_SET_TYPE: {
        JSSet::cast(*this).Rehash(isolate);
        break;
      }
      case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
        DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this).NumberOfElements());
        break;
      case ONE_BYTE_INTERNALIZED_STRING_TYPE:
      case INTERNALIZED_STRING_TYPE:
        // Rare case, rehash read-only space strings before they are sealed.
        DCHECK(ReadOnlyHeap::Contains(*this));
        String::cast(*this).EnsureHash();
        break;
      default:
        UNREACHABLE();
    }
  }
  
  bool HeapObject::IsExternal(Isolate* isolate) const {
    return map().FindRootMap(isolate) == isolate->heap()->external_map();
  }
  
  void DescriptorArray::GeneralizeAllFields() {
    int length = number_of_descriptors();
    for (InternalIndex i : InternalIndex::Range(length)) {
      PropertyDetails details = GetDetails(i);
      details = details.CopyWithRepresentation(Representation::Tagged());
      if (details.location() == kField) {
        DCHECK_EQ(kData, details.kind());
        details = details.CopyWithConstness(PropertyConstness::kMutable);
        SetValue(i, MaybeObject::FromObject(FieldType::Any()));
      }
      SetDetails(i, details);
    }
  }
  
  MaybeHandle<Object> Object::SetProperty(Isolate* isolate, Handle<Object> object,
                                          Handle<Name> name, Handle<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 value;
  }
  
  Maybe<bool> Object::SetPropertyInternal(LookupIterator* it,
                                          Handle<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());
  
    do {
      switch (it->state()) {
        case LookupIterator::NOT_FOUND:
          UNREACHABLE();
  
        case LookupIterator::ACCESS_CHECK:
          if (it->HasAccess()) break;
          // 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: {
          Handle<Object> receiver = it->GetReceiver();
          // In case of global IC, the receiver is the global object. Replace by
          // the global proxy.
          if (receiver->IsJSGlobalObject()) {
            receiver = handle(JSGlobalObject::cast(*receiver).global_proxy(),
                              it->isolate());
          }
          return JSProxy::SetProperty(it->GetHolder<JSProxy>(), it->GetName(),
                                      value, receiver, should_throw);
        }
  
        case LookupIterator::INTERCEPTOR: {
          if (it->HolderIsReceiverOrHiddenPrototype()) {
            Maybe<bool> result =
                JSObject::SetPropertyWithInterceptor(it, should_throw, value);
            if (result.IsNothing() || result.FromJust()) return result;
          } 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);
            }
            if (maybe_attributes.FromJust() == ABSENT) break;
            *found = false;
            return Nothing<bool>();
          }
          break;
        }
  
        case LookupIterator::ACCESSOR: {
          if (it->IsReadOnly()) {
            return WriteToReadOnlyProperty(it, value, should_throw);
          }
          Handle<Object> accessors = it->GetAccessors();
          if (accessors->IsAccessorInfo() &&
              !it->HolderIsReceiverOrHiddenPrototype() &&
              AccessorInfo::cast(*accessors).is_special_data_property()) {
            *found = false;
            return Nothing<bool>();
          }
          return SetPropertyWithAccessor(it, value, should_throw);
        }
        case LookupIterator::INTEGER_INDEXED_EXOTIC: {
          // 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.
          auto holder = it->GetHolder<JSTypedArray>();
          Handle<Object> throwaway_value;
          if (holder->type() == kExternalBigInt64Array ||
              holder->type() == kExternalBigUint64Array) {
            ASSIGN_RETURN_ON_EXCEPTION_VALUE(
                it->isolate(), throwaway_value,
                BigInt::FromObject(it->isolate(), value), Nothing<bool>());
          } else {
            ASSIGN_RETURN_ON_EXCEPTION_VALUE(
                it->isolate(), throwaway_value,
                Object::ToNumber(it->isolate(), value), Nothing<bool>());
          }
  
          // FIXME: Throw a TypeError if the holder is detached here
          // (IntegerIndexedElementSpec step 5).
  
          // TODO(verwaest): Per spec, we should return false here (steps 6-9
          // in IntegerIndexedElementSpec), 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);
        }
  
        case LookupIterator::DATA:
          if (it->IsReadOnly()) {
            return WriteToReadOnlyProperty(it, value, should_throw);
          }
          if (it->HolderIsReceiverOrHiddenPrototype()) {
            return SetDataProperty(it, value);
          }
          V8_FALLTHROUGH;
        case LookupIterator::TRANSITION:
          *found = false;
          return Nothing<bool>();
      }
      it->Next();
    } while (it->IsFound());
  
    *found = false;
    return Nothing<bool>();
  }
  
  Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<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 the receiver is the JSGlobalObject, the store was contextual. In case
    // the property did not exist yet on the global object itself, we have to
    // throw a reference error in strict mode.  In sloppy mode, we continue.
    if (it->GetReceiver()->IsJSGlobalObject() &&
        (GetShouldThrow(it->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(ReadOnlyRoots(it->isolate()));
      }
      it->isolate()->Throw(*it->isolate()->factory()->NewReferenceError(
          MessageTemplate::kNotDefined, it->GetName()));
      return Nothing<bool>();
    }
  
    return AddDataProperty(it, value, NONE, should_throw, store_origin);
  }
  
  Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<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 (!it->GetReceiver()->IsJSReceiver()) {
      return WriteToReadOnlyProperty(it, value, should_throw);
    }
    Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver());
  
    LookupIterator::Configuration c = LookupIterator::OWN;
    LookupIterator own_lookup =
        it->IsElement() ? LookupIterator(isolate, receiver, it->index(), c)
                        : LookupIterator(isolate, receiver, it->name(), c);
  
    for (; own_lookup.IsFound(); own_lookup.Next()) {
      switch (own_lookup.state()) {
        case LookupIterator::ACCESS_CHECK:
          if (!own_lookup.HasAccess()) {
            return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value,
                                                              should_throw);
          }
          break;
  
        case LookupIterator::ACCESSOR:
          if (own_lookup.GetAccessors()->IsAccessorInfo()) {
            if (own_lookup.IsReadOnly()) {
              return WriteToReadOnlyProperty(&own_lookup, value, should_throw);
            }
            return Object::SetPropertyWithAccessor(&own_lookup, value,
                                                   should_throw);
          }
          V8_FALLTHROUGH;
        case LookupIterator::INTEGER_INDEXED_EXOTIC:
          return RedefineIncompatibleProperty(isolate, it->GetName(), value,
                                              should_throw);
  
        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()) {
            return JSReceiver::CreateDataProperty(&own_lookup, value,
                                                  should_throw);
          }
          if (PropertyDescriptor::IsAccessorDescriptor(&desc) ||
              !desc.writable()) {
            return RedefineIncompatibleProperty(isolate, it->GetName(), value,
                                                should_throw);
          }
  
          PropertyDescriptor value_desc;
          value_desc.set_value(value);
          return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(),
                                               &value_desc, should_throw);
        }
  
        case LookupIterator::NOT_FOUND:
        case LookupIterator::TRANSITION:
          UNREACHABLE();
      }
    }
  
    return AddDataProperty(&own_lookup, value, NONE, should_throw, store_origin);
  }
  
  Maybe<bool> Object::CannotCreateProperty(Isolate* isolate,
                                           Handle<Object> receiver,
                                           Handle<Object> name,
                                           Handle<Object> value,
                                           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, Handle<Object> value,
      Maybe<ShouldThrow> maybe_should_throw) {
    ShouldThrow should_throw = GetShouldThrow(it->isolate(), maybe_should_throw);
    if (it->IsFound() && !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,
                                              Handle<Object> receiver,
                                              Handle<Object> name,
                                              Handle<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, Handle<Object> name, Handle<Object> value,
      Maybe<ShouldThrow> should_throw) {
    RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                   NewTypeError(MessageTemplate::kRedefineDisallowed, name));
  }
  
  Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) {
    DCHECK_IMPLIES(it->GetReceiver()->IsJSProxy(),
                   it->GetName()->IsPrivateName());
    DCHECK_IMPLIES(!it->IsElement() && it->GetName()->IsPrivateName(),
                   it->state() == LookupIterator::DATA);
    Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver());
  
    // Store on the holder which may be hidden behind the receiver.
    DCHECK(it->HolderIsReceiverOrHiddenPrototype());
  
    Handle<Object> to_assign = value;
    // Convert the incoming value to a number for storing into typed arrays.
    if (it->IsElement() && receiver->IsJSObject() &&
        JSObject::cast(*receiver).HasTypedArrayElements()) {
      ElementsKind elements_kind = JSObject::cast(*receiver).GetElementsKind();
      if (elements_kind == BIGINT64_ELEMENTS ||
          elements_kind == BIGUINT64_ELEMENTS) {
        ASSIGN_RETURN_ON_EXCEPTION_VALUE(it->isolate(), to_assign,
                                         BigInt::FromObject(it->isolate(), value),
                                         Nothing<bool>());
        // We have to recheck the length. However, it can only change if the
        // underlying buffer was detached, so just check that.
        if (Handle<JSArrayBufferView>::cast(receiver)->WasDetached()) {
          return Just(true);
          // TODO(neis): According to the spec, this should throw a TypeError.
        }
      } else if (!value->IsNumber() && !value->IsUndefined(it->isolate())) {
        ASSIGN_RETURN_ON_EXCEPTION_VALUE(it->isolate(), to_assign,
                                         Object::ToNumber(it->isolate(), value),
                                         Nothing<bool>());
        // We have to recheck the length. However, it can only change if the
        // underlying buffer was detached, so just check that.
        if (Handle<JSArrayBufferView>::cast(receiver)->WasDetached()) {
          return Just(true);
          // TODO(neis): According to the spec, this should throw a TypeError.
        }
      }
    }
  
    // 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 (FLAG_verify_heap) {
      receiver->HeapObjectVerify(it->isolate());
    }
  #endif
    return Just(true);
  }
  
  Maybe<bool> Object::AddDataProperty(LookupIterator* it, Handle<Object> value,
                                      PropertyAttributes attributes,
                                      Maybe<ShouldThrow> should_throw,
                                      StoreOrigin store_origin) {
    if (!it->GetReceiver()->IsJSReceiver()) {
      return CannotCreateProperty(it->isolate(), it->GetReceiver(), it->GetName(),
                                  value, should_throw);
    }
  
    // Private symbols should be installed on JSProxy using
    // JSProxy::SetPrivateSymbol.
    if (it->GetReceiver()->IsJSProxy() && it->GetName()->IsPrivate() &&
        !it->GetName()->IsPrivateName()) {
      RETURN_FAILURE(it->isolate(), GetShouldThrow(it->isolate(), should_throw),
                     NewTypeError(MessageTemplate::kProxyPrivate));
    }
  
    DCHECK_NE(LookupIterator::INTEGER_INDEXED_EXOTIC, it->state());
  
    Handle<JSReceiver> receiver = it->GetStoreTarget<JSReceiver>();
    DCHECK_IMPLIES(receiver->IsJSProxy(), it->GetName()->IsPrivateName());
    DCHECK_IMPLIES(receiver->IsJSProxy(),
                   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 (receiver->IsJSGlobalProxy()) return Just(true);
  
    Isolate* isolate = it->isolate();
  
    if (it->ExtendingNonExtensible(receiver)) {
      RETURN_FAILURE(
          isolate, GetShouldThrow(it->isolate(), should_throw),
          NewTypeError(MessageTemplate::kObjectNotExtensible, it->GetName()));
    }
  
    if (it->IsElement(*receiver)) {
      if (receiver->IsJSArray()) {
        Handle<JSArray> array = Handle<JSArray>::cast(receiver);
        if (JSArray::WouldChangeReadOnlyLength(array, it->array_index())) {
          RETURN_FAILURE(isolate, GetShouldThrow(it->isolate(), should_throw),
                         NewTypeError(MessageTemplate::kStrictReadOnlyProperty,
                                      isolate->factory()->length_string(),
                                      Object::TypeOf(isolate, array), array));
        }
      }
  
      Handle<JSObject> receiver_obj = Handle<JSObject>::cast(receiver);
      JSObject::AddDataElement(receiver_obj, it->array_index(), value,
                               attributes);
      JSObject::ValidateElements(*receiver_obj);
      return Just(true);
    } else {
      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());
      it->ApplyTransitionToDataProperty(receiver);
  
      // Write the property value.
      it->WriteDataValue(value, true);
  
  #if VERIFY_HEAP
      if (FLAG_verify_heap) {
        receiver->HeapObjectVerify(isolate);
      }
  #endif
    }
  
    return Just(true);
  }
  
  template <class T>
  static int AppendUniqueCallbacks(Isolate* isolate,
                                   Handle<TemplateList> callbacks,
                                   Handle<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--) {
      Handle<AccessorInfo> entry(AccessorInfo::cast(callbacks->get(i)), isolate);
      Handle<Name> key(Name::cast(entry->name()), isolate);
      DCHECK(key->IsUniqueName());
      // 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(Handle<Name> key, Handle<AccessorInfo> entry,
                         int valid_descriptors, Handle<FixedArray> array) {
      for (int i = 0; i < valid_descriptors; i++) {
        if (*key == AccessorInfo::cast(array->get(i)).name()) return true;
      }
      return false;
    }
    static void Insert(Handle<Name> key, Handle<AccessorInfo> entry,
                       int valid_descriptors, Handle<FixedArray> array) {
      DisallowGarbageCollection no_gc;
      array->set(valid_descriptors, *entry);
    }
  };
  
  int AccessorInfo::AppendUnique(Isolate* isolate, Handle<Object> descriptors,
                                 Handle<FixedArray> array,
                                 int valid_descriptors) {
    Handle<TemplateList> callbacks = Handle<TemplateList>::cast(descriptors);
    DCHECK_GE(array->length(), callbacks->length() + valid_descriptors);
    return AppendUniqueCallbacks<FixedArrayAppender>(isolate, callbacks, array,
                                                     valid_descriptors);
  }
  
  void JSProxy::Revoke(Handle<JSProxy> proxy) {
    Isolate* isolate = proxy->GetIsolate();
    // 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(Handle<JSProxy> proxy) {
    Isolate* isolate = proxy->GetIsolate();
    Handle<JSReceiver> object = Handle<JSReceiver>::cast(proxy);
    for (int i = 0; i < JSProxy::kMaxIterationLimit; i++) {
      Handle<JSProxy> proxy = Handle<JSProxy>::cast(object);
      if (proxy->IsRevoked()) {
        isolate->Throw(*isolate->factory()->NewTypeError(
            MessageTemplate::kProxyRevoked,
            isolate->factory()->NewStringFromAsciiChecked("IsArray")));
        return Nothing<bool>();
      }
      object = handle(JSReceiver::cast(proxy->target()), isolate);
      if (object->IsJSArray()) return Just(true);
      if (!object->IsJSProxy()) return Just(false);
    }
  
    // Too deep recursion, throw a RangeError.
    isolate->StackOverflow();
    return Nothing<bool>();
  }
  
  Maybe<bool> JSProxy::HasProperty(Isolate* isolate, Handle<JSProxy> proxy,
                                   Handle<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.
    Handle<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.
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
    // 6. Let trap be ? GetMethod(handler, "has").
    Handle<Object> trap;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap,
        Object::GetMethod(Handle<JSReceiver>::cast(handler),
                          isolate->factory()->has_string()),
        Nothing<bool>());
    // 7. If trap is undefined, then
    if (trap->IsUndefined(isolate)) {
      // 7a. Return target.[[HasProperty]](P).
      return JSReceiver::HasProperty(target, name);
    }
    // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, P»)).
    Handle<Object> trap_result_obj;
    Handle<Object> args[] = {target, name};
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap_result_obj,
        Execution::Call(isolate, trap, handler, arraysize(args), args),
        Nothing<bool>());
    bool boolean_trap_result = trap_result_obj->BooleanValue(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, Handle<Name> name,
                                    Handle<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(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(Handle<JSProxy> proxy, Handle<Name> name,
                                   Handle<Object> value, Handle<Object> receiver,
                                   Maybe<ShouldThrow> should_throw) {
    DCHECK(!name->IsPrivate());
    Isolate* isolate = proxy->GetIsolate();
    STACK_CHECK(isolate, Nothing<bool>());
    Factory* factory = isolate->factory();
    Handle<String> trap_name = factory->set_string();
  
    if (proxy->IsRevoked()) {
      isolate->Throw(
          *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
      return Nothing<bool>();
    }
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
    Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
  
    Handle<Object> trap;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>());
    if (trap->IsUndefined(isolate)) {
      LookupIterator::Key key(isolate, name);
      LookupIterator it(isolate, receiver, key, target);
  
      return Object::SetSuperProperty(&it, value, StoreOrigin::kMaybeKeyed,
                                      should_throw);
    }
  
    Handle<Object> trap_result;
    Handle<Object> args[] = {target, name, value, receiver};
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap_result,
        Execution::Call(isolate, trap, handler, arraysize(args), args),
        Nothing<bool>());
    if (!trap_result->BooleanValue(isolate)) {
      RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                     NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor,
                                  trap_name, name));
    }
  
    MaybeHandle<Object> result =
        JSProxy::CheckGetSetTrapResult(isolate, name, target, value, kSet);
  
    if (result.is_null()) {
      return Nothing<bool>();
    }
    return Just(true);
  }
  
  Maybe<bool> JSProxy::DeletePropertyOrElement(Handle<JSProxy> proxy,
                                               Handle<Name> name,
                                               LanguageMode language_mode) {
    DCHECK(!name->IsPrivate());
    ShouldThrow should_throw =
        is_sloppy(language_mode) ? kDontThrow : kThrowOnError;
    Isolate* isolate = proxy->GetIsolate();
    STACK_CHECK(isolate, Nothing<bool>());
    Factory* factory = isolate->factory();
    Handle<String> trap_name = factory->deleteProperty_string();
  
    if (proxy->IsRevoked()) {
      isolate->Throw(
          *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
      return Nothing<bool>();
    }
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
    Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
  
    Handle<Object> trap;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>());
    if (trap->IsUndefined(isolate)) {
      return JSReceiver::DeletePropertyOrElement(target, name, language_mode);
    }
  
    Handle<Object> trap_result;
    Handle<Object> args[] = {target, name};
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap_result,
        Execution::Call(isolate, trap, handler, arraysize(args), args),
        Nothing<bool>());
    if (!trap_result->BooleanValue(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, Handle<Name> name,
                                       Handle<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(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
  MaybeHandle<JSProxy> JSProxy::New(Isolate* isolate, Handle<Object> target,
                                    Handle<Object> handler) {
    if (!target->IsJSReceiver()) {
      THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject),
                      JSProxy);
    }
    if (!handler->IsJSReceiver()) {
      THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject),
                      JSProxy);
    }
    return isolate->factory()->NewJSProxy(Handle<JSReceiver>::cast(target),
                                          Handle<JSReceiver>::cast(handler));
  }
  
  // static
  MaybeHandle<NativeContext> JSProxy::GetFunctionRealm(Handle<JSProxy> proxy) {
    DCHECK(proxy->map().is_constructor());
    if (proxy->IsRevoked()) {
      THROW_NEW_ERROR(proxy->GetIsolate(),
                      NewTypeError(MessageTemplate::kProxyRevoked),
                      NativeContext);
    }
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()),
                              proxy->GetIsolate());
    return JSReceiver::GetFunctionRealm(target);
  }
  
  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(Handle<Object> value, uint32_t* length) {
    DCHECK(value->IsNumber() || value->IsName());
    if (value->ToArrayLength(length)) return true;
    if (value->IsString()) return String::cast(*value).AsArrayIndex(length);
    return false;
  }
  
  bool PropertyKeyToArrayIndex(Handle<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, Handle<JSArray> o,
                                         Handle<Object> name,
                                         PropertyDescriptor* desc,
                                         Maybe<ShouldThrow> should_throw) {
    // 1. Assert: IsPropertyKey(P) is true. ("P" is |name|.)
    // 2. If P is "length", then:
    // TODO(jkummerow): Check if we need slow string comparison.
    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(old_len_desc.value()->ToArrayLength(&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,
                                      Handle<Object> length_object,
                                      uint32_t* output) {
    // Fast path: check numbers and strings that can be converted directly
    // and unobservably.
    if (length_object->ToArrayLength(output)) return true;
    if (length_object->IsString() &&
        Handle<String>::cast(length_object)->AsArrayIndex(output)) {
      return true;
    }
    // Slow path: follow steps in ES6 9.4.2.4 "ArraySetLength".
    // 3. Let newLen be ToUint32(Desc.[[Value]]).
    Handle<Object> uint32_v;
    if (!Object::ToUint32(isolate, length_object).ToHandle(&uint32_v)) {
      // 4. ReturnIfAbrupt(newLen).
      return false;
    }
    // 5. Let numberLen be ToNumber(Desc.[[Value]]).
    Handle<Object> 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 (uint32_v->Number() != number_v->Number()) {
      Handle<Object> exception =
          isolate->factory()->NewRangeError(MessageTemplate::kInvalidArrayLength);
      isolate->Throw(*exception);
      return false;
    }
    CHECK(uint32_v->ToArrayLength(output));
    return true;
  }
  
  // ES6 9.4.2.4
  // static
  Maybe<bool> JSArray::ArraySetLength(Isolate* isolate, Handle<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_pending_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(old_len_desc.value()->ToArrayLength(&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} case 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()) {
      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.
    JSArray::SetLength(a, new_len);
    // Steps 19d-ii, 20.
    if (!new_writable) {
      PropertyDescriptor readonly;
      readonly.set_writable(false);
      Maybe<bool> success = OrdinaryDefineOwnProperty(
          isolate, a, isolate->factory()->length_string(), &readonly,
          should_throw);
      DCHECK(success.FromJust());
      USE(success);
    }
    uint32_t actual_new_len = 0;
    CHECK(a->length().ToArrayLength(&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::kStrictDeleteProperty,
                       isolate->factory()->NewNumberFromUint(actual_new_len - 1),
                       a));
    }
    return Just(result);
  }
  
  // ES6 9.5.6
  // static
  Maybe<bool> JSProxy::DefineOwnProperty(Isolate* isolate, Handle<JSProxy> proxy,
                                         Handle<Object> key,
                                         PropertyDescriptor* desc,
                                         Maybe<ShouldThrow> should_throw) {
    STACK_CHECK(isolate, Nothing<bool>());
    if (key->IsSymbol() && Handle<Symbol>::cast(key)->IsPrivate()) {
      DCHECK(!Handle<Symbol>::cast(key)->IsPrivateName());
      return JSProxy::SetPrivateSymbol(isolate, proxy, Handle<Symbol>::cast(key),
                                       desc, should_throw);
    }
    Handle<String> trap_name = isolate->factory()->defineProperty_string();
    // 1. Assert: IsPropertyKey(P) is true.
    DCHECK(key->IsName() || key->IsNumber());
    // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
    Handle<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.
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
    // 6. Let trap be ? GetMethod(handler, "defineProperty").
    Handle<Object> trap;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap,
        Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name),
        Nothing<bool>());
    // 7. If trap is undefined, then:
    if (trap->IsUndefined(isolate)) {
      // 7a. Return target.[[DefineOwnProperty]](P, Desc).
      return JSReceiver::DefineOwnProperty(isolate, target, key, desc,
                                           should_throw);
    }
    // 8. Let descObj be FromPropertyDescriptor(Desc).
    Handle<Object> desc_obj = desc->ToObject(isolate);
    // 9. Let booleanTrapResult be
    //    ToBoolean(? Call(trap, handler, «target, P, descObj»)).
    Handle<Name> property_name =
        key->IsName()
            ? Handle<Name>::cast(key)
            : Handle<Name>::cast(isolate->factory()->NumberToString(key));
    // Do not leak private property names.
    DCHECK(!property_name->IsPrivate());
    Handle<Object> trap_result_obj;
    Handle<Object> args[] = {target, property_name, desc_obj};
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap_result_obj,
        Execution::Call(isolate, trap, handler, arraysize(args), args),
        Nothing<bool>());
    // 10. If booleanTrapResult is false, return false.
    if (!trap_result_obj->BooleanValue(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(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, Handle<JSProxy> proxy,
                                        Handle<Symbol> private_name,
                                        PropertyDescriptor* desc,
                                        Maybe<ShouldThrow> should_throw) {
    DCHECK(!private_name->IsPrivateName());
    // 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());
    Handle<Object> value =
        desc->has_value()
            ? desc->value()
            : Handle<Object>::cast(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(kData, DONT_ENUM, PropertyConstness::kMutable);
    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
      Handle<SwissNameDictionary> dict(proxy->property_dictionary_swiss(),
                                       isolate);
      Handle<SwissNameDictionary> result =
          SwissNameDictionary::Add(isolate, dict, private_name, value, details);
      if (!dict.is_identical_to(result)) proxy->SetProperties(*result);
    } else {
      Handle<NameDictionary> dict(proxy->property_dictionary(), isolate);
      Handle<NameDictionary> 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,
                                                Handle<JSProxy> proxy,
                                                Handle<Name> name,
                                                PropertyDescriptor* desc) {
    DCHECK(!name->IsPrivate());
    STACK_CHECK(isolate, Nothing<bool>());
  
    Handle<String> trap_name =
        isolate->factory()->getOwnPropertyDescriptor_string();
    // 1. (Assert)
    // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
    Handle<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.
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
    // 6. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor").
    Handle<Object> trap;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap,
        Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name),
        Nothing<bool>());
    // 7. If trap is undefined, then
    if (trap->IsUndefined(isolate)) {
      // 7a. Return target.[[GetOwnProperty]](P).
      return JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, desc);
    }
    // 8. Let trapResultObj be ? Call(trap, handler, «target, P»).
    Handle<Object> trap_result_obj;
    Handle<Object> args[] = {target, name};
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap_result_obj,
        Execution::Call(isolate, trap, handler, arraysize(args), args),
        Nothing<bool>());
    // 9. If Type(trapResultObj) is neither Object nor Undefined, throw a
    //    TypeError exception.
    if (!trap_result_obj->IsJSReceiver() &&
        !trap_result_obj->IsUndefined(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 (trap_result_obj->IsUndefined(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(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(target);
    MAYBE_RETURN(extensible_target, Nothing<bool>());
    // 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj).
    if (!PropertyDescriptor::ToPropertyDescriptor(isolate, trap_result_obj,
                                                  desc)) {
      DCHECK(isolate->has_pending_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(Handle<JSProxy> proxy,
                                         ShouldThrow should_throw) {
    Isolate* isolate = proxy->GetIsolate();
    STACK_CHECK(isolate, Nothing<bool>());
    Factory* factory = isolate->factory();
    Handle<String> trap_name = factory->preventExtensions_string();
  
    if (proxy->IsRevoked()) {
      isolate->Throw(
          *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
      return Nothing<bool>();
    }
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
    Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
  
    Handle<Object> trap;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>());
    if (trap->IsUndefined(isolate)) {
      return JSReceiver::PreventExtensions(target, should_throw);
    }
  
    Handle<Object> trap_result;
    Handle<Object> args[] = {target};
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap_result,
        Execution::Call(isolate, trap, handler, arraysize(args), args),
        Nothing<bool>());
    if (!trap_result->BooleanValue(isolate)) {
      RETURN_FAILURE(
          isolate, should_throw,
          NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name));
    }
  
    // Enforce the invariant.
    Maybe<bool> target_result = JSReceiver::IsExtensible(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(Handle<JSProxy> proxy) {
    Isolate* isolate = proxy->GetIsolate();
    STACK_CHECK(isolate, Nothing<bool>());
    Factory* factory = isolate->factory();
    Handle<String> trap_name = factory->isExtensible_string();
  
    if (proxy->IsRevoked()) {
      isolate->Throw(
          *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
      return Nothing<bool>();
    }
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
    Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
  
    Handle<Object> trap;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>());
    if (trap->IsUndefined(isolate)) {
      return JSReceiver::IsExtensible(target);
    }
  
    Handle<Object> trap_result;
    Handle<Object> args[] = {target};
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap_result,
        Execution::Call(isolate, trap, handler, arraysize(args), args),
        Nothing<bool>());
  
    // Enforce the invariant.
    Maybe<bool> target_result = JSReceiver::IsExtensible(target);
    MAYBE_RETURN(target_result, Nothing<bool>());
    if (target_result.FromJust() != trap_result->BooleanValue(isolate)) {
      isolate->Throw(
          *factory->NewTypeError(MessageTemplate::kProxyIsExtensibleInconsistent,
                                 factory->ToBoolean(target_result.FromJust())));
      return Nothing<bool>();
    }
    return target_result;
  }
  
  Handle<DescriptorArray> DescriptorArray::CopyUpTo(Isolate* isolate,
                                                    Handle<DescriptorArray> desc,
                                                    int enumeration_index,
                                                    int slack) {
    return DescriptorArray::CopyUpToAddAttributes(isolate, desc,
                                                  enumeration_index, NONE, slack);
  }
  
  Handle<DescriptorArray> DescriptorArray::CopyUpToAddAttributes(
      Isolate* isolate, Handle<DescriptorArray> desc, int enumeration_index,
      PropertyAttributes attributes, int slack) {
    if (enumeration_index + slack == 0) {
      return isolate->factory()->empty_descriptor_array();
    }
  
    int size = enumeration_index;
  
    Handle<DescriptorArray> descriptors =
        DescriptorArray::Allocate(isolate, size, slack);
  
    if (attributes != NONE) {
      for (InternalIndex i : InternalIndex::Range(size)) {
        MaybeObject value_or_field_type = desc->GetValue(i);
        Name key = desc->GetKey(i);
        PropertyDetails details = desc->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.
          HeapObject heap_object;
          if (details.kind() != kAccessor ||
              !(value_or_field_type->GetHeapObjectIfStrong(&heap_object) &&
                heap_object.IsAccessorPair())) {
            mask |= READ_ONLY;
          }
          details = details.CopyAddAttributes(
              static_cast<PropertyAttributes>(attributes & mask));
        }
        descriptors->Set(i, key, value_or_field_type, details);
      }
    } else {
      for (InternalIndex i : InternalIndex::Range(size)) {
        descriptors->CopyFrom(i, *desc);
      }
    }
  
    if (desc->number_of_descriptors() != enumeration_index) descriptors->Sort();
  
    return descriptors;
  }
  
  // Create a new descriptor array with only enumerable, configurable, writeable
  // data properties, but identical field locations.
  Handle<DescriptorArray> DescriptorArray::CopyForFastObjectClone(
      Isolate* isolate, Handle<DescriptorArray> src, int enumeration_index,
      int slack) {
    if (enumeration_index + slack == 0) {
      return isolate->factory()->empty_descriptor_array();
    }
  
    int size = enumeration_index;
    Handle<DescriptorArray> descriptors =
        DescriptorArray::Allocate(isolate, size, slack);
  
    for (InternalIndex i : InternalIndex::Range(size)) {
      Name key = src->GetKey(i);
      PropertyDetails details = src->GetDetails(i);
      Representation new_representation = details.representation();
  
      DCHECK(!key.IsPrivateName());
      DCHECK(details.IsEnumerable());
      DCHECK_EQ(details.kind(), kData);
      // If the new representation is an in-place changeable field, make it
      // generic as possible (under in-place changes) to avoid type confusion if
      // the source representation changes after this feedback has been collected.
      MaybeObject type = src->GetValue(i);
      if (details.location() == PropertyLocation::kField) {
        type = MaybeObject::FromObject(FieldType::Any());
        // TODO(bmeurer,ishell): Igor suggested to use some kind of dynamic
        // checks in the fast-path for CloneObjectIC instead to avoid the
        // need to generalize the descriptors here. That will also enable
        // us to skip the defensive copying of the target map whenever a
        // CloneObjectIC misses.
        new_representation = new_representation.MostGenericInPlaceChange();
      }
  
      // Ensure the ObjectClone property details are NONE, and that all source
      // details did not contain DONT_ENUM.
      PropertyDetails new_details(kData, NONE, details.location(),
                                  details.constness(), new_representation,
                                  details.field_index());
  
      descriptors->Set(i, key, type, new_details);
    }
  
    descriptors->Sort();
  
    return descriptors;
  }
  
  bool DescriptorArray::IsEqualUpTo(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;
  }
  
  Handle<FixedArray> FixedArray::SetAndGrow(Isolate* isolate,
                                            Handle<FixedArray> array, int index,
                                            Handle<Object> value) {
    if (index < array->length()) {
      array->set(index, *value);
      return array;
    }
    int capacity = array->length();
    do {
      capacity = JSObject::NewElementsCapacity(capacity);
    } while (capacity <= index);
    Handle<FixedArray> new_array =
        isolate->factory()->NewUninitializedFixedArray(capacity);
    array->CopyTo(0, *new_array, 0, array->length());
    new_array->FillWithHoles(array->length(), new_array->length());
    new_array->set(index, *value);
    return new_array;
  }
  
  Handle<FixedArray> FixedArray::ShrinkOrEmpty(Isolate* isolate,
                                               Handle<FixedArray> array,
                                               int new_length) {
    if (new_length == 0) {
      return array->GetReadOnlyRoots().empty_fixed_array_handle();
    } else {
      array->Shrink(isolate, new_length);
      return array;
    }
  }
  
  void FixedArray::Shrink(Isolate* isolate, int new_length) {
    DCHECK(0 < new_length && new_length <= length());
    if (new_length < length()) {
      isolate->heap()->RightTrimFixedArray(*this, length() - new_length);
    }
  }
  
  void FixedArray::CopyTo(int pos, FixedArray dest, int dest_pos, int len) const {
    DisallowGarbageCollection no_gc;
    // Return early if len == 0 so that we don't try to read the write barrier off
    // a canonical read-only empty fixed array.
    if (len == 0) return;
    WriteBarrierMode mode = dest.GetWriteBarrierMode(no_gc);
    for (int index = 0; index < len; index++) {
      dest.set(dest_pos + index, get(pos + index), mode);
    }
  }
  
  // static
  Handle<ArrayList> ArrayList::Add(Isolate* isolate, Handle<ArrayList> array,
                                   Handle<Object> obj) {
    int length = array->Length();
    array = EnsureSpace(isolate, array, length + 1);
    // Check that GC didn't remove elements from the array.
    DCHECK_EQ(array->Length(), length);
    array->Set(length, *obj);
    array->SetLength(length + 1);
    return array;
  }
  
  // static
  Handle<ArrayList> ArrayList::Add(Isolate* isolate, Handle<ArrayList> array,
                                   Handle<Object> obj1, Handle<Object> obj2) {
    int length = array->Length();
    array = EnsureSpace(isolate, array, length + 2);
    // Check that GC didn't remove elements from the array.
    DCHECK_EQ(array->Length(), length);
    array->Set(length, *obj1);
    array->Set(length + 1, *obj2);
    array->SetLength(length + 2);
    return array;
  }
  
  // static
  Handle<ArrayList> ArrayList::New(Isolate* isolate, int size) {
    Handle<FixedArray> fixed_array =
        isolate->factory()->NewFixedArray(size + kFirstIndex);
    fixed_array->set_map_no_write_barrier(
        ReadOnlyRoots(isolate).array_list_map());
    Handle<ArrayList> result = Handle<ArrayList>::cast(fixed_array);
    result->SetLength(0);
    return result;
  }
  
  Handle<FixedArray> ArrayList::Elements(Isolate* isolate,
                                         Handle<ArrayList> array) {
    int length = array->Length();
    Handle<FixedArray> result = isolate->factory()->NewFixedArray(length);
    // Do not copy the first entry, i.e., the length.
    array->CopyTo(kFirstIndex, *result, 0, length);
    return result;
  }
  
  namespace {
  
  Handle<FixedArray> EnsureSpaceInFixedArray(Isolate* isolate,
                                             Handle<FixedArray> array,
                                             int length) {
    int capacity = array->length();
    if (capacity < length) {
      int new_capacity = length;
      new_capacity = new_capacity + std::max(new_capacity / 2, 2);
      int grow_by = new_capacity - capacity;
      array = isolate->factory()->CopyFixedArrayAndGrow(array, grow_by);
    }
    return array;
  }
  
  }  // namespace
  
  // static
  Handle<ArrayList> ArrayList::EnsureSpace(Isolate* isolate,
                                           Handle<ArrayList> array, int length) {
    const bool empty = (array->length() == 0);
    Handle<FixedArray> ret =
        EnsureSpaceInFixedArray(isolate, array, kFirstIndex + length);
    if (empty) {
      ret->set_map_no_write_barrier(array->GetReadOnlyRoots().array_list_map());
  
      Handle<ArrayList>::cast(ret)->SetLength(0);
    }
    return Handle<ArrayList>::cast(ret);
  }
  
  // static
  Handle<WeakArrayList> WeakArrayList::AddToEnd(Isolate* isolate,
                                                Handle<WeakArrayList> array,
                                                const MaybeObjectHandle& value) {
    int length = array->length();
    array = EnsureSpace(isolate, array, length + 1);
    // Reload length; GC might have removed elements from the array.
    length = array->length();
    array->Set(length, *value);
    array->set_length(length + 1);
    return array;
  }
  
  Handle<WeakArrayList> WeakArrayList::AddToEnd(Isolate* isolate,
                                                Handle<WeakArrayList> array,
                                                const MaybeObjectHandle& value1,
                                                const MaybeObjectHandle& value2) {
    int length = array->length();
    array = EnsureSpace(isolate, array, length + 2);
    // Reload length; GC might have removed elements from the array.
    length = array->length();
    array->Set(length, *value1);
    array->Set(length + 1, *value2);
    array->set_length(length + 2);
    return array;
  }
  
  // static
  Handle<WeakArrayList> WeakArrayList::Append(Isolate* isolate,
                                              Handle<WeakArrayList> array,
                                              const MaybeObjectHandle& value,
                                              AllocationType allocation) {
    int length = array->length();
  
    if (length < array->capacity()) {
      array->Set(length, *value);
      array->set_length(length + 1);
      return array;
    }
  
    // Not enough space in the array left, either grow, shrink or
    // compact the array.
    int new_length = array->CountLiveElements() + 1;
  
    bool shrink = new_length < length / 4;
    bool grow = 3 * (length / 4) < new_length;
  
    if (shrink || grow) {
      // Grow or shrink array and compact out-of-place.
      int new_capacity = CapacityForLength(new_length);
      array = isolate->factory()->CompactWeakArrayList(array, new_capacity,
                                                       allocation);
  
    } else {
      // Perform compaction in the current array.
      array->Compact(isolate);
    }
  
    // Now append value to the array, there should always be enough space now.
    DCHECK_LT(array->length(), array->capacity());
  
    // Reload length, allocation might have killed some weak refs.
    int index = array->length();
    array->Set(index, *value);
    array->set_length(index + 1);
    return array;
  }
  
  void WeakArrayList::Compact(Isolate* isolate) {
    int length = this->length();
    int new_length = 0;
  
    for (int i = 0; i < length; i++) {
      MaybeObject value = Get(isolate, i);
  
      if (!value->IsCleared()) {
        if (new_length != i) {
          Set(new_length, value);
        }
        ++new_length;
      }
    }
  
    set_length(new_length);
  }
  
  bool WeakArrayList::IsFull() { return length() == capacity(); }
  
  // static
  Handle<WeakArrayList> WeakArrayList::EnsureSpace(Isolate* isolate,
                                                   Handle<WeakArrayList> array,
                                                   int length,
                                                   AllocationType allocation) {
    int capacity = array->capacity();
    if (capacity < length) {
      int grow_by = CapacityForLength(length) - capacity;
      array = isolate->factory()->CopyWeakArrayListAndGrow(array, grow_by,
                                                           allocation);
    }
    return array;
  }
  
  int WeakArrayList::CountLiveWeakReferences() const {
    int live_weak_references = 0;
    for (int i = 0; i < length(); i++) {
      if (Get(i)->IsWeak()) {
        ++live_weak_references;
      }
    }
    return live_weak_references;
  }
  
  int WeakArrayList::CountLiveElements() const {
    int non_cleared_objects = 0;
    for (int i = 0; i < length(); i++) {
      if (!Get(i)->IsCleared()) {
        ++non_cleared_objects;
      }
    }
    return non_cleared_objects;
  }
  
  bool WeakArrayList::RemoveOne(const MaybeObjectHandle& value) {
    if (length() == 0) return false;
    // Optimize for the most recently added element to be removed again.
    MaybeObject cleared_weak_ref =
        HeapObjectReference::ClearedValue(GetIsolate());
    int last_index = length() - 1;
    for (int i = last_index; i >= 0; --i) {
      if (Get(i) == *value) {
        // Move the last element into the this slot (or no-op, if this is the
        // last slot).
        Set(i, Get(last_index));
        Set(last_index, cleared_weak_ref);
        set_length(last_index);
        return true;
      }
    }
    return false;
  }
  
  // static
  Handle<WeakArrayList> PrototypeUsers::Add(Isolate* isolate,
                                            Handle<WeakArrayList> array,
                                            Handle<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, HeapObjectReference::Weak(*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, HeapObjectReference::Weak(*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, HeapObjectReference::Weak(*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, HeapObjectReference::Weak(*value));
    array->set_length(length + 1);
    if (assigned_index != nullptr) *assigned_index = length;
    return array;
  }
  
  // static
  void PrototypeUsers::ScanForEmptySlots(WeakArrayList array) {
    for (int i = kFirstIndex; i < array.length(); i++) {
      if (array.Get(i)->IsCleared()) {
        PrototypeUsers::MarkSlotEmpty(array, i);
      }
    }
  }
  
  WeakArrayList PrototypeUsers::Compact(Handle<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;
    }
  
    Handle<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++) {
      MaybeObject element = array->Get(i);
      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;
  }
  
  Handle<RegExpMatchInfo> RegExpMatchInfo::New(Isolate* isolate,
                                               int capture_count) {
    Handle<RegExpMatchInfo> match_info = isolate->factory()->NewRegExpMatchInfo();
    return ReserveCaptures(isolate, match_info, capture_count);
  }
  
  Handle<RegExpMatchInfo> RegExpMatchInfo::ReserveCaptures(
      Isolate* isolate, Handle<RegExpMatchInfo> match_info, int capture_count) {
    DCHECK_GE(match_info->length(), kLastMatchOverhead);
  
    int capture_register_count =
        JSRegExp::RegistersForCaptureCount(capture_count);
    const int required_length = kFirstCaptureIndex + capture_register_count;
    Handle<RegExpMatchInfo> result = Handle<RegExpMatchInfo>::cast(
        EnsureSpaceInFixedArray(isolate, match_info, required_length));
    result->SetNumberOfCaptureRegisters(capture_register_count);
    return result;
  }
  
  template <typename LocalIsolate>
  Handle<DescriptorArray> DescriptorArray::Allocate(LocalIsolate* 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(EnumCache empty_enum_cache,
                                   HeapObject undefined_value,
                                   int nof_descriptors, int slack) {
    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_number_of_marked_descriptors(0);
    set_filler16bits(0);
    set_enum_cache(empty_enum_cache, SKIP_WRITE_BARRIER);
    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);
  }
  
  // static
  void DescriptorArray::InitializeOrChangeEnumCache(
      Handle<DescriptorArray> descriptors, Isolate* isolate,
      Handle<FixedArray> keys, Handle<FixedArray> indices) {
    EnumCache enum_cache = descriptors->enum_cache();
    if (enum_cache == ReadOnlyRoots(isolate).empty_enum_cache()) {
      enum_cache = *isolate->factory()->NewEnumCache(keys, indices);
      descriptors->set_enum_cache(enum_cache);
    } else {
      enum_cache.set_keys(*keys);
      enum_cache.set_indices(*indices);
    }
  }
  
  void DescriptorArray::CopyFrom(InternalIndex index, DescriptorArray src) {
    PropertyDetails details = src.GetDetails(index);
    Set(index, src.GetKey(index), src.GetValue(index), details);
  }
  
  void DescriptorArray::Sort() {
    // In-place heap sort.
    int 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.
    const 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();
      const int 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());
  }
  
  int16_t DescriptorArray::UpdateNumberOfMarkedDescriptors(
      unsigned mark_compact_epoch, int16_t new_marked) {
    STATIC_ASSERT(kMaxNumberOfDescriptors <=
                  NumberOfMarkedDescriptors::kMaxNumberOfMarkedDescriptors);
    int16_t old_raw_marked = raw_number_of_marked_descriptors();
    int16_t old_marked =
        NumberOfMarkedDescriptors::decode(mark_compact_epoch, old_raw_marked);
    int16_t new_raw_marked =
        NumberOfMarkedDescriptors::encode(mark_compact_epoch, new_marked);
    while (old_marked < new_marked) {
      int16_t actual_raw_marked = CompareAndSwapRawNumberOfMarkedDescriptors(
          old_raw_marked, new_raw_marked);
      if (actual_raw_marked == old_raw_marked) {
        break;
      }
      old_raw_marked = actual_raw_marked;
      old_marked =
          NumberOfMarkedDescriptors::decode(mark_compact_epoch, old_raw_marked);
    }
    return old_marked;
  }
  
  Handle<AccessorPair> AccessorPair::Copy(Isolate* isolate,
                                          Handle<AccessorPair> pair) {
    Handle<AccessorPair> copy = isolate->factory()->NewAccessorPair();
    copy->set_getter(pair->getter());
    copy->set_setter(pair->setter());
    return copy;
  }
  
  Handle<Object> AccessorPair::GetComponent(Isolate* isolate,
                                            Handle<NativeContext> native_context,
                                            Handle<AccessorPair> accessor_pair,
                                            AccessorComponent component) {
    Handle<Object> accessor(accessor_pair->get(component), isolate);
    if (accessor->IsFunctionTemplateInfo()) {
      auto function = ApiNatives::InstantiateFunction(
                          isolate, native_context,
                          Handle<FunctionTemplateInfo>::cast(accessor))
                          .ToHandleChecked();
      accessor_pair->set(component, *function);
      return function;
    }
    if (accessor->IsNull(isolate)) {
      return isolate->factory()->undefined_value();
    }
    return accessor;
  }
  
  #ifdef DEBUG
  bool DescriptorArray::IsEqualTo(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
  MaybeHandle<String> Name::ToFunctionName(Isolate* isolate, Handle<Name> name) {
    if (name->IsString()) return Handle<String>::cast(name);
    // ES6 section 9.2.11 SetFunctionName, step 4.
    Handle<Object> description(Handle<Symbol>::cast(name)->description(),
                               isolate);
    if (description->IsUndefined(isolate)) {
      return isolate->factory()->empty_string();
    }
    IncrementalStringBuilder builder(isolate);
    builder.AppendCharacter('[');
    builder.AppendString(Handle<String>::cast(description));
    builder.AppendCharacter(']');
    return builder.Finish();
  }
  
  // static
  MaybeHandle<String> Name::ToFunctionName(Isolate* isolate, Handle<Name> name,
                                           Handle<String> prefix) {
    Handle<String> name_string;
    ASSIGN_RETURN_ON_EXCEPTION(isolate, name_string,
                               ToFunctionName(isolate, name), String);
    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*);  // NOLINT
  }
  
  // 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_;
    }
  }
  
  namespace {
  
  template <typename sinkchar>
  void WriteFixedArrayToFlat(FixedArray fixed_array, int length, String separator,
                             sinkchar* sink, int sink_length) {
    DisallowGarbageCollection no_gc;
    CHECK_GT(length, 0);
    CHECK_LE(length, fixed_array.length());
  #ifdef DEBUG
    sinkchar* sink_end = sink + sink_length;
  #endif
  
    const int 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 = SeqOneByteString::cast(separator).GetChars(no_gc)[0];
    }
  
    uint32_t num_separators = 0;
    for (int i = 0; i < length; i++) {
      Object element = fixed_array.get(i);
      const bool element_is_separator_sequence = element.IsSmi();
  
      // If element is a Smi, it represents the number of separators to write.
      if (V8_UNLIKELY(element_is_separator_sequence)) {
        CHECK(element.ToUint32(&num_separators));
        // 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 == 0 || i == length - 1 || num_separators > 1);
      }
  
      // 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;
          }
        }
      }
  
      if (V8_UNLIKELY(element_is_separator_sequence)) {
        num_separators = 0;
      } else {
        DCHECK(element.IsString());
        String string = String::cast(element);
        const int 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;
      }
    }
  
    // Verify we have written to the end of the sink.
    DCHECK_EQ(sink, sink_end);
  }
  
  }  // namespace
  
  // static
  Address JSArray::ArrayJoinConcatToSequentialString(Isolate* isolate,
                                                     Address raw_fixed_array,
                                                     intptr_t length,
                                                     Address raw_separator,
                                                     Address raw_dest) {
    DisallowGarbageCollection no_gc;
    DisallowJavascriptExecution no_js(isolate);
    FixedArray fixed_array = FixedArray::cast(Object(raw_fixed_array));
    String separator = String::cast(Object(raw_separator));
    String dest = String::cast(Object(raw_dest));
    DCHECK(fixed_array.IsFixedArray());
    DCHECK(StringShape(dest).IsSequentialOneByte() ||
           StringShape(dest).IsSequentialTwoByte());
  
    if (StringShape(dest).IsSequentialOneByte()) {
      WriteFixedArrayToFlat(fixed_array, static_cast<int>(length), separator,
                            SeqOneByteString::cast(dest).GetChars(no_gc),
                            dest.length());
    } else {
      DCHECK(StringShape(dest).IsSequentialTwoByte());
      WriteFixedArrayToFlat(fixed_array, static_cast<int>(length), separator,
                            SeqTwoByteString::cast(dest).GetChars(no_gc),
                            dest.length());
    }
    return dest.ptr();
  }
  
  uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
    // For array indexes mix the length into the hash as an array index could
    // be zero.
    DCHECK_GT(length, 0);
    DCHECK_LE(length, String::kMaxArrayIndexSize);
    DCHECK(TenToThe(String::kMaxCachedArrayIndexLength) <
           (1 << String::kArrayIndexValueBits));
  
    value <<= String::ArrayIndexValueBits::kShift;
    value |= length << String::ArrayIndexLengthBits::kShift;
  
    DCHECK_EQ(value & String::kIsNotIntegerIndexMask, 0);
    DCHECK_EQ(length <= String::kMaxCachedArrayIndexLength,
              Name::ContainsCachedArrayIndex(value));
    return value;
  }
  
  Handle<Object> CacheInitialJSArrayMaps(Isolate* isolate,
                                         Handle<Context> native_context,
                                         Handle<Map> initial_map) {
    // Replace all of the cached initial array maps in the native context with
    // the appropriate transitioned elements kind maps.
    Handle<Map> current_map = initial_map;
    ElementsKind kind = current_map->elements_kind();
    DCHECK_EQ(GetInitialFastElementsKind(), kind);
    native_context->set(Context::ArrayMapIndex(kind), *current_map);
    for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1;
         i < kFastElementsKindCount; ++i) {
      Handle<Map> new_map;
      ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i);
      Map maybe_elements_transition = current_map->ElementsTransitionMap(isolate);
      if (!maybe_elements_transition.is_null()) {
        new_map = handle(maybe_elements_transition, isolate);
      } else {
        new_map = Map::CopyAsElementsKind(isolate, current_map, next_kind,
                                          INSERT_TRANSITION);
      }
      DCHECK_EQ(next_kind, new_map->elements_kind());
      native_context->set(Context::ArrayMapIndex(next_kind), *new_map);
      current_map = new_map;
    }
    return initial_map;
  }
  
  STATIC_ASSERT_FIELD_OFFSETS_EQUAL(HeapNumber::kValueOffset,
                                    Oddball::kToNumberRawOffset);
  
  void Oddball::Initialize(Isolate* isolate, Handle<Oddball> oddball,
                           const char* to_string, Handle<Object> to_number,
                           const char* type_of, byte kind) {
    Handle<String> internalized_to_string =
        isolate->factory()->InternalizeUtf8String(to_string);
    Handle<String> internalized_type_of =
        isolate->factory()->InternalizeUtf8String(type_of);
    if (to_number->IsHeapNumber()) {
      oddball->set_to_number_raw_as_bits(
          Handle<HeapNumber>::cast(to_number)->value_as_bits());
    } else {
      oddball->set_to_number_raw(to_number->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
  int Script::GetEvalPosition(Isolate* isolate, Handle<Script> script) {
    DCHECK(script->compilation_type() == Script::COMPILATION_TYPE_EVAL);
    int position = script->eval_from_position();
    if (position < 0) {
      // Due to laziness, the position may not have been translated from code
      // offset yet, which would be encoded as negative integer. In that case,
      // translate and set the position.
      if (!script->has_eval_from_shared()) {
        position = 0;
      } else {
        Handle<SharedFunctionInfo> shared =
            handle(script->eval_from_shared(), isolate);
        SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared);
        position = shared->abstract_code(isolate).SourcePosition(-position);
      }
      DCHECK_GE(position, 0);
      script->set_eval_from_position(position);
    }
    return position;
  }
  
  template <typename LocalIsolate>
  // static
  void Script::InitLineEnds(LocalIsolate* isolate, Handle<Script> script) {
    if (!script->line_ends().IsUndefined(isolate)) return;
  #if V8_ENABLE_WEBASSEMBLY
    DCHECK(script->type() != Script::TYPE_WASM ||
           script->source_mapping_url().IsString());
  #endif  // V8_ENABLE_WEBASSEMBLY
  
    Object src_obj = script->source();
    if (!src_obj.IsString()) {
      DCHECK(src_obj.IsUndefined(isolate));
      script->set_line_ends(ReadOnlyRoots(isolate).empty_fixed_array());
    } else {
      DCHECK(src_obj.IsString());
      Handle<String> src(String::cast(src_obj), isolate);
      Handle<FixedArray> array = String::CalculateLineEnds(isolate, src, true);
      script->set_line_ends(*array);
    }
  
    DCHECK(script->line_ends().IsFixedArray());
  }
  
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void Script::InitLineEnds(
      Isolate* isolate, Handle<Script> script);
  template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void Script::InitLineEnds(
      LocalIsolate* isolate, Handle<Script> script);
  
  bool Script::GetPositionInfo(Handle<Script> script, int position,
                               PositionInfo* info, OffsetFlag offset_flag) {
    bool init_line_ends = true;
  #if V8_ENABLE_WEBASSEMBLY
    // For wasm, we do not create an artificial line_ends array, but do the
    // translation directly.
    init_line_ends = script->type() != Script::TYPE_WASM;
  #endif  // V8_ENABLE_WEBASSEMBLY
    if (init_line_ends) InitLineEnds(script->GetIsolate(), script);
    return script->GetPositionInfo(position, info, offset_flag);
  }
  
  bool Script::IsUserJavaScript() const { return type() == Script::TYPE_NORMAL; }
  
  #if V8_ENABLE_WEBASSEMBLY
  bool Script::ContainsAsmModule() {
    DisallowGarbageCollection no_gc;
    SharedFunctionInfo::ScriptIterator iter(this->GetIsolate(), *this);
    for (SharedFunctionInfo info = iter.Next(); !info.is_null();
         info = iter.Next()) {
      if (info.HasAsmWasmData()) return true;
    }
    return false;
  }
  #endif  // V8_ENABLE_WEBASSEMBLY
  
  namespace {
  
  template <typename Char>
  bool GetPositionInfoSlowImpl(const Vector<Char>& source, int position,
                               Script::PositionInfo* info) {
    if (position < 0) {
      position = 0;
    }
    int line = 0;
    const auto begin = std::cbegin(source);
    const auto end = std::cend(source);
    for (auto line_begin = begin; line_begin < end;) {
      const auto line_end = std::find(line_begin, end, '\n');
      if (position <= (line_end - begin)) {
        info->line = line;
        info->column = static_cast<int>((begin + position) - line_begin);
        info->line_start = static_cast<int>(line_begin - begin);
        info->line_end = static_cast<int>(line_end - begin);
        return true;
      }
      ++line;
      line_begin = line_end + 1;
    }
    return false;
  }
  bool GetPositionInfoSlow(const Script script, int position,
                           const DisallowGarbageCollection& no_gc,
                           Script::PositionInfo* info) {
    if (!script.source().IsString()) {
      return false;
    }
    auto source = String::cast(script.source());
    const auto flat = source.GetFlatContent(no_gc);
    return flat.IsOneByte()
               ? GetPositionInfoSlowImpl(flat.ToOneByteVector(), position, info)
               : GetPositionInfoSlowImpl(flat.ToUC16Vector(), position, info);
  }
  
  }  // namespace
  
  bool Script::GetPositionInfo(int position, PositionInfo* info,
                               OffsetFlag offset_flag) const {
    DisallowGarbageCollection no_gc;
  
  #if V8_ENABLE_WEBASSEMBLY
    // For wasm, we use the byte offset as the column.
    if (type() == Script::TYPE_WASM) {
      DCHECK_LE(0, position);
      wasm::NativeModule* native_module = wasm_native_module();
      const wasm::WasmModule* module = native_module->module();
      if (module->functions.size() == 0) return false;
      info->line = 0;
      info->column = position;
      info->line_start = module->functions[0].code.offset();
      info->line_end = module->functions.back().code.end_offset();
      return true;
    }
  #endif  // V8_ENABLE_WEBASSEMBLY
  
    if (line_ends().IsUndefined()) {
      // Slow mode: we do not have line_ends. We have to iterate through source.
      if (!GetPositionInfoSlow(*this, position, no_gc, info)) {
        return false;
      }
    } else {
      DCHECK(line_ends().IsFixedArray());
      FixedArray ends = FixedArray::cast(line_ends());
  
      const int ends_len = ends.length();
      if (ends_len == 0) return false;
  
      // Return early on invalid positions. Negative positions behave as if 0 was
      // passed, and positions beyond the end of the script return as failure.
      if (position < 0) {
        position = 0;
      } else if (position > Smi::ToInt(ends.get(ends_len - 1))) {
        return false;
      }
  
      // Determine line number by doing a binary search on the line ends array.
      if (Smi::ToInt(ends.get(0)) >= position) {
        info->line = 0;
        info->line_start = 0;
        info->column = position;
      } else {
        int left = 0;
        int right = ends_len - 1;
  
        while (right > 0) {
          DCHECK_LE(left, right);
          const int mid = (left + right) / 2;
          if (position > Smi::ToInt(ends.get(mid))) {
            left = mid + 1;
          } else if (position <= Smi::ToInt(ends.get(mid - 1))) {
            right = mid - 1;
          } else {
            info->line = mid;
            break;
          }
        }
        DCHECK(Smi::ToInt(ends.get(info->line)) >= position &&
               Smi::ToInt(ends.get(info->line - 1)) < position);
        info->line_start = Smi::ToInt(ends.get(info->line - 1)) + 1;
        info->column = position - info->line_start;
      }
  
      // Line end is position of the linebreak character.
      info->line_end = Smi::ToInt(ends.get(info->line));
      if (info->line_end > 0) {
        DCHECK(source().IsString());
        String src = String::cast(source());
        if (src.length() >= info->line_end &&
            src.Get(info->line_end - 1) == '\r') {
          info->line_end--;
        }
      }
    }
  
    // Add offsets if requested.
    if (offset_flag == WITH_OFFSET) {
      if (info->line == 0) {
        info->column += column_offset();
      }
      info->line += line_offset();
    }
  
    return true;
  }
  
  int Script::GetColumnNumber(Handle<Script> script, int code_pos) {
    PositionInfo info;
    GetPositionInfo(script, code_pos, &info, WITH_OFFSET);
    return info.column;
  }
  
  int Script::GetColumnNumber(int code_pos) const {
    PositionInfo info;
    GetPositionInfo(code_pos, &info, WITH_OFFSET);
    return info.column;
  }
  
  int Script::GetLineNumber(Handle<Script> script, int code_pos) {
    PositionInfo info;
    GetPositionInfo(script, code_pos, &info, WITH_OFFSET);
    return info.line;
  }
  
  int Script::GetLineNumber(int code_pos) const {
    PositionInfo info;
    GetPositionInfo(code_pos, &info, WITH_OFFSET);
    return info.line;
  }
  
  Object Script::GetNameOrSourceURL() {
    // Keep in sync with ScriptNameOrSourceURL in messages.js.
    if (!source_url().IsUndefined()) return source_url();
    return name();
  }
  
  template <typename LocalIsolate>
  MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo(
      Handle<Script> script, LocalIsolate* isolate,
      FunctionLiteral* function_literal) {
    int function_literal_id = function_literal->function_literal_id();
    if V8_UNLIKELY (script->type() == Script::TYPE_WEB_SNAPSHOT &&
                    function_literal_id >= script->shared_function_info_count()) {
      return FindWebSnapshotSharedFunctionInfo(script, isolate, function_literal);
    }
  
    CHECK_NE(function_literal_id, kFunctionLiteralIdInvalid);
    // If this check fails, the problem is most probably the function id
    // renumbering done by AstFunctionLiteralIdReindexer; in particular, that
    // AstTraversalVisitor doesn't recurse properly in the construct which
    // triggers the mismatch.
    CHECK_LT(function_literal_id, script->shared_function_info_count());
    MaybeObject shared = script->shared_function_infos().Get(function_literal_id);
    HeapObject heap_object;
    if (!shared->GetHeapObject(&heap_object) ||
        heap_object.IsUndefined(isolate)) {
      return MaybeHandle<SharedFunctionInfo>();
    }
    return handle(SharedFunctionInfo::cast(heap_object), isolate);
  }
  template MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo(
      Handle<Script> script, Isolate* isolate, FunctionLiteral* function_literal);
  template MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo(
      Handle<Script> script, LocalIsolate* isolate,
      FunctionLiteral* function_literal);
  
  MaybeHandle<SharedFunctionInfo> Script::FindWebSnapshotSharedFunctionInfo(
      Handle<Script> script, Isolate* isolate,
      FunctionLiteral* function_literal) {
    // We might be able to de-dupe the SFI against a SFI that was
    // created when deserializing the snapshot (or when calling a function which
    // was included in the snapshot). In that case, we can find it based on the
    // start position in shared_function_info_table.
    Handle<ObjectHashTable> shared_function_info_table = handle(
        ObjectHashTable::cast(script->shared_function_info_table()), isolate);
    {
      DisallowHeapAllocation no_gc;
      Object index_object = shared_function_info_table->Lookup(
          handle(Smi::FromInt(function_literal->start_position()), isolate));
      if (!index_object.IsTheHole()) {
        int index = Smi::cast(index_object).value();
        DCHECK_LT(index, script->shared_function_info_count());
        MaybeObject maybe_shared = script->shared_function_infos().Get(index);
        HeapObject heap_object;
        if (!maybe_shared->GetHeapObject(&heap_object)) {
          // We found the correct location but it's not filled in (e.g., the weak
          // pointer to the SharedFunctionInfo has been cleared). Record the
          // location in the FunctionLiteral, so that it will be refilled later.
          // SharedFunctionInfo::SetScript will write the SharedFunctionInfo in
          // the shared_function_infos.
          function_literal->set_function_literal_id(index);
          return MaybeHandle<SharedFunctionInfo>();
        }
        SharedFunctionInfo shared = SharedFunctionInfo::cast(heap_object);
        DCHECK_EQ(shared.StartPosition(), function_literal->start_position());
        DCHECK_EQ(shared.EndPosition(), function_literal->end_position());
        return handle(shared, isolate);
      }
    }
  
    // It's possible that FunctionLiterals which were processed before this one
    // were deduplicated against existing ones. Decrease function_literal_id to
    // avoid holes in shared_function_infos.
    int old_length = script->shared_function_info_count();
    int function_literal_id = old_length;
    function_literal->set_function_literal_id(function_literal_id);
  
    // Also add to shared_function_info_table.
    shared_function_info_table = ObjectHashTable::Put(
        shared_function_info_table,
        handle(Smi::FromInt(function_literal->start_position()), isolate),
        handle(Smi::FromInt(function_literal_id), isolate));
    script->set_shared_function_info_table(*shared_function_info_table);
  
    // Grow shared_function_infos if needed (we don't know the correct amount of
    // space needed upfront).
    int new_length = old_length + 1;
    Handle<WeakFixedArray> old_infos =
        handle(script->shared_function_infos(), isolate);
    if (new_length > old_infos->length()) {
      int capacity = WeakArrayList::CapacityForLength(new_length);
      Handle<WeakFixedArray> new_infos(
          isolate->factory()->NewWeakFixedArray(capacity, AllocationType::kOld));
      new_infos->CopyElements(isolate, 0, *old_infos, 0, old_length,
                              WriteBarrierMode::UPDATE_WRITE_BARRIER);
      script->set_shared_function_infos(*new_infos);
    }
    return MaybeHandle<SharedFunctionInfo>();
  }
  
  MaybeHandle<SharedFunctionInfo> Script::FindWebSnapshotSharedFunctionInfo(
      Handle<Script> script, LocalIsolate* isolate,
      FunctionLiteral* function_literal) {
    // Off-thread serialization of web snapshots is not implemented.
    UNREACHABLE();
  }
  
  Script::Iterator::Iterator(Isolate* isolate)
      : iterator_(isolate->heap()->script_list()) {}
  
  Script Script::Iterator::Next() {
    Object o = iterator_.Next();
    if (o != Object()) {
      return Script::cast(o);
    }
    return Script();
  }
  
  // static
  void JSArray::Initialize(Handle<JSArray> array, int capacity, int length) {
    DCHECK_GE(capacity, 0);
    array->GetIsolate()->factory()->NewJSArrayStorage(
        array, length, capacity, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE);
  }
  
  void JSArray::SetLength(Handle<JSArray> array, uint32_t new_length) {
    // We should never end in here with a pixel or external array.
    DCHECK(array->AllowsSetLength());
    if (array->SetLengthWouldNormalize(new_length)) {
      JSObject::NormalizeElements(array);
    }
    array->GetElementsAccessor()->SetLength(array, new_length);
  }
  
  // ES6: 9.5.2 [[SetPrototypeOf]] (V)
  // static
  Maybe<bool> JSProxy::SetPrototype(Handle<JSProxy> proxy, Handle<Object> value,
                                    bool from_javascript,
                                    ShouldThrow should_throw) {
    Isolate* isolate = proxy->GetIsolate();
    STACK_CHECK(isolate, Nothing<bool>());
    Handle<Name> trap_name = isolate->factory()->setPrototypeOf_string();
    // 1. Assert: Either Type(V) is Object or Type(V) is Null.
    DCHECK(value->IsJSReceiver() || value->IsNull(isolate));
    // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
    Handle<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.
    Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
    // 6. Let trap be ? GetMethod(handler, "getPrototypeOf").
    Handle<Object> trap;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap,
        Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name),
        Nothing<bool>());
    // 7. If trap is undefined, then return target.[[SetPrototypeOf]]().
    if (trap->IsUndefined(isolate)) {
      return JSReceiver::SetPrototype(target, value, from_javascript,
                                      should_throw);
    }
    // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, V»)).
    Handle<Object> argv[] = {target, value};
    Handle<Object> trap_result;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, trap_result,
        Execution::Call(isolate, trap, handler, arraysize(argv), argv),
        Nothing<bool>());
    bool bool_trap_result = trap_result->BooleanValue(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(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]]().
    Handle<Object> target_proto;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_proto,
                                     JSReceiver::GetPrototype(isolate, target),
                                     Nothing<bool>());
    // 13. If SameValue(V, targetProto) is false, throw a TypeError exception.
    if (bool_trap_result && !value->SameValue(*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(GetHeap(), new_length) &&
           ShouldConvertToSlowElements(*this, capacity, new_length - 1,
                                       &new_capacity);
  }
  
  const double AllocationSite::kPretenureRatio = 0.85;
  
  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(FLAG_trace_track_allocation_sites);
    Object current = boilerplate().GetHeap()->allocation_sites_list();
    while (current.IsAllocationSite()) {
      AllocationSite current_site = AllocationSite::cast(current);
      if (current_site.nested_site() == *this) {
        return true;
      }
      current = current_site.weak_next();
    }
    return false;
  }
  
  bool AllocationSite::ShouldTrack(ElementsKind from, ElementsKind to) {
    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();
    }
    return nullptr;
  }
  
  // static
  bool JSArray::MayHaveReadOnlyLength(Map js_array_map) {
    DCHECK(js_array_map.IsJSArrayMap());
    if (js_array_map.is_dictionary_map()) return true;
  
    // Fast path: "length" is the first fast property of arrays with non
    // dictionary properties. Since it's not configurable, it's guaranteed to be
    // the first in the descriptor array.
    InternalIndex first(0);
    DCHECK(js_array_map.instance_descriptors().GetKey(first) ==
           js_array_map.GetReadOnlyRoots().length_string());
    return js_array_map.instance_descriptors().GetDetails(first).IsReadOnly();
  }
  
  bool JSArray::HasReadOnlyLength(Handle<JSArray> array) {
    Map map = array->map();
  
    // If map guarantees that there can't be a read-only length, we are done.
    if (!MayHaveReadOnlyLength(map)) return false;
  
    // Look at the object.
    Isolate* isolate = array->GetIsolate();
    LookupIterator it(isolate, array, isolate->factory()->length_string(), array,
                      LookupIterator::OWN_SKIP_INTERCEPTOR);
    CHECK_EQ(LookupIterator::ACCESSOR, it.state());
    return it.IsReadOnly();
  }
  
  bool JSArray::WouldChangeReadOnlyLength(Handle<JSArray> array, uint32_t index) {
    uint32_t length = 0;
    CHECK(array->length().ToArrayLength(&length));
    if (length <= index) return HasReadOnlyLength(array);
    return false;
  }
  
  int FixedArrayBase::GetMaxLengthForNewSpaceAllocation(ElementsKind kind) {
    return ((kMaxRegularHeapObjectSize - FixedArrayBase::kHeaderSize) >>
            ElementsKindToShiftSize(kind));
  }
  
  bool FixedArrayBase::IsCowArray() const {
    return map() == GetReadOnlyRoots().fixed_cow_array_map();
  }
  
  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";
  }
  
  void Symbol::SymbolShortPrint(std::ostream& os) {
    os << "<Symbol:";
    if (!description().IsUndefined()) {
      os << " ";
      String description_as_string = String::cast(description());
      description_as_string.PrintUC16(os, 0, description_as_string.length());
    } else {
      os << " (" << PrivateSymbolToName() << ")";
    }
    os << ">";
  }
  
  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();
  }
  
  int JSPromise::async_task_id() const {
    return AsyncTaskIdBits::decode(flags());
  }
  
  void JSPromise::set_async_task_id(int id) {
    set_flags(AsyncTaskIdBits::update(flags(), id));
  }
  
  // static
  Handle<Object> JSPromise::Fulfill(Handle<JSPromise> promise,
                                    Handle<Object> value) {
    Isolate* const isolate = promise->GetIsolate();
  
    // 1. Assert: The value of promise.[[PromiseState]] is "pending".
    CHECK_EQ(Promise::kPending, promise->status());
  
    // 2. Let reactions be promise.[[PromiseFulfillReactions]].
    Handle<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(*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, Handle<JSPromise> promise) {
    if (isolate->thread_local_top()->pending_message_obj_.IsTheHole(isolate)) {
      return;
    }
  
    Handle<Object> message =
        handle(isolate->thread_local_top()->pending_message_obj_, isolate);
    Handle<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(Handle<JSPromise> promise,
                                   Handle<Object> reason, bool debug_event) {
    Isolate* const isolate = promise->GetIsolate();
    DCHECK(
        !reinterpret_cast<v8::Isolate*>(isolate)->GetCurrentContext().IsEmpty());
  
    if (isolate->debug()->is_active()) MoveMessageToPromise(isolate, promise);
  
    if (debug_event) isolate->debug()->OnPromiseReject(promise, reason);
    isolate->RunPromiseHook(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]].
    Handle<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(*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(Handle<JSPromise> promise,
                                         Handle<Object> resolution) {
    Isolate* const isolate = promise->GetIsolate();
    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)) {
      // a. Let selfResolutionError be a newly created TypeError object.
      Handle<Object> self_resolution_error = isolate->factory()->NewTypeError(
          MessageTemplate::kPromiseCyclic, resolution);
      // b. Return RejectPromise(promise, selfResolutionError).
      return Reject(promise, self_resolution_error);
    }
  
    // 8. If Type(resolution) is not Object, then
    if (!resolution->IsJSReceiver()) {
      // a. Return FulfillPromise(promise, resolution).
      return Fulfill(promise, resolution);
    }
  
    // 9. Let then be Get(resolution, "then").
    MaybeHandle<Object> then;
    Handle<JSReceiver> receiver(Handle<JSReceiver>::cast(resolution));
  
    // 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 (receiver->IsJSPromise() &&
        isolate->IsInAnyContext(receiver->map().prototype(),
                                Context::PROMISE_PROTOTYPE_INDEX) &&
        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%.
      then = isolate->promise_then();
    } else {
      then = JSReceiver::GetProperty(isolate, receiver,
                                     isolate->factory()->then_string());
    }
  
    // 10. If then is an abrupt completion, then
    Handle<Object> then_action;
    if (!then.ToHandle(&then_action)) {
      // The "then" lookup can cause termination.
      if (!isolate->is_catchable_by_javascript(isolate->pending_exception())) {
        return kNullMaybeHandle;
      }
  
      // a. Return RejectPromise(promise, then.[[Value]]).
      Handle<Object> reason(isolate->pending_exception(), isolate);
      isolate->clear_pending_exception();
      return Reject(promise, reason, false);
    }
  
    // 11. Let thenAction be then.[[Value]].
    // 12. If IsCallable(thenAction) is false, then
    if (!then_action->IsCallable()) {
      // a. Return FulfillPromise(promise, resolution).
      return Fulfill(promise, resolution);
    }
  
    // 13. Let job be NewPromiseResolveThenableJob(promise, resolution,
    //                                             thenAction).
    Handle<NativeContext> then_context;
    if (!JSReceiver::GetContextForMicrotask(Handle<JSReceiver>::cast(then_action))
             .ToHandle(&then_context)) {
      then_context = isolate->native_context();
    }
  
    Handle<PromiseResolveThenableJobTask> task =
        isolate->factory()->NewPromiseResolveThenableJobTask(
            promise, Handle<JSReceiver>::cast(resolution),
            Handle<JSReceiver>::cast(then_action), then_context);
    if (isolate->debug()->is_active() && resolution->IsJSPromise()) {
      // Mark the dependency of the new {promise} on the {resolution}.
      Object::SetProperty(isolate, resolution,
                          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,
                                                    Handle<Object> reactions,
                                                    Handle<Object> argument,
                                                    PromiseReaction::Type type) {
    CHECK(reactions->IsSmi() || reactions->IsPromiseReaction());
  
    // We need to reverse the {reactions} here, since we record them
    // on the JSPromise in the reverse order.
    {
      DisallowGarbageCollection no_gc;
      Object current = *reactions;
      Object reversed = Smi::zero();
      while (!current.IsSmi()) {
        Object next = PromiseReaction::cast(current).next();
        PromiseReaction::cast(current).set_next(reversed);
        reversed = current;
        current = next;
      }
      reactions = handle(reversed, isolate);
    }
  
    // Morph the {reactions} into PromiseReactionJobTasks
    // and push them onto the microtask queue.
    while (!reactions->IsSmi()) {
      Handle<HeapObject> task = Handle<HeapObject>::cast(reactions);
      Handle<PromiseReaction> reaction = Handle<PromiseReaction>::cast(task);
      reactions = 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)
      Handle<NativeContext> handler_context;
  
      Handle<HeapObject> primary_handler;
      Handle<HeapObject> secondary_handler;
      if (type == PromiseReaction::kFulfill) {
        primary_handler = handle(reaction->fulfill_handler(), isolate);
        secondary_handler = handle(reaction->reject_handler(), isolate);
      } else {
        primary_handler = handle(reaction->reject_handler(), isolate);
        secondary_handler = handle(reaction->fulfill_handler(), isolate);
      }
  
      bool has_handler_context = false;
      if (primary_handler->IsJSReceiver()) {
        has_handler_context = JSReceiver::GetContextForMicrotask(
                                  Handle<JSReceiver>::cast(primary_handler))
                                  .ToHandle(&handler_context);
      }
      if (!has_handler_context && secondary_handler->IsJSReceiver()) {
        has_handler_context = JSReceiver::GetContextForMicrotask(
                                  Handle<JSReceiver>::cast(secondary_handler))
                                  .ToHandle(&handler_context);
      }
      if (!has_handler_context) handler_context = isolate->native_context();
  
      STATIC_ASSERT(
          static_cast<int>(PromiseReaction::kSize) ==
          static_cast<int>(
              PromiseReactionJobTask::kSizeOfAllPromiseReactionJobTasks));
      if (type == PromiseReaction::kFulfill) {
        task->synchronized_set_map(
            ReadOnlyRoots(isolate).promise_fulfill_reaction_job_task_map());
        Handle<PromiseFulfillReactionJobTask>::cast(task)->set_argument(
            *argument);
        Handle<PromiseFulfillReactionJobTask>::cast(task)->set_context(
            *handler_context);
        STATIC_ASSERT(
            static_cast<int>(PromiseReaction::kFulfillHandlerOffset) ==
            static_cast<int>(PromiseFulfillReactionJobTask::kHandlerOffset));
        STATIC_ASSERT(
            static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) ==
            static_cast<int>(
                PromiseFulfillReactionJobTask::kPromiseOrCapabilityOffset));
        STATIC_ASSERT(
            static_cast<int>(
                PromiseReaction::kContinuationPreservedEmbedderDataOffset) ==
            static_cast<int>(PromiseFulfillReactionJobTask::
                                 kContinuationPreservedEmbedderDataOffset));
      } else {
        DisallowGarbageCollection no_gc;
        task->synchronized_set_map(
            ReadOnlyRoots(isolate).promise_reject_reaction_job_task_map());
        Handle<PromiseRejectReactionJobTask>::cast(task)->set_argument(*argument);
        Handle<PromiseRejectReactionJobTask>::cast(task)->set_context(
            *handler_context);
        Handle<PromiseRejectReactionJobTask>::cast(task)->set_handler(
            *primary_handler);
        STATIC_ASSERT(
            static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) ==
            static_cast<int>(
                PromiseRejectReactionJobTask::kPromiseOrCapabilityOffset));
        STATIC_ASSERT(
            static_cast<int>(
                PromiseReaction::kContinuationPreservedEmbedderDataOffset) ==
            static_cast<int>(PromiseRejectReactionJobTask::
                                 kContinuationPreservedEmbedderDataOffset));
      }
  
      MicrotaskQueue* microtask_queue = handler_context->microtask_queue();
      if (microtask_queue) {
        microtask_queue->EnqueueMicrotask(
            *Handle<PromiseReactionJobTask>::cast(task));
      }
    }
  
    return isolate->factory()->undefined_value();
  }
  
  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 LocalIsolate>
  Handle<Derived> HashTable<Derived, Shape>::New(
      LocalIsolate* isolate, int at_least_space_for, AllocationType allocation,
      MinimumCapacity capacity_option) {
    DCHECK_LE(0, at_least_space_for);
    DCHECK_IMPLIES(capacity_option == USE_CUSTOM_MINIMUM_CAPACITY,
                   base::bits::IsPowerOfTwo(at_least_space_for));
  
    int capacity = (capacity_option == USE_CUSTOM_MINIMUM_CAPACITY)
                       ? at_least_space_for
                       : ComputeCapacity(at_least_space_for);
    if (capacity > HashTable::kMaxCapacity) {
      isolate->FatalProcessOutOfHeapMemory("invalid table size");
    }
    return NewInternal(isolate, capacity, allocation);
  }
  
  template <typename Derived, typename Shape>
  template <typename LocalIsolate>
  Handle<Derived> HashTable<Derived, Shape>::NewInternal(
      LocalIsolate* isolate, int capacity, AllocationType allocation) {
    auto* factory = isolate->factory();
    int length = EntryToIndex(InternalIndex(capacity));
    Handle<FixedArray> array = factory->NewFixedArrayWithMap(
        Derived::GetMap(ReadOnlyRoots(isolate)), length, allocation);
    Handle<Derived> table = Handle<Derived>::cast(array);
  
    table->SetNumberOfElements(0);
    table->SetNumberOfDeletedElements(0);
    table->SetCapacity(capacity);
    return table;
  }
  
  template <typename Derived, typename Shape>
  void HashTable<Derived, Shape>::Rehash(PtrComprCageBase cage_base,
                                         Derived new_table) {
    DisallowGarbageCollection no_gc;
    WriteBarrierMode 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(cage_base, i), mode);
    }
  
    // Rehash the elements.
    ReadOnlyRoots roots = GetReadOnlyRoots(cage_base);
    for (InternalIndex i : this->IterateEntries()) {
      uint32_t from_index = EntryToIndex(i);
      Object k = this->get(cage_base, from_index);
      if (!IsKey(roots, k)) continue;
      uint32_t hash = Shape::HashForObject(roots, k);
      uint32_t insertion_index =
          EntryToIndex(new_table.FindInsertionEntry(cage_base, roots, hash));
      new_table.set_key(insertion_index, get(cage_base, from_index), mode);
      for (int j = 1; j < Shape::kEntrySize; j++) {
        new_table.set(insertion_index + j, get(cage_base, from_index + j), mode);
      }
    }
    new_table.SetNumberOfElements(NumberOfElements());
    new_table.SetNumberOfDeletedElements(0);
  }
  
  template <typename Derived, typename Shape>
  InternalIndex HashTable<Derived, Shape>::EntryForProbe(ReadOnlyRoots roots,
                                                         Object k, int probe,
                                                         InternalIndex expected) {
    uint32_t hash = Shape::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);
    Object temp[Shape::kEntrySize];
    Derived* self = static_cast<Derived*>(this);
    for (int j = 0; j < Shape::kEntrySize; j++) {
      temp[j] = get(index1 + j);
    }
    self->set_key(index1, get(index2), mode);
    for (int j = 1; j < Shape::kEntrySize; j++) {
      set(index1 + j, get(index2 + j), mode);
    }
    self->set_key(index2, temp[0], mode);
    for (int j = 1; j < Shape::kEntrySize; j++) {
      set(index2 + j, temp[j], mode);
    }
  }
  
  template <typename Derived, typename Shape>
  void HashTable<Derived, Shape>::Rehash(PtrComprCageBase cage_base) {
    DisallowGarbageCollection no_gc;
    WriteBarrierMode mode = GetWriteBarrierMode(no_gc);
    ReadOnlyRoots roots = GetReadOnlyRoots(cage_base);
    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.*/) {
        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;
        }
        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.
    Object the_hole = roots.the_hole_value();
    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 LocalIsolate>
  Handle<Derived> HashTable<Derived, Shape>::EnsureCapacity(
      LocalIsolate* isolate, Handle<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) &&
                             !Heap::InYoungGeneration(*table));
    Handle<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>
  int HashTable<Derived, Shape>::ComputeCapacityWithShrink(
      int current_capacity, int 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.
    int 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>
  Handle<Derived> HashTable<Derived, Shape>::Shrink(Isolate* isolate,
                                                    Handle<Derived> table,
                                                    int additional_capacity) {
    int 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) &&
                     !Heap::InYoungGeneration(*table);
    Handle<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;
    }
  }
  
  template <typename Derived, typename Shape>
  InternalIndex HashTable<Derived, Shape>::FindInsertionEntry(Isolate* isolate,
                                                              uint32_t hash) {
    return FindInsertionEntry(isolate, ReadOnlyRoots(isolate), hash);
  }
  
  Handle<StringSet> StringSet::New(Isolate* isolate) {
    return HashTable::New(isolate, 0);
  }
  
  Handle<StringSet> StringSet::Add(Isolate* isolate, Handle<StringSet> stringset,
                                   Handle<String> name) {
    if (!stringset->Has(isolate, name)) {
      stringset = EnsureCapacity(isolate, stringset);
      uint32_t hash = ShapeT::Hash(ReadOnlyRoots(isolate), *name);
      InternalIndex entry = stringset->FindInsertionEntry(isolate, hash);
      stringset->set(EntryToIndex(entry), *name);
      stringset->ElementAdded();
    }
    return stringset;
  }
  
  bool StringSet::Has(Isolate* isolate, Handle<String> name) {
    return FindEntry(isolate, *name).is_found();
  }
  
  Handle<ObjectHashSet> ObjectHashSet::Add(Isolate* isolate,
                                           Handle<ObjectHashSet> set,
                                           Handle<Object> key) {
    int32_t hash = key->GetOrCreateHash(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;
  }
  
  template <typename Derived, typename Shape>
  template <typename LocalIsolate>
  Handle<Derived> BaseNameDictionary<Derived, Shape>::New(
      LocalIsolate* 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 Derived, typename Shape>
  int BaseNameDictionary<Derived, Shape>::NextEnumerationIndex(
      Isolate* isolate, Handle<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.
      Handle<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 index(Smi::ToInt(iteration_order->get(i)));
        DCHECK(dictionary->IsKey(dictionary->GetReadOnlyRoots(),
                                 dictionary->KeyAt(isolate, index)));
  
        int enum_index = PropertyDetails::kInitialIndex + i;
  
        PropertyDetails details = dictionary->DetailsAt(index);
        PropertyDetails new_details = details.set_index(enum_index);
        dictionary->DetailsAtPut(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>
  Handle<Derived> Dictionary<Derived, Shape>::DeleteEntry(
      Isolate* isolate, Handle<Derived> dictionary, InternalIndex entry) {
    DCHECK(Shape::kEntrySize != 3 ||
           dictionary->DetailsAt(entry).IsConfigurable());
    dictionary->ClearEntry(entry);
    dictionary->ElementRemoved();
    return Shrink(isolate, dictionary);
  }
  
  template <typename Derived, typename Shape>
  Handle<Derived> Dictionary<Derived, Shape>::AtPut(Isolate* isolate,
                                                    Handle<Derived> dictionary,
                                                    Key key, Handle<Object> value,
                                                    PropertyDetails 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 (Shape::kEntrySize == 3) dictionary->DetailsAtPut(entry, details);
    return dictionary;
  }
  
  template <typename Derived, typename Shape>
  template <typename LocalIsolate>
  Handle<Derived>
  BaseNameDictionary<Derived, Shape>::AddNoUpdateNextEnumerationIndex(
      LocalIsolate* isolate, Handle<Derived> dictionary, Key key,
      Handle<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>
  Handle<Derived> BaseNameDictionary<Derived, Shape>::Add(
      Isolate* isolate, Handle<Derived> dictionary, Key key, Handle<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);
    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 LocalIsolate>
  Handle<Derived> Dictionary<Derived, Shape>::Add(LocalIsolate* isolate,
                                                  Handle<Derived> dictionary,
                                                  Key key, Handle<Object> value,
                                                  PropertyDetails details,
                                                  InternalIndex* entry_out) {
    ReadOnlyRoots roots(isolate);
    uint32_t hash = Shape::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.
    Handle<Object> k = Shape::AsHandle(isolate, key);
  
    InternalIndex entry = dictionary->FindInsertionEntry(isolate, roots, hash);
    dictionary->SetEntry(entry, *k, *value, details);
    DCHECK(dictionary->KeyAt(isolate, entry).IsNumber() ||
           Shape::Unwrap(dictionary->KeyAt(isolate, entry)).IsUniqueName());
    dictionary->ElementAdded();
    if (entry_out) *entry_out = entry;
    return dictionary;
  }
  
  template <typename Derived, typename Shape>
  Handle<Derived> Dictionary<Derived, Shape>::ShallowCopy(
      Isolate* isolate, Handle<Derived> dictionary) {
    return Handle<Derived>::cast(isolate->factory()->CopyFixedArrayWithMap(
        dictionary, Derived::GetMap(ReadOnlyRoots(isolate))));
  }
  
  // static
  Handle<SimpleNumberDictionary> SimpleNumberDictionary::Set(
      Isolate* isolate, Handle<SimpleNumberDictionary> dictionary, uint32_t key,
      Handle<Object> value) {
    return AtPut(isolate, dictionary, key, value, PropertyDetails::Empty());
  }
  
  void NumberDictionary::UpdateMaxNumberKey(uint32_t key,
                                            Handle<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.
    Object max_index_object = get(kMaxNumberKeyIndex);
    if (!max_index_object.IsSmi() || max_number_key() < key) {
      FixedArray::set(kMaxNumberKeyIndex,
                      Smi::FromInt(key << kRequiresSlowElementsTagSize));
    }
  }
  
  Handle<NumberDictionary> NumberDictionary::Set(
      Isolate* isolate, Handle<NumberDictionary> dictionary, uint32_t key,
      Handle<Object> value, Handle<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.
    Handle<NumberDictionary> new_dictionary =
        AtPut(isolate, dictionary, key, value, details);
    new_dictionary->UpdateMaxNumberKey(key, dictionary_holder);
    return new_dictionary;
  }
  
  void NumberDictionary::CopyValuesTo(FixedArray elements) {
    ReadOnlyRoots roots = GetReadOnlyRoots();
    int pos = 0;
    DisallowGarbageCollection no_gc;
    WriteBarrierMode mode = elements.GetWriteBarrierMode(no_gc);
    for (InternalIndex i : this->IterateEntries()) {
      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 = this->GetReadOnlyRoots();
    int result = 0;
    for (InternalIndex i : this->IterateEntries()) {
      Object k;
      if (!this->ToKey(roots, i, &k)) continue;
      if (k.FilterKey(ENUMERABLE_STRINGS)) continue;
      PropertyDetails details = this->DetailsAt(i);
      PropertyAttributes attr = details.attributes();
      if ((attr & ONLY_ENUMERABLE) == 0) result++;
    }
    return result;
  }
  
  template <typename Derived, typename Shape>
  Handle<FixedArray> BaseNameDictionary<Derived, Shape>::IterationIndices(
      Isolate* isolate, Handle<Derived> dictionary) {
    Handle<FixedArray> array =
        isolate->factory()->NewFixedArray(dictionary->NumberOfElements());
    ReadOnlyRoots roots(isolate);
    int array_size = 0;
    {
      DisallowGarbageCollection no_gc;
      Derived raw_dictionary = *dictionary;
      for (InternalIndex i : dictionary->IterateEntries()) {
        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<Derived, GlobalDictionary>::value) {
        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->GetFirstElementAddress());
      std::sort(start, start + array_size, cmp);
    }
    return FixedArray::ShrinkOrEmpty(isolate, array, array_size);
  }
  
  // Backwards lookup (slow).
  template <typename Derived, typename Shape>
  Object Dictionary<Derived, Shape>::SlowReverseLookup(Object value) {
    Derived dictionary = Derived::cast(*this);
    ReadOnlyRoots roots = dictionary.GetReadOnlyRoots();
    for (InternalIndex i : dictionary.IterateEntries()) {
      Object k;
      if (!dictionary.ToKey(roots, i, &k)) continue;
      Object e = dictionary.ValueAt(i);
      if (e == value) return k;
    }
    return roots.undefined_value();
  }
  
  template <typename Derived, typename Shape>
  void ObjectHashTableBase<Derived, Shape>::FillEntriesWithHoles(
      Handle<Derived> table) {
    int length = table->length();
    for (int i = Derived::EntryToIndex(InternalIndex(0)); i < length; i++) {
      table->set_the_hole(i);
    }
  }
  
  template <typename Derived, typename Shape>
  Object ObjectHashTableBase<Derived, Shape>::Lookup(PtrComprCageBase cage_base,
                                                     Handle<Object> key,
                                                     int32_t hash) {
    DisallowGarbageCollection no_gc;
    ReadOnlyRoots roots = this->GetReadOnlyRoots(cage_base);
    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);
  }
  
  template <typename Derived, typename Shape>
  Object ObjectHashTableBase<Derived, Shape>::Lookup(Handle<Object> key) {
    DisallowGarbageCollection no_gc;
  
    PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
    ReadOnlyRoots roots = this->GetReadOnlyRoots(cage_base);
    DCHECK(this->IsKey(roots, *key));
  
    // If the object does not have an identity hash, it was never used as a key.
    Object hash = key->GetHash();
    if (hash.IsUndefined(roots)) {
      return roots.the_hole_value();
    }
    return Lookup(cage_base, key, Smi::ToInt(hash));
  }
  
  template <typename Derived, typename Shape>
  Object ObjectHashTableBase<Derived, Shape>::Lookup(Handle<Object> key,
                                                     int32_t hash) {
    return Lookup(GetPtrComprCageBase(*this), key, hash);
  }
  
  template <typename Derived, typename Shape>
  Object ObjectHashTableBase<Derived, Shape>::ValueAt(InternalIndex entry) {
    return this->get(EntryToValueIndex(entry));
  }
  
  template <typename Derived, typename Shape>
  Handle<Derived> ObjectHashTableBase<Derived, Shape>::Put(Handle<Derived> table,
                                                           Handle<Object> key,
                                                           Handle<Object> value) {
    Isolate* isolate = Heap::FromWritableHeapObject(*table)->isolate();
    DCHECK(table->IsKey(ReadOnlyRoots(isolate), *key));
    DCHECK(!value->IsTheHole(ReadOnlyRoots(isolate)));
  
    // Make sure the key object has an identity hash code.
    int32_t hash = key->GetOrCreateHash(isolate).value();
  
    return ObjectHashTableBase<Derived, Shape>::Put(isolate, table, key, value,
                                                    hash);
  }
  
  template <typename Derived, typename Shape>
  Handle<Derived> ObjectHashTableBase<Derived, Shape>::Put(Isolate* isolate,
                                                           Handle<Derived> table,
                                                           Handle<Object> key,
                                                           Handle<Object> value,
                                                           int32_t hash) {
    ReadOnlyRoots roots(isolate);
    DCHECK(table->IsKey(roots, *key));
    DCHECK(!value->IsTheHole(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;
    }
  
    // Rehash if more than 33% of the entries are deleted entries.
    // TODO(jochen): 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)) {
      int nof = table->NumberOfElements() + 1;
      int capacity = ObjectHashTable::ComputeCapacity(nof * 2);
      if (capacity > ObjectHashTable::kMaxCapacity) {
        for (size_t i = 0; i < 2; ++i) {
          isolate->heap()->CollectAllGarbage(
              Heap::kNoGCFlags, GarbageCollectionReason::kFullHashtable);
        }
        table->Rehash(isolate);
      }
    }
  
    // 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, Handle<Object> key,
      bool* was_present) {
    DCHECK(table->IsKey(table->GetReadOnlyRoots(), *key));
  
    Object hash = key->GetHash();
    if (hash.IsUndefined()) {
      *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, Handle<Object> key,
      bool* was_present, int32_t hash) {
    ReadOnlyRoots roots = table->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,
                                                     Object key, 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) {
    this->set_the_hole(Derived::EntryToIndex(entry));
    this->set_the_hole(Derived::EntryToValueIndex(entry));
    this->ElementRemoved();
  }
  
  void JSSet::Initialize(Handle<JSSet> set, Isolate* isolate) {
    Handle<OrderedHashSet> table = isolate->factory()->NewOrderedHashSet();
    set->set_table(*table);
  }
  
  void JSSet::Clear(Isolate* isolate, Handle<JSSet> set) {
    Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()), isolate);
    table = OrderedHashSet::Clear(isolate, table);
    set->set_table(*table);
  }
  
  void JSSet::Rehash(Isolate* isolate) {
    Handle<OrderedHashSet> table_handle(OrderedHashSet::cast(table()), isolate);
    Handle<OrderedHashSet> new_table =
        OrderedHashSet::Rehash(isolate, table_handle).ToHandleChecked();
    set_table(*new_table);
  }
  
  void JSMap::Initialize(Handle<JSMap> map, Isolate* isolate) {
    Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
    map->set_table(*table);
  }
  
  void JSMap::Clear(Isolate* isolate, Handle<JSMap> map) {
    Handle<OrderedHashMap> table(OrderedHashMap::cast(map->table()), isolate);
    table = OrderedHashMap::Clear(isolate, table);
    map->set_table(*table);
  }
  
  void JSMap::Rehash(Isolate* isolate) {
    Handle<OrderedHashMap> table_handle(OrderedHashMap::cast(table()), isolate);
    Handle<OrderedHashMap> new_table =
        OrderedHashMap::Rehash(isolate, table_handle).ToHandleChecked();
    set_table(*new_table);
  }
  
  void JSWeakCollection::Initialize(Handle<JSWeakCollection> weak_collection,
                                    Isolate* isolate) {
    Handle<EphemeronHashTable> table = EphemeronHashTable::New(isolate, 0);
    weak_collection->set_table(*table);
  }
  
  void JSWeakCollection::Set(Handle<JSWeakCollection> weak_collection,
                             Handle<Object> key, Handle<Object> value,
                             int32_t hash) {
    DCHECK(key->IsJSReceiver() || key->IsSymbol());
    Handle<EphemeronHashTable> table(
        EphemeronHashTable::cast(weak_collection->table()),
        weak_collection->GetIsolate());
    DCHECK(table->IsKey(weak_collection->GetReadOnlyRoots(), *key));
    Handle<EphemeronHashTable> new_table = EphemeronHashTable::Put(
        weak_collection->GetIsolate(), 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(Handle<JSWeakCollection> weak_collection,
                                Handle<Object> key, int32_t hash) {
    DCHECK(key->IsJSReceiver() || key->IsSymbol());
    Handle<EphemeronHashTable> table(
        EphemeronHashTable::cast(weak_collection->table()),
        weak_collection->GetIsolate());
    DCHECK(table->IsKey(weak_collection->GetReadOnlyRoots(), *key));
    bool was_present = false;
    Handle<EphemeronHashTable> new_table = EphemeronHashTable::Remove(
        weak_collection->GetIsolate(), 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;
  }
  
  Handle<JSArray> JSWeakCollection::GetEntries(Handle<JSWeakCollection> holder,
                                               int max_entries) {
    Isolate* isolate = holder->GetIsolate();
    Handle<EphemeronHashTable> table(EphemeronHashTable::cast(holder->table()),
                                     isolate);
    if (max_entries == 0 || max_entries > table->NumberOfElements()) {
      max_entries = table->NumberOfElements();
    }
    int values_per_entry = holder->IsJSWeakMap() ? 2 : 1;
    Handle<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);
      int count = 0;
      for (int i = 0;
           count / values_per_entry < max_entries && i < table->Capacity(); i++) {
        Object key;
        if (table->ToKey(roots, InternalIndex(i), &key)) {
          entries->set(count++, key);
          if (values_per_entry > 1) {
            Object value = table->Lookup(handle(key, isolate));
            entries->set(count++, value);
          }
        }
      }
      DCHECK_EQ(max_entries * values_per_entry, count);
    }
    return isolate->factory()->NewJSArrayWithElements(entries);
  }
  
  void PropertyCell::ClearAndInvalidate(ReadOnlyRoots roots) {
    DCHECK(!value().IsTheHole(roots));
    PropertyDetails details = property_details();
    details = details.set_cell_type(PropertyCellType::kConstant);
    Transition(details, roots.the_hole_value_handle());
    dependent_code().DeoptimizeDependentCodeGroup(
        DependentCode::kPropertyCellChangedGroup);
  }
  
  // static
  Handle<PropertyCell> PropertyCell::InvalidateAndReplaceEntry(
      Isolate* isolate, Handle<GlobalDictionary> dictionary, InternalIndex entry,
      PropertyDetails new_details, Handle<Object> new_value) {
    Handle<PropertyCell> cell(dictionary->CellAt(entry), isolate);
    Handle<Name> name(cell->name(), isolate);
    DCHECK(cell->property_details().IsConfigurable());
    DCHECK(!cell->value().IsTheHole(isolate));
  
    // 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(ReadOnlyRoots(isolate));
    return new_cell;
  }
  
  static bool RemainsConstantType(Handle<PropertyCell> cell,
                                  Handle<Object> value) {
    // TODO(dcarney): double->smi and smi->double transition from kConstant
    if (cell->value().IsSmi() && value->IsSmi()) {
      return true;
    } else if (cell->value().IsHeapObject() && value->IsHeapObject()) {
      return HeapObject::cast(cell->value()).map() ==
                 HeapObject::cast(*value).map() &&
             HeapObject::cast(*value).map().is_stable();
    }
    return false;
  }
  
  // static
  PropertyCellType PropertyCell::InitialType(Isolate* isolate,
                                             Handle<Object> value) {
    return value->IsUndefined(isolate) ? PropertyCellType::kUndefined
                                       : PropertyCellType::kConstant;
  }
  
  // static
  PropertyCellType PropertyCell::UpdatedType(Isolate* isolate,
                                             Handle<PropertyCell> cell,
                                             Handle<Object> value,
                                             PropertyDetails details) {
    DCHECK(!value->IsTheHole(isolate));
    DCHECK(!cell->value().IsTheHole(isolate));
    switch (details.cell_type()) {
      case PropertyCellType::kUndefined:
        return PropertyCellType::kConstant;
      case PropertyCellType::kConstant:
        if (*value == cell->value()) return PropertyCellType::kConstant;
        V8_FALLTHROUGH;
      case PropertyCellType::kConstantType:
        if (RemainsConstantType(cell, value)) {
          return PropertyCellType::kConstantType;
        }
        V8_FALLTHROUGH;
      case PropertyCellType::kMutable:
        return PropertyCellType::kMutable;
    }
  }
  
  Handle<PropertyCell> PropertyCell::PrepareForAndSetValue(
      Isolate* isolate, Handle<GlobalDictionary> dictionary, InternalIndex entry,
      Handle<Object> value, PropertyDetails details) {
    DCHECK(!value->IsTheHole(isolate));
    Handle<PropertyCell> cell(dictionary->CellAt(entry), isolate);
    CHECK(!cell->value().IsTheHole(isolate));
    const PropertyDetails original_details = cell->property_details();
    // Data accesses could be cached in ics or optimized code.
    bool invalidate =
        original_details.kind() == kData && details.kind() == kAccessor;
    int index = original_details.dictionary_index();
    DCHECK_LT(0, index);
    details = details.set_index(index);
  
    PropertyCellType new_type =
        UpdatedType(isolate, cell, value, original_details);
    details = details.set_cell_type(new_type);
  
    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())) {
        cell->dependent_code().DeoptimizeDependentCodeGroup(
            DependentCode::kPropertyCellChangedGroup);
      }
    }
    return cell;
  }
  
  // static
  void PropertyCell::InvalidateProtector() {
    if (value() != Smi::FromInt(Protectors::kProtectorInvalid)) {
      DCHECK_EQ(value(), Smi::FromInt(Protectors::kProtectorValid));
      set_value(Smi::FromInt(Protectors::kProtectorInvalid), kReleaseStore);
      dependent_code().DeoptimizeDependentCodeGroup(
          DependentCode::kPropertyCellChangedGroup);
    }
  }
  
  // static
  bool PropertyCell::CheckDataIsCompatible(PropertyDetails details,
                                           Object value) {
    DisallowGarbageCollection no_gc;
    PropertyCellType cell_type = details.cell_type();
    if (value.IsTheHole()) {
      CHECK_EQ(cell_type, PropertyCellType::kConstant);
    } else {
      CHECK_EQ(value.IsAccessorInfo() || value.IsAccessorPair(),
               details.kind() == kAccessor);
      DCHECK_IMPLIES(cell_type == PropertyCellType::kUndefined,
                     value.IsUndefined());
    }
    return true;
  }
  
  #ifdef DEBUG
  bool PropertyCell::CanTransitionTo(PropertyDetails new_details,
                                     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 !value().IsTheHole() &&
               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 &&
                new_value.IsTheHole());
      case PropertyCellType::kMutable:
        return new_details.cell_type() == PropertyCellType::kMutable ||
               (new_details.cell_type() == PropertyCellType::kConstant &&
                new_value.IsTheHole());
    }
    UNREACHABLE();
  }
  #endif  // DEBUG
  
  int JSGeneratorObject::source_position() const {
    CHECK(is_suspended());
    DCHECK(function().shared().HasBytecodeArray());
    Isolate* isolate = GetIsolate();
    DCHECK(
        function().shared().GetBytecodeArray(isolate).HasSourcePositionTable());
  
    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;
    AbstractCode code =
        AbstractCode::cast(function().shared().GetBytecodeArray(isolate));
    return code.SourcePosition(code_offset);
  }
  
  // static
  AccessCheckInfo AccessCheckInfo::Get(Isolate* isolate,
                                       Handle<JSObject> receiver) {
    DisallowGarbageCollection no_gc;
    DCHECK(receiver->map().is_access_check_needed());
    Object maybe_constructor = receiver->map().GetConstructor();
    if (maybe_constructor.IsFunctionTemplateInfo()) {
      Object data_obj =
          FunctionTemplateInfo::cast(maybe_constructor).GetAccessCheckInfo();
      if (data_obj.IsUndefined(isolate)) return AccessCheckInfo();
      return AccessCheckInfo::cast(data_obj);
    }
    // Might happen for a detached context.
    if (!maybe_constructor.IsJSFunction()) return AccessCheckInfo();
    JSFunction constructor = JSFunction::cast(maybe_constructor);
    // Might happen for the debug context.
    if (!constructor.shared().IsApiFunction()) return AccessCheckInfo();
  
    Object data_obj =
        constructor.shared().get_api_func_data().GetAccessCheckInfo();
    if (data_obj.IsUndefined(isolate)) return AccessCheckInfo();
  
    return AccessCheckInfo::cast(data_obj);
  }
  
  MaybeHandle<Name> FunctionTemplateInfo::TryGetCachedPropertyName(
      Isolate* isolate, Handle<Object> getter) {
    if (getter->IsFunctionTemplateInfo()) {
      Handle<FunctionTemplateInfo> fti =
          Handle<FunctionTemplateInfo>::cast(getter);
      // Check if the accessor uses a cached property.
      if (!fti->cached_property_name().IsTheHole(isolate)) {
        return handle(Name::cast(fti->cached_property_name()), isolate);
      }
    }
    return MaybeHandle<Name>();
  }
  
  Address Smi::LexicographicCompare(Isolate* isolate, Smi x, 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();
  }
  
  // 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) Handle<DERIVED>        \
    HashTable<DERIVED, SHAPE>::New(Isolate*, int, AllocationType,             \
                                   MinimumCapacity);                          \
    template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED>        \
    HashTable<DERIVED, SHAPE>::New(LocalIsolate*, int, 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);
  
  #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_DICTIONARY(DERIVED, SHAPE)                               \
    EXTERN_DEFINE_HASH_TABLE(DERIVED, SHAPE)                                     \
    template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)                     \
        Dictionary<DERIVED, SHAPE>;                                              \
                                                                                 \
    template V8_EXPORT_PRIVATE Handle<DERIVED> Dictionary<DERIVED, SHAPE>::Add(  \
        Isolate* isolate, Handle<DERIVED>, Key, Handle<Object>, PropertyDetails, \
        InternalIndex*);                                                         \
    template V8_EXPORT_PRIVATE Handle<DERIVED> Dictionary<DERIVED, SHAPE>::Add(  \
        LocalIsolate* isolate, Handle<DERIVED>, Key, Handle<Object>,             \
        PropertyDetails, InternalIndex*);
  
  #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 Handle<DERIVED>                                                     \
    BaseNameDictionary<DERIVED, SHAPE>::AddNoUpdateNextEnumerationIndex(         \
        Isolate* isolate, Handle<DERIVED>, Key, Handle<Object>, PropertyDetails, \
        InternalIndex*);                                                         \
    template Handle<DERIVED>                                                     \
    BaseNameDictionary<DERIVED, SHAPE>::AddNoUpdateNextEnumerationIndex(         \
        LocalIsolate* isolate, Handle<DERIVED>, Key, Handle<Object>,             \
        PropertyDetails, InternalIndex*);
  
  EXTERN_DEFINE_HASH_TABLE(StringSet, StringSetShape)
  EXTERN_DEFINE_HASH_TABLE(CompilationCacheTable, CompilationCacheShape)
  EXTERN_DEFINE_HASH_TABLE(ObjectHashSet, ObjectHashSetShape)
  
  EXTERN_DEFINE_OBJECT_BASE_HASH_TABLE(ObjectHashTable, ObjectHashTableShape)
  EXTERN_DEFINE_OBJECT_BASE_HASH_TABLE(EphemeronHashTable, ObjectHashTableShape)
  
  EXTERN_DEFINE_DICTIONARY(SimpleNumberDictionary, SimpleNumberDictionaryShape)
  EXTERN_DEFINE_DICTIONARY(NumberDictionary, NumberDictionaryShape)
  
  EXTERN_DEFINE_BASE_NAME_DICTIONARY(NameDictionary, NameDictionaryShape)
  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
  
  void JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap(
      Isolate* isolate, Address raw_finalization_registry,
      Address raw_weak_cell) {
    DisallowGarbageCollection no_gc;
    JSFinalizationRegistry finalization_registry =
        JSFinalizationRegistry::cast(Object(raw_finalization_registry));
    WeakCell weak_cell = WeakCell::cast(Object(raw_weak_cell));
    DCHECK(!weak_cell.unregister_token().IsUndefined(isolate));
  
    // Remove weak_cell from the linked list of other WeakCells with the same
    // unregister token and remove its unregister token from key_map if necessary
    // without shrinking it. Since shrinking may allocate, it is performed by the
    // caller after looping, or on exception.
    if (weak_cell.key_list_prev().IsUndefined(isolate)) {
      SimpleNumberDictionary key_map =
          SimpleNumberDictionary::cast(finalization_registry.key_map());
      Object unregister_token = weak_cell.unregister_token();
      uint32_t key = Smi::ToInt(unregister_token.GetHash());
      InternalIndex entry = key_map.FindEntry(isolate, key);
      DCHECK(entry.is_found());
  
      if (weak_cell.key_list_next().IsUndefined(isolate)) {
        // weak_cell is the only one associated with its key; remove the key
        // from the hash table.
        key_map.ClearEntry(entry);
        key_map.ElementRemoved();
      } else {
        // weak_cell is the list head for its key; we need to change the value
        // of the key in the hash table.
        WeakCell next = WeakCell::cast(weak_cell.key_list_next());
        DCHECK_EQ(next.key_list_prev(), weak_cell);
        next.set_key_list_prev(ReadOnlyRoots(isolate).undefined_value());
        weak_cell.set_key_list_next(ReadOnlyRoots(isolate).undefined_value());
        key_map.ValueAtPut(entry, next);
      }
    } else {
      // weak_cell is somewhere in the middle of its key list.
      WeakCell prev = WeakCell::cast(weak_cell.key_list_prev());
      prev.set_key_list_next(weak_cell.key_list_next());
      if (!weak_cell.key_list_next().IsUndefined()) {
        WeakCell next = WeakCell::cast(weak_cell.key_list_next());
        next.set_key_list_prev(weak_cell.key_list_prev());
      }
    }
  }
  
  }  // namespace internal
  }  // namespace v8
  