| // Copyright 2018 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_OBJECTS_JS_OBJECTS_INL_H_ |
| #define V8_OBJECTS_JS_OBJECTS_INL_H_ |
| |
| #include "src/objects/js-objects.h" |
| // Include the non-inl header before the rest of the headers. |
| |
| #include <optional> |
| |
| #include "src/common/globals.h" |
| #include "src/heap/heap-layout-inl.h" |
| #include "src/heap/heap-write-barrier.h" |
| #include "src/objects/cpp-heap-external-object.h" |
| #include "src/objects/cpp-heap-object-wrapper.h" |
| #include "src/objects/dictionary.h" |
| #include "src/objects/elements.h" |
| #include "src/objects/embedder-data-slot-inl.h" |
| #include "src/objects/feedback-vector.h" |
| #include "src/objects/field-index-inl.h" |
| #include "src/objects/fixed-array-base.h" |
| #include "src/objects/fixed-array.h" |
| #include "src/objects/fixed-primitive-array.h" |
| #include "src/objects/hash-table-inl.h" |
| #include "src/objects/heap-number-inl.h" |
| #include "src/objects/heap-object-field-inl.h" |
| #include "src/objects/heap-object-inl.h" |
| #include "src/objects/heap-object.h" |
| #include "src/objects/instance-type-inl.h" |
| #include "src/objects/js-interceptor-map-inl.h" |
| #include "src/objects/keys.h" |
| #include "src/objects/lookup-inl.h" |
| #include "src/objects/primitive-heap-object.h" |
| #include "src/objects/property-array-inl.h" |
| #include "src/objects/prototype-inl.h" |
| #include "src/objects/shared-function-info.h" |
| #include "src/objects/slots.h" |
| #include "src/objects/smi-inl.h" |
| #include "src/objects/string.h" |
| #include "src/objects/swiss-name-dictionary-inl.h" |
| |
| // Has to be the last include (doesn't have include guards): |
| #include "src/objects/object-macros.h" |
| |
| namespace v8::internal { |
| |
| Tagged<FixedArrayBase> JSObject::elements() const { return elements_.load(); } |
| |
| Tagged<FixedArrayBase> JSObject::elements(RelaxedLoadTag) const { |
| return elements_.Relaxed_Load(); |
| } |
| |
| void JSObject::set_elements(Tagged<FixedArrayBase> value, |
| WriteBarrierMode mode) { |
| elements_.Relaxed_Store(this, value, mode); |
| } |
| |
| MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate, |
| DirectHandle<JSReceiver> receiver, |
| DirectHandle<Name> name) { |
| LookupIterator it(isolate, receiver, name, receiver); |
| if (!it.IsFound()) return it.factory()->undefined_value(); |
| return Object::GetProperty(&it); |
| } |
| |
| MaybeHandle<Object> JSReceiver::GetElement(Isolate* isolate, |
| DirectHandle<JSReceiver> receiver, |
| uint32_t index) { |
| LookupIterator it(isolate, receiver, index, receiver); |
| if (!it.IsFound()) return it.factory()->undefined_value(); |
| return Object::GetProperty(&it); |
| } |
| |
| Handle<Object> JSReceiver::GetDataProperty(Isolate* isolate, |
| DirectHandle<JSReceiver> object, |
| DirectHandle<Name> name, |
| AllowAllocation allow_allocation) { |
| LookupIterator it(isolate, object, name, object, |
| LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); |
| if (!it.IsFound()) return it.factory()->undefined_value(); |
| return GetDataProperty(&it, allow_allocation); |
| } |
| |
| MaybeDirectHandle<JSPrototype> JSReceiver::GetPrototype( |
| Isolate* isolate, DirectHandle<JSReceiver> receiver) { |
| // We don't expect access checks to be needed on JSProxy objects. |
| DCHECK(!IsAccessCheckNeeded(*receiver) || IsJSObject(*receiver)); |
| |
| PrototypeIterator iter(isolate, receiver, kStartAtReceiver, |
| PrototypeIterator::END_AT_NON_HIDDEN); |
| do { |
| if (!iter.AdvanceFollowingProxies()) return {}; |
| } while (!iter.IsAtEnd()); |
| return PrototypeIterator::GetCurrent(iter); |
| } |
| |
| MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate, |
| DirectHandle<JSReceiver> receiver, |
| const char* name) { |
| DirectHandle<String> str = isolate->factory()->InternalizeUtf8String(name); |
| return GetProperty(isolate, receiver, str); |
| } |
| |
| // static |
| V8_WARN_UNUSED_RESULT MaybeDirectHandle<FixedArray> JSReceiver::OwnPropertyKeys( |
| Isolate* isolate, DirectHandle<JSReceiver> object) { |
| return KeyAccumulator::GetKeys(isolate, object, KeyCollectionMode::kOwnOnly, |
| ALL_PROPERTIES, |
| GetKeysConversion::kConvertToString); |
| } |
| |
| bool JSObject::PrototypeHasNoElements(Isolate* isolate, |
| Tagged<JSObject> object) { |
| DisallowGarbageCollection no_gc; |
| Tagged<HeapObject> prototype = Cast<HeapObject>(object->map()->prototype()); |
| ReadOnlyRoots roots(isolate); |
| Tagged<HeapObject> null = roots.null_value(); |
| Tagged<FixedArrayBase> empty_fixed_array = roots.empty_fixed_array(); |
| Tagged<FixedArrayBase> empty_slow_element_dictionary = |
| roots.empty_slow_element_dictionary(); |
| while (prototype != null) { |
| Tagged<Map> map = prototype->map(); |
| if (IsCustomElementsReceiverMap(map)) return false; |
| Tagged<FixedArrayBase> elements = Cast<JSObject>(prototype)->elements(); |
| if (elements != empty_fixed_array && |
| elements != empty_slow_element_dictionary) { |
| return false; |
| } |
| prototype = Cast<HeapObject>(map->prototype()); |
| } |
| return true; |
| } |
| |
| Tagged<JSReceiver::PropertiesOrHash> JSReceiver::raw_properties_or_hash() |
| const { |
| return properties_or_hash_.load(); |
| } |
| void JSReceiver::set_raw_properties_or_hash(Tagged<PropertiesOrHash> value, |
| WriteBarrierMode mode) { |
| properties_or_hash_.store(this, value, mode); |
| } |
| Tagged<JSReceiver::PropertiesOrHash> JSReceiver::raw_properties_or_hash( |
| RelaxedLoadTag tag) const { |
| return properties_or_hash_.Relaxed_Load(); |
| } |
| void JSReceiver::set_raw_properties_or_hash(Tagged<PropertiesOrHash> value, |
| RelaxedStoreTag, |
| WriteBarrierMode mode) { |
| properties_or_hash_.Relaxed_Store(this, value, mode); |
| } |
| |
| void JSObject::EnsureCanContainHeapObjectElements( |
| Isolate* isolate, DirectHandle<JSObject> object) { |
| JSObject::ValidateElements(isolate, *object); |
| ElementsKind elements_kind = object->map()->elements_kind(); |
| if (!IsObjectElementsKind(elements_kind)) { |
| if (IsHoleyElementsKind(elements_kind)) { |
| TransitionElementsKind(isolate, object, HOLEY_ELEMENTS); |
| } else { |
| TransitionElementsKind(isolate, object, PACKED_ELEMENTS); |
| } |
| } |
| } |
| |
| template <typename TSlot> |
| ElementsKind JSObject::GetTransitionedElementsKind(Isolate* isolate, |
| ElementsKind current_kind, |
| TSlot elements, |
| uint32_t count, |
| EnsureElementsMode mode) { |
| static_assert(std::is_same_v<TSlot, FullObjectSlot> || |
| std::is_same_v<TSlot, ObjectSlot>, |
| "Only ObjectSlot and FullObjectSlot are expected here"); |
| DisallowGarbageCollection no_gc; |
| if (current_kind == HOLEY_ELEMENTS) return current_kind; |
| ElementsKind target_kind = current_kind; |
| |
| DCHECK(mode != ALLOW_COPIED_DOUBLE_ELEMENTS); |
| bool is_holey = IsHoleyElementsKind(current_kind); |
| Tagged<Object> the_hole = GetReadOnlyRoots().the_hole_value(); |
| TSlot end = elements + count; |
| for (; elements < end; ++elements) { |
| Tagged<Object> current = *elements; |
| if (current == the_hole) { |
| is_holey = true; |
| target_kind = GetHoleyElementsKind(target_kind); |
| #ifdef V8_ENABLE_UNDEFINED_DOUBLE |
| } else if (IsUndefined(current)) { |
| if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS) { |
| if (IsSmiElementsKind(target_kind)) { |
| target_kind = HOLEY_DOUBLE_ELEMENTS; |
| } else if (target_kind == PACKED_DOUBLE_ELEMENTS) { |
| target_kind = HOLEY_DOUBLE_ELEMENTS; |
| } else { |
| DCHECK(target_kind == PACKED_ELEMENTS || |
| target_kind == HOLEY_ELEMENTS || |
| target_kind == HOLEY_DOUBLE_ELEMENTS); |
| } |
| } else if (is_holey) { |
| if (IsSmiElementsKind(target_kind)) { |
| return HOLEY_ELEMENTS; |
| } |
| } else { |
| target_kind = PACKED_ELEMENTS; |
| } |
| #endif // V8_ENABLE_UNDEFINED_DOUBLE |
| } else if (!IsSmi(current)) { |
| if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS && IsNumber(current)) { |
| if (IsSmiElementsKind(target_kind)) { |
| if (is_holey) { |
| target_kind = HOLEY_DOUBLE_ELEMENTS; |
| } else { |
| target_kind = PACKED_DOUBLE_ELEMENTS; |
| } |
| } |
| } else if (is_holey) { |
| return HOLEY_ELEMENTS; |
| } else { |
| target_kind = PACKED_ELEMENTS; |
| } |
| } |
| } |
| |
| return target_kind; |
| } |
| |
| template <typename TSlot> |
| void JSObject::EnsureCanContainElements(Isolate* isolate, |
| DirectHandle<JSObject> object, |
| TSlot objects, uint32_t count, |
| EnsureElementsMode mode) { |
| static_assert(std::is_same_v<TSlot, FullObjectSlot> || |
| std::is_same_v<TSlot, ObjectSlot>, |
| "Only ObjectSlot and FullObjectSlot are expected here"); |
| ElementsKind current_kind = object->GetElementsKind(); |
| ElementsKind target_kind = |
| GetTransitionedElementsKind(isolate, current_kind, objects, count, mode); |
| if (target_kind != current_kind) { |
| TransitionElementsKind(isolate, object, target_kind); |
| } |
| } |
| |
| void JSObject::EnsureCanContainElements(Isolate* isolate, |
| DirectHandle<JSObject> object, |
| DirectHandle<FixedArrayBase> elements, |
| uint32_t length, |
| EnsureElementsMode mode) { |
| ReadOnlyRoots roots = GetReadOnlyRoots(); |
| if (elements->map() != roots.fixed_double_array_map()) { |
| DCHECK(elements->map() == roots.fixed_array_map() || |
| elements->map() == roots.fixed_cow_array_map()); |
| if (mode == ALLOW_COPIED_DOUBLE_ELEMENTS) { |
| mode = DONT_ALLOW_DOUBLE_ELEMENTS; |
| } |
| ObjectSlot objects = Cast<FixedArray>(elements)->RawFieldOfFirstElement(); |
| EnsureCanContainElements(isolate, object, objects, length, mode); |
| return; |
| } |
| |
| DCHECK(mode == ALLOW_COPIED_DOUBLE_ELEMENTS); |
| if (object->GetElementsKind() == HOLEY_SMI_ELEMENTS) { |
| TransitionElementsKind(isolate, object, HOLEY_DOUBLE_ELEMENTS); |
| } else if (object->GetElementsKind() == PACKED_SMI_ELEMENTS) { |
| auto double_array = Cast<FixedDoubleArray>(elements); |
| for (uint32_t i = 0; i < length; ++i) { |
| if (double_array->is_the_hole(i)) { |
| TransitionElementsKind(isolate, object, HOLEY_DOUBLE_ELEMENTS); |
| return; |
| } |
| } |
| TransitionElementsKind(isolate, object, PACKED_DOUBLE_ELEMENTS); |
| } |
| } |
| |
| void JSObject::SetMapAndElements(Isolate* isolate, |
| DirectHandle<JSObject> object, |
| DirectHandle<Map> new_map, |
| DirectHandle<FixedArrayBase> value) { |
| JSObject::MigrateToMap(isolate, object, new_map); |
| DCHECK((object->map()->has_fast_smi_or_object_elements() || |
| (*value == ReadOnlyRoots(isolate).empty_fixed_array()) || |
| object->map()->has_fast_string_wrapper_elements()) == |
| (value->map() == ReadOnlyRoots(isolate).fixed_array_map() || |
| value->map() == ReadOnlyRoots(isolate).fixed_cow_array_map())); |
| DCHECK((*value == ReadOnlyRoots(isolate).empty_fixed_array()) || |
| (object->map()->has_fast_double_elements() == |
| IsFixedDoubleArray(*value))); |
| object->set_elements(*value); |
| } |
| |
| void JSObject::initialize_elements() { |
| Tagged<FixedArrayBase> elements = map()->GetInitialElements(); |
| set_elements(elements, SKIP_WRITE_BARRIER); |
| } |
| |
| DEF_GETTER(JSObject, GetIndexedInterceptor, Tagged<InterceptorInfo>) { |
| return map()->GetIndexedInterceptor(); |
| } |
| |
| DEF_GETTER(JSObject, GetNamedInterceptor, Tagged<InterceptorInfo>) { |
| return map()->GetNamedInterceptor(); |
| } |
| |
| // static |
| int JSObject::GetHeaderSize(Tagged<Map> map) { |
| // Check for the most common kind of JavaScript object before |
| // falling into the generic switch. This speeds up the internal |
| // field operations considerably on average. |
| InstanceType instance_type = map->instance_type(); |
| return instance_type == JS_OBJECT_TYPE ? JSObject::kHeaderSize |
| : GetHeaderSize(instance_type); |
| } |
| |
| // static |
| int JSObject::GetEmbedderFieldsStartOffset(Tagged<Map> map) { |
| // Embedder fields are located after the object header. |
| return GetHeaderSize(map); |
| } |
| |
| int JSObject::GetEmbedderFieldsStartOffset() { |
| return GetEmbedderFieldsStartOffset(map()); |
| } |
| |
| // static |
| bool JSObject::MayHaveEmbedderFields(Tagged<Map> map) { |
| InstanceType instance_type = map->instance_type(); |
| // TODO(v8) It'd be nice if all objects with embedder data slots inherited |
| // from JSObjectJSAPIObjectWithEmbedderSlotsWithEmbedderSlots, but this is |
| // currently not possible due to instance_type constraints. |
| return InstanceTypeChecker::IsJSObjectWithEmbedderSlots(instance_type) || |
| InstanceTypeChecker::IsJSAPIObjectWithEmbedderSlots(instance_type) || |
| InstanceTypeChecker::IsJSSpecialObject(instance_type); |
| } |
| |
| bool JSObject::MayHaveEmbedderFields() const { |
| return MayHaveEmbedderFields(map()); |
| } |
| |
| // static |
| int JSObject::GetEmbedderFieldCount(Tagged<Map> map) { |
| // We inline some code from Map::instance_size and Map::GetInObjectProperties |
| // here, to avoid reading the map's instance size field twice. |
| // See https://crbug.com/355120682. |
| int instance_size_in_words = map->instance_size_in_words(); |
| int instance_size = instance_size_in_words << kTaggedSizeLog2; |
| if (instance_size == kVariableSizeSentinel) return 0; |
| // Embedder fields are located after the object header, whereas in-object |
| // properties are located at the end of the object. We don't have to round up |
| // the header size here because division by kEmbedderDataSlotSizeInTaggedSlots |
| // will swallow potential padding in case of (kTaggedSize != |
| // kSystemPointerSize) anyway. |
| int in_object_properties = |
| instance_size_in_words - map->GetInObjectPropertiesStartInWords(); |
| return (((instance_size - GetEmbedderFieldsStartOffset(map)) >> |
| kTaggedSizeLog2) - |
| in_object_properties) / |
| kEmbedderDataSlotSizeInTaggedSlots; |
| } |
| |
| int JSObject::GetEmbedderFieldCount() const { |
| return GetEmbedderFieldCount(map()); |
| } |
| |
| int JSObject::GetEmbedderFieldOffset(int index) { |
| DCHECK_LT(static_cast<unsigned>(index), |
| static_cast<unsigned>(GetEmbedderFieldCount())); |
| return GetEmbedderFieldsStartOffset() + (kEmbedderDataSlotSize * index); |
| } |
| |
| Tagged<Object> JSObject::GetEmbedderField(int index) { |
| return EmbedderDataSlot(this, index).load_tagged(); |
| } |
| |
| void JSObject::SetEmbedderField(int index, Tagged<Object> value) { |
| EmbedderDataSlot::store_tagged(this, index, value); |
| } |
| |
| void JSObject::SetEmbedderField(int index, Tagged<Smi> value) { |
| EmbedderDataSlot(this, index).store_smi(value); |
| } |
| |
| // static |
| bool JSObject::IsDroppableApiObject(const Tagged<Map> map) { |
| auto instance_type = map->instance_type(); |
| return InstanceTypeChecker::IsJSApiObject(instance_type) || |
| instance_type == JS_SPECIAL_API_OBJECT_TYPE; |
| } |
| |
| bool JSObject::IsDroppableApiObject() const { |
| return IsDroppableApiObject(map()); |
| } |
| |
| // Access fast-case object properties at index. The use of these routines |
| // is needed to correctly distinguish between properties stored in-object and |
| // properties stored in the properties array. |
| |
| // Type system violation: the declared return type Tagged<JSAny> is incorrect. |
| // Property slots can also hold non-JSAny values such as the Hole sentinel |
| // (`uninitialized_value` during MigrateFastToFast) and class metadata Structs |
| // (e.g. ClassPositions, AccessorPair, AccessorInfo). The legacy code |
| // reinterpret-loaded the slot as JSAny without validation; callers dispatch |
| // on the descriptor's PropertyKind to know what they actually got. |
| // TODO(jgruber): Change this to return |
| // Tagged<UnionOf<JSAny, Hole, ClassPositions, AccessorPair, AccessorInfo>> |
| // (or Tagged<Object>) and update the ~50 callers to Cast<JSAny> (or |
| // whatever narrower type they actually expect) at the use site. |
| Tagged<JSAny> JSObject::RawFastPropertyAt(FieldIndex index) const { |
| if (index.is_inobject()) { |
| return TaggedField<JSAny>::Relaxed_Load(this, index.offset()); |
| } else { |
| return UncheckedCast<JSAny>( |
| property_array()->get(index.outobject_array_index())); |
| } |
| } |
| |
| // The SeqCst versions of RawFastPropertyAt are used for atomically accessing |
| // shared struct fields. |
| |
| // See the TODO(jgruber) on the non-SeqCst overload above. |
| Tagged<JSAny> JSObject::RawFastPropertyAt(FieldIndex index, |
| SeqCstAccessTag tag) const { |
| if (index.is_inobject()) { |
| return TaggedField<JSAny>::SeqCst_Load(this, index.offset()); |
| } else { |
| return UncheckedCast<JSAny>( |
| property_array()->get(index.outobject_array_index(), tag)); |
| } |
| } |
| |
| std::optional<Tagged<Object>> JSObject::RawInobjectPropertyAt( |
| Tagged<Map> original_map, FieldIndex index) const { |
| CHECK(index.is_inobject()); |
| |
| // This method implements a "snapshot" protocol to protect against reading out |
| // of bounds of an object. It's used to access a fast in-object property from |
| // a background thread with no locking. That caller does have the guarantee |
| // that a garbage collection cannot happen during its query. However, it must |
| // contend with the main thread altering the object in heavy ways through |
| // object migration. Specifically, the object can get smaller. Initially, this |
| // may seem benign, because object migration fills the freed-up space with |
| // FillerMap words which, even though they offer wrong values, are at |
| // least tagged values. |
| |
| // However, there is an additional danger. Sweeper threads may discover the |
| // filler words and offer that space to the main thread for allocation. Should |
| // a HeapNumber be allocated into that space while we're reading a property at |
| // that location (from our out-of-date information), we risk interpreting a |
| // double value as a pointer. This must be prevented. |
| // |
| // We do this by: |
| // |
| // a) Reading the map first |
| // b) Reading the property with acquire semantics (but do not inspect it!) |
| // c) Re-read the map with acquire semantics. |
| // |
| // Only if the maps match can the property be inspected. It may have a "wrong" |
| // value, but it will be within the bounds of the objects instance size as |
| // given by the map and it will be a valid Smi or object pointer. |
| Tagged<Object> maybe_tagged_object = |
| TaggedField<Object>::Acquire_Load(this, index.offset()); |
| if (original_map != map(kAcquireLoad)) return {}; |
| return maybe_tagged_object; |
| } |
| |
| void JSObject::RawFastInobjectPropertyAtPut(FieldIndex index, |
| Tagged<Object> value, |
| WriteBarrierMode mode) { |
| DCHECK(index.is_inobject()); |
| int offset = index.offset(); |
| RELAXED_WRITE_FIELD(this, offset, value); |
| CONDITIONAL_WRITE_BARRIER(this, offset, value, mode); |
| } |
| |
| void JSObject::RawFastInobjectPropertyAtPut(FieldIndex index, |
| Tagged<Object> value, |
| SeqCstAccessTag tag) { |
| DCHECK(index.is_inobject()); |
| DCHECK(IsShared(value)); |
| SEQ_CST_WRITE_FIELD(this, index.offset(), value); |
| CONDITIONAL_WRITE_BARRIER(this, index.offset(), value, UPDATE_WRITE_BARRIER); |
| } |
| |
| void JSObject::FastPropertyAtPut(FieldIndex index, Tagged<Object> value, |
| WriteBarrierMode mode) { |
| if (index.is_inobject()) { |
| RawFastInobjectPropertyAtPut(index, value, mode); |
| } else { |
| DCHECK_EQ(UPDATE_WRITE_BARRIER, mode); |
| property_array()->set(index.outobject_array_index(), value); |
| } |
| } |
| |
| void JSObject::FastPropertyAtPut(FieldIndex index, Tagged<Object> value, |
| SeqCstAccessTag tag) { |
| if (index.is_inobject()) { |
| RawFastInobjectPropertyAtPut(index, value, tag); |
| } else { |
| property_array()->set(index.outobject_array_index(), value, tag); |
| } |
| } |
| |
| void JSObject::WriteToField(InternalIndex descriptor, PropertyDetails details, |
| Tagged<Object> value) { |
| DCHECK_EQ(PropertyLocation::kField, details.location()); |
| DCHECK_EQ(PropertyKind::kData, details.kind()); |
| DisallowGarbageCollection no_gc; |
| FieldIndex index = FieldIndex::ForDetails(map(), details); |
| if (details.representation().IsDouble()) { |
| // Manipulating the signaling NaN used for the hole and uninitialized |
| // double field sentinel in C++, e.g. with base::bit_cast or |
| // value()/set_value(), will change its value on ia32 (the x87 stack is used |
| // to return values and stores to the stack silently clear the signalling |
| // bit). |
| uint64_t bits; |
| if (IsSmi(value)) { |
| bits = base::bit_cast<uint64_t>(static_cast<double>(Smi::ToInt(value))); |
| } else if (IsUninitializedHole(value)) { |
| bits = kHoleNanInt64; |
| } else { |
| DCHECK(IsHeapNumber(value)); |
| bits = Cast<HeapNumber>(value)->value_as_bits(); |
| } |
| auto box = Cast<HeapNumber>(RawFastPropertyAt(index)); |
| box->set_value_as_bits(bits); |
| } else { |
| FastPropertyAtPut(index, value); |
| } |
| } |
| |
| Tagged<Object> JSObject::RawFastInobjectPropertyAtSwap(FieldIndex index, |
| Tagged<Object> value, |
| SeqCstAccessTag tag) { |
| DCHECK(index.is_inobject()); |
| DCHECK(IsShared(value)); |
| int offset = index.offset(); |
| Tagged<Object> old_value = SEQ_CST_SWAP_FIELD(this, offset, value); |
| CONDITIONAL_WRITE_BARRIER(this, offset, value, UPDATE_WRITE_BARRIER); |
| return old_value; |
| } |
| |
| Tagged<Object> JSObject::RawFastPropertyAtSwap(FieldIndex index, |
| Tagged<Object> value, |
| SeqCstAccessTag tag) { |
| if (index.is_inobject()) { |
| return RawFastInobjectPropertyAtSwap(index, value, tag); |
| } |
| return property_array()->Swap(index.outobject_array_index(), value, tag); |
| } |
| |
| Tagged<Object> JSObject::RawFastInobjectPropertyAtCompareAndSwap( |
| FieldIndex index, Tagged<Object> expected, Tagged<Object> value, |
| SeqCstAccessTag tag) { |
| DCHECK(index.is_inobject()); |
| DCHECK(IsShared(value)); |
| Tagged<Object> previous_value = |
| SEQ_CST_COMPARE_AND_SWAP_FIELD(this, index.offset(), expected, value); |
| if (previous_value == expected) { |
| CONDITIONAL_WRITE_BARRIER(this, index.offset(), value, |
| UPDATE_WRITE_BARRIER); |
| } |
| return previous_value; |
| } |
| |
| Tagged<Object> JSObject::RawFastPropertyAtCompareAndSwapInternal( |
| FieldIndex index, Tagged<Object> expected, Tagged<Object> value, |
| SeqCstAccessTag tag) { |
| if (index.is_inobject()) { |
| return RawFastInobjectPropertyAtCompareAndSwap(index, expected, value, tag); |
| } |
| return property_array()->CompareAndSwap(index.outobject_array_index(), |
| expected, value, tag); |
| } |
| |
| int JSObject::GetInObjectPropertyOffset(int index) { |
| return map()->GetInObjectPropertyOffset(index); |
| } |
| |
| Tagged<Object> JSObject::InObjectPropertyAtOffset(int offset) { |
| DCHECK_GE(offset, GetInObjectPropertyOffset(0)); |
| DCHECK_LT(offset, Size()); |
| return TaggedField<Object>::load(this, offset); |
| } |
| |
| Tagged<Object> JSObject::InObjectPropertyPutAtIndex(int index, |
| Tagged<Object> value, |
| WriteBarrierMode mode) { |
| // Adjust for the number of properties stored in the object. |
| return InObjectPropertyPutAtOffset(GetInObjectPropertyOffset(index), value, |
| mode); |
| } |
| |
| Tagged<Object> JSObject::InObjectPropertyPutAtOffset(int offset, |
| Tagged<Object> value, |
| WriteBarrierMode mode) { |
| DCHECK_GE(offset, GetInObjectPropertyOffset(0)); |
| DCHECK_LT(offset, Size()); |
| WRITE_FIELD(this, offset, value); |
| CONDITIONAL_WRITE_BARRIER(this, offset, value, mode); |
| return value; |
| } |
| |
| void JSObject::InitializeBody(Tagged<Map> map, int start_offset, |
| bool is_slack_tracking_in_progress, |
| NewJSObjectType new_js_object_type) { |
| const uint32_t size = map->instance_size(); |
| uint32_t current_offset = start_offset; |
| |
| #if V8_STATIC_ROOTS_BOOL |
| using FillerType = Tagged_t; |
| constexpr Tagged_t undefined_filler = StaticReadOnlyRoot::kUndefinedValue; |
| constexpr Tagged_t filler_map = StaticReadOnlyRoot::kOnePointerFillerMap; |
| #else // !STATIC_ROOTS_BOOL |
| using FillerType = Tagged<HeapObject>; |
| Tagged<Undefined> undefined_filler = GetReadOnlyRoots().undefined_value(); |
| Tagged<Map> filler_map = GetReadOnlyRoots().one_pointer_filler_map(); |
| #endif // !STATIC_ROOTS_BOOL |
| |
| // Fills memory at current offset and moves forward the offset. |
| const auto FillMemoryTaggedUntil = |
| [this, ¤t_offset, size](uint32_t end_offset, FillerType filler) { |
| DCHECK_LE(kHeaderSize, current_offset); |
| DCHECK_LE(current_offset, end_offset); |
| DCHECK_LE(end_offset, size); |
| USE(size); |
| // TODO(442989225): This loop should ideally be just a MemsetTagged(). |
| // Unfortunately, this currently results in performance regressions. |
| while (current_offset < end_offset) { |
| #if V8_STATIC_ROOTS_BOOL |
| RELAXED_WRITE_UINT32_FIELD(this, current_offset, filler); |
| #else // !STATIC_ROOTS_BOOL |
| WRITE_FIELD(this, current_offset, filler); |
| #endif // !STATIC_ROOTS_BOOL |
| current_offset += kTaggedSize; |
| } |
| DCHECK_LE(current_offset, size); |
| }; |
| |
| DCHECK_IMPLIES( |
| new_js_object_type == NewJSObjectType::kNoEmbedderFieldsAndNoApiWrapper, |
| !MayHaveEmbedderFields(map)); |
| DCHECK_IMPLIES(!MayHaveEmbedderFields(map), GetEmbedderFieldCount(map) == 0); |
| if (new_js_object_type != NewJSObjectType::kNoEmbedderFieldsAndNoApiWrapper && |
| MayHaveEmbedderFields(map)) { |
| const uint32_t embedder_field_start = GetEmbedderFieldsStartOffset(map); |
| // Fill start with references to the undefined value object. |
| FillMemoryTaggedUntil(embedder_field_start, undefined_filler); |
| |
| // Initialize embedder data slots. |
| DCHECK_EQ(current_offset, embedder_field_start); |
| for (int i = 0; i < GetEmbedderFieldCount(map); i++) { |
| // TODO(v8): consider initializing embedded data slots with Smi::zero(). |
| EmbedderDataSlot(this, i).Initialize( |
| GetReadOnlyRoots().undefined_value()); |
| current_offset += kEmbedderDataSlotSize; |
| } |
| } |
| |
| DCHECK_LE(current_offset, size); |
| if (is_slack_tracking_in_progress) { |
| const uint32_t end_of_pre_allocated_offset = |
| size - (map->UnusedPropertyFields() * kTaggedSize); |
| // Fill pre allocated slots with references to the undefined value object. |
| FillMemoryTaggedUntil(end_of_pre_allocated_offset, undefined_filler); |
| // Fill the remainder with one word filler objects (ie just a map word) |
| FillMemoryTaggedUntil(size, filler_map); |
| } else { |
| FillMemoryTaggedUntil(size, undefined_filler); |
| } |
| } |
| |
| template <typename T, template <typename> typename HandleType> |
| requires(std::is_convertible_v<HandleType<T>, DirectHandle<T>>) |
| inline typename HandleType<Object>::MaybeType |
| JSObject::DefineOwnPropertyIgnoreAttributes(LookupIterator* it, |
| HandleType<T> value, |
| PropertyAttributes attributes, |
| AccessorInfoHandling handling, |
| EnforceDefineSemantics semantics) { |
| MAYBE_RETURN_NULL(DefineOwnPropertyIgnoreAttributes( |
| it, value, attributes, Just(ShouldThrow::kThrowOnError), handling, |
| semantics)); |
| return value; |
| } |
| |
| void* JSExternalObject::value(ExternalPointerTagRange tag_range) const { |
| i::IsolateForSandbox isolate = GetCurrentIsolateForSandbox(); |
| return value(isolate, tag_range); |
| } |
| |
| void* JSExternalObject::value(i::IsolateForSandbox isolate, |
| ExternalPointerTagRange tag_range) const { |
| // Accessors take a runtime tag / tag_range (callers pick it dynamically |
| // based on v8::External tag values); ExternalPointerMember's template |
| // member variants require compile-time tags, so route through the |
| // field_address-based free functions instead. |
| Address result = ::v8::internal::ReadExternalPointerField( |
| value_.storage_address(), isolate, tag_range); |
| return reinterpret_cast<void*>(result); |
| } |
| |
| void JSExternalObject::init_value(i::IsolateForSandbox isolate, |
| ExternalPointerTag tag, void* initial_value) { |
| ::v8::internal::InitExternalPointerField( |
| reinterpret_cast<Address>(this), value_.storage_address(), isolate, tag, |
| reinterpret_cast<Address>(initial_value)); |
| } |
| |
| void JSExternalObject::set_value(i::IsolateForSandbox isolate, |
| ExternalPointerTag tag, void* value) { |
| ::v8::internal::WriteExternalPointerField( |
| value_.storage_address(), isolate, tag, reinterpret_cast<Address>(value)); |
| } |
| |
| Tagged<String> JSStringIterator::string() const { return string_.load(); } |
| |
| void JSStringIterator::set_string(Tagged<String> value, WriteBarrierMode mode) { |
| string_.store(this, value, mode); |
| } |
| |
| int JSStringIterator::index() const { return index_.load().value(); } |
| |
| void JSStringIterator::set_index(int value) { |
| index_.store(this, Smi::FromInt(value)); |
| } |
| |
| Tagged<JSReceiver> JSAsyncFromSyncIterator::sync_iterator() const { |
| return sync_iterator_.load(); |
| } |
| |
| void JSAsyncFromSyncIterator::set_sync_iterator(Tagged<JSReceiver> value, |
| WriteBarrierMode mode) { |
| sync_iterator_.store(this, value, mode); |
| } |
| |
| Tagged<Object> JSAsyncFromSyncIterator::next() const { return next_.load(); } |
| |
| void JSAsyncFromSyncIterator::set_next(Tagged<Object> value, |
| WriteBarrierMode mode) { |
| next_.store(this, value, mode); |
| } |
| |
| Tagged<JSAny> JSPrimitiveWrapper::value() const { return value_.load(); } |
| |
| void JSPrimitiveWrapper::set_value(Tagged<JSAny> value, WriteBarrierMode mode) { |
| value_.store(this, value, mode); |
| } |
| |
| Tagged<JSReceiver> JSValidIteratorWrapper::underlying_object() const { |
| return underlying_object_.load(); |
| } |
| |
| void JSValidIteratorWrapper::set_underlying_object(Tagged<JSReceiver> value, |
| WriteBarrierMode mode) { |
| underlying_object_.store(this, value, mode); |
| } |
| |
| Tagged<JSAny> JSValidIteratorWrapper::underlying_next() const { |
| return underlying_next_.load(); |
| } |
| |
| void JSValidIteratorWrapper::set_underlying_next(Tagged<JSAny> value, |
| WriteBarrierMode mode) { |
| underlying_next_.store(this, value, mode); |
| } |
| |
| double JSDate::value() const { return value_.value(); } |
| void JSDate::set_value(double v) { value_.set_value(v); } |
| |
| #define DEFINE_JSDATE_CACHED_ACCESSOR(name) \ |
| Tagged<UnionOf<Smi, HeapNumber>> JSDate::name() const { \ |
| return name##_.load(); \ |
| } \ |
| void JSDate::set_##name(Tagged<UnionOf<Smi, HeapNumber>> value, \ |
| WriteBarrierMode mode) { \ |
| name##_.store(this, value, mode); \ |
| } |
| DEFINE_JSDATE_CACHED_ACCESSOR(year) |
| DEFINE_JSDATE_CACHED_ACCESSOR(month) |
| DEFINE_JSDATE_CACHED_ACCESSOR(day) |
| DEFINE_JSDATE_CACHED_ACCESSOR(weekday) |
| DEFINE_JSDATE_CACHED_ACCESSOR(hour) |
| DEFINE_JSDATE_CACHED_ACCESSOR(min) |
| DEFINE_JSDATE_CACHED_ACCESSOR(sec) |
| DEFINE_JSDATE_CACHED_ACCESSOR(cache_stamp) |
| #undef DEFINE_JSDATE_CACHED_ACCESSOR |
| |
| bool JSMessageObject::DidEnsureSourcePositionsAvailable() const { |
| return shared_info() == Smi::zero(); |
| } |
| |
| // static |
| void JSMessageObject::EnsureSourcePositionsAvailable( |
| Isolate* isolate, DirectHandle<JSMessageObject> message) { |
| if (message->DidEnsureSourcePositionsAvailable()) { |
| DCHECK(message->script()->has_line_ends()); |
| } else { |
| JSMessageObject::InitializeSourcePositions(isolate, message); |
| } |
| } |
| |
| int JSMessageObject::GetStartPosition() const { |
| // TODO(cbruni): make this DCHECK stricter (>= 0). |
| DCHECK_LE(-1, start_position()); |
| return start_position(); |
| } |
| |
| int JSMessageObject::GetEndPosition() const { |
| // TODO(cbruni): make this DCHECK stricter (>= 0). |
| DCHECK_LE(-1, end_position()); |
| return end_position(); |
| } |
| |
| MessageTemplate JSMessageObject::type() const { |
| return MessageTemplateFromInt(raw_type()); |
| } |
| |
| void JSMessageObject::set_type(MessageTemplate value) { |
| set_raw_type(static_cast<int>(value)); |
| } |
| |
| Tagged<Object> JSMessageObject::shared_info() const { |
| return shared_info_.load(); |
| } |
| void JSMessageObject::set_shared_info(Tagged<Object> value, |
| WriteBarrierMode mode) { |
| shared_info_.store(this, Cast<UnionOf<SharedFunctionInfo, Smi>>(value), mode); |
| } |
| |
| Tagged<Smi> JSMessageObject::bytecode_offset() const { |
| return bytecode_offset_.load(); |
| } |
| void JSMessageObject::set_bytecode_offset(Tagged<Smi> value) { |
| bytecode_offset_.store(this, value); |
| } |
| |
| int JSMessageObject::start_position() const { |
| return start_position_.load().value(); |
| } |
| void JSMessageObject::set_start_position(int value) { |
| start_position_.store(this, Smi::FromInt(value)); |
| } |
| |
| int JSMessageObject::end_position() const { |
| return end_position_.load().value(); |
| } |
| void JSMessageObject::set_end_position(int value) { |
| end_position_.store(this, Smi::FromInt(value)); |
| } |
| |
| int JSMessageObject::error_level() const { return error_level_.load().value(); } |
| void JSMessageObject::set_error_level(int value) { |
| error_level_.store(this, Smi::FromInt(value)); |
| } |
| |
| int JSMessageObject::raw_type() const { return message_type_.load().value(); } |
| void JSMessageObject::set_raw_type(int value) { |
| message_type_.store(this, Smi::FromInt(value)); |
| } |
| |
| Tagged<Object> JSMessageObject::argument() const { return argument_.load(); } |
| void JSMessageObject::set_argument(Tagged<Object> value, |
| WriteBarrierMode mode) { |
| argument_.store(this, value, mode); |
| } |
| |
| Tagged<Script> JSMessageObject::script() const { return script_.load(); } |
| void JSMessageObject::set_script(Tagged<Script> value, WriteBarrierMode mode) { |
| script_.store(this, value, mode); |
| } |
| |
| Tagged<UnionOf<StackTraceInfo, Hole>> JSMessageObject::stack_trace() const { |
| return stack_trace_.load(); |
| } |
| void JSMessageObject::set_stack_trace( |
| Tagged<UnionOf<StackTraceInfo, Hole>> value, WriteBarrierMode mode) { |
| stack_trace_.store(this, value, mode); |
| } |
| |
| DEF_GETTER(JSObject, GetElementsKind, ElementsKind) { |
| ElementsKind kind = map()->elements_kind(); |
| #if VERIFY_HEAP && DEBUG |
| Tagged<FixedArrayBase> fixed_array = UncheckedCast<FixedArrayBase>( |
| TaggedField<HeapObject, offsetof(JSObject, elements_)>::load(this)); |
| |
| // If a GC was caused while constructing this object, the elements |
| // pointer may point to a one pointer filler map. |
| if (ElementsAreSafeToExamine()) { |
| Tagged<Map> map = fixed_array->map(); |
| if (IsSmiOrObjectElementsKind(kind)) { |
| CHECK(map == GetReadOnlyRoots().fixed_array_map() || |
| map == GetReadOnlyRoots().fixed_cow_array_map()); |
| } else if (IsDoubleElementsKind(kind)) { |
| CHECK(IsFixedDoubleArray(fixed_array) || |
| fixed_array == GetReadOnlyRoots().empty_fixed_array()); |
| } else if (kind == DICTIONARY_ELEMENTS) { |
| CHECK(IsFixedArray(fixed_array)); |
| CHECK(IsNumberDictionary(fixed_array)); |
| } else { |
| CHECK(kind > DICTIONARY_ELEMENTS || IsAnyNonextensibleElementsKind(kind)); |
| } |
| CHECK_IMPLIES(IsSloppyArgumentsElementsKind(kind), |
| IsSloppyArgumentsElements(elements())); |
| } |
| #endif |
| return kind; |
| } |
| |
| DEF_GETTER(JSObject, GetElementsAccessor, ElementsAccessor*) { |
| return ElementsAccessor::ForKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasObjectElements, bool) { |
| return IsObjectElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasSmiElements, bool) { |
| return IsSmiElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasSmiOrObjectElements, bool) { |
| return IsSmiOrObjectElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasDoubleElements, bool) { |
| return IsDoubleElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasHoleyElements, bool) { |
| return IsHoleyElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasFastElements, bool) { |
| return IsFastElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasFastPackedElements, bool) { |
| return IsFastPackedElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasDictionaryElements, bool) { |
| return IsDictionaryElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasPackedElements, bool) { |
| return GetElementsKind() == PACKED_ELEMENTS; |
| } |
| |
| DEF_GETTER(JSObject, HasAnyNonextensibleElements, bool) { |
| return IsAnyNonextensibleElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasSealedElements, bool) { |
| return IsSealedElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasSharedArrayElements, bool) { |
| return GetElementsKind() == SHARED_ARRAY_ELEMENTS; |
| } |
| |
| DEF_GETTER(JSObject, HasNonextensibleElements, bool) { |
| return IsNonextensibleElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasFastArgumentsElements, bool) { |
| return IsFastArgumentsElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasSlowArgumentsElements, bool) { |
| return IsSlowArgumentsElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasSloppyArgumentsElements, bool) { |
| return IsSloppyArgumentsElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasStringWrapperElements, bool) { |
| return IsStringWrapperElementsKind(GetElementsKind()); |
| } |
| |
| DEF_GETTER(JSObject, HasFastStringWrapperElements, bool) { |
| return GetElementsKind() == FAST_STRING_WRAPPER_ELEMENTS; |
| } |
| |
| DEF_GETTER(JSObject, HasSlowStringWrapperElements, bool) { |
| return GetElementsKind() == SLOW_STRING_WRAPPER_ELEMENTS; |
| } |
| |
| DEF_GETTER(JSObject, HasTypedArrayOrRabGsabTypedArrayElements, bool) { |
| DCHECK(!elements().is_null()); |
| return map()->has_typed_array_or_rab_gsab_typed_array_elements(); |
| } |
| |
| #define FIXED_TYPED_ELEMENTS_CHECK(Type, type, TYPE, ctype) \ |
| DEF_GETTER(JSObject, HasFixed##Type##Elements, bool) { \ |
| return map()->elements_kind() == TYPE##_ELEMENTS; \ |
| } |
| |
| TYPED_ARRAYS(FIXED_TYPED_ELEMENTS_CHECK) |
| |
| #undef FIXED_TYPED_ELEMENTS_CHECK |
| |
| DEF_GETTER(JSObject, HasNamedInterceptor, bool) { |
| return map()->has_named_interceptor(); |
| } |
| |
| DEF_GETTER(JSObject, HasIndexedInterceptor, bool) { |
| return map()->has_indexed_interceptor(); |
| } |
| |
| Tagged<JSGlobalProxy> JSGlobalObject::global_proxy() const { |
| return global_proxy_.load(); |
| } |
| void JSGlobalObject::set_global_proxy(Tagged<JSGlobalProxy> value, |
| WriteBarrierMode mode) { |
| global_proxy_.store(this, value, mode); |
| } |
| |
| Tagged<JSGlobalProxy> JSGlobalObject::global_proxy_for_api() const { |
| return global_proxy_for_api_.load(); |
| } |
| void JSGlobalObject::set_global_proxy_for_api(Tagged<JSGlobalProxy> value, |
| WriteBarrierMode mode) { |
| global_proxy_for_api_.store(this, value, mode); |
| } |
| |
| Tagged<GlobalDictionary> JSGlobalObject::global_dictionary( |
| AcquireLoadTag) const { |
| DCHECK(!HasFastProperties()); |
| return Cast<GlobalDictionary>(properties_or_hash_.Acquire_Load()); |
| } |
| void JSGlobalObject::set_global_dictionary(Tagged<GlobalDictionary> value, |
| ReleaseStoreTag, |
| WriteBarrierMode mode) { |
| properties_or_hash_.Release_Store(this, Cast<FixedArrayBase>(value), mode); |
| } |
| |
| Tagged<HeapObject> JSGlobalObject::raw_global_proxy() const { |
| return Cast<HeapObject>(global_proxy_.load()); |
| } |
| |
| DEF_GETTER(JSObject, element_dictionary, Tagged<NumberDictionary>) { |
| DCHECK(HasDictionaryElements() || HasSlowStringWrapperElements()); |
| return Cast<NumberDictionary>(elements()); |
| } |
| |
| void JSReceiver::initialize_properties(Isolate* isolate) { |
| ReadOnlyRoots roots(isolate); |
| DCHECK(!HeapLayout::InYoungGeneration(roots.empty_fixed_array())); |
| DCHECK(!HeapLayout::InYoungGeneration(roots.empty_property_dictionary())); |
| DCHECK(!HeapLayout::InYoungGeneration( |
| roots.empty_ordered_property_dictionary())); |
| if (map()->is_dictionary_map()) { |
| if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) { |
| properties_or_hash_.store_no_write_barrier( |
| roots.empty_swiss_property_dictionary()); |
| } else { |
| properties_or_hash_.store_no_write_barrier( |
| roots.empty_property_dictionary()); |
| } |
| } else { |
| properties_or_hash_.store_no_write_barrier(roots.empty_fixed_array()); |
| } |
| } |
| |
| DEF_GETTER(JSReceiver, HasFastProperties, bool) { |
| Tagged<JSReceiver::PropertiesOrHash> properties_or_hash_obj = |
| raw_properties_or_hash(kRelaxedLoad); |
| DCHECK(IsSmi(properties_or_hash_obj) || |
| ((IsGlobalDictionary(properties_or_hash_obj) || |
| IsPropertyDictionary(properties_or_hash_obj)) == |
| map()->is_dictionary_map())); |
| USE(properties_or_hash_obj); |
| return !map()->is_dictionary_map(); |
| } |
| |
| DEF_GETTER(JSReceiver, property_dictionary, Tagged<NameDictionary>) { |
| DCHECK(!Is<JSGlobalObject>(this)); |
| DCHECK(!HasFastProperties()); |
| DCHECK(!V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL); |
| |
| Tagged<JSReceiver::PropertiesOrHash> prop = raw_properties_or_hash(); |
| if (IsSmi(prop)) { |
| return GetReadOnlyRoots().empty_property_dictionary(); |
| } |
| return Cast<NameDictionary>(prop); |
| } |
| |
| DEF_GETTER(JSReceiver, property_dictionary_swiss, Tagged<SwissNameDictionary>) { |
| DCHECK(!Is<JSGlobalObject>(this)); |
| DCHECK(!HasFastProperties()); |
| DCHECK(V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL); |
| |
| Tagged<JSReceiver::PropertiesOrHash> prop = raw_properties_or_hash(); |
| if (IsSmi(prop)) { |
| return GetReadOnlyRoots().empty_swiss_property_dictionary(); |
| } |
| return Cast<SwissNameDictionary>(prop); |
| } |
| |
| // TODO(gsathya): Pass isolate directly to this function and access |
| // the heap from this. |
| DEF_GETTER(JSReceiver, property_array, Tagged<PropertyArray>) { |
| DCHECK(HasFastProperties()); |
| Tagged<JSReceiver::PropertiesOrHash> prop = raw_properties_or_hash(); |
| if (IsSmi(prop) || prop == GetReadOnlyRoots().empty_fixed_array()) { |
| return GetReadOnlyRoots().empty_property_array(); |
| } |
| return Cast<PropertyArray>(prop); |
| } |
| |
| void JSObject::EnsureWritableFastElements(Isolate* isolate, |
| DirectHandle<JSObject> object) { |
| DCHECK(object->HasSmiOrObjectElements() || |
| object->HasFastStringWrapperElements() || |
| object->HasAnyNonextensibleElements()); |
| Tagged<FixedArray> raw_elems = Cast<FixedArray>(object->elements()); |
| if (V8_UNLIKELY(raw_elems->map() == |
| ReadOnlyRoots(isolate).fixed_cow_array_map())) { |
| MakeElementsWritable(isolate, object); |
| } |
| } |
| |
| std::optional<Tagged<NativeContext>> JSReceiver::GetCreationContext() const { |
| DisallowGarbageCollection no_gc; |
| Tagged<Map> meta_map = map()->map(); |
| DCHECK(IsMapMap(meta_map)); |
| Tagged<Object> maybe_native_context = meta_map->native_context_or_null(); |
| if (V8_UNLIKELY(IsNull(maybe_native_context))) return {}; |
| DCHECK(IsNativeContext(maybe_native_context)); |
| return Cast<NativeContext>(maybe_native_context); |
| } |
| |
| MaybeDirectHandle<NativeContext> JSReceiver::GetCreationContext( |
| Isolate* isolate) const { |
| DisallowGarbageCollection no_gc; |
| std::optional<Tagged<NativeContext>> maybe_context = GetCreationContext(); |
| if (!maybe_context.has_value()) return {}; |
| return direct_handle(maybe_context.value(), isolate); |
| } |
| |
| Maybe<bool> JSReceiver::HasProperty(Isolate* isolate, |
| DirectHandle<JSReceiver> object, |
| DirectHandle<Name> name) { |
| return HasPropertyOrElement(isolate, object, PropertyKey(isolate, name)); |
| } |
| |
| Maybe<bool> JSReceiver::HasElement(Isolate* isolate, |
| DirectHandle<JSReceiver> object, |
| uint32_t index) { |
| LookupIterator it(isolate, object, index, object); |
| return HasProperty(&it); |
| } |
| |
| Maybe<bool> JSReceiver::HasPropertyOrElement(Isolate* isolate, |
| DirectHandle<JSReceiver> object, |
| PropertyKey key) { |
| LookupIterator it(isolate, object, key, object); |
| return HasProperty(&it); |
| } |
| |
| Maybe<bool> JSReceiver::HasOwnProperty(Isolate* isolate, |
| DirectHandle<JSReceiver> object, |
| size_t index) { |
| if (IsJSObject(*object)) { // Shortcut. |
| LookupIterator it(isolate, object, index, object, LookupIterator::OWN); |
| return HasProperty(&it); |
| } |
| |
| Maybe<PropertyAttributes> attributes = |
| JSReceiver::GetOwnPropertyAttributes(isolate, object, index); |
| MAYBE_RETURN(attributes, Nothing<bool>()); |
| return Just(attributes.FromJust() != ABSENT); |
| } |
| |
| Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes( |
| Isolate* isolate, DirectHandle<JSReceiver> object, |
| DirectHandle<Name> name) { |
| PropertyKey key(isolate, name); |
| LookupIterator it(isolate, object, key, object); |
| return GetPropertyAttributes(&it); |
| } |
| |
| Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes( |
| Isolate* isolate, DirectHandle<JSReceiver> object, |
| DirectHandle<Name> name) { |
| PropertyKey key(isolate, name); |
| LookupIterator it(isolate, object, key, object, LookupIterator::OWN); |
| return GetPropertyAttributes(&it); |
| } |
| |
| Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes( |
| Isolate* isolate, DirectHandle<JSReceiver> object, size_t index) { |
| LookupIterator it(isolate, object, index, object, LookupIterator::OWN); |
| return GetPropertyAttributes(&it); |
| } |
| |
| Maybe<PropertyAttributes> JSReceiver::GetElementAttributes( |
| Isolate* isolate, DirectHandle<JSReceiver> object, uint32_t index) { |
| LookupIterator it(isolate, object, index, object); |
| return GetPropertyAttributes(&it); |
| } |
| |
| Maybe<PropertyAttributes> JSReceiver::GetOwnElementAttributes( |
| Isolate* isolate, DirectHandle<JSReceiver> object, uint32_t index) { |
| LookupIterator it(isolate, object, index, object, LookupIterator::OWN); |
| return GetPropertyAttributes(&it); |
| } |
| |
| Tagged<NativeContext> JSGlobalObject::native_context() { |
| return *GetCreationContext(); |
| } |
| |
| bool JSGlobalObject::IsDetached() { |
| return global_proxy()->IsDetachedFrom(this); |
| } |
| |
| bool JSGlobalProxy::IsDetachedFrom(Tagged<JSGlobalObject> global) const { |
| return map()->prototype() != global; |
| } |
| |
| bool JSGlobalProxy::IsDetached() const { |
| // Currently we expect a non-detached global proxy to have a non-null |
| // hidden prototype. |
| bool is_detached = IsNull(map()->prototype()); |
| DCHECK_IMPLIES(is_detached, !GetCreationContext().has_value()); |
| return is_detached; |
| } |
| |
| inline int JSGlobalProxy::SizeWithEmbedderFields(int embedder_field_count) { |
| DCHECK_GE(embedder_field_count, 0); |
| return kHeaderSize + embedder_field_count * kEmbedderDataSlotSize; |
| } |
| |
| ACCESSORS(JSIteratorResult, value, Tagged<Object>, kValueOffset) |
| ACCESSORS(JSIteratorResult, done, Tagged<Object>, kDoneOffset) |
| |
| ACCESSORS(JSUint8ArraySetFromResult, read, Tagged<Object>, kReadOffset) |
| ACCESSORS(JSUint8ArraySetFromResult, written, Tagged<Object>, kWrittenOffset) |
| |
| // If the fast-case backing storage takes up much more memory than a dictionary |
| // backing storage would, the object should have slow elements. |
| // static |
| static inline bool ShouldConvertToSlowElements(uint32_t used_elements, |
| uint32_t new_capacity) { |
| uint32_t size_threshold = NumberDictionary::kPreferFastElementsSizeFactor * |
| NumberDictionary::ComputeCapacity(used_elements) * |
| NumberDictionary::kEntrySize; |
| return size_threshold <= new_capacity; |
| } |
| |
| static inline bool ShouldConvertToSlowElements(Tagged<JSObject> object, |
| uint32_t capacity, |
| uint32_t index, |
| uint32_t* new_capacity) { |
| static_assert(JSObject::kMaxUncheckedOldFastElementsLength <= |
| JSObject::kMaxUncheckedFastElementsLength); |
| if (index < capacity) { |
| *new_capacity = capacity; |
| return false; |
| } |
| if (index - capacity >= JSObject::kMaxGap) return true; |
| *new_capacity = JSObject::NewElementsCapacity(index + 1); |
| DCHECK_LT(index, *new_capacity); |
| if (*new_capacity <= JSObject::kMaxUncheckedOldFastElementsLength || |
| (*new_capacity <= JSObject::kMaxUncheckedFastElementsLength && |
| HeapLayout::InYoungGeneration(object))) { |
| return false; |
| } |
| return ShouldConvertToSlowElements(object->GetFastElementsUsage(), |
| *new_capacity); |
| } |
| |
| } // namespace v8::internal |
| |
| #include "src/objects/object-macros-undef.h" |
| |
| #endif // V8_OBJECTS_JS_OBJECTS_INL_H_ |