blob: 6c42528d0d82178d1b13198202d7cb25a2f1a1a2 [file] [log] [blame]
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/objects/objects.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <sstream>
#include <vector>
#include "src/api/api-arguments-inl.h"
#include "src/api/api-natives.h"
#include "src/api/api.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/base/bits.h"
#include "src/base/debug/stack_trace.h"
#include "src/base/logging.h"
#include "src/base/overflowing-math.h"
#include "src/base/utils/random-number-generator.h"
#include "src/builtins/accessors.h"
#include "src/builtins/builtins.h"
#include "src/codegen/compiler.h"
#include "src/common/globals.h"
#include "src/common/message-template.h"
#include "src/date/date.h"
#include "src/debug/debug.h"
#include "src/diagnostics/code-tracer.h"
#include "src/execution/arguments.h"
#include "src/execution/execution.h"
#include "src/execution/frames-inl.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/isolate-utils-inl.h"
#include "src/execution/isolate-utils.h"
#include "src/execution/microtask-queue.h"
#include "src/execution/protectors-inl.h"
#include "src/heap/factory-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/heap/read-only-heap.h"
#include "src/ic/ic.h"
#include "src/init/bootstrapper.h"
#include "src/logging/counters.h"
#include "src/logging/log.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/allocation-site-inl.h"
#include "src/objects/allocation-site-scopes.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/arguments-inl.h"
#include "src/objects/bigint.h"
#include "src/objects/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/foreign.h"
#include "src/objects/free-space-inl.h"
#include "src/objects/function-kind.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/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/keys.h"
#include "src/objects/lookup-inl.h"
#include "src/objects/map-updater.h"
#include "src/objects/objects-body-descriptors-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/property-details.h"
#include "src/roots/roots.h"
#include "src/snapshot/deserializer.h"
#include "src/utils/identity-map.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-break-iterator.h"
#include "src/objects/js-collator.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-collection-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-date-time-format.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-generator-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-list-format.h"
#include "src/objects/js-locale.h"
#include "src/objects/js-number-format.h"
#include "src/objects/js-plural-rules.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-regexp-inl.h"
#include "src/objects/js-regexp-string-iterator.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-relative-time-format.h"
#include "src/objects/js-segment-iterator.h"
#include "src/objects/js-segmenter.h"
#include "src/objects/js-segments.h"
#endif // V8_INTL_SUPPORT
#include "src/codegen/source-position-table.h"
#include "src/objects/js-weak-refs-inl.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/map-inl.h"
#include "src/objects/map.h"
#include "src/objects/megadom-handler-inl.h"
#include "src/objects/microtask-inl.h"
#include "src/objects/module-inl.h"
#include "src/objects/promise-inl.h"
#include "src/objects/property-descriptor-object-inl.h"
#include "src/objects/property-descriptor.h"
#include "src/objects/prototype.h"
#include "src/objects/slots-atomic-inl.h"
#include "src/objects/string-comparator.h"
#include "src/objects/string-set-inl.h"
#include "src/objects/struct-inl.h"
#include "src/objects/template-objects-inl.h"
#include "src/objects/transitions-inl.h"
#include "src/parsing/preparse-data.h"
#include "src/regexp/regexp.h"
#include "src/strings/string-builder-inl.h"
#include "src/strings/string-search.h"
#include "src/strings/string-stream.h"
#include "src/strings/unicode-decoder.h"
#include "src/strings/unicode-inl.h"
#include "src/utils/hex-format.h"
#include "src/utils/ostreams.h"
#include "src/utils/sha-256.h"
#include "src/utils/utils-inl.h"
#include "src/zone/zone.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-objects.h"
#endif // V8_ENABLE_WEBASSEMBLY
namespace v8 {
namespace internal {
ShouldThrow GetShouldThrow(Isolate* isolate, Maybe<ShouldThrow> should_throw) {
if (should_throw.IsJust()) return should_throw.FromJust();
LanguageMode mode = isolate->context()->scope_info()->language_mode();
if (mode == LanguageMode::kStrict) return kThrowOnError;
for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
if (!it.frame()->is_java_script()) continue;
// Get the language mode from closure.
JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(it.frame());
std::vector<Tagged<SharedFunctionInfo>> functions;
js_frame->GetFunctions(&functions);
LanguageMode closure_language_mode = functions.back()->language_mode();
if (closure_language_mode > mode) {
mode = closure_language_mode;
}
break;
}
return is_sloppy(mode) ? kDontThrow : kThrowOnError;
}
bool ComparisonResultToBool(Operation op, ComparisonResult result) {
switch (op) {
case Operation::kLessThan:
return result == ComparisonResult::kLessThan;
case Operation::kLessThanOrEqual:
return result == ComparisonResult::kLessThan ||
result == ComparisonResult::kEqual;
case Operation::kGreaterThan:
return result == ComparisonResult::kGreaterThan;
case Operation::kGreaterThanOrEqual:
return result == ComparisonResult::kGreaterThan ||
result == ComparisonResult::kEqual;
default:
break;
}
UNREACHABLE();
}
std::ostream& operator<<(std::ostream& os, InstanceType instance_type) {
if (InstanceTypeChecker::IsJSApiObject(instance_type)) {
return os << "[api object] "
<< static_cast<int16_t>(instance_type) -
i::Internals::kFirstJSApiObjectType;
}
switch (instance_type) {
#define WRITE_TYPE(TYPE) \
case TYPE: \
return os << #TYPE;
INSTANCE_TYPE_LIST(WRITE_TYPE)
#undef WRITE_TYPE
}
return os << "[unknown instance type " << static_cast<int16_t>(instance_type)
<< "]";
}
std::ostream& operator<<(std::ostream& os, PropertyCellType type) {
switch (type) {
case PropertyCellType::kUndefined:
return os << "Undefined";
case PropertyCellType::kConstant:
return os << "Constant";
case PropertyCellType::kConstantType:
return os << "ConstantType";
case PropertyCellType::kMutable:
return os << "Mutable";
case PropertyCellType::kInTransition:
return os << "InTransition";
}
UNREACHABLE();
}
// static
Handle<FieldType> Object::OptimalType(Tagged<Object> obj, Isolate* isolate,
Representation representation) {
if (representation.IsNone()) return FieldType::None(isolate);
if (v8_flags.track_field_types) {
if (representation.IsHeapObject() && IsHeapObject(obj)) {
// We can track only JavaScript objects with stable maps.
Handle<Map> map(HeapObject::cast(obj)->map(), isolate);
if (map->is_stable() && IsJSReceiverMap(*map)) {
return FieldType::Class(map, isolate);
}
}
}
return FieldType::Any(isolate);
}
Handle<Object> Object::NewStorageFor(Isolate* isolate, Handle<Object> object,
Representation representation) {
if (!representation.IsDouble()) return object;
Handle<HeapNumber> result = isolate->factory()->NewHeapNumberWithHoleNaN();
if (IsUninitialized(*object, isolate)) {
result->set_value_as_bits(kHoleNanInt64);
} else if (IsHeapNumber(*object)) {
// Ensure that all bits of the double value are preserved.
result->set_value_as_bits(HeapNumber::cast(*object)->value_as_bits());
} else {
result->set_value(Object::Number(*object));
}
return result;
}
template <AllocationType allocation_type, typename IsolateT>
Handle<Object> Object::WrapForRead(IsolateT* isolate, Handle<Object> object,
Representation representation) {
DCHECK(!IsUninitialized(*object, isolate));
if (!representation.IsDouble()) {
DCHECK(Object::FitsRepresentation(*object, representation));
return object;
}
return isolate->factory()->template NewHeapNumberFromBits<allocation_type>(
HeapNumber::cast(*object)->value_as_bits());
}
template Handle<Object> Object::WrapForRead<AllocationType::kYoung>(
Isolate* isolate, Handle<Object> object, Representation representation);
template Handle<Object> Object::WrapForRead<AllocationType::kOld>(
LocalIsolate* isolate, Handle<Object> object,
Representation representation);
MaybeHandle<JSReceiver> Object::ToObjectImpl(Isolate* isolate,
Handle<Object> object,
const char* method_name) {
DCHECK(!IsJSReceiver(*object)); // Use ToObject() for fast path.
Handle<Context> native_context = isolate->native_context();
Handle<JSFunction> constructor;
if (IsSmi(*object)) {
constructor = handle(native_context->number_function(), isolate);
} else {
int constructor_function_index =
Handle<HeapObject>::cast(object)->map()->GetConstructorFunctionIndex();
if (constructor_function_index == Map::kNoConstructorFunctionIndex) {
if (method_name != nullptr) {
THROW_NEW_ERROR(
isolate,
NewTypeError(
MessageTemplate::kCalledOnNullOrUndefined,
isolate->factory()->NewStringFromAsciiChecked(method_name)),
JSReceiver);
}
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kUndefinedOrNullToObject),
JSReceiver);
}
constructor = handle(
JSFunction::cast(native_context->get(constructor_function_index)),
isolate);
}
Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
Handle<JSPrimitiveWrapper>::cast(result)->set_value(*object);
return result;
}
// ES6 section 9.2.1.2, OrdinaryCallBindThis for sloppy callee.
// static
MaybeHandle<JSReceiver> Object::ConvertReceiver(Isolate* isolate,
Handle<Object> object) {
if (IsJSReceiver(*object)) return Handle<JSReceiver>::cast(object);
if (IsNullOrUndefined(*object, isolate)) {
return isolate->global_proxy();
}
return Object::ToObject(isolate, object);
}
// static
MaybeHandle<Object> Object::ConvertToNumberOrNumeric(Isolate* isolate,
Handle<Object> input,
Conversion mode) {
while (true) {
if (IsNumber(*input)) {
return input;
}
if (IsString(*input)) {
return String::ToNumber(isolate, Handle<String>::cast(input));
}
if (IsOddball(*input)) {
return Oddball::ToNumber(isolate, Handle<Oddball>::cast(input));
}
if (IsSymbol(*input)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber),
Object);
}
if (IsBigInt(*input)) {
if (mode == Conversion::kToNumeric) return input;
DCHECK_EQ(mode, Conversion::kToNumber);
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kBigIntToNumber),
Object);
}
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(input),
ToPrimitiveHint::kNumber),
Object);
}
}
// static
MaybeHandle<Object> Object::ConvertToInteger(Isolate* isolate,
Handle<Object> input) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object);
if (IsSmi(*input)) return input;
return isolate->factory()->NewNumber(DoubleToInteger(Object::Number(*input)));
}
// static
MaybeHandle<Object> Object::ConvertToInt32(Isolate* isolate,
Handle<Object> input) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object);
if (IsSmi(*input)) return input;
return isolate->factory()->NewNumberFromInt(
DoubleToInt32(Object::Number(*input)));
}
// static
MaybeHandle<Object> Object::ConvertToUint32(Isolate* isolate,
Handle<Object> input) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object);
if (IsSmi(*input))
return handle(Smi::ToUint32Smi(Smi::cast(*input)), isolate);
return isolate->factory()->NewNumberFromUint(
DoubleToUint32(Object::Number(*input)));
}
// static
MaybeHandle<Name> Object::ConvertToName(Isolate* isolate,
Handle<Object> input) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
Object::ToPrimitive(isolate, input, ToPrimitiveHint::kString), Name);
if (IsName(*input)) return Handle<Name>::cast(input);
return ToString(isolate, input);
}
// ES6 7.1.14
// static
MaybeHandle<Object> Object::ConvertToPropertyKey(Isolate* isolate,
Handle<Object> value) {
// 1. Let key be ToPrimitive(argument, hint String).
MaybeHandle<Object> maybe_key =
Object::ToPrimitive(isolate, value, ToPrimitiveHint::kString);
// 2. ReturnIfAbrupt(key).
Handle<Object> key;
if (!maybe_key.ToHandle(&key)) return key;
// 3. If Type(key) is Symbol, then return key.
if (IsSymbol(*key)) return key;
// 4. Return ToString(key).
// Extending spec'ed behavior, we'd be happy to return an element index.
if (IsSmi(*key)) return key;
if (IsHeapNumber(*key)) {
uint32_t uint_value;
if (Object::ToArrayLength(*value, &uint_value) &&
uint_value <= static_cast<uint32_t>(Smi::kMaxValue)) {
return handle(Smi::FromInt(static_cast<int>(uint_value)), isolate);
}
}
return Object::ToString(isolate, key);
}
// static
MaybeHandle<String> Object::ConvertToString(Isolate* isolate,
Handle<Object> input) {
while (true) {
if (IsOddball(*input)) {
return handle(Handle<Oddball>::cast(input)->to_string(), isolate);
}
if (IsNumber(*input)) {
return isolate->factory()->NumberToString(input);
}
if (IsSymbol(*input)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToString),
String);
}
if (IsBigInt(*input)) {
return BigInt::ToString(isolate, Handle<BigInt>::cast(input));
}
#if V8_ENABLE_WEBASSEMBLY
// We generally don't let the WasmNull escape into the JavaScript world,
// but some builtins may encounter it when called directly from Wasm code.
if (IsWasmNull(*input)) {
return isolate->factory()->null_string();
}
#endif
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(input),
ToPrimitiveHint::kString),
String);
// The previous isString() check happened in Object::ToString and thus we
// put it at the end of the loop in this helper.
if (IsString(*input)) {
return Handle<String>::cast(input);
}
}
}
namespace {
bool IsErrorObject(Isolate* isolate, Handle<Object> object) {
if (!IsJSObject(*object)) return false;
return ErrorUtils::HasErrorStackSymbolOwnProperty(
isolate, Handle<JSObject>::cast(object));
}
Handle<String> AsStringOrEmpty(Isolate* isolate, Handle<Object> object) {
return IsString(*object) ? Handle<String>::cast(object)
: isolate->factory()->empty_string();
}
Handle<String> NoSideEffectsErrorToString(Isolate* isolate,
Handle<JSReceiver> error) {
Handle<Name> name_key = isolate->factory()->name_string();
Handle<Object> name = JSReceiver::GetDataProperty(isolate, error, name_key);
Handle<String> name_str = AsStringOrEmpty(isolate, name);
Handle<Name> msg_key = isolate->factory()->message_string();
Handle<Object> msg = JSReceiver::GetDataProperty(isolate, error, msg_key);
Handle<String> msg_str = AsStringOrEmpty(isolate, msg);
if (name_str->length() == 0) return msg_str;
if (msg_str->length() == 0) return name_str;
IncrementalStringBuilder builder(isolate);
builder.AppendString(name_str);
builder.AppendCStringLiteral(": ");
if (builder.Length() + msg_str->length() <= String::kMaxLength) {
builder.AppendString(msg_str);
} else {
builder.AppendCStringLiteral("<a very large string>");
}
return builder.Finish().ToHandleChecked();
}
} // namespace
// static
MaybeHandle<String> Object::NoSideEffectsToMaybeString(Isolate* isolate,
Handle<Object> input) {
DisallowJavascriptExecution no_js(isolate);
if (IsString(*input) || IsNumber(*input) || IsOddball(*input)) {
return Object::ToString(isolate, input).ToHandleChecked();
} else if (IsJSProxy(*input)) {
Handle<Object> currInput = input;
do {
Tagged<HeapObject> target =
Handle<JSProxy>::cast(currInput)->target(isolate);
currInput = Handle<Object>(target, isolate);
} while (IsJSProxy(*currInput));
return NoSideEffectsToString(isolate, currInput);
} else if (IsBigInt(*input)) {
return BigInt::NoSideEffectsToString(isolate, Handle<BigInt>::cast(input));
} else if (IsJSFunctionOrBoundFunctionOrWrappedFunction(*input)) {
// -- F u n c t i o n
Handle<String> fun_str;
if (IsJSBoundFunction(*input)) {
fun_str = JSBoundFunction::ToString(Handle<JSBoundFunction>::cast(input));
} else if (IsJSWrappedFunction(*input)) {
fun_str =
JSWrappedFunction::ToString(Handle<JSWrappedFunction>::cast(input));
} else {
DCHECK(IsJSFunction(*input));
fun_str = JSFunction::ToString(Handle<JSFunction>::cast(input));
}
if (fun_str->length() > 128) {
IncrementalStringBuilder builder(isolate);
builder.AppendString(isolate->factory()->NewSubString(fun_str, 0, 111));
builder.AppendCStringLiteral("...<omitted>...");
builder.AppendString(isolate->factory()->NewSubString(
fun_str, fun_str->length() - 2, fun_str->length()));
return builder.Finish().ToHandleChecked();
}
return fun_str;
} else if (IsSymbol(*input)) {
// -- S y m b o l
Handle<Symbol> symbol = Handle<Symbol>::cast(input);
if (symbol->is_private_name()) {
return Handle<String>(String::cast(symbol->description()), isolate);
}
IncrementalStringBuilder builder(isolate);
builder.AppendCStringLiteral("Symbol(");
if (IsString(symbol->description())) {
Handle<String> description =
handle(String::cast(symbol->description()), isolate);
if (description->length() > 128) {
builder.AppendString(
isolate->factory()->NewSubString(description, 0, 56));
builder.AppendCStringLiteral("...<omitted>...");
builder.AppendString(isolate->factory()->NewSubString(
description, description->length() - 56, description->length()));
} else {
builder.AppendString(description);
}
}
builder.AppendCharacter(')');
return builder.Finish().ToHandleChecked();
} else if (IsJSReceiver(*input)) {
// -- J S R e c e i v e r
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(input);
Handle<Object> to_string = JSReceiver::GetDataProperty(
isolate, receiver, isolate->factory()->toString_string());
if (IsErrorObject(isolate, input) ||
*to_string == *isolate->error_to_string()) {
// When internally formatting error objects, use a side-effects-free
// version of Error.prototype.toString independent of the actually
// installed toString method.
return NoSideEffectsErrorToString(isolate,
Handle<JSReceiver>::cast(input));
} else if (*to_string == *isolate->object_to_string()) {
Handle<Object> ctor = JSReceiver::GetDataProperty(
isolate, receiver, isolate->factory()->constructor_string());
if (IsJSFunctionOrBoundFunctionOrWrappedFunction(*ctor)) {
Handle<String> ctor_name;
if (IsJSBoundFunction(*ctor)) {
ctor_name = JSBoundFunction::GetName(
isolate, Handle<JSBoundFunction>::cast(ctor))
.ToHandleChecked();
} else if (IsJSFunction(*ctor)) {
ctor_name =
JSFunction::GetName(isolate, Handle<JSFunction>::cast(ctor));
}
if (ctor_name->length() != 0) {
IncrementalStringBuilder builder(isolate);
builder.AppendCStringLiteral("#<");
builder.AppendString(ctor_name);
builder.AppendCharacter('>');
return builder.Finish().ToHandleChecked();
}
}
}
}
return MaybeHandle<String>(kNullMaybeHandle);
}
// static
Handle<String> Object::NoSideEffectsToString(Isolate* isolate,
Handle<Object> input) {
DisallowJavascriptExecution no_js(isolate);
// Try to convert input to a meaningful string.
MaybeHandle<String> maybe_string = NoSideEffectsToMaybeString(isolate, input);
Handle<String> string_handle;
if (maybe_string.ToHandle(&string_handle)) {
return string_handle;
}
// At this point, input is either none of the above or a JSReceiver.
Handle<JSReceiver> receiver;
if (IsJSReceiver(*input)) {
receiver = Handle<JSReceiver>::cast(input);
} else {
// This is the only case where Object::ToObject throws.
DCHECK(!IsSmi(*input));
int constructor_function_index =
Handle<HeapObject>::cast(input)->map()->GetConstructorFunctionIndex();
if (constructor_function_index == Map::kNoConstructorFunctionIndex) {
return isolate->factory()->NewStringFromAsciiChecked("[object Unknown]");
}
receiver = Object::ToObjectImpl(isolate, input).ToHandleChecked();
}
Handle<String> builtin_tag = handle(receiver->class_name(), isolate);
Handle<Object> tag_obj = JSReceiver::GetDataProperty(
isolate, receiver, isolate->factory()->to_string_tag_symbol());
Handle<String> tag =
IsString(*tag_obj) ? Handle<String>::cast(tag_obj) : builtin_tag;
IncrementalStringBuilder builder(isolate);
builder.AppendCStringLiteral("[object ");
builder.AppendString(tag);
builder.AppendCharacter(']');
return builder.Finish().ToHandleChecked();
}
// static
MaybeHandle<Object> Object::ConvertToLength(Isolate* isolate,
Handle<Object> input) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input), Object);
if (IsSmi(*input)) {
int value = std::max(Smi::ToInt(*input), 0);
return handle(Smi::FromInt(value), isolate);
}
double len = DoubleToInteger(Object::Number(*input));
if (len <= 0.0) {
return handle(Smi::zero(), isolate);
} else if (len >= kMaxSafeInteger) {
len = kMaxSafeInteger;
}
return isolate->factory()->NewNumber(len);
}
// static
MaybeHandle<Object> Object::ConvertToIndex(Isolate* isolate,
Handle<Object> input,
MessageTemplate error_index) {
if (IsUndefined(*input, isolate)) return handle(Smi::zero(), isolate);
ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input), Object);
if (IsSmi(*input) && Smi::ToInt(*input) >= 0) return input;
double len = DoubleToInteger(Object::Number(*input));
Handle<Object> js_len = isolate->factory()->NewNumber(len);
if (len < 0.0 || len > kMaxSafeInteger) {
THROW_NEW_ERROR(isolate, NewRangeError(error_index, js_len), Object);
}
return js_len;
}
template <typename IsolateT>
// static
bool Object::BooleanValue(Tagged<Object> obj, IsolateT* isolate) {
if (IsSmi(obj)) return Smi::ToInt(obj) != 0;
DCHECK(IsHeapObject(obj));
if (IsBoolean(obj)) return IsTrue(obj, isolate);
if (IsNullOrUndefined(obj, isolate)) return false;
#ifdef V8_ENABLE_WEBASSEMBLY
if (IsWasmNull(obj)) return false;
#endif
if (IsUndetectable(obj)) return false; // Undetectable object is false.
if (IsString(obj)) return String::cast(obj)->length() != 0;
if (IsHeapNumber(obj)) return DoubleToBoolean(HeapNumber::cast(obj)->value());
if (IsBigInt(obj)) return BigInt::cast(obj)->ToBoolean();
return true;
}
template bool Object::BooleanValue(Tagged<Object>, Isolate*);
template bool Object::BooleanValue(Tagged<Object>, LocalIsolate*);
// static
Tagged<Object> Object::ToBoolean(Tagged<Object> obj, Isolate* isolate) {
if (IsBoolean(obj)) return obj;
return isolate->heap()->ToBoolean(Object::BooleanValue(obj, isolate));
}
namespace {
// TODO(bmeurer): Maybe we should introduce a marker interface Number,
// where we put all these methods at some point?
ComparisonResult StrictNumberCompare(double x, double y) {
if (std::isnan(x) || std::isnan(y)) {
return ComparisonResult::kUndefined;
} else if (x < y) {
return ComparisonResult::kLessThan;
} else if (x > y) {
return ComparisonResult::kGreaterThan;
} else {
return ComparisonResult::kEqual;
}
}
// See Number case of ES6#sec-strict-equality-comparison
// Returns false if x or y is NaN, treats -0.0 as equal to 0.0.
bool StrictNumberEquals(double x, double y) {
// Must check explicitly for NaN's on Windows, but -0 works fine.
if (std::isnan(x) || std::isnan(y)) return false;
return x == y;
}
bool StrictNumberEquals(const Tagged<Object> x, const Tagged<Object> y) {
return StrictNumberEquals(Object::Number(x), Object::Number(y));
}
bool StrictNumberEquals(Handle<Object> x, Handle<Object> y) {
return StrictNumberEquals(*x, *y);
}
ComparisonResult Reverse(ComparisonResult result) {
if (result == ComparisonResult::kLessThan) {
return ComparisonResult::kGreaterThan;
}
if (result == ComparisonResult::kGreaterThan) {
return ComparisonResult::kLessThan;
}
return result;
}
} // anonymous namespace
// static
Maybe<ComparisonResult> Object::Compare(Isolate* isolate, Handle<Object> x,
Handle<Object> y) {
// ES6 section 7.2.11 Abstract Relational Comparison step 3 and 4.
if (!Object::ToPrimitive(isolate, x, ToPrimitiveHint::kNumber).ToHandle(&x) ||
!Object::ToPrimitive(isolate, y, ToPrimitiveHint::kNumber).ToHandle(&y)) {
return Nothing<ComparisonResult>();
}
if (IsString(*x) && IsString(*y)) {
// ES6 section 7.2.11 Abstract Relational Comparison step 5.
return Just(String::Compare(isolate, Handle<String>::cast(x),
Handle<String>::cast(y)));
}
if (IsBigInt(*x) && IsString(*y)) {
return BigInt::CompareToString(isolate, Handle<BigInt>::cast(x),
Handle<String>::cast(y));
}
if (IsString(*x) && IsBigInt(*y)) {
Maybe<ComparisonResult> maybe_result = BigInt::CompareToString(
isolate, Handle<BigInt>::cast(y), Handle<String>::cast(x));
ComparisonResult result;
if (maybe_result.To(&result)) {
return Just(Reverse(result));
} else {
return Nothing<ComparisonResult>();
}
}
// ES6 section 7.2.11 Abstract Relational Comparison step 6.
if (!Object::ToNumeric(isolate, x).ToHandle(&x) ||
!Object::ToNumeric(isolate, y).ToHandle(&y)) {
return Nothing<ComparisonResult>();
}
bool x_is_number = IsNumber(*x);
bool y_is_number = IsNumber(*y);
if (x_is_number && y_is_number) {
return Just(StrictNumberCompare(Object::Number(*x), Object::Number(*y)));
} else if (!x_is_number && !y_is_number) {
return Just(BigInt::CompareToBigInt(Handle<BigInt>::cast(x),
Handle<BigInt>::cast(y)));
} else if (x_is_number) {
return Just(Reverse(BigInt::CompareToNumber(Handle<BigInt>::cast(y), x)));
} else {
return Just(BigInt::CompareToNumber(Handle<BigInt>::cast(x), y));
}
}
// static
Maybe<bool> Object::Equals(Isolate* isolate, Handle<Object> x,
Handle<Object> y) {
// This is the generic version of Abstract Equality Comparison. Must be in
// sync with CodeStubAssembler::Equal.
while (true) {
if (IsNumber(*x)) {
if (IsNumber(*y)) {
return Just(StrictNumberEquals(x, y));
} else if (IsBoolean(*y)) {
return Just(
StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
} else if (IsString(*y)) {
return Just(StrictNumberEquals(
x, String::ToNumber(isolate, Handle<String>::cast(y))));
} else if (IsBigInt(*y)) {
return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x));
} else if (IsJSReceiver(*y)) {
if (!JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
} else {
return Just(false);
}
} else if (IsString(*x)) {
if (IsString(*y)) {
return Just(String::Equals(isolate, Handle<String>::cast(x),
Handle<String>::cast(y)));
} else if (IsNumber(*y)) {
x = String::ToNumber(isolate, Handle<String>::cast(x));
return Just(StrictNumberEquals(x, y));
} else if (IsBoolean(*y)) {
x = String::ToNumber(isolate, Handle<String>::cast(x));
return Just(
StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
} else if (IsBigInt(*y)) {
return BigInt::EqualToString(isolate, Handle<BigInt>::cast(y),
Handle<String>::cast(x));
} else if (IsJSReceiver(*y)) {
if (!JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(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(Handle<Oddball>::cast(x)->to_number(), *y));
} else if (IsString(*y)) {
y = String::ToNumber(isolate, Handle<String>::cast(y));
return Just(
StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
} else if (IsBigInt(*y)) {
x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x));
return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x));
} else if (IsJSReceiver(*y)) {
if (!JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(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, Handle<JSReceiver>::cast(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
} else {
return Just(false);
}
} else if (IsBigInt(*x)) {
if (IsBigInt(*y)) {
return Just(BigInt::EqualToBigInt(BigInt::cast(*x), BigInt::cast(*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, Handle<Oddball>::cast(y));
} else if (!JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(x))
.ToHandle(&x)) {
return Nothing<bool>();
}
} else {
return Just(IsUndetectable(*x) && IsUndetectable(*y));
}
}
}
// static
bool Object::StrictEquals(Tagged<Object> obj, Tagged<Object> that) {
if (IsNumber(obj)) {
if (!IsNumber(that)) return false;
return StrictNumberEquals(obj, that);
} else if (IsString(obj)) {
if (!IsString(that)) return false;
return String::cast(obj)->Equals(String::cast(that));
} else if (IsBigInt(obj)) {
if (!IsBigInt(that)) return false;
return BigInt::EqualToBigInt(BigInt::cast(obj), BigInt::cast(that));
}
return obj == that;
}
// static
Handle<String> Object::TypeOf(Isolate* isolate, Handle<Object> object) {
if (IsNumber(*object)) return isolate->factory()->number_string();
if (IsOddball(*object))
return handle(Oddball::cast(*object)->type_of(), isolate);
if (IsUndetectable(*object)) {
return isolate->factory()->undefined_string();
}
if (IsString(*object)) return isolate->factory()->string_string();
if (IsSymbol(*object)) return isolate->factory()->symbol_string();
if (IsBigInt(*object)) return isolate->factory()->bigint_string();
if (IsCallable(*object)) return isolate->factory()->function_string();
return isolate->factory()->object_string();
}
// static
MaybeHandle<Object> Object::Add(Isolate* isolate, Handle<Object> lhs,
Handle<Object> rhs) {
if (IsNumber(*lhs) && IsNumber(*rhs)) {
return isolate->factory()->NewNumber(Object::Number(*lhs) +
Object::Number(*rhs));
} else if (IsString(*lhs) && IsString(*rhs)) {
return isolate->factory()->NewConsString(Handle<String>::cast(lhs),
Handle<String>::cast(rhs));
}
ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToPrimitive(isolate, lhs),
Object);
ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToPrimitive(isolate, rhs),
Object);
if (IsString(*lhs) || IsString(*rhs)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToString(isolate, rhs),
Object);
ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToString(isolate, lhs),
Object);
return isolate->factory()->NewConsString(Handle<String>::cast(lhs),
Handle<String>::cast(rhs));
}
ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(isolate, rhs),
Object);
ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(isolate, lhs),
Object);
return isolate->factory()->NewNumber(Object::Number(*lhs) +
Object::Number(*rhs));
}
// static
MaybeHandle<Object> Object::OrdinaryHasInstance(Isolate* isolate,
Handle<Object> callable,
Handle<Object> object) {
// The {callable} must have a [[Call]] internal method.
if (!IsCallable(*callable)) return isolate->factory()->false_value();
// Check if {callable} is a bound function, and if so retrieve its
// [[BoundTargetFunction]] and use that instead of {callable}.
if (IsJSBoundFunction(*callable)) {
// Since there is a mutual recursion here, we might run out of stack
// space for long chains of bound functions.
STACK_CHECK(isolate, MaybeHandle<Object>());
Handle<Object> bound_callable(
Handle<JSBoundFunction>::cast(callable)->bound_target_function(),
isolate);
return Object::InstanceOf(isolate, object, bound_callable);
}
// If {object} is not a receiver, return false.
if (!IsJSReceiver(*object)) return isolate->factory()->false_value();
// Get the "prototype" of {callable}; raise an error if it's not a receiver.
Handle<Object> prototype;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, prototype,
Object::GetProperty(isolate, callable,
isolate->factory()->prototype_string()),
Object);
if (!IsJSReceiver(*prototype)) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype),
Object);
}
// Return whether or not {prototype} is in the prototype chain of {object}.
Maybe<bool> result = JSReceiver::HasInPrototypeChain(
isolate, Handle<JSReceiver>::cast(object), prototype);
if (result.IsNothing()) return MaybeHandle<Object>();
return isolate->factory()->ToBoolean(result.FromJust());
}
// static
MaybeHandle<Object> Object::InstanceOf(Isolate* isolate, Handle<Object> object,
Handle<Object> callable) {
// The {callable} must be a receiver.
if (!IsJSReceiver(*callable)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kNonObjectInInstanceOfCheck),
Object);
}
// Lookup the @@hasInstance method on {callable}.
Handle<Object> inst_of_handler;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, inst_of_handler,
Object::GetMethod(isolate, Handle<JSReceiver>::cast(callable),
isolate->factory()->has_instance_symbol()),
Object);
if (!IsUndefined(*inst_of_handler, isolate)) {
// Call the {inst_of_handler} on the {callable}.
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
Execution::Call(isolate, inst_of_handler, callable, 1, &object),
Object);
return isolate->factory()->ToBoolean(
Object::BooleanValue(*result, isolate));
}
// The {callable} must have a [[Call]] internal method.
if (!IsCallable(*callable)) {
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kNonCallableInInstanceOfCheck),
Object);
}
// Fall back to OrdinaryHasInstance with {callable} and {object}.
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result, Object::OrdinaryHasInstance(isolate, callable, object),
Object);
return result;
}
// static
MaybeHandle<Object> Object::GetMethod(Isolate* isolate,
Handle<JSReceiver> receiver,
Handle<Name> name) {
Handle<Object> func;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, func, JSReceiver::GetProperty(isolate, receiver, name), Object);
if (IsNullOrUndefined(*func, isolate)) {
return isolate->factory()->undefined_value();
}
if (!IsCallable(*func)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kPropertyNotFunction, func,
name, receiver),
Object);
}
return func;
}
namespace {
MaybeHandle<FixedArray> CreateListFromArrayLikeFastPath(
Isolate* isolate, Handle<Object> object, ElementTypes element_types) {
if (element_types == ElementTypes::kAll) {
if (IsJSArray(*object)) {
Handle<JSArray> array = Handle<JSArray>::cast(object);
uint32_t length;
if (!array->HasArrayPrototype(isolate) ||
!Object::ToUint32(array->length(), &length) ||
!array->HasFastElements() ||
!JSObject::PrototypeHasNoElements(isolate, *array)) {
return MaybeHandle<FixedArray>();
}
return array->GetElementsAccessor()->CreateListFromArrayLike(
isolate, array, length);
} else if (IsJSTypedArray(*object)) {
Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(object);
size_t length = array->GetLength();
if (array->IsDetachedOrOutOfBounds() ||
length > static_cast<size_t>(FixedArray::kMaxLength)) {
return MaybeHandle<FixedArray>();
}
static_assert(FixedArray::kMaxLength <=
std::numeric_limits<uint32_t>::max());
return array->GetElementsAccessor()->CreateListFromArrayLike(
isolate, array, static_cast<uint32_t>(length));
}
}
return MaybeHandle<FixedArray>();
}
} // namespace
// static
MaybeHandle<FixedArray> Object::CreateListFromArrayLike(
Isolate* isolate, Handle<Object> object, ElementTypes element_types) {
// Fast-path for JSArray and JSTypedArray.
MaybeHandle<FixedArray> fast_result =
CreateListFromArrayLikeFastPath(isolate, object, element_types);
if (!fast_result.is_null()) return fast_result;
// 1. ReturnIfAbrupt(object).
// 2. (default elementTypes -- not applicable.)
// 3. If Type(obj) is not Object, throw a TypeError exception.
if (!IsJSReceiver(*object)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kCalledOnNonObject,
isolate->factory()->NewStringFromAsciiChecked(
"CreateListFromArrayLike")),
FixedArray);
}
// 4. Let len be ? ToLength(? Get(obj, "length")).
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
Handle<Object> raw_length_number;
ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number,
Object::GetLengthFromArrayLike(isolate, receiver),
FixedArray);
uint32_t len;
if (!Object::ToUint32(*raw_length_number, &len) ||
len > static_cast<uint32_t>(FixedArray::kMaxLength)) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kInvalidArrayLength),
FixedArray);
}
// 5. Let list be an empty List.
Handle<FixedArray> list = isolate->factory()->NewFixedArray(len);
// 6. Let index be 0.
// 7. Repeat while index < len:
for (uint32_t index = 0; index < len; ++index) {
// 7a. Let indexName be ToString(index).
// 7b. Let next be ? Get(obj, indexName).
Handle<Object> next;
ASSIGN_RETURN_ON_EXCEPTION(isolate, next,
JSReceiver::GetElement(isolate, receiver, index),
FixedArray);
switch (element_types) {
case ElementTypes::kAll:
// Nothing to do.
break;
case ElementTypes::kStringAndSymbol: {
// 7c. If Type(next) is not an element of elementTypes, throw a
// TypeError exception.
if (!IsName(*next)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kNotPropertyName, next),
FixedArray);
}
// 7d. Append next as the last element of list.
// Internalize on the fly so we can use pointer identity later.
next = isolate->factory()->InternalizeName(Handle<Name>::cast(next));
break;
}
}
list->set(index, *next);
// 7e. Set index to index + 1. (See loop header.)
}
// 8. Return list.
return list;
}
// static
MaybeHandle<Object> Object::GetLengthFromArrayLike(Isolate* isolate,
Handle<JSReceiver> object) {
Handle<Object> val;
Handle<Name> key = isolate->factory()->length_string();
ASSIGN_RETURN_ON_EXCEPTION(
isolate, val, JSReceiver::GetProperty(isolate, object, key), Object);
return Object::ToLength(isolate, val);
}
// static
MaybeHandle<Object> Object::GetProperty(LookupIterator* it,
bool is_global_reference) {
for (;; it->Next()) {
switch (it->state()) {
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::JSPROXY: {
bool was_found;
Handle<Object> receiver = it->GetReceiver();
// In case of global IC, the receiver is the global object. Replace by
// the global proxy.
if (IsJSGlobalObject(*receiver)) {
receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(),
it->isolate());
}
if (is_global_reference) {
Maybe<bool> maybe = JSProxy::HasProperty(
it->isolate(), it->GetHolder<JSProxy>(), it->GetName());
if (maybe.IsNothing()) return MaybeHandle<Object>();
if (!maybe.FromJust()) {
it->NotFound();
return it->isolate()->factory()->undefined_value();
}
}
MaybeHandle<Object> result =
JSProxy::GetProperty(it->isolate(), it->GetHolder<JSProxy>(),
it->GetName(), receiver, &was_found);
if (!was_found && !is_global_reference) it->NotFound();
return result;
}
case LookupIterator::WASM_OBJECT:
return it->isolate()->factory()->undefined_value();
case LookupIterator::INTERCEPTOR: {
bool done;
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
it->isolate(), result,
JSObject::GetPropertyWithInterceptor(it, &done), Object);
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::NOT_FOUND:
if (it->IsPrivateName()) {
Handle<Symbol> private_symbol = Handle<Symbol>::cast(it->name());
Handle<String> name_string(
String::cast(private_symbol->description()), it->isolate());
if (private_symbol->is_private_brand()) {
Handle<String> class_name =
(name_string->length() == 0)
? it->isolate()->factory()->anonymous_string()
: name_string;
THROW_NEW_ERROR(
it->isolate(),
NewTypeError(MessageTemplate::kInvalidPrivateBrandInstance,
class_name),
Object);
}
THROW_NEW_ERROR(
it->isolate(),
NewTypeError(MessageTemplate::kInvalidPrivateMemberRead,
name_string),
Object);
}
return it->isolate()->factory()->undefined_value();
}
UNREACHABLE();
}
}
// static
MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
Handle<JSProxy> proxy,
Handle<Name> name,
Handle<Object> receiver,
bool* was_found) {
*was_found = true;
DCHECK(!name->IsPrivate());
STACK_CHECK(isolate, MaybeHandle<Object>());
Handle<Name> trap_name = isolate->factory()->get_string();
// 1. Assert: IsPropertyKey(P) is true.
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
Handle<Object> handler(proxy->handler(), isolate);
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
if (proxy->IsRevoked()) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kProxyRevoked, trap_name),
Object);
}
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
// 6. Let trap be ? GetMethod(handler, "get").
Handle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap,
Object::GetMethod(isolate, Handle<JSReceiver>::cast(handler), trap_name),
Object);
// 7. If trap is undefined, then
if (IsUndefined(*trap, isolate)) {
// 7.a Return target.[[Get]](P, Receiver).
PropertyKey key(isolate, name);
LookupIterator it(isolate, receiver, key, target);
MaybeHandle<Object> result = Object::GetProperty(&it);
*was_found = it.IsFound();
return result;
}
// 8. Let trapResult be ? Call(trap, handler, «target, P, Receiver»).
Handle<Object> trap_result;
Handle<Object> args[] = {target, name, receiver};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result,
Execution::Call(isolate, trap, handler, arraysize(args), args), Object);
MaybeHandle<Object> result =
JSProxy::CheckGetSetTrapResult(isolate, name, target, trap_result, kGet);
if (result.is_null()) {
return result;
}
// 11. Return trap_result
return trap_result;
}
// static
MaybeHandle<Object> JSProxy::CheckGetSetTrapResult(Isolate* isolate,
Handle<Name> name,
Handle<JSReceiver> target,
Handle<Object> trap_result,
AccessKind access_kind) {
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
PropertyDescriptor target_desc;
Maybe<bool> target_found =
JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
MAYBE_RETURN_NULL(target_found);
// 10. If targetDesc is not undefined, then
if (target_found.FromJust()) {
// 10.a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is
// false and targetDesc.[[Writable]] is false, then
// 10.a.i. If SameValue(trapResult, targetDesc.[[Value]]) is false,
// throw a TypeError exception.
bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) &&
!target_desc.configurable() &&
!target_desc.writable() &&
!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),
Object);
} else {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxySetFrozenData, name));
return MaybeHandle<Object>();
}
}
// 10.b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]]
// is false and targetDesc.[[Get]] is undefined, then
// 10.b.i. If trapResult is not undefined, throw a TypeError exception.
if (access_kind == kGet) {
inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
!target_desc.configurable() &&
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),
Object);
} else {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxySetFrozenAccessor, name));
return MaybeHandle<Object>();
}
}
}
return isolate->factory()->undefined_value();
}
// static
bool Object::ToInt32(Tagged<Object> obj, int32_t* value) {
if (IsSmi(obj)) {
*value = Smi::ToInt(obj);
return true;
}
if (IsHeapNumber(obj)) {
double num = HeapNumber::cast(obj)->value();
// Check range before conversion to avoid undefined behavior.
if (num >= kMinInt && num <= kMaxInt && FastI2D(FastD2I(num)) == num) {
*value = FastD2I(num);
return true;
}
}
return false;
}
// ES6 9.5.1
// static
MaybeHandle<HeapObject> JSProxy::GetPrototype(Handle<JSProxy> proxy) {
Isolate* isolate = proxy->GetIsolate();
Handle<String> trap_name = isolate->factory()->getPrototypeOf_string();
STACK_CHECK(isolate, MaybeHandle<HeapObject>());
// 1. Let handler be the value of the [[ProxyHandler]] internal slot.
// 2. If handler is null, throw a TypeError exception.
// 3. Assert: Type(handler) is Object.
// 4. Let target be the value of the [[ProxyTarget]] internal slot.
if (proxy->IsRevoked()) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kProxyRevoked, trap_name),
HeapObject);
}
Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
// 5. Let trap be ? GetMethod(handler, "getPrototypeOf").
Handle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(isolate, trap,
Object::GetMethod(isolate, handler, trap_name),
HeapObject);
// 6. If trap is undefined, then return target.[[GetPrototypeOf]]().
if (IsUndefined(*trap, isolate)) {
return JSReceiver::GetPrototype(isolate, target);
}
// 7. Let handlerProto be ? Call(trap, handler, «target»).
Handle<Object> argv[] = {target};
Handle<Object> handler_proto;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, handler_proto,
Execution::Call(isolate, trap, handler, arraysize(argv), argv),
HeapObject);
// 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError.
if (!(IsJSReceiver(*handler_proto) || IsNull(*handler_proto, isolate))) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid),
HeapObject);
}
// 9. Let extensibleTarget be ? IsExtensible(target).
Maybe<bool> is_extensible = JSReceiver::IsExtensible(isolate, target);
MAYBE_RETURN(is_extensible, MaybeHandle<HeapObject>());
// 10. If extensibleTarget is true, return handlerProto.
if (is_extensible.FromJust()) return Handle<HeapObject>::cast(handler_proto);
// 11. Let targetProto be ? target.[[GetPrototypeOf]]().
Handle<HeapObject> target_proto;
ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto,
JSReceiver::GetPrototype(isolate, target),
HeapObject);
// 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError.
if (!Object::SameValue(*handler_proto, *target_proto)) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible),
HeapObject);
}
// 13. Return handlerProto.
return Handle<HeapObject>::cast(handler_proto);
}
MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) {
Isolate* isolate = it->isolate();
Handle<Object> structure = it->GetAccessors();
Handle<Object> receiver = it->GetReceiver();
// In case of global IC, the receiver is the global object. Replace by the
// global proxy.
if (IsJSGlobalObject(*receiver)) {
receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(), isolate);
}
// We should never get here to initialize a const with the hole value since a
// const declaration would conflict with the getter.
DCHECK(!IsForeign(*structure));
// API style callbacks.
Handle<JSObject> holder = it->GetHolder<JSObject>();
if (IsAccessorInfo(*structure)) {
Handle<Name> name = it->GetName();
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(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),
Object);
}
PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder,
Just(kDontThrow));
Handle<Object> result = args.CallAccessorGetter(info, name);
RETURN_EXCEPTION_IF_EXCEPTION(isolate, Object);
if (result.is_null()) return isolate->factory()->undefined_value();
Handle<Object> reboxed_result = handle(*result, isolate);
if (info->replace_on_access() && IsJSReceiver(*receiver)) {
RETURN_ON_EXCEPTION(isolate,
Accessors::ReplaceAccessorWithDataProperty(
isolate, receiver, holder, name, result),
Object);
}
return reboxed_result;
}
Handle<AccessorPair> accessor_pair = Handle<AccessorPair>::cast(structure);
// AccessorPair with 'cached' private property.
if (it->TryLookupCachedProperty(accessor_pair)) {
return Object::GetProperty(it);
}
// Regular accessor.
Handle<Object> getter(accessor_pair->getter(), isolate);
if (IsFunctionTemplateInfo(*getter)) {
SaveAndSwitchContext save(isolate, holder->GetCreationContext().value());
return Builtins::InvokeApiFunction(
isolate, false, Handle<FunctionTemplateInfo>::cast(getter), receiver, 0,
nullptr, isolate->factory()->undefined_value());
} else if (IsCallable(*getter)) {
// TODO(rossberg): nicer would be to cast to some JSCallable here...
return Object::GetPropertyWithDefinedGetter(
receiver, Handle<JSReceiver>::cast(getter));
}
// Getter is not a function.
return isolate->factory()->undefined_value();
}
Maybe<bool> Object::SetPropertyWithAccessor(
LookupIterator* it, Handle<Object> value,
Maybe<ShouldThrow> maybe_should_throw) {
Isolate* isolate = it->isolate();
Handle<Object> structure = it->GetAccessors();
Handle<Object> receiver = it->GetReceiver();
// In case of global IC, the receiver is the global object. Replace by the
// global proxy.
if (IsJSGlobalObject(*receiver)) {
receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(), isolate);
}
// We should never get here to initialize a const with the hole value since a
// const declaration would conflict with the setter.
DCHECK(!IsForeign(*structure));
// API style callbacks.
Handle<JSObject> holder = it->GetHolder<JSObject>();
if (IsAccessorInfo(*structure)) {
Handle<Name> name = it->GetName();
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure);
if (!info->has_setter(isolate)) {
// TODO(verwaest): We should not get here anymore once all AccessorInfos
// are marked as special_data_property. They cannot both be writable and
// not have a setter.
return Just(true);
}
if (info->is_sloppy() && !IsJSReceiver(*receiver)) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, receiver, Object::ConvertReceiver(isolate, receiver),
Nothing<bool>());
}
// The actual type of setter callback is either
// v8::AccessorNameSetterCallback or
// i::Accesors::AccessorNameBooleanSetterCallback, depending on whether the
// AccessorInfo was created by the API or internally (see accessors.cc).
// Here we handle both cases using GenericNamedPropertySetterCallback and
// its Call method.
PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder,
maybe_should_throw);
Handle<Object> result = args.CallAccessorSetter(info, name, value);
// In the case of AccessorNameSetterCallback, we know that the result value
// cannot have been set, so the result of Call will be null. In the case of
// AccessorNameBooleanSetterCallback, the result will either be null
// (signalling an exception) or a boolean Oddball.
RETURN_VALUE_IF_EXCEPTION(isolate, Nothing<bool>());
if (result.is_null()) return Just(true);
DCHECK(Object::BooleanValue(*result, isolate) ||
GetShouldThrow(isolate, maybe_should_throw) == kDontThrow);
return Just(Object::BooleanValue(*result, isolate));
}
// Regular accessor.
Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate);
if (IsFunctionTemplateInfo(*setter)) {
SaveAndSwitchContext save(isolate, holder->GetCreationContext().value());
Handle<Object> argv[] = {value};
RETURN_ON_EXCEPTION_VALUE(
isolate,
Builtins::InvokeApiFunction(isolate, false,
Handle<FunctionTemplateInfo>::cast(setter),
receiver, arraysize(argv), argv,
isolate->factory()->undefined_value()),
Nothing<bool>());
return Just(true);
} else if (IsCallable(*setter)) {
// TODO(rossberg): nicer would be to cast to some JSCallable here...
return SetPropertyWithDefinedSetter(
receiver, Handle<JSReceiver>::cast(setter), value, maybe_should_throw);
}
RETURN_FAILURE(isolate, GetShouldThrow(isolate, maybe_should_throw),
NewTypeError(MessageTemplate::kNoSetterInCallback,
it->GetName(), it->GetHolder<JSObject>()));
}
MaybeHandle<Object> Object::GetPropertyWithDefinedGetter(
Handle<Object> receiver, Handle<JSReceiver> getter) {
Isolate* isolate = getter->GetIsolate();
// Platforms with simulators like arm/arm64 expose a funny issue. If the
// simulator has a separate JS stack pointer from the C++ stack pointer, it
// can miss C++ stack overflows in the stack guard at the start of JavaScript
// functions. It would be very expensive to check the C++ stack pointer at
// that location. The best solution seems to be to break the impasse by
// adding checks at possible recursion points. What's more, we don't put
// this stack check behind the USE_SIMULATOR define in order to keep
// behavior the same between hardware and simulators.
StackLimitCheck check(isolate);
if (check.JsHasOverflowed()) {
isolate->StackOverflow();
return MaybeHandle<Object>();
}
return Execution::Call(isolate, getter, receiver, 0, nullptr);
}
Maybe<bool> Object::SetPropertyWithDefinedSetter(
Handle<Object> receiver, Handle<JSReceiver> setter, Handle<Object> value,
Maybe<ShouldThrow> should_throw) {
Isolate* isolate = setter->GetIsolate();
Handle<Object> argv[] = {value};
RETURN_ON_EXCEPTION_VALUE(
isolate,
Execution::Call(isolate, setter, receiver, arraysize(argv), argv),
Nothing<bool>());
return Just(true);
}
// static
Tagged<Map> Object::GetPrototypeChainRootMap(Tagged<Object> obj,
Isolate* isolate) {
DisallowGarbageCollection no_alloc;
if (IsSmi(obj)) {
Tagged<Context> native_context = isolate->context()->native_context();
return native_context->number_function()->initial_map();
}
const Tagged<HeapObject> heap_object = HeapObject::cast(obj);
return heap_object->map()->GetPrototypeChainRootMap(isolate);
}
// static
Tagged<Smi> Object::GetOrCreateHash(Tagged<Object> obj, Isolate* isolate) {
DisallowGarbageCollection no_gc;
Tagged<Object> hash = Object::GetSimpleHash(obj);
if (IsSmi(hash)) return Smi::cast(hash);
DCHECK(IsJSReceiver(obj));
return JSReceiver::cast(obj)->GetOrCreateIdentityHash(isolate);
}
// static
bool Object::SameValue(Tagged<Object> obj, Tagged<Object> other) {
if (other == obj) return true;
if (IsNumber(obj) && IsNumber(other)) {
return SameNumberValue(Object::Number(obj), Object::Number(other));
}
if (IsString(obj) && IsString(other)) {
return String::cast(obj)->Equals(String::cast(other));
}
if (IsBigInt(obj) && IsBigInt(other)) {
return BigInt::EqualToBigInt(BigInt::cast(obj), BigInt::cast(other));
}
return false;
}
// static
bool Object::SameValueZero(Tagged<Object> obj, Tagged<Object> other) {
if (other == obj) return true;
if (IsNumber(obj) && IsNumber(other)) {
double this_value = Object::Number(obj);
double other_value = Object::Number(other);
// +0 == -0 is true
return this_value == other_value ||
(std::isnan(this_value) && std::isnan(other_value));
}
if (IsString(obj) && IsString(other)) {
return String::cast(obj)->Equals(String::cast(other));
}
if (IsBigInt(obj) && IsBigInt(other)) {
return BigInt::EqualToBigInt(BigInt::cast(obj), BigInt::cast(other));
}
return false;
}
MaybeHandle<Object> Object::ArraySpeciesConstructor(
Isolate* isolate, Handle<Object> original_array) {
Handle<Object> default_species = isolate->array_function();
if (!v8_flags.builtin_subclassing) return default_species;
if (IsJSArray(*original_array) &&
Handle<JSArray>::cast(original_array)->HasArrayPrototype(isolate) &&
Protectors::IsArraySpeciesLookupChainIntact(isolate)) {
return default_species;
}
Handle<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()),
Object);
if (IsConstructor(*constructor)) {
Handle<NativeContext> constructor_context;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, constructor_context,
JSReceiver::GetFunctionRealm(Handle<JSReceiver>::cast(constructor)),
Object);
if (*constructor_context != *isolate->native_context() &&
*constructor == constructor_context->array_function()) {
constructor = isolate->factory()->undefined_value();
}
}
if (IsJSReceiver(*constructor)) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, constructor,
JSReceiver::GetProperty(isolate,
Handle<JSReceiver>::cast(constructor),
isolate->factory()->species_symbol()),
Object);
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),
Object);
}
return constructor;
}
}
// ES6 section 7.3.20 SpeciesConstructor ( O, defaultConstructor )
V8_WARN_UNUSED_RESULT MaybeHandle<Object> Object::SpeciesConstructor(
Isolate* isolate, Handle<JSReceiver> recv,
Handle<JSFunction> default_ctor) {
Handle<Object> ctor_obj;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, ctor_obj,
JSObject::GetProperty(isolate, recv,
isolate->factory()->constructor_string()),
Object);
if (IsUndefined(*ctor_obj, isolate)) return default_ctor;
if (!IsJSReceiver(*ctor_obj)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kConstructorNotReceiver),
Object);
}
Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj);
Handle<Object> species;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, species,
JSObject::GetProperty(isolate, ctor,
isolate->factory()->species_symbol()),
Object);
if (IsNullOrUndefined(*species, isolate)) {
return default_ctor;
}
if (IsConstructor(*species)) return species;
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object);
}
// static
bool Object::IterationHasObservableEffects(Tagged<Object> obj) {
DisallowGarbageCollection no_gc;
// Check that this object is an array.
if (!IsJSArray(obj)) return true;
Tagged<JSArray> array = JSArray::cast(obj);
// Check that we have the original ArrayPrototype.
Tagged<Object> array_proto = array->map()->prototype();
if (!IsJSObject(array_proto)) return true;
Tagged<NativeContext> native_context = array->GetCreationContext().value();
auto initial_array_prototype = native_context->initial_array_prototype();
if (initial_array_prototype != JSObject::cast(array_proto)) return true;
Isolate* isolate = array->GetIsolate();
// Check that the ArrayPrototype hasn't been modified in a way that would
// affect iteration.
if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return true;
// For FastPacked kinds, iteration will have the same effect as simply
// accessing each property in order.
ElementsKind array_kind = array->GetElementsKind();
if (IsFastPackedElementsKind(array_kind)) return false;
// For FastHoley kinds, an element access on a hole would cause a lookup on
// the prototype. This could have different results if the prototype has been
// changed.
if (IsHoleyElementsKind(array_kind) &&
Protectors::IsNoElementsIntact(isolate)) {
return false;
}
return true;
}
// static
bool Object::IsCodeLike(Tagged<Object> obj, Isolate* isolate) {
DisallowGarbageCollection no_gc;
return IsJSReceiver(obj) && JSReceiver::cast(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, const Brief& v) {
MaybeObject maybe_object(v.value);
Tagged<Smi> smi;
Tagged<HeapObject> heap_object;
if (maybe_object->ToSmi(&smi)) {
Smi::SmiPrint(smi, os);
} else if (maybe_object->IsCleared()) {
os << "[cleared]";
} else if (maybe_object.GetHeapObjectIfWeak(&heap_object)) {
os << "[weak] ";
heap_object->HeapObjectShortPrint(os);
} else if (maybe_object.GetHeapObjectIfStrong(&heap_object)) {
heap_object->HeapObjectShortPrint(os);
} else {
UNREACHABLE();
}
return os;
}
// static
void Smi::SmiPrint(Tagged<Smi> smi, std::ostream& os) { os << smi.value(); }
void Struct::BriefPrintDetails(std::ostream& os) {}
void 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());
}
void HeapObject::Iterate(PtrComprCageBase cage_base, ObjectVisitor* v) {
IterateFast<ObjectVisitor>(cage_base, v);
}
void HeapObject::IterateBody(PtrComprCageBase cage_base, ObjectVisitor* v) {
Tagged<Map> m = map(cage_base);
IterateBodyFast<ObjectVisitor>(m, SizeFromMap(m), v);
}
void HeapObject::IterateBody(Tagged<Map> map, int object_size,
ObjectVisitor* v) {
IterateBodyFast<ObjectVisitor>(map, object_size, v);
}
int HeapObjectLayout::SizeFromMap(Tagged<Map> map) const {
return Tagged<HeapObject>(this)->SizeFromMap(map);
}
int HeapObject::SizeFromMap(Tagged<Map> map) const {
int instance_size = map->instance_size();
if (instance_size != kVariableSizeSentinel) return instance_size;
// Only inline the most frequent cases.
InstanceType instance_type = map->instance_type();
if (base::IsInRange(instance_type, FIRST_FIXED_ARRAY_TYPE,
LAST_FIXED_ARRAY_TYPE)) {
return FixedArray::unchecked_cast(*this)->AllocatedSize();
}
#define CASE(TypeCamelCase, TYPE_UPPER_CASE) \
if (instance_type == TYPE_UPPER_CASE##_TYPE) { \
return TypeCamelCase::unchecked_cast(*this)->AllocatedSize(); \
}
SIMPLE_HEAP_OBJECT_LIST2(CASE)
#undef CASE
if (instance_type == SLOPPY_ARGUMENTS_ELEMENTS_TYPE) {
return SloppyArgumentsElements::unchecked_cast(*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(Context::unchecked_cast(*this)->length());
}
if (instance_type == SEQ_ONE_BYTE_STRING_TYPE ||
instance_type == INTERNALIZED_ONE_BYTE_STRING_TYPE ||
instance_type == SHARED_SEQ_ONE_BYTE_STRING_TYPE) {
// Strings may get concurrently truncated, hence we have to access its
// length synchronized.
return SeqOneByteString::SizeFor(
SeqOneByteString::unchecked_cast(*this)->length(kAcquireLoad));
}
if (instance_type == BYTECODE_ARRAY_TYPE) {
return BytecodeArray::SizeFor(
BytecodeArray::unchecked_cast(*this)->length(kAcquireLoad));
}
if (instance_type == EXTERNAL_POINTER_ARRAY_TYPE) {
return ExternalPointerArray::SizeFor(
ExternalPointerArray::unchecked_cast(*this)->length(kAcquireLoad));
}
if (instance_type == FREE_SPACE_TYPE) {
return FreeSpace::unchecked_cast(*this)->size(kRelaxedLoad);
}
if (instance_type == SEQ_TWO_BYTE_STRING_TYPE ||
instance_type == INTERNALIZED_TWO_BYTE_STRING_TYPE ||
instance_type == SHARED_SEQ_TWO_BYTE_STRING_TYPE) {
// Strings may get concurrently truncated, hence we have to access its
// length synchronized.
return SeqTwoByteString::SizeFor(
SeqTwoByteString::unchecked_cast(*this)->length(kAcquireLoad));
}
if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) {
return FixedDoubleArray::unchecked_cast(*this)->AllocatedSize();
}
if (instance_type == TRUSTED_FIXED_ARRAY_TYPE) {
return TrustedFixedArray::unchecked_cast(*this)->AllocatedSize();
}
if (instance_type == TRUSTED_BYTE_ARRAY_TYPE) {
return TrustedByteArray::unchecked_cast(*this)->AllocatedSize();
}
if (instance_type == FEEDBACK_METADATA_TYPE) {
return FeedbackMetadata::SizeFor(
FeedbackMetadata::unchecked_cast(*this)->slot_count(kAcquireLoad));
}
if (base::IsInRange(instance_type, FIRST_DESCRIPTOR_ARRAY_TYPE,
LAST_DESCRIPTOR_ARRAY_TYPE)) {
return DescriptorArray::SizeFor(
DescriptorArray::unchecked_cast(*this)->number_of_all_descriptors());
}
if (base::IsInRange(instance_type, FIRST_WEAK_FIXED_ARRAY_TYPE,
LAST_WEAK_FIXED_ARRAY_TYPE)) {
return WeakFixedArray::unchecked_cast(*this)->AllocatedSize();
}
if (instance_type == WEAK_ARRAY_LIST_TYPE) {
return WeakArrayList::SizeForCapacity(
WeakArrayList::unchecked_cast(*this)->capacity());
}
if (instance_type == SMALL_ORDERED_HASH_SET_TYPE) {
return SmallOrderedHashSet::SizeFor(
SmallOrderedHashSet::unchecked_cast(*this)->Capacity());
}
if (instance_type == SMALL_ORDERED_HASH_MAP_TYPE) {
return SmallOrderedHashMap::SizeFor(
SmallOrderedHashMap::unchecked_cast(*this)->Capacity());
}
if (instance_type == SMALL_ORDERED_NAME_DICTIONARY_TYPE) {
return SmallOrderedNameDictionary::SizeFor(
SmallOrderedNameDictionary::unchecked_cast(*this)->Capacity());
}
if (instance_type == SWISS_NAME_DICTIONARY_TYPE) {
return SwissNameDictionary::SizeFor(
SwissNameDictionary::unchecked_cast(*this)->Capacity());
}
if (instance_type == PROPERTY_ARRAY_TYPE) {
return PropertyArray::SizeFor(
PropertyArray::unchecked_cast(*this)->length(kAcquireLoad));
}
if (instance_type == FEEDBACK_VECTOR_TYPE) {
return FeedbackVector::SizeFor(
FeedbackVector::unchecked_cast(*this)->length());
}
if (instance_type == BIGINT_TYPE) {
return BigInt::SizeFor(BigInt::unchecked_cast(*this)->length());
}
if (instance_type == PREPARSE_DATA_TYPE) {
Tagged<PreparseData> data = PreparseData::unchecked_cast(*this);
return PreparseData::SizeFor(data->data_length(), data->children_length());
}
#define MAKE_TORQUE_SIZE_FOR(TYPE, TypeName) \
if (instance_type == TYPE) { \
return TypeName::unchecked_cast(*this)->AllocatedSize(); \
}
TORQUE_INSTANCE_TYPE_TO_BODY_DESCRIPTOR_LIST(MAKE_TORQUE_SIZE_FOR)
#undef MAKE_TORQUE_SIZE_FOR
if (instance_type == INSTRUCTION_STREAM_TYPE) {
return InstructionStream::unchecked_cast(*this)->Size();
}
if (instance_type == COVERAGE_INFO_TYPE) {
return CoverageInfo::SizeFor(
CoverageInfo::unchecked_cast(*this)->slot_count());
}
#if V8_ENABLE_WEBASSEMBLY
if (instance_type == WASM_TYPE_INFO_TYPE) {
return WasmTypeInfo::SizeFor(
WasmTypeInfo::unchecked_cast(*this)->supertypes_length());
}
if (instance_type == WASM_STRUCT_TYPE) {
return WasmStruct::GcSafeSize(map);
}
if (instance_type == WASM_ARRAY_TYPE) {
return WasmArray::SizeFor(map, WasmArray::unchecked_cast(*this)->length());
}
if (instance_type == WASM_NULL_TYPE) {
return WasmNull::kSize;
}
#endif // V8_ENABLE_WEBASSEMBLY
DCHECK_EQ(instance_type, EMBEDDER_DATA_ARRAY_TYPE);
return EmbedderDataArray::SizeFor(
EmbedderDataArray::unchecked_cast(*this)->length());
}
bool HeapObject::NeedsRehashing(PtrComprCageBase cage_base) const {
return NeedsRehashing(map(cage_base)->instance_type());
}
bool HeapObject::NeedsRehashing(InstanceType instance_type) const {
if (V8_EXTERNAL_CODE_SPACE_BOOL) {
// Use map() only when it's guaranteed that it's not a InstructionStream
// object.
DCHECK_IMPLIES(instance_type != INSTRUCTION_STREAM_TYPE,
instance_type == map()->instance_type());
} else {
DCHECK_EQ(instance_type, map()->instance_type());
}
switch (instance_type) {
case DESCRIPTOR_ARRAY_TYPE:
case STRONG_DESCRIPTOR_ARRAY_TYPE:
return DescriptorArray::cast(*this)->number_of_descriptors() > 1;
case TRANSITION_ARRAY_TYPE:
return TransitionArray::cast(*this)->number_of_entries() > 1;
case ORDERED_HASH_MAP_TYPE:
case ORDERED_HASH_SET_TYPE:
return false; // We'll rehash from the JSMap or JSSet referencing them.
case NAME_DICTIONARY_TYPE:
case 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;
}
}
bool HeapObject::CanBeRehashed(PtrComprCageBase cage_base) const {
DCHECK(NeedsRehashing(cage_base));
switch (map(cage_base)->instance_type()) {
case JS_MAP_TYPE:
case JS_SET_TYPE:
return true;
case ORDERED_HASH_MAP_TYPE:
case ORDERED_HASH_SET_TYPE:
UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them.
case ORDERED_NAME_DICTIONARY_TYPE:
return false;
case NAME_DICTIONARY_TYPE:
case NAME_TO_INDEX_HASH_TABLE_TYPE:
case REGISTERED_SYMBOL_TABLE_TYPE:
case GLOBAL_DICTIONARY_TYPE:
case NUMBER_DICTIONARY_TYPE:
case SIMPLE_NUMBER_DICTIONARY_TYPE:
case SWISS_NAME_DICTIONARY_TYPE:
return true;
case DESCRIPTOR_ARRAY_TYPE:
case STRONG_DESCRIPTOR_ARRAY_TYPE:
return true;
case TRANSITION_ARRAY_TYPE:
return true;
case SMALL_ORDERED_HASH_MAP_TYPE:
return SmallOrderedHashMap::cast(*this)->NumberOfElements() == 0;
case SMALL_ORDERED_HASH_SET_TYPE:
return SmallOrderedHashMap::cast(*this)->NumberOfElements() == 0;
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
return SmallOrderedNameDictionary::cast(*this)->NumberOfElements() == 0;
default:
return false;
}
}
template <typename IsolateT>
void HeapObject::RehashBasedOnMap(IsolateT* isolate) {
switch (map(isolate)->instance_type()) {
case HASH_TABLE_TYPE:
UNREACHABLE();
case NAME_DICTIONARY_TYPE:
NameDictionary::cast(*this)->Rehash(isolate);
break;
case NAME_TO_INDEX_HASH_TABLE_TYPE:
NameToIndexHashTable::cast(*this)->Rehash(isolate);
break;
case REGISTERED_SYMBOL_TABLE_TYPE:
RegisteredSymbolTable::cast(*this)->Rehash(isolate);
break;
case SWISS_NAME_DICTIONARY_TYPE:
SwissNameDictionary::cast(*this)->Rehash(isolate);
break;
case GLOBAL_DICTIONARY_TYPE:
GlobalDictionary::cast(*this)->Rehash(isolate);
break;
case NUMBER_DICTIONARY_TYPE:
NumberDictionary::cast(*this)->Rehash(isolate);
break;
case SIMPLE_NUMBER_DICTIONARY_TYPE:
SimpleNumberDictionary::cast(*this)->Rehash(isolate);
break;
case DESCRIPTOR_ARRAY_TYPE:
case STRONG_DESCRIPTOR_ARRAY_TYPE:
DCHECK_LE(1, DescriptorArray::cast(*this)->number_of_descriptors());
DescriptorArray::cast(*this)->Sort();
break;
case TRANSITION_ARRAY_TYPE:
TransitionArray::cast(*this)->Sort();
break;
case SMALL_ORDERED_HASH_MAP_TYPE:
DCHECK_EQ(0, SmallOrderedHashMap::cast(*this)->NumberOfElements());
break;
case SMALL_ORDERED_HASH_SET_TYPE:
DCHECK_EQ(0, SmallOrderedHashSet::cast(*this)->NumberOfElements());
break;
case ORDERED_HASH_MAP_TYPE:
case ORDERED_HASH_SET_TYPE:
UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them.
case JS_MAP_TYPE: {
JSMap::cast(*this)->Rehash(isolate->AsIsolate());
break;
}
case JS_SET_TYPE: {
JSSet::cast(*this)->Rehash(isolate->AsIsolate());
break;
}
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this)->NumberOfElements());
break;
case INTERNALIZED_ONE_BYTE_STRING_TYPE:
case INTERNALIZED_TWO_BYTE_STRING_TYPE:
// Rare case, rehash read-only space strings before they are sealed.
DCHECK(ReadOnlyHeap::Contains(*this));
String::cast(*this)->EnsureHash();
break;
default:
UNREACHABLE();
}
}
template void HeapObject::RehashBasedOnMap(Isolate* isolate);
template void HeapObject::RehashBasedOnMap(LocalIsolate* isolate);
void DescriptorArray::GeneralizeAllFields() {
int length = number_of_descriptors();
for (InternalIndex i : InternalIndex::Range(length)) {
PropertyDetails details = GetDetails(i);
details = details.CopyWithRepresentation(Representation::Tagged());
if (details.location() == PropertyLocation::kField) {
DCHECK_EQ(PropertyKind::kData, details.kind());
SetValue(i, MaybeObject::FromObject(FieldType::Any()));
}
SetDetails(i, details);
}
}
MaybeHandle<Object> Object::SetProperty(Isolate* isolate, Handle<Object> object,
Handle<Name> name, Handle<Object> value,
StoreOrigin store_origin,
Maybe<ShouldThrow> should_throw) {
LookupIterator it(isolate, object, name);
MAYBE_RETURN_NULL(SetProperty(&it, value, store_origin, should_throw));
return value;
}
Maybe<bool> Object::SetPropertyInternal(LookupIterator* it,
Handle<Object> value,
Maybe<ShouldThrow> should_throw,
StoreOrigin store_origin, bool* found) {
it->UpdateProtector();
DCHECK(it->IsFound());
// Make sure that the top context does not change when doing callbacks or
// interceptor calls.
AssertNoContextChange ncc(it->isolate());
for (;; it->Next()) {
switch (it->state()) {
case LookupIterator::ACCESS_CHECK:
if (it->HasAccess()) continue;
// Check whether it makes sense to reuse the lookup iterator. Here it
// might still call into setters up the prototype chain.
return JSObject::SetPropertyWithFailedAccessCheck(it, value,
should_throw);
case LookupIterator::JSPROXY: {
Handle<Object> receiver = it->GetReceiver();
// In case of global IC, the receiver is the global object. Replace by
// the global proxy.
if (IsJSGlobalObject(*receiver)) {
receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(),
it->isolate());
}
return JSProxy::SetProperty(it->GetHolder<JSProxy>(), it->GetName(),
value, receiver, should_throw);
}
case LookupIterator::WASM_OBJECT:
RETURN_FAILURE(it->isolate(), kThrowOnError,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
case LookupIterator::INTERCEPTOR: {
if (it->HolderIsReceiverOrHiddenPrototype()) {
Maybe<bool> result =
JSObject::SetPropertyWithInterceptor(it, should_throw, value);
if (result.IsNothing() || result.FromJust()) return result;
// Assuming that the callback have side effects, we use
// Object::SetSuperProperty() which works properly regardless on
// whether the property was present on the receiver or not when
// storing to the receiver.
// Proceed lookup from the next state.
it->Next();
} else {
Maybe<PropertyAttributes> maybe_attributes =
JSObject::GetPropertyAttributesWithInterceptor(it);
if (maybe_attributes.IsNothing()) return Nothing<bool>();
if ((maybe_attributes.FromJust() & READ_ONLY) != 0) {
return WriteToReadOnlyProperty(it, value, should_throw);
}
// At this point we might have called interceptor's query or getter
// callback. Assuming that the callbacks have side effects, we use
// Object::SetSuperProperty() which works properly regardless on
// whether the property was present on the receiver or not when
// storing to the receiver.
if (maybe_attributes.FromJust() == ABSENT) {
// Proceed lookup from the next state.
it->Next();
} else {
// Finish lookup in order to make Object::SetSuperProperty() store
// property to the receiver.
it->NotFound();
}
}
return Object::SetSuperProperty(it, value, store_origin, should_throw);
}
case LookupIterator::ACCESSOR: {
if (it->IsReadOnly()) {
return WriteToReadOnlyProperty(it, value, should_throw);
}
Handle<Object> accessors = it->GetAccessors();
if (IsAccessorInfo(*accessors) &&
!it->HolderIsReceiverOrHiddenPrototype() &&
AccessorInfo::cast(*accessors)->is_special_data_property()) {
*found = false;
return Nothing<bool>();
}
return SetPropertyWithAccessor(it, value, should_throw);
}
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND: {
// IntegerIndexedElementSet converts value to a Number/BigInt prior to
// the bounds check. The bounds check has already happened here, but
// perform the possibly effectful ToNumber (or ToBigInt) operation
// anyways.
Handle<JSTypedArray> holder = it->GetHolder<JSTypedArray>();
Handle<Object> throwaway_value;
if (holder->type() == kExternalBigInt64Array ||
holder->type() == kExternalBigUint64Array) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
it->isolate(), throwaway_value,
BigInt::FromObject(it->isolate(), value), Nothing<bool>());
} else {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
it->isolate(), throwaway_value,
Object::ToNumber(it->isolate(), value), Nothing<bool>());
}
// FIXME: Throw a TypeError if the holder is detached here
// (IntegerIndexedElementSpec step 5).
// TODO(verwaest): Per spec, we should return false here (steps 6-9
// in IntegerIndexedElementSpec), resulting in an exception being thrown
// on OOB accesses in strict code. Historically, v8 has not done made
// this change due to uncertainty about web compat. (v8:4901)
return Just(true);
}
case LookupIterator::DATA:
if (it->IsReadOnly()) {
return WriteToReadOnlyProperty(it, value, should_throw);
}
if (it->HolderIsReceiverOrHiddenPrototype()) {
return SetDataProperty(it, value);
}
V8_FALLTHROUGH;
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
*found = false;
return Nothing<bool>();
}
UNREACHABLE();
}
}
bool Object::CheckContextualStoreToJSGlobalObject(
LookupIterator* it, Maybe<ShouldThrow> should_throw) {
Isolate* isolate = it->isolate();
if (IsJSGlobalObject(*it->GetReceiver(), isolate) &&
(GetShouldThrow(isolate, should_throw) == ShouldThrow::kThrowOnError)) {
if (it->state() == LookupIterator::TRANSITION) {
// The property cell that we have created is garbage because we are going
// to throw now instead of putting it into the global dictionary. However,
// the cell might already have been stored into the feedback vector, so
// we must invalidate it nevertheless.
it->transition_cell()->ClearAndInvalidate(ReadOnlyRoots(isolate));
}
isolate->Throw(*isolate->factory()->NewReferenceError(
MessageTemplate::kNotDefined, it->GetName()));
return false;
}
return true;
}
Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value,
StoreOrigin store_origin,
Maybe<ShouldThrow> should_throw) {
if (it->IsFound()) {
bool found = true;
Maybe<bool> result =
SetPropertyInternal(it, value, should_throw, store_origin, &found);
if (found) return result;
}
if (!CheckContextualStoreToJSGlobalObject(it, should_throw)) {
return Nothing<bool>();
}
return AddDataProperty(it, value, NONE, should_throw, store_origin);
}
Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value,
StoreOrigin store_origin,
Maybe<ShouldThrow> should_throw) {
Isolate* isolate = it->isolate();
if (it->IsFound()) {
bool found = true;
Maybe<bool> result =
SetPropertyInternal(it, value, should_throw, store_origin, &found);
if (found) return result;
}
it->UpdateProtector();
// The property either doesn't exist on the holder or exists there as a data
// property.
if (!IsJSReceiver(*it->GetReceiver())) {
return WriteToReadOnlyProperty(it, value, should_throw);
}
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver());
// Note, the callers rely on the fact that this code is redoing the full own
// lookup from scratch.
LookupIterator own_lookup(isolate, receiver, it->GetKey(),
LookupIterator::OWN);
for (;; own_lookup.Next()) {
switch (own_lookup.state()) {
case LookupIterator::ACCESS_CHECK:
if (!own_lookup.HasAccess()) {
return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value,
should_throw);
}
continue;
case LookupIterator::ACCESSOR:
if (IsAccessorInfo(*own_lookup.GetAccessors())) {
if (own_lookup.IsReadOnly()) {
return WriteToReadOnlyProperty(&own_lookup, value, should_throw);
}
return Object::SetPropertyWithAccessor(&own_lookup, value,
should_throw);
}
V8_FALLTHROUGH;
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
return RedefineIncompatibleProperty(isolate, it->GetName(), value,
should_throw);
case LookupIterator::DATA: {
if (own_lookup.IsReadOnly()) {
return WriteToReadOnlyProperty(&own_lookup, value, should_throw);
}
return SetDataProperty(&own_lookup, value);
}
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY: {
PropertyDescriptor desc;
Maybe<bool> owned =
JSReceiver::GetOwnPropertyDescriptor(&own_lookup, &desc);
MAYBE_RETURN(owned, Nothing<bool>());
if (!owned.FromJust()) {
// |own_lookup| might become outdated at this point anyway.
own_lookup.Restart();
if (!CheckContextualStoreToJSGlobalObject(&own_lookup,
should_throw)) {
return Nothing<bool>();
}
return JSReceiver::CreateDataProperty(&own_lookup, value,
should_throw);
}
if (PropertyDescriptor::IsAccessorDescriptor(&desc) ||
!desc.writable()) {
return RedefineIncompatibleProperty(isolate, it->GetName(), value,
should_throw);
}
PropertyDescriptor value_desc;
value_desc.set_value(value);
return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(),
&value_desc, should_throw);
}
case LookupIterator::NOT_FOUND:
if (!CheckContextualStoreToJSGlobalObject(&own_lookup, should_throw)) {
return Nothing<bool>();
}
return AddDataProperty(&own_lookup, value, NONE, should_throw,
store_origin);
case LookupIterator::TRANSITION:
case LookupIterator::WASM_OBJECT:
UNREACHABLE();
}
UNREACHABLE();
}
}
Maybe<bool> Object::CannotCreateProperty(Isolate* isolate,
Handle<Object> receiver,
Handle<Object> name,
Handle<Object> value,
Maybe<ShouldThrow> should_throw) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kStrictCannotCreateProperty, name,
Object::TypeOf(isolate, receiver), receiver));
}
Maybe<bool> Object::WriteToReadOnlyProperty(
LookupIterator* it, Handle<Object> value,
Maybe<ShouldThrow> maybe_should_throw) {
ShouldThrow should_throw = GetShouldThrow(it->isolate(), maybe_should_throw);
if (it->IsFound() && !it->HolderIsReceiver()) {
// "Override mistake" attempted, record a use count to track this per
// v8:8175
v8::Isolate::UseCounterFeature feature =
should_throw == kThrowOnError
? v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeStrict
: v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeSloppy;
it->isolate()->CountUsage(feature);
}
return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(),
it->GetName(), value, should_throw);
}
Maybe<bool> Object::WriteToReadOnlyProperty(Isolate* isolate,
Handle<Object> receiver,
Handle<Object> name,
Handle<Object> value,
ShouldThrow should_throw) {
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kStrictReadOnlyProperty, name,
Object::TypeOf(isolate, receiver), receiver));
}
Maybe<bool> Object::RedefineIncompatibleProperty(
Isolate* isolate, Handle<Object> name, Handle<Object> value,
Maybe<ShouldThrow> should_throw) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed, name));
}
Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) {
Isolate* isolate = it->isolate();
DCHECK_IMPLIES(IsJSProxy(*it->GetReceiver(), isolate),
it->GetName()->IsPrivateName());
DCHECK_IMPLIES(!it->IsElement() && it->GetName()->IsPrivateName(),
it->state() == LookupIterator::DATA);
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver());
// Store on the holder which may be hidden behind the receiver.
DCHECK(it->HolderIsReceiverOrHiddenPrototype());
Handle<Object> to_assign = value;
// Convert the incoming value to a number for storing into typed arrays.
if (it->IsElement() && IsJSObject(*receiver, isolate) &&
JSObject::cast(*receiver)->HasTypedArrayOrRabGsabTypedArrayElements(
isolate)) {
Handle<JSTypedArray> receiver_ta = Handle<JSTypedArray>::cast(receiver);
ElementsKind elements_kind = JSObject::cast(*receiver)->GetElementsKind();
if (IsBigIntTypedArrayElementsKind(elements_kind)) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, to_assign,
BigInt::FromObject(isolate, value),
Nothing<bool>());
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_VALUE(isolate, to_assign,
Object::ToNumber(isolate, value),
Nothing<bool>());
if (V8_UNLIKELY(receiver_ta->IsDetachedOrOutOfBounds() ||
it->index() >= receiver_ta->GetLength())) {
return Just(true);
}
}
}
DCHECK(!IsWasmObject(*receiver, isolate));
if (V8_UNLIKELY(IsJSSharedStruct(*receiver, isolate) ||
IsJSSharedArray(*receiver, isolate))) {
// Shared structs can only point to primitives or shared values.
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, to_assign, Object::Share(isolate, to_assign, kThrowOnError),
Nothing<bool>());
it->WriteDataValue(to_assign, false);
} else {
// Possibly migrate to the most up-to-date map that will be able to store
// |value| under it->name().
it->PrepareForDataProperty(to_assign);
// Write the property value.
it->WriteDataValue(to_assign, false);
}
#if VERIFY_HEAP
if (v8_flags.verify_heap) {
receiver->HeapObjectVerify(isolate);
}
#endif
return Just(true);
}
Maybe<bool> Object::AddDataProperty(LookupIterator* it, Handle<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(),
value, should_throw);
}
// Private symbols should be installed on JSProxy using
// JSProxy::SetPrivateSymbol.
if (IsJSProxy(*it->GetReceiver()) && it->GetName()->IsPrivate() &&
!it->GetName()->IsPrivateName()) {
RETURN_FAILURE(it->isolate(), GetShouldThrow(it->isolate(), should_throw),
NewTypeError(MessageTemplate::kProxyPrivate));
}
DCHECK_NE(LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND, it->state());
Handle<JSReceiver> receiver = it->GetStoreTarget<JSReceiver>();
DCHECK_IMPLIES(IsJSProxy(*receiver), it->GetName()->IsPrivateName());
DCHECK_IMPLIES(IsJSProxy(*receiver),
it->state() == LookupIterator::NOT_FOUND);
// If the receiver is a JSGlobalProxy, store on the prototype (JSGlobalObject)
// instead. If the prototype is Null, the proxy is detached.
if (IsJSGlobalProxy(*receiver)) return Just(true);
Isolate* isolate = it->isolate();
if (it->ExtendingNonExtensible(receiver)) {
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)) {
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
if (JSArray::WouldChangeReadOnlyLength(array, it->array_index())) {
RETURN_FAILURE(isolate, GetShouldThrow(it->isolate(), should_throw),
NewTypeError(MessageTemplate::kStrictReadOnlyProperty,
isolate->factory()->length_string(),
Object::TypeOf(isolate, array), array));
}
}
Handle<JSObject> receiver_obj = Handle<JSObject>::cast(receiver);
MAYBE_RETURN(JSObject::AddDataElement(receiver_obj, it->array_index(),
value, attributes),
Nothing<bool>());
JSObject::ValidateElements(*receiver_obj);
return Just(true);
}
return Object::TransitionAndWriteDataProperty(it, value, attributes,
should_throw, store_origin);
}
// static
Maybe<bool> Object::TransitionAndWriteDataProperty(
LookupIterator* it, Handle<Object> value, PropertyAttributes attributes,
Maybe<ShouldThrow> should_throw, StoreOrigin store_origin) {
Handle<JSReceiver