| // 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 "src/heap/heap-write-barrier.h" |
| #include "src/objects/elements.h" |
| #include "src/objects/embedder-data-slot-inl.h" |
| #include "src/objects/feedback-cell-inl.h" |
| #include "src/objects/feedback-vector.h" |
| #include "src/objects/field-index-inl.h" |
| #include "src/objects/hash-table-inl.h" |
| #include "src/objects/heap-number-inl.h" |
| #include "src/objects/keys.h" |
| #include "src/objects/lookup-inl.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" |
| |
| // Has to be the last include (doesn't have include guards): |
| #include "src/objects/object-macros.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| OBJECT_CONSTRUCTORS_IMPL(JSReceiver, HeapObject) |
| TQ_OBJECT_CONSTRUCTORS_IMPL(JSObject) |
| OBJECT_CONSTRUCTORS_IMPL(JSAsyncFromSyncIterator, JSObject) |
| OBJECT_CONSTRUCTORS_IMPL(JSBoundFunction, JSObject) |
| OBJECT_CONSTRUCTORS_IMPL(JSDate, JSObject) |
| OBJECT_CONSTRUCTORS_IMPL(JSFunction, JSObject) |
| OBJECT_CONSTRUCTORS_IMPL(JSGlobalObject, JSObject) |
| TQ_OBJECT_CONSTRUCTORS_IMPL(JSGlobalProxy) |
| JSIteratorResult::JSIteratorResult(Address ptr) : JSObject(ptr) {} |
| OBJECT_CONSTRUCTORS_IMPL(JSMessageObject, JSObject) |
| TQ_OBJECT_CONSTRUCTORS_IMPL(JSPrimitiveWrapper) |
| OBJECT_CONSTRUCTORS_IMPL(JSStringIterator, JSObject) |
| |
| NEVER_READ_ONLY_SPACE_IMPL(JSReceiver) |
| |
| CAST_ACCESSOR(JSAsyncFromSyncIterator) |
| CAST_ACCESSOR(JSBoundFunction) |
| CAST_ACCESSOR(JSDate) |
| CAST_ACCESSOR(JSFunction) |
| CAST_ACCESSOR(JSGlobalObject) |
| CAST_ACCESSOR(JSIteratorResult) |
| CAST_ACCESSOR(JSMessageObject) |
| CAST_ACCESSOR(JSReceiver) |
| CAST_ACCESSOR(JSStringIterator) |
| |
| MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate, |
| Handle<JSReceiver> receiver, |
| Handle<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, |
| Handle<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(Handle<JSReceiver> object, |
| Handle<Name> name) { |
| LookupIterator it(object, name, object, |
| LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); |
| if (!it.IsFound()) return it.factory()->undefined_value(); |
| return GetDataProperty(&it); |
| } |
| |
| MaybeHandle<HeapObject> JSReceiver::GetPrototype(Isolate* isolate, |
| Handle<JSReceiver> receiver) { |
| // We don't expect access checks to be needed on JSProxy objects. |
| DCHECK(!receiver->IsAccessCheckNeeded() || receiver->IsJSObject()); |
| PrototypeIterator iter(isolate, receiver, kStartAtReceiver, |
| PrototypeIterator::END_AT_NON_HIDDEN); |
| do { |
| if (!iter.AdvanceFollowingProxies()) return MaybeHandle<HeapObject>(); |
| } while (!iter.IsAtEnd()); |
| return PrototypeIterator::GetCurrent(iter); |
| } |
| |
| MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate, |
| Handle<JSReceiver> receiver, |
| const char* name) { |
| Handle<String> str = isolate->factory()->InternalizeUtf8String(name); |
| return GetProperty(isolate, receiver, str); |
| } |
| |
| // static |
| V8_WARN_UNUSED_RESULT MaybeHandle<FixedArray> JSReceiver::OwnPropertyKeys( |
| Handle<JSReceiver> object) { |
| return KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, |
| ALL_PROPERTIES, |
| GetKeysConversion::kConvertToString); |
| } |
| |
| bool JSObject::PrototypeHasNoElements(Isolate* isolate, JSObject object) { |
| DisallowHeapAllocation no_gc; |
| HeapObject prototype = HeapObject::cast(object.map().prototype()); |
| ReadOnlyRoots roots(isolate); |
| HeapObject null = roots.null_value(); |
| FixedArrayBase empty_fixed_array = roots.empty_fixed_array(); |
| FixedArrayBase empty_slow_element_dictionary = |
| roots.empty_slow_element_dictionary(); |
| while (prototype != null) { |
| Map map = prototype.map(); |
| if (map.IsCustomElementsReceiverMap()) return false; |
| FixedArrayBase elements = JSObject::cast(prototype).elements(); |
| if (elements != empty_fixed_array && |
| elements != empty_slow_element_dictionary) { |
| return false; |
| } |
| prototype = HeapObject::cast(map.prototype()); |
| } |
| return true; |
| } |
| |
| ACCESSORS(JSReceiver, raw_properties_or_hash, Object, kPropertiesOrHashOffset) |
| |
| void JSObject::EnsureCanContainHeapObjectElements(Handle<JSObject> object) { |
| JSObject::ValidateElements(*object); |
| ElementsKind elements_kind = object->map().elements_kind(); |
| if (!IsObjectElementsKind(elements_kind)) { |
| if (IsHoleyElementsKind(elements_kind)) { |
| TransitionElementsKind(object, HOLEY_ELEMENTS); |
| } else { |
| TransitionElementsKind(object, PACKED_ELEMENTS); |
| } |
| } |
| } |
| |
| template <typename TSlot> |
| void JSObject::EnsureCanContainElements(Handle<JSObject> object, TSlot objects, |
| uint32_t count, |
| EnsureElementsMode mode) { |
| static_assert(std::is_same<TSlot, FullObjectSlot>::value || |
| std::is_same<TSlot, ObjectSlot>::value, |
| "Only ObjectSlot and FullObjectSlot are expected here"); |
| ElementsKind current_kind = object->GetElementsKind(); |
| ElementsKind target_kind = current_kind; |
| { |
| DisallowHeapAllocation no_allocation; |
| DCHECK(mode != ALLOW_COPIED_DOUBLE_ELEMENTS); |
| bool is_holey = IsHoleyElementsKind(current_kind); |
| if (current_kind == HOLEY_ELEMENTS) return; |
| Object the_hole = object->GetReadOnlyRoots().the_hole_value(); |
| for (uint32_t i = 0; i < count; ++i, ++objects) { |
| Object current = *objects; |
| if (current == the_hole) { |
| is_holey = true; |
| target_kind = GetHoleyElementsKind(target_kind); |
| } else if (!current.IsSmi()) { |
| if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS && current.IsNumber()) { |
| if (IsSmiElementsKind(target_kind)) { |
| if (is_holey) { |
| target_kind = HOLEY_DOUBLE_ELEMENTS; |
| } else { |
| target_kind = PACKED_DOUBLE_ELEMENTS; |
| } |
| } |
| } else if (is_holey) { |
| target_kind = HOLEY_ELEMENTS; |
| break; |
| } else { |
| target_kind = PACKED_ELEMENTS; |
| } |
| } |
| } |
| } |
| if (target_kind != current_kind) { |
| TransitionElementsKind(object, target_kind); |
| } |
| } |
| |
| void JSObject::EnsureCanContainElements(Handle<JSObject> object, |
| Handle<FixedArrayBase> elements, |
| uint32_t length, |
| EnsureElementsMode mode) { |
| ReadOnlyRoots roots = object->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 = |
| Handle<FixedArray>::cast(elements)->GetFirstElementAddress(); |
| EnsureCanContainElements(object, objects, length, mode); |
| return; |
| } |
| |
| DCHECK(mode == ALLOW_COPIED_DOUBLE_ELEMENTS); |
| if (object->GetElementsKind() == HOLEY_SMI_ELEMENTS) { |
| TransitionElementsKind(object, HOLEY_DOUBLE_ELEMENTS); |
| } else if (object->GetElementsKind() == PACKED_SMI_ELEMENTS) { |
| Handle<FixedDoubleArray> double_array = |
| Handle<FixedDoubleArray>::cast(elements); |
| for (uint32_t i = 0; i < length; ++i) { |
| if (double_array->is_the_hole(i)) { |
| TransitionElementsKind(object, HOLEY_DOUBLE_ELEMENTS); |
| return; |
| } |
| } |
| TransitionElementsKind(object, PACKED_DOUBLE_ELEMENTS); |
| } |
| } |
| |
| void JSObject::SetMapAndElements(Handle<JSObject> object, Handle<Map> new_map, |
| Handle<FixedArrayBase> value) { |
| Isolate* isolate = object->GetIsolate(); |
| 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() == |
| value->IsFixedDoubleArray())); |
| object->set_elements(*value); |
| } |
| |
| void JSObject::initialize_elements() { |
| FixedArrayBase elements = map().GetInitialElements(); |
| set_elements(elements, SKIP_WRITE_BARRIER); |
| } |
| |
| DEF_GETTER(JSObject, GetIndexedInterceptor, InterceptorInfo) { |
| return map(isolate).GetIndexedInterceptor(isolate); |
| } |
| |
| DEF_GETTER(JSObject, GetNamedInterceptor, InterceptorInfo) { |
| return map(isolate).GetNamedInterceptor(isolate); |
| } |
| |
| // static |
| int JSObject::GetHeaderSize(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, map.has_prototype_slot()); |
| } |
| |
| // static |
| int JSObject::GetEmbedderFieldsStartOffset(Map map) { |
| // Embedder fields are located after the object header. |
| return GetHeaderSize(map); |
| } |
| |
| int JSObject::GetEmbedderFieldsStartOffset() { |
| return GetEmbedderFieldsStartOffset(map()); |
| } |
| |
| // static |
| int JSObject::GetEmbedderFieldCount(Map map) { |
| int instance_size = map.instance_size(); |
| 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. |
| return (((instance_size - GetEmbedderFieldsStartOffset(map)) >> |
| kTaggedSizeLog2) - |
| map.GetInObjectProperties()) / |
| 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); |
| } |
| |
| Object JSObject::GetEmbedderField(int index) { |
| return EmbedderDataSlot(*this, index).load_tagged(); |
| } |
| |
| void JSObject::SetEmbedderField(int index, Object value) { |
| EmbedderDataSlot::store_tagged(*this, index, value); |
| } |
| |
| void JSObject::SetEmbedderField(int index, Smi value) { |
| EmbedderDataSlot(*this, index).store_smi(value); |
| } |
| |
| bool JSObject::IsUnboxedDoubleField(FieldIndex index) const { |
| Isolate* isolate = GetIsolateForPtrCompr(*this); |
| return IsUnboxedDoubleField(isolate, index); |
| } |
| |
| bool JSObject::IsUnboxedDoubleField(Isolate* isolate, FieldIndex index) const { |
| if (!FLAG_unbox_double_fields) return false; |
| return map(isolate).IsUnboxedDoubleField(isolate, index); |
| } |
| |
| // 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. |
| Object JSObject::RawFastPropertyAt(FieldIndex index) const { |
| Isolate* isolate = GetIsolateForPtrCompr(*this); |
| return RawFastPropertyAt(isolate, index); |
| } |
| |
| Object JSObject::RawFastPropertyAt(Isolate* isolate, FieldIndex index) const { |
| DCHECK(!IsUnboxedDoubleField(isolate, index)); |
| if (index.is_inobject()) { |
| return TaggedField<Object>::load(isolate, *this, index.offset()); |
| } else { |
| return property_array(isolate).get(isolate, index.outobject_array_index()); |
| } |
| } |
| |
| double JSObject::RawFastDoublePropertyAt(FieldIndex index) const { |
| DCHECK(IsUnboxedDoubleField(index)); |
| return ReadField<double>(index.offset()); |
| } |
| |
| uint64_t JSObject::RawFastDoublePropertyAsBitsAt(FieldIndex index) const { |
| DCHECK(IsUnboxedDoubleField(index)); |
| return ReadField<uint64_t>(index.offset()); |
| } |
| |
| void JSObject::RawFastInobjectPropertyAtPut(FieldIndex index, Object value, |
| WriteBarrierMode mode) { |
| DCHECK(index.is_inobject()); |
| int offset = index.offset(); |
| WRITE_FIELD(*this, offset, value); |
| CONDITIONAL_WRITE_BARRIER(*this, offset, value, mode); |
| } |
| |
| void JSObject::RawFastPropertyAtPut(FieldIndex index, 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::RawFastDoublePropertyAsBitsAtPut(FieldIndex index, |
| uint64_t bits) { |
| // Double unboxing is enabled only on 64-bit platforms without pointer |
| // compression. |
| DCHECK_EQ(kDoubleSize, kTaggedSize); |
| Address field_addr = FIELD_ADDR(*this, index.offset()); |
| base::Relaxed_Store(reinterpret_cast<base::AtomicWord*>(field_addr), |
| static_cast<base::AtomicWord>(bits)); |
| } |
| |
| void JSObject::FastPropertyAtPut(FieldIndex index, Object value) { |
| if (IsUnboxedDoubleField(index)) { |
| DCHECK(value.IsMutableHeapNumber()); |
| // Ensure that all bits of the double value are preserved. |
| RawFastDoublePropertyAsBitsAtPut( |
| index, MutableHeapNumber::cast(value).value_as_bits()); |
| } else { |
| RawFastPropertyAtPut(index, value); |
| } |
| } |
| |
| void JSObject::WriteToField(int descriptor, PropertyDetails details, |
| Object value) { |
| DCHECK_EQ(kField, details.location()); |
| DCHECK_EQ(kData, details.kind()); |
| DisallowHeapAllocation no_gc; |
| FieldIndex index = FieldIndex::ForDescriptor(map(), descriptor); |
| if (details.representation().IsDouble()) { |
| // Manipulating the signaling NaN used for the hole and uninitialized |
| // double field sentinel in C++, e.g. with 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 (value.IsSmi()) { |
| bits = bit_cast<uint64_t>(static_cast<double>(Smi::ToInt(value))); |
| } else if (value.IsUninitialized()) { |
| bits = kHoleNanInt64; |
| } else { |
| DCHECK(value.IsHeapNumber()); |
| bits = HeapNumber::cast(value).value_as_bits(); |
| } |
| if (IsUnboxedDoubleField(index)) { |
| RawFastDoublePropertyAsBitsAtPut(index, bits); |
| } else { |
| auto box = MutableHeapNumber::cast(RawFastPropertyAt(index)); |
| box.set_value_as_bits(bits); |
| } |
| } else { |
| RawFastPropertyAtPut(index, value); |
| } |
| } |
| |
| int JSObject::GetInObjectPropertyOffset(int index) { |
| return map().GetInObjectPropertyOffset(index); |
| } |
| |
| Object JSObject::InObjectPropertyAt(int index) { |
| int offset = GetInObjectPropertyOffset(index); |
| return TaggedField<Object>::load(*this, offset); |
| } |
| |
| Object JSObject::InObjectPropertyAtPut(int index, Object value, |
| WriteBarrierMode mode) { |
| // Adjust for the number of properties stored in the object. |
| int offset = GetInObjectPropertyOffset(index); |
| WRITE_FIELD(*this, offset, value); |
| CONDITIONAL_WRITE_BARRIER(*this, offset, value, mode); |
| return value; |
| } |
| |
| void JSObject::InitializeBody(Map map, int start_offset, |
| Object pre_allocated_value, Object filler_value) { |
| DCHECK_IMPLIES(filler_value.IsHeapObject(), |
| !ObjectInYoungGeneration(filler_value)); |
| DCHECK_IMPLIES(pre_allocated_value.IsHeapObject(), |
| !ObjectInYoungGeneration(pre_allocated_value)); |
| int size = map.instance_size(); |
| int offset = start_offset; |
| if (filler_value != pre_allocated_value) { |
| int end_of_pre_allocated_offset = |
| size - (map.UnusedPropertyFields() * kTaggedSize); |
| DCHECK_LE(kHeaderSize, end_of_pre_allocated_offset); |
| while (offset < end_of_pre_allocated_offset) { |
| WRITE_FIELD(*this, offset, pre_allocated_value); |
| offset += kTaggedSize; |
| } |
| } |
| while (offset < size) { |
| WRITE_FIELD(*this, offset, filler_value); |
| offset += kTaggedSize; |
| } |
| } |
| |
| ACCESSORS(JSBoundFunction, bound_target_function, JSReceiver, |
| kBoundTargetFunctionOffset) |
| ACCESSORS(JSBoundFunction, bound_this, Object, kBoundThisOffset) |
| ACCESSORS(JSBoundFunction, bound_arguments, FixedArray, kBoundArgumentsOffset) |
| |
| ACCESSORS(JSFunction, raw_feedback_cell, FeedbackCell, kFeedbackCellOffset) |
| |
| ACCESSORS(JSGlobalObject, native_context, NativeContext, kNativeContextOffset) |
| ACCESSORS(JSGlobalObject, global_proxy, JSGlobalProxy, kGlobalProxyOffset) |
| |
| FeedbackVector JSFunction::feedback_vector() const { |
| DCHECK(has_feedback_vector()); |
| return FeedbackVector::cast(raw_feedback_cell().value()); |
| } |
| |
| ClosureFeedbackCellArray JSFunction::closure_feedback_cell_array() const { |
| DCHECK(has_closure_feedback_cell_array()); |
| return ClosureFeedbackCellArray::cast(raw_feedback_cell().value()); |
| } |
| |
| // Code objects that are marked for deoptimization are not considered to be |
| // optimized. This is because the JSFunction might have been already |
| // deoptimized but its code() still needs to be unlinked, which will happen on |
| // its next activation. |
| // TODO(jupvfranco): rename this function. Maybe RunOptimizedCode, |
| // or IsValidOptimizedCode. |
| bool JSFunction::IsOptimized() { |
| return is_compiled() && code().kind() == Code::OPTIMIZED_FUNCTION && |
| !code().marked_for_deoptimization(); |
| } |
| |
| bool JSFunction::HasOptimizedCode() { |
| return IsOptimized() || |
| (has_feedback_vector() && feedback_vector().has_optimized_code() && |
| !feedback_vector().optimized_code().marked_for_deoptimization()); |
| } |
| |
| bool JSFunction::HasOptimizationMarker() { |
| return has_feedback_vector() && feedback_vector().has_optimization_marker(); |
| } |
| |
| void JSFunction::ClearOptimizationMarker() { |
| DCHECK(has_feedback_vector()); |
| feedback_vector().ClearOptimizationMarker(); |
| } |
| |
| // Optimized code marked for deoptimization will tier back down to running |
| // interpreted on its next activation, and already doesn't count as IsOptimized. |
| bool JSFunction::IsInterpreted() { |
| return is_compiled() && (code().is_interpreter_trampoline_builtin() || |
| (code().kind() == Code::OPTIMIZED_FUNCTION && |
| code().marked_for_deoptimization())); |
| } |
| |
| bool JSFunction::ChecksOptimizationMarker() { |
| return code().checks_optimization_marker(); |
| } |
| |
| bool JSFunction::IsMarkedForOptimization() { |
| return has_feedback_vector() && feedback_vector().optimization_marker() == |
| OptimizationMarker::kCompileOptimized; |
| } |
| |
| bool JSFunction::IsMarkedForConcurrentOptimization() { |
| return has_feedback_vector() && |
| feedback_vector().optimization_marker() == |
| OptimizationMarker::kCompileOptimizedConcurrent; |
| } |
| |
| bool JSFunction::IsInOptimizationQueue() { |
| return has_feedback_vector() && feedback_vector().optimization_marker() == |
| OptimizationMarker::kInOptimizationQueue; |
| } |
| |
| void JSFunction::CompleteInobjectSlackTrackingIfActive() { |
| if (!has_prototype_slot()) return; |
| if (has_initial_map() && initial_map().IsInobjectSlackTrackingInProgress()) { |
| initial_map().CompleteInobjectSlackTracking(GetIsolate()); |
| } |
| } |
| |
| AbstractCode JSFunction::abstract_code() { |
| if (IsInterpreted()) { |
| return AbstractCode::cast(shared().GetBytecodeArray()); |
| } else { |
| return AbstractCode::cast(code()); |
| } |
| } |
| |
| int JSFunction::length() { return shared().length(); } |
| |
| Code JSFunction::code() const { |
| return Code::cast(RELAXED_READ_FIELD(*this, kCodeOffset)); |
| } |
| |
| void JSFunction::set_code(Code value) { |
| DCHECK(!ObjectInYoungGeneration(value)); |
| RELAXED_WRITE_FIELD(*this, kCodeOffset, value); |
| MarkingBarrier(*this, RawField(kCodeOffset), value); |
| } |
| |
| void JSFunction::set_code_no_write_barrier(Code value) { |
| DCHECK(!ObjectInYoungGeneration(value)); |
| RELAXED_WRITE_FIELD(*this, kCodeOffset, value); |
| } |
| |
| // TODO(ishell): Why relaxed read but release store? |
| DEF_GETTER(JSFunction, shared, SharedFunctionInfo) { |
| return SharedFunctionInfo::cast( |
| RELAXED_READ_FIELD(*this, kSharedFunctionInfoOffset)); |
| } |
| |
| void JSFunction::set_shared(SharedFunctionInfo value, WriteBarrierMode mode) { |
| // Release semantics to support acquire read in NeedsResetDueToFlushedBytecode |
| RELEASE_WRITE_FIELD(*this, kSharedFunctionInfoOffset, value); |
| CONDITIONAL_WRITE_BARRIER(*this, kSharedFunctionInfoOffset, value, mode); |
| } |
| |
| void JSFunction::ClearOptimizedCodeSlot(const char* reason) { |
| if (has_feedback_vector() && feedback_vector().has_optimized_code()) { |
| if (FLAG_trace_opt) { |
| PrintF("[evicting entry from optimizing code feedback slot (%s) for ", |
| reason); |
| ShortPrint(); |
| PrintF("]\n"); |
| } |
| feedback_vector().ClearOptimizedCode(); |
| } |
| } |
| |
| void JSFunction::SetOptimizationMarker(OptimizationMarker marker) { |
| DCHECK(has_feedback_vector()); |
| DCHECK(ChecksOptimizationMarker()); |
| DCHECK(!HasOptimizedCode()); |
| |
| feedback_vector().SetOptimizationMarker(marker); |
| } |
| |
| bool JSFunction::has_feedback_vector() const { |
| return shared().is_compiled() && |
| raw_feedback_cell().value().IsFeedbackVector(); |
| } |
| |
| bool JSFunction::has_closure_feedback_cell_array() const { |
| return shared().is_compiled() && |
| raw_feedback_cell().value().IsClosureFeedbackCellArray(); |
| } |
| |
| Context JSFunction::context() { |
| return TaggedField<Context, kContextOffset>::load(*this); |
| } |
| |
| bool JSFunction::has_context() const { |
| return TaggedField<HeapObject, kContextOffset>::load(*this).IsContext(); |
| } |
| |
| JSGlobalProxy JSFunction::global_proxy() { return context().global_proxy(); } |
| |
| NativeContext JSFunction::native_context() { |
| return context().native_context(); |
| } |
| |
| void JSFunction::set_context(HeapObject value) { |
| DCHECK(value.IsUndefined() || value.IsContext()); |
| WRITE_FIELD(*this, kContextOffset, value); |
| WRITE_BARRIER(*this, kContextOffset, value); |
| } |
| |
| ACCESSORS_CHECKED(JSFunction, prototype_or_initial_map, HeapObject, |
| kPrototypeOrInitialMapOffset, map().has_prototype_slot()) |
| |
| DEF_GETTER(JSFunction, has_prototype_slot, bool) { |
| return map(isolate).has_prototype_slot(); |
| } |
| |
| DEF_GETTER(JSFunction, initial_map, Map) { |
| return Map::cast(prototype_or_initial_map(isolate)); |
| } |
| |
| DEF_GETTER(JSFunction, has_initial_map, bool) { |
| DCHECK(has_prototype_slot(isolate)); |
| return prototype_or_initial_map(isolate).IsMap(isolate); |
| } |
| |
| DEF_GETTER(JSFunction, has_instance_prototype, bool) { |
| DCHECK(has_prototype_slot(isolate)); |
| // Can't use ReadOnlyRoots(isolate) as this isolate could be produced by |
| // i::GetIsolateForPtrCompr(HeapObject). |
| return has_initial_map(isolate) || |
| !prototype_or_initial_map(isolate).IsTheHole( |
| GetReadOnlyRoots(isolate)); |
| } |
| |
| DEF_GETTER(JSFunction, has_prototype, bool) { |
| DCHECK(has_prototype_slot(isolate)); |
| return map(isolate).has_non_instance_prototype() || |
| has_instance_prototype(isolate); |
| } |
| |
| DEF_GETTER(JSFunction, has_prototype_property, bool) { |
| return (has_prototype_slot(isolate) && IsConstructor(isolate)) || |
| IsGeneratorFunction(shared(isolate).kind()); |
| } |
| |
| DEF_GETTER(JSFunction, PrototypeRequiresRuntimeLookup, bool) { |
| return !has_prototype_property(isolate) || |
| map(isolate).has_non_instance_prototype(); |
| } |
| |
| DEF_GETTER(JSFunction, instance_prototype, HeapObject) { |
| DCHECK(has_instance_prototype(isolate)); |
| if (has_initial_map(isolate)) return initial_map(isolate).prototype(isolate); |
| // When there is no initial map and the prototype is a JSReceiver, the |
| // initial map field is used for the prototype field. |
| return HeapObject::cast(prototype_or_initial_map(isolate)); |
| } |
| |
| DEF_GETTER(JSFunction, prototype, Object) { |
| DCHECK(has_prototype(isolate)); |
| // If the function's prototype property has been set to a non-JSReceiver |
| // value, that value is stored in the constructor field of the map. |
| if (map(isolate).has_non_instance_prototype()) { |
| Object prototype = map(isolate).GetConstructor(isolate); |
| // The map must have a prototype in that field, not a back pointer. |
| DCHECK(!prototype.IsMap(isolate)); |
| DCHECK(!prototype.IsFunctionTemplateInfo(isolate)); |
| return prototype; |
| } |
| return instance_prototype(isolate); |
| } |
| |
| bool JSFunction::is_compiled() const { |
| return code().builtin_index() != Builtins::kCompileLazy && |
| shared().is_compiled(); |
| } |
| |
| bool JSFunction::NeedsResetDueToFlushedBytecode() { |
| // Do a raw read for shared and code fields here since this function may be |
| // called on a concurrent thread and the JSFunction might not be fully |
| // initialized yet. |
| Object maybe_shared = ACQUIRE_READ_FIELD(*this, kSharedFunctionInfoOffset); |
| Object maybe_code = RELAXED_READ_FIELD(*this, kCodeOffset); |
| |
| if (!maybe_shared.IsSharedFunctionInfo() || !maybe_code.IsCode()) { |
| return false; |
| } |
| |
| SharedFunctionInfo shared = SharedFunctionInfo::cast(maybe_shared); |
| Code code = Code::cast(maybe_code); |
| return !shared.is_compiled() && |
| code.builtin_index() != Builtins::kCompileLazy; |
| } |
| |
| void JSFunction::ResetIfBytecodeFlushed() { |
| if (FLAG_flush_bytecode && NeedsResetDueToFlushedBytecode()) { |
| // Bytecode was flushed and function is now uncompiled, reset JSFunction |
| // by setting code to CompileLazy and clearing the feedback vector. |
| set_code(GetIsolate()->builtins()->builtin(i::Builtins::kCompileLazy)); |
| raw_feedback_cell().reset(); |
| } |
| } |
| |
| ACCESSORS(JSDate, value, Object, kValueOffset) |
| ACCESSORS(JSDate, cache_stamp, Object, kCacheStampOffset) |
| ACCESSORS(JSDate, year, Object, kYearOffset) |
| ACCESSORS(JSDate, month, Object, kMonthOffset) |
| ACCESSORS(JSDate, day, Object, kDayOffset) |
| ACCESSORS(JSDate, weekday, Object, kWeekdayOffset) |
| ACCESSORS(JSDate, hour, Object, kHourOffset) |
| ACCESSORS(JSDate, min, Object, kMinOffset) |
| ACCESSORS(JSDate, sec, Object, kSecOffset) |
| |
| bool JSMessageObject::DidEnsureSourcePositionsAvailable() const { |
| return shared_info().IsUndefined(); |
| } |
| |
| int JSMessageObject::GetStartPosition() const { |
| DCHECK(DidEnsureSourcePositionsAvailable()); |
| return start_position(); |
| } |
| |
| int JSMessageObject::GetEndPosition() const { |
| DCHECK(DidEnsureSourcePositionsAvailable()); |
| return end_position(); |
| } |
| |
| MessageTemplate JSMessageObject::type() const { |
| return MessageTemplateFromInt(raw_type()); |
| } |
| |
| void JSMessageObject::set_type(MessageTemplate value) { |
| set_raw_type(static_cast<int>(value)); |
| } |
| |
| ACCESSORS(JSMessageObject, argument, Object, kArgumentsOffset) |
| ACCESSORS(JSMessageObject, script, Script, kScriptOffset) |
| ACCESSORS(JSMessageObject, stack_frames, Object, kStackFramesOffset) |
| ACCESSORS(JSMessageObject, shared_info, HeapObject, kSharedInfoOffset) |
| ACCESSORS(JSMessageObject, bytecode_offset, Smi, kBytecodeOffsetOffset) |
| SMI_ACCESSORS(JSMessageObject, start_position, kStartPositionOffset) |
| SMI_ACCESSORS(JSMessageObject, end_position, kEndPositionOffset) |
| SMI_ACCESSORS(JSMessageObject, error_level, kErrorLevelOffset) |
| SMI_ACCESSORS(JSMessageObject, raw_type, kMessageTypeOffset) |
| |
| DEF_GETTER(JSObject, GetElementsKind, ElementsKind) { |
| ElementsKind kind = map(isolate).elements_kind(); |
| #if VERIFY_HEAP && DEBUG |
| FixedArrayBase fixed_array = FixedArrayBase::unchecked_cast( |
| TaggedField<HeapObject, kElementsOffset>::load(isolate, *this)); |
| |
| // If a GC was caused while constructing this object, the elements |
| // pointer may point to a one pointer filler map. |
| if (ElementsAreSafeToExamine(isolate)) { |
| Map map = fixed_array.map(isolate); |
| if (IsSmiOrObjectElementsKind(kind)) { |
| DCHECK(map == GetReadOnlyRoots(isolate).fixed_array_map() || |
| map == GetReadOnlyRoots(isolate).fixed_cow_array_map()); |
| } else if (IsDoubleElementsKind(kind)) { |
| DCHECK(fixed_array.IsFixedDoubleArray(isolate) || |
| fixed_array == GetReadOnlyRoots(isolate).empty_fixed_array()); |
| } else if (kind == DICTIONARY_ELEMENTS) { |
| DCHECK(fixed_array.IsFixedArray(isolate)); |
| DCHECK(fixed_array.IsNumberDictionary(isolate)); |
| } else { |
| DCHECK(kind > DICTIONARY_ELEMENTS || IsFrozenOrSealedElementsKind(kind)); |
| } |
| DCHECK( |
| !IsSloppyArgumentsElementsKind(kind) || |
| (elements(isolate).IsFixedArray() && elements(isolate).length() >= 2)); |
| } |
| #endif |
| return kind; |
| } |
| |
| DEF_GETTER(JSObject, GetElementsAccessor, ElementsAccessor*) { |
| return ElementsAccessor::ForKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasObjectElements, bool) { |
| return IsObjectElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasSmiElements, bool) { |
| return IsSmiElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasSmiOrObjectElements, bool) { |
| return IsSmiOrObjectElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasDoubleElements, bool) { |
| return IsDoubleElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasHoleyElements, bool) { |
| return IsHoleyElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasFastElements, bool) { |
| return IsFastElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasFastPackedElements, bool) { |
| return IsFastPackedElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasDictionaryElements, bool) { |
| return GetElementsKind(isolate) == DICTIONARY_ELEMENTS; |
| } |
| |
| DEF_GETTER(JSObject, HasPackedElements, bool) { |
| return GetElementsKind(isolate) == PACKED_ELEMENTS; |
| } |
| |
| DEF_GETTER(JSObject, HasFrozenOrSealedElements, bool) { |
| return IsFrozenOrSealedElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasSealedElements, bool) { |
| return IsSealedElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasFastArgumentsElements, bool) { |
| return GetElementsKind(isolate) == FAST_SLOPPY_ARGUMENTS_ELEMENTS; |
| } |
| |
| DEF_GETTER(JSObject, HasSlowArgumentsElements, bool) { |
| return GetElementsKind(isolate) == SLOW_SLOPPY_ARGUMENTS_ELEMENTS; |
| } |
| |
| DEF_GETTER(JSObject, HasSloppyArgumentsElements, bool) { |
| return IsSloppyArgumentsElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasStringWrapperElements, bool) { |
| return IsStringWrapperElementsKind(GetElementsKind(isolate)); |
| } |
| |
| DEF_GETTER(JSObject, HasFastStringWrapperElements, bool) { |
| return GetElementsKind(isolate) == FAST_STRING_WRAPPER_ELEMENTS; |
| } |
| |
| DEF_GETTER(JSObject, HasSlowStringWrapperElements, bool) { |
| return GetElementsKind(isolate) == SLOW_STRING_WRAPPER_ELEMENTS; |
| } |
| |
| DEF_GETTER(JSObject, HasTypedArrayElements, bool) { |
| DCHECK(!elements(isolate).is_null()); |
| return map(isolate).has_typed_array_elements(); |
| } |
| |
| #define FIXED_TYPED_ELEMENTS_CHECK(Type, type, TYPE, ctype) \ |
| DEF_GETTER(JSObject, HasFixed##Type##Elements, bool) { \ |
| return map(isolate).elements_kind() == TYPE##_ELEMENTS; \ |
| } |
| |
| TYPED_ARRAYS(FIXED_TYPED_ELEMENTS_CHECK) |
| |
| #undef FIXED_TYPED_ELEMENTS_CHECK |
| |
| DEF_GETTER(JSObject, HasNamedInterceptor, bool) { |
| return map(isolate).has_named_interceptor(); |
| } |
| |
| DEF_GETTER(JSObject, HasIndexedInterceptor, bool) { |
| return map(isolate).has_indexed_interceptor(); |
| } |
| |
| DEF_GETTER(JSGlobalObject, global_dictionary, GlobalDictionary) { |
| DCHECK(!HasFastProperties(isolate)); |
| DCHECK(IsJSGlobalObject(isolate)); |
| return GlobalDictionary::cast(raw_properties_or_hash(isolate)); |
| } |
| |
| void JSGlobalObject::set_global_dictionary(GlobalDictionary dictionary) { |
| DCHECK(IsJSGlobalObject()); |
| set_raw_properties_or_hash(dictionary); |
| } |
| |
| DEF_GETTER(JSObject, element_dictionary, NumberDictionary) { |
| DCHECK(HasDictionaryElements(isolate) || |
| HasSlowStringWrapperElements(isolate)); |
| return NumberDictionary::cast(elements(isolate)); |
| } |
| |
| void JSReceiver::initialize_properties(Isolate* isolate) { |
| ReadOnlyRoots roots(isolate); |
| DCHECK(!ObjectInYoungGeneration(roots.empty_fixed_array())); |
| DCHECK(!ObjectInYoungGeneration(roots.empty_property_dictionary())); |
| if (map(isolate).is_dictionary_map()) { |
| WRITE_FIELD(*this, kPropertiesOrHashOffset, |
| roots.empty_property_dictionary()); |
| } else { |
| WRITE_FIELD(*this, kPropertiesOrHashOffset, roots.empty_fixed_array()); |
| } |
| } |
| |
| DEF_GETTER(JSReceiver, HasFastProperties, bool) { |
| DCHECK(raw_properties_or_hash(isolate).IsSmi() || |
| ((raw_properties_or_hash(isolate).IsGlobalDictionary(isolate) || |
| raw_properties_or_hash(isolate).IsNameDictionary(isolate)) == |
| map(isolate).is_dictionary_map())); |
| return !map(isolate).is_dictionary_map(); |
| } |
| |
| DEF_GETTER(JSReceiver, property_dictionary, NameDictionary) { |
| DCHECK(!IsJSGlobalObject(isolate)); |
| DCHECK(!HasFastProperties(isolate)); |
| // Can't use ReadOnlyRoots(isolate) as this isolate could be produced by |
| // i::GetIsolateForPtrCompr(HeapObject). |
| Object prop = raw_properties_or_hash(isolate); |
| if (prop.IsSmi()) { |
| return GetReadOnlyRoots(isolate).empty_property_dictionary(); |
| } |
| return NameDictionary::cast(prop); |
| } |
| |
| // TODO(gsathya): Pass isolate directly to this function and access |
| // the heap from this. |
| DEF_GETTER(JSReceiver, property_array, PropertyArray) { |
| DCHECK(HasFastProperties(isolate)); |
| // Can't use ReadOnlyRoots(isolate) as this isolate could be produced by |
| // i::GetIsolateForPtrCompr(HeapObject). |
| Object prop = raw_properties_or_hash(isolate); |
| if (prop.IsSmi() || prop == GetReadOnlyRoots(isolate).empty_fixed_array()) { |
| return GetReadOnlyRoots(isolate).empty_property_array(); |
| } |
| return PropertyArray::cast(prop); |
| } |
| |
| Maybe<bool> JSReceiver::HasProperty(Handle<JSReceiver> object, |
| Handle<Name> name) { |
| LookupIterator it = LookupIterator::PropertyOrElement(object->GetIsolate(), |
| object, name, object); |
| return HasProperty(&it); |
| } |
| |
| Maybe<bool> JSReceiver::HasOwnProperty(Handle<JSReceiver> object, |
| uint32_t index) { |
| if (object->IsJSModuleNamespace()) return Just(false); |
| |
| if (object->IsJSObject()) { // Shortcut. |
| LookupIterator it(object->GetIsolate(), object, index, object, |
| LookupIterator::OWN); |
| return HasProperty(&it); |
| } |
| |
| Maybe<PropertyAttributes> attributes = |
| JSReceiver::GetOwnPropertyAttributes(object, index); |
| MAYBE_RETURN(attributes, Nothing<bool>()); |
| return Just(attributes.FromJust() != ABSENT); |
| } |
| |
| Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes( |
| Handle<JSReceiver> object, Handle<Name> name) { |
| LookupIterator it = LookupIterator::PropertyOrElement(object->GetIsolate(), |
| object, name, object); |
| return GetPropertyAttributes(&it); |
| } |
| |
| Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes( |
| Handle<JSReceiver> object, Handle<Name> name) { |
| LookupIterator it = LookupIterator::PropertyOrElement( |
| object->GetIsolate(), object, name, object, LookupIterator::OWN); |
| return GetPropertyAttributes(&it); |
| } |
| |
| Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes( |
| Handle<JSReceiver> object, uint32_t index) { |
| LookupIterator it(object->GetIsolate(), object, index, object, |
| LookupIterator::OWN); |
| return GetPropertyAttributes(&it); |
| } |
| |
| Maybe<bool> JSReceiver::HasElement(Handle<JSReceiver> object, uint32_t index) { |
| LookupIterator it(object->GetIsolate(), object, index, object); |
| return HasProperty(&it); |
| } |
| |
| Maybe<PropertyAttributes> JSReceiver::GetElementAttributes( |
| Handle<JSReceiver> object, uint32_t index) { |
| Isolate* isolate = object->GetIsolate(); |
| LookupIterator it(isolate, object, index, object); |
| return GetPropertyAttributes(&it); |
| } |
| |
| Maybe<PropertyAttributes> JSReceiver::GetOwnElementAttributes( |
| Handle<JSReceiver> object, uint32_t index) { |
| Isolate* isolate = object->GetIsolate(); |
| LookupIterator it(isolate, object, index, object, LookupIterator::OWN); |
| return GetPropertyAttributes(&it); |
| } |
| |
| bool JSGlobalObject::IsDetached() { |
| return global_proxy().IsDetachedFrom(*this); |
| } |
| |
| bool JSGlobalProxy::IsDetachedFrom(JSGlobalObject global) const { |
| const PrototypeIterator iter(this->GetIsolate(), *this); |
| return iter.GetCurrent() != global; |
| } |
| |
| inline int JSGlobalProxy::SizeWithEmbedderFields(int embedder_field_count) { |
| DCHECK_GE(embedder_field_count, 0); |
| return kSize + embedder_field_count * kEmbedderDataSlotSize; |
| } |
| |
| ACCESSORS(JSIteratorResult, value, Object, kValueOffset) |
| ACCESSORS(JSIteratorResult, done, Object, kDoneOffset) |
| |
| ACCESSORS(JSAsyncFromSyncIterator, sync_iterator, JSReceiver, |
| kSyncIteratorOffset) |
| ACCESSORS(JSAsyncFromSyncIterator, next, Object, kNextOffset) |
| |
| ACCESSORS(JSStringIterator, string, String, kStringOffset) |
| SMI_ACCESSORS(JSStringIterator, index, kNextIndexOffset) |
| |
| // 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(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); |
| // TODO(ulan): Check if it works with young large objects. |
| if (*new_capacity <= JSObject::kMaxUncheckedOldFastElementsLength || |
| (*new_capacity <= JSObject::kMaxUncheckedFastElementsLength && |
| ObjectInYoungGeneration(object))) { |
| return false; |
| } |
| return ShouldConvertToSlowElements(object.GetFastElementsUsage(), |
| *new_capacity); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #include "src/objects/object-macros-undef.h" |
| |
| #endif // V8_OBJECTS_JS_OBJECTS_INL_H_ |