#include "src/objects/objects.h"
#include <algorithm>
#include <cmath>
#include <optional>
#include <sstream>
#include <vector>
#include "src/api/api-arguments-inl.h"
#include "src/api/api-natives.h"
#include "src/ast/scopes.h"
#include "src/base/bits.h"
#include "src/base/logging.h"
#include "src/base/overflowing-math.h"
#include "src/builtins/accessors.h"
#include "src/builtins/builtins.h"
#include "src/codegen/source-position-table.h"
#include "src/common/globals.h"
#include "src/common/message-template.h"
#include "src/debug/debug.h"
#include "src/execution/execution.h"
#include "src/execution/frames-inl.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/isolate-utils-inl.h"
#include "src/execution/isolate-utils.h"
#include "src/execution/microtask-queue.h"
#include "src/execution/protectors-inl.h"
#include "src/heap/factory-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/heap/read-only-heap.h"
#include "src/logging/counters.h"
#include "src/logging/log.h"
#include "src/objects/allocation-site-inl.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/arguments-inl.h"
#include "src/objects/bigint.h"
#include "src/objects/call-site-info-inl.h"
#include "src/objects/cell-inl.h"
#include "src/objects/code-inl.h"
#include "src/objects/compilation-cache-table-inl.h"
#include "src/objects/debug-objects-inl.h"
#include "src/objects/dictionary.h"
#include "src/objects/elements.h"
#include "src/objects/embedder-data-array-inl.h"
#include "src/objects/field-index-inl.h"
#include "src/objects/field-index.h"
#include "src/objects/field-type.h"
#include "src/objects/free-space-inl.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/heap-object-inl.h"
#include "src/objects/instance-type.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-collection-inl.h"
#include "src/objects/js-disposable-stack-inl.h"
#include "src/objects/js-generator-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/js-weak-refs-inl.h"
#include "src/objects/keys.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/lookup-inl.h"
#include "src/objects/map-inl.h"
#include "src/objects/map.h"
#include "src/objects/megadom-handler-inl.h"
#include "src/objects/microtask-inl.h"
#include "src/objects/module-inl.h"
#include "src/objects/objects-body-descriptors-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/promise-inl.h"
#include "src/objects/promise.h"
#include "src/objects/property-descriptor-object-inl.h"
#include "src/objects/property-descriptor.h"
#include "src/objects/property-details.h"
#include "src/objects/slots-atomic-inl.h"
#include "src/objects/string-set-inl.h"
#include "src/objects/struct-inl.h"
#include "src/objects/template-objects-inl.h"
#include "src/objects/transitions-inl.h"
#include "src/parsing/preparse-data.h"
#include "src/regexp/regexp.h"
#include "src/roots/roots.h"
#include "src/snapshot/deserializer.h"
#include "src/strings/string-builder-inl.h"
#include "src/strings/string-stream.h"
#include "src/strings/unicode-inl.h"
#include "src/utils/identity-map.h"
#include "src/utils/ostreams.h"
#include "src/utils/utils-inl.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-objects.h"
#endif
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-break-iterator.h"
#include "src/objects/js-collator.h"
#include "src/objects/js-date-time-format.h"
#include "src/objects/js-list-format.h"
#include "src/objects/js-locale.h"
#include "src/objects/js-number-format.h"
#include "src/objects/js-plural-rules.h"
#include "src/objects/js-relative-time-format.h"
#include "src/objects/js-segment-iterator.h"
#include "src/objects/js-segmenter.h"
#include "src/objects/js-segments.h"
#endif
namespace v8::internal {
ShouldThrow GetShouldThrow(Isolate* isolate, Maybe<ShouldThrow> should_throw) {
if (should_throw.IsJust()) return should_throw.FromJust();
LanguageMode mode = isolate->context()->scope_info()->language_mode();
if (mode == LanguageMode::kStrict) return kThrowOnError;
for (StackFrameIterator it(isolate, isolate->thread_local_top()); !it.done();
it.Advance()) {
if (!it.frame()->is_javascript()) continue;
JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(it.frame());
std::vector<Tagged<SharedFunctionInfo>> functions;
js_frame->GetFunctions(&functions);
LanguageMode closure_language_mode = functions.back()->language_mode();
if (closure_language_mode > mode) {
mode = closure_language_mode;
}
break;
}
return is_sloppy(mode) ? kDontThrow : kThrowOnError;
}
bool ComparisonResultToBool(Operation op, ComparisonResult result) {
switch (op) {
case Operation::kLessThan:
return result == ComparisonResult::kLessThan;
case Operation::kLessThanOrEqual:
return result == ComparisonResult::kLessThan ||
result == ComparisonResult::kEqual;
case Operation::kGreaterThan:
return result == ComparisonResult::kGreaterThan;
case Operation::kGreaterThanOrEqual:
return result == ComparisonResult::kGreaterThan ||
result == ComparisonResult::kEqual;
default:
break;
}
UNREACHABLE();
}
std::string ToString(InstanceType instance_type) {
if (InstanceTypeChecker::IsJSApiObject(instance_type)) {
std::stringstream ss;
ss << "[api object] "
<< static_cast<int16_t>(instance_type) -
i::Internals::kFirstJSApiObjectType;
return ss.str();
}
switch (instance_type) {
#define WRITE_TYPE(TYPE) \
case TYPE: \
return #TYPE;
INSTANCE_TYPE_LIST(WRITE_TYPE)
#undef WRITE_TYPE
}
std::stringstream ss;
ss << "[unknown instance type " << static_cast<int16_t>(instance_type) << "]";
return ss.str();
}
std::ostream& operator<<(std::ostream& os, InstanceType instance_type) {
return os << ToString(instance_type);
}
std::ostream& operator<<(std::ostream& os, PropertyCellType type) {
switch (type) {
case PropertyCellType::kUndefined:
return os << "Undefined";
case PropertyCellType::kConstant:
return os << "Constant";
case PropertyCellType::kConstantType:
return os << "ConstantType";
case PropertyCellType::kMutable:
return os << "Mutable";
case PropertyCellType::kInTransition:
return os << "InTransition";
}
UNREACHABLE();
}
DirectHandle<FieldType> Object::OptimalType(Tagged<Object> obj,
Isolate* isolate,
Representation representation) {
if (representation.IsNone()) return FieldType::None(isolate);
if (v8_flags.track_field_types) {
if (representation.IsHeapObject() && IsHeapObject(obj)) {
DirectHandle<Map> map(Cast<HeapObject>(obj)->map(), isolate);
if (map->is_stable() && IsJSReceiverMap(*map)) {
return FieldType::Class(map, isolate);
}
}
}
return FieldType::Any(isolate);
}
Handle<UnionOf<JSAny, Hole>> Object::NewStorageFor(
Isolate* isolate, Handle<UnionOf<JSAny, Hole>> object,
Representation representation) {
if (!representation.IsDouble()) return object;
Handle<HeapNumber> result = isolate->factory()->NewHeapNumberWithHoleNaN();
if (IsUninitializedHole(*object, isolate)) {
result->set_value_as_bits(kHoleNanInt64);
} else if (IsHeapNumber(*object)) {
result->set_value_as_bits(Cast<HeapNumber>(*object)->value_as_bits());
} else {
result->set_value(Cast<Smi>(*object).value());
}
return result;
}
template <AllocationType allocation_type, typename IsolateT>
Handle<JSAny> Object::WrapForRead(IsolateT* isolate, Handle<JSAny> object,
Representation representation) {
DCHECK(!IsUninitializedHole(*object, isolate));
if (!representation.IsDouble()) {
DCHECK(Object::FitsRepresentation(*object, representation));
return object;
}
return isolate->factory()->template NewHeapNumberFromBits<allocation_type>(
Cast<HeapNumber>(*object)->value_as_bits());
}
template Handle<JSAny> Object::WrapForRead<AllocationType::kYoung>(
Isolate* isolate, Handle<JSAny> object, Representation representation);
template Handle<JSAny> Object::WrapForRead<AllocationType::kOld>(
LocalIsolate* isolate, Handle<JSAny> object, Representation representation);
MaybeHandle<JSReceiver> Object::ToObjectImpl(Isolate* isolate,
DirectHandle<Object> object,
const char* method_name) {
DCHECK(!IsJSReceiver(*object));
DirectHandle<Context> native_context = isolate->native_context();
DirectHandle<JSFunction> constructor;
if (IsSmi(*object)) {
constructor = direct_handle(native_context->number_function(), isolate);
} else {
int constructor_function_index =
Cast<HeapObject>(object)->map()->GetConstructorFunctionIndex();
if (constructor_function_index == Map::kNoConstructorFunctionIndex) {
if (method_name != nullptr) {
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kUndefinedOrNullToObject));
}
constructor = direct_handle(
Cast<JSFunction>(native_context->GetNoCell(constructor_function_index)),
isolate);
}
Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
Cast<JSPrimitiveWrapper>(result)->set_value(Cast<JSAny>(*object));
return result;
}
MaybeDirectHandle<JSReceiver> Object::ConvertReceiver(
Isolate* isolate, DirectHandle<Object> object) {
if (IsJSReceiver(*object)) return Cast<JSReceiver>(object);
if (IsNullOrUndefined(*object, isolate)) {
return isolate->global_proxy();
}
return Object::ToObject(isolate, object);
}
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Number>::MaybeType Object::ConvertToNumber(
Isolate* isolate, HandleType<Object> input) {
while (true) {
if (IsNumber(*input)) {
return Cast<Number>(input);
}
if (IsString(*input)) {
return String::ToNumber(isolate, Cast<String>(input));
}
if (IsOddball(*input)) {
return Oddball::ToNumber(isolate, Cast<Oddball>(input));
}
if (IsSymbol(*input)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber));
}
if (IsBigInt(*input)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kBigIntToNumber));
}
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(input),
ToPrimitiveHint::kNumber));
}
}
template V8_EXPORT_PRIVATE MaybeDirectHandle<Number> Object::ConvertToNumber(
Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Number> Object::ConvertToNumber(
Isolate* isolate, IndirectHandle<Object> input);
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Numeric>::MaybeType Object::ConvertToNumeric(
Isolate* isolate, HandleType<Object> input) {
while (true) {
if (IsNumber(*input)) {
return Cast<Number>(input);
}
if (IsString(*input)) {
return String::ToNumber(isolate, Cast<String>(input));
}
if (IsOddball(*input)) {
return Oddball::ToNumber(isolate, Cast<Oddball>(input));
}
if (IsSymbol(*input)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber));
}
if (IsBigInt(*input)) {
return Cast<BigInt>(input);
}
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(input),
ToPrimitiveHint::kNumber));
}
}
template V8_EXPORT_PRIVATE MaybeDirectHandle<Numeric> Object::ConvertToNumeric(
Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Numeric>
Object::ConvertToNumeric(Isolate* isolate, IndirectHandle<Object> input);
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Number>::MaybeType Object::ConvertToInteger(
Isolate* isolate, HandleType<Object> input) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ConvertToNumber(isolate, input));
if (IsSmi(*input)) return Cast<Smi>(input);
return isolate->factory()->NewNumber(
DoubleToInteger(Cast<HeapNumber>(*input)->value()));
}
template V8_EXPORT_PRIVATE MaybeDirectHandle<Number> Object::ConvertToInteger(
Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Number> Object::ConvertToInteger(
Isolate* isolate, IndirectHandle<Object> input);
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Number>::MaybeType Object::ConvertToInt32(
Isolate* isolate, HandleType<Object> input) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ConvertToNumber(isolate, input));
if (IsSmi(*input)) return Cast<Smi>(input);
return isolate->factory()->NewNumberFromInt(
DoubleToInt32(Cast<HeapNumber>(*input)->value()));
}
template MaybeDirectHandle<Number> Object::ConvertToInt32(
Isolate* isolate, DirectHandle<Object> input);
template MaybeIndirectHandle<Number> Object::ConvertToInt32(
Isolate* isolate, IndirectHandle<Object> input);
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Number>::MaybeType Object::ConvertToUint32(
Isolate* isolate, HandleType<Object> input) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ConvertToNumber(isolate, input));
if (IsSmi(*input))
return typename HandleType<Number>::MaybeType(
Smi::ToUint32Smi(Cast<Smi>(*input)), isolate);
return isolate->factory()->NewNumberFromUint(
DoubleToUint32(Cast<HeapNumber>(*input)->value()));
}
template MaybeDirectHandle<Number> Object::ConvertToUint32(
Isolate* isolate, DirectHandle<Object> input);
template MaybeIndirectHandle<Number> Object::ConvertToUint32(
Isolate* isolate, IndirectHandle<Object> input);
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Name>::MaybeType Object::ConvertToName(
Isolate* isolate, HandleType<Object> input) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
Object::ToPrimitive(isolate, input, ToPrimitiveHint::kString));
if (IsName(*input)) return Cast<Name>(input);
return ToString(isolate, input);
}
template V8_EXPORT_PRIVATE MaybeDirectHandle<Name> Object::ConvertToName(
Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Name> Object::ConvertToName(
Isolate* isolate, IndirectHandle<Object> input);
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Object>::MaybeType Object::ConvertToPropertyKey(
Isolate* isolate, HandleType<Object> value) {
typename HandleType<Object>::MaybeType maybe_key =
Object::ToPrimitive(isolate, value, ToPrimitiveHint::kString);
HandleType<Object> key;
if (!maybe_key.ToHandle(&key)) return key;
if (IsSymbol(*key)) return key;
if (IsSmi(*key)) return key;
if (IsHeapNumber(*key)) {
uint32_t uint_value;
if (Object::ToArrayLength(*value, &uint_value) &&
uint_value <= static_cast<uint32_t>(Smi::kMaxValue)) {
return HandleType<Object>(Smi::FromInt(static_cast<int>(uint_value)),
isolate);
}
}
return Object::ToString(isolate, key);
}
template V8_EXPORT_PRIVATE MaybeDirectHandle<Object>
Object::ConvertToPropertyKey(Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Object>
Object::ConvertToPropertyKey(Isolate* isolate, IndirectHandle<Object> input);
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<String>::MaybeType Object::ConvertToString(
Isolate* isolate, HandleType<Object> input) {
while (true) {
if (IsOddball(*input)) {
HandleType<String> result(Cast<Oddball>(input)->to_string(), isolate);
return result;
}
if (IsNumber(*input)) {
return isolate->factory()->NumberToString(input);
}
if (IsSymbol(*input)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToString));
}
if (IsBigInt(*input)) {
return BigInt::ToString(isolate, Cast<BigInt>(input));
}
#if V8_ENABLE_WEBASSEMBLY
if (IsWasmNull(*input)) {
return isolate->factory()->null_string();
}
#endif
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(input),
ToPrimitiveHint::kString));
if (IsString(*input)) {
return Cast<String>(input);
}
}
}
template V8_EXPORT_PRIVATE MaybeDirectHandle<String> Object::ConvertToString(
Isolate* isolate, DirectHandle<Object> input);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<String> Object::ConvertToString(
Isolate* isolate, IndirectHandle<Object> input);
namespace {
bool IsErrorObject(Isolate* isolate, DirectHandle<Object> object) {
if (!IsJSObject(*object)) return false;
return ErrorUtils::HasErrorStackSymbolOwnProperty(isolate,
Cast<JSObject>(object));
}
DirectHandle<String> AsStringOrEmpty(Isolate* isolate,
DirectHandle<Object> object) {
return IsString(*object) ? Cast<String>(object)
: isolate->factory()->empty_string();
}
DirectHandle<String> NoSideEffectsErrorToString(
Isolate* isolate, DirectHandle<JSReceiver> error) {
DirectHandle<Name> name_key = isolate->factory()->name_string();
DirectHandle<Object> name =
JSReceiver::GetDataProperty(isolate, error, name_key);
DirectHandle<String> name_str = AsStringOrEmpty(isolate, name);
DirectHandle<Name> msg_key = isolate->factory()->message_string();
DirectHandle<Object> msg =
JSReceiver::GetDataProperty(isolate, error, msg_key);
DirectHandle<String> msg_str = AsStringOrEmpty(isolate, msg);
if (name_str->length() == 0) return msg_str;
if (msg_str->length() == 0) return name_str;
constexpr const char error_suffix[] = "<a very large string>";
constexpr uint32_t error_suffix_size = sizeof(error_suffix);
uint32_t suffix_size = std::min(error_suffix_size, msg_str->length());
IncrementalStringBuilder builder(isolate);
if (name_str->length() + suffix_size + 2 > String::kMaxLength) {
constexpr const char connector[] = "... : ";
int connector_size = sizeof(connector);
DirectHandle<String> truncated_name =
isolate->factory()->NewProperSubString(
name_str, 0,
name_str->length() - error_suffix_size - connector_size);
builder.AppendString(truncated_name);
builder.AppendCStringLiteral(connector);
builder.AppendCStringLiteral(error_suffix);
} else {
builder.AppendString(name_str);
builder.AppendCStringLiteral(": ");
if (builder.Length() + msg_str->length() <= String::kMaxLength) {
builder.AppendString(msg_str);
} else {
builder.AppendCStringLiteral(error_suffix);
}
}
return builder.Finish().ToHandleChecked();
}
}
MaybeDirectHandle<String> Object::NoSideEffectsToMaybeString(
Isolate* isolate, DirectHandle<Object> input) {
DisallowJavascriptExecution no_js(isolate);
if (IsAnyHole(*input, isolate)) {
ReadOnlyRoots roots(isolate);
#define HOLE_CASE(CamelName, snake_name, _) \
if (Is##CamelName(*input)) { \
return isolate->factory()->NewStringFromAsciiChecked(#CamelName); \
}
HOLE_LIST(HOLE_CASE)
#undef HOLE_CASE
UNREACHABLE();
} else if (IsString(*input) || IsNumber(*input) || IsOddball(*input)) {
return Object::ToString(isolate, input).ToHandleChecked();
} else if (IsJSProxy(*input)) {
DirectHandle<Object> currInput = input;
do {
Tagged<HeapObject> target = Cast<JSProxy>(currInput)->target(isolate);
currInput = direct_handle(target, isolate);
} while (IsJSProxy(*currInput));
return NoSideEffectsToString(isolate, currInput);
} else if (IsBigInt(*input)) {
return BigInt::NoSideEffectsToString(isolate, Cast<BigInt>(input));
} else if (IsJSFunctionOrBoundFunctionOrWrappedFunction(*input)) {
DirectHandle<String> fun_str;
if (IsJSBoundFunction(*input)) {
fun_str =
JSBoundFunction::ToString(isolate, Cast<JSBoundFunction>(input));
} else if (IsJSWrappedFunction(*input)) {
fun_str =
JSWrappedFunction::ToString(isolate, Cast<JSWrappedFunction>(input));
} else {
DCHECK(IsJSFunction(*input));
fun_str = JSFunction::ToString(isolate, Cast<JSFunction>(input));
}
if (fun_str->length() > 128) {
IncrementalStringBuilder builder(isolate);
builder.AppendString(isolate->factory()->NewSubString(fun_str, 0, 111));
builder.AppendCStringLiteral("...<omitted>...");
builder.AppendString(isolate->factory()->NewSubString(
fun_str, fun_str->length() - 2, fun_str->length()));
return builder.Finish().ToHandleChecked();
}
return fun_str;
} else if (IsSymbol(*input)) {
DirectHandle<Symbol> symbol = Cast<Symbol>(input);
if (symbol->is_private_name()) {
return DirectHandle<String>(Cast<String>(symbol->description()), isolate);
}
IncrementalStringBuilder builder(isolate);
builder.AppendCStringLiteral("Symbol(");
if (IsString(symbol->description())) {
DirectHandle<String> description(Cast<String>(symbol->description()),
isolate);
if (description->length() > 128) {
builder.AppendString(
isolate->factory()->NewSubString(description, 0, 56));
builder.AppendCStringLiteral("...<omitted>...");
builder.AppendString(isolate->factory()->NewSubString(
description, description->length() - 56, description->length()));
} else {
builder.AppendString(description);
}
}
builder.AppendCharacter(')');
return builder.Finish().ToHandleChecked();
} else if (IsJSReceiver(*input)) {
DirectHandle<JSReceiver> receiver = Cast<JSReceiver>(input);
DirectHandle<Object> to_string = JSReceiver::GetDataProperty(
isolate, receiver, isolate->factory()->toString_string());
if (IsErrorObject(isolate, input) ||
*to_string == *isolate->error_to_string()) {
return NoSideEffectsErrorToString(isolate, receiver);
} else if (*to_string == *isolate->object_to_string()) {
DirectHandle<Object> ctor = JSReceiver::GetDataProperty(
isolate, receiver, isolate->factory()->constructor_string());
if (IsJSFunctionOrBoundFunctionOrWrappedFunction(*ctor)) {
DirectHandle<String> ctor_name;
if (IsJSBoundFunction(*ctor)) {
ctor_name =
JSBoundFunction::GetName(isolate, Cast<JSBoundFunction>(ctor))
.ToHandleChecked();
} else if (IsJSFunction(*ctor)) {
ctor_name = JSFunction::GetName(isolate, Cast<JSFunction>(ctor));
} else if (IsJSWrappedFunction(*ctor)) {
ctor_name =
JSWrappedFunction::GetName(isolate, Cast<JSWrappedFunction>(ctor))
.ToHandleChecked();
} else {
UNREACHABLE();
}
if (ctor_name->length() != 0) {
IncrementalStringBuilder builder(isolate);
builder.AppendCStringLiteral("#<");
builder.AppendString(ctor_name);
builder.AppendCharacter('>');
return builder.Finish().ToHandleChecked();
}
}
}
}
return {};
}
DirectHandle<String> Object::NoSideEffectsToString(Isolate* isolate,
DirectHandle<Object> input) {
DisallowJavascriptExecution no_js(isolate);
MaybeDirectHandle<String> maybe_string =
NoSideEffectsToMaybeString(isolate, input);
DirectHandle<String> string_handle;
if (maybe_string.ToHandle(&string_handle)) {
return string_handle;
}
DirectHandle<JSReceiver> receiver;
if (IsJSReceiver(*input)) {
receiver = Cast<JSReceiver>(input);
} else {
DCHECK(!IsSmi(*input));
int constructor_function_index =
Cast<HeapObject>(input)->map()->GetConstructorFunctionIndex();
if (constructor_function_index == Map::kNoConstructorFunctionIndex) {
return isolate->factory()->NewStringFromAsciiChecked("[object Unknown]");
}
receiver = Object::ToObjectImpl(isolate, input).ToHandleChecked();
}
DirectHandle<String> builtin_tag =
direct_handle(receiver->class_name(), isolate);
DirectHandle<Object> tag_obj = JSReceiver::GetDataProperty(
isolate, receiver, isolate->factory()->to_string_tag_symbol());
DirectHandle<String> tag =
IsString(*tag_obj) ? Cast<String>(tag_obj) : builtin_tag;
IncrementalStringBuilder builder(isolate);
builder.AppendCStringLiteral("[object ");
builder.AppendString(tag);
builder.AppendCharacter(']');
return builder.Finish().ToHandleChecked();
}
MaybeHandle<Number> Object::ConvertToLength(Isolate* isolate,
DirectHandle<Object> input) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input));
if (IsSmi(*input)) {
int value = std::max(Smi::ToInt(*input), 0);
return handle(Smi::FromInt(value), isolate);
}
double len = DoubleToInteger(Cast<HeapNumber>(*input)->value());
if (len <= 0.0) {
return handle(Smi::zero(), isolate);
} else if (len >= kMaxSafeInteger) {
len = kMaxSafeInteger;
}
return isolate->factory()->NewNumber(len);
}
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Number>::MaybeType Object::ConvertToIndex(
Isolate* isolate, HandleType<Object> input, MessageTemplate error_index) {
if (IsUndefined(*input, isolate))
return HandleType<Number>(Smi::zero(), isolate);
ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input));
if (IsSmi(*input) && Smi::ToInt(*input) >= 0) return Cast<Smi>(input);
double len = DoubleToInteger(Object::NumberValue(Cast<Number>(*input)));
HandleType<Number> js_len = isolate->factory()->NewNumber(len);
if (len < 0.0 || len > kMaxSafeInteger) {
THROW_NEW_ERROR(isolate, NewRangeError(error_index, js_len));
}
return js_len;
}
template V8_EXPORT_PRIVATE MaybeDirectHandle<Number> Object::ConvertToIndex(
Isolate* isolate, DirectHandle<Object> input, MessageTemplate error_index);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Number> Object::ConvertToIndex(
Isolate* isolate, IndirectHandle<Object> input,
MessageTemplate error_index);
template <typename IsolateT>
bool Object::BooleanValue(Tagged<Object> obj, IsolateT* isolate) {
if (IsSmi(obj)) return Smi::ToInt(obj) != 0;
DCHECK(IsHeapObject(obj));
#ifdef V8_ENABLE_WEBASSEMBLY
DCHECK(!IsWasmNull(obj));
#endif
if (IsBoolean(obj)) return IsTrue(obj, isolate);
if (IsNullOrUndefined(obj, isolate)) return false;
if (IsUndetectable(obj)) return false;
if (IsString(obj)) return Cast<String>(obj)->length() != 0;
if (IsHeapNumber(obj)) return DoubleToBoolean(Cast<HeapNumber>(obj)->value());
if (IsBigInt(obj)) return Cast<BigInt>(obj)->ToBoolean();
return true;
}
template bool Object::BooleanValue(Tagged<Object>, Isolate*);
template bool Object::BooleanValue(Tagged<Object>, LocalIsolate*);
Tagged<Object> Object::ToBoolean(Tagged<Object> obj, Isolate* isolate) {
if (IsBoolean(obj)) return obj;
return isolate->heap()->ToBoolean(Object::BooleanValue(obj, isolate));
}
namespace {
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;
}
}
bool StrictNumberEquals(double x, double y) {
if (std::isnan(x) || std::isnan(y)) return false;
return x == y;
}
bool StrictNumberEquals(const Tagged<Number> x, const Tagged<Number> y) {
return StrictNumberEquals(Object::NumberValue(x), Object::NumberValue(y));
}
bool StrictNumberEquals(DirectHandle<Number> x, DirectHandle<Number> y) {
return StrictNumberEquals(*x, *y);
}
ComparisonResult Reverse(ComparisonResult result) {
if (result == ComparisonResult::kLessThan) {
return ComparisonResult::kGreaterThan;
}
if (result == ComparisonResult::kGreaterThan) {
return ComparisonResult::kLessThan;
}
return result;
}
}
Maybe<ComparisonResult> Object::Compare(Isolate* isolate,
DirectHandle<Object> x,
DirectHandle<Object> y) {
if (!Object::ToPrimitive(isolate, x, ToPrimitiveHint::kNumber).ToHandle(&x) ||
!Object::ToPrimitive(isolate, y, ToPrimitiveHint::kNumber).ToHandle(&y)) {
return Nothing<ComparisonResult>();
}
if (IsString(*x) && IsString(*y)) {
return Just(String::Compare(isolate, Cast<String>(x), Cast<String>(y)));
}
if (IsBigInt(*x) && IsString(*y)) {
return BigInt::CompareToString(isolate, Cast<BigInt>(x), Cast<String>(y));
}
if (IsString(*x) && IsBigInt(*y)) {
Maybe<ComparisonResult> maybe_result =
BigInt::CompareToString(isolate, Cast<BigInt>(y), Cast<String>(x));
ComparisonResult result;
if (maybe_result.To(&result)) {
return Just(Reverse(result));
} else {
return Nothing<ComparisonResult>();
}
}
if (!Object::ToNumeric(isolate, x).ToHandle(&x) ||
!Object::ToNumeric(isolate, y).ToHandle(&y)) {
return Nothing<ComparisonResult>();
}
bool x_is_number = IsNumber(*x);
bool y_is_number = IsNumber(*y);
if (x_is_number && y_is_number) {
return Just(
StrictNumberCompare(Object::NumberValue(*x), Object::NumberValue(*y)));
} else if (!x_is_number && !y_is_number) {
return Just(BigInt::CompareToBigInt(Cast<BigInt>(x), Cast<BigInt>(y)));
} else if (x_is_number) {
return Just(Reverse(BigInt::CompareToNumber(Cast<BigInt>(y), x)));
} else {
return Just(BigInt::CompareToNumber(Cast<BigInt>(x), y));
}
}
Maybe<bool> Object::Equals(Isolate* isolate, DirectHandle<Object> x,
DirectHandle<Object> y) {
while (true) {
if (IsNumber(*x)) {
if (IsNumber(*y)) {
return Just(StrictNumberEquals(Cast<Number>(*x), Cast<Number>(*y)));
} else if (IsBoolean(*y)) {
return Just(StrictNumberEquals(Cast<Number>(*x),
Cast<Oddball>(y)->to_number()));
} else if (IsString(*y)) {
return Just(StrictNumberEquals(
Cast<Number>(x), String::ToNumber(isolate, Cast<String>(y))));
} else if (IsBigInt(*y)) {
return Just(BigInt::EqualToNumber(Cast<BigInt>(y), Cast<Number>(x)));
} else if (IsJSReceiver(*y)) {
if (!JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
} else {
return Just(false);
}
} else if (IsString(*x)) {
if (IsString(*y)) {
return Just(String::Equals(isolate, Cast<String>(x), Cast<String>(y)));
} else if (IsNumber(*y)) {
x = String::ToNumber(isolate, Cast<String>(x));
return Just(StrictNumberEquals(Cast<Number>(*x), Cast<Number>(*y)));
} else if (IsBoolean(*y)) {
x = String::ToNumber(isolate, Cast<String>(x));
return Just(StrictNumberEquals(Cast<Number>(*x),
Cast<Oddball>(y)->to_number()));
} else if (IsBigInt(*y)) {
return BigInt::EqualToString(isolate, Cast<BigInt>(y), Cast<String>(x));
} else if (IsJSReceiver(*y)) {
if (!JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
} else {
return Just(false);
}
} else if (IsBoolean(*x)) {
if (IsOddball(*y)) {
return Just(x.is_identical_to(y));
} else if (IsNumber(*y)) {
return Just(StrictNumberEquals(Cast<Oddball>(x)->to_number(),
Cast<Number>(*y)));
} else if (IsString(*y)) {
y = String::ToNumber(isolate, Cast<String>(y));
return Just(StrictNumberEquals(Cast<Oddball>(x)->to_number(),
Cast<Number>(*y)));
} else if (IsBigInt(*y)) {
x = Oddball::ToNumber(isolate, Cast<Oddball>(x));
return Just(BigInt::EqualToNumber(Cast<BigInt>(y), x));
} else if (IsJSReceiver(*y)) {
if (!JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
x = Oddball::ToNumber(isolate, Cast<Oddball>(x));
} else {
return Just(false);
}
} else if (IsSymbol(*x)) {
if (IsSymbol(*y)) {
return Just(x.is_identical_to(y));
} else if (IsJSReceiver(*y)) {
if (!JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
} else {
return Just(false);
}
} else if (IsBigInt(*x)) {
if (IsBigInt(*y)) {
return Just(BigInt::EqualToBigInt(Cast<BigInt>(*x), Cast<BigInt>(*y)));
}
return Equals(isolate, y, x);
} else if (IsJSReceiver(*x)) {
if (IsJSReceiver(*y)) {
return Just(x.is_identical_to(y));
} else if (IsUndetectable(*y)) {
return Just(IsUndetectable(*x));
} else if (IsBoolean(*y)) {
y = Oddball::ToNumber(isolate, Cast<Oddball>(y));
} else if (!JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(x))
.ToHandle(&x)) {
return Nothing<bool>();
}
} else {
return Just(IsUndetectable(*x) && IsUndetectable(*y));
}
}
}
bool Object::StrictEquals(Tagged<Object> obj, Tagged<Object> that) {
if (IsNumber(obj)) {
if (!IsNumber(that)) return false;
return StrictNumberEquals(Cast<Number>(obj), Cast<Number>(that));
} else if (IsString(obj)) {
if (!IsString(that)) return false;
return Cast<String>(obj)->Equals(Cast<String>(that));
} else if (IsBigInt(obj)) {
if (!IsBigInt(that)) return false;
return BigInt::EqualToBigInt(Cast<BigInt>(obj), Cast<BigInt>(that));
}
return obj == that;
}
Handle<String> Object::TypeOf(Isolate* isolate, DirectHandle<Object> object) {
if (IsNumber(*object)) return isolate->factory()->number_string();
if (IsOddball(*object))
return handle(Cast<Oddball>(*object)->type_of(), isolate);
if (IsUndetectable(*object)) {
return isolate->factory()->undefined_string();
}
if (IsString(*object)) return isolate->factory()->string_string();
if (IsSymbol(*object)) return isolate->factory()->symbol_string();
if (IsBigInt(*object)) return isolate->factory()->bigint_string();
if (IsCallable(*object)) return isolate->factory()->function_string();
return isolate->factory()->object_string();
}
MaybeDirectHandle<Object> Object::Add(Isolate* isolate, Handle<Object> lhs,
Handle<Object> rhs) {
if (IsNumber(*lhs) && IsNumber(*rhs)) {
return isolate->factory()->NewNumber(
Object::NumberValue(Cast<Number>(*lhs)) +
Object::NumberValue(Cast<Number>(*rhs)));
} else if (IsString(*lhs) && IsString(*rhs)) {
return isolate->factory()->NewConsString(Cast<String>(lhs),
Cast<String>(rhs));
}
ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToPrimitive(isolate, lhs));
ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToPrimitive(isolate, rhs));
if (IsString(*lhs) || IsString(*rhs)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToString(isolate, rhs));
ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToString(isolate, lhs));
return isolate->factory()->NewConsString(Cast<String>(lhs),
Cast<String>(rhs));
}
DirectHandle<Number> lhs_number;
DirectHandle<Number> rhs_number;
ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs_number,
Object::ToNumber(isolate, rhs));
ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs_number,
Object::ToNumber(isolate, lhs));
return isolate->factory()->NewNumber(Object::NumberValue(*lhs_number) +
Object::NumberValue(*rhs_number));
}
MaybeHandle<Object> Object::OrdinaryHasInstance(Isolate* isolate,
DirectHandle<JSAny> callable,
DirectHandle<JSAny> object) {
if (!IsCallable(*callable)) return isolate->factory()->false_value();
if (IsJSBoundFunction(*callable)) {
STACK_CHECK(isolate, MaybeHandle<Object>());
DirectHandle<JSCallable> bound_callable(
Cast<JSBoundFunction>(callable)->bound_target_function(), isolate);
return Object::InstanceOf(isolate, object, bound_callable);
}
if (!IsJSReceiver(*object)) return isolate->factory()->false_value();
DirectHandle<Object> prototype;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, prototype,
Object::GetProperty(isolate, callable,
isolate->factory()->prototype_string()));
if (!IsJSReceiver(*prototype)) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype));
}
Maybe<bool> result = JSReceiver::HasInPrototypeChain(
isolate, Cast<JSReceiver>(object), prototype);
if (result.IsNothing()) return MaybeHandle<Object>();
return isolate->factory()->ToBoolean(result.FromJust());
}
MaybeHandle<Object> Object::InstanceOf(Isolate* isolate,
DirectHandle<JSAny> object,
DirectHandle<JSAny> callable) {
if (!IsJSReceiver(*callable)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kNonObjectInInstanceOfCheck));
}
DirectHandle<Object> inst_of_handler;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, inst_of_handler,
Object::GetMethod(isolate, Cast<JSReceiver>(callable),
isolate->factory()->has_instance_symbol()));
if (!IsUndefined(*inst_of_handler, isolate)) {
DirectHandle<Object> result;
DirectHandle<Object> args[] = {object};
ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
Execution::Call(isolate, inst_of_handler,
callable, base::VectorOf(args)));
return isolate->factory()->ToBoolean(
Object::BooleanValue(*result, isolate));
}
if (!IsCallable(*callable)) {
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kNonCallableInInstanceOfCheck));
}
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result, Object::OrdinaryHasInstance(isolate, callable, object));
return result;
}
MaybeDirectHandle<Object> Object::GetMethod(Isolate* isolate,
DirectHandle<JSReceiver> receiver,
DirectHandle<Name> name) {
Handle<Object> func;
ASSIGN_RETURN_ON_EXCEPTION(isolate, func,
JSReceiver::GetProperty(isolate, receiver, name));
if (IsNullOrUndefined(*func, isolate)) {
return isolate->factory()->undefined_value();
}
if (!IsCallable(*func)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kPropertyNotFunction,
func, name, receiver));
}
return func;
}
namespace {
MaybeDirectHandle<FixedArray> CreateListFromArrayLikeFastPath(
Isolate* isolate, DirectHandle<Object> object, ElementTypes element_types) {
if (element_types == ElementTypes::kAll) {
if (IsJSArray(*object)) {
DirectHandle<JSArray> array = Cast<JSArray>(object);
uint32_t length;
if (!array->HasArrayPrototype(isolate) ||
!Object::ToUint32(array->length(), &length) ||
!array->HasFastElements() ||
!JSObject::PrototypeHasNoElements(isolate, *array)) {
return MaybeDirectHandle<FixedArray>();
}
return array->GetElementsAccessor()->CreateListFromArrayLike(
isolate, array, length);
} else if (IsJSTypedArray(*object)) {
DirectHandle<JSTypedArray> array = Cast<JSTypedArray>(object);
size_t length = array->GetLength();
if (array->IsDetachedOrOutOfBounds() ||
length > static_cast<size_t>(FixedArray::kMaxLength)) {
return MaybeDirectHandle<FixedArray>();
}
static_assert(FixedArray::kMaxLength <=
std::numeric_limits<uint32_t>::max());
return array->GetElementsAccessor()->CreateListFromArrayLike(
isolate, array, static_cast<uint32_t>(length));
}
}
return MaybeDirectHandle<FixedArray>();
}
}
MaybeDirectHandle<FixedArray> Object::CreateListFromArrayLike(
Isolate* isolate, DirectHandle<Object> object, ElementTypes element_types) {
MaybeDirectHandle<FixedArray> fast_result =
CreateListFromArrayLikeFastPath(isolate, object, element_types);
if (!fast_result.is_null()) return fast_result;
if (!IsJSReceiver(*object)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kCalledOnNonObject,
isolate->factory()->NewStringFromAsciiChecked(
"CreateListFromArrayLike")));
}
DirectHandle<JSReceiver> receiver = Cast<JSReceiver>(object);
DirectHandle<Object> raw_length_number;
ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number,
Object::GetLengthFromArrayLike(isolate, receiver));
uint32_t len;
if (!Object::ToUint32(*raw_length_number, &len) ||
len > static_cast<uint32_t>(FixedArray::kMaxLength)) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kInvalidArrayLength));
}
DirectHandle<FixedArray> list = isolate->factory()->NewFixedArray(len);
for (uint32_t index = 0; index < len; ++index) {
DirectHandle<Object> next;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, next, JSReceiver::GetElement(isolate, receiver, index));
switch (element_types) {
case ElementTypes::kAll:
break;
case ElementTypes::kStringAndSymbol: {
if (!IsName(*next)) {
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kNotPropertyName, next));
}
next = isolate->factory()->InternalizeName(Cast<Name>(next));
break;
}
}
list->set(index, *next);
}
return list;
}
MaybeDirectHandle<Object> Object::GetLengthFromArrayLike(
Isolate* isolate, DirectHandle<JSReceiver> object) {
DirectHandle<Object> val;
DirectHandle<Name> key = isolate->factory()->length_string();
ASSIGN_RETURN_ON_EXCEPTION(isolate, val,
JSReceiver::GetProperty(isolate, object, key));
return Object::ToLength(isolate, val);
}
MaybeHandle<Object> Object::GetProperty(LookupIterator* it,
bool is_global_reference) {
for (;; it->Next()) {
switch (it->state()) {
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::JSPROXY: {
bool was_found;
DirectHandle<JSAny> receiver = it->GetReceiver();
if (IsJSGlobalObject(*receiver)) {
receiver = direct_handle(
Cast<JSGlobalObject>(*receiver)->global_proxy(), it->isolate());
}
if (is_global_reference) {
Maybe<bool> maybe = JSProxy::HasProperty(
it->isolate(), it->GetHolder<JSProxy>(), it->GetName());
if (maybe.IsNothing()) return {};
if (!maybe.FromJust()) {
it->NotFound();
return it->isolate()->factory()->undefined_value();
}
}
MaybeHandle<JSAny> result =
JSProxy::GetProperty(it->isolate(), it->GetHolder<JSProxy>(),
it->GetName(), receiver, &was_found);
if (!was_found && !is_global_reference) it->NotFound();
return result;
}
case LookupIterator::WASM_OBJECT:
continue;
case LookupIterator::INTERCEPTOR: {
bool done;
Handle<JSAny> result;
ASSIGN_RETURN_ON_EXCEPTION(
it->isolate(), result,
JSObject::GetPropertyWithInterceptor(it, &done));
if (done) return result;
continue;
}
case LookupIterator::ACCESS_CHECK:
if (it->HasAccess()) continue;
return JSObject::GetPropertyWithFailedAccessCheck(it);
case LookupIterator::ACCESSOR:
return GetPropertyWithAccessor(it);
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
return it->isolate()->factory()->undefined_value();
case LookupIterator::DATA:
return it->GetDataValue();
case LookupIterator::STRING_LOOKUP_START_OBJECT:
return it->GetStringPropertyValue();
case LookupIterator::NOT_FOUND:
if (it->IsPrivateName()) {
auto private_symbol = Cast<Symbol>(it->name());
DirectHandle<String> name_string(
Cast<String>(private_symbol->description()), it->isolate());
if (private_symbol->is_private_brand()) {
DirectHandle<String> class_name =
name_string->length() == 0
? it->isolate()->factory()->anonymous_string()
: name_string;
THROW_NEW_ERROR(
it->isolate(),
NewTypeError(MessageTemplate::kInvalidPrivateBrandInstance,
class_name));
}
THROW_NEW_ERROR(
it->isolate(),
NewTypeError(MessageTemplate::kInvalidPrivateMemberRead,
name_string));
}
return it->isolate()->factory()->undefined_value();
}
UNREACHABLE();
}
}
MaybeHandle<JSAny> JSProxy::GetProperty(Isolate* isolate,
DirectHandle<JSProxy> proxy,
DirectHandle<Name> name,
DirectHandle<JSAny> receiver,
bool* was_found) {
*was_found = true;
DCHECK(!name->IsPrivate());
STACK_CHECK(isolate, kNullMaybeHandle);
DirectHandle<Name> trap_name = isolate->factory()->get_string();
DirectHandle<UnionOf<JSReceiver, Null>> handler(proxy->handler(), isolate);
if (proxy->IsRevoked()) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
}
DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
DirectHandle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap,
Object::GetMethod(isolate, Cast<JSReceiver>(handler), trap_name));
if (IsUndefined(*trap, isolate)) {
PropertyKey key(isolate, name);
LookupIterator it(isolate, receiver, key, target);
MaybeHandle<JSAny> result = Cast<JSAny>(Object::GetProperty(&it));
*was_found = it.IsFound();
return result;
}
Handle<Object> trap_result;
DirectHandle<Object> args[] = {target, name, receiver};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result,
Execution::Call(isolate, trap, handler, base::VectorOf(args)));
MaybeHandle<JSAny> result =
JSProxy::CheckGetSetTrapResult(isolate, name, target, trap_result, kGet);
if (result.is_null()) {
return result;
}
return Cast<JSAny>(trap_result);
}
MaybeHandle<JSAny> JSProxy::CheckGetSetTrapResult(
Isolate* isolate, DirectHandle<Name> name, DirectHandle<JSReceiver> target,
DirectHandle<Object> trap_result, AccessKind access_kind) {
PropertyDescriptor target_desc;
Maybe<bool> target_found =
JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
MAYBE_RETURN_NULL(target_found);
if (target_found.FromJust()) {
bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) &&
!target_desc.configurable() &&
!target_desc.writable() &&
!Object::SameValue(*trap_result, *target_desc.value());
if (inconsistent) {
if (access_kind == kGet) {
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kProxyGetNonConfigurableData,
name, target_desc.value(), trap_result));
} else {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxySetFrozenData, name));
return {};
}
}
if (access_kind == kGet) {
inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
!target_desc.configurable() &&
IsUndefined(*target_desc.get(), isolate) &&
!IsUndefined(*trap_result, isolate);
} else {
inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
!target_desc.configurable() &&
IsUndefined(*target_desc.set(), isolate);
}
if (inconsistent) {
if (access_kind == kGet) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor,
name, trap_result));
} else {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxySetFrozenAccessor, name));
return {};
}
}
}
return isolate->factory()->undefined_value();
}
bool Object::ToInt32(Tagged<Object> obj, int32_t* value) {
if (IsSmi(obj)) {
*value = Smi::ToInt(obj);
return true;
}
if (IsHeapNumber(obj)) {
double num = Cast<HeapNumber>(obj)->value();
if (num >= kMinInt && num <= kMaxInt && FastI2D(FastD2I(num)) == num) {
*value = FastD2I(num);
return true;
}
}
return false;
}
MaybeDirectHandle<JSPrototype> JSProxy::GetPrototype(
DirectHandle<JSProxy> proxy) {
Isolate* isolate = Isolate::Current();
DirectHandle<String> trap_name = isolate->factory()->getPrototypeOf_string();
STACK_CHECK(isolate, {});
if (proxy->IsRevoked()) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
}
DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
DirectHandle<JSReceiver> handler(Cast<JSReceiver>(proxy->handler()), isolate);
DirectHandle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
Object::GetMethod(isolate, handler, trap_name));
if (IsUndefined(*trap, isolate)) {
return JSReceiver::GetPrototype(isolate, target);
}
DirectHandle<Object> args[] = {target};
DirectHandle<Object> handler_proto_result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, handler_proto_result,
Execution::Call(isolate, trap, handler, base::VectorOf(args)));
DirectHandle<JSPrototype> handler_proto;
if (!TryCast(handler_proto_result, &handler_proto)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid));
}
Maybe<bool> is_extensible = JSReceiver::IsExtensible(isolate, target);
MAYBE_RETURN(is_extensible, {});
if (is_extensible.FromJust()) return handler_proto;
DirectHandle<JSPrototype> target_proto;
ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto,
JSReceiver::GetPrototype(isolate, target));
if (!Object::SameValue(*handler_proto, *target_proto)) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible));
}
return handler_proto;
}
MaybeHandle<JSAny> Object::GetPropertyWithAccessor(LookupIterator* it) {
Isolate* isolate = it->isolate();
DirectHandle<Object> structure = it->GetAccessors();
DirectHandle<JSAny> receiver = it->GetReceiver();
if (IsJSGlobalObject(*receiver)) {
receiver =
direct_handle(Cast<JSGlobalObject>(*receiver)->global_proxy(), isolate);
}
DCHECK(!IsForeign(*structure));
if (IsAccessorInfo(*structure)) {
DirectHandle<Name> name = it->GetName();
auto info = Cast<AccessorInfo>(structure);
if (!info->has_getter(isolate)) {
return isolate->factory()->undefined_value();
}
if (info->is_sloppy() && !IsJSReceiver(*receiver)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
Object::ConvertReceiver(isolate, receiver));
}
PropertyCallbackArguments args(isolate, *receiver, it->GetHolderForApi(),
Just(kDontThrow));
DirectHandle<JSAny> result = args.CallAccessorGetter(info, name);
RETURN_EXCEPTION_IF_EXCEPTION(isolate);
Handle<JSAny> reboxed_result(*result, isolate);
if (info->replace_on_access() && IsJSReceiver(*receiver)) {
RETURN_ON_EXCEPTION(isolate, Accessors::ReplaceAccessorWithDataProperty(
isolate, args.holder(), name, result));
}
return reboxed_result;
}
auto accessor_pair = Cast<AccessorPair>(structure);
if (it->TryLookupCachedProperty(accessor_pair)) {
return Cast<JSAny>(Object::GetProperty(it));
}
DirectHandle<Object> getter(accessor_pair->getter(), isolate);
if (IsFunctionTemplateInfo(*getter)) {
DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
SaveAndSwitchContext save(isolate, holder->GetCreationContext().value());
return Cast<JSAny>(Builtins::InvokeApiFunction(
isolate, false, Cast<FunctionTemplateInfo>(getter), receiver, {},
isolate->factory()->undefined_value()));
} else if (IsCallable(*getter)) {
return Object::GetPropertyWithDefinedGetter(receiver,
Cast<JSReceiver>(getter));
}
return isolate->factory()->undefined_value();
}
Maybe<bool> Object::SetPropertyWithAccessor(LookupIterator* it,
DirectHandle<Object> value,
Maybe<ShouldThrow> should_throw) {
Isolate* isolate = it->isolate();
DirectHandle<Object> structure = it->GetAccessors();
DirectHandle<JSAny> receiver = it->GetReceiver();
if (IsJSGlobalObject(*receiver)) {
receiver =
direct_handle(Cast<JSGlobalObject>(*receiver)->global_proxy(), isolate);
}
DCHECK(!IsForeign(*structure));
if (IsAccessorInfo(*structure)) {
DirectHandle<Name> name = it->GetName();
auto info = Cast<AccessorInfo>(structure);
if (!info->has_setter(isolate)) {
return Just(true);
}
if (info->is_sloppy() && !IsJSReceiver(*receiver)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
Object::ConvertReceiver(isolate, receiver));
}
PropertyCallbackArguments args(isolate, *receiver, it->GetHolderForApi(),
should_throw);
bool result = args.CallAccessorSetter(info, name, value);
RETURN_VALUE_IF_EXCEPTION(isolate, Nothing<bool>());
if (!result) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kStrictCannotSetProperty,
it->GetName(), receiver));
}
return Just(result);
}
DirectHandle<Object> setter(Cast<AccessorPair>(*structure)->setter(),
isolate);
if (IsFunctionTemplateInfo(*setter)) {
DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
SaveAndSwitchContext save(isolate, holder->GetCreationContext().value());
DirectHandle<Object> args[] = {value};
RETURN_ON_EXCEPTION_VALUE(
isolate,
Builtins::InvokeApiFunction(
isolate, false, Cast<FunctionTemplateInfo>(setter), receiver,
base::VectorOf(args), isolate->factory()->undefined_value()),
Nothing<bool>());
return Just(true);
} else if (IsCallable(*setter)) {
return SetPropertyWithDefinedSetter(receiver, Cast<JSReceiver>(setter),
value, should_throw);
}
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kNoSetterInCallback,
it->GetName(), it->GetHolder<JSObject>()));
}
MaybeHandle<JSAny> Object::GetPropertyWithDefinedGetter(
DirectHandle<JSAny> receiver, DirectHandle<JSReceiver> getter) {
Isolate* isolate = Isolate::Current();
StackLimitCheck check(isolate);
if (check.JsHasOverflowed()) {
isolate->StackOverflow();
return kNullMaybeHandle;
}
return Cast<JSAny>(Execution::Call(isolate, getter, receiver, {}));
}
Maybe<bool> Object::SetPropertyWithDefinedSetter(
DirectHandle<JSAny> receiver, DirectHandle<JSReceiver> setter,
DirectHandle<Object> value, Maybe<ShouldThrow> should_throw) {
Isolate* isolate = Isolate::Current();
DirectHandle<Object> args[] = {value};
RETURN_ON_EXCEPTION_VALUE(
isolate, Execution::Call(isolate, setter, receiver, base::VectorOf(args)),
Nothing<bool>());
return Just(true);
}
Tagged<Map> Object::GetPrototypeChainRootMap(Tagged<Object> obj,
Isolate* isolate) {
DisallowGarbageCollection no_alloc;
if (IsSmi(obj)) {
Tagged<Context> native_context = isolate->context()->native_context();
return native_context->number_function()->initial_map();
}
const Tagged<HeapObject> heap_object = Cast<HeapObject>(obj);
return heap_object->map()->GetPrototypeChainRootMap(isolate);
}
Tagged<Smi> Object::GetOrCreateHash(Tagged<Object> obj, Isolate* isolate) {
DisallowGarbageCollection no_gc;
Tagged<Object> hash = Object::GetSimpleHash(obj);
if (IsSmi(hash)) return Cast<Smi>(hash);
DCHECK(IsJSReceiver(obj));
return Cast<JSReceiver>(obj)->GetOrCreateIdentityHash(isolate);
}
bool Object::SameValue(Tagged<Object> obj, Tagged<Object> other) {
if (other == obj) return true;
if (IsNumber(obj) && IsNumber(other)) {
return SameNumberValue(Object::NumberValue(Cast<Number>(obj)),
Object::NumberValue(Cast<Number>(other)));
}
if (IsString(obj) && IsString(other)) {
return Cast<String>(obj)->Equals(Cast<String>(other));
}
if (IsBigInt(obj) && IsBigInt(other)) {
return BigInt::EqualToBigInt(Cast<BigInt>(obj), Cast<BigInt>(other));
}
return false;
}
bool Object::SameValueZero(Tagged<Object> obj, Tagged<Object> other) {
if (other == obj) return true;
if (IsNumber(obj) && IsNumber(other)) {
double this_value = Object::NumberValue(Cast<Number>(obj));
double other_value = Object::NumberValue(Cast<Number>(other));
return this_value == other_value ||
(std::isnan(this_value) && std::isnan(other_value));
}
if (IsString(obj) && IsString(other)) {
return Cast<String>(obj)->Equals(Cast<String>(other));
}
if (IsBigInt(obj) && IsBigInt(other)) {
return BigInt::EqualToBigInt(Cast<BigInt>(obj), Cast<BigInt>(other));
}
return false;
}
MaybeDirectHandle<Object> Object::ArraySpeciesConstructor(
Isolate* isolate, DirectHandle<JSAny> original_array) {
DirectHandle<Object> default_species = isolate->array_function();
if (!v8_flags.builtin_subclassing) return default_species;
if (IsJSArray(*original_array) &&
Cast<JSArray>(original_array)->HasArrayPrototype(isolate) &&
Protectors::IsArraySpeciesLookupChainIntact(isolate)) {
return default_species;
}
DirectHandle<Object> constructor = isolate->factory()->undefined_value();
Maybe<bool> is_array = IsArray(original_array);
MAYBE_RETURN_NULL(is_array);
if (is_array.FromJust()) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, constructor,
Object::GetProperty(isolate, original_array,
isolate->factory()->constructor_string()));
if (IsConstructor(*constructor)) {
DirectHandle<NativeContext> constructor_context;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, constructor_context,
JSReceiver::GetFunctionRealm(Cast<JSReceiver>(constructor)));
if (*constructor_context != *isolate->native_context() &&
*constructor == constructor_context->array_function()) {
constructor = isolate->factory()->undefined_value();
}
}
if (IsJSReceiver(*constructor)) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, constructor,
JSReceiver::GetProperty(isolate, Cast<JSReceiver>(constructor),
isolate->factory()->species_symbol()));
if (IsNull(*constructor, isolate)) {
constructor = isolate->factory()->undefined_value();
}
}
}
if (IsUndefined(*constructor, isolate)) {
return default_species;
} else {
if (!IsConstructor(*constructor)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kSpeciesNotConstructor));
}
return constructor;
}
}
V8_WARN_UNUSED_RESULT MaybeDirectHandle<Object> Object::SpeciesConstructor(
Isolate* isolate, DirectHandle<JSReceiver> recv,
DirectHandle<JSFunction> default_ctor) {
DirectHandle<Object> ctor_obj;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, ctor_obj,
JSObject::GetProperty(isolate, recv,
isolate->factory()->constructor_string()));
if (IsUndefined(*ctor_obj, isolate)) return default_ctor;
if (!IsJSReceiver(*ctor_obj)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kConstructorNotReceiver));
}
DirectHandle<JSReceiver> ctor = Cast<JSReceiver>(ctor_obj);
DirectHandle<Object> species;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, species,
JSObject::GetProperty(isolate, ctor,
isolate->factory()->species_symbol()));
if (IsNullOrUndefined(*species, isolate)) {
return default_ctor;
}
if (IsConstructor(*species)) return species;
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kSpeciesNotConstructor));
}
bool Object::IterationHasObservableEffects(Tagged<Object> obj) {
DisallowGarbageCollection no_gc;
if (!IsJSArray(obj)) return true;
Tagged<JSArray> array = Cast<JSArray>(obj);
Tagged<Object> array_proto = array->map()->prototype();
if (!IsJSObject(array_proto)) return true;
Tagged<NativeContext> native_context = array->GetCreationContext().value();
auto initial_array_prototype = native_context->initial_array_prototype();
if (initial_array_prototype != array_proto) return true;
Isolate* isolate = Isolate::Current();
if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return true;
ElementsKind array_kind = array->GetElementsKind();
if (IsFastPackedElementsKind(array_kind)) return false;
if (IsHoleyElementsKind(array_kind) &&
Protectors::IsNoElementsIntact(isolate)) {
return false;
}
return true;
}
bool Object::IsCodeLike(Tagged<Object> obj, Isolate* isolate) {
DisallowGarbageCollection no_gc;
return IsJSReceiver(obj) && Cast<JSReceiver>(obj)->IsCodeLike(isolate);
}
void ShortPrint(Tagged<Object> obj, FILE* out) {
OFStream os(out);
os << Brief(obj);
}
void ShortPrint(Tagged<Object> obj, StringStream* accumulator) {
std::ostringstream os;
os << Brief(obj);
accumulator->Add(os.str().c_str());
}
void ShortPrint(Tagged<Object> obj, std::ostream& os) { os << Brief(obj); }
std::ostream& operator<<(std::ostream& os, Tagged<Object> obj) {
ShortPrint(obj, os);
return os;
}
std::ostream& operator<<(std::ostream& os, Object::Conversion kind) {
switch (kind) {
case Object::Conversion::kToNumber:
return os << "ToNumber";
case Object::Conversion::kToNumeric:
return os << "ToNumeric";
}
UNREACHABLE();
}
std::ostream& operator<<(std::ostream& os, const Brief& v) {
Tagged<MaybeObject> maybe_object(v.value);
Tagged<Smi> smi;
Tagged<HeapObject> heap_object;
if (maybe_object.ToSmi(&smi)) {
Smi::SmiPrint(smi, os);
} else if (maybe_object.IsCleared()) {
os << "[cleared]";
} else if (maybe_object.GetHeapObjectIfWeak(&heap_object)) {
os << "[weak] ";
heap_object->HeapObjectShortPrint(os);
} else if (maybe_object.GetHeapObjectIfStrong(&heap_object)) {
heap_object->HeapObjectShortPrint(os);
} else {
UNREACHABLE();
}
return os;
}
void Smi::SmiPrint(Tagged<Smi> smi, std::ostream& os) { os << smi.value(); }
void Struct::BriefPrintDetails(std::ostream& os) {}
void StructLayout::BriefPrintDetails(std::ostream& os) {}
void Tuple2::BriefPrintDetails(std::ostream& os) {
os << " " << Brief(value1()) << ", " << Brief(value2());
}
void MegaDomHandler::BriefPrintDetails(std::ostream& os) {
os << " " << Brief(accessor(kAcquireLoad)) << ", " << Brief(context());
}
void ClassPositions::BriefPrintDetails(std::ostream& os) {
os << " " << start() << ", " << end();
}
void CallableTask::BriefPrintDetails(std::ostream& os) {
os << " callable=" << Brief(callable());
}
int HeapObjectLayout::SizeFromMap(Tagged<Map> map) const {
return Tagged<HeapObject>(this)->SizeFromMap(map);
}
SafeHeapObjectSize HeapObjectLayout::SafeSizeFromMap(Tagged<Map> map) const {
return Tagged<HeapObject>(this)->SafeSizeFromMap(map);
}
int HeapObject::SizeFromMap(Tagged<Map> map) const {
int instance_size = map->instance_size();
if (instance_size != kVariableSizeSentinel) return instance_size;
InstanceType instance_type = map->instance_type();
if (base::IsInRange(instance_type, FIRST_FIXED_ARRAY_TYPE,
LAST_FIXED_ARRAY_TYPE)) {
return UncheckedCast<FixedArray>(*this)->AllocatedSize();
}
#define CASE(TypeCamelCase, TYPE_UPPER_CASE) \
if (instance_type == TYPE_UPPER_CASE##_TYPE) { \
return UncheckedCast<TypeCamelCase>(*this)->AllocatedSize(); \
}
SIMPLE_HEAP_OBJECT_LIST2(CASE)
#undef CASE
if (instance_type == SLOPPY_ARGUMENTS_ELEMENTS_TYPE) {
return UncheckedCast<SloppyArgumentsElements>(*this)->AllocatedSize();
}
if (base::IsInRange(instance_type, FIRST_CONTEXT_TYPE, LAST_CONTEXT_TYPE)) {
if (instance_type == NATIVE_CONTEXT_TYPE) return NativeContext::kSize;
return Context::SizeFor(UncheckedCast<Context>(*this)->length());
}
if (instance_type == SEQ_ONE_BYTE_STRING_TYPE ||
instance_type == INTERNALIZED_ONE_BYTE_STRING_TYPE ||
instance_type == SHARED_SEQ_ONE_BYTE_STRING_TYPE) {
return SeqOneByteString::SizeFor(
UncheckedCast<SeqOneByteString>(*this)->length(kAcquireLoad));
}
if (instance_type == BYTECODE_ARRAY_TYPE) {
return BytecodeArray::SizeFor(
UncheckedCast<BytecodeArray>(*this)->length(kAcquireLoad));
}
if (instance_type == FREE_SPACE_TYPE) {
return UncheckedCast<FreeSpace>(*this)->size(kRelaxedLoad);
}
if (instance_type == SEQ_TWO_BYTE_STRING_TYPE ||
instance_type == INTERNALIZED_TWO_BYTE_STRING_TYPE ||
instance_type == SHARED_SEQ_TWO_BYTE_STRING_TYPE) {
return SeqTwoByteString::SizeFor(
UncheckedCast<SeqTwoByteString>(*this)->length(kAcquireLoad));
}
if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) {
return UncheckedCast<FixedDoubleArray>(*this)->AllocatedSize();
}
if (instance_type == TRUSTED_FIXED_ARRAY_TYPE) {
return UncheckedCast<TrustedFixedArray>(*this)->AllocatedSize();
}
if (instance_type == PROTECTED_FIXED_ARRAY_TYPE) {
return UncheckedCast<ProtectedFixedArray>(*this)->AllocatedSize();
}
if (instance_type == PROTECTED_WEAK_FIXED_ARRAY_TYPE) {
return UncheckedCast<ProtectedWeakFixedArray>(*this)->AllocatedSize();
}
if (instance_type == TRUSTED_WEAK_FIXED_ARRAY_TYPE) {
return UncheckedCast<TrustedWeakFixedArray>(*this)->AllocatedSize();
}
if (instance_type == TRUSTED_BYTE_ARRAY_TYPE) {
return UncheckedCast<TrustedByteArray>(*this)->AllocatedSize();
}
if (instance_type == FEEDBACK_METADATA_TYPE) {
return UncheckedCast<FeedbackMetadata>(*this)->AllocatedSize();
}
if (base::IsInRange(instance_type, FIRST_DESCRIPTOR_ARRAY_TYPE,
LAST_DESCRIPTOR_ARRAY_TYPE)) {
return DescriptorArray::SizeFor(
UncheckedCast<DescriptorArray>(*this)->number_of_all_descriptors());
}
if (base::IsInRange(instance_type, FIRST_WEAK_FIXED_ARRAY_TYPE,
LAST_WEAK_FIXED_ARRAY_TYPE)) {
return UncheckedCast<WeakFixedArray>(*this)->AllocatedSize();
}
if (instance_type == WEAK_ARRAY_LIST_TYPE) {
return UncheckedCast<WeakArrayList>(*this)->AllocatedSize();
}
if (instance_type == SMALL_ORDERED_HASH_SET_TYPE) {
return SmallOrderedHashSet::SizeFor(
UncheckedCast<SmallOrderedHashSet>(*this)->Capacity());
}
if (instance_type == SMALL_ORDERED_HASH_MAP_TYPE) {
return SmallOrderedHashMap::SizeFor(
UncheckedCast<SmallOrderedHashMap>(*this)->Capacity());
}
if (instance_type == SMALL_ORDERED_NAME_DICTIONARY_TYPE) {
return SmallOrderedNameDictionary::SizeFor(
UncheckedCast<SmallOrderedNameDictionary>(*this)->Capacity());
}
if (instance_type == SWISS_NAME_DICTIONARY_TYPE) {
return SwissNameDictionary::SizeFor(
UncheckedCast<SwissNameDictionary>(*this)->Capacity());
}
if (instance_type == PROPERTY_ARRAY_TYPE) {
return PropertyArray::SizeFor(
UncheckedCast<PropertyArray>(*this)->length(kAcquireLoad));
}
if (instance_type == FEEDBACK_VECTOR_TYPE) {
return FeedbackVector::SizeFor(
UncheckedCast<FeedbackVector>(*this)->length());
}
if (instance_type == BIGINT_TYPE) {
return BigInt::SizeFor(UncheckedCast<BigInt>(*this)->length());
}
if (instance_type == PREPARSE_DATA_TYPE) {
Tagged<PreparseData> data = UncheckedCast<PreparseData>(*this);
return PreparseData::SizeFor(data->data_length(), data->children_length());
}
#define MAKE_TORQUE_SIZE_FOR(TYPE, TypeName) \
if (instance_type == TYPE) { \
return UncheckedCast<TypeName>(*this)->AllocatedSize(); \
}
TORQUE_INSTANCE_TYPE_TO_BODY_DESCRIPTOR_LIST(MAKE_TORQUE_SIZE_FOR)
#undef MAKE_TORQUE_SIZE_FOR
if (instance_type == INSTRUCTION_STREAM_TYPE) {
return UncheckedCast<InstructionStream>(*this)->Size();
}
if (instance_type == COVERAGE_INFO_TYPE) {
return CoverageInfo::SizeFor(
UncheckedCast<CoverageInfo>(*this)->slot_count());
}
#if V8_ENABLE_WEBASSEMBLY
if (instance_type == WASM_TYPE_INFO_TYPE) {
return WasmTypeInfo::SizeFor(
UncheckedCast<WasmTypeInfo>(*this)->supertypes_length());
}
if (instance_type == WASM_STRUCT_TYPE) {
return WasmStruct::GcSafeSize(map);
}
if (instance_type == WASM_ARRAY_TYPE) {
return WasmArray::SizeFor(map, UncheckedCast<WasmArray>(*this)->length());
}
if (instance_type == WASM_NULL_TYPE) {
return WasmNull::kSize;
}
if (instance_type == WASM_DISPATCH_TABLE_TYPE) {
return WasmDispatchTable::SizeFor(
UncheckedCast<WasmDispatchTable>(*this)->capacity());
}
if (instance_type == WASM_DISPATCH_TABLE_FOR_IMPORTS_TYPE) {
return WasmDispatchTableForImports::SizeFor(
UncheckedCast<WasmDispatchTableForImports>(*this)->length());
}
#endif
if (instance_type == DOUBLE_STRING_CACHE_TYPE) {
return DoubleStringCache::SizeFor(
UncheckedCast<DoubleStringCache>(*this)->capacity());
}
if (instance_type == EMBEDDER_DATA_ARRAY_TYPE) {
return EmbedderDataArray::SizeFor(
UncheckedCast<EmbedderDataArray>(*this)->length());
}
if (instance_type == HOLE_TYPE) {
return sizeof(Hole);
}
UNREACHABLE();
}
SafeHeapObjectSize HeapObject::SafeSizeFromMap(Tagged<Map> map) const {
const int unsafe_size = SizeFromMap(map);
return SafeHeapObjectSize(static_cast<uint32_t>(unsafe_size));
}
bool HeapObject::NeedsRehashing(PtrComprCageBase cage_base) const {
return NeedsRehashing(map(cage_base)->instance_type());
}
bool HeapObject::NeedsRehashing(InstanceType instance_type) const {
if (V8_EXTERNAL_CODE_SPACE_BOOL) {
DCHECK_IMPLIES(instance_type != INSTRUCTION_STREAM_TYPE,
instance_type == map()->instance_type());
} else {
DCHECK_EQ(instance_type, map()->instance_type());
}
switch (instance_type) {
case DESCRIPTOR_ARRAY_TYPE:
case STRONG_DESCRIPTOR_ARRAY_TYPE:
return Cast<DescriptorArray>(*this)->number_of_descriptors() > 1;
case TRANSITION_ARRAY_TYPE:
return Cast<TransitionArray>(*this)->number_of_transitions() > 1;
case ORDERED_HASH_MAP_TYPE:
case ORDERED_HASH_SET_TYPE:
return false;
case NAME_DICTIONARY_TYPE:
case NAME_TO_INDEX_HASH_TABLE_TYPE:
case REGISTERED_SYMBOL_TABLE_TYPE:
case GLOBAL_DICTIONARY_TYPE:
case NUMBER_DICTIONARY_TYPE:
case SIMPLE_NUMBER_DICTIONARY_TYPE:
case HASH_TABLE_TYPE:
case SMALL_ORDERED_HASH_MAP_TYPE:
case SMALL_ORDERED_HASH_SET_TYPE:
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
case SWISS_NAME_DICTIONARY_TYPE:
case JS_MAP_TYPE:
case JS_SET_TYPE:
return true;
default:
return false;
}
UNREACHABLE();
}
bool HeapObject::CanBeRehashed(PtrComprCageBase cage_base) const {
DCHECK(NeedsRehashing(cage_base));
switch (map(cage_base)->instance_type()) {
case JS_MAP_TYPE:
case JS_SET_TYPE:
return true;
case ORDERED_HASH_MAP_TYPE:
case ORDERED_HASH_SET_TYPE:
UNREACHABLE();
case ORDERED_NAME_DICTIONARY_TYPE:
return false;
case NAME_DICTIONARY_TYPE:
case NAME_TO_INDEX_HASH_TABLE_TYPE:
case REGISTERED_SYMBOL_TABLE_TYPE:
case GLOBAL_DICTIONARY_TYPE:
case NUMBER_DICTIONARY_TYPE:
case SIMPLE_NUMBER_DICTIONARY_TYPE:
case SWISS_NAME_DICTIONARY_TYPE:
return true;
case DESCRIPTOR_ARRAY_TYPE:
case STRONG_DESCRIPTOR_ARRAY_TYPE:
return true;
case TRANSITION_ARRAY_TYPE:
return true;
case SMALL_ORDERED_HASH_MAP_TYPE:
return Cast<SmallOrderedHashMap>(*this)->NumberOfElements() == 0;
case SMALL_ORDERED_HASH_SET_TYPE:
return Cast<SmallOrderedHashMap>(*this)->NumberOfElements() == 0;
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
return Cast<SmallOrderedNameDictionary>(*this)->NumberOfElements() == 0;
default:
return false;
}
UNREACHABLE();
}
template <typename IsolateT>
void HeapObject::RehashBasedOnMap(IsolateT* isolate) {
switch (map(isolate)->instance_type()) {
case HASH_TABLE_TYPE:
Cast<ObjectHashTable>(*this)->Rehash(isolate);
break;
case NAME_DICTIONARY_TYPE:
Cast<NameDictionary>(*this)->Rehash(isolate);
break;
case NAME_TO_INDEX_HASH_TABLE_TYPE:
Cast<NameToIndexHashTable>(*this)->Rehash(isolate);
break;
case REGISTERED_SYMBOL_TABLE_TYPE:
Cast<RegisteredSymbolTable>(*this)->Rehash(isolate);
break;
case SWISS_NAME_DICTIONARY_TYPE:
Cast<SwissNameDictionary>(*this)->Rehash(isolate);
break;
case GLOBAL_DICTIONARY_TYPE:
Cast<GlobalDictionary>(*this)->Rehash(isolate);
break;
case NUMBER_DICTIONARY_TYPE:
Cast<NumberDictionary>(*this)->Rehash(isolate);
break;
case SIMPLE_NAME_DICTIONARY_TYPE:
Cast<SimpleNameDictionary>(*this)->Rehash(isolate);
break;
case SIMPLE_NUMBER_DICTIONARY_TYPE:
Cast<SimpleNumberDictionary>(*this)->Rehash(isolate);
break;
case DESCRIPTOR_ARRAY_TYPE:
case STRONG_DESCRIPTOR_ARRAY_TYPE:
DCHECK_LE(1, Cast<DescriptorArray>(*this)->number_of_descriptors());
Cast<DescriptorArray>(*this)->Sort();
break;
case TRANSITION_ARRAY_TYPE:
Cast<TransitionArray>(*this)->Sort();
break;
case SMALL_ORDERED_HASH_MAP_TYPE:
DCHECK_EQ(0, Cast<SmallOrderedHashMap>(*this)->NumberOfElements());
break;
case SMALL_ORDERED_HASH_SET_TYPE:
DCHECK_EQ(0, Cast<SmallOrderedHashSet>(*this)->NumberOfElements());
break;
case ORDERED_HASH_MAP_TYPE:
case ORDERED_HASH_SET_TYPE:
UNREACHABLE();
case JS_MAP_TYPE: {
Cast<JSMap>(*this)->Rehash(isolate->AsIsolate());
break;
}
case JS_SET_TYPE: {
Cast<JSSet>(*this)->Rehash(isolate->AsIsolate());
break;
}
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
DCHECK_EQ(0, Cast<SmallOrderedNameDictionary>(*this)->NumberOfElements());
break;
case INTERNALIZED_ONE_BYTE_STRING_TYPE:
case INTERNALIZED_TWO_BYTE_STRING_TYPE:
DCHECK(ReadOnlyHeap::Contains(*this));
Cast<String>(*this)->EnsureHash();
break;
default:
isolate->AsIsolate()->PushParamsAndDie(
reinterpret_cast<void*>(ptr()), reinterpret_cast<void*>(map().ptr()),
reinterpret_cast<void*>(
static_cast<uintptr_t>(map()->instance_type())));
UNREACHABLE();
}
}
template void HeapObject::RehashBasedOnMap(Isolate* isolate);
template void HeapObject::RehashBasedOnMap(LocalIsolate* isolate);
void DescriptorArray::GeneralizeAllFields(bool clear_constness) {
int length = number_of_descriptors();
for (InternalIndex i : InternalIndex::Range(length)) {
PropertyDetails details = GetDetails(i);
details = details.CopyWithRepresentation(Representation::Tagged());
if (details.location() == PropertyLocation::kField) {
if (clear_constness) {
details = details.CopyWithConstness(PropertyConstness::kMutable);
}
DCHECK_EQ(PropertyKind::kData, details.kind());
SetValue(i, FieldType::Any());
}
SetDetails(i, details);
}
}
MaybeHandle<Object> Object::SetProperty(Isolate* isolate,
DirectHandle<JSAny> object,
DirectHandle<Name> name,
DirectHandle<Object> value,
StoreOrigin store_origin,
Maybe<ShouldThrow> should_throw) {
LookupIterator it(isolate, object, name);
MAYBE_RETURN_NULL(SetProperty(&it, value, store_origin, should_throw));
return indirect_handle(value, isolate);
}
Maybe<bool> Object::SetPropertyInternal(LookupIterator* it,
DirectHandle<Object> value,
Maybe<ShouldThrow> should_throw,
StoreOrigin store_origin, bool* found) {
it->UpdateProtector();
DCHECK(it->IsFound());
AssertNoContextChange ncc(it->isolate());
for (;; it->Next()) {
switch (it->state()) {
case LookupIterator::ACCESS_CHECK:
if (it->HasAccess()) continue;
return JSObject::SetPropertyWithFailedAccessCheck(it, value,
should_throw);
case LookupIterator::JSPROXY: {
DirectHandle<JSAny> receiver = it->GetReceiver();
if (IsJSGlobalObject(*receiver)) {
receiver = direct_handle(
Cast<JSGlobalObject>(*receiver)->global_proxy(), it->isolate());
}
return JSProxy::SetProperty(it->GetHolder<JSProxy>(), it->GetName(),
value, receiver, should_throw);
}
case LookupIterator::WASM_OBJECT:
continue;
case LookupIterator::INTERCEPTOR: {
if (it->HolderIsReceiverOrHiddenPrototype()) {
InterceptorResult result;
if (!JSObject::SetPropertyWithInterceptor(it, should_throw, value)
.To(&result)) {
return Nothing<bool>();
}
switch (result) {
case InterceptorResult::kFalse: {
Isolate* isolate = it->isolate();
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kStrictCannotSetProperty,
it->GetName(), it->GetReceiver()));
}
case InterceptorResult::kTrue:
return Just(true);
case InterceptorResult::kNotIntercepted:
break;
}
it->Next();
} else {
Maybe<PropertyAttributes> maybe_attributes =
JSObject::GetPropertyAttributesWithInterceptor(it);
if (maybe_attributes.IsNothing()) return Nothing<bool>();
if ((maybe_attributes.FromJust() & READ_ONLY) != 0) {
return WriteToReadOnlyProperty(it, value, should_throw);
}
if (maybe_attributes.FromJust() == ABSENT) {
it->Next();
} else {
it->NotFound();
}
}
return Object::SetSuperProperty(it, value, store_origin, should_throw);
}
case LookupIterator::ACCESSOR: {
if (it->IsReadOnly()) {
return WriteToReadOnlyProperty(it, value, should_throw);
}
DirectHandle<Object> accessors = it->GetAccessors();
if (IsAccessorInfo(*accessors) &&
!it->HolderIsReceiverOrHiddenPrototype()) {
*found = false;
return Nothing<bool>();
}
return SetPropertyWithAccessor(it, value, should_throw);
}
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND: {
DirectHandle<JSTypedArray> holder = it->GetHolder<JSTypedArray>();
if (it->HolderIsReceiver()) {
DirectHandle<Object> converted_value;
if (holder->type() == kExternalBigInt64Array ||
holder->type() == kExternalBigUint64Array) {
ASSIGN_RETURN_ON_EXCEPTION(
it->isolate(), converted_value,
BigInt::FromObject(it->isolate(), value));
} else {
ASSIGN_RETURN_ON_EXCEPTION(it->isolate(), converted_value,
Object::ToNumber(it->isolate(), value));
}
it->RecheckTypedArrayBounds();
value = converted_value;
}
if (it->state() != LookupIterator::DATA) {
DCHECK_EQ(it->state(), LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND);
return Just(true);
}
[[fallthrough]];
}
case LookupIterator::DATA:
if (it->IsReadOnly()) {
return WriteToReadOnlyProperty(it, value, should_throw);
}
if (it->HolderIsReceiverOrHiddenPrototype()) {
return SetDataProperty(it, value);
}
[[fallthrough]];
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
*found = false;
return Nothing<bool>();
case LookupIterator::STRING_LOOKUP_START_OBJECT:
return WriteToReadOnlyProperty(it, value, should_throw);
}
UNREACHABLE();
}
}
bool Object::CheckContextualStoreToJSGlobalObject(
LookupIterator* it, Maybe<ShouldThrow> should_throw) {
Isolate* isolate = it->isolate();
if (IsJSGlobalObject(*it->GetReceiver(), isolate) &&
(GetShouldThrow(isolate, should_throw) == ShouldThrow::kThrowOnError)) {
if (it->state() == LookupIterator::TRANSITION) {
it->transition_cell()->ClearAndInvalidate(isolate);
}
isolate->Throw(*isolate->factory()->NewReferenceError(
MessageTemplate::kNotDefined, it->GetName()));
return false;
}
return true;
}
Maybe<bool> Object::SetProperty(LookupIterator* it, DirectHandle<Object> value,
StoreOrigin store_origin,
Maybe<ShouldThrow> should_throw) {
if (it->IsFound()) {
bool found = true;
Maybe<bool> result =
SetPropertyInternal(it, value, should_throw, store_origin, &found);
if (found) return result;
}
if (!CheckContextualStoreToJSGlobalObject(it, should_throw)) {
return Nothing<bool>();
}
return AddDataProperty(it, value, NONE, should_throw, store_origin);
}
Maybe<bool> Object::SetSuperProperty(LookupIterator* it,
DirectHandle<Object> value,
StoreOrigin store_origin,
Maybe<ShouldThrow> should_throw) {
Isolate* isolate = it->isolate();
if (it->IsFound()) {
bool found = true;
Maybe<bool> result =
SetPropertyInternal(it, value, should_throw, store_origin, &found);
if (found) return result;
}
it->UpdateProtector();
if (!IsJSReceiver(*it->GetReceiver())) {
return WriteToReadOnlyProperty(it, value, should_throw);
}
DirectHandle<JSReceiver> receiver = Cast<JSReceiver>(it->GetReceiver());
LookupIterator own_lookup(isolate, receiver, it->GetKey(),
LookupIterator::OWN);
for (;; own_lookup.Next()) {
switch (own_lookup.state()) {
case LookupIterator::ACCESS_CHECK:
if (!own_lookup.HasAccess()) {
return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value,
should_throw);
}
continue;
case LookupIterator::ACCESSOR:
if (IsAccessorInfo(*own_lookup.GetAccessors())) {
if (own_lookup.IsReadOnly()) {
return WriteToReadOnlyProperty(&own_lookup, value, should_throw);
}
return Object::SetPropertyWithAccessor(&own_lookup, value,
should_throw);
}
[[fallthrough]];
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
return RedefineIncompatibleProperty(isolate, it->GetName(), value,
should_throw);
case LookupIterator::STRING_LOOKUP_START_OBJECT:
UNREACHABLE();
case LookupIterator::DATA: {
if (own_lookup.IsReadOnly()) {
return WriteToReadOnlyProperty(&own_lookup, value, should_throw);
}
return SetDataProperty(&own_lookup, value);
}
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY: {
PropertyDescriptor desc;
Maybe<bool> owned =
JSReceiver::GetOwnPropertyDescriptor(&own_lookup, &desc);
MAYBE_RETURN(owned, Nothing<bool>());
if (!owned.FromJust()) {
own_lookup.Restart();
if (!CheckContextualStoreToJSGlobalObject(&own_lookup,
should_throw)) {
return Nothing<bool>();
}
return JSReceiver::CreateDataProperty(isolate, receiver, it->GetKey(),
value, should_throw);
}
if (PropertyDescriptor::IsAccessorDescriptor(&desc) ||
!desc.writable()) {
return RedefineIncompatibleProperty(isolate, it->GetName(), value,
should_throw);
}
PropertyDescriptor value_desc;
value_desc.set_value(Cast<JSAny>(value));
return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(),
&value_desc, should_throw);
}
case LookupIterator::NOT_FOUND:
if (!CheckContextualStoreToJSGlobalObject(&own_lookup, should_throw)) {
return Nothing<bool>();
}
return AddDataProperty(&own_lookup, value, NONE, should_throw,
store_origin);
case LookupIterator::WASM_OBJECT:
RETURN_FAILURE(it->isolate(), kThrowOnError,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
case LookupIterator::TRANSITION:
UNREACHABLE();
}
UNREACHABLE();
}
}
Maybe<bool> Object::CannotCreateProperty(Isolate* isolate,
DirectHandle<JSAny> receiver,
DirectHandle<Object> name,
Maybe<ShouldThrow> should_throw) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kStrictCannotCreateProperty, name,
Object::TypeOf(isolate, receiver), receiver));
}
Maybe<bool> Object::WriteToReadOnlyProperty(
LookupIterator* it, DirectHandle<Object> value,
Maybe<ShouldThrow> maybe_should_throw) {
ShouldThrow should_throw = GetShouldThrow(it->isolate(), maybe_should_throw);
if (it->IsFound() &&
it->state() != LookupIterator::STRING_LOOKUP_START_OBJECT &&
!it->HolderIsReceiver()) {
v8::Isolate::UseCounterFeature feature =
should_throw == kThrowOnError
? v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeStrict
: v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeSloppy;
it->isolate()->CountUsage(feature);
}
return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(),
it->GetName(), value, should_throw);
}
Maybe<bool> Object::WriteToReadOnlyProperty(Isolate* isolate,
DirectHandle<JSAny> receiver,
DirectHandle<Object> name,
DirectHandle<Object> value,
ShouldThrow should_throw) {
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kStrictReadOnlyProperty, name,
Object::TypeOf(isolate, receiver), receiver));
}
Maybe<bool> Object::RedefineIncompatibleProperty(
Isolate* isolate, DirectHandle<Object> name, DirectHandle<Object> value,
Maybe<ShouldThrow> should_throw) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed, name));
}
Maybe<bool> Object::SetDataProperty(LookupIterator* it,
DirectHandle<Object> value) {
Isolate* isolate = it->isolate();
DCHECK_IMPLIES(IsJSProxy(*it->GetReceiver(), isolate),
it->GetName()->IsPrivateName());
DCHECK_IMPLIES(!it->IsElement() && it->GetName()->IsPrivateName(),
it->state() == LookupIterator::DATA);
DirectHandle<JSReceiver> receiver = Cast<JSReceiver>(it->GetReceiver());
DCHECK(it->HolderIsReceiverOrHiddenPrototype());
DirectHandle<Object> to_assign = value;
if (it->IsElement() && IsJSObject(*receiver, isolate) &&
Cast<JSObject>(*receiver)->HasTypedArrayOrRabGsabTypedArrayElements(
isolate)) {
auto receiver_ta = Cast<JSTypedArray>(receiver);
ElementsKind elements_kind = Cast<JSObject>(*receiver)->GetElementsKind();
if (IsBigIntTypedArrayElementsKind(elements_kind)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, to_assign,
BigInt::FromObject(isolate, value));
if (V8_UNLIKELY(receiver_ta->IsDetachedOrOutOfBounds() ||
it->index() >= receiver_ta->GetLength())) {
return Just(true);
}
} else if (!IsNumber(*value) && !IsUndefined(*value, isolate)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, to_assign,
Object::ToNumber(isolate, value));
if (V8_UNLIKELY(receiver_ta->IsDetachedOrOutOfBounds() ||
it->index() >= receiver_ta->GetLength())) {
return Just(true);
}
}
}
DCHECK(!IsWasmObject(*receiver, isolate));
if (V8_UNLIKELY(IsJSSharedStruct(*receiver, isolate) ||
IsJSSharedArray(*receiver, isolate))) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, to_assign, Object::Share(isolate, to_assign, kThrowOnError));
it->WriteDataValue(to_assign, false);
} else {
it->PrepareForDataProperty(to_assign);
it->WriteDataValue(to_assign, false);
}
#if VERIFY_HEAP
if (v8_flags.verify_heap) {
receiver->HeapObjectVerify(isolate);
}
#endif
return Just(true);
}
Maybe<bool> Object::AddDataProperty(LookupIterator* it,
DirectHandle<Object> value,
PropertyAttributes attributes,
Maybe<ShouldThrow> should_throw,
StoreOrigin store_origin,
EnforceDefineSemantics semantics) {
if (!IsJSReceiver(*it->GetReceiver())) {
return CannotCreateProperty(it->isolate(), it->GetReceiver(), it->GetName(),
should_throw);
}
if (IsJSProxy(*it->GetReceiver()) && it->GetName()->IsPrivate() &&
!it->GetName()->IsPrivateName()) {
RETURN_FAILURE(it->isolate(), GetShouldThrow(it->isolate(), should_throw),
NewTypeError(MessageTemplate::kProxyPrivate));
}
DCHECK_NE(LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND, it->state());
DirectHandle<JSReceiver> receiver = it->GetStoreTarget<JSReceiver>();
DCHECK_IMPLIES(IsJSProxy(*receiver), it->GetName()->IsPrivateName());
DCHECK_IMPLIES(IsJSProxy(*receiver),
it->state() == LookupIterator::NOT_FOUND);
if (IsJSGlobalProxy(*receiver)) return Just(true);
Isolate* isolate = it->isolate();
if (it->ExtendingNonExtensible(receiver)) {
if (IsWasmObject(*receiver)) {
RETURN_FAILURE(it->isolate(), kThrowOnError,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
}
bool is_shared_object = IsAlwaysSharedSpaceJSObject(*receiver);
RETURN_FAILURE(
isolate, GetShouldThrow(it->isolate(), should_throw),
NewTypeError(
semantics == EnforceDefineSemantics::kDefine
? (is_shared_object
? MessageTemplate::kDefineDisallowedFixedLayout
: MessageTemplate::kDefineDisallowed)
: (is_shared_object ? MessageTemplate::kObjectFixedLayout
: MessageTemplate::kObjectNotExtensible),
it->GetName()));
}
if (it->IsElement(*receiver)) {
if (IsJSArray(*receiver)) {
DirectHandle<JSArray> array = Cast<JSArray>(receiver);
if (JSArray::WouldChangeReadOnlyLength(array, it->array_index())) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kStrictReadOnlyProperty,
isolate->factory()->length_string(),
Object::TypeOf(isolate, array), array));
}
}
DirectHandle<JSObject> receiver_obj = Cast<JSObject>(receiver);
MAYBE_RETURN(JSObject::AddDataElement(isolate, receiver_obj,
it->array_index(), value, attributes),
Nothing<bool>());
JSObject::ValidateElements(isolate, *receiver_obj);
return Just(true);
}
return Object::TransitionAndWriteDataProperty(it, value, attributes,
should_throw, store_origin);
}
Maybe<bool> Object::TransitionAndWriteDataProperty(
LookupIterator* it, DirectHandle<Object> value,
PropertyAttributes attributes, Maybe<ShouldThrow> should_throw,
StoreOrigin store_origin) {
DirectHandle<JSReceiver> receiver = it->GetStoreTarget<JSReceiver>();
it->UpdateProtector();
it->PrepareTransitionToDataProperty(receiver, value, attributes,
store_origin);
DCHECK_EQ(LookupIterator::TRANSITION, it->state());
Maybe<bool> transitioned =
it->ApplyTransitionToDataProperty(receiver, should_throw);
if (!transitioned.FromMaybe(false)) return transitioned;
it->WriteDataValue(value, true);
#if VERIFY_HEAP
if (v8_flags.verify_heap) {
receiver->HeapObjectVerify(it->isolate());
}
#endif
return Just(true);
}
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Object>::MaybeType Object::ShareSlow(
Isolate* isolate, HandleType<HeapObject> value,
ShouldThrow throw_if_cannot_be_shared) {
DCHECK(!IsShared(*value));
SharedObjectSafePublishGuard publish_guard;
if (IsString(*value)) {
return String::Share(isolate, Cast<String>(value));
}
if (IsHeapNumber(*value)) {
uint64_t bits = Cast<HeapNumber>(*value)->value_as_bits();
return isolate->factory()
->NewHeapNumberFromBits<AllocationType::kSharedOld>(bits);
}
if (throw_if_cannot_be_shared == kThrowOnError) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kCannotBeShared, value));
}
return {};
}
template V8_EXPORT_PRIVATE MaybeDirectHandle<Object> Object::ShareSlow(
Isolate* isolate, DirectHandle<HeapObject> value,
ShouldThrow throw_if_cannot_be_shared);
template V8_EXPORT_PRIVATE MaybeIndirectHandle<Object> Object::ShareSlow(
Isolate* isolate, IndirectHandle<HeapObject> value,
ShouldThrow throw_if_cannot_be_shared);
namespace {
template <class T>
int AppendUniqueCallbacks(Isolate* isolate, DirectHandle<ArrayList> callbacks,
DirectHandle<typename T::Array> array,
int valid_descriptors) {
int nof_callbacks = callbacks->length();
for (int i = nof_callbacks - 1; i >= 0; i--) {
DirectHandle<AccessorInfo> entry(Cast<AccessorInfo>(callbacks->get(i)),
isolate);
DirectHandle<Name> key(Cast<Name>(entry->name()), isolate);
DCHECK(IsUniqueName(*key));
if (!T::Contains(key, entry, valid_descriptors, array)) {
T::Insert(key, entry, valid_descriptors, array);
valid_descriptors++;
}
}
return valid_descriptors;
}
struct FixedArrayAppender {
using Array = FixedArray;
static bool Contains(DirectHandle<Name> key, DirectHandle<AccessorInfo> entry,
int valid_descriptors, DirectHandle<FixedArray> array) {
for (int i = 0; i < valid_descriptors; i++) {
if (*key == Cast<AccessorInfo>(array->get(i))->name()) return true;
}
return false;
}
static void Insert(DirectHandle<Name> key, DirectHandle<AccessorInfo> entry,
int valid_descriptors, DirectHandle<FixedArray> array) {
DisallowGarbageCollection no_gc;
array->set(valid_descriptors, *entry);
}
};
}
int AccessorInfo::AppendUnique(Isolate* isolate,
DirectHandle<Object> descriptors,
DirectHandle<FixedArray> array,
int valid_descriptors) {
auto callbacks = Cast<ArrayList>(descriptors);
DCHECK_GE(array->length(), callbacks->length() + valid_descriptors);
return AppendUniqueCallbacks<FixedArrayAppender>(isolate, callbacks, array,
valid_descriptors);
}
void JSProxy::Revoke(DirectHandle<JSProxy> proxy) {
Isolate* isolate = Isolate::Current();
CHECK(JSProxy::IsRevocableBit::decode(proxy->flags()));
if (!proxy->IsRevoked()) {
proxy->set_target(ReadOnlyRoots(isolate).null_value());
proxy->set_handler(ReadOnlyRoots(isolate).null_value());
}
DCHECK(proxy->IsRevoked());
}
Maybe<bool> JSProxy::IsArray(DirectHandle<JSProxy> proxy) {
Isolate* isolate = Isolate::Current();
DirectHandle<JSReceiver> object = Cast<JSReceiver>(proxy);
for (int i = 0; i < JSProxy::kMaxIterationLimit; i++) {
proxy = Cast<JSProxy>(object);
if (proxy->IsRevoked()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyRevoked,
isolate->factory()->NewStringFromAsciiChecked("IsArray")));
return Nothing<bool>();
}
object = direct_handle(Cast<JSReceiver>(proxy->target()), isolate);
if (IsJSArray(*object)) return Just(true);
if (!IsJSProxy(*object)) return Just(false);
}
isolate->StackOverflow();
return Nothing<bool>();
}
Maybe<bool> JSProxy::HasProperty(Isolate* isolate, DirectHandle<JSProxy> proxy,
DirectHandle<Name> name) {
DCHECK(!name->IsPrivate());
STACK_CHECK(isolate, Nothing<bool>());
DirectHandle<Object> handler(proxy->handler(), isolate);
if (proxy->IsRevoked()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyRevoked, isolate->factory()->has_string()));
return Nothing<bool>();
}
DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
DirectHandle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap,
Object::GetMethod(isolate, Cast<JSReceiver>(handler),
isolate->factory()->has_string()));
if (IsUndefined(*trap, isolate)) {
return JSReceiver::HasProperty(isolate, target, name);
}
DirectHandle<Object> trap_result_obj;
DirectHandle<Object> args[] = {target, name};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result_obj,
Execution::Call(isolate, trap, handler, base::VectorOf(args)));
bool boolean_trap_result = Object::BooleanValue(*trap_result_obj, isolate);
if (!boolean_trap_result) {
MAYBE_RETURN(JSProxy::CheckHasTrap(isolate, name, target), Nothing<bool>());
}
return Just(boolean_trap_result);
}
Maybe<bool> JSProxy::CheckHasTrap(Isolate* isolate, DirectHandle<Name> name,
DirectHandle<JSReceiver> target) {
PropertyDescriptor target_desc;
Maybe<bool> target_found =
JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
MAYBE_RETURN(target_found, Nothing<bool>());
if (target_found.FromJust()) {
if (!target_desc.configurable()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyHasNonConfigurable, name));
return Nothing<bool>();
}
Maybe<bool> extensible_target = JSReceiver::IsExtensible(isolate, target);
MAYBE_RETURN(extensible_target, Nothing<bool>());
if (!extensible_target.FromJust()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyHasNonExtensible, name));
return Nothing<bool>();
}
}
return Just(true);
}
Maybe<bool> JSProxy::SetProperty(DirectHandle<JSProxy> proxy,
DirectHandle<Name> name,
DirectHandle<Object> value,
DirectHandle<JSAny> receiver,
Maybe<ShouldThrow> should_throw) {
DCHECK(!name->IsPrivate());
Isolate* isolate = Isolate::Current();
STACK_CHECK(isolate, Nothing<bool>());
Factory* factory = isolate->factory();
DirectHandle<String> trap_name = factory->set_string();
if (proxy->IsRevoked()) {
isolate->Throw(
*factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
return Nothing<bool>();
}
DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
DirectHandle<JSReceiver> handler(Cast<JSReceiver>(proxy->handler()), isolate);
DirectHandle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
Object::GetMethod(isolate, handler, trap_name));
if (IsUndefined(*trap, isolate)) {
PropertyKey key(isolate, name);
LookupIterator it(isolate, receiver, key, target);
return Object::SetSuperProperty(&it, value, StoreOrigin::kMaybeKeyed,
should_throw);
}
DirectHandle<Object> trap_result;
DirectHandle<Object> args[] = {target, name, value, receiver};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result,
Execution::Call(isolate, trap, handler, base::VectorOf(args)));
if (!Object::BooleanValue(*trap_result, isolate)) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor,
trap_name, name));
}
MaybeDirectHandle<Object> result =
JSProxy::CheckGetSetTrapResult(isolate, name, target, value, kSet);
if (result.is_null()) {
return Nothing<bool>();
}
return Just(true);
}
Maybe<bool> JSProxy::DeletePropertyOrElement(DirectHandle<JSProxy> proxy,
DirectHandle<Name> name,
LanguageMode language_mode) {
DCHECK(!name->IsPrivate());
ShouldThrow should_throw =
is_sloppy(language_mode) ? kDontThrow : kThrowOnError;
Isolate* isolate = Isolate::Current();
STACK_CHECK(isolate, Nothing<bool>());
Factory* factory = isolate->factory();
DirectHandle<String> trap_name = factory->deleteProperty_string();
if (proxy->IsRevoked()) {
isolate->Throw(
*factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
return Nothing<bool>();
}
DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
DirectHandle<JSReceiver> handler(Cast<JSReceiver>(proxy->handler()), isolate);
DirectHandle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
Object::GetMethod(isolate, handler, trap_name));
if (IsUndefined(*trap, isolate)) {
return JSReceiver::DeletePropertyOrElement(isolate, target, name,
language_mode);
}
DirectHandle<Object> trap_result;
DirectHandle<Object> args[] = {target, name};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result,
Execution::Call(isolate, trap, handler, base::VectorOf(args)));
if (!Object::BooleanValue(*trap_result, isolate)) {
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor,
trap_name, name));
}
return JSProxy::CheckDeleteTrap(isolate, name, target);
}
Maybe<bool> JSProxy::CheckDeleteTrap(Isolate* isolate, DirectHandle<Name> name,
DirectHandle<JSReceiver> target) {
PropertyDescriptor target_desc;
Maybe<bool> target_found =
JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
MAYBE_RETURN(target_found, Nothing<bool>());
if (target_found.FromJust()) {
if (!target_desc.configurable()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyDeletePropertyNonConfigurable, name));
return Nothing<bool>();
}
Maybe<bool> extensible_target = JSReceiver::IsExtensible(isolate, target);
MAYBE_RETURN(extensible_target, Nothing<bool>());
if (!extensible_target.FromJust()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyDeletePropertyNonExtensible, name));
return Nothing<bool>();
}
}
return Just(true);
}
MaybeDirectHandle<JSProxy> JSProxy::New(Isolate* isolate,
DirectHandle<Object> target,
DirectHandle<Object> handler,
bool revocable) {
if (!IsJSReceiver(*target)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject));
}
if (!IsJSReceiver(*handler)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject));
}
return isolate->factory()->NewJSProxy(Cast<JSReceiver>(target),
Cast<JSReceiver>(handler), revocable);
}
Maybe<PropertyAttributes> JSProxy::GetPropertyAttributes(LookupIterator* it) {
PropertyDescriptor desc;
Maybe<bool> found = JSProxy::GetOwnPropertyDescriptor(
it->isolate(), it->GetHolder<JSProxy>(), it->GetName(), &desc);
MAYBE_RETURN(found, Nothing<PropertyAttributes>());
if (!found.FromJust()) return Just(ABSENT);
return Just(desc.ToAttributes());
}
bool PropertyKeyToArrayLength(DirectHandle<Object> value, uint32_t* length) {
DCHECK(IsNumber(*value) || IsName(*value));
if (Object::ToArrayLength(*value, length)) return true;
if (IsString(*value)) return Cast<String>(*value)->AsArrayIndex(length);
return false;
}
bool PropertyKeyToArrayIndex(DirectHandle<Object> index_obj, uint32_t* output) {
return PropertyKeyToArrayLength(index_obj, output) && *output != kMaxUInt32;
}
Maybe<bool> JSArray::DefineOwnProperty(Isolate* isolate,
DirectHandle<JSArray> o,
DirectHandle<Object> name,
PropertyDescriptor* desc,
Maybe<ShouldThrow> should_throw) {
if (IsName(*name)) {
name = isolate->factory()->InternalizeName(Cast<Name>(name));
}
if (*name == ReadOnlyRoots(isolate).length_string()) {
return ArraySetLength(isolate, o, desc, should_throw);
}
uint32_t index = 0;
if (PropertyKeyToArrayIndex(name, &index)) {
PropertyDescriptor old_len_desc;
Maybe<bool> success = GetOwnPropertyDescriptor(
isolate, o, isolate->factory()->length_string(), &old_len_desc);
DCHECK(success.FromJust());
USE(success);
uint32_t old_len = 0;
CHECK(Object::ToArrayLength(*old_len_desc.value(), &old_len));
if (index >= old_len && old_len_desc.has_writable() &&
!old_len_desc.writable()) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kDefineDisallowed, name));
}
Maybe<bool> succeeded =
OrdinaryDefineOwnProperty(isolate, o, name, desc, should_throw);
if (succeeded.IsNothing() || !succeeded.FromJust()) return succeeded;
if (index >= old_len) {
old_len_desc.set_value(isolate->factory()->NewNumberFromUint(index + 1));
succeeded = OrdinaryDefineOwnProperty(isolate, o,
isolate->factory()->length_string(),
&old_len_desc, should_throw);
DCHECK(succeeded.FromJust());
USE(succeeded);
}
return Just(true);
}
return OrdinaryDefineOwnProperty(isolate, o, name, desc, should_throw);
}
bool JSArray::AnythingToArrayLength(Isolate* isolate,
DirectHandle<Object> length_object,
uint32_t* output) {
if (Object::ToArrayLength(*length_object, output)) return true;
if (IsString(*length_object) &&
Cast<String>(length_object)->AsArrayIndex(output)) {
return true;
}
DirectHandle<Number> uint32_v;
if (!Object::ToUint32(isolate, length_object).ToHandle(&uint32_v)) {
return false;
}
DirectHandle<Number> number_v;
if (!Object::ToNumber(isolate, length_object).ToHandle(&number_v)) {
return false;
}
if (Object::NumberValue(*uint32_v) != Object::NumberValue(*number_v)) {
DirectHandle<Object> exception =
isolate->factory()->NewRangeError(MessageTemplate::kInvalidArrayLength);
isolate->Throw(*exception);
return false;
}
CHECK(Object::ToArrayLength(*uint32_v, output));
return true;
}
Maybe<bool> JSArray::ArraySetLength(Isolate* isolate, DirectHandle<JSArray> a,
PropertyDescriptor* desc,
Maybe<ShouldThrow> should_throw) {
if (!desc->has_value()) {
return OrdinaryDefineOwnProperty(
isolate, a, isolate->factory()->length_string(), desc, should_throw);
}
PropertyDescriptor* new_len_desc = desc;
uint32_t new_len = 0;
if (!AnythingToArrayLength(isolate, desc->value(), &new_len)) {
DCHECK(isolate->has_exception());
return Nothing<bool>();
}
PropertyDescriptor old_len_desc;
Maybe<bool> success = GetOwnPropertyDescriptor(
isolate, a, isolate->factory()->length_string(), &old_len_desc);
DCHECK(success.FromJust());
USE(success);
uint32_t old_len = 0;
CHECK(Object::ToArrayLength(*old_len_desc.value(), &old_len));
if (new_len >= old_len) {
new_len_desc->set_value(isolate->factory()->NewNumberFromUint(new_len));
return OrdinaryDefineOwnProperty(isolate, a,
isolate->factory()->length_string(),
new_len_desc, should_throw);
}
if (!old_len_desc.writable() ||
new_len_desc->configurable() ||
(new_len_desc->has_enumerable() &&
(old_len_desc.enumerable() != new_len_desc->enumerable()))) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed,
isolate->factory()->length_string()));
}
bool new_writable = false;
if (!new_len_desc->has_writable() || new_len_desc->writable()) {
new_writable = true;
} else {
}
MAYBE_RETURN(JSArray::SetLength(isolate, a, new_len), Nothing<bool>());
if (!new_writable) {
PropertyDescriptor readonly;
readonly.set_writable(false);
success = OrdinaryDefineOwnProperty(isolate, a,
isolate->factory()->length_string(),
&readonly, should_throw);
DCHECK(success.FromJust());
USE(success);
}
uint32_t actual_new_len = 0;
CHECK(Object::ToArrayLength(a->length(), &actual_new_len));
bool result = actual_new_len == new_len;
if (!result) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kStrictCannotDeleteProperty,
isolate->factory()->NewNumberFromUint(actual_new_len - 1),
a));
}
return Just(result);
}
Maybe<bool> JSProxy::DefineOwnProperty(Isolate* isolate,
DirectHandle<JSProxy> proxy,
DirectHandle<Object> key,
PropertyDescriptor* desc,
Maybe<ShouldThrow> should_throw) {
STACK_CHECK(isolate, Nothing<bool>());
if (IsSymbol(*key) && Cast<Symbol>(key)->IsPrivate()) {
DCHECK(!Cast<Symbol>(key)->IsPrivateName());
return JSProxy::SetPrivateSymbol(isolate, proxy, Cast<Symbol>(key), desc,
should_throw);
}
DirectHandle<String> trap_name = isolate->factory()->defineProperty_string();
DCHECK(IsName(*key) || IsNumber(*key));
DirectHandle<Object> handler(proxy->handler(), isolate);
if (proxy->IsRevoked()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyRevoked, trap_name));
return Nothing<bool>();
}
DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
DirectHandle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap,
Object::GetMethod(isolate, Cast<JSReceiver>(handler), trap_name));
if (IsUndefined(*trap, isolate)) {
return JSReceiver::DefineOwnProperty(isolate, target, key, desc,
should_throw);
}
DirectHandle<Object> desc_obj = desc->ToObject(isolate);
DirectHandle<Name> property_name =
IsName(*key) ? Cast<Name>(key)
: Cast<Name>(isolate->factory()->NumberToString(key));
DCHECK(!property_name->IsPrivate());
DirectHandle<Object> trap_result_obj;
DirectHandle<Object> args[] = {target, property_name, desc_obj};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result_obj,
Execution::Call(isolate, trap, handler, base::VectorOf(args)));
if (!Object::BooleanValue(*trap_result_obj, isolate)) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor,
trap_name, property_name));
}
PropertyDescriptor target_desc;
Maybe<bool> target_found =
JSReceiver::GetOwnPropertyDescriptor(isolate, target, key, &target_desc);
MAYBE_RETURN(target_found, Nothing<bool>());
Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(isolate, target);
MAYBE_RETURN(maybe_extensible, Nothing<bool>());
bool extensible_target = maybe_extensible.FromJust();
bool setting_config_false = desc->has_configurable() && !desc->configurable();
if (!target_found.FromJust()) {
if (!extensible_target) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyDefinePropertyNonExtensible, property_name));
return Nothing<bool>();
}
if (setting_config_false) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name));
return Nothing<bool>();
}
} else {
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>();
}
if (setting_config_false && target_desc.configurable()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name));
return Nothing<bool>();
}
if (PropertyDescriptor::IsDataDescriptor(&target_desc) &&
!target_desc.configurable() && target_desc.writable()) {
if (desc->has_writable() && !desc->writable()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyDefinePropertyNonConfigurableWritable,
property_name));
return Nothing<bool>();
}
}
}
return Just(true);
}
Maybe<bool> JSProxy::SetPrivateSymbol(Isolate* isolate,
DirectHandle<JSProxy> proxy,
DirectHandle<Symbol> private_name,
PropertyDescriptor* desc,
Maybe<ShouldThrow> should_throw) {
if (!PropertyDescriptor::IsDataDescriptor(desc) ||
desc->ToAttributes() != DONT_ENUM) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kProxyPrivate));
}
DCHECK(proxy->map()->is_dictionary_map());
DirectHandle<Object> value =
desc->has_value() ? desc->value()
: Cast<Object>(isolate->factory()->undefined_value());
LookupIterator it(isolate, proxy, private_name, proxy);
if (it.IsFound()) {
DCHECK_EQ(LookupIterator::DATA, it.state());
DCHECK_EQ(DONT_ENUM, it.property_attributes());
DCHECK_EQ(PropertyConstness::kMutable, it.property_details().constness());
it.WriteDataValue(value, false);
return Just(true);
}
PropertyDetails details(PropertyKind::kData, DONT_ENUM,
PropertyConstness::kMutable);
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
DirectHandle<SwissNameDictionary> dict(proxy->property_dictionary_swiss(),
isolate);
DirectHandle<SwissNameDictionary> result =
SwissNameDictionary::Add(isolate, dict, private_name, value, details);
if (!dict.is_identical_to(result)) proxy->SetProperties(*result);
} else {
DirectHandle<NameDictionary> dict(proxy->property_dictionary(), isolate);
DirectHandle<NameDictionary> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
NameDictionary::Add(isolate, dict, private_name, value, details));
if (!dict.is_identical_to(result)) proxy->SetProperties(*result);
}
return Just(true);
}
Maybe<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate,
DirectHandle<JSProxy> proxy,
DirectHandle<Name> name,
PropertyDescriptor* desc) {
DCHECK(!name->IsPrivate());
STACK_CHECK(isolate, Nothing<bool>());
DirectHandle<String> trap_name =
isolate->factory()->getOwnPropertyDescriptor_string();
DirectHandle<Object> handler(proxy->handler(), isolate);
if (proxy->IsRevoked()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyRevoked, trap_name));
return Nothing<bool>();
}
DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
DirectHandle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap,
Object::GetMethod(isolate, Cast<JSReceiver>(handler), trap_name));
if (IsUndefined(*trap, isolate)) {
return JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, desc);
}
Handle<JSAny> trap_result_obj;
DirectHandle<Object> args[] = {target, name};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result_obj,
Cast<JSAny>(
Execution::Call(isolate, trap, handler, base::VectorOf(args))));
if (!IsJSReceiver(*trap_result_obj) &&
!IsUndefined(*trap_result_obj, isolate)) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyGetOwnPropertyDescriptorInvalid, name));
return Nothing<bool>();
}
PropertyDescriptor target_desc;
Maybe<bool> found =
JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
MAYBE_RETURN(found, Nothing<bool>());
if (IsUndefined(*trap_result_obj, isolate)) {
if (!found.FromJust()) return Just(false);
if (!target_desc.configurable()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyGetOwnPropertyDescriptorUndefined, name));
return Nothing<bool>();
}
Maybe<bool> extensible_target = JSReceiver::IsExtensible(isolate, target);
MAYBE_RETURN(extensible_target, Nothing<bool>());
if (!extensible_target.FromJust()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyGetOwnPropertyDescriptorNonExtensible, name));
return Nothing<bool>();
}
return Just(false);
}
Maybe<bool> extensible_target = JSReceiver::IsExtensible(isolate, target);
MAYBE_RETURN(extensible_target, Nothing<bool>());
if (!PropertyDescriptor::ToPropertyDescriptor(isolate, trap_result_obj,
desc)) {
DCHECK(isolate->has_exception());
return Nothing<bool>();
}
PropertyDescriptor::CompletePropertyDescriptor(isolate, desc);
Maybe<bool> valid = IsCompatiblePropertyDescriptor(
isolate, extensible_target.FromJust(), desc, &target_desc, name,
Just(kDontThrow));
MAYBE_RETURN(valid, Nothing<bool>());
if (!valid.FromJust()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyGetOwnPropertyDescriptorIncompatible, name));
return Nothing<bool>();
}
if (!desc->configurable()) {
if (target_desc.is_empty() || target_desc.configurable()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyGetOwnPropertyDescriptorNonConfigurable,
name));
return Nothing<bool>();
}
if (desc->has_writable() && !desc->writable()) {
if (target_desc.writable()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::
kProxyGetOwnPropertyDescriptorNonConfigurableWritable,
name));
return Nothing<bool>();
}
}
}
return Just(true);
}
Maybe<bool> JSProxy::PreventExtensions(DirectHandle<JSProxy> proxy,
ShouldThrow should_throw) {
Isolate* isolate = Isolate::Current();
STACK_CHECK(isolate, Nothing<bool>());
Factory* factory = isolate->factory();
DirectHandle<String> trap_name = factory->preventExtensions_string();
if (proxy->IsRevoked()) {
isolate->Throw(
*factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
return Nothing<bool>();
}
DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
DirectHandle<JSReceiver> handler(Cast<JSReceiver>(proxy->handler()), isolate);
DirectHandle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
Object::GetMethod(isolate, handler, trap_name));
if (IsUndefined(*trap, isolate)) {
return JSReceiver::PreventExtensions(isolate, target, should_throw);
}
DirectHandle<Object> trap_result;
DirectHandle<Object> args[] = {target};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result,
Execution::Call(isolate, trap, handler, base::VectorOf(args)));
if (!Object::BooleanValue(*trap_result, isolate)) {
RETURN_FAILURE(
isolate, should_throw,
NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name));
}
Maybe<bool> target_result = JSReceiver::IsExtensible(isolate, target);
MAYBE_RETURN(target_result, Nothing<bool>());
if (target_result.FromJust()) {
isolate->Throw(*factory->NewTypeError(
MessageTemplate::kProxyPreventExtensionsExtensible));
return Nothing<bool>();
}
return Just(true);
}
Maybe<bool> JSProxy::IsExtensible(DirectHandle<JSProxy> proxy) {
Isolate* isolate = Isolate::Current();
STACK_CHECK(isolate, Nothing<bool>());
Factory* factory = isolate->factory();
DirectHandle<String> trap_name = factory->isExtensible_string();
if (proxy->IsRevoked()) {
isolate->Throw(
*factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
return Nothing<bool>();
}
DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
DirectHandle<JSReceiver> handler(Cast<JSReceiver>(proxy->handler()), isolate);
DirectHandle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
Object::GetMethod(isolate, handler, trap_name));
if (IsUndefined(*trap, isolate)) {
return JSReceiver::IsExtensible(isolate, target);
}
DirectHandle<Object> trap_result;
DirectHandle<Object> args[] = {target};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result,
Execution::Call(isolate, trap, handler, base::VectorOf(args)));
Maybe<bool> target_result = JSReceiver::IsExtensible(isolate, target);
MAYBE_RETURN(target_result, Nothing<bool>());
if (target_result.FromJust() != Object::BooleanValue(*trap_result, isolate)) {
isolate->Throw(
*factory->NewTypeError(MessageTemplate::kProxyIsExtensibleInconsistent,
factory->ToBoolean(target_result.FromJust())));
return Nothing<bool>();
}
return target_result;
}
DirectHandle<DescriptorArray> DescriptorArray::CopyUpTo(
Isolate* isolate, DirectHandle<DescriptorArray> desc, int enumeration_index,
int slack) {
return DescriptorArray::CopyUpToAddAttributes(isolate, desc,
enumeration_index, NONE, slack);
}
DirectHandle<DescriptorArray> DescriptorArray::CopyUpToAddAttributes(
Isolate* isolate, DirectHandle<DescriptorArray> source_handle,
int enumeration_index, PropertyAttributes attributes, int slack) {
if (enumeration_index + slack == 0) {
return isolate->factory()->empty_descriptor_array();
}
int size = enumeration_index;
DirectHandle<DescriptorArray> copy_handle =
DescriptorArray::Allocate(isolate, size, slack);
DisallowGarbageCollection no_gc;
Tagged<DescriptorArray> source = *source_handle;
Tagged<DescriptorArray> copy = *copy_handle;
if (attributes != NONE) {
for (InternalIndex i : InternalIndex::Range(size)) {
Tagged<MaybeObject> value_or_field_type = source->GetValue(i);
Tagged<Name> key = source->GetKey(i);
PropertyDetails details = source->GetDetails(i);
if (!key->IsPrivate()) {
int mask = DONT_DELETE | DONT_ENUM;
Tagged<HeapObject> heap_object;
if (details.kind() != PropertyKind::kAccessor ||
!(value_or_field_type.GetHeapObjectIfStrong(&heap_object) &&
IsAccessorPair(heap_object))) {
mask |= READ_ONLY;
}
details = details.CopyAddAttributes(
static_cast<PropertyAttributes>(attributes & mask));
}
copy->Set(i, key, value_or_field_type, details);
}
} else {
for (InternalIndex i : InternalIndex::Range(size)) {
copy->CopyFrom(i, source);
}
}
if (source->number_of_descriptors() != enumeration_index) copy->Sort();
return copy_handle;
}
bool DescriptorArray::IsEqualUpTo(Tagged<DescriptorArray> desc,
int nof_descriptors) {
for (InternalIndex i : InternalIndex::Range(nof_descriptors)) {
if (GetKey(i) != desc->GetKey(i) || GetValue(i) != desc->GetValue(i)) {
return false;
}
PropertyDetails details = GetDetails(i);
PropertyDetails other_details = desc->GetDetails(i);
if (details.kind() != other_details.kind() ||
details.location() != other_details.location() ||
!details.representation().Equals(other_details.representation())) {
return false;
}
}
return true;
}
Handle<WeakArrayList> PrototypeUsers::Add(Isolate* isolate,
Handle<WeakArrayList> array,
DirectHandle<Map> value,
int* assigned_index) {
int length = array->length();
if (length == 0) {
array = WeakArrayList::EnsureSpace(isolate, array, kFirstIndex + 1);
set_empty_slot_index(*array, kNoEmptySlotsMarker);
array->Set(kFirstIndex, MakeWeak(*value));
array->set_length(kFirstIndex + 1);
if (assigned_index != nullptr) *assigned_index = kFirstIndex;
return array;
}
if (!array->IsFull()) {
array->Set(length, MakeWeak(*value));
array->set_length(length + 1);
if (assigned_index != nullptr) *assigned_index = length;
return array;
}
int empty_slot = Smi::ToInt(empty_slot_index(*array));
if (empty_slot == kNoEmptySlotsMarker) {
PrototypeUsers::ScanForEmptySlots(*array);
empty_slot = Smi::ToInt(empty_slot_index(*array));
}
if (empty_slot != kNoEmptySlotsMarker) {
DCHECK_GE(empty_slot, kFirstIndex);
CHECK_LT(empty_slot, array->length());
int next_empty_slot = array->Get(empty_slot).ToSmi().value();
array->Set(empty_slot, MakeWeak(*value));
if (assigned_index != nullptr) *assigned_index = empty_slot;
set_empty_slot_index(*array, next_empty_slot);
return array;
} else {
DCHECK_EQ(empty_slot, kNoEmptySlotsMarker);
}
array = WeakArrayList::EnsureSpace(isolate, array, length + 1);
array->Set(length, MakeWeak(*value));
array->set_length(length + 1);
if (assigned_index != nullptr) *assigned_index = length;
return array;
}
void PrototypeUsers::ScanForEmptySlots(Tagged<WeakArrayList> array) {
for (int i = kFirstIndex; i < array->length(); i++) {
if (array->Get(i).IsCleared()) {
PrototypeUsers::MarkSlotEmpty(array, i);
}
}
}
Tagged<WeakArrayList> PrototypeUsers::Compact(DirectHandle<WeakArrayList> array,
Heap* heap,
CompactionCallback callback,
AllocationType allocation) {
if (array->length() == 0) {
return *array;
}
int new_length = kFirstIndex + array->CountLiveWeakReferences();
if (new_length == array->length()) {
return *array;
}
DirectHandle<WeakArrayList> new_array = WeakArrayList::EnsureSpace(
heap->isolate(),
handle(ReadOnlyRoots(heap).empty_weak_array_list(), heap->isolate()),
new_length, allocation);
int copy_to = kFirstIndex;
for (int i = kFirstIndex; i < array->length(); i++) {
Tagged<MaybeObject> element = array->Get(i);
Tagged<HeapObject> value;
if (element.GetHeapObjectIfWeak(&value)) {
callback(value, i, copy_to);
new_array->Set(copy_to++, element);
} else {
DCHECK(element.IsCleared() || element.IsSmi());
}
}
new_array->set_length(copy_to);
set_empty_slot_index(*new_array, kNoEmptySlotsMarker);
return *new_array;
}
template <typename IsolateT>
Handle<DescriptorArray> DescriptorArray::Allocate(IsolateT* isolate,
int nof_descriptors,
int slack,
AllocationType allocation) {
return nof_descriptors + slack == 0
? isolate->factory()->empty_descriptor_array()
: isolate->factory()->NewDescriptorArray(nof_descriptors, slack,
allocation);
}
template Handle<DescriptorArray> DescriptorArray::Allocate(
Isolate* isolate, int nof_descriptors, int slack,
AllocationType allocation);
template Handle<DescriptorArray> DescriptorArray::Allocate(
LocalIsolate* isolate, int nof_descriptors, int slack,
AllocationType allocation);
void DescriptorArray::Initialize(Tagged<EnumCache> empty_enum_cache,
Tagged<HeapObject> undefined_value,
int nof_descriptors, int slack,
uint32_t raw_gc_state) {
DCHECK_GE(nof_descriptors, 0);
DCHECK_GE(slack, 0);
DCHECK_LE(nof_descriptors + slack, kMaxNumberOfDescriptors);
set_number_of_all_descriptors(nof_descriptors + slack);
set_number_of_descriptors(nof_descriptors);
set_raw_gc_state(raw_gc_state, kRelaxedStore);
set_enum_cache(empty_enum_cache, SKIP_WRITE_BARRIER);
set_flags(FastIterableBits::encode(FastIterableState::kUnknown),
kRelaxedStore);
MemsetTagged(GetDescriptorSlot(0), undefined_value,
number_of_all_descriptors() * kEntrySize);
}
void DescriptorArray::ClearEnumCache() {
set_enum_cache(GetReadOnlyRoots().empty_enum_cache(), SKIP_WRITE_BARRIER);
}
void DescriptorArray::Replace(InternalIndex index, Descriptor* descriptor) {
descriptor->SetSortedKeyIndex(GetSortedKeyIndex(index.as_int()));
Set(index, descriptor);
DCHECK_NE(fast_iterable(), FastIterableState::kJsonFast);
}
void DescriptorArray::InitializeOrChangeEnumCache(
DirectHandle<DescriptorArray> descriptors, Isolate* isolate,
DirectHandle<FixedArray> keys, DirectHandle<FixedArray> indices,
AllocationType allocation_if_initialize) {
Tagged<EnumCache> enum_cache = descriptors->enum_cache();
if (enum_cache == ReadOnlyRoots(isolate).empty_enum_cache()) {
enum_cache = *isolate->factory()->NewEnumCache(keys, indices,
allocation_if_initialize);
descriptors->set_enum_cache(enum_cache);
} else {
enum_cache->set_keys(*keys);
enum_cache->set_indices(*indices);
}
}
void DescriptorArray::CopyFrom(InternalIndex index,
Tagged<DescriptorArray> src) {
PropertyDetails details = src->GetDetails(index);
Set(index, src->GetKey(index), src->GetValue(index), details);
}
void DescriptorArray::SortImpl(const int len) {
DCHECK_EQ(len, number_of_descriptors());
for (int i = 0; i < len; ++i) SetSortedKey(i, i);
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);
parent_index = child_index;
}
}
for (int i = len - 1; i > 0; --i) {
SwapSortedKeys(0, i);
int parent_index = 0;
const uint32_t parent_hash = GetSortedKey(parent_index)->hash();
max_parent_index = (i / 2) - 1;
while (parent_index <= max_parent_index) {
int child_index = parent_index * 2 + 1;
uint32_t child_hash = GetSortedKey(child_index)->hash();
if (child_index + 1 < i) {
uint32_t right_child_hash = GetSortedKey(child_index + 1)->hash();
if (right_child_hash > child_hash) {
child_index++;
child_hash = right_child_hash;
}
}
if (child_hash <= parent_hash) break;
SwapSortedKeys(parent_index, child_index);
parent_index = child_index;
}
}
DCHECK(IsSortedNoDuplicates());
}
void DescriptorArray::CheckNameCollisionDuringInsertion(Descriptor* desc,
uint32_t desc_hash,
int insertion_index) {
DCHECK_GE(insertion_index, 0);
DCHECK_LE(insertion_index, number_of_all_descriptors());
if (insertion_index <= 0) return;
for (int i = insertion_index; i > 0; --i) {
Tagged<Name> current_key = GetSortedKey(i - 1);
if (current_key->hash() != desc_hash) return;
CHECK(current_key != *desc->GetKey());
}
}
DirectHandle<AccessorPair> AccessorPair::Copy(Isolate* isolate,
DirectHandle<AccessorPair> pair) {
DirectHandle<AccessorPair> copy = isolate->factory()->NewAccessorPair();
DisallowGarbageCollection no_gc;
Tagged<AccessorPair> raw_src = *pair;
Tagged<AccessorPair> raw_copy = *copy;
raw_copy->set_getter(raw_src->getter());
raw_copy->set_setter(raw_src->setter());
return copy;
}
Handle<JSAny> AccessorPair::GetComponent(
Isolate* isolate, DirectHandle<NativeContext> native_context,
DirectHandle<AccessorPair> accessor_pair, AccessorComponent component) {
Handle<Object> accessor(accessor_pair->get(component), isolate);
if (IsFunctionTemplateInfo(*accessor)) {
Handle<JSFunction> function =
ApiNatives::InstantiateFunction(isolate, native_context,
Cast<FunctionTemplateInfo>(accessor))
.ToHandleChecked();
accessor_pair->set(component, *function, kReleaseStore);
return function;
}
if (IsNull(*accessor, isolate)) {
return isolate->factory()->undefined_value();
}
return Cast<JSAny>(accessor);
}
#ifdef DEBUG
bool DescriptorArray::IsEqualTo(Tagged<DescriptorArray> other) {
if (number_of_all_descriptors() != other->number_of_all_descriptors()) {
return false;
}
for (InternalIndex i : InternalIndex::Range(number_of_descriptors())) {
if (GetKey(i) != other->GetKey(i)) return false;
if (GetDetails(i).AsSmi() != other->GetDetails(i).AsSmi()) return false;
if (GetValue(i) != other->GetValue(i)) return false;
}
return true;
}
#endif
MaybeDirectHandle<String> Name::ToFunctionName(Isolate* isolate,
DirectHandle<Name> name) {
if (IsString(*name)) return Cast<String>(name);
DirectHandle<Object> description(Cast<Symbol>(name)->description(), isolate);
if (IsUndefined(*description, isolate)) {
return isolate->factory()->empty_string();
}
IncrementalStringBuilder builder(isolate);
builder.AppendCharacter('[');
builder.AppendString(Cast<String>(description));
builder.AppendCharacter(']');
return builder.Finish();
}
MaybeDirectHandle<String> Name::ToFunctionName(Isolate* isolate,
DirectHandle<Name> name,
DirectHandle<String> prefix) {
DirectHandle<String> name_string;
ASSIGN_RETURN_ON_EXCEPTION(isolate, name_string,
ToFunctionName(isolate, name));
IncrementalStringBuilder builder(isolate);
builder.AppendString(prefix);
builder.AppendCharacter(' ');
builder.AppendString(name_string);
return builder.Finish();
}
void Relocatable::PostGarbageCollectionProcessing(Isolate* isolate) {
Relocatable* current = isolate->relocatable_top();
while (current != nullptr) {
current->PostGarbageCollection();
current = current->prev_;
}
}
int Relocatable::ArchiveSpacePerThread() { return sizeof(Relocatable*); }
char* Relocatable::ArchiveState(Isolate* isolate, char* to) {
*reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top();
isolate->set_relocatable_top(nullptr);
return to + ArchiveSpacePerThread();
}
char* Relocatable::RestoreState(Isolate* isolate, char* from) {
isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from));
return from + ArchiveSpacePerThread();
}
char* Relocatable::Iterate(RootVisitor* v, char* thread_storage) {
Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
Iterate(v, top);
return thread_storage + ArchiveSpacePerThread();
}
void Relocatable::Iterate(Isolate* isolate, RootVisitor* v) {
Iterate(v, isolate->relocatable_top());
}
void Relocatable::Iterate(RootVisitor* v, Relocatable* top) {
Relocatable* current = top;
while (current != nullptr) {
current->IterateInstance(v);
current = current->prev_;
}
}
START_PROHIBIT_SIGN_CONVERSION()
namespace {
template <typename sinkchar>
void WriteChunkListToFlat(Tagged<FixedArray> chunk_list_head,
uint32_t last_chunk_length, Tagged<String> separator,
sinkchar* sink, uint32_t sink_length) {
DisallowGarbageCollection no_gc;
#ifdef DEBUG
sinkchar* sink_end = sink + sink_length;
#endif
const uint32_t separator_length = separator->length();
const bool use_one_byte_separator_fast_path =
separator_length == 1 && sizeof(sinkchar) == 1 &&
StringShape(separator).IsSequentialOneByte();
uint8_t separator_one_char;
if (use_one_byte_separator_fast_path) {
CHECK(StringShape(separator).IsSequentialOneByte());
CHECK_EQ(separator->length(), 1);
separator_one_char = Cast<SeqOneByteString>(separator)->GetChars(no_gc)[0];
}
uint32_t num_separators = 0;
uint32_t repeat_last = 0;
Tagged<FixedArray> chunk = chunk_list_head;
Tagged<Object> last_element = Smi::zero();
#ifdef DEBUG
Tagged<FixedArray> prev_chunk = GetReadOnlyRoots().empty_fixed_array();
#endif
while (true) {
Tagged<Object> maybe_next_chunk = chunk->get(0);
bool is_last_chunk = IsUndefined(maybe_next_chunk);
uint32_t length = is_last_chunk ? last_chunk_length : chunk->ulength();
CHECK_GT(length, 0);
CHECK_LE(length, chunk->length());
for (uint32_t i = 1; i < length; i++) {
Tagged<Object> element = chunk->get(i);
const bool element_is_special = IsSmi(element);
if (V8_UNLIKELY(element_is_special)) {
int count;
CHECK(Object::ToInt32(element, &count));
if (count > 0) {
num_separators = static_cast<uint32_t>(count);
DCHECK(i == 1 || i == length - 1 || num_separators > 1);
} else {
repeat_last = static_cast<uint32_t>(-count);
DCHECK_GE(i, 1);
DCHECK(IsString(last_element));
DCHECK_IMPLIES(i == 1, prev_chunk->get(prev_chunk->ulength() - 1) ==
last_element);
DCHECK_IMPLIES(i > 1, chunk->get(i - 1) == last_element);
}
}
if (num_separators > 0 && separator_length > 0) {
if (use_one_byte_separator_fast_path) {
DCHECK_LE(sink + num_separators, sink_end);
memset(sink, separator_one_char, num_separators);
DCHECK_EQ(separator_length, 1);
sink += num_separators;
} else {
for (uint32_t j = 0; j < num_separators; j++) {
DCHECK_LE(sink + separator_length, sink_end);
String::WriteToFlat(separator, sink, 0, separator_length);
sink += separator_length;
}
}
num_separators = 0;
}
if (V8_UNLIKELY(repeat_last > 0)) {
uint32_t string_length = Cast<String>(last_element)->length();
DCHECK_GT(string_length, 0);
uint32_t length_with_sep = string_length + separator_length;
sinkchar* copy_end =
sink + (length_with_sep * repeat_last) - separator_length;
uint32_t copy_length = length_with_sep;
while (sink < copy_end - copy_length) {
DCHECK_LE(sink + copy_length, sink_end);
memcpy(sink, sink - copy_length, copy_length * sizeof(sinkchar));
sink += copy_length;
copy_length *= 2;
}
uint32_t remaining = static_cast<uint32_t>(copy_end - sink);
if (remaining > 0) {
DCHECK_LE(sink + remaining, sink_end);
memcpy(sink, sink - remaining - separator_length,
remaining * sizeof(sinkchar));
sink += remaining;
}
repeat_last = 0;
num_separators = 1;
}
if (V8_LIKELY(!element_is_special)) {
DCHECK(IsString(element));
Tagged<String> string = Cast<String>(element);
const uint32_t string_length = string->length();
DCHECK(string_length == 0 || sink < sink_end);
String::WriteToFlat(string, sink, 0, string_length);
sink += string_length;
num_separators = 1;
}
last_element = element;
}
if (is_last_chunk) break;
#ifdef DEBUG
prev_chunk = chunk;
#endif
chunk = Cast<FixedArray>(maybe_next_chunk);
}
DCHECK_EQ(sink, sink_end);
}
}
Address JSArray::ArrayJoinConcatToSequentialString(
Isolate* isolate, Address raw_chunk_list_head,
uintptr_t raw_last_chunk_length, Address raw_separator, Address raw_dest) {
DisallowGarbageCollection no_gc;
DisallowJavascriptExecution no_js(isolate);
Tagged<FixedArray> chunk_list_head =
Cast<FixedArray>(Tagged<Object>(raw_chunk_list_head));
Tagged<String> separator = Cast<String>(Tagged<Object>(raw_separator));
Tagged<String> dest = Cast<String>(Tagged<Object>(raw_dest));
DCHECK(IsFixedArray(chunk_list_head));
DCHECK(StringShape(dest).IsSequentialOneByte() ||
StringShape(dest).IsSequentialTwoByte());
uint32_t last_chunk_length = static_cast<uint32_t>(raw_last_chunk_length);
if (StringShape(dest).IsSequentialOneByte()) {
WriteChunkListToFlat(chunk_list_head, last_chunk_length, separator,
Cast<SeqOneByteString>(dest)->GetChars(no_gc),
dest->length());
} else {
DCHECK(StringShape(dest).IsSequentialTwoByte());
WriteChunkListToFlat(chunk_list_head, last_chunk_length, separator,
Cast<SeqTwoByteString>(dest)->GetChars(no_gc),
dest->length());
}
return dest.ptr();
}
END_PROHIBIT_SIGN_CONVERSION()
void Oddball::Initialize(Isolate* isolate, DirectHandle<Oddball> oddball,
const char* to_string, DirectHandle<Number> to_number,
const char* type_of, uint8_t kind) {
DirectHandle<String> internalized_to_string =
isolate->factory()->InternalizeUtf8String(to_string);
DirectHandle<String> internalized_type_of =
isolate->factory()->InternalizeUtf8String(type_of);
if (IsHeapNumber(*to_number)) {
oddball->set_to_number_raw_as_bits(
Cast<HeapNumber>(to_number)->value_as_bits());
} else {
oddball->set_to_number_raw(Object::NumberValue(*to_number));
}
oddball->set_to_number(*to_number);
oddball->set_to_string(*internalized_to_string);
oddball->set_type_of(*internalized_type_of);
oddball->set_kind(kind);
}
void JSArray::Initialize(Isolate* isolate, DirectHandle<JSArray> array,
int capacity, int length) {
DCHECK_GE(capacity, 0);
isolate->factory()->NewJSArrayStorage(
array, length, capacity,
ArrayStorageAllocationMode::INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE);
}
Maybe<bool> JSArray::SetLength(Isolate* isolate, DirectHandle<JSArray> array,
uint32_t new_length) {
if (array->SetLengthWouldNormalize(new_length)) {
JSObject::NormalizeElements(isolate, array);
}
return array->GetElementsAccessor()->SetLength(isolate, array, new_length);
}
Maybe<bool> JSProxy::SetPrototype(Isolate* isolate, DirectHandle<JSProxy> proxy,
DirectHandle<Object> value,
bool from_javascript,
ShouldThrow should_throw) {
STACK_CHECK(isolate, Nothing<bool>());
DirectHandle<Name> trap_name = isolate->factory()->setPrototypeOf_string();
DCHECK(IsJSReceiver(*value) || IsNull(*value, isolate));
DirectHandle<Object> handler(proxy->handler(), isolate);
if (proxy->IsRevoked()) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyRevoked, trap_name));
return Nothing<bool>();
}
DirectHandle<JSReceiver> target(Cast<JSReceiver>(proxy->target()), isolate);
DirectHandle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap,
Object::GetMethod(isolate, Cast<JSReceiver>(handler), trap_name));
if (IsUndefined(*trap, isolate)) {
return JSReceiver::SetPrototype(isolate, target, value, from_javascript,
should_throw);
}
DirectHandle<Object> args[] = {target, value};
DirectHandle<Object> trap_result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result,
Execution::Call(isolate, trap, handler, base::VectorOf(args)));
bool bool_trap_result = Object::BooleanValue(*trap_result, isolate);
if (!bool_trap_result) {
RETURN_FAILURE(
isolate, should_throw,
NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name));
}
Maybe<bool> is_extensible = JSReceiver::IsExtensible(isolate, target);
if (is_extensible.IsNothing()) return Nothing<bool>();
if (is_extensible.FromJust()) {
if (bool_trap_result) return Just(true);
RETURN_FAILURE(
isolate, should_throw,
NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name));
}
DirectHandle<Object> target_proto;
ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto,
JSReceiver::GetPrototype(isolate, target));
if (bool_trap_result && !Object::SameValue(*value, *target_proto)) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxySetPrototypeOfNonExtensible));
return Nothing<bool>();
}
return Just(true);
}
bool JSArray::SetLengthWouldNormalize(uint32_t new_length) {
if (!HasFastElements()) return false;
uint32_t capacity = static_cast<uint32_t>(elements()->length());
uint32_t new_capacity;
return JSArray::SetLengthWouldNormalize(Isolate::Current()->heap(),
new_length) &&
ShouldConvertToSlowElements(*this, capacity, new_length - 1,
&new_capacity);
}
void AllocationSite::ResetPretenureDecision() {
set_pretenure_decision(kUndecided);
set_memento_found_count(0);
set_memento_create_count(0);
}
AllocationType AllocationSite::GetAllocationType() const {
PretenureDecision mode = pretenure_decision();
return mode == kTenure ? AllocationType::kOld : AllocationType::kYoung;
}
bool AllocationSite::IsNested() {
DCHECK(v8_flags.trace_track_allocation_sites);
Tagged<Object> current = Isolate::Current()->heap()->allocation_sites_list();
while (IsAllocationSite(current)) {
Tagged<AllocationSiteWithWeakNext> current_site =
Cast<AllocationSiteWithWeakNext>(current);
if (current_site->nested_site() == this) {
return true;
}
current = current_site->weak_next();
}
return false;
}
bool AllocationSite::ShouldTrack(ElementsKind from, ElementsKind to) {
if (!V8_ALLOCATION_SITE_TRACKING_BOOL) return false;
return IsMoreGeneralElementsKindTransition(from, to);
}
const char* AllocationSite::PretenureDecisionName(PretenureDecision decision) {
switch (decision) {
case kUndecided:
return "undecided";
case kDontTenure:
return "don't tenure";
case kMaybeTenure:
return "maybe tenure";
case kTenure:
return "tenure";
case kZombie:
return "zombie";
default:
UNREACHABLE();
}
}
bool JSArray::WouldChangeReadOnlyLength(DirectHandle<JSArray> array,
uint32_t index) {
uint32_t length = 0;
CHECK(Object::ToArrayLength(array->length(), &length));
if (length <= index) return HasReadOnlyLength(array);
return false;
}
const char* Symbol::PrivateSymbolToName() const {
ReadOnlyRoots roots = GetReadOnlyRoots();
#define SYMBOL_CHECK_AND_PRINT(_, name) \
if (this == roots.name()) return #name;
PRIVATE_SYMBOL_LIST_GENERATOR(SYMBOL_CHECK_AND_PRINT, )
#undef SYMBOL_CHECK_AND_PRINT
return "UNKNOWN";
}
v8::Promise::PromiseState JSPromise::status() const {
int value = flags() & StatusBits::kMask;
DCHECK(value == 0 || value == 1 || value == 2);
return static_cast<v8::Promise::PromiseState>(value);
}
void JSPromise::set_status(Promise::PromiseState status) {
int value = flags() & ~StatusBits::kMask;
set_flags(value | status);
}
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();
}
Handle<Object> JSPromise::Fulfill(DirectHandle<JSPromise> promise,
DirectHandle<Object> value) {
Isolate* const isolate = Isolate::Current();
#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
if (isolate->HasContextPromiseHooks()) {
isolate->raw_native_context()->RunPromiseHook(
PromiseHookType::kResolve, indirect_handle(promise, isolate),
isolate->factory()->undefined_value());
}
#endif
CHECK_EQ(Promise::kPending, promise->status());
DirectHandle<Object> reactions(promise->reactions(), isolate);
promise->set_reactions_or_result(Cast<JSAny>(*value));
promise->set_status(Promise::kFulfilled);
return TriggerPromiseReactions(isolate, reactions, value,
PromiseReaction::kFulfill);
}
static void MoveMessageToPromise(Isolate* isolate,
DirectHandle<JSPromise> promise) {
if (!isolate->has_pending_message()) return;
if (isolate->debug()->is_active()) {
DirectHandle<Object> message(isolate->pending_message(), isolate);
DirectHandle<Symbol> key =
isolate->factory()->promise_debug_message_symbol();
Object::SetProperty(isolate, promise, key, message,
StoreOrigin::kMaybeKeyed,
Just(ShouldThrow::kThrowOnError))
.Assert();
}
isolate->clear_pending_message();
}
Handle<Object> JSPromise::Reject(DirectHandle<JSPromise> promise,
DirectHandle<Object> reason,
bool debug_event) {
Isolate* const isolate = Isolate::Current();
DCHECK(
!reinterpret_cast<v8::Isolate*>(isolate)->GetCurrentContext().IsEmpty());
MoveMessageToPromise(isolate, promise);
if (debug_event) isolate->debug()->OnPromiseReject(promise, reason);
isolate->RunAllPromiseHooks(PromiseHookType::kResolve, promise,
isolate->factory()->undefined_value());
CHECK_EQ(Promise::kPending, promise->status());
DirectHandle<Object> reactions(promise->reactions(), isolate);
promise->set_reactions_or_result(Cast<JSAny>(*reason));
promise->set_status(Promise::kRejected);
if (!promise->has_handler()) {
isolate->ReportPromiseReject(promise, reason, kPromiseRejectWithNoHandler);
}
return TriggerPromiseReactions(isolate, reactions, reason,
PromiseReaction::kReject);
}
MaybeHandle<Object> JSPromise::Resolve(DirectHandle<JSPromise> promise,
DirectHandle<Object> resolution_obj) {
Isolate* const isolate = Isolate::Current();
DCHECK(
!reinterpret_cast<v8::Isolate*>(isolate)->GetCurrentContext().IsEmpty());
isolate->RunPromiseHook(PromiseHookType::kResolve, promise,
isolate->factory()->undefined_value());
if (promise.is_identical_to(resolution_obj)) {
DirectHandle<Object> self_resolution_error =
isolate->factory()->NewTypeError(MessageTemplate::kPromiseCyclic,
resolution_obj);
return Reject(promise, self_resolution_error);
}
DirectHandle<JSReceiver> resolution_recv;
if (!TryCast<JSReceiver>(resolution_obj, &resolution_recv)) {
return Fulfill(promise, resolution_obj);
}
MaybeDirectHandle<Object> then;
if (IsJSPromise(*resolution_recv) &&
resolution_recv->map()->prototype()->map()->instance_type() ==
JS_PROMISE_PROTOTYPE_TYPE &&
Protectors::IsPromiseThenLookupChainIntact(isolate)) {
DirectHandle<NativeContext> resolution_proto_context =
Cast<JSReceiver>(resolution_recv->map()->prototype())
->GetCreationContext(isolate)
.ToHandleChecked();
then =
DirectHandle<Object>(resolution_proto_context->promise_then(), isolate);
} else {
then = JSReceiver::GetProperty(isolate, resolution_recv,
isolate->factory()->then_string());
}
DirectHandle<Object> then_action;
if (!then.ToHandle(&then_action)) {
if (!isolate->is_catchable_by_javascript(isolate->exception())) {
return kNullMaybeHandle;
}
DirectHandle<Object> reason(isolate->exception(), isolate);
isolate->clear_exception();
return Reject(promise, reason, false);
}
if (!IsCallable(*then_action)) {
return Fulfill(promise, resolution_recv);
}
DirectHandle<NativeContext> then_context;
if (!JSReceiver::GetContextForMicrotask(Cast<JSReceiver>(then_action))
.ToHandle(&then_context)) {
then_context = isolate->native_context();
}
DirectHandle<PromiseResolveThenableJobTask> task =
isolate->factory()->NewPromiseResolveThenableJobTask(
promise, resolution_recv, Cast<JSReceiver>(then_action),
then_context);
if (isolate->debug()->is_active() && IsJSPromise(*resolution_recv)) {
Object::SetProperty(isolate, resolution_recv,
isolate->factory()->promise_handled_by_symbol(),
promise)
.Check();
}
MicrotaskQueue* microtask_queue = then_context->microtask_queue();
if (microtask_queue) microtask_queue->EnqueueMicrotask(*task);
return isolate->factory()->undefined_value();
}
Handle<Object> JSPromise::TriggerPromiseReactions(
Isolate* isolate, DirectHandle<Object> reactions,
DirectHandle<Object> argument, PromiseReaction::Type type) {
CHECK(IsSmi(*reactions) || IsPromiseReaction(*reactions));
{
DisallowGarbageCollection no_gc;
Tagged<UnionOf<Smi, PromiseReaction>> current =
Cast<UnionOf<Smi, PromiseReaction>>(*reactions);
Tagged<UnionOf<Smi, PromiseReaction>> reversed = Smi::zero();
while (!IsSmi(current)) {
Tagged<UnionOf<Smi, PromiseReaction>> next =
Cast<PromiseReaction>(current)->next();
Cast<PromiseReaction>(current)->set_next(reversed);
reversed = current;
current = next;
}
reactions = direct_handle(reversed, isolate);
}
while (!IsSmi(*reactions)) {
auto task = Cast<HeapObject>(reactions);
auto reaction = Cast<PromiseReaction>(task);
reactions = direct_handle(reaction->next(), isolate);
DirectHandle<NativeContext> handler_context;
DirectHandle<UnionOf<Undefined, JSCallable>> primary_handler;
DirectHandle<UnionOf<Undefined, JSCallable>> secondary_handler;
if (type == PromiseReaction::kFulfill) {
primary_handler = direct_handle(reaction->fulfill_handler(), isolate);
secondary_handler = direct_handle(reaction->reject_handler(), isolate);
} else {
primary_handler = direct_handle(reaction->reject_handler(), isolate);
secondary_handler = direct_handle(reaction->fulfill_handler(), isolate);
}
bool has_handler_context = false;
if (IsJSReceiver(*primary_handler)) {
has_handler_context =
JSReceiver::GetContextForMicrotask(Cast<JSReceiver>(primary_handler))
.ToHandle(&handler_context);
}
if (!has_handler_context && IsJSReceiver(*secondary_handler)) {
has_handler_context = JSReceiver::GetContextForMicrotask(
Cast<JSReceiver>(secondary_handler))
.ToHandle(&handler_context);
}
if (!has_handler_context) handler_context = isolate->native_context();
static_assert(static_cast<int>(sizeof(PromiseReaction)) ==
static_cast<int>(sizeof(PromiseReactionJobTask)));
if (type == PromiseReaction::kFulfill) {
task->set_map(
isolate,
ReadOnlyRoots(isolate).promise_fulfill_reaction_job_task_map(),
kReleaseStore);
Cast<PromiseFulfillReactionJobTask>(task)->set_argument(*argument);
Cast<PromiseFulfillReactionJobTask>(task)->set_context(*handler_context);
static_assert(
static_cast<int>(offsetof(PromiseReaction, fulfill_handler_)) ==
static_cast<int>(offsetof(PromiseFulfillReactionJobTask, handler_)));
static_assert(
static_cast<int>(offsetof(PromiseReaction, promise_or_capability_)) ==
static_cast<int>(
offsetof(PromiseFulfillReactionJobTask, promise_or_capability_)));
#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
static_assert(
static_cast<int>(offsetof(PromiseReaction,
continuation_preserved_embedder_data_)) ==
static_cast<int>(offsetof(PromiseFulfillReactionJobTask,
continuation_preserved_embedder_data_)));
#endif
} else {
DisallowGarbageCollection no_gc;
task->set_map(
isolate,
ReadOnlyRoots(isolate).promise_reject_reaction_job_task_map(),
kReleaseStore);
Cast<PromiseRejectReactionJobTask>(task)->set_argument(*argument);
Cast<PromiseRejectReactionJobTask>(task)->set_context(*handler_context);
Cast<PromiseRejectReactionJobTask>(task)->set_handler(*primary_handler);
static_assert(
static_cast<int>(offsetof(PromiseReaction, promise_or_capability_)) ==
static_cast<int>(
offsetof(PromiseRejectReactionJobTask, promise_or_capability_)));
#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
static_assert(
static_cast<int>(offsetof(PromiseReaction,
continuation_preserved_embedder_data_)) ==
static_cast<int>(offsetof(PromiseRejectReactionJobTask,
continuation_preserved_embedder_data_)));
#endif
}
MicrotaskQueue* microtask_queue = handler_context->microtask_queue();
if (microtask_queue) {
microtask_queue->EnqueueMicrotask(*Cast<PromiseReactionJobTask>(task));
}
}
return isolate->factory()->undefined_value();
}
#ifdef V8_LOWER_LIMITS_MODE
const uint32_t EphemeronHashTableShape::kHashBits = 10;
#else
const uint32_t EphemeronHashTableShape::kHashBits =
PropertyArray::HashField::kSize;
#endif
template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::IteratePrefix(ObjectVisitor* v) {
BodyDescriptorBase::IteratePointers(this, 0, kElementsStartOffset, v);
}
template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::IterateElements(ObjectVisitor* v) {
BodyDescriptorBase::IteratePointers(this, kElementsStartOffset,
SizeFor(length()), v);
}
template <typename Derived, typename Shape>
template <typename IsolateT>
Handle<Derived> HashTable<Derived, Shape>::New(
IsolateT* isolate, uint32_t at_least_space_for, AllocationType allocation,
MinimumCapacity capacity_option) {
Handle<Derived> result;
if (TryNew(isolate, at_least_space_for, allocation, capacity_option)
.To(&result)) {
return result;
}
isolate->FatalProcessOutOfHeapMemory("invalid table size");
UNREACHABLE();
}
template <typename Derived, typename Shape>
template <typename IsolateT>
MaybeHandle<Derived> HashTable<Derived, Shape>::TryNew(
IsolateT* isolate, uint32_t at_least_space_for, AllocationType allocation,
MinimumCapacity capacity_option) {
DCHECK_LE(0, at_least_space_for);
uint32_t capacity;
if (capacity_option == USE_CUSTOM_MINIMUM_CAPACITY) {
DCHECK(base::bits::IsPowerOfTwo(at_least_space_for));
capacity = at_least_space_for;
if (capacity > HashTable::kMaxCapacity) {
return kNullMaybeHandle;
}
} else {
const uint32_t kMaxAtLeastSpaceFor = HashTable::kMaxCapacity * 2 / 3;
static_assert(ComputeCapacity(kMaxAtLeastSpaceFor) <=
HashTable::kMaxCapacity);
static_assert(ComputeCapacity(kMaxAtLeastSpaceFor + 1) >=
HashTable::kMaxCapacity);
static_assert(ComputeCapacity(kMaxAtLeastSpaceFor + 4) >
HashTable::kMaxCapacity);
if (at_least_space_for > kMaxAtLeastSpaceFor) {
return kNullMaybeHandle;
}
capacity = ComputeCapacity(at_least_space_for);
DCHECK_LE(capacity, HashTable::kMaxCapacity);
}
return NewInternal(isolate, capacity, allocation);
}
template <typename Derived, typename Shape>
template <typename IsolateT>
Handle<Derived> HashTable<Derived, Shape>::NewInternal(
IsolateT* isolate, uint32_t capacity, AllocationType allocation) {
auto* factory = isolate->factory();
int length = EntryToIndex(InternalIndex(capacity));
Handle<FixedArray> array = factory->NewFixedArrayWithMap(
Derived::GetMap(isolate->roots_table()), length, allocation);
Handle<Derived> table = Cast<Derived>(array);
DisallowGarbageCollection no_gc;
Tagged<Derived> raw_table = *table;
raw_table->SetNumberOfElements(0);
raw_table->SetNumberOfDeletedElements(0);
raw_table->SetCapacity(capacity);
return table;
}
template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::Rehash(PtrComprCageBase cage_base,
Tagged<Derived> new_table) {
DisallowGarbageCollection no_gc;
WriteBarrierModeScope mode = new_table->GetWriteBarrierMode(no_gc);
DCHECK_LT(NumberOfElements(), new_table->Capacity());
for (int i = kPrefixStartIndex; i < kElementsStartIndex; i++) {
new_table->set(i, get(i), *mode);
}
ReadOnlyRoots roots = GetReadOnlyRoots();
for (InternalIndex i : this->IterateEntries()) {
uint32_t from_index = EntryToIndex(i);
Tagged<Object> k = this->get(from_index);
if (!IsKey(roots, k)) continue;
uint32_t hash = TodoShape::HashForObject(roots, k);
uint32_t insertion_index =
EntryToIndex(new_table->FindInsertionEntry(cage_base, roots, hash));
new_table->set_key(insertion_index, get(from_index), *mode);
for (int j = 1; j < TodoShape::kEntrySize; j++) {
new_table->set(insertion_index + j, get(from_index + j), *mode);
}
}
new_table->SetNumberOfElements(NumberOfElements());
new_table->SetNumberOfDeletedElements(0);
}
template <typename Derived, typename Shape>
InternalIndex HashTable<Derived, Shape>::EntryForProbe(ReadOnlyRoots roots,
Tagged<Object> k,
int probe,
InternalIndex expected) {
uint32_t hash = TodoShape::HashForObject(roots, k);
uint32_t capacity = this->Capacity();
InternalIndex entry = FirstProbe(hash, capacity);
for (int i = 1; i < probe; i++) {
if (entry == expected) return expected;
entry = NextProbe(entry, i, capacity);
}
return entry;
}
template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::Swap(InternalIndex entry1, InternalIndex entry2,
WriteBarrierMode mode) {
int index1 = EntryToIndex(entry1);
int index2 = EntryToIndex(entry2);
Tagged<Object> temp[TodoShape::kEntrySize];
Derived* self = static_cast<Derived*>(this);
for (int j = 0; j < TodoShape::kEntrySize; j++) {
temp[j] = get(index1 + j);
}
self->set_key(index1, get(index2), mode);
for (int j = 1; j < TodoShape::kEntrySize; j++) {
set(index1 + j, get(index2 + j), mode);
}
self->set_key(index2, temp[0], mode);
for (int j = 1; j < TodoShape::kEntrySize; j++) {
set(index2 + j, temp[j], mode);
}
}
template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::Rehash(PtrComprCageBase cage_base) {
DisallowGarbageCollection no_gc;
WriteBarrierModeScope mode = GetWriteBarrierMode(no_gc);
ReadOnlyRoots roots = EarlyGetReadOnlyRoots();
uint32_t capacity = Capacity();
bool done = false;
for (int probe = 1; !done; probe++) {
done = true;
for (InternalIndex current(0); current.raw_value() < capacity;
) {
Tagged<Object> current_key = KeyAt(cage_base, current);
if (!IsKey(roots, current_key)) {
++current;
continue;
}
InternalIndex target = EntryForProbe(roots, current_key, probe, current);
if (current == target) {
++current;
continue;
}
Tagged<Object> target_key = KeyAt(cage_base, target);
if (!IsKey(roots, target_key) ||
EntryForProbe(roots, target_key, probe, target) != target) {
Swap(current, target, *mode);
} else {
done = false;
++current;
}
}
}
Tagged<Object> the_hole = roots.the_hole_value();
Tagged<HeapObject> undefined = roots.undefined_value();
Derived* self = static_cast<Derived*>(this);
for (InternalIndex current : InternalIndex::Range(capacity)) {
if (KeyAt(cage_base, current) == the_hole) {
self->set_key(EntryToIndex(current) + kEntryKeyIndex, undefined,
SKIP_WRITE_BARRIER);
}
}
SetNumberOfDeletedElements(0);
}
template <typename Derived, typename Shape>
template <typename IsolateT, template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived> HashTable<Derived, Shape>::EnsureCapacity(
IsolateT* isolate, HandleType<Derived> table, int n,
AllocationType allocation) {
if (table->HasSufficientCapacityToAdd(n)) return table;
int capacity = table->Capacity();
int new_nof = table->NumberOfElements() + n;
bool should_pretenure = allocation == AllocationType::kOld ||
((capacity > kMinCapacityForPretenure) &&
!HeapLayout::InYoungGeneration(*table));
HandleType<Derived> new_table = HashTable::New(
isolate, new_nof,
should_pretenure ? AllocationType::kOld : AllocationType::kYoung);
table->Rehash(isolate, *new_table);
return new_table;
}
template <typename Derived, typename Shape>
bool HashTable<Derived, Shape>::HasSufficientCapacityToAdd(
int number_of_additional_elements) {
return HasSufficientCapacityToAdd(Capacity(), NumberOfElements(),
NumberOfDeletedElements(),
number_of_additional_elements);
}
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;
if ((nof < capacity) &&
((number_of_deleted_elements <= (capacity - nof) / 2))) {
int needed_free = nof / 2;
if (nof + needed_free <= capacity) return true;
}
return false;
}
template <typename Derived, typename Shape>
uint32_t HashTable<Derived, Shape>::ComputeCapacityWithShrink(
uint32_t current_capacity, uint32_t at_least_room_for) {
if (at_least_room_for > (current_capacity / 4)) return current_capacity;
uint32_t new_capacity = ComputeCapacity(at_least_room_for);
DCHECK_GE(new_capacity, at_least_room_for);
if (new_capacity < Derived::kMinShrinkCapacity) return current_capacity;
return new_capacity;
}
template <typename Derived, typename Shape>
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived> HashTable<Derived, Shape>::Shrink(Isolate* isolate,
HandleType<Derived> table,
int additional_capacity) {
uint32_t new_capacity = ComputeCapacityWithShrink(
table->Capacity(), table->NumberOfElements() + additional_capacity);
if (new_capacity == table->Capacity()) return table;
DCHECK_GE(new_capacity, Derived::kMinShrinkCapacity);
bool pretenure = (new_capacity > kMinCapacityForPretenure) &&
!HeapLayout::InYoungGeneration(*table);
HandleType<Derived> new_table =
HashTable::New(isolate, new_capacity,
pretenure ? AllocationType::kOld : AllocationType::kYoung,
USE_CUSTOM_MINIMUM_CAPACITY);
table->Rehash(isolate, *new_table);
return new_table;
}
template <typename Derived, typename Shape>
InternalIndex HashTable<Derived, Shape>::FindInsertionEntry(
PtrComprCageBase cage_base, ReadOnlyRoots roots, uint32_t hash) {
uint32_t capacity = Capacity();
uint32_t count = 1;
for (InternalIndex entry = FirstProbe(hash, capacity);;
entry = NextProbe(entry, count++, capacity)) {
if (!IsKey(roots, KeyAt(cage_base, entry))) return entry;
}
}
std::optional<Tagged<PropertyCell>>
GlobalDictionary::TryFindPropertyCellForConcurrentLookupIterator(
Isolate* isolate, DirectHandle<Name> name, RelaxedLoadTag tag) {
DisallowGarbageCollection no_gc;
PtrComprCageBase cage_base{isolate};
ReadOnlyRoots roots(isolate);
const int32_t hash = TodoShape::Hash(roots, name);
const uint32_t capacity = Capacity();
uint32_t count = 1;
Tagged<Object> undefined = roots.undefined_value();
Tagged<Object> the_hole = roots.the_hole_value();
for (InternalIndex entry = FirstProbe(hash, capacity);;
entry = NextProbe(entry, count++, capacity)) {
Tagged<Object> element = KeyAt(cage_base, entry, kRelaxedLoad);
if (isolate->heap()->IsPendingAllocation(element)) return {};
if (element == undefined) return {};
if (TodoShape::kMatchNeedsHoleCheck && element == the_hole) continue;
if (!TodoShape::IsMatch(name, element)) continue;
CHECK(IsPropertyCell(element, cage_base));
return Cast<PropertyCell>(element);
}
}
Handle<StringSet> StringSet::New(Isolate* isolate) {
return HashTable::New(isolate, 0);
}
Handle<StringSet> StringSet::Add(Isolate* isolate, Handle<StringSet> stringset,
DirectHandle<String> name) {
if (!stringset->Has(isolate, name)) {
stringset = EnsureCapacity(isolate, stringset);
uint32_t hash = TodoShape::Hash(ReadOnlyRoots(isolate), *name);
InternalIndex entry = stringset->FindInsertionEntry(isolate, hash);
stringset->set(EntryToIndex(entry), *name);
stringset->ElementAdded();
}
return stringset;
}
bool StringSet::Has(Isolate* isolate, DirectHandle<String> name) {
return FindEntry(isolate, *name).is_found();
}
Handle<RegisteredSymbolTable> RegisteredSymbolTable::Add(
Isolate* isolate, Handle<RegisteredSymbolTable> table,
DirectHandle<String> key, DirectHandle<Symbol> symbol) {
SLOW_DCHECK(table->FindEntry(isolate, key).is_not_found());
table = EnsureCapacity(isolate, table);
uint32_t hash = TodoShape::Hash(ReadOnlyRoots(isolate), key);
InternalIndex entry = table->FindInsertionEntry(isolate, hash);
table->set(EntryToIndex(entry), *key);
table->set(EntryToValueIndex(entry), *symbol);
table->ElementAdded();
return table;
}
template <typename Derived, typename Shape>
template <typename IsolateT>
Handle<Derived> BaseNameDictionary<Derived, Shape>::New(
IsolateT* isolate, int at_least_space_for, AllocationType allocation,
MinimumCapacity capacity_option) {
DCHECK_LE(0, at_least_space_for);
Handle<Derived> dict = Dictionary<Derived, Shape>::New(
isolate, at_least_space_for, allocation, capacity_option);
dict->SetHash(PropertyArray::kNoHashSentinel);
dict->set_next_enumeration_index(PropertyDetails::kInitialIndex);
return dict;
}
template <typename IsolateT>
Handle<NameDictionary> NameDictionary::New(IsolateT* isolate,
int at_least_space_for,
AllocationType allocation,
MinimumCapacity capacity_option) {
Handle<NameDictionary> dict =
BaseNameDictionary<NameDictionary, NameDictionaryShape>::New(
isolate, at_least_space_for, allocation, capacity_option);
dict->set_flags(kFlagsDefault);
return dict;
}
template <typename Derived, typename Shape>
int BaseNameDictionary<Derived, Shape>::NextEnumerationIndex(
Isolate* isolate, DirectHandle<Derived> dictionary) {
int index = dictionary->next_enumeration_index();
if (!PropertyDetails::IsValidIndex(index)) {
DirectHandle<FixedArray> iteration_order =
IterationIndices(isolate, dictionary);
int length = iteration_order->length();
DCHECK_LE(length, dictionary->NumberOfElements());
for (int i = 0; i < length; i++) {
InternalIndex internal_index(Smi::ToInt(iteration_order->get(i)));
DCHECK(dictionary->IsKey(GetReadOnlyRoots(),
dictionary->KeyAt(isolate, internal_index)));
int enum_index = PropertyDetails::kInitialIndex + i;
PropertyDetails details = dictionary->DetailsAt(internal_index);
PropertyDetails new_details = details.set_index(enum_index);
dictionary->DetailsAtPut(internal_index, new_details);
}
index = PropertyDetails::kInitialIndex + length;
}
return index;
}
template <typename Derived, typename Shape>
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived> Dictionary<Derived, Shape>::DeleteEntry(
Isolate* isolate, HandleType<Derived> dictionary, InternalIndex entry) {
DCHECK(TodoShape::kEntrySize != 3 ||
dictionary->DetailsAt(entry).IsConfigurable());
dictionary->ClearEntry(entry);
dictionary->ElementRemoved();
return Shrink(isolate, dictionary);
}
template <typename Derived, typename Shape>
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
auto Dictionary<Derived, Shape>::AtPut(Isolate* isolate,
HandleType<Derived> dictionary, Key key,
DirectHandle<Object> value,
PropertyDetails details) {
using AtPutReturnType =
decltype(Derived::Add(isolate, dictionary, key, value, details));
InternalIndex entry = dictionary->FindEntry(isolate, key);
if (entry.is_not_found()) {
return Derived::Add(isolate, dictionary, key, value, details);
}
dictionary->ValueAtPut(entry, *value);
if (TodoShape::kEntrySize == 3) dictionary->DetailsAtPut(entry, details);
return AtPutReturnType(dictionary);
}
template <typename Derived, typename Shape>
void Dictionary<Derived, Shape>::UncheckedAtPut(
Isolate* isolate, DirectHandle<Derived> dictionary, Key key,
DirectHandle<Object> value, PropertyDetails details) {
InternalIndex entry = dictionary->FindEntry(isolate, key);
if (entry.is_not_found()) {
Derived::UncheckedAdd(isolate, dictionary, key, value, details);
} else {
dictionary->ValueAtPut(entry, *value);
if (TodoShape::kEntrySize == 3) dictionary->DetailsAtPut(entry, details);
}
}
template <typename Derived, typename Shape>
template <typename IsolateT, template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived>
BaseNameDictionary<Derived, Shape>::AddNoUpdateNextEnumerationIndex(
IsolateT* isolate, HandleType<Derived> dictionary, Key key,
DirectHandle<Object> value, PropertyDetails details,
InternalIndex* entry_out) {
return Dictionary<Derived, Shape>::Add(isolate, dictionary, key, value,
details, entry_out);
}
template <typename Derived, typename Shape>
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived>::MaybeType BaseNameDictionary<Derived, Shape>::Add(
Isolate* isolate, HandleType<Derived> dictionary, Key key,
DirectHandle<Object> value, PropertyDetails details,
InternalIndex* entry_out) {
DCHECK_EQ(0, details.dictionary_index());
int index = Derived::NextEnumerationIndex(isolate, dictionary);
if (!PropertyDetails::CanSetIndex(index)) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kTooManyProperties));
}
details = details.set_index(index);
dictionary = AddNoUpdateNextEnumerationIndex(isolate, dictionary, key, value,
details, entry_out);
dictionary->set_next_enumeration_index(index + 1);
return dictionary;
}
template <typename Derived, typename Shape>
template <typename IsolateT, template <typename> typename HandleType,
AllocationType key_allocation>
requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
HandleType<Derived> Dictionary<Derived, Shape>::Add(
IsolateT* isolate, HandleType<Derived> dictionary, Key key,
DirectHandle<Object> value, PropertyDetails details,
InternalIndex* entry_out) {
ReadOnlyRoots roots(isolate);
uint32_t hash = TodoShape::Hash(roots, key);
SLOW_DCHECK(dictionary->FindEntry(isolate, key).is_not_found());
dictionary = Derived::EnsureCapacity(isolate, dictionary);
DirectHandle<Object> k =
TodoShape::template AsHandle<key_allocation>(isolate, key);
InternalIndex entry = dictionary->FindInsertionEntry(isolate, roots, hash);
dictionary->SetEntry(entry, *k, *value, details);
DCHECK(IsNumber(dictionary->KeyAt(isolate, entry)) ||
IsUniqueName(TodoShape::Unwrap(dictionary->KeyAt(isolate, entry))));
dictionary->ElementAdded();
if (entry_out) *entry_out = entry;
return dictionary;
}
template <typename Derived, typename Shape>
template <typename IsolateT, template <typename> typename HandleType,
AllocationType key_allocation>
requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
void Dictionary<Derived, Shape>::UncheckedAdd(IsolateT* isolate,
HandleType<Derived> dictionary,
Key key,
DirectHandle<Object> value,
PropertyDetails details) {
ReadOnlyRoots roots(isolate);
uint32_t hash = TodoShape::Hash(roots, key);
SLOW_DCHECK(dictionary->FindEntry(isolate, key).is_not_found());
DCHECK(dictionary->HasSufficientCapacityToAdd(1));
DirectHandle<Object> k =
TodoShape::template AsHandle<key_allocation>(isolate, key);
InternalIndex entry = dictionary->FindInsertionEntry(isolate, roots, hash);
dictionary->SetEntry(entry, *k, *value, details);
DCHECK(IsNumber(dictionary->KeyAt(isolate, entry)) ||
IsUniqueName(TodoShape::Unwrap(dictionary->KeyAt(isolate, entry))));
}
template <typename Derived, typename Shape>
Handle<Derived> Dictionary<Derived, Shape>::ShallowCopy(
Isolate* isolate, DirectHandle<Derived> dictionary,
AllocationType allocation) {
return Cast<Derived>(isolate->factory()->CopyFixedArrayWithMap(
dictionary, Derived::GetMap(isolate->roots_table()), allocation));
}
Handle<SimpleNumberDictionary> SimpleNumberDictionary::Set(
Isolate* isolate, Handle<SimpleNumberDictionary> dictionary, uint32_t key,
DirectHandle<Object> value) {
return AtPut(isolate, dictionary, key, value, PropertyDetails::Empty());
}
Handle<SimpleNameDictionary> SimpleNameDictionary::Set(
Isolate* isolate, Handle<SimpleNameDictionary> dictionary,
DirectHandle<Name> key, DirectHandle<Object> value) {
return AtPut(isolate, dictionary, key, value, PropertyDetails::Empty());
}
void NumberDictionary::UpdateMaxNumberKey(
uint32_t key, DirectHandle<JSObject> dictionary_holder) {
DisallowGarbageCollection no_gc;
if (requires_slow_elements()) return;
if (key > kRequiresSlowElementsLimit) {
if (!dictionary_holder.is_null()) {
dictionary_holder->RequireSlowElements(this);
}
set_requires_slow_elements();
return;
}
Tagged<Object> max_index_object = get(kMaxNumberKeyIndex);
if (!IsSmi(max_index_object) || max_number_key() < key) {
FixedArray::set(kMaxNumberKeyIndex,
Smi::FromInt(key << kRequiresSlowElementsTagSize));
}
}
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<NumberDictionary>,
DirectHandle<NumberDictionary>>)
HandleType<NumberDictionary> NumberDictionary::Set(
Isolate* isolate, HandleType<NumberDictionary> dictionary, uint32_t key,
DirectHandle<Object> value, DirectHandle<JSObject> dictionary_holder,
PropertyDetails details) {
HandleType<NumberDictionary> new_dictionary =
AtPut(isolate, dictionary, key, value, details);
new_dictionary->UpdateMaxNumberKey(key, dictionary_holder);
return new_dictionary;
}
template DirectHandle<NumberDictionary> NumberDictionary::Set(
Isolate* isolate, DirectHandle<NumberDictionary> dictionary, uint32_t key,
DirectHandle<Object> value, DirectHandle<JSObject> dictionary_holder,
PropertyDetails details);
template IndirectHandle<NumberDictionary> NumberDictionary::Set(
Isolate* isolate, IndirectHandle<NumberDictionary> dictionary, uint32_t key,
DirectHandle<Object> value, DirectHandle<JSObject> dictionary_holder,
PropertyDetails details);
void NumberDictionary::UncheckedSet(Isolate* isolate,
DirectHandle<NumberDictionary> dictionary,
uint32_t key, DirectHandle<Object> value) {
UncheckedAtPut(isolate, dictionary, key, value, PropertyDetails::Empty());
}
void NumberDictionary::CopyValuesTo(Tagged<FixedArray> elements) {
ReadOnlyRoots roots = GetReadOnlyRoots();
int pos = 0;
DisallowGarbageCollection no_gc;
WriteBarrierModeScope mode = elements->GetWriteBarrierMode(no_gc);
for (InternalIndex i : this->IterateEntries()) {
Tagged<Object> k;
if (this->ToKey(roots, i, &k)) {
elements->set(pos++, this->ValueAt(i), *mode);
}
}
DCHECK_EQ(pos, elements->length());
}
template <typename Derived, typename Shape>
int Dictionary<Derived, Shape>::NumberOfEnumerableProperties() {
ReadOnlyRoots roots = GetReadOnlyRoots();
int result = 0;
for (InternalIndex i : this->IterateEntries()) {
Tagged<Object> k;
if (!this->ToKey(roots, i, &k)) continue;
if (Object::FilterKey(k, ENUMERABLE_STRINGS)) continue;
PropertyDetails details = this->DetailsAt(i);
PropertyAttributes attr = details.attributes();
if ((int{attr} & ONLY_ENUMERABLE) == 0) result++;
}
return result;
}
template <typename Derived, typename Shape>
DirectHandle<FixedArray> BaseNameDictionary<Derived, Shape>::IterationIndices(
Isolate* isolate, DirectHandle<Derived> dictionary) {
DirectHandle<FixedArray> array =
isolate->factory()->NewFixedArray(dictionary->NumberOfElements());
ReadOnlyRoots roots(isolate);
int array_size = 0;
{
DisallowGarbageCollection no_gc;
Tagged<Derived> raw_dictionary = *dictionary;
for (InternalIndex i : dictionary->IterateEntries()) {
Tagged<Object> k;
if (!raw_dictionary->ToKey(roots, i, &k)) continue;
array->set(array_size++, Smi::FromInt(i.as_int()));
}
if (std::is_same_v<Derived, GlobalDictionary>) {
DCHECK_LE(array_size, dictionary->NumberOfElements());
} else {
DCHECK_EQ(array_size, dictionary->NumberOfElements());
}
EnumIndexComparator<Derived> cmp(raw_dictionary);
AtomicSlot start(array->RawFieldOfFirstElement());
std::sort(start, start + array_size, cmp);
}
return FixedArray::RightTrimOrEmpty(isolate, array, array_size);
}
template <typename Derived, typename Shape>
Tagged<Object> Dictionary<Derived, Shape>::SlowReverseLookup(
Tagged<Object> value) {
Tagged<Derived> dictionary = Cast<Derived>(this);
ReadOnlyRoots roots = GetReadOnlyRoots();
for (InternalIndex i : dictionary->IterateEntries()) {
Tagged<Object> k;
if (!dictionary->ToKey(roots, i, &k)) continue;
Tagged<Object> e = dictionary->ValueAt(i);
if (e == value) return k;
}
return roots.undefined_value();
}
template <typename Derived, typename Shape>
void ObjectHashTableBase<Derived, Shape>::FillEntriesWithHoles(
DirectHandle<Derived> table) {
auto roots = GetReadOnlyRoots();
int length = table->length();
for (int i = Derived::EntryToIndex(InternalIndex(0)); i < length; i++) {
table->set_the_hole(roots, i);
}
}
template <typename Derived, typename Shape>
Tagged<Object> ObjectHashTableBase<Derived, Shape>::Lookup(
PtrComprCageBase cage_base, DirectHandle<Object> key, int32_t hash) {
DisallowGarbageCollection no_gc;
ReadOnlyRoots roots = GetReadOnlyRoots();
DCHECK(this->IsKey(roots, *key));
InternalIndex entry = this->FindEntry(cage_base, roots, key, hash);
if (entry.is_not_found()) return roots.the_hole_value();
return this->get(Derived::EntryToIndex(entry) + 1);
}
int NameToIndexHashTable::Lookup(Tagged<Name> key) {
DisallowGarbageCollection no_gc;
PtrComprCageBase cage_base = GetPtrComprCageBase(this);
ReadOnlyRoots roots = GetReadOnlyRoots();
InternalIndex entry = this->FindEntry(cage_base, roots, key, key->hash());
if (entry.is_not_found()) return -1;
return Cast<Smi>(this->get(EntryToValueIndex(entry))).value();
}
template <typename Derived, typename Shape>
Tagged<Object> ObjectHashTableBase<Derived, Shape>::Lookup(
DirectHandle<Object> key) {
DisallowGarbageCollection no_gc;
PtrComprCageBase cage_base = GetPtrComprCageBase(this);
ReadOnlyRoots roots = GetReadOnlyRoots();
DCHECK(this->IsKey(roots, *key));
Tagged<Object> hash = Object::GetHash(*key);
if (IsUndefined(hash, roots)) {
return roots.the_hole_value();
}
return Lookup(cage_base, key, Smi::ToInt(hash));
}
template <typename Derived, typename Shape>
Tagged<Object> ObjectHashTableBase<Derived, Shape>::Lookup(
DirectHandle<Object> key, int32_t hash) {
return Lookup(GetPtrComprCageBase(this), key, hash);
}
template <typename Derived, typename Shape>
Tagged<Object> ObjectHashTableBase<Derived, Shape>::ValueAt(
InternalIndex entry) {
return this->get(EntryToValueIndex(entry));
}
Tagged<Object> RegisteredSymbolTable::ValueAt(InternalIndex entry) {
return this->get(EntryToValueIndex(entry));
}
Tagged<Object> NameToIndexHashTable::ValueAt(InternalIndex entry) {
return this->get(EntryToValueIndex(entry));
}
int NameToIndexHashTable::IndexAt(InternalIndex entry) {
Tagged<Object> value = ValueAt(entry);
if (IsSmi(value)) {
int index = Smi::ToInt(value);
DCHECK_LE(0, index);
return index;
}
return -1;
}
template <typename Derived, typename Shape>
Handle<Derived> ObjectHashTableBase<Derived, Shape>::Put(
Isolate* isolate, Handle<Derived> table, DirectHandle<Object> key,
DirectHandle<Object> value) {
DCHECK(table->IsKey(ReadOnlyRoots(isolate), *key));
DCHECK(!IsTheHole(*value, ReadOnlyRoots(isolate)));
int32_t hash = Object::GetOrCreateHash(*key, isolate).value();
return ObjectHashTableBase<Derived, Shape>::Put(isolate, table, key, value,
hash);
}
namespace {
template <typename T, template <typename> typename HandleType>
void RehashObjectHashTableAndGCIfNeeded(Isolate* isolate, HandleType<T> table) {
if ((table->NumberOfDeletedElements() << 1) > table->NumberOfElements()) {
table->Rehash(isolate);
}
if (!table->HasSufficientCapacityToAdd(1)) {
uint32_t nof = table->NumberOfElements() + 1;
uint32_t capacity = T::ComputeCapacity(nof);
if (capacity > T::kMaxCapacity) {
for (size_t i = 0; i < 2; ++i) {
isolate->heap()->CollectAllGarbage(
GCFlag::kNoFlags, GarbageCollectionReason::kFullHashtable);
}
table->Rehash(isolate);
}
}
}
}
template <typename Derived, typename Shape>
Handle<Derived> ObjectHashTableBase<Derived, Shape>::Put(
Isolate* isolate, Handle<Derived> table, DirectHandle<Object> key,
DirectHandle<Object> value, int32_t hash) {
ReadOnlyRoots roots(isolate);
DCHECK(table->IsKey(roots, *key));
DCHECK(!IsTheHole(*value, roots));
InternalIndex entry = table->FindEntry(isolate, roots, key, hash);
if (entry.is_found()) {
table->set(Derived::EntryToValueIndex(entry), *value);
return table;
}
RehashObjectHashTableAndGCIfNeeded(isolate, table);
table = Derived::EnsureCapacity(isolate, table);
table->AddEntry(table->FindInsertionEntry(isolate, hash), *key, *value);
return table;
}
template <typename Derived, typename Shape>
Handle<Derived> ObjectHashTableBase<Derived, Shape>::Remove(
Isolate* isolate, Handle<Derived> table, DirectHandle<Object> key,
bool* was_present) {
DCHECK(table->IsKey(GetReadOnlyRoots(), *key));
Tagged<Object> hash = Object::GetHash(*key);
if (IsUndefined(hash)) {
*was_present = false;
return table;
}
return Remove(isolate, table, key, was_present, Smi::ToInt(hash));
}
template <typename Derived, typename Shape>
Handle<Derived> ObjectHashTableBase<Derived, Shape>::Remove(
Isolate* isolate, Handle<Derived> table, DirectHandle<Object> key,
bool* was_present, int32_t hash) {
ReadOnlyRoots roots = GetReadOnlyRoots();
DCHECK(table->IsKey(roots, *key));
InternalIndex entry = table->FindEntry(isolate, roots, key, hash);
if (entry.is_not_found()) {
*was_present = false;
return table;
}
*was_present = true;
table->RemoveEntry(entry);
return Derived::Shrink(isolate, table);
}
template <typename Derived, typename Shape>
void ObjectHashTableBase<Derived, Shape>::AddEntry(InternalIndex entry,
Tagged<Object> key,
Tagged<Object> value) {
Derived* self = static_cast<Derived*>(this);
self->set_key(Derived::EntryToIndex(entry), key);
self->set(Derived::EntryToValueIndex(entry), value);
self->ElementAdded();
}
template <typename Derived, typename Shape>
void ObjectHashTableBase<Derived, Shape>::RemoveEntry(InternalIndex entry) {
auto roots = GetReadOnlyRoots();
this->set_the_hole(roots, Derived::EntryToIndex(entry));
this->set_the_hole(roots, Derived::EntryToValueIndex(entry));
this->ElementRemoved();
}
template <typename Derived, int N>
std::array<Tagged<Object>, N> ObjectMultiHashTableBase<Derived, N>::Lookup(
DirectHandle<Object> key) {
return Lookup(GetPtrComprCageBase(this), key);
}
template <typename Derived, int N>
std::array<Tagged<Object>, N> ObjectMultiHashTableBase<Derived, N>::Lookup(
PtrComprCageBase cage_base, DirectHandle<Object> key) {
DisallowGarbageCollection no_gc;
ReadOnlyRoots roots = GetReadOnlyRoots();
DCHECK(this->IsKey(roots, *key));
Tagged<Object> hash_obj = Object::GetHash(*key);
if (IsUndefined(hash_obj, roots)) {
return {roots.the_hole_value(), roots.the_hole_value()};
}
int32_t hash = Smi::ToInt(hash_obj);
InternalIndex entry = this->FindEntry(cage_base, roots, key, hash);
if (entry.is_not_found()) {
return {roots.the_hole_value(), roots.the_hole_value()};
}
int start_index = this->EntryToIndex(entry) +
ObjectMultiHashTableShape<N>::kEntryValueIndex;
std::array<Tagged<Object>, N> values;
for (int i = 0; i < N; i++) {
values[i] = this->get(start_index + i);
DCHECK(!IsTheHole(values[i]));
}
return values;
}
template <typename Derived, int N>
Handle<Derived> ObjectMultiHashTableBase<Derived, N>::Put(
Isolate* isolate, Handle<Derived> table, DirectHandle<Object> key,
const std::array<DirectHandle<Object>, N>& values) {
ReadOnlyRoots roots(isolate);
DCHECK(table->IsKey(roots, *key));
int32_t hash = Object::GetOrCreateHash(*key, isolate).value();
InternalIndex entry = table->FindEntry(isolate, roots, key, hash);
if (entry.is_found()) {
table->SetEntryValues(entry, values);
return table;
}
RehashObjectHashTableAndGCIfNeeded(isolate, table);
table = Derived::EnsureCapacity(isolate, table);
entry = table->FindInsertionEntry(isolate, hash);
table->set(Derived::EntryToIndex(entry), *key);
table->SetEntryValues(entry, values);
return table;
}
template <typename Derived, int N>
void ObjectMultiHashTableBase<Derived, N>::SetEntryValues(
InternalIndex entry, const std::array<DirectHandle<Object>, N>& values) {
int start_index = EntryToValueIndexStart(entry);
for (int i = 0; i < N; i++) {
this->set(start_index + i, *values[i]);
}
}
Handle<ObjectHashSet> ObjectHashSet::Add(Isolate* isolate,
Handle<ObjectHashSet> set,
DirectHandle<Object> key) {
int32_t hash = Object::GetOrCreateHash(*key, isolate).value();
if (!set->Has(isolate, key, hash)) {
set = EnsureCapacity(isolate, set);
InternalIndex entry = set->FindInsertionEntry(isolate, hash);
set->set(EntryToIndex(entry), *key);
set->ElementAdded();
}
return set;
}
void JSSet::Initialize(DirectHandle<JSSet> set, Isolate* isolate) {
DirectHandle<OrderedHashSet> table = isolate->factory()->NewOrderedHashSet();
set->set_table(*table);
}
void JSSet::Clear(Isolate* isolate, DirectHandle<JSSet> set) {
Handle<OrderedHashSet> table(Cast<OrderedHashSet>(set->table()), isolate);
table = OrderedHashSet::Clear(isolate, table);
set->set_table(*table);
}
void JSSet::Rehash(Isolate* isolate) {
Handle<OrderedHashSet> table_handle(Cast<OrderedHashSet>(table()), isolate);
DirectHandle<OrderedHashSet> new_table =
OrderedHashSet::Rehash(isolate, table_handle).ToHandleChecked();
set_table(*new_table);
}
void JSMap::Initialize(DirectHandle<JSMap> map, Isolate* isolate) {
DirectHandle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
map->set_table(*table);
}
void JSMap::Clear(Isolate* isolate, DirectHandle<JSMap> map) {
Handle<OrderedHashMap> table(Cast<OrderedHashMap>(map->table()), isolate);
table = OrderedHashMap::Clear(isolate, table);
map->set_table(*table);
}
void JSMap::Rehash(Isolate* isolate) {
Handle<OrderedHashMap> table_handle(Cast<OrderedHashMap>(table()), isolate);
DirectHandle<OrderedHashMap> new_table =
OrderedHashMap::Rehash(isolate, table_handle).ToHandleChecked();
set_table(*new_table);
}
void JSWeakCollection::Initialize(
DirectHandle<JSWeakCollection> weak_collection, Isolate* isolate) {
DirectHandle<EphemeronHashTable> table = EphemeronHashTable::New(isolate, 0);
weak_collection->set_table(*table);
}
void JSWeakCollection::Set(DirectHandle<JSWeakCollection> weak_collection,
DirectHandle<Object> key, DirectHandle<Object> value,
int32_t hash) {
DCHECK(IsJSReceiver(*key) || IsSymbol(*key));
Handle<EphemeronHashTable> table(
Cast<EphemeronHashTable>(weak_collection->table()), Isolate::Current());
DCHECK(table->IsKey(GetReadOnlyRoots(), *key));
DirectHandle<EphemeronHashTable> new_table =
EphemeronHashTable::Put(Isolate::Current(), table, key, value, hash);
weak_collection->set_table(*new_table);
if (*table != *new_table) {
EphemeronHashTable::FillEntriesWithHoles(table);
}
}
bool JSWeakCollection::Delete(DirectHandle<JSWeakCollection> weak_collection,
DirectHandle<Object> key, int32_t hash) {
DCHECK(IsJSReceiver(*key) || IsSymbol(*key));
Handle<EphemeronHashTable> table(
Cast<EphemeronHashTable>(weak_collection->table()), Isolate::Current());
DCHECK(table->IsKey(GetReadOnlyRoots(), *key));
bool was_present = false;
DirectHandle<EphemeronHashTable> new_table = EphemeronHashTable::Remove(
Isolate::Current(), table, key, &was_present, hash);
weak_collection->set_table(*new_table);
if (*table != *new_table) {
EphemeronHashTable::FillEntriesWithHoles(table);
}
return was_present;
}
DirectHandle<JSArray> JSWeakCollection::GetEntries(
DirectHandle<JSWeakCollection> holder, uint32_t max_entries) {
Isolate* isolate = Isolate::Current();
DirectHandle<EphemeronHashTable> table(
Cast<EphemeronHashTable>(holder->table()), isolate);
if (max_entries == 0 || max_entries > table->NumberOfElements()) {
max_entries = table->NumberOfElements();
}
int values_per_entry = IsJSWeakMap(*holder) ? 2 : 1;
DirectHandle<FixedArray> entries =
isolate->factory()->NewFixedArray(max_entries * values_per_entry);
if (max_entries > table->NumberOfElements()) {
max_entries = table->NumberOfElements();
}
{
DisallowGarbageCollection no_gc;
ReadOnlyRoots roots = ReadOnlyRoots(isolate);
uint32_t count = 0;
for (uint32_t i = 0;
count / values_per_entry < max_entries && i < table->Capacity(); i++) {
Tagged<Object> key;
if (table->ToKey(roots, InternalIndex(i), &key)) {
entries->set(count++, key);
if (values_per_entry > 1) {
Tagged<Object> value = table->Lookup(direct_handle(key, isolate));
entries->set(count++, value);
}
}
}
DCHECK_EQ(max_entries * values_per_entry, count);
}
return isolate->factory()->NewJSArrayWithElements(entries);
}
void JSDisposableStackBase::InitializeJSDisposableStackBase(
Isolate* isolate, DirectHandle<JSDisposableStackBase> disposable_stack) {
DirectHandle<FixedArray> array = isolate->factory()->NewFixedArray(0);
disposable_stack->set_stack(*array);
disposable_stack->set_needs_await(false);
disposable_stack->set_has_awaited(false);
disposable_stack->set_suppressed_error_created(false);
disposable_stack->set_length(0);
disposable_stack->set_state(DisposableStackState::kPending);
disposable_stack->set_error(*(isolate->factory()->uninitialized_value()));
disposable_stack->set_error_message(
*(isolate->factory()->uninitialized_value()));
}
void PropertyCell::ClearAndInvalidate(Isolate* isolate) {
DCHECK(!IsPropertyCellHole(value(), isolate));
PropertyDetails details = property_details();
details = details.set_cell_type(PropertyCellType::kConstant);
Transition(details, isolate->factory()->property_cell_hole_value());
DependentCode::DeoptimizeDependencyGroups(
isolate, *this, DependentCode::kPropertyCellChangedGroup);
}
Handle<PropertyCell> PropertyCell::InvalidateAndReplaceEntry(
Isolate* isolate, DirectHandle<GlobalDictionary> dictionary,
InternalIndex entry, PropertyDetails new_details,
DirectHandle<Object> new_value) {
DirectHandle<PropertyCell> cell(dictionary->CellAt(entry), isolate);
DirectHandle<Name> name(cell->name(), isolate);
DCHECK(cell->property_details().IsConfigurable());
DCHECK(!IsAnyHole(cell->value()));
Handle<PropertyCell> new_cell =
isolate->factory()->NewPropertyCell(name, new_details, new_value);
dictionary->ValueAtPut(entry, *new_cell);
cell->ClearAndInvalidate(isolate);
return new_cell;
}
static bool RemainsConstantType(Tagged<PropertyCell> cell,
Tagged<Object> value) {
DisallowGarbageCollection no_gc;
if (IsSmi(cell->value()) && IsSmi(value)) {
return true;
} else if (IsHeapObject(cell->value()) && IsHeapObject(value)) {
Tagged<Map> map = Cast<HeapObject>(value)->map();
return Cast<HeapObject>(cell->value())->map() == map && map->is_stable();
}
return false;
}
PropertyCellType PropertyCell::InitialType(Isolate* isolate,
Tagged<Object> value) {
return IsUndefined(value, isolate) ? PropertyCellType::kUndefined
: PropertyCellType::kConstant;
}
PropertyCellType PropertyCell::UpdatedType(Isolate* isolate,
Tagged<PropertyCell> cell,
Tagged<Object> value,
PropertyDetails details) {
DisallowGarbageCollection no_gc;
DCHECK(!IsAnyHole(value));
DCHECK(!IsAnyHole(cell->value()));
switch (details.cell_type()) {
case PropertyCellType::kUndefined:
return PropertyCellType::kConstant;
case PropertyCellType::kConstant:
if (value == cell->value()) return PropertyCellType::kConstant;
[[fallthrough]];
case PropertyCellType::kConstantType:
if (RemainsConstantType(cell, value)) {
return PropertyCellType::kConstantType;
}
[[fallthrough]];
case PropertyCellType::kMutable:
return PropertyCellType::kMutable;
case PropertyCellType::kInTransition:
UNREACHABLE();
}
UNREACHABLE();
}
Handle<PropertyCell> PropertyCell::PrepareForAndSetValue(
Isolate* isolate, DirectHandle<GlobalDictionary> dictionary,
InternalIndex entry, DirectHandle<Object> value, PropertyDetails details) {
DCHECK(!IsAnyHole(*value));
Tagged<PropertyCell> raw_cell = dictionary->CellAt(entry);
CHECK(!IsAnyHole(raw_cell->value()));
const PropertyDetails original_details = raw_cell->property_details();
bool invalidate = original_details.kind() == PropertyKind::kData &&
details.kind() == PropertyKind::kAccessor;
int index = original_details.dictionary_index();
DCHECK_LT(0, index);
details = details.set_index(index);
PropertyCellType new_type =
UpdatedType(isolate, raw_cell, *value, original_details);
details = details.set_cell_type(new_type);
Handle<PropertyCell> cell(raw_cell, isolate);
if (invalidate) {
cell = PropertyCell::InvalidateAndReplaceEntry(isolate, dictionary, entry,
details, value);
} else {
cell->Transition(details, value);
if (original_details.cell_type() != new_type ||
(!original_details.IsReadOnly() && details.IsReadOnly())) {
DependentCode::DeoptimizeDependencyGroups(
isolate, *cell, DependentCode::kPropertyCellChangedGroup);
}
}
return cell;
}
void PropertyCell::InvalidateProtector(Isolate* isolate) {
if (value() != Smi::FromInt(Protectors::kProtectorInvalid)) {
DCHECK_EQ(value(), Smi::FromInt(Protectors::kProtectorValid));
set_value(Smi::FromInt(Protectors::kProtectorInvalid), kReleaseStore,
SKIP_WRITE_BARRIER);
DependentCode::DeoptimizeDependencyGroups(
isolate, *this, DependentCode::kPropertyCellChangedGroup);
}
}
bool PropertyCell::CheckDataIsCompatible(PropertyDetails details,
Tagged<Object> value) {
DisallowGarbageCollection no_gc;
PropertyCellType cell_type = details.cell_type();
CHECK_NE(cell_type, PropertyCellType::kInTransition);
if (IsPropertyCellHole(value)) {
CHECK_EQ(cell_type, PropertyCellType::kConstant);
} else {
CHECK_EQ(IsAccessorInfo(value) || IsAccessorPair(value),
details.kind() == PropertyKind::kAccessor);
DCHECK_IMPLIES(cell_type == PropertyCellType::kUndefined,
IsUndefined(value));
}
return true;
}
#ifdef DEBUG
bool PropertyCell::CanTransitionTo(PropertyDetails new_details,
Tagged<Object> new_value) const {
DisallowGarbageCollection no_gc;
DCHECK(CheckDataIsCompatible(new_details, new_value));
switch (property_details().cell_type()) {
case PropertyCellType::kUndefined:
return new_details.cell_type() != PropertyCellType::kUndefined;
case PropertyCellType::kConstant:
return !IsPropertyCellHole(value()) &&
new_details.cell_type() != PropertyCellType::kUndefined;
case PropertyCellType::kConstantType:
return new_details.cell_type() == PropertyCellType::kConstantType ||
new_details.cell_type() == PropertyCellType::kMutable ||
(new_details.cell_type() == PropertyCellType::kConstant &&
IsPropertyCellHole(new_value));
case PropertyCellType::kMutable:
return new_details.cell_type() == PropertyCellType::kMutable ||
(new_details.cell_type() == PropertyCellType::kConstant &&
IsPropertyCellHole(new_value));
case PropertyCellType::kInTransition:
UNREACHABLE();
}
UNREACHABLE();
}
#endif
int JSGeneratorObject::code_offset() const {
DCHECK(IsSmi(input_or_debug_pos()));
int code_offset = Smi::ToInt(input_or_debug_pos());
code_offset -= BytecodeArray::kHeaderSize - kHeapObjectTag;
return code_offset;
}
int JSGeneratorObject::source_position() const {
CHECK(is_suspended());
DCHECK(function()->shared()->HasBytecodeArray());
Isolate* isolate = Isolate::Current();
DCHECK(function()
->shared()
->GetBytecodeArray(isolate)
->HasSourcePositionTable());
Tagged<BytecodeArray> bytecode =
function()->shared()->GetBytecodeArray(isolate);
return bytecode->SourcePosition(code_offset());
}
Tagged<AccessCheckInfo> AccessCheckInfo::Get(Isolate* isolate,
DirectHandle<JSObject> receiver) {
DisallowGarbageCollection no_gc;
DCHECK(receiver->map()->is_access_check_needed());
Tagged<Object> maybe_constructor = receiver->map()->GetConstructor();
if (IsFunctionTemplateInfo(maybe_constructor)) {
Tagged<Object> data_obj =
Cast<FunctionTemplateInfo>(maybe_constructor)->GetAccessCheckInfo();
if (IsUndefined(data_obj, isolate)) return {};
return Cast<AccessCheckInfo>(data_obj);
}
if (!IsJSFunction(maybe_constructor)) return {};
Tagged<JSFunction> constructor = Cast<JSFunction>(maybe_constructor);
if (!constructor->shared()->IsApiFunction()) return {};
Tagged<Object> data_obj =
constructor->shared()->api_func_data()->GetAccessCheckInfo();
if (IsUndefined(data_obj, isolate)) return {};
return Cast<AccessCheckInfo>(data_obj);
}
Address Smi::LexicographicCompare(Isolate* isolate, Tagged<Smi> x,
Tagged<Smi> y) {
DisallowGarbageCollection no_gc;
DisallowJavascriptExecution no_js(isolate);
int x_value = Smi::ToInt(x);
int y_value = Smi::ToInt(y);
if (x_value == y_value) return Smi::FromInt(0).ptr();
if (x_value == 0 || y_value == 0) {
return Smi::FromInt(x_value < y_value ? -1 : 1).ptr();
}
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();
}
static const uint32_t kPowersOf10[] = {
1, 10, 100, 1000,
10 * 1000, 100 * 1000, 1000 * 1000, 10 * 1000 * 1000,
100 * 1000 * 1000, 1000 * 1000 * 1000};
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_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();
}
bool MapWord::IsMapOrForwarded(Tagged<Map> map) {
MapWord map_word = map->map_word(kRelaxedLoad);
if (map_word.IsForwardingAddress()) {
return true;
}
return InstanceTypeChecker::IsMap(map_word.ToMap()->instance_type());
}
#define EXTERN_DEFINE_HASH_TABLE(DERIVED, SHAPE) \
template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) \
HashTable<DERIVED, SHAPE>; \
\
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) MaybeHandle<DERIVED> \
HashTable<DERIVED, SHAPE>::TryNew(Isolate*, uint32_t, AllocationType, \
MinimumCapacity); \
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) MaybeHandle<DERIVED> \
HashTable<DERIVED, SHAPE>::TryNew(LocalIsolate*, uint32_t, AllocationType, \
MinimumCapacity); \
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
HashTable<DERIVED, SHAPE>::New(Isolate*, uint32_t, AllocationType, \
MinimumCapacity); \
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
HashTable<DERIVED, SHAPE>::New(LocalIsolate*, uint32_t, AllocationType, \
MinimumCapacity); \
\
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
HashTable<DERIVED, SHAPE>::EnsureCapacity(Isolate*, Handle<DERIVED>, int, \
AllocationType); \
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
HashTable<DERIVED, SHAPE>::EnsureCapacity(LocalIsolate*, Handle<DERIVED>, \
int, AllocationType); \
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) DirectHandle<DERIVED> \
HashTable<DERIVED, SHAPE>::EnsureCapacity(Isolate*, DirectHandle<DERIVED>, \
int, AllocationType); \
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) DirectHandle<DERIVED> \
HashTable<DERIVED, SHAPE>::EnsureCapacity( \
LocalIsolate*, DirectHandle<DERIVED>, int, AllocationType); \
\
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
HashTable<DERIVED, SHAPE>::Shrink(Isolate*, Handle<DERIVED>, int); \
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) DirectHandle<DERIVED> \
HashTable<DERIVED, SHAPE>::Shrink(Isolate*, DirectHandle<DERIVED>, int);
#define EXTERN_DEFINE_OBJECT_BASE_HASH_TABLE(DERIVED, SHAPE) \
EXTERN_DEFINE_HASH_TABLE(DERIVED, SHAPE) \
template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) \
ObjectHashTableBase<DERIVED, SHAPE>;
#define EXTERN_DEFINE_MULTI_OBJECT_BASE_HASH_TABLE(DERIVED, N) \
EXTERN_DEFINE_HASH_TABLE(DERIVED, ObjectMultiHashTableShape<N>) \
template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) \
ObjectMultiHashTableBase<DERIVED, N>;
#define EXTERN_DEFINE_DICTIONARY(DERIVED, SHAPE) \
EXTERN_DEFINE_HASH_TABLE(DERIVED, SHAPE) \
template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) \
Dictionary<DERIVED, SHAPE>; \
\
template V8_EXPORT_PRIVATE DirectHandle<DERIVED> \
Dictionary<DERIVED, SHAPE>::Add(Isolate* isolate, DirectHandle<DERIVED>, \
Key, DirectHandle<Object>, PropertyDetails, \
InternalIndex*); \
template V8_EXPORT_PRIVATE DirectHandle<DERIVED> \
Dictionary<DERIVED, SHAPE>::Add( \
LocalIsolate* isolate, DirectHandle<DERIVED>, Key, DirectHandle<Object>, \
PropertyDetails, InternalIndex*); \
template V8_EXPORT_PRIVATE IndirectHandle<DERIVED> \
Dictionary<DERIVED, SHAPE>::Add(Isolate* isolate, IndirectHandle<DERIVED>, \
Key, DirectHandle<Object>, PropertyDetails, \
InternalIndex*); \
template V8_EXPORT_PRIVATE IndirectHandle<DERIVED> \
Dictionary<DERIVED, SHAPE>::Add( \
LocalIsolate* isolate, IndirectHandle<DERIVED>, Key, \
DirectHandle<Object>, PropertyDetails, InternalIndex*); \
template V8_EXPORT_PRIVATE DirectHandle<DERIVED> \
Dictionary<DERIVED, SHAPE>::DeleteEntry( \
Isolate* isolate, DirectHandle<DERIVED>, InternalIndex); \
template V8_EXPORT_PRIVATE IndirectHandle<DERIVED> \
Dictionary<DERIVED, SHAPE>::DeleteEntry( \
Isolate* isolate, IndirectHandle<DERIVED>, InternalIndex); \
template V8_EXPORT_PRIVATE DirectHandle<DERIVED> \
Dictionary<DERIVED, SHAPE>::Shrink(Isolate* isolate, DirectHandle<DERIVED>); \
template V8_EXPORT_PRIVATE IndirectHandle<DERIVED> \
Dictionary<DERIVED, SHAPE>::Shrink(Isolate* isolate, \
IndirectHandle<DERIVED>);
#define EXTERN_DEFINE_BASE_NAME_DICTIONARY(DERIVED, SHAPE) \
EXTERN_DEFINE_DICTIONARY(DERIVED, SHAPE) \
template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) \
BaseNameDictionary<DERIVED, SHAPE>; \
\
template V8_EXPORT_PRIVATE Handle<DERIVED> \
BaseNameDictionary<DERIVED, SHAPE>::New(Isolate*, int, AllocationType, \
MinimumCapacity); \
template V8_EXPORT_PRIVATE Handle<DERIVED> \
BaseNameDictionary<DERIVED, SHAPE>::New(LocalIsolate*, int, AllocationType, \
MinimumCapacity); \
\
template V8_EXPORT_PRIVATE MaybeDirectHandle<DERIVED> \
BaseNameDictionary<DERIVED, SHAPE>::Add( \
Isolate* isolate, DirectHandle<DERIVED>, Key, DirectHandle<Object>, \
PropertyDetails, InternalIndex*); \
template V8_EXPORT_PRIVATE MaybeIndirectHandle<DERIVED> \
BaseNameDictionary<DERIVED, SHAPE>::Add( \
Isolate* isolate, IndirectHandle<DERIVED>, Key, DirectHandle<Object>, \
PropertyDetails, InternalIndex*); \
\
template DirectHandle<DERIVED> \
BaseNameDictionary<DERIVED, SHAPE>::AddNoUpdateNextEnumerationIndex( \
Isolate* isolate, DirectHandle<DERIVED>, Key, DirectHandle<Object>, \
PropertyDetails, InternalIndex*); \
template DirectHandle<DERIVED> \
BaseNameDictionary<DERIVED, SHAPE>::AddNoUpdateNextEnumerationIndex( \
LocalIsolate* isolate, DirectHandle<DERIVED>, Key, DirectHandle<Object>, \
PropertyDetails, InternalIndex*); \
template IndirectHandle<DERIVED> \
BaseNameDictionary<DERIVED, SHAPE>::AddNoUpdateNextEnumerationIndex( \
Isolate* isolate, IndirectHandle<DERIVED>, Key, DirectHandle<Object>, \
PropertyDetails, InternalIndex*); \
template IndirectHandle<DERIVED> \
BaseNameDictionary<DERIVED, SHAPE>::AddNoUpdateNextEnumerationIndex( \
LocalIsolate* isolate, IndirectHandle<DERIVED>, Key, \
DirectHandle<Object>, PropertyDetails, InternalIndex*);
EXTERN_DEFINE_HASH_TABLE(StringSet, StringSetShape)
EXTERN_DEFINE_HASH_TABLE(CompilationCacheTable, CompilationCacheShape)
EXTERN_DEFINE_HASH_TABLE(ObjectHashSet, ObjectHashSetShape)
EXTERN_DEFINE_HASH_TABLE(NameToIndexHashTable, NameToIndexShape)
EXTERN_DEFINE_HASH_TABLE(RegisteredSymbolTable, RegisteredSymbolTableShape)
EXTERN_DEFINE_OBJECT_BASE_HASH_TABLE(ObjectHashTable, ObjectHashTableShape)
EXTERN_DEFINE_OBJECT_BASE_HASH_TABLE(EphemeronHashTable,
EphemeronHashTableShape)
EXTERN_DEFINE_MULTI_OBJECT_BASE_HASH_TABLE(ObjectTwoHashTable, 2)
EXTERN_DEFINE_DICTIONARY(SimpleNumberDictionary, SimpleNumberDictionaryShape)
EXTERN_DEFINE_DICTIONARY(NumberDictionary, NumberDictionaryShape)
EXTERN_DEFINE_DICTIONARY(SimpleNameDictionary, SimpleNameDictionaryShape)
template V8_EXPORT_PRIVATE void
Dictionary<NumberDictionary, NumberDictionaryShape>::UncheckedAdd<
Isolate, Handle, AllocationType::kSharedOld>(Isolate*,
Handle<NumberDictionary>,
uint32_t, DirectHandle<Object>,
PropertyDetails);
template V8_EXPORT_PRIVATE void
Dictionary<NumberDictionary, NumberDictionaryShape>::UncheckedAdd<
Isolate, DirectHandle, AllocationType::kSharedOld>(
Isolate*, DirectHandle<NumberDictionary>, uint32_t, DirectHandle<Object>,
PropertyDetails);
EXTERN_DEFINE_BASE_NAME_DICTIONARY(NameDictionary, NameDictionaryShape)
template V8_EXPORT_PRIVATE Handle<NameDictionary> NameDictionary::New(
Isolate*, int, AllocationType, MinimumCapacity);
template V8_EXPORT_PRIVATE Handle<NameDictionary> NameDictionary::New(
LocalIsolate*, int, AllocationType, MinimumCapacity);
EXTERN_DEFINE_BASE_NAME_DICTIONARY(GlobalDictionary, GlobalDictionaryShape)
#undef EXTERN_DEFINE_HASH_TABLE
#undef EXTERN_DEFINE_OBJECT_BASE_HASH_TABLE
#undef EXTERN_DEFINE_DICTIONARY
#undef EXTERN_DEFINE_BASE_NAME_DICTIONARY
}