#include "src/objects/js-objects.h"
#include <limits>
#include <optional>
#include "src/api/api-arguments-inl.h"
#include "src/api/api-natives.h"
#include "src/common/assert-scope.h"
#include "src/common/globals.h"
#include "src/date/date.h"
#include "src/execution/arguments.h"
#include "src/execution/frames.h"
#include "src/execution/isolate-utils.h"
#include "src/execution/isolate.h"
#include "src/handles/handles-inl.h"
#include "src/handles/maybe-handles.h"
#include "src/heap/factory-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/heap-layout-inl.h"
#include "src/heap/mutable-page-metadata.h"
#include "src/heap/pretenuring-handler-inl.h"
#include "src/heap/read-only-heap.h"
#include "src/init/bootstrapper.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/dictionary.h"
#include "src/objects/elements.h"
#include "src/objects/field-type.h"
#include "src/objects/fixed-array.h"
#include "src/objects/heap-number.h"
#include "src/objects/heap-object.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-atomics-synchronization.h"
#include "src/objects/js-collection.h"
#include "src/objects/js-disposable-stack.h"
#include "src/objects/js-generator-inl.h"
#include "src/objects/js-iterator-helpers-inl.h"
#include "src/objects/js-promise.h"
#include "src/objects/js-raw-json-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/js-regexp-string-iterator.h"
#include "src/objects/js-shadow-realm.h"
#include "src/objects/js-shared-array-inl.h"
#include "src/objects/js-struct-inl.h"
#ifdef V8_TEMPORAL_SUPPORT
#include "src/objects/js-temporal-objects-inl.h"
#endif
#include "src/objects/js-weak-refs.h"
#include "src/objects/lookup.h"
#include "src/objects/map-inl.h"
#include "src/objects/map-updater.h"
#include "src/objects/module.h"
#include "src/objects/objects-body-descriptors-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/oddball.h"
#include "src/objects/property-cell.h"
#include "src/objects/property-descriptor.h"
#include "src/objects/property.h"
#include "src/objects/prototype-info.h"
#include "src/objects/prototype.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/swiss-name-dictionary-inl.h"
#include "src/objects/tagged.h"
#include "src/objects/transitions.h"
#include "src/strings/string-builder-inl.h"
#include "src/strings/string-stream.h"
#include "src/utils/ostreams.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/debug/debug-wasm-objects.h"
#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-display-names.h"
#include "src/objects/js-duration-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 {
Maybe<bool> JSReceiver::HasProperty(LookupIterator* it) {
for (;; it->Next()) {
switch (it->state()) {
case LookupIterator::TRANSITION:
case LookupIterator::STRING_LOOKUP_START_OBJECT:
UNREACHABLE();
case LookupIterator::JSPROXY:
return JSProxy::HasProperty(it->isolate(), it->GetHolder<JSProxy>(),
it->GetName());
case LookupIterator::WASM_OBJECT:
continue;
case LookupIterator::INTERCEPTOR: {
Maybe<PropertyAttributes> result =
JSObject::GetPropertyAttributesWithInterceptor(it);
if (result.IsNothing()) return Nothing<bool>();
if (result.FromJust() != ABSENT) return Just(true);
continue;
}
case LookupIterator::ACCESS_CHECK: {
if (it->HasAccess()) continue;
Maybe<PropertyAttributes> result =
JSObject::GetPropertyAttributesWithFailedAccessCheck(it);
if (result.IsNothing()) return Nothing<bool>();
return Just(result.FromJust() != ABSENT);
}
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
return Just(false);
case LookupIterator::ACCESSOR:
case LookupIterator::DATA:
return Just(true);
case LookupIterator::NOT_FOUND:
return Just(false);
}
UNREACHABLE();
}
}
Maybe<bool> JSReceiver::HasOwnProperty(Isolate* isolate,
DirectHandle<JSReceiver> object,
DirectHandle<Name> name) {
if (IsJSModuleNamespace(*object)) {
PropertyDescriptor desc;
return JSReceiver::GetOwnPropertyDescriptor(isolate, object, name, &desc);
}
if (IsJSObject(*object)) {
PropertyKey key(isolate, name);
LookupIterator it(isolate, object, key, LookupIterator::OWN);
return HasProperty(&it);
}
Maybe<PropertyAttributes> attributes =
JSReceiver::GetOwnPropertyAttributes(isolate, object, name);
MAYBE_RETURN(attributes, Nothing<bool>());
return Just(attributes.FromJust() != ABSENT);
}
Handle<Object> JSReceiver::GetDataProperty(LookupIterator* it,
AllocationPolicy allocation_policy) {
for (;; it->Next()) {
switch (it->state()) {
case LookupIterator::INTERCEPTOR:
case LookupIterator::TRANSITION:
case LookupIterator::STRING_LOOKUP_START_OBJECT:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
if (!it->isolate()->context().is_null() && it->HasAccess()) continue;
[[fallthrough]];
case LookupIterator::JSPROXY:
it->NotFound();
return it->isolate()->factory()->undefined_value();
case LookupIterator::WASM_OBJECT:
continue;
case LookupIterator::ACCESSOR: {
auto accessors = it->GetAccessors();
if (IsAccessorInfo(*accessors)) {
auto info = Cast<AccessorInfo>(*accessors);
if (info->getter_side_effect_type() ==
SideEffectType::kHasNoSideEffect) {
v8::TryCatch try_catch(
reinterpret_cast<v8::Isolate*>(it->isolate()));
try_catch.SetVerbose(false);
try_catch.SetCaptureMessage(false);
Handle<Object> result;
if (Object::GetPropertyWithAccessor(it).ToHandle(&result)) {
return result;
}
}
}
it->NotFound();
return it->isolate()->factory()->undefined_value();
}
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
return it->isolate()->factory()->undefined_value();
case LookupIterator::DATA:
return it->GetDataValue(allocation_policy);
case LookupIterator::NOT_FOUND:
return it->isolate()->factory()->undefined_value();
}
UNREACHABLE();
}
}
Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate,
DirectHandle<JSReceiver> object,
DirectHandle<Object> proto) {
PrototypeIterator iter(isolate, object, kStartAtReceiver);
while (true) {
if (!iter.AdvanceFollowingProxies()) return Nothing<bool>();
if (iter.IsAtEnd()) return Just(false);
if (PrototypeIterator::GetCurrent(iter).is_identical_to(proto)) {
return Just(true);
}
}
}
Maybe<bool> JSReceiver::CheckPrivateNameStore(LookupIterator* it,
bool is_define) {
DCHECK(it->GetName()->IsPrivateName());
Isolate* isolate = it->isolate();
DirectHandle<String> name_string(
Cast<String>(Cast<Symbol>(it->GetName())->description()), isolate);
for (;; it->Next()) {
switch (it->state()) {
case LookupIterator::TRANSITION:
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY:
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
case LookupIterator::ACCESSOR:
case LookupIterator::STRING_LOOKUP_START_OBJECT:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
if (!it->HasAccess()) {
RETURN_ON_EXCEPTION_VALUE(isolate,
isolate->ReportFailedAccessCheck(
Cast<JSObject>(it->GetReceiver())),
Nothing<bool>());
UNREACHABLE();
}
continue;
case LookupIterator::DATA:
if (is_define) {
MessageTemplate message =
it->GetName()->IsPrivateBrand()
? MessageTemplate::kInvalidPrivateBrandReinitialization
: MessageTemplate::kInvalidPrivateFieldReinitialization;
RETURN_FAILURE(isolate,
GetShouldThrow(isolate, Nothing<ShouldThrow>()),
NewTypeError(message, name_string, it->GetReceiver()));
}
return Just(true);
case LookupIterator::WASM_OBJECT:
RETURN_FAILURE(isolate, kThrowOnError,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
case LookupIterator::NOT_FOUND:
if (!is_define) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, Nothing<ShouldThrow>()),
NewTypeError(MessageTemplate::kInvalidPrivateMemberWrite,
name_string, it->GetReceiver()));
} else if (it->ExtendingNonExtensible(
it->GetStoreTarget<JSReceiver>())) {
RETURN_FAILURE(
isolate, kThrowOnError,
NewTypeError(MessageTemplate::kDefineDisallowed, name_string));
}
return Just(true);
}
UNREACHABLE();
}
}
namespace {
bool HasExcludedProperty(base::Vector<DirectHandle<Object>> excluded_properties,
DirectHandle<Object> search_element) {
for (DirectHandle<Object> object : excluded_properties) {
if (Object::SameValue(*search_element, *object)) {
return true;
}
}
return false;
}
V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign(
Isolate* isolate, DirectHandle<JSReceiver> target,
DirectHandle<Object> source, PropertiesEnumerationMode mode,
base::Vector<DirectHandle<Object>> excluded_properties, bool use_set) {
if (!IsJSReceiver(*source)) {
return Just(!IsString(*source) || Cast<String>(*source)->length() == 0);
}
if (target->map()->is_deprecated()) {
JSObject::MigrateInstance(isolate, Cast<JSObject>(target));
}
DirectHandle<Map> map(Cast<JSReceiver>(*source)->map(), isolate);
if (!IsJSObjectMap(*map)) return Just(false);
if (!map->OnlyHasSimpleProperties()) return Just(false);
DirectHandle<JSObject> from = Cast<JSObject>(source);
if (from->elements() != ReadOnlyRoots(isolate).empty_fixed_array()) {
return Just(false);
}
CHECK_IMPLIES(!use_set, !target.is_identical_to(from));
bool stable = true;
bool has_symbol = false;
bool process_symbol_only = false;
while (true) {
for (InternalIndex i : map->IterateOwnDescriptors()) {
HandleScope inner_scope(isolate);
DirectHandle<Name> next_key(map->instance_descriptors(isolate)->GetKey(i),
isolate);
if (mode == PropertiesEnumerationMode::kEnumerationOrder) {
if (IsSymbol(*next_key)) {
has_symbol = true;
if (!process_symbol_only) continue;
} else {
if (process_symbol_only) continue;
}
}
DirectHandle<Object> prop_value;
if (stable) {
DCHECK_EQ(from->map(), *map);
PropertyDetails details =
map->instance_descriptors(isolate)->GetDetails(i);
if (!details.IsEnumerable()) continue;
if (details.kind() == PropertyKind::kData) {
CHECK_EQ(details.location(), PropertyLocation::kField);
Representation representation = details.representation();
FieldIndex index = FieldIndex::ForPropertyIndex(
*map, details.field_index(), representation);
prop_value =
JSObject::FastPropertyAt(isolate, from, representation, index);
} else {
LookupIterator it(isolate, from, next_key,
LookupIterator::OWN_SKIP_INTERCEPTOR);
ASSIGN_RETURN_ON_EXCEPTION(isolate, prop_value,
Object::GetProperty(&it));
stable = from->map() == *map;
}
} else {
LookupIterator it(isolate, from, next_key, from,
LookupIterator::OWN_SKIP_INTERCEPTOR);
if (!it.IsFound()) continue;
DCHECK(it.state() == LookupIterator::DATA ||
it.state() == LookupIterator::ACCESSOR);
if (!it.IsEnumerable()) continue;
ASSIGN_RETURN_ON_EXCEPTION(isolate, prop_value,
Object::GetProperty(&it));
}
if (use_set) {
PropertyKey key(isolate, next_key);
LookupIterator it(isolate, target, key);
Maybe<bool> result =
Object::SetProperty(&it, prop_value, StoreOrigin::kNamed,
Just(ShouldThrow::kThrowOnError));
if (result.IsNothing()) return result;
if (stable) {
stable = from->map() == *map;
}
} else {
if (!excluded_properties.empty() &&
HasExcludedProperty(excluded_properties, next_key)) {
continue;
}
CHECK(JSReceiver::CreateDataProperty(isolate, target, next_key,
prop_value, Just(kThrowOnError))
.FromJust());
}
}
if (mode == PropertiesEnumerationMode::kEnumerationOrder) {
if (process_symbol_only || !has_symbol) {
return Just(true);
}
if (has_symbol) {
process_symbol_only = true;
}
} else {
DCHECK_EQ(mode, PropertiesEnumerationMode::kPropertyAdditionOrder);
return Just(true);
}
}
UNREACHABLE();
}
}
Maybe<bool> JSReceiver::SetOrCopyDataProperties(
Isolate* isolate, DirectHandle<JSReceiver> target,
DirectHandle<Object> source, PropertiesEnumerationMode mode,
base::Vector<DirectHandle<Object>> excluded_properties, bool use_set) {
Maybe<bool> fast_assign =
FastAssign(isolate, target, source, mode, excluded_properties, use_set);
if (fast_assign.IsNothing()) return Nothing<bool>();
if (fast_assign.FromJust()) return Just(true);
DirectHandle<JSReceiver> from =
Object::ToObject(isolate, source).ToHandleChecked();
DirectHandle<FixedArray> keys;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, keys,
KeyAccumulator::GetKeys(isolate, from, KeyCollectionMode::kOwnOnly,
ALL_PROPERTIES, GetKeysConversion::kKeepNumbers));
if (!from->HasFastProperties() && target->HasFastProperties() &&
IsJSObject(*target) && !IsJSGlobalProxy(*target)) {
int source_length;
if (IsJSGlobalObject(*from)) {
source_length = Cast<JSGlobalObject>(*from)
->global_dictionary(kAcquireLoad)
->NumberOfEnumerableProperties();
} else if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
source_length =
from->property_dictionary_swiss()->NumberOfEnumerableProperties();
} else {
source_length =
from->property_dictionary()->NumberOfEnumerableProperties();
}
if (source_length > kMaxNumberOfDescriptors) {
JSObject::NormalizeProperties(isolate, Cast<JSObject>(target),
CLEAR_INOBJECT_PROPERTIES, source_length,
"Copying data properties");
}
}
for (int i = 0; i < keys->length(); ++i) {
DirectHandle<Object> next_key(keys->get(i), isolate);
if (!excluded_properties.empty() &&
HasExcludedProperty(excluded_properties, next_key)) {
continue;
}
PropertyDescriptor desc;
Maybe<bool> found =
JSReceiver::GetOwnPropertyDescriptor(isolate, from, next_key, &desc);
if (found.IsNothing()) return Nothing<bool>();
if (found.FromJust() && desc.enumerable()) {
DirectHandle<Object> prop_value;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, prop_value,
Runtime::GetObjectProperty(isolate, from, next_key));
if (use_set) {
DirectHandle<Object> status;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, status,
Runtime::SetObjectProperty(isolate, target, next_key, prop_value,
StoreOrigin::kMaybeKeyed,
Just(ShouldThrow::kThrowOnError)));
} else {
PropertyKey key(isolate, next_key);
CHECK(JSReceiver::CreateDataProperty(isolate, target, key, prop_value,
Just(kThrowOnError))
.FromJust());
}
}
}
return Just(true);
}
Tagged<String> JSReceiver::class_name() {
ReadOnlyRoots roots = GetReadOnlyRoots();
if (IsJSFunctionOrBoundFunctionOrWrappedFunction(*this)) {
return roots.Function_string();
}
if (IsJSArgumentsObject(*this)) return roots.Arguments_string();
if (IsJSArray(*this)) return roots.Array_string();
if (IsJSArrayBuffer(*this)) {
if (Cast<JSArrayBuffer>(*this)->is_shared()) {
return roots.SharedArrayBuffer_string();
}
return roots.ArrayBuffer_string();
}
if (IsJSArrayIterator(*this)) return roots.ArrayIterator_string();
if (IsJSDate(*this)) return roots.Date_string();
if (IsJSError(*this)) return roots.Error_string();
if (IsJSGeneratorObject(*this)) return roots.Generator_string();
if (IsJSMap(*this)) return roots.Map_string();
if (IsJSMapIterator(*this)) return roots.MapIterator_string();
if (IsJSProxy(*this)) {
return map()->is_callable() ? roots.Function_string()
: roots.Object_string();
}
if (IsJSRegExp(*this)) return roots.RegExp_string();
if (IsJSSet(*this)) return roots.Set_string();
if (IsJSSetIterator(*this)) return roots.SetIterator_string();
if (IsJSTypedArray(*this)) {
#define SWITCH_KIND(Type, type, TYPE, ctype) \
if (map()->elements_kind() == TYPE##_ELEMENTS) { \
return roots.Type##Array_string(); \
}
TYPED_ARRAYS(SWITCH_KIND)
#undef SWITCH_KIND
}
if (IsJSPrimitiveWrapper(*this)) {
Tagged<Object> value = Cast<JSPrimitiveWrapper>(*this)->value();
if (IsBoolean(value)) return roots.Boolean_string();
if (IsString(value)) return roots.String_string();
if (IsNumber(value)) return roots.Number_string();
if (IsBigInt(value)) return roots.BigInt_string();
if (IsSymbol(value)) return roots.Symbol_string();
if (IsScript(value)) return roots.Script_string();
UNREACHABLE();
}
if (IsJSWeakMap(*this)) return roots.WeakMap_string();
if (IsJSWeakSet(*this)) return roots.WeakSet_string();
if (IsShared(*this)) {
if (IsJSSharedStruct(*this)) return roots.SharedStruct_string();
if (IsJSSharedArray(*this)) return roots.SharedArray_string();
if (IsJSAtomicsMutex(*this)) return roots.AtomicsMutex_string();
if (IsJSAtomicsCondition(*this)) return roots.AtomicsCondition_string();
UNREACHABLE();
}
return roots.Object_string();
}
namespace {
std::pair<MaybeDirectHandle<JSFunction>, DirectHandle<String>>
GetConstructorHelper(Isolate* isolate, DirectHandle<JSReceiver> receiver) {
if (!IsJSProxy(*receiver) && receiver->map()->new_target_is_base() &&
!receiver->map()->is_prototype_map()) {
DirectHandle<Object> maybe_constructor(receiver->map()->GetConstructor(),
isolate);
if (IsJSFunction(*maybe_constructor)) {
DirectHandle<JSFunction> constructor =
Cast<JSFunction>(maybe_constructor);
DirectHandle<String> name =
JSFunction::GetDebugName(isolate, constructor);
if (name->length() != 0 &&
!name->Equals(ReadOnlyRoots(isolate).Object_string())) {
return std::make_pair(indirect_handle(constructor, isolate), name);
}
} else if (IsFunctionTemplateInfo(*maybe_constructor)) {
DirectHandle<FunctionTemplateInfo> function_template =
Cast<FunctionTemplateInfo>(maybe_constructor);
if (!IsUndefined(function_template->class_name(), isolate)) {
return std::make_pair(
MaybeHandle<JSFunction>(),
handle(Cast<String>(function_template->class_name()), isolate));
}
}
}
bool is_hidden_prototype = false;
for (PrototypeIterator it(isolate, receiver, kStartAtReceiver); !it.IsAtEnd();
it.AdvanceIgnoringProxies()) {
auto current = PrototypeIterator::GetCurrent<JSReceiver>(it);
LookupIterator it_to_string_tag(
isolate, receiver, isolate->factory()->to_string_tag_symbol(), current,
LookupIterator::OWN_SKIP_INTERCEPTOR);
auto maybe_to_string_tag = JSReceiver::GetDataProperty(
&it_to_string_tag, AllocationPolicy::kAllocationDisallowed);
if (IsString(*maybe_to_string_tag)) {
return std::make_pair(MaybeHandle<JSFunction>(),
Cast<String>(maybe_to_string_tag));
}
if (is_hidden_prototype) {
DirectHandle<Object> maybe_constructor(current->map()->GetConstructor(),
isolate);
if (IsFunctionTemplateInfo(*maybe_constructor)) {
DirectHandle<FunctionTemplateInfo> function_template =
Cast<FunctionTemplateInfo>(maybe_constructor);
if (!IsUndefined(function_template->class_name(), isolate)) {
return std::make_pair(
MaybeHandle<JSFunction>(),
handle(Cast<String>(function_template->class_name()), isolate));
}
}
}
if (!receiver.is_identical_to(current)) {
LookupIterator it_constructor(
isolate, receiver, isolate->factory()->constructor_string(), current,
LookupIterator::OWN_SKIP_INTERCEPTOR);
auto maybe_constructor = JSReceiver::GetDataProperty(
&it_constructor, AllocationPolicy::kAllocationDisallowed);
if (IsJSFunction(*maybe_constructor)) {
auto constructor = Cast<JSFunction>(maybe_constructor);
auto name = SharedFunctionInfo::DebugName(
isolate, direct_handle(constructor->shared(), isolate));
if (name->length() != 0 &&
!name->Equals(ReadOnlyRoots(isolate).Object_string())) {
return std::make_pair(constructor, name);
}
}
}
is_hidden_prototype = IsJSGlobalProxy(*current);
}
return std::make_pair(MaybeHandle<JSFunction>(),
handle(receiver->class_name(), isolate));
}
}
MaybeDirectHandle<JSFunction> JSReceiver::GetConstructor(
Isolate* isolate, DirectHandle<JSReceiver> receiver) {
return GetConstructorHelper(isolate, receiver).first;
}
DirectHandle<String> JSReceiver::GetConstructorName(
Isolate* isolate, DirectHandle<JSReceiver> receiver) {
return GetConstructorHelper(isolate, receiver).second;
}
MaybeDirectHandle<NativeContext> JSReceiver::GetFunctionRealm(
DirectHandle<JSReceiver> receiver) {
Isolate* isolate = Isolate::Current();
DisallowGarbageCollection no_gc;
Tagged<JSReceiver> current = *receiver;
do {
DCHECK(current->map()->is_constructor());
InstanceType instance_type = current->map()->instance_type();
if (InstanceTypeChecker::IsJSProxy(instance_type)) {
Tagged<JSProxy> proxy = Cast<JSProxy>(current);
if (proxy->IsRevoked()) {
AllowGarbageCollection allow_allocating_errors;
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyRevoked));
}
current = Cast<JSReceiver>(proxy->target());
continue;
}
if (InstanceTypeChecker::IsJSFunction(instance_type)) {
Tagged<JSFunction> function = Cast<JSFunction>(current);
return direct_handle(function->native_context(), isolate);
}
if (InstanceTypeChecker::IsJSBoundFunction(instance_type)) {
Tagged<JSBoundFunction> function = Cast<JSBoundFunction>(current);
current = function->bound_target_function();
continue;
}
if (InstanceTypeChecker::IsJSWrappedFunction(instance_type)) {
Tagged<JSWrappedFunction> function = Cast<JSWrappedFunction>(current);
current = function->wrapped_target_function();
continue;
}
Tagged<JSObject> object = Cast<JSObject>(current);
DCHECK(!IsJSFunction(object));
return object->GetCreationContext(isolate);
} while (true);
}
MaybeHandle<NativeContext> JSReceiver::GetContextForMicrotask(
DirectHandle<JSReceiver> receiver) {
Isolate* isolate = Isolate::Current();
while (IsJSBoundFunction(*receiver) || IsJSProxy(*receiver)) {
if (IsJSBoundFunction(*receiver)) {
receiver = direct_handle(
Cast<JSBoundFunction>(receiver)->bound_target_function(), isolate);
} else {
DCHECK(IsJSProxy(*receiver));
DirectHandle<Object> target(Cast<JSProxy>(receiver)->target(), isolate);
if (!IsJSReceiver(*target)) return MaybeHandle<NativeContext>();
receiver = Cast<JSReceiver>(target);
}
}
if (!IsJSFunction(*receiver)) return MaybeHandle<NativeContext>();
return handle(Cast<JSFunction>(receiver)->native_context(), isolate);
}
Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes(
LookupIterator* it) {
for (;; it->Next()) {
switch (it->state()) {
case LookupIterator::TRANSITION:
case LookupIterator::STRING_LOOKUP_START_OBJECT:
UNREACHABLE();
case LookupIterator::JSPROXY:
return JSProxy::GetPropertyAttributes(it);
case LookupIterator::WASM_OBJECT:
return Just(ABSENT);
case LookupIterator::INTERCEPTOR: {
Maybe<PropertyAttributes> result =
JSObject::GetPropertyAttributesWithInterceptor(it);
if (result.IsNothing()) return result;
if (result.FromJust() != ABSENT) return result;
continue;
}
case LookupIterator::ACCESS_CHECK:
if (it->HasAccess()) continue;
return JSObject::GetPropertyAttributesWithFailedAccessCheck(it);
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
return Just(ABSENT);
case LookupIterator::ACCESSOR:
if (IsJSModuleNamespace(*it->GetHolder<Object>())) {
return JSModuleNamespace::GetPropertyAttributes(it);
} else {
return Just(it->property_attributes());
}
case LookupIterator::DATA:
return Just(it->property_attributes());
case LookupIterator::NOT_FOUND:
return Just(ABSENT);
}
UNREACHABLE();
}
}
namespace {
Tagged<Object> SetHashAndUpdateProperties(Tagged<HeapObject> properties,
int hash) {
DCHECK_NE(PropertyArray::kNoHashSentinel, hash);
DCHECK(PropertyArray::HashField::is_valid(hash));
ReadOnlyRoots roots = GetReadOnlyRoots();
if (properties == roots.empty_fixed_array() ||
properties == roots.empty_property_array() ||
properties == roots.empty_property_dictionary() ||
properties == roots.empty_swiss_property_dictionary()) {
return Smi::FromInt(hash);
}
if (IsPropertyArray(properties)) {
Cast<PropertyArray>(properties)->SetHash(hash);
DCHECK_LT(0, Cast<PropertyArray>(properties)->length());
return properties;
}
if (IsGlobalDictionary(properties)) {
Cast<GlobalDictionary>(properties)->SetHash(hash);
return properties;
}
DCHECK(IsPropertyDictionary(properties));
Cast<PropertyDictionary>(properties)->SetHash(hash);
return properties;
}
int GetIdentityHashHelper(Tagged<JSReceiver> object) {
DisallowGarbageCollection no_gc;
Tagged<Object> properties = object->raw_properties_or_hash();
if (IsSmi(properties)) {
return Smi::ToInt(properties);
}
if (IsPropertyArray(properties)) {
return Cast<PropertyArray>(properties)->Hash();
}
if (IsPropertyDictionary(properties)) {
return Cast<PropertyDictionary>(properties)->Hash();
}
if (IsGlobalDictionary(properties)) {
return Cast<GlobalDictionary>(properties)->Hash();
}
#ifdef DEBUG
ReadOnlyRoots roots = GetReadOnlyRoots();
DCHECK(properties == roots.empty_fixed_array() ||
properties == roots.empty_property_dictionary() ||
properties == roots.empty_swiss_property_dictionary());
#endif
return PropertyArray::kNoHashSentinel;
}
}
void JSReceiver::SetIdentityHash(int hash) {
DisallowGarbageCollection no_gc;
DCHECK_NE(PropertyArray::kNoHashSentinel, hash);
DCHECK(PropertyArray::HashField::is_valid(hash));
Tagged<HeapObject> existing_properties =
Cast<HeapObject>(raw_properties_or_hash());
Tagged<Object> new_properties =
SetHashAndUpdateProperties(existing_properties, hash);
set_raw_properties_or_hash(new_properties, kRelaxedStore);
}
void JSReceiver::SetProperties(Tagged<HeapObject> properties) {
DCHECK_IMPLIES(IsPropertyArray(properties) &&
Cast<PropertyArray>(properties)->length() == 0,
properties == GetReadOnlyRoots().empty_property_array());
DisallowGarbageCollection no_gc;
int hash = GetIdentityHashHelper(*this);
Tagged<Object> new_properties = properties;
if (hash != PropertyArray::kNoHashSentinel) {
new_properties = SetHashAndUpdateProperties(properties, hash);
}
set_raw_properties_or_hash(new_properties, kRelaxedStore);
}
Tagged<Object> JSReceiver::GetIdentityHash() {
DisallowGarbageCollection no_gc;
int hash = GetIdentityHashHelper(*this);
if (hash == PropertyArray::kNoHashSentinel) {
return GetReadOnlyRoots().undefined_value();
}
return Smi::FromInt(hash);
}
Tagged<Smi> JSReceiver::CreateIdentityHash(Isolate* isolate,
Tagged<JSReceiver> key) {
DisallowGarbageCollection no_gc;
int hash = isolate->GenerateIdentityHash(PropertyArray::HashField::kMax);
DCHECK_NE(PropertyArray::kNoHashSentinel, hash);
key->SetIdentityHash(hash);
return Smi::FromInt(hash);
}
Tagged<Smi> JSReceiver::GetOrCreateIdentityHash(Isolate* isolate) {
DisallowGarbageCollection no_gc;
int hash = GetIdentityHashHelper(*this);
if (hash != PropertyArray::kNoHashSentinel) {
return Smi::FromInt(hash);
}
return JSReceiver::CreateIdentityHash(isolate, *this);
}
void JSReceiver::DeleteNormalizedProperty(DirectHandle<JSReceiver> object,
InternalIndex entry) {
DCHECK(!object->HasFastProperties());
Isolate* isolate = Isolate::Current();
DCHECK(entry.is_found());
if (IsJSGlobalObject(*object)) {
DirectHandle<GlobalDictionary> dictionary(
Cast<JSGlobalObject>(*object)->global_dictionary(kAcquireLoad),
isolate);
DirectHandle<PropertyCell> cell(dictionary->CellAt(entry), isolate);
DirectHandle<GlobalDictionary> new_dictionary =
GlobalDictionary::DeleteEntry(isolate, dictionary, entry);
Cast<JSGlobalObject>(*object)->set_global_dictionary(*new_dictionary,
kReleaseStore);
cell->ClearAndInvalidate(isolate);
} else {
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
DirectHandle<SwissNameDictionary> dictionary(
object->property_dictionary_swiss(), isolate);
dictionary = SwissNameDictionary::DeleteEntry(isolate, dictionary, entry);
object->SetProperties(*dictionary);
} else {
DirectHandle<NameDictionary> dictionary(object->property_dictionary(),
isolate);
dictionary = NameDictionary::DeleteEntry(isolate, dictionary, entry);
object->SetProperties(*dictionary);
}
}
if (object->map()->is_prototype_map()) {
JSObject::InvalidatePrototypeChains(object->map());
}
}
Maybe<bool> JSReceiver::DeleteProperty(LookupIterator* it,
LanguageMode language_mode) {
it->UpdateProtector();
Isolate* isolate = it->isolate();
if (it->state() == LookupIterator::JSPROXY) {
return JSProxy::DeletePropertyOrElement(it->GetHolder<JSProxy>(),
it->GetName(), language_mode);
}
if (IsJSProxy(*it->GetReceiver())) {
if (it->state() != LookupIterator::NOT_FOUND) {
DCHECK_EQ(LookupIterator::DATA, it->state());
DCHECK(it->name()->IsPrivate());
it->Delete();
}
return Just(true);
}
for (;; it->Next()) {
switch (it->state()) {
case LookupIterator::JSPROXY:
case LookupIterator::TRANSITION:
case LookupIterator::STRING_LOOKUP_START_OBJECT:
UNREACHABLE();
case LookupIterator::WASM_OBJECT:
RETURN_FAILURE(isolate, kThrowOnError,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
case LookupIterator::ACCESS_CHECK:
if (it->HasAccess()) continue;
RETURN_ON_EXCEPTION_VALUE(
isolate,
isolate->ReportFailedAccessCheck(it->GetHolder<JSObject>()),
Nothing<bool>());
UNREACHABLE();
case LookupIterator::INTERCEPTOR: {
ShouldThrow should_throw =
is_sloppy(language_mode) ? kDontThrow : kThrowOnError;
InterceptorResult result;
if (!JSObject::DeletePropertyWithInterceptor(it, should_throw)
.To(&result)) {
return Nothing<bool>();
}
switch (result) {
case InterceptorResult::kFalse: {
RETURN_FAILURE(
isolate, should_throw,
NewTypeError(MessageTemplate::kStrictCannotDeleteProperty,
it->GetName(), it->GetReceiver()));
}
case InterceptorResult::kTrue:
return Just(true);
case InterceptorResult::kNotIntercepted:
continue;
}
UNREACHABLE();
}
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
return Just(true);
case LookupIterator::DATA:
case LookupIterator::ACCESSOR: {
DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
if (!it->IsConfigurable() ||
(IsJSTypedArray(*holder) && it->IsElement(*holder))) {
ShouldThrow should_throw =
is_sloppy(language_mode) ? kDontThrow : kThrowOnError;
RETURN_FAILURE(
isolate, should_throw,
NewTypeError(MessageTemplate::kStrictCannotDeleteProperty,
it->GetName(), it->GetReceiver()));
}
it->Delete();
return Just(true);
}
case LookupIterator::NOT_FOUND:
return Just(true);
}
UNREACHABLE();
}
}
Maybe<bool> JSReceiver::DeleteElement(Isolate* isolate,
DirectHandle<JSReceiver> object,
uint32_t index,
LanguageMode language_mode) {
LookupIterator it(isolate, object, index, object, LookupIterator::OWN);
return DeleteProperty(&it, language_mode);
}
Maybe<bool> JSReceiver::DeleteProperty(Isolate* isolate,
DirectHandle<JSReceiver> object,
DirectHandle<Name> name,
LanguageMode language_mode) {
LookupIterator it(isolate, object, name, object, LookupIterator::OWN);
return DeleteProperty(&it, language_mode);
}
Maybe<bool> JSReceiver::DeletePropertyOrElement(Isolate* isolate,
DirectHandle<JSReceiver> object,
DirectHandle<Name> name,
LanguageMode language_mode) {
return DeletePropertyOrElement(isolate, object, PropertyKey(isolate, name),
language_mode);
}
Maybe<bool> JSReceiver::DeletePropertyOrElement(Isolate* isolate,
DirectHandle<JSReceiver> object,
PropertyKey key,
LanguageMode language_mode) {
LookupIterator it(isolate, object, key, object, LookupIterator::OWN);
return DeleteProperty(&it, language_mode);
}
Tagged<Object> JSReceiver::DefineProperty(Isolate* isolate,
DirectHandle<Object> object,
DirectHandle<Object> key,
Handle<Object> attributes) {
if (!IsJSReceiver(*object)) {
DirectHandle<String> fun_name =
isolate->factory()->InternalizeUtf8String("Object.defineProperty");
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, fun_name));
}
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, key,
Object::ToPropertyKey(isolate, key));
PropertyDescriptor desc;
if (!PropertyDescriptor::ToPropertyDescriptor(
isolate, Cast<JSAny>(attributes), &desc)) {
return ReadOnlyRoots(isolate).exception();
}
Maybe<bool> success = DefineOwnProperty(isolate, Cast<JSReceiver>(object),
key, &desc, Just(kThrowOnError));
MAYBE_RETURN(success, ReadOnlyRoots(isolate).exception());
CHECK(success.FromJust());
return *object;
}
MaybeDirectHandle<Object> JSReceiver::DefineProperties(
Isolate* isolate, DirectHandle<Object> object,
DirectHandle<Object> properties) {
if (!IsJSReceiver(*object)) {
DirectHandle<String> fun_name =
isolate->factory()->InternalizeUtf8String("Object.defineProperties");
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, fun_name));
}
DirectHandle<JSReceiver> props;
ASSIGN_RETURN_ON_EXCEPTION(isolate, props,
Object::ToObject(isolate, properties));
DirectHandle<FixedArray> keys;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, keys,
KeyAccumulator::GetKeys(isolate, props, KeyCollectionMode::kOwnOnly,
ALL_PROPERTIES));
int capacity = keys->length();
std::vector<PropertyDescriptor> descriptors(capacity);
size_t descriptors_index = 0;
for (int i = 0; i < keys->length(); ++i) {
DirectHandle<JSAny> next_key(Cast<JSAny>(keys->get(i)), isolate);
PropertyKey key(isolate, next_key);
LookupIterator it(isolate, props, key, LookupIterator::OWN);
Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
if (maybe.IsNothing()) return MaybeDirectHandle<Object>();
PropertyAttributes attrs = maybe.FromJust();
if (attrs == ABSENT) continue;
if (attrs & DONT_ENUM) continue;
Handle<JSAny> desc_obj;
ASSIGN_RETURN_ON_EXCEPTION(isolate, desc_obj,
Cast<JSAny>(Object::GetProperty(&it)));
bool success = PropertyDescriptor::ToPropertyDescriptor(
isolate, desc_obj, &descriptors[descriptors_index]);
if (!success) return MaybeDirectHandle<Object>();
descriptors[descriptors_index].set_name(next_key);
descriptors_index++;
}
for (size_t i = 0; i < descriptors_index; ++i) {
PropertyDescriptor* desc = &descriptors[i];
Maybe<bool> status =
DefineOwnProperty(isolate, Cast<JSReceiver>(object), desc->name(), desc,
Just(kThrowOnError));
if (status.IsNothing()) return MaybeDirectHandle<Object>();
CHECK(status.FromJust());
}
return object;
}
Maybe<bool> JSReceiver::DefineOwnProperty(Isolate* isolate,
DirectHandle<JSReceiver> object,
DirectHandle<Object> key,
PropertyDescriptor* desc,
Maybe<ShouldThrow> should_throw) {
if (IsJSArray(*object)) {
return JSArray::DefineOwnProperty(isolate, Cast<JSArray>(object), key, desc,
should_throw);
}
if (IsJSProxy(*object)) {
return JSProxy::DefineOwnProperty(isolate, Cast<JSProxy>(object), key, desc,
should_throw);
}
if (IsJSTypedArray(*object)) {
return JSTypedArray::DefineOwnProperty(isolate, Cast<JSTypedArray>(object),
key, desc, should_throw);
}
if (IsJSModuleNamespace(*object)) {
return JSModuleNamespace::DefineOwnProperty(
isolate, Cast<JSModuleNamespace>(object), key, desc, should_throw);
}
if (IsWasmObject(*object)) {
RETURN_FAILURE(isolate, kThrowOnError,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
}
if (IsAlwaysSharedSpaceJSObject(*object)) {
return AlwaysSharedSpaceJSObject::DefineOwnProperty(
isolate, Cast<AlwaysSharedSpaceJSObject>(object), key, desc,
should_throw);
}
return OrdinaryDefineOwnProperty(isolate, Cast<JSObject>(object), key, desc,
should_throw);
}
Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(
Isolate* isolate, DirectHandle<JSObject> object, DirectHandle<Object> key,
PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw) {
DCHECK(IsName(*key) || IsNumber(*key));
PropertyKey lookup_key(isolate, key);
return OrdinaryDefineOwnProperty(isolate, object, lookup_key, desc,
should_throw);
}
namespace {
MaybeHandle<JSAny> GetPropertyWithInterceptorInternal(
LookupIterator* it, DirectHandle<InterceptorInfo> interceptor, bool* done) {
*done = false;
Isolate* isolate = it->isolate();
AssertNoContextChange ncc(isolate);
if (!interceptor->has_getter()) {
return isolate->factory()->undefined_value();
}
DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
DirectHandle<Object> receiver = it->GetReceiver();
if (!IsJSReceiver(*receiver)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
Object::ConvertReceiver(isolate, receiver));
}
PropertyCallbackArguments args(isolate, *receiver, it->GetHolderForApi(),
Just(kDontThrow));
DirectHandle<JSAny> result;
if (it->IsElement(*holder)) {
result = args.CallIndexedGetter(interceptor, it->array_index());
} else {
result = args.CallNamedGetter(interceptor, it->name());
}
RETURN_VALUE_IF_EXCEPTION_DETECTOR(isolate, args, kNullMaybeHandle);
if (result.is_null()) return isolate->factory()->undefined_value();
*done = true;
args.AcceptSideEffects();
return handle(*result, isolate);
}
Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal(
LookupIterator* it, DirectHandle<InterceptorInfo> interceptor) {
Isolate* isolate = it->isolate();
AssertNoContextChange ncc(isolate);
HandleScope scope(isolate);
DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
DCHECK_IMPLIES(!it->IsElement(*holder) && IsSymbol(*it->name()),
interceptor->can_intercept_symbols());
DirectHandle<Object> receiver = it->GetReceiver();
if (!IsJSReceiver(*receiver)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
Object::ConvertReceiver(isolate, receiver));
}
PropertyCallbackArguments args(isolate, *receiver, it->GetHolderForApi(),
Just(kDontThrow));
if (interceptor->has_query()) {
DirectHandle<Object> result;
if (it->IsElement(*holder)) {
result = args.CallIndexedQuery(interceptor, it->array_index());
} else {
result = args.CallNamedQuery(interceptor, it->name());
}
RETURN_VALUE_IF_EXCEPTION_DETECTOR(isolate, args,
Nothing<PropertyAttributes>());
if (!result.is_null()) {
int32_t value;
CHECK(Object::ToInt32(*result, &value));
DCHECK_IMPLIES((value & ~PropertyAttributes::ALL_ATTRIBUTES_MASK) != 0,
value == PropertyAttributes::ABSENT);
if (value != PropertyAttributes::ABSENT) {
args.AcceptSideEffects();
}
return Just(static_cast<PropertyAttributes>(value));
}
} else if (interceptor->has_getter()) {
DirectHandle<Object> result;
if (it->IsElement(*holder)) {
result = args.CallIndexedGetter(interceptor, it->array_index());
} else {
result = args.CallNamedGetter(interceptor, it->name());
}
RETURN_VALUE_IF_EXCEPTION_DETECTOR(isolate, args,
Nothing<PropertyAttributes>());
if (!result.is_null()) {
args.AcceptSideEffects();
return Just(DONT_ENUM);
}
}
return Just(ABSENT);
}
Maybe<InterceptorResult> SetPropertyWithInterceptorInternal(
LookupIterator* it, DirectHandle<InterceptorInfo> interceptor,
Maybe<ShouldThrow> should_throw, DirectHandle<Object> value) {
Isolate* isolate = it->isolate();
AssertNoContextChange ncc(isolate);
if (!interceptor->has_setter()) {
return Just(InterceptorResult::kNotIntercepted);
}
DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
DirectHandle<Object> receiver = it->GetReceiver();
if (!IsJSReceiver(*receiver)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
Object::ConvertReceiver(isolate, receiver));
}
PropertyCallbackArguments args(isolate, *receiver, it->GetHolderForApi(),
should_throw);
v8::Intercepted intercepted =
it->IsElement(*holder)
? args.CallIndexedSetter(interceptor, it->array_index(), value)
: args.CallNamedSetter(interceptor, it->name(), value);
return args.GetBooleanReturnValue(intercepted, "Setter");
}
Maybe<InterceptorResult> DefinePropertyWithInterceptorInternal(
LookupIterator* it, DirectHandle<InterceptorInfo> interceptor,
Maybe<ShouldThrow> should_throw, PropertyDescriptor* desc) {
Isolate* isolate = it->isolate();
AssertNoContextChange ncc(isolate);
if (!interceptor->has_definer()) {
return Just(InterceptorResult::kNotIntercepted);
}
DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
DirectHandle<Object> receiver = it->GetReceiver();
if (!IsJSReceiver(*receiver)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
Object::ConvertReceiver(isolate, receiver));
}
std::unique_ptr<v8::PropertyDescriptor> descriptor(
new v8::PropertyDescriptor());
if (PropertyDescriptor::IsAccessorDescriptor(desc)) {
DirectHandle<Object> getter = desc->get();
if (!getter.is_null() && IsFunctionTemplateInfo(*getter)) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, getter,
ApiNatives::InstantiateFunction(isolate,
Cast<FunctionTemplateInfo>(getter),
MaybeDirectHandle<Name>()));
}
DirectHandle<Object> setter = desc->set();
if (!setter.is_null() && IsFunctionTemplateInfo(*setter)) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, setter,
ApiNatives::InstantiateFunction(isolate,
Cast<FunctionTemplateInfo>(setter),
MaybeDirectHandle<Name>()));
}
descriptor.reset(new v8::PropertyDescriptor(v8::Utils::ToLocal(getter),
v8::Utils::ToLocal(setter)));
} else if (PropertyDescriptor::IsDataDescriptor(desc)) {
if (desc->has_writable()) {
descriptor.reset(new v8::PropertyDescriptor(
v8::Utils::ToLocal(desc->value()), desc->writable()));
} else {
descriptor.reset(
new v8::PropertyDescriptor(v8::Utils::ToLocal(desc->value())));
}
}
if (desc->has_enumerable()) {
descriptor->set_enumerable(desc->enumerable());
}
if (desc->has_configurable()) {
descriptor->set_configurable(desc->configurable());
}
PropertyCallbackArguments args(isolate, *receiver, it->GetHolderForApi(),
should_throw);
v8::Intercepted intercepted =
it->IsElement(*holder)
? args.CallIndexedDefiner(interceptor, it->array_index(), *descriptor)
: args.CallNamedDefiner(interceptor, it->name(), *descriptor);
return args.GetBooleanReturnValue(intercepted, "Definer");
}
}
Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(
Isolate* isolate, DirectHandle<JSObject> object, const PropertyKey& key,
PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw) {
LookupIterator it(isolate, object, key, LookupIterator::OWN);
while (it.state() == LookupIterator::ACCESS_CHECK) {
if (!it.HasAccess()) {
RETURN_ON_EXCEPTION_VALUE(
isolate, isolate->ReportFailedAccessCheck(it.GetHolder<JSObject>()),
Nothing<bool>());
UNREACHABLE();
}
it.Next();
}
PropertyDescriptor current;
MAYBE_RETURN(GetOwnPropertyDescriptor(&it, ¤t), Nothing<bool>());
it.Restart();
while (it.state() == LookupIterator::ACCESS_CHECK) {
DCHECK(it.HasAccess());
it.Next();
}
if (it.state() == LookupIterator::INTERCEPTOR) {
if (it.HolderIsReceiverOrHiddenPrototype()) {
InterceptorResult result;
if (!DefinePropertyWithInterceptorInternal(&it, it.GetInterceptor(),
should_throw, desc)
.To(&result)) {
return Nothing<bool>();
}
switch (result) {
case InterceptorResult::kFalse: {
return Object::CannotCreateProperty(isolate, it.GetReceiver(),
it.GetName(), should_throw);
}
case InterceptorResult::kTrue:
return Just(true);
case InterceptorResult::kNotIntercepted:
break;
}
it.Restart();
}
}
bool extensible = JSObject::IsExtensible(isolate, object);
return ValidateAndApplyPropertyDescriptor(isolate, &it, extensible, desc,
¤t, should_throw, {});
}
Maybe<bool> JSReceiver::IsCompatiblePropertyDescriptor(
Isolate* isolate, bool extensible, PropertyDescriptor* desc,
PropertyDescriptor* current, DirectHandle<Name> property_name,
Maybe<ShouldThrow> should_throw) {
return ValidateAndApplyPropertyDescriptor(
isolate, nullptr, extensible, desc, current, should_throw, property_name);
}
Maybe<bool> JSReceiver::ValidateAndApplyPropertyDescriptor(
Isolate* isolate, LookupIterator* it, bool extensible,
PropertyDescriptor* desc, PropertyDescriptor* current,
Maybe<ShouldThrow> should_throw, DirectHandle<Name> property_name) {
DCHECK((it == nullptr) != property_name.is_null());
bool desc_is_data_descriptor = PropertyDescriptor::IsDataDescriptor(desc);
bool desc_is_accessor_descriptor =
PropertyDescriptor::IsAccessorDescriptor(desc);
bool desc_is_generic_descriptor =
PropertyDescriptor::IsGenericDescriptor(desc);
if (current->is_empty()) {
if (!extensible) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kDefineDisallowed,
it != nullptr ? it->GetName() : property_name));
}
DCHECK_EQ(desc_is_generic_descriptor || desc_is_data_descriptor,
!desc_is_accessor_descriptor);
if (!desc_is_accessor_descriptor) {
if (it != nullptr) {
if (!desc->has_writable()) desc->set_writable(false);
if (!desc->has_enumerable()) desc->set_enumerable(false);
if (!desc->has_configurable()) desc->set_configurable(false);
DirectHandle<Object> value(
desc->has_value()
? desc->value()
: Cast<Object>(isolate->factory()->undefined_value()));
MaybeDirectHandle<Object> result =
JSObject::DefineOwnPropertyIgnoreAttributes(it, value,
desc->ToAttributes());
if (result.is_null()) return Nothing<bool>();
}
} else {
DCHECK(desc_is_accessor_descriptor);
if (it != nullptr) {
if (!desc->has_enumerable()) desc->set_enumerable(false);
if (!desc->has_configurable()) desc->set_configurable(false);
DirectHandle<Object> getter(
desc->has_get() ? desc->get()
: Cast<Object>(isolate->factory()->null_value()));
DirectHandle<Object> setter(
desc->has_set() ? desc->set()
: Cast<Object>(isolate->factory()->null_value()));
MaybeDirectHandle<Object> result =
JSObject::DefineOwnAccessorIgnoreAttributes(it, getter, setter,
desc->ToAttributes());
if (result.is_null()) return Nothing<bool>();
}
}
return Just(true);
}
if ((!desc->has_enumerable() ||
desc->enumerable() == current->enumerable()) &&
(!desc->has_configurable() ||
desc->configurable() == current->configurable()) &&
!desc->has_value() &&
(!desc->has_writable() ||
(current->has_writable() && current->writable() == desc->writable())) &&
(!desc->has_get() ||
(current->has_get() &&
Object::SameValue(*current->get(), *desc->get()))) &&
(!desc->has_set() ||
(current->has_set() &&
Object::SameValue(*current->set(), *desc->set())))) {
return Just(true);
}
if (!current->configurable()) {
if (desc->has_configurable() && desc->configurable()) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed,
it != nullptr ? it->GetName() : property_name));
}
if (desc->has_enumerable() && desc->enumerable() != current->enumerable()) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed,
it != nullptr ? it->GetName() : property_name));
}
}
bool current_is_data_descriptor =
PropertyDescriptor::IsDataDescriptor(current);
if (desc_is_generic_descriptor) {
} else if (current_is_data_descriptor != desc_is_data_descriptor) {
if (!current->configurable()) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed,
it != nullptr ? it->GetName() : property_name));
}
if (current_is_data_descriptor) {
} else {
}
} else if (current_is_data_descriptor && desc_is_data_descriptor) {
if (!current->configurable() && !current->writable()) {
if (desc->has_writable() && desc->writable()) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed,
it != nullptr ? it->GetName() : property_name));
}
if (desc->has_value()) {
if (Object::SameValue(*desc->value(), *current->value()))
return Just(true);
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed,
it != nullptr ? it->GetName() : property_name));
}
}
} else {
DCHECK(PropertyDescriptor::IsAccessorDescriptor(current) &&
desc_is_accessor_descriptor);
if (!current->configurable()) {
if (desc->has_set() &&
!Object::SameValue(*desc->set(), *current->set())) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed,
it != nullptr ? it->GetName() : property_name));
}
if (desc->has_get() &&
!Object::SameValue(*desc->get(), *current->get())) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed,
it != nullptr ? it->GetName() : property_name));
}
}
}
if (it != nullptr) {
PropertyAttributes attrs = NONE;
if (desc->has_enumerable()) {
attrs = static_cast<PropertyAttributes>(
attrs | (desc->enumerable() ? NONE : DONT_ENUM));
} else {
attrs = static_cast<PropertyAttributes>(
attrs | (current->enumerable() ? NONE : DONT_ENUM));
}
if (desc->has_configurable()) {
attrs = static_cast<PropertyAttributes>(
attrs | (desc->configurable() ? NONE : DONT_DELETE));
} else {
attrs = static_cast<PropertyAttributes>(
attrs | (current->configurable() ? NONE : DONT_DELETE));
}
if (desc_is_data_descriptor ||
(desc_is_generic_descriptor && current_is_data_descriptor)) {
if (desc->has_writable()) {
attrs = static_cast<PropertyAttributes>(
attrs | (desc->writable() ? NONE : READ_ONLY));
} else {
attrs = static_cast<PropertyAttributes>(
attrs | (current->writable() ? NONE : READ_ONLY));
}
DirectHandle<Object> value(
desc->has_value() ? desc->value()
: current->has_value()
? current->value()
: Cast<Object>(isolate->factory()->undefined_value()));
return JSObject::DefineOwnPropertyIgnoreAttributes(
it, value, attrs, should_throw, JSObject::DONT_FORCE_FIELD,
EnforceDefineSemantics::kSet, StoreOrigin::kNamed,
current->has_value() ? current->value()
: MaybeDirectHandle<Object>());
} else {
DCHECK(desc_is_accessor_descriptor ||
(desc_is_generic_descriptor &&
PropertyDescriptor::IsAccessorDescriptor(current)));
DirectHandle<Object> getter(
desc->has_get() ? desc->get()
: current->has_get()
? current->get()
: Cast<Object>(isolate->factory()->null_value()));
DirectHandle<Object> setter(
desc->has_set() ? desc->set()
: current->has_set()
? current->set()
: Cast<Object>(isolate->factory()->null_value()));
MaybeDirectHandle<Object> result =
JSObject::DefineOwnAccessorIgnoreAttributes(it, getter, setter,
attrs);
if (result.is_null()) return Nothing<bool>();
}
}
return Just(true);
}
Maybe<bool> JSReceiver::CreateDataProperty(Isolate* isolate,
DirectHandle<JSReceiver> object,
DirectHandle<Name> key,
DirectHandle<Object> value,
Maybe<ShouldThrow> should_throw) {
return CreateDataProperty(isolate, object, PropertyKey(isolate, key), value,
should_throw);
}
Maybe<bool> JSReceiver::CreateDataProperty(Isolate* isolate,
DirectHandle<JSAny> object,
PropertyKey key,
DirectHandle<Object> value,
Maybe<ShouldThrow> should_throw) {
if (!IsJSReceiver(*object)) {
return Object::CannotCreateProperty(isolate, object, key.GetName(isolate),
should_throw);
}
return CreateDataProperty(isolate, Cast<JSReceiver>(object), key, value,
should_throw);
}
Maybe<bool> JSReceiver::CreateDataProperty(Isolate* isolate,
DirectHandle<JSReceiver> object,
PropertyKey key,
DirectHandle<Object> value,
Maybe<ShouldThrow> should_throw) {
if (IsJSObject(*object)) {
return JSObject::CreateDataProperty(isolate, Cast<JSObject>(object), key,
value, should_throw);
}
PropertyDescriptor new_desc;
new_desc.set_value(Cast<JSAny>(value));
new_desc.set_writable(true);
new_desc.set_enumerable(true);
new_desc.set_configurable(true);
return JSReceiver::DefineOwnProperty(isolate, object, key.GetName(isolate),
&new_desc, should_throw);
}
Maybe<bool> JSReceiver::AddPrivateField(LookupIterator* it,
DirectHandle<Object> value,
Maybe<ShouldThrow> should_throw) {
DirectHandle<JSReceiver> receiver = Cast<JSReceiver>(it->GetReceiver());
DCHECK(!IsAlwaysSharedSpaceJSObject(*receiver));
Isolate* isolate = it->isolate();
DCHECK(it->GetName()->IsPrivateName());
DirectHandle<Symbol> symbol = Cast<Symbol>(it->GetName());
switch (it->state()) {
case LookupIterator::JSPROXY: {
PropertyDescriptor new_desc;
new_desc.set_value(Cast<JSAny>(value));
new_desc.set_writable(true);
new_desc.set_enumerable(true);
new_desc.set_configurable(true);
return JSProxy::SetPrivateSymbol(isolate, Cast<JSProxy>(receiver), symbol,
&new_desc, should_throw);
}
case LookupIterator::WASM_OBJECT:
RETURN_FAILURE(isolate, kThrowOnError,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
case LookupIterator::DATA:
case LookupIterator::INTERCEPTOR:
case LookupIterator::ACCESSOR:
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
case LookupIterator::STRING_LOOKUP_START_OBJECT:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK: {
if (!it->HasAccess()) {
RETURN_ON_EXCEPTION_VALUE(
isolate,
it->isolate()->ReportFailedAccessCheck(it->GetHolder<JSObject>()),
Nothing<bool>());
UNREACHABLE();
}
break;
}
case LookupIterator::TRANSITION:
case LookupIterator::NOT_FOUND:
break;
}
return Object::TransitionAndWriteDataProperty(it, value, NONE, should_throw,
StoreOrigin::kMaybeKeyed);
}
Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(
Isolate* isolate, DirectHandle<JSReceiver> object, DirectHandle<Object> key,
PropertyDescriptor* desc) {
DCHECK(IsName(*key) || IsNumber(*key));
PropertyKey lookup_key(isolate, key);
LookupIterator it(isolate, object, lookup_key, LookupIterator::OWN);
return GetOwnPropertyDescriptor(&it, desc);
}
namespace {
Maybe<bool> GetPropertyDescriptorWithInterceptor(LookupIterator* it,
PropertyDescriptor* desc) {
DirectHandle<InterceptorInfo> interceptor;
while (it->state() == LookupIterator::ACCESS_CHECK) {
if (it->HasAccess()) {
it->Next();
} else {
interceptor = it->GetInterceptorForFailedAccessCheck();
if (interceptor.is_null()) {
it->Restart();
return Just(false);
}
CHECK(!interceptor.is_null());
break;
}
}
if (it->state() == LookupIterator::INTERCEPTOR) {
interceptor = it->GetInterceptor();
}
if (interceptor.is_null()) return Just(false);
Isolate* isolate = it->isolate();
if (!interceptor->has_descriptor()) return Just(false);
Handle<JSAny> result;
DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
DirectHandle<Object> receiver = it->GetReceiver();
if (!IsJSReceiver(*receiver)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
Object::ConvertReceiver(isolate, receiver));
}
PropertyCallbackArguments args(isolate, *receiver, it->GetHolderForApi(),
Just(kDontThrow));
if (it->IsElement(*holder)) {
result = args.CallIndexedDescriptor(interceptor, it->array_index());
} else {
result = args.CallNamedDescriptor(interceptor, it->name());
}
RETURN_VALUE_IF_EXCEPTION_DETECTOR(isolate, args, Nothing<bool>());
if (!result.is_null()) {
args.AcceptSideEffects();
bool is_descriptor =
PropertyDescriptor::ToPropertyDescriptor(isolate, result, desc);
return is_descriptor ? Just(true) : Nothing<bool>();
}
it->Next();
return Just(false);
}
}
Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(LookupIterator* it,
PropertyDescriptor* desc) {
Isolate* isolate = it->isolate();
if (it->IsFound() && IsJSProxy(*it->GetHolder<JSReceiver>())) {
return JSProxy::GetOwnPropertyDescriptor(isolate, it->GetHolder<JSProxy>(),
it->GetName(), desc);
}
Maybe<bool> intercepted = GetPropertyDescriptorWithInterceptor(it, desc);
MAYBE_RETURN(intercepted, Nothing<bool>());
if (intercepted.FromJust()) {
return Just(true);
}
Maybe<PropertyAttributes> maybe = JSObject::GetPropertyAttributes(it);
MAYBE_RETURN(maybe, Nothing<bool>());
PropertyAttributes attrs = maybe.FromJust();
if (attrs == ABSENT) return Just(false);
DCHECK(!isolate->has_exception());
DCHECK(desc->is_empty());
bool is_accessor_pair = it->state() == LookupIterator::ACCESSOR &&
IsAccessorPair(*it->GetAccessors());
if (!is_accessor_pair) {
DirectHandle<JSAny> value;
if (!Cast<JSAny>(Object::GetProperty(it)).ToHandle(&value)) {
DCHECK(isolate->has_exception());
return Nothing<bool>();
}
desc->set_value(value);
desc->set_writable((attrs & READ_ONLY) == 0);
} else {
auto accessors = Cast<AccessorPair>(it->GetAccessors());
DirectHandle<NativeContext> holder_realm(
it->GetHolder<JSReceiver>()->GetCreationContext().value(), isolate);
desc->set_get(AccessorPair::GetComponent(isolate, holder_realm, accessors,
ACCESSOR_GETTER));
desc->set_set(AccessorPair::GetComponent(isolate, holder_realm, accessors,
ACCESSOR_SETTER));
}
desc->set_enumerable((attrs & DONT_ENUM) == 0);
desc->set_configurable((attrs & DONT_DELETE) == 0);
DCHECK(PropertyDescriptor::IsAccessorDescriptor(desc) !=
PropertyDescriptor::IsDataDescriptor(desc));
return Just(true);
}
Maybe<bool> JSReceiver::SetIntegrityLevel(Isolate* isolate,
DirectHandle<JSReceiver> receiver,
IntegrityLevel level,
ShouldThrow should_throw) {
DCHECK(level == SEALED || level == FROZEN);
if (IsJSObject(*receiver)) {
DirectHandle<JSObject> object = Cast<JSObject>(receiver);
if (!object->HasSloppyArgumentsElements() &&
!IsJSModuleNamespace(*object)) {
Maybe<bool> test = JSObject::TestIntegrityLevel(isolate, object, level);
MAYBE_RETURN(test, Nothing<bool>());
if (test.FromJust()) return test;
if (level == SEALED) {
return JSObject::PreventExtensionsWithTransition<SEALED>(
isolate, object, should_throw);
} else {
return JSObject::PreventExtensionsWithTransition<FROZEN>(
isolate, object, should_throw);
}
}
}
MAYBE_RETURN(JSReceiver::PreventExtensions(isolate, receiver, should_throw),
Nothing<bool>());
DirectHandle<FixedArray> keys;
ASSIGN_RETURN_ON_EXCEPTION(isolate, keys,
JSReceiver::OwnPropertyKeys(isolate, receiver));
PropertyDescriptor no_conf;
no_conf.set_configurable(false);
PropertyDescriptor no_conf_no_write;
no_conf_no_write.set_configurable(false);
no_conf_no_write.set_writable(false);
if (level == SEALED) {
for (int i = 0; i < keys->length(); ++i) {
DirectHandle<Object> key(keys->get(i), isolate);
MAYBE_RETURN(DefineOwnProperty(isolate, receiver, key, &no_conf,
Just(kThrowOnError)),
Nothing<bool>());
}
return Just(true);
}
for (int i = 0; i < keys->length(); ++i) {
DirectHandle<Object> key(keys->get(i), isolate);
PropertyDescriptor current_desc;
Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor(
isolate, receiver, key, ¤t_desc);
MAYBE_RETURN(owned, Nothing<bool>());
if (owned.FromJust()) {
PropertyDescriptor desc =
PropertyDescriptor::IsAccessorDescriptor(¤t_desc)
? no_conf
: no_conf_no_write;
MAYBE_RETURN(
DefineOwnProperty(isolate, receiver, key, &desc, Just(kThrowOnError)),
Nothing<bool>());
}
}
return Just(true);
}
namespace {
Maybe<bool> GenericTestIntegrityLevel(Isolate* isolate,
DirectHandle<JSReceiver> receiver,
PropertyAttributes level) {
DCHECK(level == SEALED || level == FROZEN);
Maybe<bool> extensible = JSReceiver::IsExtensible(isolate, receiver);
MAYBE_RETURN(extensible, Nothing<bool>());
if (extensible.FromJust()) return Just(false);
DirectHandle<FixedArray> keys;
ASSIGN_RETURN_ON_EXCEPTION(isolate, keys,
JSReceiver::OwnPropertyKeys(isolate, receiver));
for (int i = 0; i < keys->length(); ++i) {
DirectHandle<Object> key(keys->get(i), isolate);
PropertyDescriptor current_desc;
Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor(
isolate, receiver, key, ¤t_desc);
MAYBE_RETURN(owned, Nothing<bool>());
if (owned.FromJust()) {
if (current_desc.configurable()) return Just(false);
if (level == FROZEN &&
PropertyDescriptor::IsDataDescriptor(¤t_desc) &&
current_desc.writable()) {
return Just(false);
}
}
}
return Just(true);
}
}
Maybe<bool> JSReceiver::TestIntegrityLevel(Isolate* isolate,
DirectHandle<JSReceiver> receiver,
IntegrityLevel level) {
if (!IsCustomElementsReceiverMap(receiver->map())) {
return JSObject::TestIntegrityLevel(isolate, Cast<JSObject>(receiver),
level);
}
return GenericTestIntegrityLevel(isolate, receiver, level);
}
Maybe<bool> JSReceiver::PreventExtensions(Isolate* isolate,
DirectHandle<JSReceiver> object,
ShouldThrow should_throw) {
if (IsJSProxy(*object)) {
return JSProxy::PreventExtensions(Cast<JSProxy>(object), should_throw);
}
if (IsWasmObject(*object)) {
RETURN_FAILURE(isolate, kThrowOnError,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
}
DCHECK(IsJSObject(*object));
return JSObject::PreventExtensions(isolate, Cast<JSObject>(object),
should_throw);
}
Maybe<bool> JSReceiver::IsExtensible(Isolate* isolate,
DirectHandle<JSReceiver> object) {
if (IsJSProxy(*object)) {
return JSProxy::IsExtensible(Cast<JSProxy>(object));
}
if (IsWasmObject(*object)) {
return Just(false);
}
return Just(JSObject::IsExtensible(isolate, Cast<JSObject>(object)));
}
template <template <typename> typename HandleType>
requires(
std::is_convertible_v<HandleType<JSReceiver>, DirectHandle<JSReceiver>>)
typename HandleType<Object>::MaybeType JSReceiver::ToPrimitive(
Isolate* isolate, HandleType<JSReceiver> receiver, ToPrimitiveHint hint) {
DirectHandle<Object> exotic_to_prim;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, exotic_to_prim,
Object::GetMethod(isolate, receiver,
isolate->factory()->to_primitive_symbol()));
if (!IsUndefined(*exotic_to_prim, isolate)) {
DirectHandle<Object> hint_string =
isolate->factory()->ToPrimitiveHintString(hint);
HandleType<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
Execution::Call(isolate, exotic_to_prim, receiver, {&hint_string, 1}));
if (IsPrimitive(*result)) return result;
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kCannotConvertToPrimitive));
}
return OrdinaryToPrimitive<HandleType>(
isolate, receiver,
(hint == ToPrimitiveHint::kString) ? OrdinaryToPrimitiveHint::kString
: OrdinaryToPrimitiveHint::kNumber);
}
template MaybeDirectHandle<Object> JSReceiver::ToPrimitive(
Isolate* isolate, DirectHandle<JSReceiver> receiver, ToPrimitiveHint hint);
template MaybeIndirectHandle<Object> JSReceiver::ToPrimitive(
Isolate* isolate, IndirectHandle<JSReceiver> receiver,
ToPrimitiveHint hint);
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Object>, DirectHandle<Object>>)
typename HandleType<Object>::MaybeType JSReceiver::OrdinaryToPrimitive(
Isolate* isolate, DirectHandle<JSReceiver> receiver,
OrdinaryToPrimitiveHint hint) {
DirectHandle<String> method_names[2];
switch (hint) {
case OrdinaryToPrimitiveHint::kNumber:
method_names[0] = isolate->factory()->valueOf_string();
method_names[1] = isolate->factory()->toString_string();
break;
case OrdinaryToPrimitiveHint::kString:
method_names[0] = isolate->factory()->toString_string();
method_names[1] = isolate->factory()->valueOf_string();
break;
}
for (DirectHandle<String> name : method_names) {
DirectHandle<Object> method;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, method, JSReceiver::GetProperty(isolate, receiver, name));
if (IsCallable(*method)) {
HandleType<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result, Execution::Call(isolate, method, receiver, {}));
if (IsPrimitive(*result)) return result;
}
}
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kCannotConvertToPrimitive));
}
V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries(
Isolate* isolate, DirectHandle<JSReceiver> receiver, bool get_entries,
Handle<FixedArray>* result) {
DirectHandle<Map> map(Cast<JSReceiver>(*receiver)->map(), isolate);
if (!IsJSObjectMap(*map)) return Just(false);
if (!map->OnlyHasSimpleProperties()) return Just(false);
DirectHandle<JSObject> object(Cast<JSObject>(*receiver), isolate);
DirectHandle<DescriptorArray> descriptors(map->instance_descriptors(isolate),
isolate);
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
size_t number_of_own_elements =
object->GetElementsAccessor()->GetCapacity(*object, object->elements());
if (number_of_own_elements >
static_cast<size_t>(FixedArray::kMaxLength - number_of_own_descriptors)) {
isolate->Throw(*isolate->factory()->NewRangeError(
MessageTemplate::kInvalidArrayLength));
return Nothing<bool>();
}
Handle<FixedArray> values_or_entries = isolate->factory()->NewFixedArray(
static_cast<int>(number_of_own_descriptors + number_of_own_elements));
int count = 0;
if (object->elements() != ReadOnlyRoots(isolate).empty_fixed_array()) {
MAYBE_RETURN(object->GetElementsAccessor()->CollectValuesOrEntries(
isolate, object, values_or_entries, get_entries, &count,
ENUMERABLE_STRINGS),
Nothing<bool>());
}
bool stable = *map == object->map();
if (stable) {
descriptors.SetValue(map->instance_descriptors(isolate));
}
for (InternalIndex index : InternalIndex::Range(number_of_own_descriptors)) {
HandleScope inner_scope(isolate);
DirectHandle<Name> next_key(descriptors->GetKey(index), isolate);
if (!IsString(*next_key)) continue;
DirectHandle<Object> prop_value;
if (stable) {
DCHECK_EQ(object->map(), *map);
DCHECK_EQ(*descriptors, map->instance_descriptors(isolate));
PropertyDetails details = descriptors->GetDetails(index);
if (!details.IsEnumerable()) continue;
if (details.kind() == PropertyKind::kData) {
if (details.location() == PropertyLocation::kDescriptor) {
prop_value =
direct_handle(descriptors->GetStrongValue(index), isolate);
} else {
Representation representation = details.representation();
FieldIndex field_index = FieldIndex::ForPropertyIndex(
*map, details.field_index(), representation);
prop_value = JSObject::FastPropertyAt(isolate, object, representation,
field_index);
}
} else {
LookupIterator it(isolate, object, next_key,
LookupIterator::OWN_SKIP_INTERCEPTOR);
DCHECK_EQ(LookupIterator::ACCESSOR, it.state());
ASSIGN_RETURN_ON_EXCEPTION(isolate, prop_value,
Object::GetProperty(&it));
stable = object->map() == *map;
descriptors.SetValue(map->instance_descriptors(isolate));
}
} else {
LookupIterator it(isolate, object, next_key,
LookupIterator::OWN_SKIP_INTERCEPTOR);
if (!it.IsFound()) continue;
DCHECK(it.state() == LookupIterator::DATA ||
it.state() == LookupIterator::ACCESSOR);
if (!it.IsEnumerable()) continue;
ASSIGN_RETURN_ON_EXCEPTION(isolate, prop_value, Object::GetProperty(&it));
}
if (get_entries) {
prop_value = MakeEntryPair(isolate, next_key, prop_value);
}
values_or_entries->set(count, *prop_value);
count++;
}
DCHECK_LE(count, values_or_entries->length());
*result = FixedArray::RightTrimOrEmpty(isolate, values_or_entries, count);
return Just(true);
}
MaybeDirectHandle<FixedArray> GetOwnValuesOrEntries(
Isolate* isolate, DirectHandle<JSReceiver> object, PropertyFilter filter,
bool try_fast_path, bool get_entries) {
Handle<FixedArray> values_or_entries;
if (try_fast_path && filter == ENUMERABLE_STRINGS) {
Maybe<bool> fast_values_or_entries = FastGetOwnValuesOrEntries(
isolate, object, get_entries, &values_or_entries);
if (fast_values_or_entries.IsNothing())
return MaybeDirectHandle<FixedArray>();
if (fast_values_or_entries.FromJust()) return values_or_entries;
}
PropertyFilter key_filter =
static_cast<PropertyFilter>(filter & ~ONLY_ENUMERABLE);
DirectHandle<FixedArray> keys;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, keys,
KeyAccumulator::GetKeys(isolate, object, KeyCollectionMode::kOwnOnly,
key_filter, GetKeysConversion::kConvertToString));
values_or_entries = isolate->factory()->NewFixedArray(keys->length());
int length = 0;
for (int i = 0; i < keys->length(); ++i) {
DirectHandle<Name> key(Cast<Name>(keys->get(i)), isolate);
if (filter & ONLY_ENUMERABLE) {
PropertyDescriptor descriptor;
Maybe<bool> did_get_descriptor = JSReceiver::GetOwnPropertyDescriptor(
isolate, object, key, &descriptor);
MAYBE_RETURN(did_get_descriptor, MaybeDirectHandle<FixedArray>());
if (!did_get_descriptor.FromJust() || !descriptor.enumerable()) continue;
}
DirectHandle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value, Object::GetPropertyOrElement(isolate, object, key));
if (get_entries) {
DirectHandle<FixedArray> entry_storage =
isolate->factory()->NewFixedArray(2);
entry_storage->set(0, *key);
entry_storage->set(1, *value);
value = isolate->factory()->NewJSArrayWithElements(entry_storage,
PACKED_ELEMENTS, 2);
}
values_or_entries->set(length, *value);
length++;
}
DCHECK_LE(length, values_or_entries->length());
return FixedArray::RightTrimOrEmpty(isolate, values_or_entries, length);
}
MaybeDirectHandle<FixedArray> JSReceiver::GetOwnValues(
Isolate* isolate, DirectHandle<JSReceiver> object, PropertyFilter filter,
bool try_fast_path) {
return GetOwnValuesOrEntries(isolate, object, filter, try_fast_path, false);
}
MaybeDirectHandle<FixedArray> JSReceiver::GetOwnEntries(
Isolate* isolate, DirectHandle<JSReceiver> object, PropertyFilter filter,
bool try_fast_path) {
return GetOwnValuesOrEntries(isolate, object, filter, try_fast_path, true);
}
Maybe<bool> JSReceiver::SetPrototype(Isolate* isolate,
DirectHandle<JSReceiver> object,
DirectHandle<Object> value,
bool from_javascript,
ShouldThrow should_throw) {
if (IsWasmObject(*object)) {
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
}
if (IsJSProxy(*object)) {
return JSProxy::SetPrototype(isolate, Cast<JSProxy>(object), value,
from_javascript, should_throw);
}
return JSObject::SetPrototype(isolate, Cast<JSObject>(object), value,
from_javascript, should_throw);
}
bool JSReceiver::HasProxyInPrototype(Isolate* isolate) {
for (PrototypeIterator iter(isolate, *this, kStartAtReceiver,
PrototypeIterator::END_AT_NULL);
!iter.IsAtEnd(); iter.AdvanceIgnoringProxies()) {
if (IsJSProxy(iter.GetCurrent())) return true;
}
return false;
}
bool JSReceiver::IsCodeLike(Isolate* isolate) const {
DisallowGarbageCollection no_gc;
Tagged<Object> maybe_constructor = map()->GetConstructor();
if (!IsJSFunction(maybe_constructor)) return false;
if (!Cast<JSFunction>(maybe_constructor)->shared()->IsApiFunction()) {
return false;
}
Tagged<Object> instance_template = Cast<JSFunction>(maybe_constructor)
->shared()
->api_func_data()
->GetInstanceTemplate();
if (IsUndefined(instance_template, isolate)) return false;
return Cast<ObjectTemplateInfo>(instance_template)->code_like();
}
MaybeHandle<JSObject> JSObject::New(DirectHandle<JSFunction> constructor,
DirectHandle<JSReceiver> new_target,
DirectHandle<AllocationSite> site,
NewJSObjectType new_js_object_type) {
Isolate* const isolate = Isolate::Current();
DCHECK(IsConstructor(*constructor));
DCHECK(IsConstructor(*new_target));
DCHECK(!constructor->has_initial_map() ||
!InstanceTypeChecker::IsJSFunction(
constructor->initial_map()->instance_type()));
DirectHandle<Map> initial_map;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, initial_map,
JSFunction::GetDerivedMap(isolate, constructor, new_target));
constexpr int initial_capacity = PropertyDictionary::kInitialCapacity;
Handle<JSObject> result = isolate->factory()->NewFastOrSlowJSObjectFromMap(
initial_map, initial_capacity, AllocationType::kYoung, site,
new_js_object_type);
return result;
}
MaybeDirectHandle<JSObject> JSObject::NewWithMap(
Isolate* isolate, DirectHandle<Map> initial_map,
DirectHandle<AllocationSite> site, NewJSObjectType new_js_object_type) {
constexpr int initial_capacity = PropertyDictionary::kInitialCapacity;
DirectHandle<JSObject> result =
isolate->factory()->NewFastOrSlowJSObjectFromMap(
initial_map, initial_capacity, AllocationType::kYoung, site,
new_js_object_type);
return result;
}
MaybeDirectHandle<JSObject> JSObject::ObjectCreate(
Isolate* isolate, DirectHandle<JSPrototype> prototype) {
DirectHandle<Map> map = Map::GetObjectCreateMap(isolate, prototype);
return isolate->factory()->NewFastOrSlowJSObjectFromMap(map);
}
bool JSArray::HasReadOnlyLengthSlowPath(DirectHandle<JSArray> array) {
Isolate* isolate = Isolate::Current();
LookupIterator it(isolate, array, isolate->factory()->length_string(), array,
LookupIterator::OWN_SKIP_INTERCEPTOR);
CHECK_EQ(LookupIterator::ACCESSOR, it.state());
return it.IsReadOnly();
}
void JSObject::MakeElementsWritable(Isolate* isolate,
DirectHandle<JSObject> object) {
Tagged<FixedArray> raw_elems = Cast<FixedArray>(object->elements());
DirectHandle<FixedArray> elems(raw_elems, isolate);
DirectHandle<FixedArray> writable_elems =
isolate->factory()->CopyFixedArrayWithMap(
elems, isolate->factory()->fixed_array_map());
object->set_elements(*writable_elems);
}
static const char* NonAPIInstanceTypeToString(InstanceType instance_type) {
DCHECK(!InstanceTypeChecker::IsJSApiObject(instance_type));
switch (instance_type) {
#define WRITE_TYPE(TYPE) \
case TYPE: \
return #TYPE;
INSTANCE_TYPE_LIST(WRITE_TYPE)
#undef WRITE_TYPE
}
UNREACHABLE();
}
int JSObject::GetHeaderSize(InstanceType type,
bool function_has_prototype_slot) {
switch (type) {
case JS_SPECIAL_API_OBJECT_TYPE:
case JS_API_OBJECT_TYPE:
return JSAPIObjectWithEmbedderSlots::BodyDescriptor::kHeaderSize;
case JS_ITERATOR_PROTOTYPE_TYPE:
case JS_MAP_ITERATOR_PROTOTYPE_TYPE:
case JS_OBJECT_PROTOTYPE_TYPE:
case JS_OBJECT_TYPE:
case JS_PROMISE_PROTOTYPE_TYPE:
case JS_REG_EXP_PROTOTYPE_TYPE:
case JS_SET_ITERATOR_PROTOTYPE_TYPE:
case JS_SET_PROTOTYPE_TYPE:
case JS_STRING_ITERATOR_PROTOTYPE_TYPE:
case JS_ARRAY_ITERATOR_PROTOTYPE_TYPE:
case JS_TYPED_ARRAY_PROTOTYPE_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_ARGUMENTS_OBJECT_TYPE:
case JS_ERROR_TYPE:
return JSObject::kHeaderSize;
case JS_GENERATOR_OBJECT_TYPE:
return JSGeneratorObject::kHeaderSize;
case JS_ASYNC_FUNCTION_OBJECT_TYPE:
return JSAsyncFunctionObject::kHeaderSize;
case JS_ASYNC_GENERATOR_OBJECT_TYPE:
return JSAsyncGeneratorObject::kHeaderSize;
case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
return JSAsyncFromSyncIterator::kHeaderSize;
case JS_GLOBAL_PROXY_TYPE:
return JSGlobalProxy::kHeaderSize;
case JS_GLOBAL_OBJECT_TYPE:
return JSGlobalObject::kHeaderSize;
case JS_BOUND_FUNCTION_TYPE:
return JSBoundFunction::kHeaderSize;
case JS_FUNCTION_TYPE:
case JS_CLASS_CONSTRUCTOR_TYPE:
case JS_PROMISE_CONSTRUCTOR_TYPE:
case JS_REG_EXP_CONSTRUCTOR_TYPE:
case JS_ARRAY_CONSTRUCTOR_TYPE:
#define TYPED_ARRAY_CONSTRUCTORS_SWITCH(Type, type, TYPE, Ctype) \
case TYPE##_TYPED_ARRAY_CONSTRUCTOR_TYPE:
TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTORS_SWITCH)
#undef TYPED_ARRAY_CONSTRUCTORS_SWITCH
return JSFunction::GetHeaderSize(function_has_prototype_slot);
case JS_PRIMITIVE_WRAPPER_TYPE:
return JSPrimitiveWrapper::kHeaderSize;
case JS_DATE_TYPE:
return JSDate::kHeaderSize;
case JS_DISPOSABLE_STACK_BASE_TYPE:
return JSDisposableStackBase::kHeaderSize;
case JS_ASYNC_DISPOSABLE_STACK_TYPE:
return JSAsyncDisposableStack::kHeaderSize;
case JS_SYNC_DISPOSABLE_STACK_TYPE:
return JSSyncDisposableStack::kHeaderSize;
case JS_ARRAY_TYPE:
return JSArray::kHeaderSize;
case JS_ARRAY_BUFFER_TYPE:
return JSArrayBuffer::kHeaderSize;
case JS_ARRAY_ITERATOR_TYPE:
return JSArrayIterator::kHeaderSize;
case JS_TYPED_ARRAY_TYPE:
return JSTypedArray::kHeaderSize;
case JS_DATA_VIEW_TYPE:
return JSDataView::kHeaderSize;
case JS_RAB_GSAB_DATA_VIEW_TYPE:
return JSRabGsabDataView::kHeaderSize;
case JS_SET_TYPE:
return JSSet::kHeaderSize;
case JS_MAP_TYPE:
return JSMap::kHeaderSize;
case JS_SET_KEY_VALUE_ITERATOR_TYPE:
case JS_SET_VALUE_ITERATOR_TYPE:
return JSSetIterator::kHeaderSize;
case JS_MAP_KEY_ITERATOR_TYPE:
case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
case JS_MAP_VALUE_ITERATOR_TYPE:
return JSMapIterator::kHeaderSize;
case JS_WEAK_REF_TYPE:
return JSWeakRef::kHeaderSize;
case JS_FINALIZATION_REGISTRY_TYPE:
return JSFinalizationRegistry::kHeaderSize;
case JS_WEAK_MAP_TYPE:
return JSWeakMap::kHeaderSize;
case JS_WEAK_SET_TYPE:
return JSWeakSet::kHeaderSize;
case JS_PROMISE_TYPE:
return JSPromise::kHeaderSize;
case JS_REG_EXP_TYPE:
return JSRegExp::kHeaderSize;
case JS_REG_EXP_STRING_ITERATOR_TYPE:
return JSRegExpStringIterator::kHeaderSize;
case JS_MESSAGE_OBJECT_TYPE:
return JSMessageObject::kHeaderSize;
case JS_EXTERNAL_OBJECT_TYPE:
return JSExternalObject::kHeaderSize;
case JS_SHADOW_REALM_TYPE:
return JSShadowRealm::kHeaderSize;
case JS_STRING_ITERATOR_TYPE:
return JSStringIterator::kHeaderSize;
case JS_ITERATOR_MAP_HELPER_TYPE:
return JSIteratorMapHelper::kHeaderSize;
case JS_ITERATOR_FILTER_HELPER_TYPE:
return JSIteratorFilterHelper::kHeaderSize;
case JS_ITERATOR_TAKE_HELPER_TYPE:
return JSIteratorTakeHelper::kHeaderSize;
case JS_ITERATOR_DROP_HELPER_TYPE:
return JSIteratorDropHelper::kHeaderSize;
case JS_ITERATOR_FLAT_MAP_HELPER_TYPE:
return JSIteratorFlatMapHelper::kHeaderSize;
case JS_MODULE_NAMESPACE_TYPE:
return JSModuleNamespace::kHeaderSize;
case JS_SHARED_ARRAY_TYPE:
return JSSharedArray::kHeaderSize;
case JS_SHARED_STRUCT_TYPE:
return JSSharedStruct::kHeaderSize;
case JS_ATOMICS_MUTEX_TYPE:
return JSAtomicsMutex::kHeaderSize;
case JS_ATOMICS_CONDITION_TYPE:
return JSAtomicsCondition::kHeaderSize;
#ifdef V8_TEMPORAL_SUPPORT
case JS_TEMPORAL_DURATION_TYPE:
return JSTemporalDuration::kHeaderSize;
case JS_TEMPORAL_INSTANT_TYPE:
return JSTemporalInstant::kHeaderSize;
case JS_TEMPORAL_PLAIN_DATE_TYPE:
return JSTemporalPlainDate::kHeaderSize;
case JS_TEMPORAL_PLAIN_DATE_TIME_TYPE:
return JSTemporalPlainDateTime::kHeaderSize;
case JS_TEMPORAL_PLAIN_MONTH_DAY_TYPE:
return JSTemporalPlainMonthDay::kHeaderSize;
case JS_TEMPORAL_PLAIN_TIME_TYPE:
return JSTemporalPlainTime::kHeaderSize;
case JS_TEMPORAL_PLAIN_YEAR_MONTH_TYPE:
return JSTemporalPlainYearMonth::kHeaderSize;
case JS_TEMPORAL_ZONED_DATE_TIME_TYPE:
return JSTemporalZonedDateTime::kHeaderSize;
#endif
case JS_VALID_ITERATOR_WRAPPER_TYPE:
return JSValidIteratorWrapper::kHeaderSize;
case JS_WRAPPED_FUNCTION_TYPE:
return JSWrappedFunction::kHeaderSize;
case JS_RAW_JSON_TYPE:
return JSRawJson::kHeaderSize;
#ifdef V8_INTL_SUPPORT
case JS_V8_BREAK_ITERATOR_TYPE:
return JSV8BreakIterator::kHeaderSize;
case JS_COLLATOR_TYPE:
return JSCollator::kHeaderSize;
case JS_DATE_TIME_FORMAT_TYPE:
return JSDateTimeFormat::kHeaderSize;
case JS_DISPLAY_NAMES_TYPE:
return JSDisplayNames::kHeaderSize;
case JS_DURATION_FORMAT_TYPE:
return JSDurationFormat::kHeaderSize;
case JS_LIST_FORMAT_TYPE:
return JSListFormat::kHeaderSize;
case JS_LOCALE_TYPE:
return JSLocale::kHeaderSize;
case JS_NUMBER_FORMAT_TYPE:
return JSNumberFormat::kHeaderSize;
case JS_PLURAL_RULES_TYPE:
return JSPluralRules::kHeaderSize;
case JS_RELATIVE_TIME_FORMAT_TYPE:
return JSRelativeTimeFormat::kHeaderSize;
case JS_SEGMENT_ITERATOR_TYPE:
return JSSegmentIterator::kHeaderSize;
case JS_SEGMENTER_TYPE:
return JSSegmenter::kHeaderSize;
case JS_SEGMENTS_TYPE:
return JSSegments::kHeaderSize;
#endif
#if V8_ENABLE_WEBASSEMBLY
case WASM_GLOBAL_OBJECT_TYPE:
return WasmGlobalObject::kHeaderSize;
case WASM_INSTANCE_OBJECT_TYPE:
return WasmInstanceObject::kHeaderSize;
case WASM_MEMORY_OBJECT_TYPE:
return WasmMemoryObject::kHeaderSize;
case WASM_MEMORY_MAP_DESCRIPTOR_TYPE:
return WasmMemoryMapDescriptor::kHeaderSize;
case WASM_MODULE_OBJECT_TYPE:
return WasmModuleObject::kHeaderSize;
case WASM_TABLE_OBJECT_TYPE:
return WasmTableObject::kHeaderSize;
case WASM_VALUE_OBJECT_TYPE:
return WasmValueObject::kHeaderSize;
case WASM_TAG_OBJECT_TYPE:
return WasmTagObject::kHeaderSize;
case WASM_EXCEPTION_PACKAGE_TYPE:
return WasmExceptionPackage::kHeaderSize;
case WASM_SUSPENDING_OBJECT_TYPE:
return WasmSuspendingObject::kHeaderSize;
#endif
default: {
if (InstanceTypeChecker::IsJSApiObject(type)) {
return JSAPIObjectWithEmbedderSlots::BodyDescriptor::kHeaderSize;
}
FATAL("unexpected instance type: %s\n", NonAPIInstanceTypeToString(type));
}
}
}
MaybeHandle<JSAny> JSObject::GetPropertyWithFailedAccessCheck(
LookupIterator* it) {
Isolate* isolate = it->isolate();
DirectHandle<JSObject> checked = it->GetHolder<JSObject>();
DirectHandle<InterceptorInfo> interceptor =
it->GetInterceptorForFailedAccessCheck();
if (!interceptor.is_null()) {
Handle<JSAny> result;
bool done;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
GetPropertyWithInterceptorInternal(it, interceptor, &done));
if (done) return result;
}
DirectHandle<Name> name = it->GetName();
if (IsSymbol(*name) && Cast<Symbol>(*name)->is_well_known_symbol()) {
return it->factory()->undefined_value();
}
RETURN_ON_EXCEPTION(isolate, isolate->ReportFailedAccessCheck(checked));
UNREACHABLE();
}
Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck(
LookupIterator* it) {
Isolate* isolate = it->isolate();
DirectHandle<JSObject> checked = it->GetHolder<JSObject>();
DirectHandle<InterceptorInfo> interceptor =
it->GetInterceptorForFailedAccessCheck();
if (!interceptor.is_null()) {
Maybe<PropertyAttributes> result =
GetPropertyAttributesWithInterceptorInternal(it, interceptor);
if (isolate->has_exception()) return Nothing<PropertyAttributes>();
if (result.FromMaybe(ABSENT) != ABSENT) return result;
}
RETURN_ON_EXCEPTION_VALUE(isolate, isolate->ReportFailedAccessCheck(checked),
Nothing<PropertyAttributes>());
UNREACHABLE();
}
Maybe<bool> JSObject::SetPropertyWithFailedAccessCheck(
LookupIterator* it, DirectHandle<Object> value,
Maybe<ShouldThrow> should_throw) {
Isolate* isolate = it->isolate();
DirectHandle<JSObject> checked = it->GetHolder<JSObject>();
DirectHandle<InterceptorInfo> interceptor =
it->GetInterceptorForFailedAccessCheck();
if (!interceptor.is_null()) {
InterceptorResult result;
if (!SetPropertyWithInterceptorInternal(it, interceptor, should_throw,
value)
.To(&result)) {
return Nothing<bool>();
}
switch (result) {
case InterceptorResult::kFalse:
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kStrictCannotSetProperty,
it->GetName(), it->GetReceiver()));
case InterceptorResult::kTrue:
return Just(true);
case InterceptorResult::kNotIntercepted:
break;
}
}
RETURN_ON_EXCEPTION_VALUE(isolate, isolate->ReportFailedAccessCheck(checked),
Nothing<bool>());
UNREACHABLE();
}
void JSObject::SetNormalizedProperty(DirectHandle<JSObject> object,
DirectHandle<Name> name,
DirectHandle<Object> value,
PropertyDetails details) {
DCHECK(!object->HasFastProperties());
DCHECK(IsUniqueName(*name));
Isolate* isolate = Isolate::Current();
uint32_t hash = name->hash();
if (IsJSGlobalObject(*object)) {
auto global_obj = Cast<JSGlobalObject>(object);
DirectHandle<GlobalDictionary> dictionary(
global_obj->global_dictionary(kAcquireLoad), isolate);
ReadOnlyRoots roots(isolate);
InternalIndex entry = dictionary->FindEntry(isolate, roots, name, hash);
if (entry.is_not_found()) {
DCHECK_IMPLIES(global_obj->map()->is_prototype_map(),
!global_obj->map()->IsPrototypeValidityCellValid());
auto cell_type = IsUndefined(*value, roots) ? PropertyCellType::kUndefined
: PropertyCellType::kConstant;
details = details.set_cell_type(cell_type);
auto cell = isolate->factory()->NewPropertyCell(name, details, value);
dictionary =
GlobalDictionary::Add(isolate, dictionary, name, cell, details)
.ToHandleChecked();
global_obj->set_global_dictionary(*dictionary, kReleaseStore);
} else {
PropertyCell::PrepareForAndSetValue(isolate, dictionary, entry, value,
details);
DCHECK_EQ(dictionary->CellAt(entry)->value(), *value);
}
} else {
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
DirectHandle<SwissNameDictionary> dictionary(
object->property_dictionary_swiss(), isolate);
InternalIndex entry = dictionary->FindEntry(isolate, *name);
if (entry.is_not_found()) {
DCHECK_IMPLIES(object->map()->is_prototype_map(),
!object->map()->IsPrototypeValidityCellValid());
dictionary =
SwissNameDictionary::Add(isolate, dictionary, name, value, details);
object->SetProperties(*dictionary);
} else {
dictionary->ValueAtPut(entry, *value);
dictionary->DetailsAtPut(entry, details);
}
} else {
DirectHandle<NameDictionary> dictionary(object->property_dictionary(),
isolate);
InternalIndex entry = dictionary->FindEntry(isolate, name);
if (entry.is_not_found()) {
DCHECK_IMPLIES(object->map()->is_prototype_map(),
!object->map()->IsPrototypeValidityCellValid());
dictionary =
NameDictionary::Add(isolate, dictionary, name, value, details)
.ToHandleChecked();
object->SetProperties(*dictionary);
} else {
PropertyDetails original_details = dictionary->DetailsAt(entry);
int enumeration_index = original_details.dictionary_index();
DCHECK_GT(enumeration_index, 0);
details = details.set_index(enumeration_index);
dictionary->SetEntry(entry, *name, *value, details);
}
if (name->IsInteresting(isolate)) {
dictionary->set_may_have_interesting_properties(true);
}
}
}
}
void JSObject::SetNormalizedElement(DirectHandle<JSObject> object,
uint32_t index, DirectHandle<Object> value,
PropertyDetails details) {
DCHECK_EQ(object->GetElementsKind(), DICTIONARY_ELEMENTS);
Isolate* isolate = Isolate::Current();
DirectHandle<NumberDictionary> dictionary(
Cast<NumberDictionary>(object->elements()), isolate);
dictionary =
NumberDictionary::Set(isolate, dictionary, index, value, object, details);
object->set_elements(*dictionary);
}
void JSObject::JSObjectShortPrint(StringStream* accumulator) {
switch (map()->instance_type()) {
case JS_ARRAY_TYPE: {
double length = IsUndefined(Cast<JSArray>(*this)->length())
? 0
: Object::NumberValue(Cast<JSArray>(*this)->length());
accumulator->Add("<JSArray[%u]>", static_cast<uint32_t>(length));
break;
}
case JS_BOUND_FUNCTION_TYPE: {
Tagged<JSBoundFunction> bound_function = Cast<JSBoundFunction>(*this);
accumulator->Add("<JSBoundFunction");
accumulator->Add(" (BoundTargetFunction %p)>",
reinterpret_cast<void*>(
bound_function->bound_target_function().ptr()));
break;
}
case JS_WEAK_MAP_TYPE: {
accumulator->Add("<JSWeakMap>");
break;
}
case JS_WEAK_SET_TYPE: {
accumulator->Add("<JSWeakSet>");
break;
}
case JS_REG_EXP_TYPE: {
accumulator->Add("<JSRegExp");
Tagged<JSRegExp> regexp = Cast<JSRegExp>(*this);
if (IsString(regexp->source())) {
accumulator->Add(" ");
Cast<String>(regexp->source())->StringShortPrint(accumulator);
}
accumulator->Add(">");
break;
}
case JS_PROMISE_CONSTRUCTOR_TYPE:
case JS_REG_EXP_CONSTRUCTOR_TYPE:
case JS_ARRAY_CONSTRUCTOR_TYPE:
#define TYPED_ARRAY_CONSTRUCTORS_SWITCH(Type, type, TYPE, Ctype) \
case TYPE##_TYPED_ARRAY_CONSTRUCTOR_TYPE:
TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTORS_SWITCH)
#undef TYPED_ARRAY_CONSTRUCTORS_SWITCH
case JS_CLASS_CONSTRUCTOR_TYPE:
case JS_FUNCTION_TYPE: {
Tagged<JSFunction> function = Cast<JSFunction>(*this);
std::unique_ptr<char[]> fun_name = function->shared()->DebugNameCStr();
if (fun_name[0] != '\0') {
accumulator->Add("<JSFunction ");
accumulator->Add(fun_name.get());
} else {
accumulator->Add("<JSFunction");
}
if (v8_flags.trace_file_names) {
if (Tagged<Script> script;
TryCast<Script>(function->shared()->script(), &script)) {
Tagged<Object> source_name = script->name();
if (Tagged<String> str; TryCast<String>(source_name, &str)) {
if (str->length() > 0) {
accumulator->Add(" <");
accumulator->Put(str);
accumulator->Add(">");
}
}
}
}
accumulator->Add(" (sfi = %p)",
reinterpret_cast<void*>(function->shared().ptr()));
accumulator->Put('>');
break;
}
case JS_GENERATOR_OBJECT_TYPE: {
accumulator->Add("<JSGenerator>");
break;
}
case JS_ASYNC_FUNCTION_OBJECT_TYPE: {
accumulator->Add("<JSAsyncFunctionObject>");
break;
}
case JS_ASYNC_GENERATOR_OBJECT_TYPE: {
accumulator->Add("<JS AsyncGenerator>");
break;
}
case JS_SHARED_ARRAY_TYPE:
accumulator->Add("<JSSharedArray>");
break;
case JS_SHARED_STRUCT_TYPE:
accumulator->Add("<JSSharedStruct>");
break;
case JS_ATOMICS_MUTEX_TYPE:
accumulator->Add("<JSAtomicsMutex>");
break;
case JS_ATOMICS_CONDITION_TYPE:
accumulator->Add("<JSAtomicsCondition>");
break;
case JS_MESSAGE_OBJECT_TYPE:
accumulator->Add("<JSMessageObject>");
break;
case JS_EXTERNAL_OBJECT_TYPE:
accumulator->Add("<JSExternalObject>");
break;
case CPP_HEAP_EXTERNAL_OBJECT_TYPE:
accumulator->Add("<CppHeapExternalObject>");
break;
default: {
Tagged<Map> map_of_this = map();
Tagged<Object> constructor = map_of_this->GetConstructor();
bool printed = false;
bool is_global_proxy = IsJSGlobalProxy(*this);
if (IsJSFunction(constructor)) {
Tagged<SharedFunctionInfo> sfi =
Cast<JSFunction>(constructor)->shared();
Tagged<String> constructor_name = sfi->Name();
if (constructor_name->length() > 0) {
accumulator->Add(is_global_proxy ? "<GlobalObject " : "<");
accumulator->Put(constructor_name);
accumulator->Add(" %smap = %p",
map_of_this->is_deprecated() ? "deprecated-" : "",
map_of_this);
printed = true;
}
} else if (IsFunctionTemplateInfo(constructor)) {
accumulator->Add("<RemoteObject>");
printed = true;
}
if (!printed) {
accumulator->Add("<JS");
if (is_global_proxy) {
accumulator->Add("GlobalProxy");
} else if (IsJSGlobalObject(*this)) {
accumulator->Add("GlobalObject");
} else {
accumulator->Add("Object");
}
}
if (IsJSPrimitiveWrapper(*this)) {
accumulator->Add(" value = ");
ShortPrint(Cast<JSPrimitiveWrapper>(*this)->value(), accumulator);
}
accumulator->Put('>');
break;
}
}
}
void JSObject::PrintElementsTransition(
FILE* file, DirectHandle<JSObject> object, ElementsKind from_kind,
DirectHandle<FixedArrayBase> from_elements, ElementsKind to_kind,
DirectHandle<FixedArrayBase> to_elements) {
if (from_kind != to_kind) {
OFStream os(file);
os << "elements transition [" << ElementsKindToString(from_kind) << " -> "
<< ElementsKindToString(to_kind) << "] in ";
JavaScriptFrame::PrintTop(Isolate::Current(), file, false, true);
PrintF(file, " for ");
ShortPrint(*object, file);
PrintF(file, " from ");
ShortPrint(*from_elements, file);
PrintF(file, " to ");
ShortPrint(*to_elements, file);
PrintF(file, "\n");
}
}
void JSObject::PrintInstanceMigration(FILE* file, Tagged<Map> original_map,
Tagged<Map> new_map) {
if (new_map->is_dictionary_map()) {
PrintF(file, "[migrating to slow]\n");
return;
}
PrintF(file, "[migrating]");
Isolate* isolate = Isolate::Current();
Tagged<DescriptorArray> o = original_map->instance_descriptors(isolate);
Tagged<DescriptorArray> n = new_map->instance_descriptors(isolate);
for (InternalIndex i : original_map->IterateOwnDescriptors()) {
Representation o_r = o->GetDetails(i).representation();
Representation n_r = n->GetDetails(i).representation();
Tagged<Name> name = o->GetKey(i);
if (IsString(name)) {
Cast<String>(name)->PrintOn(file);
} else {
PrintF(file, "{symbol %p}", reinterpret_cast<void*>(name.ptr()));
}
if (!o_r.Equals(n_r)) {
PrintF(file, ":%s->%s ", o_r.Mnemonic(), n_r.Mnemonic());
} else if (o->GetDetails(i).location() == PropertyLocation::kDescriptor &&
n->GetDetails(i).location() == PropertyLocation::kField) {
PrintF(file, " ");
}
}
if (original_map->elements_kind() != new_map->elements_kind()) {
PrintF(file, "elements_kind[%i->%i]", original_map->elements_kind(),
new_map->elements_kind());
}
PrintF(file, "\n");
}
bool JSObject::IsUnmodifiedApiObject(FullObjectSlot o) {
Tagged<Object> object = *o;
if (IsSmi(object)) return false;
Tagged<HeapObject> heap_object = Cast<HeapObject>(object);
Tagged<Map> map = heap_object->map();
if (!InstanceTypeChecker::IsJSObject(map)) return false;
if (!JSObject::IsDroppableApiObject(map)) return false;
Tagged<Object> maybe_constructor = map->GetConstructor();
if (!IsJSFunction(maybe_constructor)) return false;
Tagged<JSObject> js_object = Cast<JSObject>(object);
if (js_object->elements()->length() != 0) return false;
if (!IsUndefined(js_object->GetIdentityHash())) return false;
Tagged<JSFunction> constructor = Cast<JSFunction>(maybe_constructor);
return constructor->initial_map() == map;
}
void JSObject::UpdatePrototypeUserRegistration(DirectHandle<Map> old_map,
DirectHandle<Map> new_map,
Isolate* isolate) {
DCHECK(old_map->is_prototype_map());
DCHECK(new_map->is_prototype_map());
bool was_registered = JSObject::UnregisterPrototypeUser(old_map, isolate);
new_map->set_prototype_info(old_map->prototype_info(), kReleaseStore);
old_map->set_prototype_info(Smi::zero(), kReleaseStore);
if (v8_flags.trace_prototype_users) {
PrintF("Moving prototype_info %p from map %p to map %p.\n",
reinterpret_cast<void*>(new_map->prototype_info().ptr()),
reinterpret_cast<void*>(old_map->ptr()),
reinterpret_cast<void*>(new_map->ptr()));
}
if (was_registered) {
if (new_map->has_prototype_info()) {
Cast<PrototypeInfo>(new_map->prototype_info())
->set_registry_slot(PrototypeInfo::UNREGISTERED);
}
JSObject::LazyRegisterPrototypeUser(new_map, isolate);
}
}
void JSObject::NotifyMapChange(DirectHandle<Map> old_map,
DirectHandle<Map> new_map, Isolate* isolate) {
if (!old_map->is_prototype_map()) return;
InvalidatePrototypeChains(*old_map);
UpdatePrototypeUserRegistration(old_map, new_map, isolate);
}
namespace {
void MigrateFastToFast(Isolate* isolate, DirectHandle<JSObject> object,
DirectHandle<Map> new_map) {
DirectHandle<Map> old_map(object->map(), isolate);
if (new_map->GetBackPointer(isolate) == *old_map) {
if (old_map->NumberOfOwnDescriptors() ==
new_map->NumberOfOwnDescriptors()) {
object->set_map(isolate, *new_map, kReleaseStore);
return;
}
PropertyDetails details = new_map->GetLastDescriptorDetails(isolate);
if (details.location() == PropertyLocation::kDescriptor) {
object->set_map(isolate, *new_map, kReleaseStore);
return;
}
FieldIndex index = FieldIndex::ForDetails(*new_map, details);
if (index.is_inobject() || index.outobject_array_index() <
object->property_array(isolate)->length()) {
if (index.is_double()) {
auto value = isolate->factory()->NewHeapNumberWithHoleNaN();
object->FastPropertyAtPut(index, *value);
}
object->set_map(isolate, *new_map, kReleaseStore);
return;
}
int grow_by = new_map->UnusedPropertyFields() + 1;
DirectHandle<PropertyArray> old_storage(object->property_array(isolate),
isolate);
DirectHandle<PropertyArray> new_storage =
isolate->factory()->CopyPropertyArrayAndGrow(old_storage, grow_by);
DirectHandle<Object> value;
if (details.representation().IsDouble()) {
value = isolate->factory()->NewHeapNumberWithHoleNaN();
} else {
value = isolate->factory()->uninitialized_value();
}
DCHECK_EQ(PropertyLocation::kField, details.location());
DCHECK_EQ(PropertyKind::kData, details.kind());
DCHECK(!index.is_inobject());
new_storage->set(index.outobject_array_index(), *value);
DisallowGarbageCollection no_gc;
object->SetProperties(*new_storage);
object->set_map(isolate, *new_map, kReleaseStore);
return;
}
int old_number_of_fields;
int number_of_fields = new_map->NumberOfFields(ConcurrencyMode::kSynchronous);
int inobject = new_map->GetInObjectProperties();
int unused = new_map->UnusedPropertyFields();
if (!old_map->InstancesNeedRewriting(*new_map, number_of_fields, inobject,
unused, &old_number_of_fields,
ConcurrencyMode::kSynchronous)) {
object->set_map(isolate, *new_map, kReleaseStore);
return;
}
int total_size = number_of_fields + unused;
int external = total_size - inobject;
DirectHandle<PropertyArray> array =
isolate->factory()->NewPropertyArray(external);
DirectHandle<FixedArray> inobject_props =
isolate->factory()->NewFixedArray(inobject);
DirectHandle<DescriptorArray> old_descriptors(
old_map->instance_descriptors(isolate), isolate);
DirectHandle<DescriptorArray> new_descriptors(
new_map->instance_descriptors(isolate), isolate);
int old_nof = old_map->NumberOfOwnDescriptors();
int new_nof = new_map->NumberOfOwnDescriptors();
DCHECK(old_nof <= new_nof);
for (InternalIndex i : InternalIndex::Range(old_nof)) {
PropertyDetails details = new_descriptors->GetDetails(i);
if (details.location() != PropertyLocation::kField) continue;
DCHECK_EQ(PropertyKind::kData, details.kind());
PropertyDetails old_details = old_descriptors->GetDetails(i);
Representation old_representation = old_details.representation();
Representation representation = details.representation();
Handle<UnionOf<JSAny, Hole>> value;
if (old_details.location() == PropertyLocation::kDescriptor) {
if (old_details.kind() == PropertyKind::kAccessor) {
DCHECK(!details.representation().IsNone());
if (details.representation().IsDouble()) {
value = isolate->factory()->NewHeapNumberWithHoleNaN();
} else {
value = isolate->factory()->uninitialized_value();
}
} else {
DCHECK_EQ(PropertyKind::kData, old_details.kind());
value = handle(Cast<JSAny>(old_descriptors->GetStrongValue(isolate, i)),
isolate);
DCHECK(!old_representation.IsDouble() && !representation.IsDouble());
}
} else {
DCHECK_EQ(PropertyLocation::kField, old_details.location());
FieldIndex index = FieldIndex::ForDetails(*old_map, old_details);
value = handle(object->RawFastPropertyAt(isolate, index), isolate);
if (!old_representation.IsDouble() && representation.IsDouble()) {
DCHECK_IMPLIES(old_representation.IsNone(),
IsUninitializedHole(*value, isolate));
value = Object::NewStorageFor(isolate, value, representation);
} else if (old_representation.IsDouble() && !representation.IsDouble()) {
value = Object::WrapForRead(isolate, Cast<JSAny>(value),
old_representation);
}
}
DCHECK(!(representation.IsDouble() && IsSmi(*value)));
int target_index = new_descriptors->GetFieldIndex(i);
if (target_index < inobject) {
inobject_props->set(target_index, *value);
} else {
array->set(target_index - inobject, *value);
}
}
for (InternalIndex i : InternalIndex::Range(old_nof, new_nof)) {
PropertyDetails details = new_descriptors->GetDetails(i);
if (details.location() != PropertyLocation::kField) continue;
DCHECK_EQ(PropertyKind::kData, details.kind());
DirectHandle<Object> value;
if (details.representation().IsDouble()) {
value = isolate->factory()->NewHeapNumberWithHoleNaN();
} else {
value = isolate->factory()->uninitialized_value();
}
int target_index = new_descriptors->GetFieldIndex(i);
if (target_index < inobject) {
inobject_props->set(target_index, *value);
} else {
array->set(target_index - inobject, *value);
}
}
DisallowGarbageCollection no_gc;
Heap* heap = isolate->heap();
int limit = std::min(inobject, number_of_fields);
for (int i = 0; i < limit; i++) {
FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i);
Tagged<Object> value = inobject_props->get(i);
object->FastPropertyAtPut(index, value);
}
object->SetProperties(*array);
int old_instance_size = old_map->instance_size();
int new_instance_size = new_map->instance_size();
int instance_size_delta = old_instance_size - new_instance_size;
DCHECK_GE(instance_size_delta, 0);
if (instance_size_delta > 0) {
heap->NotifyObjectSizeChange(*object, old_instance_size, new_instance_size,
ClearRecordedSlots::kYes);
}
object->set_map(isolate, *new_map, kReleaseStore);
}
void MigrateFastToSlow(Isolate* isolate, DirectHandle<JSObject> object,
DirectHandle<Map> new_map,
int expected_additional_properties) {
DCHECK(!IsJSGlobalObject(*object, isolate));
DCHECK(!IsJSGlobalProxy(*object, isolate));
DCHECK_IMPLIES(new_map->is_prototype_map(),
!new_map->IsPrototypeValidityCellValid());
HandleScope scope(isolate);
DirectHandle<Map> map(object->map(isolate), isolate);
int real_size = map->NumberOfOwnDescriptors();
int property_count = real_size;
if (expected_additional_properties > 0) {
property_count += expected_additional_properties;
} else {
constexpr int initial_capacity = PropertyDictionary::kInitialCapacity;
property_count += initial_capacity;
}
DirectHandle<NameDictionary> dictionary;
DirectHandle<SwissNameDictionary> ord_dictionary;
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
ord_dictionary = isolate->factory()->NewSwissNameDictionary(property_count);
} else {
dictionary = isolate->factory()->NewNameDictionary(property_count);
}
DirectHandle<DescriptorArray> descs(map->instance_descriptors(isolate),
isolate);
for (InternalIndex i : InternalIndex::Range(real_size)) {
PropertyDetails details = descs->GetDetails(i);
DirectHandle<Name> key(descs->GetKey(isolate, i), isolate);
DirectHandle<Object> value;
if (details.location() == PropertyLocation::kField) {
FieldIndex index = FieldIndex::ForDetails(*map, details);
if (details.kind() == PropertyKind::kData) {
value =
direct_handle(object->RawFastPropertyAt(isolate, index), isolate);
if (details.representation().IsDouble()) {
DCHECK(IsHeapNumber(*value, isolate));
double old_value = Cast<HeapNumber>(value)->value();
value = isolate->factory()->NewHeapNumber(old_value);
}
} else {
DCHECK_EQ(PropertyKind::kAccessor, details.kind());
value =
direct_handle(object->RawFastPropertyAt(isolate, index), isolate);
}
} else {
DCHECK_EQ(PropertyLocation::kDescriptor, details.location());
value = direct_handle(descs->GetStrongValue(isolate, i), isolate);
}
DCHECK(!value.is_null());
PropertyConstness constness = V8_DICT_PROPERTY_CONST_TRACKING_BOOL
? details.constness()
: PropertyConstness::kMutable;
PropertyDetails d(details.kind(), details.attributes(), constness);
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
ord_dictionary =
SwissNameDictionary::Add(isolate, ord_dictionary, key, value, d);
} else {
dictionary = NameDictionary::Add(isolate, dictionary, key, value, d)
.ToHandleChecked();
}
}
if constexpr (!V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
dictionary->set_next_enumeration_index(real_size + 1);
dictionary->set_may_have_interesting_properties(
map->may_have_interesting_properties());
}
DisallowGarbageCollection no_gc;
Heap* heap = isolate->heap();
int old_instance_size = map->instance_size();
int new_instance_size = new_map->instance_size();
int instance_size_delta = old_instance_size - new_instance_size;
DCHECK_GE(instance_size_delta, 0);
if (instance_size_delta > 0) {
heap->NotifyObjectSizeChange(*object, old_instance_size, new_instance_size,
ClearRecordedSlots::kYes);
}
object->set_map(isolate, *new_map, kReleaseStore);
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
object->SetProperties(*ord_dictionary);
} else {
object->SetProperties(*dictionary);
}
int inobject_properties = new_map->GetInObjectProperties();
if (inobject_properties) {
for (int i = 0; i < inobject_properties; i++) {
FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i);
object->FastPropertyAtPut(index, Smi::zero());
}
}
#ifdef DEBUG
if (v8_flags.trace_normalization) {
StdoutStream os;
os << "Object properties have been normalized:\n";
Print(*object, os);
}
#endif
}
}
void JSObject::MigrateToMap(Isolate* isolate, DirectHandle<JSObject> object,
DirectHandle<Map> new_map,
int expected_additional_properties) {
if (object->map(isolate) == *new_map) return;
DirectHandle<Map> old_map(object->map(isolate), isolate);
NotifyMapChange(old_map, new_map, isolate);
if (old_map->is_dictionary_map()) {
CHECK(new_map->is_dictionary_map());
object->set_map(isolate, *new_map, kReleaseStore);
} else if (!new_map->is_dictionary_map()) {
MigrateFastToFast(isolate, object, new_map);
if (old_map->is_prototype_map()) {
DCHECK(!old_map->is_stable());
DCHECK(new_map->is_stable());
DCHECK(new_map->owns_descriptors());
DCHECK(old_map->owns_descriptors());
old_map->set_owns_descriptors(false);
DCHECK(old_map->is_abandoned_prototype_map());
DCHECK_EQ(0,
TransitionsAccessor(isolate, *old_map).NumberOfTransitions());
DCHECK(IsUndefined(new_map->GetBackPointer(isolate), isolate));
DCHECK(object->map(isolate) != *old_map);
}
} else {
MigrateFastToSlow(isolate, object, new_map, expected_additional_properties);
}
}
void JSObject::ForceSetPrototype(Isolate* isolate,
DirectHandle<JSObject> object,
DirectHandle<JSPrototype> proto) {
DirectHandle<Map> old_map(object->map(), isolate);
DirectHandle<Map> new_map = Map::Copy(isolate, old_map, "ForceSetPrototype");
Map::SetPrototype(isolate, new_map, proto);
JSObject::MigrateToMap(isolate, object, new_map);
}
Maybe<InterceptorResult> JSObject::SetPropertyWithInterceptor(
LookupIterator* it, Maybe<ShouldThrow> should_throw,
DirectHandle<Object> value) {
DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
return SetPropertyWithInterceptorInternal(it, it->GetInterceptor(),
should_throw, value);
}
DirectHandle<Map> JSObject::GetElementsTransitionMap(
Isolate* isolate, DirectHandle<JSObject> object, ElementsKind to_kind) {
DirectHandle<Map> map(object->map(), isolate);
return Map::TransitionElementsTo(isolate, map, to_kind);
}
void JSObject::AllocateStorageForMap(Isolate* isolate,
DirectHandle<JSObject> object,
DirectHandle<Map> map) {
DCHECK(object->map()->GetInObjectProperties() ==
map->GetInObjectProperties());
ElementsKind obj_kind = object->map()->elements_kind();
ElementsKind map_kind = map->elements_kind();
if (map_kind != obj_kind) {
ElementsKind to_kind = GetMoreGeneralElementsKind(map_kind, obj_kind);
if (IsDictionaryElementsKind(obj_kind)) {
to_kind = obj_kind;
}
if (IsDictionaryElementsKind(to_kind)) {
NormalizeElements(isolate, object);
} else {
TransitionElementsKind(isolate, object, to_kind);
}
map = MapUpdater{isolate, map}.ReconfigureElementsKind(to_kind);
}
int number_of_fields = map->NumberOfFields(ConcurrencyMode::kSynchronous);
int inobject = map->GetInObjectProperties();
int unused = map->UnusedPropertyFields();
int total_size = number_of_fields + unused;
int external = total_size - inobject;
DirectHandle<DescriptorArray> descriptors(map->instance_descriptors(isolate),
isolate);
DirectHandle<FixedArray> storage =
isolate->factory()->NewFixedArray(inobject);
DirectHandle<PropertyArray> array =
isolate->factory()->NewPropertyArray(external);
for (InternalIndex i : map->IterateOwnDescriptors()) {
PropertyDetails details = descriptors->GetDetails(i);
Representation representation = details.representation();
if (!representation.IsDouble()) continue;
FieldIndex index = FieldIndex::ForDetails(*map, details);
auto box = isolate->factory()->NewHeapNumberWithHoleNaN();
if (index.is_inobject()) {
storage->set(index.property_index(), *box);
} else {
array->set(index.outobject_array_index(), *box);
}
}
object->SetProperties(*array);
for (int i = 0; i < inobject; i++) {
FieldIndex index = FieldIndex::ForPropertyIndex(*map, i);
Tagged<Object> value = storage->get(i);
object->FastPropertyAtPut(index, value);
}
object->set_map(isolate, *map, kReleaseStore);
}
void JSObject::MigrateInstance(Isolate* isolate,
DirectHandle<JSObject> object) {
Handle<Map> original_map(object->map(), isolate);
DirectHandle<Map> map = Map::Update(isolate, original_map);
map->set_is_migration_target(true);
JSObject::MigrateToMap(isolate, object, map);
if (v8_flags.trace_migration) {
object->PrintInstanceMigration(stdout, *original_map, *map);
}
#if VERIFY_HEAP
if (v8_flags.verify_heap) {
object->JSObjectVerify(isolate);
}
#endif
}
bool JSObject::TryMigrateInstance(Isolate* isolate,
DirectHandle<JSObject> object) {
DisallowDeoptimization no_deoptimization(isolate);
Handle<Map> original_map(object->map(), isolate);
DirectHandle<Map> new_map;
if (!Map::TryUpdate(isolate, original_map).ToHandle(&new_map)) {
return false;
}
JSObject::MigrateToMap(isolate, object, new_map);
if (v8_flags.trace_migration && *original_map != object->map()) {
object->PrintInstanceMigration(stdout, *original_map, object->map());
}
#if VERIFY_HEAP
if (v8_flags.verify_heap) {
object->JSObjectVerify(isolate);
}
#endif
return true;
}
namespace {
bool TryFastAddDataProperty(Isolate* isolate, DirectHandle<JSObject> object,
DirectHandle<Name> name, DirectHandle<Object> value,
PropertyAttributes attributes) {
DCHECK(IsUniqueName(*name));
Tagged<Map> map =
TransitionsAccessor(isolate, object->map())
.SearchTransition(*name, PropertyKind::kData, attributes);
if (map.is_null()) return false;
DCHECK(!map->is_dictionary_map());
DirectHandle<Map> new_map(map, isolate);
if (map->is_deprecated()) {
new_map = Map::Update(isolate, new_map);
if (new_map->is_dictionary_map()) return false;
}
InternalIndex descriptor = new_map->LastAdded();
new_map = Map::PrepareForDataProperty(isolate, new_map, descriptor,
PropertyConstness::kConst, value);
JSObject::MigrateToMap(isolate, object, new_map);
object->WriteToField(descriptor,
new_map->instance_descriptors()->GetDetails(descriptor),
*value);
return true;
}
}
void JSObject::AddProperty(Isolate* isolate, DirectHandle<JSObject> object,
DirectHandle<Name> name, DirectHandle<Object> value,
PropertyAttributes attributes) {
name = isolate->factory()->InternalizeName(name);
if (TryFastAddDataProperty(isolate, object, name, value, attributes)) {
return;
}
LookupIterator it(isolate, object, name, object,
LookupIterator::OWN_SKIP_INTERCEPTOR);
CHECK_NE(LookupIterator::ACCESS_CHECK, it.state());
#ifdef DEBUG
uint32_t index;
DCHECK(!IsJSProxy(*object));
DCHECK(!IsWasmObject(*object));
DCHECK(!name->AsArrayIndex(&index));
Maybe<PropertyAttributes> maybe = GetPropertyAttributes(&it);
DCHECK(maybe.IsJust());
DCHECK(!it.IsFound());
DCHECK(object->map()->is_extensible() || name->IsPrivate());
#endif
CHECK(Object::AddDataProperty(&it, value, attributes,
Just(ShouldThrow::kThrowOnError),
StoreOrigin::kNamed)
.IsJust());
}
void JSObject::AddProperty(Isolate* isolate, DirectHandle<JSObject> object,
const char* name, DirectHandle<Object> value,
PropertyAttributes attributes) {
JSObject::AddProperty(isolate, object,
isolate->factory()->InternalizeUtf8String(name), value,
attributes);
}
Maybe<bool> JSObject::DefineOwnPropertyIgnoreAttributes(
LookupIterator* it, DirectHandle<Object> value,
PropertyAttributes attributes, Maybe<ShouldThrow> should_throw,
AccessorInfoHandling handling, EnforceDefineSemantics semantics,
StoreOrigin store_origin, MaybeDirectHandle<Object> old_value) {
it->UpdateProtector(value, old_value);
for (;; it->Next()) {
switch (it->state()) {
case LookupIterator::JSPROXY:
case LookupIterator::TRANSITION:
case LookupIterator::STRING_LOOKUP_START_OBJECT:
UNREACHABLE();
case LookupIterator::WASM_OBJECT:
continue;
case LookupIterator::ACCESS_CHECK:
if (!it->HasAccess()) {
Isolate* isolate = it->isolate();
RETURN_ON_EXCEPTION_VALUE(
isolate,
isolate->ReportFailedAccessCheck(it->GetHolder<JSObject>()),
Nothing<bool>());
UNREACHABLE();
}
continue;
case LookupIterator::INTERCEPTOR: {
InterceptorResult result;
if (semantics == EnforceDefineSemantics::kDefine) {
PropertyDescriptor descriptor;
descriptor.set_configurable((attributes & DONT_DELETE) != 0);
descriptor.set_enumerable((attributes & DONT_ENUM) != 0);
descriptor.set_writable((attributes & READ_ONLY) != 0);
descriptor.set_value(Cast<JSAny>(value));
if (!DefinePropertyWithInterceptorInternal(it, it->GetInterceptor(),
should_throw, &descriptor)
.To(&result)) {
return Nothing<bool>();
}
} else {
DCHECK_EQ(semantics, EnforceDefineSemantics::kSet);
if (handling == DONT_FORCE_FIELD) {
if (!JSObject::SetPropertyWithInterceptor(it, should_throw, value)
.To(&result)) {
return Nothing<bool>();
}
} else {
result = InterceptorResult::kNotIntercepted;
}
}
switch (result) {
case InterceptorResult::kFalse: {
return Object::CannotCreateProperty(
it->isolate(), it->GetReceiver(), it->GetName(), should_throw);
}
case InterceptorResult::kTrue:
return Just(true);
case InterceptorResult::kNotIntercepted:
break;
}
if (semantics == EnforceDefineSemantics::kDefine) {
it->Restart();
Maybe<bool> can_define = JSObject::CheckIfCanDefineAsConfigurable(
it->isolate(), it, value, should_throw);
if (can_define.IsNothing() || !can_define.FromJust()) {
return can_define;
}
}
Isolate* isolate = it->isolate();
DirectHandle<JSAny> receiver = it->GetReceiver();
LookupIterator own_lookup(isolate, receiver, it->GetKey(),
LookupIterator::OWN_SKIP_INTERCEPTOR);
return JSObject::DefineOwnPropertyIgnoreAttributes(
&own_lookup, value, attributes, should_throw, handling, semantics,
store_origin);
}
case LookupIterator::ACCESSOR: {
DirectHandle<Object> accessors = it->GetAccessors();
if (IsAccessorInfo(*accessors) && handling == DONT_FORCE_FIELD) {
PropertyAttributes current_attributes = it->property_attributes();
AssertNoContextChange ncc(it->isolate());
if (current_attributes != attributes) {
it->TransitionToAccessorPair(accessors, attributes);
}
return Object::SetPropertyWithAccessor(it, value, should_throw);
}
it->ReconfigureDataProperty(value, attributes);
return Just(true);
}
case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND:
return Object::RedefineIncompatibleProperty(
it->isolate(), it->GetName(), value, should_throw);
case LookupIterator::DATA: {
if (it->property_attributes() == attributes) {
return Object::SetDataProperty(it, value);
}
DCHECK(!it->IsElement() ||
!Cast<JSObject>(it->GetReceiver())
->HasTypedArrayOrRabGsabTypedArrayElements());
it->ReconfigureDataProperty(value, attributes);
return Just(true);
}
case LookupIterator::NOT_FOUND:
return Object::AddDataProperty(it, value, attributes, should_throw,
store_origin, semantics);
}
UNREACHABLE();
}
}
MaybeDirectHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
DirectHandle<JSObject> object, DirectHandle<Name> name,
DirectHandle<Object> value, PropertyAttributes attributes) {
DCHECK(!IsTheHole(*value));
LookupIterator it(Isolate::Current(), object, name, object,
LookupIterator::OWN);
return DefineOwnPropertyIgnoreAttributes(&it, value, attributes);
}
MaybeDirectHandle<Object> JSObject::SetOwnElementIgnoreAttributes(
DirectHandle<JSObject> object, size_t index, DirectHandle<Object> value,
PropertyAttributes attributes) {
DCHECK(!IsJSTypedArray(*object));
Isolate* isolate = Isolate::Current();
LookupIterator it(isolate, object, index, object, LookupIterator::OWN);
return DefineOwnPropertyIgnoreAttributes(&it, value, attributes);
}
MaybeDirectHandle<Object> JSObject::DefinePropertyOrElementIgnoreAttributes(
DirectHandle<JSObject> object, DirectHandle<Name> name,
DirectHandle<Object> value, PropertyAttributes attributes) {
Isolate* isolate = Isolate::Current();
PropertyKey key(isolate, name);
LookupIterator it(isolate, object, key, object, LookupIterator::OWN);
return DefineOwnPropertyIgnoreAttributes(&it, value, attributes);
}
Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor(
LookupIterator* it) {
return GetPropertyAttributesWithInterceptorInternal(it, it->GetInterceptor());
}
void JSObject::NormalizeProperties(Isolate* isolate,
DirectHandle<JSObject> object,
PropertyNormalizationMode mode,
int expected_additional_properties,
bool use_cache, const char* reason) {
if (!object->HasFastProperties()) return;
DirectHandle<Map> map(object->map(), isolate);
DirectHandle<Map> new_map = Map::Normalize(isolate, map, map->elements_kind(),
{}, mode, use_cache, reason);
JSObject::MigrateToMap(isolate, object, new_map,
expected_additional_properties);
}
void JSObject::MigrateSlowToFast(DirectHandle<JSObject> object,
int unused_property_fields,
const char* reason) {
if (object->HasFastProperties()) return;
DCHECK(!IsJSGlobalObject(*object));
Isolate* isolate = Isolate::Current();
Factory* factory = isolate->factory();
DirectHandle<NameDictionary> dictionary;
DirectHandle<SwissNameDictionary> swiss_dictionary;
int number_of_elements;
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
swiss_dictionary =
direct_handle(object->property_dictionary_swiss(), isolate);
number_of_elements = swiss_dictionary->NumberOfElements();
} else {
dictionary = direct_handle(object->property_dictionary(), isolate);
number_of_elements = dictionary->NumberOfElements();
}
if (number_of_elements > kMaxNumberOfDescriptors) return;
DirectHandle<FixedArray> iteration_order;
int iteration_length;
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
iteration_length = swiss_dictionary->UsedCapacity();
} else {
iteration_order = NameDictionary::IterationIndices(isolate, dictionary);
iteration_length = dictionary->NumberOfElements();
}
int number_of_fields = 0;
ReadOnlyRoots roots(isolate);
for (int i = 0; i < iteration_length; i++) {
PropertyKind kind;
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
InternalIndex index(swiss_dictionary->EntryForEnumerationIndex(i));
Tagged<Object> key = swiss_dictionary->KeyAt(index);
if (!SwissNameDictionary::IsKey(roots, key)) {
continue;
}
kind = swiss_dictionary->DetailsAt(index).kind();
} else {
InternalIndex index(Smi::ToInt(iteration_order->get(i)));
DCHECK(dictionary->IsKey(roots, dictionary->KeyAt(isolate, index)));
kind = dictionary->DetailsAt(index).kind();
}
if (kind == PropertyKind::kData) {
number_of_fields += 1;
}
}
DirectHandle<Map> old_map(object->map(), isolate);
int inobject_props = old_map->GetInObjectProperties();
DirectHandle<Map> new_map = Map::CopyDropDescriptors(isolate, old_map);
new_map->set_may_have_interesting_properties(
new_map->has_named_interceptor() || new_map->is_access_check_needed());
new_map->set_is_dictionary_map(false);
NotifyMapChange(old_map, new_map, isolate);
if (number_of_elements == 0) {
DisallowGarbageCollection no_gc;
DCHECK_LE(unused_property_fields, inobject_props);
new_map->SetInObjectUnusedPropertyFields(inobject_props);
object->set_map(isolate, *new_map, kReleaseStore);
object->SetProperties(ReadOnlyRoots(isolate).empty_fixed_array());
DCHECK(object->HasFastProperties());
if (v8_flags.log_maps) {
LOG(isolate, MapEvent("SlowToFast", old_map, new_map, reason));
}
return;
}
DirectHandle<DescriptorArray> descriptors =
DescriptorArray::Allocate(isolate, number_of_elements, 0);
int number_of_allocated_fields =
number_of_fields + unused_property_fields - inobject_props;
if (number_of_allocated_fields < 0) {
number_of_allocated_fields = 0;
unused_property_fields = inobject_props - number_of_fields;
}
DirectHandle<PropertyArray> fields =
factory->NewPropertyArray(number_of_allocated_fields);
bool is_transitionable_elements_kind =
IsTransitionableFastElementsKind(old_map->elements_kind());
int current_offset = 0;
int descriptor_index = 0;
for (int i = 0; i < iteration_length; i++) {
Tagged<Name> k;
Tagged<Object> value;
PropertyDetails details = PropertyDetails::Empty();
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
InternalIndex index(swiss_dictionary->EntryForEnumerationIndex(i));
Tagged<Object> key_obj = swiss_dictionary->KeyAt(index);
if (!SwissNameDictionary::IsKey(roots, key_obj)) {
continue;
}
k = Cast<Name>(key_obj);
value = swiss_dictionary->ValueAt(index);
details = swiss_dictionary->DetailsAt(index);
} else {
InternalIndex index(Smi::ToInt(iteration_order->get(i)));
k = dictionary->NameAt(index);
value = dictionary->ValueAt(index);
details = dictionary->DetailsAt(index);
}
CHECK(IsUniqueName(k));
DirectHandle<Name> key(k, isolate);
if (key->IsInteresting(isolate)) {
new_map->set_may_have_interesting_properties(true);
}
DCHECK_EQ(PropertyLocation::kField, details.location());
DCHECK_IMPLIES(!V8_DICT_PROPERTY_CONST_TRACKING_BOOL,
details.constness() == PropertyConstness::kMutable);
Descriptor d;
if (details.kind() == PropertyKind::kData) {
PropertyConstness constness = is_transitionable_elements_kind
? PropertyConstness::kMutable
: PropertyConstness::kConst;
d = Descriptor::DataField(
key, current_offset, details.attributes(), constness,
Representation::Tagged(),
MaybeObjectDirectHandle(FieldType::Any(isolate)));
} else {
DCHECK_EQ(PropertyKind::kAccessor, details.kind());
d = Descriptor::AccessorConstant(key, direct_handle(value, isolate),
details.attributes());
}
details = d.GetDetails();
if (details.location() == PropertyLocation::kField) {
if (current_offset < inobject_props) {
object->InObjectPropertyAtPut(current_offset, value,
UPDATE_WRITE_BARRIER);
} else {
int offset = current_offset - inobject_props;
fields->set(offset, value);
}
current_offset += details.field_width_in_words();
}
descriptors->Set(InternalIndex(descriptor_index++), &d);
}
DCHECK_EQ(current_offset, number_of_fields);
DCHECK_EQ(descriptor_index, number_of_elements);
descriptors->Sort();
DisallowGarbageCollection no_gc;
new_map->InitializeDescriptors(isolate, *descriptors);
if (number_of_allocated_fields == 0) {
new_map->SetInObjectUnusedPropertyFields(unused_property_fields);
} else {
new_map->SetOutOfObjectUnusedPropertyFields(unused_property_fields);
}
if (v8_flags.log_maps) {
LOG(isolate, MapEvent("SlowToFast", old_map, new_map, reason));
}
object->set_map(isolate, *new_map, kReleaseStore);
object->SetProperties(*fields);
DCHECK(IsJSObject(*object));
DCHECK(object->HasFastProperties());
}
void JSObject::RequireSlowElements(Tagged<NumberDictionary> dictionary) {
DCHECK_NE(dictionary,
ReadOnlyRoots(Isolate::Current()).empty_slow_element_dictionary());
if (dictionary->requires_slow_elements()) return;
dictionary->set_requires_slow_elements();
if (map()->is_prototype_map()) {
InvalidatePrototypeChains(map());
}
}
DirectHandle<NumberDictionary> JSObject::NormalizeElements(
Isolate* isolate, DirectHandle<JSObject> object) {
DCHECK(!object->HasTypedArrayOrRabGsabTypedArrayElements());
bool is_sloppy_arguments = object->HasSloppyArgumentsElements();
{
DisallowGarbageCollection no_gc;
Tagged<FixedArrayBase> elements = object->elements();
if (is_sloppy_arguments) {
elements = Cast<SloppyArgumentsElements>(elements)->arguments();
}
if (IsNumberDictionary(elements)) {
return direct_handle(Cast<NumberDictionary>(elements), isolate);
}
}
DCHECK(object->HasSmiOrObjectElements() || object->HasDoubleElements() ||
object->HasFastArgumentsElements() ||
object->HasFastStringWrapperElements() ||
object->HasSealedElements() || object->HasNonextensibleElements());
DirectHandle<NumberDictionary> dictionary =
object->GetElementsAccessor()->Normalize(isolate, object);
ElementsKind target_kind =
is_sloppy_arguments ? SLOW_SLOPPY_ARGUMENTS_ELEMENTS
: object->HasFastStringWrapperElements() ? SLOW_STRING_WRAPPER_ELEMENTS
: DICTIONARY_ELEMENTS;
DirectHandle<Map> new_map =
JSObject::GetElementsTransitionMap(isolate, object, target_kind);
JSObject::MigrateToMap(isolate, object, new_map);
if (is_sloppy_arguments) {
Cast<SloppyArgumentsElements>(object->elements())
->set_arguments(*dictionary);
} else {
object->set_elements(*dictionary);
}
#ifdef DEBUG
if (v8_flags.trace_normalization) {
StdoutStream os;
os << "Object elements have been normalized:\n";
Print(*object, os);
}
#endif
DCHECK(object->HasDictionaryElements() ||
object->HasSlowArgumentsElements() ||
object->HasSlowStringWrapperElements());
return dictionary;
}
Maybe<InterceptorResult> JSObject::DeletePropertyWithInterceptor(
LookupIterator* it, ShouldThrow should_throw) {
Isolate* isolate = it->isolate();
AssertNoContextChange ncc(isolate);
DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
DirectHandle<InterceptorInfo> interceptor(it->GetInterceptor());
if (!interceptor->has_deleter()) {
return Just(InterceptorResult::kNotIntercepted);
}
DirectHandle<JSObject> holder = it->GetHolder<JSObject>();
DirectHandle<Object> receiver = it->GetReceiver();
if (!IsJSReceiver(*receiver)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
Object::ConvertReceiver(isolate, receiver));
}
PropertyCallbackArguments args(isolate, *receiver, it->GetHolderForApi(),
Just(should_throw));
v8::Intercepted intercepted =
it->IsElement(*holder)
? args.CallIndexedDeleter(interceptor, it->array_index())
: args.CallNamedDeleter(interceptor, it->name());
return args.GetBooleanReturnValue(intercepted, "Deleter");
}
Maybe<bool> JSObject::CreateDataProperty(Isolate* isolate,
DirectHandle<JSObject> object,
PropertyKey key,
DirectHandle<Object> value,
Maybe<ShouldThrow> should_throw) {
if (!key.is_element()) {
if (TryFastAddDataProperty(isolate, object, key.name(), value, NONE)) {
return Just(true);
}
}
LookupIterator it(isolate, object, key, LookupIterator::OWN);
Maybe<bool> can_define = JSObject::CheckIfCanDefineAsConfigurable(
isolate, &it, value, should_throw);
if (can_define.IsNothing() || !can_define.FromJust()) {
return can_define;
}
RETURN_ON_EXCEPTION_VALUE(isolate,
DefineOwnPropertyIgnoreAttributes(&it, value, NONE),
Nothing<bool>());
return Just(true);
}
namespace {
template <typename Dictionary>
bool TestDictionaryPropertiesIntegrityLevel(Tagged<Dictionary> dict,
ReadOnlyRoots roots,
PropertyAttributes level) {
DCHECK(level == SEALED || level == FROZEN);
for (InternalIndex i : dict->IterateEntries()) {
Tagged<Object> key;
if (!dict->ToKey(roots, i, &key)) continue;
if (Object::FilterKey(key, ALL_PROPERTIES)) continue;
PropertyDetails details = dict->DetailsAt(i);
if (details.IsConfigurable()) return false;
if (level == FROZEN && details.kind() == PropertyKind::kData &&
!details.IsReadOnly()) {
return false;
}
}
return true;
}
bool TestFastPropertiesIntegrityLevel(Tagged<Map> map,
PropertyAttributes level) {
DCHECK(level == SEALED || level == FROZEN);
DCHECK(!IsCustomElementsReceiverMap(map));
DCHECK(!map->is_dictionary_map());
Tagged<DescriptorArray> descriptors = map->instance_descriptors();
for (InternalIndex i : map->IterateOwnDescriptors()) {
if (descriptors->GetKey(i)->IsPrivate()) continue;
PropertyDetails details = descriptors->GetDetails(i);
if (details.IsConfigurable()) return false;
if (level == FROZEN && details.kind() == PropertyKind::kData &&
!details.IsReadOnly()) {
return false;
}
}
return true;
}
bool TestPropertiesIntegrityLevel(Tagged<JSObject> object,
PropertyAttributes level) {
DCHECK(!IsCustomElementsReceiverMap(object->map()));
if (object->HasFastProperties()) {
return TestFastPropertiesIntegrityLevel(object->map(), level);
}
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
return TestDictionaryPropertiesIntegrityLevel(
object->property_dictionary_swiss(), GetReadOnlyRoots(), level);
} else {
return TestDictionaryPropertiesIntegrityLevel(object->property_dictionary(),
GetReadOnlyRoots(), level);
}
}
bool TestElementsIntegrityLevel(Isolate* isolate, Tagged<JSObject> object,
PropertyAttributes level) {
DCHECK(!object->HasSloppyArgumentsElements());
ElementsKind kind = object->GetElementsKind();
if (IsDictionaryElementsKind(kind)) {
return TestDictionaryPropertiesIntegrityLevel(
Cast<NumberDictionary>(object->elements()), GetReadOnlyRoots(), level);
}
if (IsTypedArrayOrRabGsabTypedArrayElementsKind(kind)) {
if (level == FROZEN && Cast<JSArrayBufferView>(object)->byte_length() > 0) {
return false;
}
return TestPropertiesIntegrityLevel(object, level);
}
if (IsFrozenElementsKind(kind)) return true;
if (IsSealedElementsKind(kind) && level != FROZEN) return true;
if (IsNonextensibleElementsKind(kind) && level == NONE) return true;
ElementsAccessor* accessor = ElementsAccessor::ForKind(kind);
return accessor->NumberOfElements(isolate, object) == 0;
}
bool FastTestIntegrityLevel(Isolate* isolate, Tagged<JSObject> object,
PropertyAttributes level) {
DCHECK(!IsCustomElementsReceiverMap(object->map()));
return !object->map()->is_extensible() &&
TestElementsIntegrityLevel(isolate, object, level) &&
TestPropertiesIntegrityLevel(object, level);
}
}
Maybe<bool> JSObject::TestIntegrityLevel(Isolate* isolate,
DirectHandle<JSObject> object,
IntegrityLevel level) {
if (!IsCustomElementsReceiverMap(object->map()) &&
!object->HasSloppyArgumentsElements()) {
return Just(FastTestIntegrityLevel(isolate, *object, level));
}
return GenericTestIntegrityLevel(isolate, Cast<JSReceiver>(object), level);
}
Maybe<bool> JSObject::PreventExtensions(Isolate* isolate,
DirectHandle<JSObject> object,
ShouldThrow should_throw) {
if (!object->HasSloppyArgumentsElements()) {
return PreventExtensionsWithTransition<NONE>(isolate, object, should_throw);
}
if (IsAccessCheckNeeded(*object) &&
!isolate->MayAccess(isolate->native_context(), object)) {
RETURN_ON_EXCEPTION_VALUE(isolate, isolate->ReportFailedAccessCheck(object),
Nothing<bool>());
UNREACHABLE();
}
if (!object->map()->is_extensible()) return Just(true);
if (IsJSGlobalProxy(*object)) {
PrototypeIterator iter(isolate, object);
if (iter.IsAtEnd()) return Just(true);
DCHECK(IsJSGlobalObject(*PrototypeIterator::GetCurrent(iter)));
return PreventExtensions(
isolate, PrototypeIterator::GetCurrent<JSObject>(iter), should_throw);
}
if (object->map()->has_named_interceptor() ||
object->map()->has_indexed_interceptor()) {
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kCannotPreventExt));
}
DCHECK(!object->HasTypedArrayOrRabGsabTypedArrayElements());
DirectHandle<NumberDictionary> dictionary =
NormalizeElements(isolate, object);
DCHECK(object->HasDictionaryElements() || object->HasSlowArgumentsElements());
if (*dictionary != ReadOnlyRoots(isolate).empty_slow_element_dictionary()) {
object->RequireSlowElements(*dictionary);
}
DirectHandle<Map> new_map = Map::Copy(
isolate, direct_handle(object->map(), isolate), "PreventExtensions");
new_map->set_is_extensible(false);
JSObject::MigrateToMap(isolate, object, new_map);
DCHECK(!object->map()->is_extensible());
return Just(true);
}
bool JSObject::IsExtensible(Isolate* isolate, DirectHandle<JSObject> object) {
if (IsAccessCheckNeeded(*object) &&
!isolate->MayAccess(isolate->native_context(), object)) {
return true;
}
if (IsJSGlobalProxy(*object)) {
PrototypeIterator iter(isolate, *object);
if (iter.IsAtEnd()) return false;
DCHECK(IsJSGlobalObject(iter.GetCurrent()));
return iter.GetCurrent<JSObject>()->map()->is_extensible();
}
return object->map()->is_extensible();
}
MaybeDirectHandle<Object> JSObject::ReadFromOptionsBag(
DirectHandle<Object> options, DirectHandle<String> option_name,
Isolate* isolate) {
if (IsJSReceiver(*options)) {
DirectHandle<JSReceiver> js_options = Cast<JSReceiver>(options);
return JSObject::GetProperty(isolate, js_options, option_name);
}
return MaybeDirectHandle<Object>(isolate->factory()->undefined_value());
}
template <typename Dictionary>
void JSObject::ApplyAttributesToDictionary(
Isolate* isolate, ReadOnlyRoots roots, DirectHandle<Dictionary> dictionary,
const PropertyAttributes attributes) {
for (InternalIndex i : dictionary->IterateEntries()) {
Tagged<Object> k;
if (!dictionary->ToKey(roots, i, &k)) continue;
if (Object::FilterKey(k, ALL_PROPERTIES)) continue;
PropertyDetails details = dictionary->DetailsAt(i);
int attrs = attributes;
if ((attributes & READ_ONLY) && details.kind() == PropertyKind::kAccessor) {
Tagged<Object> v = dictionary->ValueAt(i);
if (IsAccessorPair(v)) attrs &= ~READ_ONLY;
}
details = details.CopyAddAttributes(PropertyAttributesFromInt(attrs));
dictionary->DetailsAtPut(i, details);
}
}
template void JSObject::ApplyAttributesToDictionary(
Isolate* isolate, ReadOnlyRoots roots,
DirectHandle<NumberDictionary> dictionary,
const PropertyAttributes attributes);
DirectHandle<NumberDictionary> CreateElementDictionary(
Isolate* isolate, DirectHandle<JSObject> object) {
DirectHandle<NumberDictionary> new_element_dictionary;
if (!object->HasTypedArrayOrRabGsabTypedArrayElements() &&
!object->HasDictionaryElements() &&
!object->HasSlowStringWrapperElements()) {
int length = IsJSArray(*object)
? Smi::ToInt(Cast<JSArray>(object)->length())
: object->elements()->length();
new_element_dictionary =
length == 0 ? isolate->factory()->empty_slow_element_dictionary()
: object->GetElementsAccessor()->Normalize(isolate, object);
}
return new_element_dictionary;
}
template <PropertyAttributes attrs>
Maybe<bool> JSObject::PreventExtensionsWithTransition(
Isolate* isolate, DirectHandle<JSObject> object, ShouldThrow should_throw) {
static_assert(attrs == NONE || attrs == SEALED || attrs == FROZEN);
DCHECK(!object->HasSloppyArgumentsElements());
DCHECK_IMPLIES(IsJSModuleNamespace(*object), attrs == NONE);
if (IsAccessCheckNeeded(*object) &&
!isolate->MayAccess(isolate->native_context(), object)) {
RETURN_ON_EXCEPTION_VALUE(isolate, isolate->ReportFailedAccessCheck(object),
Nothing<bool>());
UNREACHABLE();
}
if (attrs == NONE && !object->map()->is_extensible()) {
return Just(true);
}
{
ElementsKind old_elements_kind = object->map()->elements_kind();
if (IsFrozenElementsKind(old_elements_kind)) return Just(true);
if (attrs != FROZEN && IsSealedElementsKind(old_elements_kind)) {
return Just(true);
}
}
if (IsJSGlobalProxy(*object)) {
PrototypeIterator iter(isolate, object);
if (iter.IsAtEnd()) return Just(true);
DCHECK(IsJSGlobalObject(*PrototypeIterator::GetCurrent(iter)));
return PreventExtensionsWithTransition<attrs>(
isolate, PrototypeIterator::GetCurrent<JSObject>(iter), should_throw);
}
if (IsAlwaysSharedSpaceJSObject(*object)) {
DCHECK(FastTestIntegrityLevel(isolate, *object, SEALED));
if (attrs != FROZEN) return Just(true);
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kCannotFreeze));
}
if (object->map()->has_named_interceptor() ||
object->map()->has_indexed_interceptor() ||
(object->HasTypedArrayOrRabGsabTypedArrayElements() &&
Cast<JSTypedArray>(*object)->IsVariableLength())) {
MessageTemplate message = MessageTemplate::kNone;
switch (attrs) {
case NONE:
message = MessageTemplate::kCannotPreventExt;
break;
case SEALED:
message = MessageTemplate::kCannotSeal;
break;
case FROZEN:
message = MessageTemplate::kCannotFreeze;
break;
}
RETURN_FAILURE(isolate, should_throw, NewTypeError(message));
}
DirectHandle<Symbol> transition_marker;
if (attrs == NONE) {
transition_marker = isolate->factory()->nonextensible_symbol();
} else if (attrs == SEALED) {
transition_marker = isolate->factory()->sealed_symbol();
} else {
DCHECK(attrs == FROZEN);
transition_marker = isolate->factory()->frozen_symbol();
}
switch (object->map()->elements_kind()) {
case PACKED_SMI_ELEMENTS:
case PACKED_DOUBLE_ELEMENTS:
JSObject::TransitionElementsKind(isolate, object, PACKED_ELEMENTS);
break;
case HOLEY_SMI_ELEMENTS:
case HOLEY_DOUBLE_ELEMENTS:
JSObject::TransitionElementsKind(isolate, object, HOLEY_ELEMENTS);
break;
default:
break;
}
DirectHandle<NumberDictionary> new_element_dictionary;
DirectHandle<Map> old_map(object->map(), isolate);
old_map = Map::Update(isolate, old_map);
DirectHandle<Map> transition_map;
MaybeDirectHandle<Map> maybe_transition_map =
TransitionsAccessor::SearchSpecial(isolate, old_map, *transition_marker);
if (maybe_transition_map.ToHandle(&transition_map)) {
DCHECK(transition_map->has_dictionary_elements() ||
transition_map->has_typed_array_or_rab_gsab_typed_array_elements() ||
transition_map->elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS ||
transition_map->has_any_nonextensible_elements());
DCHECK(!transition_map->is_extensible());
if (!transition_map->has_any_nonextensible_elements()) {
new_element_dictionary = CreateElementDictionary(isolate, object);
}
JSObject::MigrateToMap(isolate, object, transition_map);
} else if (TransitionsAccessor::CanHaveMoreTransitions(isolate, old_map)) {
DirectHandle<Map> new_map = Map::CopyForPreventExtensions(
isolate, old_map, attrs, transition_marker, "CopyForPreventExtensions");
if (!new_map->has_any_nonextensible_elements()) {
new_element_dictionary = CreateElementDictionary(isolate, object);
}
JSObject::MigrateToMap(isolate, object, new_map);
} else {
DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map());
NormalizeProperties(isolate, object, CLEAR_INOBJECT_PROPERTIES, 0,
"SlowPreventExtensions");
DirectHandle<Map> new_map =
Map::Copy(isolate, direct_handle(object->map(), isolate),
"SlowCopyForPreventExtensions");
new_map->set_is_extensible(false);
new_element_dictionary = CreateElementDictionary(isolate, object);
if (!new_element_dictionary.is_null()) {
ElementsKind new_kind =
IsStringWrapperElementsKind(old_map->elements_kind())
? SLOW_STRING_WRAPPER_ELEMENTS
: DICTIONARY_ELEMENTS;
new_map->set_elements_kind(new_kind);
}
JSObject::MigrateToMap(isolate, object, new_map);
if (attrs != NONE) {
ReadOnlyRoots roots(isolate);
if (IsJSGlobalObject(*object)) {
DirectHandle<GlobalDictionary> dictionary(
Cast<JSGlobalObject>(*object)->global_dictionary(kAcquireLoad),
isolate);
JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary,
attrs);
} else if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
DirectHandle<SwissNameDictionary> dictionary(
object->property_dictionary_swiss(), isolate);
JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary,
attrs);
} else {
DirectHandle<NameDictionary> dictionary(object->property_dictionary(),
isolate);
JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary,
attrs);
}
}
}
if (object->map()->has_any_nonextensible_elements()) {
DCHECK(new_element_dictionary.is_null());
return Just(true);
}
if (object->HasTypedArrayOrRabGsabTypedArrayElements()) {
DCHECK(new_element_dictionary.is_null());
if (attrs != NONE && Cast<JSTypedArray>(*object)->GetLength() > 0) {
isolate->Throw(*isolate->factory()->NewTypeError(
attrs == SEALED ? MessageTemplate::kCannotSealArrayBufferView
: MessageTemplate::kCannotFreezeArrayBufferView));
return Nothing<bool>();
}
return Just(true);
}
DCHECK(object->map()->has_dictionary_elements() ||
object->map()->elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS);
if (!new_element_dictionary.is_null()) {
object->set_elements(*new_element_dictionary);
}
if (object->elements() !=
ReadOnlyRoots(isolate).empty_slow_element_dictionary()) {
DirectHandle<NumberDictionary> dictionary(object->element_dictionary(),
isolate);
object->RequireSlowElements(*dictionary);
if (attrs != NONE) {
JSObject::ApplyAttributesToDictionary(isolate, ReadOnlyRoots(isolate),
dictionary, attrs);
}
}
return Just(true);
}
Handle<JSAny> JSObject::FastPropertyAt(Isolate* isolate,
DirectHandle<JSObject> object,
Representation representation,
FieldIndex index) {
Handle<JSAny> raw_value(object->RawFastPropertyAt(index), isolate);
return Object::WrapForRead(isolate, raw_value, representation);
}
DirectHandle<JSAny> JSObject::FastPropertyAt(Isolate* isolate,
DirectHandle<JSObject> object,
Representation representation,
FieldIndex index,
SeqCstAccessTag tag) {
Handle<JSAny> raw_value(object->RawFastPropertyAt(index, tag), isolate);
return Object::WrapForRead(isolate, raw_value, representation);
}
Handle<Object> JSObject::DictionaryPropertyAt(Isolate* isolate,
DirectHandle<JSObject> object,
InternalIndex dict_index) {
DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
Tagged<SwissNameDictionary> dict = object->property_dictionary_swiss();
return handle(dict->ValueAt(dict_index), isolate);
} else {
Tagged<NameDictionary> dict = object->property_dictionary();
return handle(dict->ValueAt(dict_index), isolate);
}
}
std::optional<Tagged<Object>> JSObject::DictionaryPropertyAt(
DirectHandle<JSObject> object, InternalIndex dict_index, Heap* heap) {
Tagged<Object> backing_store = object->raw_properties_or_hash(kRelaxedLoad);
if (!IsHeapObject(backing_store)) return {};
if (heap->IsPendingAllocation(Cast<HeapObject>(backing_store))) return {};
if (!IsPropertyDictionary(backing_store)) return {};
std::optional<Tagged<Object>> maybe_obj =
Cast<PropertyDictionary>(backing_store)->TryValueAt(dict_index);
if (!maybe_obj) return {};
return maybe_obj.value();
}
bool JSObject::HasEnumerableElements() {
Tagged<JSObject> object = *this;
switch (object->GetElementsKind()) {
case PACKED_SMI_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case PACKED_NONEXTENSIBLE_ELEMENTS:
case PACKED_DOUBLE_ELEMENTS:
case SHARED_ARRAY_ELEMENTS: {
int length = IsJSArray(object)
? Smi::ToInt(Cast<JSArray>(object)->length())
: object->elements()->length();
return length > 0;
}
case HOLEY_SMI_ELEMENTS:
case HOLEY_FROZEN_ELEMENTS:
case HOLEY_SEALED_ELEMENTS:
case HOLEY_NONEXTENSIBLE_ELEMENTS:
case HOLEY_ELEMENTS: {
Tagged<FixedArray> elements = Cast<FixedArray>(object->elements());
int length = IsJSArray(object)
? Smi::ToInt(Cast<JSArray>(object)->length())
: elements->length();
Isolate* isolate = Isolate::Current();
for (int i = 0; i < length; i++) {
if (!elements->is_the_hole(isolate, i)) return true;
}
return false;
}
case HOLEY_DOUBLE_ELEMENTS: {
int length = IsJSArray(object)
? Smi::ToInt(Cast<JSArray>(object)->length())
: object->elements()->length();
if (length == 0) return false;
Tagged<FixedDoubleArray> elements =
Cast<FixedDoubleArray>(object->elements());
for (int i = 0; i < length; i++) {
if (!elements->is_the_hole(i)) return true;
}
return false;
}
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE) {
size_t byte_length = Cast<JSTypedArray>(object)->byte_length();
return byte_length > 0;
}
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
{
size_t length = Cast<JSTypedArray>(object)->GetLength();
return length > 0;
}
case DICTIONARY_ELEMENTS: {
Tagged<NumberDictionary> elements =
Cast<NumberDictionary>(object->elements());
return elements->NumberOfEnumerableProperties() > 0;
}
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
return true;
case FAST_STRING_WRAPPER_ELEMENTS:
case SLOW_STRING_WRAPPER_ELEMENTS:
if (Cast<String>(Cast<JSPrimitiveWrapper>(object)->value())->length() >
0) {
return true;
}
return object->elements()->length() > 0;
case WASM_ARRAY_ELEMENTS:
UNIMPLEMENTED();
case NO_ELEMENTS:
return false;
}
UNREACHABLE();
}
MaybeDirectHandle<Object> JSObject::DefineOwnAccessorIgnoreAttributes(
DirectHandle<JSObject> object, DirectHandle<Name> name,
DirectHandle<Object> getter, DirectHandle<Object> setter,
PropertyAttributes attributes) {
Isolate* isolate = Isolate::Current();
PropertyKey key(isolate, name);
LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
return DefineOwnAccessorIgnoreAttributes(&it, getter, setter, attributes);
}
MaybeDirectHandle<Object> JSObject::DefineOwnAccessorIgnoreAttributes(
LookupIterator* it, DirectHandle<Object> getter,
DirectHandle<Object> setter, PropertyAttributes attributes) {
Isolate* isolate = it->isolate();
it->UpdateProtector();
while (it->state() == LookupIterator::ACCESS_CHECK) {
if (!it->HasAccess()) {
RETURN_ON_EXCEPTION(
isolate, isolate->ReportFailedAccessCheck(it->GetHolder<JSObject>()));
UNREACHABLE();
}
it->Next();
}
auto object = Cast<JSObject>(it->GetReceiver());
if (it->IsElement() && object->HasTypedArrayOrRabGsabTypedArrayElements()) {
return it->factory()->undefined_value();
}
DCHECK(IsCallable(*getter) || IsUndefined(*getter, isolate) ||
IsNull(*getter, isolate) || IsFunctionTemplateInfo(*getter));
DCHECK(IsCallable(*setter) || IsUndefined(*setter, isolate) ||
IsNull(*setter, isolate) || IsFunctionTemplateInfo(*setter));
it->TransitionToAccessorProperty(getter, setter, attributes);
return isolate->factory()->undefined_value();
}
MaybeDirectHandle<Object> JSObject::SetAccessor(DirectHandle<JSObject> object,
DirectHandle<Name> name,
DirectHandle<AccessorInfo> info,
PropertyAttributes attributes) {
Isolate* isolate = Isolate::Current();
PropertyKey key(isolate, name);
LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
while (it.state() == LookupIterator::ACCESS_CHECK) {
if (!it.HasAccess()) {
RETURN_ON_EXCEPTION(isolate, isolate->ReportFailedAccessCheck(object));
UNREACHABLE();
}
it.Next();
}
if (it.IsElement() && object->HasTypedArrayOrRabGsabTypedArrayElements()) {
return it.factory()->undefined_value();
}
Maybe<bool> can_define = JSObject::CheckIfCanDefineAsConfigurable(
isolate, &it, info, Nothing<ShouldThrow>());
MAYBE_RETURN_NULL(can_define);
if (!can_define.FromJust()) return it.factory()->undefined_value();
it.TransitionToAccessorPair(info, attributes);
return object;
}
Maybe<bool> JSObject::CheckIfCanDefineAsConfigurable(
Isolate* isolate, LookupIterator* it, DirectHandle<Object> value,
Maybe<ShouldThrow> should_throw) {
DCHECK(IsJSObject(*it->GetReceiver()));
if (it->IsFound()) {
Maybe<PropertyAttributes> attributes = GetPropertyAttributes(it);
MAYBE_RETURN(attributes, Nothing<bool>());
if (attributes.FromJust() != ABSENT) {
if ((attributes.FromJust() & DONT_DELETE) != 0) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed, it->GetName()));
}
return Just(true);
}
}
if (!JSObject::IsExtensible(isolate, Cast<JSObject>(it->GetReceiver()))) {
RETURN_FAILURE(
isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kDefineDisallowed, it->GetName()));
}
return Just(true);
}
Tagged<Object> JSObject::SlowReverseLookup(Tagged<Object> value) {
if (HasFastProperties()) {
Tagged<DescriptorArray> descs = map()->instance_descriptors();
bool value_is_number = IsNumber(value);
for (InternalIndex i : map()->IterateOwnDescriptors()) {
PropertyDetails details = descs->GetDetails(i);
if (details.location() == PropertyLocation::kField) {
DCHECK_EQ(PropertyKind::kData, details.kind());
FieldIndex field_index = FieldIndex::ForDetails(map(), details);
Tagged<Object> property = RawFastPropertyAt(field_index);
if (field_index.is_double()) {
DCHECK(IsHeapNumber(property));
if (value_is_number && Cast<HeapNumber>(property)->value() ==
Object::NumberValue(Cast<Number>(value))) {
return descs->GetKey(i);
}
} else if (property == value) {
return descs->GetKey(i);
}
} else {
DCHECK_EQ(PropertyLocation::kDescriptor, details.location());
if (details.kind() == PropertyKind::kData) {
if (descs->GetStrongValue(i) == value) {
return descs->GetKey(i);
}
}
}
}
return GetReadOnlyRoots().undefined_value();
} else if (IsJSGlobalObject(*this)) {
return Cast<JSGlobalObject>(*this)
->global_dictionary(kAcquireLoad)
->SlowReverseLookup(value);
} else if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
return property_dictionary_swiss()->SlowReverseLookup(Isolate::Current(),
value);
} else {
return property_dictionary()->SlowReverseLookup(value);
}
}
void JSObject::PrototypeRegistryCompactionCallback(Tagged<HeapObject> value,
int old_index,
int new_index) {
DCHECK(IsMap(value) && Cast<Map>(value)->is_prototype_map());
Tagged<Map> map = Cast<Map>(value);
DCHECK(IsPrototypeInfo(map->prototype_info()));
Tagged<PrototypeInfo> proto_info = Cast<PrototypeInfo>(map->prototype_info());
DCHECK_EQ(old_index, proto_info->registry_slot());
proto_info->set_registry_slot(new_index);
}
void JSObject::MakePrototypesFast(DirectHandle<Object> receiver,
WhereToStart where_to_start,
Isolate* isolate) {
if (!IsJSReceiver(*receiver)) return;
for (PrototypeIterator iter(isolate, Cast<JSReceiver>(receiver),
where_to_start);
!iter.IsAtEnd(); iter.Advance()) {
DirectHandle<Object> current = PrototypeIterator::GetCurrent(iter);
#if V8_ENABLE_WEBASSEMBLY
if (IsWasmObject(*current)) continue;
#endif
if (!IsJSObjectThatCanBeTrackedAsPrototype(*current)) return;
DirectHandle<JSObject> current_obj = Cast<JSObject>(current);
Tagged<Map> current_map = current_obj->map();
if (current_map->is_prototype_map()) {
if (current_map->should_be_fast_prototype_map()) return;
DirectHandle<Map> map(current_map, isolate);
Map::SetShouldBeFastPrototypeMap(map, true, isolate);
JSObject::OptimizeAsPrototype(current_obj);
}
}
}
static bool PrototypeBenefitsFromNormalization(Tagged<JSObject> object) {
DisallowGarbageCollection no_gc;
if (!object->HasFastProperties()) return false;
if (IsJSGlobalProxy(object)) return false;
if (Isolate::Current()->bootstrapper()->IsActive()) return false;
if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL) return true;
return !object->map()->is_prototype_map() ||
!object->map()->should_be_fast_prototype_map();
}
void JSObject::OptimizeAsPrototype(DirectHandle<JSObject> object,
bool enable_setup_mode) {
DCHECK(IsJSObjectThatCanBeTrackedAsPrototype(*object));
if (IsJSGlobalObject(*object)) return;
Isolate* isolate = Isolate::Current();
if (object->map()->is_prototype_map()) {
if (enable_setup_mode && PrototypeBenefitsFromNormalization(*object)) {
DCHECK(!object->map()->should_be_fast_prototype_map());
constexpr bool kUseCache = true;
JSObject::NormalizeProperties(isolate, object, KEEP_INOBJECT_PROPERTIES,
0, kUseCache, "NormalizeAsPrototype");
}
if (!V8_DICT_PROPERTY_CONST_TRACKING_BOOL &&
object->map()->should_be_fast_prototype_map() &&
!object->HasFastProperties()) {
JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype");
}
} else {
DirectHandle<Map> new_map;
if (enable_setup_mode && PrototypeBenefitsFromNormalization(*object)) {
#if DEBUG
DirectHandle<Map> old_map(object->map(isolate), isolate);
#endif
constexpr bool kUseCache = false;
JSObject::NormalizeProperties(isolate, object, KEEP_INOBJECT_PROPERTIES,
0, kUseCache,
"NormalizeAndCopyAsPrototype");
DCHECK_NE(*old_map, object->map(isolate));
new_map = direct_handle(object->map(isolate), isolate);
} else {
new_map = Map::Copy(isolate, direct_handle(object->map(), isolate),
"CopyAsPrototype");
}
new_map->set_is_prototype_map(true);
Tagged<Object> maybe_constructor = new_map->GetConstructorRaw();
Tagged<Tuple2> tuple;
if (IsTuple2(maybe_constructor)) {
tuple = Cast<Tuple2>(maybe_constructor);
maybe_constructor = tuple->value1();
}
if (IsJSFunction(maybe_constructor)) {
Tagged<JSFunction> constructor = Cast<JSFunction>(maybe_constructor);
if (!constructor->shared()->IsApiFunction()) {
Tagged<NativeContext> context = constructor->native_context();
Tagged<JSFunction> object_function = context->object_function();
if (!tuple.is_null()) {
tuple->set_value1(object_function);
} else {
new_map->SetConstructor(object_function);
}
}
}
JSObject::MigrateToMap(isolate, object, new_map);
if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && !object->HasFastProperties()) {
ReadOnlyRoots roots(isolate);
DisallowHeapAllocation no_gc;
auto make_constant = [&](auto dict) {
for (InternalIndex index : dict->IterateEntries()) {
Tagged<Object> k;
if (!dict->ToKey(roots, index, &k)) continue;
PropertyDetails details = dict->DetailsAt(index);
details = details.CopyWithConstness(PropertyConstness::kConst);
dict->DetailsAtPut(index, details);
}
};
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
make_constant(object->property_dictionary_swiss());
} else {
make_constant(object->property_dictionary());
}
}
}
#ifdef DEBUG
bool should_be_dictionary = V8_DICT_PROPERTY_CONST_TRACKING_BOOL &&
enable_setup_mode && !IsJSGlobalProxy(*object) &&
!isolate->bootstrapper()->IsActive();
DCHECK_IMPLIES(should_be_dictionary,
object->map(isolate)->is_dictionary_map());
#endif
}
void JSObject::ReoptimizeIfPrototype(DirectHandle<JSObject> object) {
{
Tagged<Map> map = object->map();
if (!map->is_prototype_map()) return;
if (!map->should_be_fast_prototype_map()) return;
}
OptimizeAsPrototype(object);
}
void JSObject::LazyRegisterPrototypeUser(DirectHandle<Map> user,
Isolate* isolate) {
#if V8_ENABLE_WEBASSEMBLY
DCHECK(user->is_prototype_map() || IsWasmObjectMap(*user));
#else
DCHECK(user->is_prototype_map());
#endif
DirectHandle<Map> current_user = user;
DirectHandle<PrototypeInfo> current_user_info =
Map::GetOrCreatePrototypeInfo(user, isolate);
for (PrototypeIterator iter(isolate, user); !iter.IsAtEnd(); iter.Advance()) {
if (current_user_info->registry_slot() != PrototypeInfo::UNREGISTERED) {
break;
}
DirectHandle<Object> maybe_proto = PrototypeIterator::GetCurrent(iter);
if (!IsAnyObjectThatCanBeTrackedAsPrototype(*maybe_proto)) continue;
DirectHandle<JSReceiver> proto = Cast<JSReceiver>(maybe_proto);
DirectHandle<PrototypeInfo> proto_info =
Map::GetOrCreatePrototypeInfo(proto, isolate);
Handle<Object> maybe_registry(proto_info->prototype_users(), isolate);
Handle<WeakArrayList> registry =
IsSmi(*maybe_registry)
? handle(ReadOnlyRoots(isolate->heap()).empty_weak_array_list(),
isolate)
: Cast<WeakArrayList>(maybe_registry);
int slot = 0;
Handle<WeakArrayList> new_array =
PrototypeUsers::Add(isolate, registry, current_user, &slot);
current_user_info->set_registry_slot(slot);
if (!maybe_registry.is_identical_to(new_array)) {
proto_info->set_prototype_users(*new_array);
}
if (v8_flags.trace_prototype_users) {
PrintF("Registering %p as a user of prototype %p (map=%p).\n",
reinterpret_cast<void*>(current_user->ptr()),
reinterpret_cast<void*>(proto->ptr()),
reinterpret_cast<void*>(proto->map().ptr()));
}
current_user = direct_handle(proto->map(), isolate);
current_user_info = proto_info;
}
}
bool JSObject::UnregisterPrototypeUser(DirectHandle<Map> user,
Isolate* isolate) {
DCHECK(user->is_prototype_map());
if (!user->has_prototype_info()) return false;
DCHECK(IsPrototypeInfo(user->prototype_info()));
if (!IsAnyObjectThatCanBeTrackedAsPrototype(user->prototype())) {
Tagged<Object> users =
Cast<PrototypeInfo>(user->prototype_info())->prototype_users();
return IsWeakArrayList(users);
}
DirectHandle<JSReceiver> prototype(Cast<JSReceiver>(user->prototype()),
isolate);
DirectHandle<PrototypeInfo> user_info =
Map::GetOrCreatePrototypeInfo(user, isolate);
int slot = user_info->registry_slot();
if (slot == PrototypeInfo::UNREGISTERED) return false;
#if V8_ENABLE_WEBASSEMBLY
DCHECK(prototype->map()->is_prototype_map() ||
IsWasmObjectMap(prototype->map()));
#else
DCHECK(prototype->map()->is_prototype_map());
#endif
Tagged<Object> maybe_proto_info = prototype->map()->prototype_info();
DCHECK(IsPrototypeInfo(maybe_proto_info));
DirectHandle<PrototypeInfo> proto_info(Cast<PrototypeInfo>(maybe_proto_info),
isolate);
DirectHandle<WeakArrayList> prototype_users(
Cast<WeakArrayList>(proto_info->prototype_users()), isolate);
DCHECK_EQ(prototype_users->Get(slot), MakeWeak(*user));
PrototypeUsers::MarkSlotEmpty(*prototype_users, slot);
if (v8_flags.trace_prototype_users) {
PrintF("Unregistering %p as a user of prototype %p.\n",
reinterpret_cast<void*>(user->ptr()),
reinterpret_cast<void*>(prototype->ptr()));
}
return true;
}
namespace {
void InvalidateOnePrototypeValidityCellInternal(Tagged<Map> map) {
#if V8_ENABLE_WEBASSEMBLY
DCHECK(map->is_prototype_map() || IsWasmObjectMap(map));
#else
DCHECK(map->is_prototype_map());
#endif
if (v8_flags.trace_prototype_users) {
PrintF("Invalidating prototype map %p 's cell\n",
reinterpret_cast<void*>(map.ptr()));
}
Tagged<Object> maybe_cell = map->prototype_validity_cell(kRelaxedLoad);
if (maybe_cell != Map::kNoValidityCellSentinel) {
Tagged<Cell> cell = Cast<Cell>(maybe_cell);
if (cell->maybe_value() != Map::kPrototypeChainInvalid) {
cell->set_maybe_value(Map::kPrototypeChainInvalid, SKIP_WRITE_BARRIER);
}
}
Tagged<PrototypeInfo> prototype_info;
if (map->TryGetPrototypeInfo(&prototype_info)) {
prototype_info->set_prototype_chain_enum_cache(Smi::zero());
for (int i = 0; i < PrototypeInfo::kCachedHandlerCount; i++) {
prototype_info->set_cached_handler(i, Smi::zero(), SKIP_WRITE_BARRIER);
}
}
if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && map->is_dictionary_map()) {
Isolate* isolate = Isolate::Current();
DependentCode::DeoptimizeDependencyGroups(
isolate, map, DependentCode::kPrototypeCheckGroup);
}
}
void InvalidatePrototypeChainsInternal(Tagged<Map> map) {
Tagged<Map> next_map;
for (; !map.is_null(); map = next_map, next_map = Map()) {
InvalidateOnePrototypeValidityCellInternal(map);
Tagged<PrototypeInfo> proto_info;
if (!map->TryGetPrototypeInfo(&proto_info)) return;
if (!IsWeakArrayList(proto_info->prototype_users())) {
return;
}
Tagged<WeakArrayList> prototype_users =
Cast<WeakArrayList>(proto_info->prototype_users());
for (int i = PrototypeUsers::kFirstIndex; i < prototype_users->length();
++i) {
Tagged<HeapObject> heap_object;
if (prototype_users->Get(i).GetHeapObjectIfWeak(&heap_object) &&
IsMap(heap_object)) {
if (next_map.is_null()) {
next_map = Cast<Map>(heap_object);
} else {
InvalidatePrototypeChainsInternal(Cast<Map>(heap_object));
}
}
}
}
}
}
Tagged<Map> JSObject::InvalidatePrototypeChains(Tagged<Map> map) {
DisallowGarbageCollection no_gc;
InvalidatePrototypeChainsInternal(map);
return map;
}
void JSObject::InvalidatePrototypeValidityCell(Tagged<JSGlobalObject> global) {
DisallowGarbageCollection no_gc;
InvalidateOnePrototypeValidityCellInternal(global->map());
}
Maybe<bool> JSObject::SetPrototype(Isolate* isolate,
DirectHandle<JSObject> object,
DirectHandle<Object> value_obj,
bool from_javascript,
ShouldThrow should_throw) {
#ifdef DEBUG
int size = object->Size();
#endif
if (from_javascript) {
if (IsAccessCheckNeeded(*object) &&
!isolate->MayAccess(isolate->native_context(), object)) {
RETURN_ON_EXCEPTION_VALUE(
isolate, isolate->ReportFailedAccessCheck(object), Nothing<bool>());
UNREACHABLE();
}
} else {
DCHECK(!IsAccessCheckNeeded(*object));
}
DirectHandle<JSPrototype> value;
if (!TryCast(value_obj, &value)) return Just(true);
bool all_extensible = object->map()->is_extensible();
DirectHandle<JSObject> real_receiver = object;
if (from_javascript) {
PrototypeIterator iter(isolate, real_receiver, kStartAtPrototype,
PrototypeIterator::END_AT_NON_HIDDEN);
while (!iter.IsAtEnd()) {
real_receiver = PrototypeIterator::GetCurrent<JSObject>(iter);
iter.Advance();
all_extensible = all_extensible && real_receiver->map()->is_extensible();
}
}
DirectHandle<Map> map(real_receiver->map(), isolate);
if (map->prototype() == *value) return Just(true);
bool immutable_proto = map->is_immutable_proto();
if (immutable_proto) {
DirectHandle<Object> msg;
if (IsJSObjectPrototype(*object)) {
msg = isolate->factory()->Object_prototype_string();
} else {
msg = object;
}
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kImmutablePrototypeSet, msg));
}
if (!all_extensible) {
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kNonExtensibleProto, object));
}
if (Tagged<JSReceiver> receiver; TryCast<JSReceiver>(*value, &receiver)) {
for (PrototypeIterator iter(isolate, receiver, kStartAtReceiver);
!iter.IsAtEnd(); iter.Advance()) {
if (iter.GetCurrent<JSReceiver>() == *object) {
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kCyclicProto));
}
}
}
isolate->UpdateProtectorsOnSetPrototype(real_receiver, value);
DirectHandle<Map> new_map =
v8_flags.move_prototype_transitions_first
? MapUpdater(isolate, map).ApplyPrototypeTransition(value)
: Map::TransitionToUpdatePrototype(isolate, map, value);
DCHECK(new_map->prototype() == *value);
JSObject::MigrateToMap(isolate, real_receiver, new_map);
DCHECK_IMPLIES(!new_map->is_dictionary_map() && !map->is_deprecated() &&
!IsUndefined(new_map->GetBackPointer()),
size == object->Size());
return Just(true);
}
void JSObject::SetImmutableProto(Isolate* isolate,
DirectHandle<JSObject> object) {
DirectHandle<Map> map(object->map(), isolate);
if (map->is_immutable_proto()) return;
DirectHandle<Map> new_map = Map::TransitionToImmutableProto(isolate, map);
object->set_map(isolate, *new_map, kReleaseStore);
}
void JSObject::EnsureCanContainElements(Isolate* isolate,
DirectHandle<JSObject> object,
JavaScriptArguments* args,
uint32_t arg_count,
EnsureElementsMode mode) {
return EnsureCanContainElements(isolate, object,
FullObjectSlot(args->address_of_arg_at(0)),
arg_count, mode);
}
void JSObject::ValidateElements(Isolate* isolate, Tagged<JSObject> object) {
#ifdef ENABLE_SLOW_DCHECKS
if (v8_flags.enable_slow_asserts) {
object->GetElementsAccessor()->Validate(isolate, object);
}
#endif
}
bool JSObject::WouldConvertToSlowElements(uint32_t index) {
if (!HasFastElements()) return false;
uint32_t capacity = static_cast<uint32_t>(elements()->length());
uint32_t new_capacity;
return ShouldConvertToSlowElements(*this, capacity, index, &new_capacity);
}
static bool ShouldConvertToFastElements(Tagged<JSObject> object,
Tagged<NumberDictionary> dictionary,
uint32_t index,
uint32_t* new_capacity) {
if (dictionary->requires_slow_elements()) return false;
if (index >= static_cast<uint32_t>(Smi::kMaxValue)) return false;
if (IsJSArray(object)) {
Tagged<Object> length = Cast<JSArray>(object)->length();
if (!IsSmi(length)) return false;
*new_capacity = static_cast<uint32_t>(Smi::ToInt(length));
} else if (IsJSArgumentsObject(object)) {
return false;
} else {
*new_capacity = dictionary->max_number_key() + 1;
}
*new_capacity = std::max(index + 1, *new_capacity);
uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
NumberDictionary::kEntrySize;
return 2 * dictionary_size >= *new_capacity;
}
static ElementsKind BestFittingFastElementsKind(Tagged<JSObject> object) {
if (!object->map()->CanHaveFastTransitionableElementsKind()) {
return HOLEY_ELEMENTS;
}
if (object->HasSloppyArgumentsElements()) {
return FAST_SLOPPY_ARGUMENTS_ELEMENTS;
}
if (object->HasStringWrapperElements()) {
return FAST_STRING_WRAPPER_ELEMENTS;
}
DCHECK(object->HasDictionaryElements());
Tagged<NumberDictionary> dictionary = object->element_dictionary();
ElementsKind kind = HOLEY_SMI_ELEMENTS;
for (InternalIndex i : dictionary->IterateEntries()) {
Tagged<Object> key;
if (!dictionary->ToKey(GetReadOnlyRoots(), i, &key)) continue;
if (IsNumber(key)) {
Tagged<Object> value = dictionary->ValueAt(i);
if (!IsNumber(value)) return HOLEY_ELEMENTS;
if (!IsSmi(value)) {
if (!v8_flags.unbox_double_arrays) return HOLEY_ELEMENTS;
kind = HOLEY_DOUBLE_ELEMENTS;
}
}
}
return kind;
}
Maybe<bool> JSObject::AddDataElement(Isolate* isolate,
DirectHandle<JSObject> object,
uint32_t index, DirectHandle<Object> value,
PropertyAttributes attributes) {
DCHECK(object->map(isolate)->is_extensible());
uint32_t old_length = 0;
uint32_t new_capacity = 0;
if (IsJSArray(*object, isolate)) {
CHECK(Object::ToArrayLength(Cast<JSArray>(*object)->length(), &old_length));
}
ElementsKind kind = object->GetElementsKind(isolate);
Tagged<FixedArrayBase> elements = object->elements(isolate);
ElementsKind dictionary_kind = DICTIONARY_ELEMENTS;
if (IsSloppyArgumentsElementsKind(kind)) {
elements = Cast<SloppyArgumentsElements>(elements)->arguments();
dictionary_kind = SLOW_SLOPPY_ARGUMENTS_ELEMENTS;
} else if (IsStringWrapperElementsKind(kind)) {
dictionary_kind = SLOW_STRING_WRAPPER_ELEMENTS;
}
if (attributes != NONE) {
kind = dictionary_kind;
} else if (IsNumberDictionary(elements, isolate)) {
kind = ShouldConvertToFastElements(
*object, Cast<NumberDictionary>(elements), index, &new_capacity)
? BestFittingFastElementsKind(*object)
: dictionary_kind;
} else if (ShouldConvertToSlowElements(
*object, static_cast<uint32_t>(elements->length()), index,
&new_capacity)) {
kind = dictionary_kind;
}
ElementsKind to = Object::OptimalElementsKind(*value, isolate);
if (IsHoleyElementsKind(kind) || !IsJSArray(*object, isolate) ||
index > old_length) {
to = GetHoleyElementsKind(to);
kind = GetHoleyElementsKind(kind);
}
to = GetMoreGeneralElementsKind(kind, to);
ElementsAccessor* accessor = ElementsAccessor::ForKind(to);
MAYBE_RETURN(
accessor->Add(isolate, object, index, value, attributes, new_capacity),
Nothing<bool>());
if (IsJSArray(*object, isolate) && index >= old_length) {
DirectHandle<Number> new_length =
isolate->factory()->NewNumberFromUint(index + 1);
Cast<JSArray>(*object)->set_length(*new_length);
}
return Just(true);
}
template <AllocationSiteUpdateMode update_or_check>
bool JSObject::UpdateAllocationSite(Isolate* isolate,
DirectHandle<JSObject> object,
ElementsKind to_kind) {
if (!IsJSArray(*object)) return false;
if (!HeapLayout::InYoungGeneration(*object)) return false;
if (HeapLayout::InAnyLargeSpace(*object)) return false;
DirectHandle<AllocationSite> site;
{
DisallowGarbageCollection no_gc;
Heap* heap = isolate->heap();
Tagged<AllocationMemento> memento =
PretenuringHandler::FindAllocationMemento<
PretenuringHandler::kForRuntime>(heap, object->map(), *object);
if (memento.is_null()) return false;
site = direct_handle(memento->GetAllocationSite(), heap->isolate());
}
return AllocationSite::DigestTransitionFeedback<update_or_check>(
isolate, site, to_kind);
}
template bool
JSObject::UpdateAllocationSite<AllocationSiteUpdateMode::kCheckOnly>(
Isolate* isolate, DirectHandle<JSObject> object, ElementsKind to_kind);
template bool JSObject::UpdateAllocationSite<AllocationSiteUpdateMode::kUpdate>(
Isolate* isolate, DirectHandle<JSObject> object, ElementsKind to_kind);
void JSObject::TransitionElementsKind(Isolate* isolate,
DirectHandle<JSObject> object,
ElementsKind to_kind) {
ElementsKind from_kind = object->GetElementsKind();
if (IsHoleyElementsKind(from_kind)) {
to_kind = GetHoleyElementsKind(to_kind);
}
if (from_kind == to_kind) return;
DCHECK(IsFastElementsKind(from_kind) ||
IsNonextensibleElementsKind(from_kind));
DCHECK(IsFastElementsKind(to_kind) || IsNonextensibleElementsKind(to_kind));
DCHECK_NE(TERMINAL_FAST_ELEMENTS_KIND, from_kind);
UpdateAllocationSite(isolate, object, to_kind);
if (object->elements() == ReadOnlyRoots(isolate).empty_fixed_array() ||
IsDoubleElementsKind(from_kind) == IsDoubleElementsKind(to_kind)) {
DirectHandle<Map> new_map =
GetElementsTransitionMap(isolate, object, to_kind);
JSObject::MigrateToMap(isolate, object, new_map);
if (v8_flags.trace_elements_transitions) {
DirectHandle<FixedArrayBase> elms(object->elements(), isolate);
PrintElementsTransition(stdout, object, from_kind, elms, to_kind, elms);
}
} else {
DCHECK((IsSmiElementsKind(from_kind) && IsDoubleElementsKind(to_kind)) ||
(IsDoubleElementsKind(from_kind) && IsObjectElementsKind(to_kind)));
uint32_t c = static_cast<uint32_t>(object->elements()->length());
if (ElementsAccessor::ForKind(to_kind)
->GrowCapacityAndConvert(isolate, object, c)
.IsNothing()) {
FATAL(
"Fatal JavaScript invalid size error when transitioning elements "
"kind");
UNREACHABLE();
}
}
}
template <typename BackingStore>
static int HoleyElementsUsage(Tagged<JSObject> object,
Tagged<BackingStore> store) {
Isolate* isolate = Isolate::Current();
int limit = IsJSArray(object) ? Smi::ToInt(Cast<JSArray>(object)->length())
: store->length();
int used = 0;
for (int i = 0; i < limit; ++i) {
if (!store->is_the_hole(isolate, i)) ++used;
}
return used;
}
int JSObject::GetFastElementsUsage() {
Tagged<FixedArrayBase> store = elements();
switch (GetElementsKind()) {
case PACKED_SMI_ELEMENTS:
case PACKED_DOUBLE_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case PACKED_NONEXTENSIBLE_ELEMENTS:
case SHARED_ARRAY_ELEMENTS:
return IsJSArray(*this) ? Smi::ToInt(Cast<JSArray>(*this)->length())
: store->length();
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
store = Cast<SloppyArgumentsElements>(store)->arguments();
[[fallthrough]];
case HOLEY_SMI_ELEMENTS:
case HOLEY_ELEMENTS:
case HOLEY_FROZEN_ELEMENTS:
case HOLEY_SEALED_ELEMENTS:
case HOLEY_NONEXTENSIBLE_ELEMENTS:
case FAST_STRING_WRAPPER_ELEMENTS:
return HoleyElementsUsage(*this, Cast<FixedArray>(store));
case HOLEY_DOUBLE_ELEMENTS:
if (elements()->length() == 0) return 0;
return HoleyElementsUsage(*this, Cast<FixedDoubleArray>(store));
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
case SLOW_STRING_WRAPPER_ELEMENTS:
case DICTIONARY_ELEMENTS:
case WASM_ARRAY_ELEMENTS:
case NO_ELEMENTS:
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
UNREACHABLE();
}
return 0;
}
MaybeHandle<JSAny> JSObject::GetPropertyWithInterceptor(LookupIterator* it,
bool* done) {
DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
return GetPropertyWithInterceptorInternal(it, it->GetInterceptor(), done);
}
Maybe<bool> JSObject::HasRealNamedProperty(Isolate* isolate,
DirectHandle<JSObject> object,
DirectHandle<Name> name) {
PropertyKey key(isolate, name);
LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
return HasProperty(&it);
}
Maybe<bool> JSObject::HasRealElementProperty(Isolate* isolate,
DirectHandle<JSObject> object,
uint32_t index) {
LookupIterator it(isolate, object, index, object,
LookupIterator::OWN_SKIP_INTERCEPTOR);
return HasProperty(&it);
}
Maybe<bool> JSObject::HasRealNamedCallbackProperty(
Isolate* isolate, DirectHandle<JSObject> object, DirectHandle<Name> name) {
PropertyKey key(isolate, name);
LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
Maybe<PropertyAttributes> maybe_result = GetPropertyAttributes(&it);
return maybe_result.IsJust() ? Just(it.state() == LookupIterator::ACCESSOR)
: Nothing<bool>();
}
Tagged<Object> JSObject::RawFastPropertyAtCompareAndSwap(
FieldIndex index, Tagged<Object> expected, Tagged<Object> value,
SeqCstAccessTag tag) {
return HeapObject::SeqCst_CompareAndSwapField(
expected, value,
[=, this](Tagged<Object> expected_value, Tagged<Object> new_value) {
return RawFastPropertyAtCompareAndSwapInternal(index, expected_value,
new_value, tag);
});
}
bool JSGlobalProxy::IsDetached() { return !GetCreationContext().has_value(); }
void JSGlobalObject::InvalidatePropertyCell(DirectHandle<JSGlobalObject> global,
DirectHandle<Name> name) {
Isolate* isolate = Isolate::Current();
JSObject::InvalidatePrototypeValidityCell(*global);
DCHECK(!global->HasFastProperties());
auto dictionary =
direct_handle(global->global_dictionary(kAcquireLoad), isolate);
InternalIndex entry = dictionary->FindEntry(isolate, name);
if (entry.is_not_found()) return;
DirectHandle<PropertyCell> cell(dictionary->CellAt(entry), isolate);
DirectHandle<Object> value(cell->value(), isolate);
PropertyDetails details = cell->property_details();
details = details.set_cell_type(PropertyCellType::kMutable);
PropertyCell::InvalidateAndReplaceEntry(isolate, dictionary, entry, details,
value);
}
MaybeDirectHandle<JSDate> JSDate::New(Isolate* isolate,
DirectHandle<JSFunction> constructor,
DirectHandle<JSReceiver> new_target,
double tv) {
DirectHandle<JSDate> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
Cast<JSDate>(JSObject::New(constructor, new_target, {})));
if (DateCache::TryTimeClip(&tv)) {
result->SetValue(isolate, tv);
} else {
result->SetNanValue();
}
return result;
}
int64_t JSDate::CurrentTimeValue(Isolate* isolate) {
if (v8_flags.log_timer_events) LOG(isolate, CurrentTimeEvent());
if (v8_flags.correctness_fuzzer_suppressions) return 4;
return V8::GetCurrentPlatform()->CurrentClockTimeMilliseconds();
}
Address JSDate::GetField(Isolate* isolate, Address raw_object,
Address smi_index) {
DisallowGarbageCollection no_gc;
DisallowHandleAllocation no_handles;
DisallowJavascriptExecution no_js(isolate);
Tagged<Object> object(raw_object);
Tagged<Smi> index(smi_index);
return Cast<JSDate>(object)
->DoGetField(isolate, static_cast<FieldIndex>(index.value()))
.ptr();
}
Tagged<Object> JSDate::DoGetField(Isolate* isolate, FieldIndex index) {
DateCache* date_cache = isolate->date_cache();
if (index < kFirstUncachedField) {
Tagged<Object> stamp = cache_stamp();
if (stamp != isolate->date_cache_stamp() && IsSmi(stamp)) {
int64_t local_time_ms =
date_cache->ToLocal(static_cast<int64_t>(value()));
SetCachedFields(isolate, local_time_ms, date_cache);
}
switch (index) {
case kYear:
return year();
case kMonth:
return month();
case kDay:
return day();
case kWeekday:
return weekday();
case kHour:
return hour();
case kMinute:
return min();
case kSecond:
return sec();
default:
UNREACHABLE();
}
}
if (index >= kFirstUTCField) {
return GetUTCField(index, value(), date_cache);
}
double time = value();
if (std::isnan(time)) return GetReadOnlyRoots().nan_value();
int64_t local_time_ms = date_cache->ToLocal(static_cast<int64_t>(time));
int days = DateCache::DaysFromTime(local_time_ms);
if (index == kDays) return Smi::FromInt(days);
int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
if (index == kMillisecond) return Smi::FromInt(time_in_day_ms % 1000);
DCHECK_EQ(index, kTimeInDay);
return Smi::FromInt(time_in_day_ms);
}
Tagged<Object> JSDate::GetUTCField(FieldIndex index, double value,
DateCache* date_cache) {
DCHECK_GE(index, kFirstUTCField);
if (std::isnan(value)) return GetReadOnlyRoots().nan_value();
int64_t time_ms = static_cast<int64_t>(value);
if (index == kTimezoneOffset) {
return Smi::FromInt(date_cache->TimezoneOffset(time_ms));
}
int days = DateCache::DaysFromTime(time_ms);
if (index == kWeekdayUTC) return Smi::FromInt(date_cache->Weekday(days));
if (index <= kDayUTC) {
int year, month, day;
date_cache->YearMonthDayFromDays(days, &year, &month, &day);
if (index == kYearUTC) return Smi::FromInt(year);
if (index == kMonthUTC) return Smi::FromInt(month);
DCHECK_EQ(index, kDayUTC);
return Smi::FromInt(day);
}
int time_in_day_ms = DateCache::TimeInDay(time_ms, days);
switch (index) {
case kHourUTC:
return Smi::FromInt(time_in_day_ms / (60 * 60 * 1000));
case kMinuteUTC:
return Smi::FromInt((time_in_day_ms / (60 * 1000)) % 60);
case kSecondUTC:
return Smi::FromInt((time_in_day_ms / 1000) % 60);
case kMillisecondUTC:
return Smi::FromInt(time_in_day_ms % 1000);
case kDaysUTC:
return Smi::FromInt(days);
case kTimeInDayUTC:
return Smi::FromInt(time_in_day_ms);
default:
UNREACHABLE();
}
UNREACHABLE();
}
void JSDate::SetValue(Isolate* isolate, double value) {
#ifdef DEBUG
DCHECK(!std::isnan(value));
double clipped_value = value;
DCHECK(DateCache::TryTimeClip(&clipped_value));
DCHECK_EQ(value, clipped_value);
#endif
set_value(value);
DateCache* date_cache = isolate->date_cache();
int64_t local_time_ms = date_cache->ToLocal(static_cast<int64_t>(value));
SetCachedFields(isolate, local_time_ms, date_cache);
}
void JSDate::SetNanValue() {
set_value(std::numeric_limits<double>::quiet_NaN());
Tagged<HeapNumber> nan = GetReadOnlyRoots().nan_value();
set_cache_stamp(nan, SKIP_WRITE_BARRIER);
set_year(nan, SKIP_WRITE_BARRIER);
set_month(nan, SKIP_WRITE_BARRIER);
set_day(nan, SKIP_WRITE_BARRIER);
set_hour(nan, SKIP_WRITE_BARRIER);
set_min(nan, SKIP_WRITE_BARRIER);
set_sec(nan, SKIP_WRITE_BARRIER);
set_weekday(nan, SKIP_WRITE_BARRIER);
}
void JSDate::UpdateFieldsAfterDeserialization(Isolate* isolate) {
double time = value();
if (std::isnan(time)) {
return;
}
DateCache* date_cache = isolate->date_cache();
int64_t local_time_ms = date_cache->ToLocal(static_cast<int64_t>(time));
SetCachedFields(isolate, local_time_ms, date_cache);
}
void JSDate::SetCachedFields(Isolate* isolate, int64_t local_time_ms,
DateCache* date_cache) {
int days = DateCache::DaysFromTime(local_time_ms);
int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
int year, month, day;
date_cache->YearMonthDayFromDays(days, &year, &month, &day);
int weekday = date_cache->Weekday(days);
int hour = time_in_day_ms / (60 * 60 * 1000);
int min = (time_in_day_ms / (60 * 1000)) % 60;
int sec = (time_in_day_ms / 1000) % 60;
set_cache_stamp(isolate->GetDateCacheStampAndRecordUsage(),
SKIP_WRITE_BARRIER);
set_year(Smi::FromInt(year), SKIP_WRITE_BARRIER);
set_month(Smi::FromInt(month), SKIP_WRITE_BARRIER);
set_day(Smi::FromInt(day), SKIP_WRITE_BARRIER);
set_weekday(Smi::FromInt(weekday), SKIP_WRITE_BARRIER);
set_hour(Smi::FromInt(hour), SKIP_WRITE_BARRIER);
set_min(Smi::FromInt(min), SKIP_WRITE_BARRIER);
set_sec(Smi::FromInt(sec), SKIP_WRITE_BARRIER);
}
void JSMessageObject::InitializeSourcePositions(
Isolate* isolate, DirectHandle<JSMessageObject> message) {
DCHECK(!message->DidEnsureSourcePositionsAvailable());
Script::InitLineEnds(isolate, direct_handle(message->script(), isolate));
if (message->shared_info() == Smi::FromInt(-1)) {
message->set_shared_info(Smi::zero());
return;
}
DCHECK(IsSharedFunctionInfo(message->shared_info()));
DCHECK_GE(message->bytecode_offset().value(), kFunctionEntryBytecodeOffset);
Handle<SharedFunctionInfo> shared_info(
Cast<SharedFunctionInfo>(message->shared_info()), isolate);
IsCompiledScope is_compiled_scope;
SharedFunctionInfo::EnsureBytecodeArrayAvailable(
isolate, shared_info, &is_compiled_scope, CreateSourcePositions::kYes);
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared_info);
DCHECK(shared_info->HasBytecodeArray());
int position = shared_info->abstract_code(isolate)->SourcePosition(
isolate, message->bytecode_offset().value());
DCHECK_GE(position, 0);
message->set_start_position(position);
message->set_end_position(position + 1);
message->set_shared_info(Smi::zero());
}
int JSMessageObject::GetLineNumber() const {
DisallowGarbageCollection no_gc;
DCHECK(DidEnsureSourcePositionsAvailable());
if (start_position() == -1) return Message::kNoLineNumberInfo;
DCHECK(script()->has_line_ends());
DirectHandle<Script> the_script(script(), Isolate::Current());
Script::PositionInfo info;
if (!script()->GetPositionInfo(start_position(), &info)) {
return Message::kNoLineNumberInfo;
}
return info.line + 1;
}
int JSMessageObject::GetColumnNumber() const {
DisallowGarbageCollection no_gc;
DCHECK(DidEnsureSourcePositionsAvailable());
if (start_position() == -1) return -1;
DCHECK(script()->has_line_ends());
DirectHandle<Script> the_script(script(), Isolate::Current());
Script::PositionInfo info;
if (!script()->GetPositionInfo(start_position(), &info)) {
return -1;
}
return info.column;
}
Tagged<String> JSMessageObject::GetSource() const {
DisallowGarbageCollection no_gc;
Tagged<Script> script_object = script();
if (script_object->HasValidSource()) {
Tagged<Object> source = script_object->source();
if (IsString(source)) return Cast<String>(source);
}
return ReadOnlyRoots(Isolate::Current()).empty_string();
}
DirectHandle<String> JSMessageObject::GetSourceLine() const {
Isolate* isolate = Isolate::Current();
#if V8_ENABLE_WEBASSEMBLY
if (script()->type() == Script::Type::kWasm) {
return isolate->factory()->empty_string();
}
#endif
Script::PositionInfo info;
{
DisallowGarbageCollection no_gc;
DCHECK(DidEnsureSourcePositionsAvailable());
DCHECK(script()->has_line_ends());
if (!script()->GetPositionInfo(start_position(), &info)) {
return isolate->factory()->empty_string();
}
}
Handle<String> src(Cast<String>(script()->source()), isolate);
return isolate->factory()->NewSubString(src, info.line_start, info.line_end);
}
}