blob: b9b17f97fa12ccbeaa0a69cb37fec81cc4f53022 [file] [log] [blame]
// Copyright 2017 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_MAP_INL_H_
#define V8_OBJECTS_MAP_INL_H_
#include "src/objects/map.h"
#include "src/field-type.h"
#include "src/objects-inl.h"
#include "src/objects/api-callbacks-inl.h"
#include "src/objects/descriptor-array.h"
#include "src/objects/prototype-info-inl.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/templates-inl.h"
#include "src/property.h"
#include "src/transitions.h"
// For pulling in heap/incremental-marking.h which is needed by
// ACCESSORS_CHECKED.
#include "src/heap/heap-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(Map, HeapObjectPtr)
CAST_ACCESSOR2(Map)
ACCESSORS(Map, instance_descriptors, DescriptorArray, kDescriptorsOffset)
// A freshly allocated layout descriptor can be set on an existing map.
// We need to use release-store and acquire-load accessor pairs to ensure
// that the concurrent marking thread observes initializing stores of the
// layout descriptor.
SYNCHRONIZED_ACCESSORS_CHECKED(Map, layout_descriptor, LayoutDescriptor,
kLayoutDescriptorOffset,
FLAG_unbox_double_fields)
WEAK_ACCESSORS(Map, raw_transitions, kTransitionsOrPrototypeInfoOffset)
// |bit_field| fields.
BIT_FIELD_ACCESSORS(Map, bit_field, has_non_instance_prototype,
Map::HasNonInstancePrototypeBit)
BIT_FIELD_ACCESSORS(Map, bit_field, is_callable, Map::IsCallableBit)
BIT_FIELD_ACCESSORS(Map, bit_field, has_named_interceptor,
Map::HasNamedInterceptorBit)
BIT_FIELD_ACCESSORS(Map, bit_field, has_indexed_interceptor,
Map::HasIndexedInterceptorBit)
BIT_FIELD_ACCESSORS(Map, bit_field, is_undetectable, Map::IsUndetectableBit)
BIT_FIELD_ACCESSORS(Map, bit_field, is_access_check_needed,
Map::IsAccessCheckNeededBit)
BIT_FIELD_ACCESSORS(Map, bit_field, is_constructor, Map::IsConstructorBit)
BIT_FIELD_ACCESSORS(Map, bit_field, has_prototype_slot,
Map::HasPrototypeSlotBit)
// |bit_field2| fields.
BIT_FIELD_ACCESSORS(Map, bit_field2, is_extensible, Map::IsExtensibleBit)
BIT_FIELD_ACCESSORS(Map, bit_field2, is_prototype_map, Map::IsPrototypeMapBit)
BIT_FIELD_ACCESSORS(Map, bit_field2, is_in_retained_map_list,
Map::IsInRetainedMapListBit)
// |bit_field3| fields.
BIT_FIELD_ACCESSORS(Map, bit_field3, owns_descriptors, Map::OwnsDescriptorsBit)
BIT_FIELD_ACCESSORS(Map, bit_field3, has_hidden_prototype,
Map::HasHiddenPrototypeBit)
BIT_FIELD_ACCESSORS(Map, bit_field3, is_deprecated, Map::IsDeprecatedBit)
BIT_FIELD_ACCESSORS(Map, bit_field3, is_migration_target,
Map::IsMigrationTargetBit)
BIT_FIELD_ACCESSORS(Map, bit_field3, is_immutable_proto,
Map::IsImmutablePrototypeBit)
BIT_FIELD_ACCESSORS(Map, bit_field3, new_target_is_base,
Map::NewTargetIsBaseBit)
BIT_FIELD_ACCESSORS(Map, bit_field3, may_have_interesting_symbols,
Map::MayHaveInterestingSymbolsBit)
BIT_FIELD_ACCESSORS(Map, bit_field3, construction_counter,
Map::ConstructionCounterBits)
InterceptorInfo* Map::GetNamedInterceptor() {
DCHECK(has_named_interceptor());
FunctionTemplateInfo* info = GetFunctionTemplateInfo();
return InterceptorInfo::cast(info->GetNamedPropertyHandler());
}
InterceptorInfo* Map::GetIndexedInterceptor() {
DCHECK(has_indexed_interceptor());
FunctionTemplateInfo* info = GetFunctionTemplateInfo();
return InterceptorInfo::cast(info->GetIndexedPropertyHandler());
}
bool Map::IsInplaceGeneralizableField(PropertyConstness constness,
Representation representation,
FieldType field_type) {
if (FLAG_track_constant_fields && FLAG_modify_map_inplace &&
(constness == PropertyConstness::kConst)) {
// VariableMode::kConst -> PropertyConstness::kMutable field generalization
// may happen in-place.
return true;
}
if (representation.IsHeapObject() && !field_type->IsAny()) {
return true;
}
return false;
}
bool Map::CanHaveFastTransitionableElementsKind(InstanceType instance_type) {
return instance_type == JS_ARRAY_TYPE || instance_type == JS_VALUE_TYPE ||
instance_type == JS_ARGUMENTS_TYPE;
}
bool Map::CanHaveFastTransitionableElementsKind() const {
return CanHaveFastTransitionableElementsKind(instance_type());
}
// static
void Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
Isolate* isolate, InstanceType instance_type, PropertyConstness* constness,
Representation* representation, Handle<FieldType>* field_type) {
if (CanHaveFastTransitionableElementsKind(instance_type)) {
// We don't support propagation of field generalization through elements
// kind transitions because they are inserted into the transition tree
// before field transitions. In order to avoid complexity of handling
// such a case we ensure that all maps with transitionable elements kinds
// do not have fields that can be generalized in-place (without creation
// of a new map).
if (FLAG_track_constant_fields && FLAG_modify_map_inplace) {
// The constness is either already PropertyConstness::kMutable or should
// become PropertyConstness::kMutable if it was VariableMode::kConst.
*constness = PropertyConstness::kMutable;
}
if (representation->IsHeapObject()) {
// The field type is either already Any or should become Any if it was
// something else.
*field_type = FieldType::Any(isolate);
}
}
}
bool Map::IsUnboxedDoubleField(FieldIndex index) const {
if (!FLAG_unbox_double_fields) return false;
if (index.is_hidden_field() || !index.is_inobject()) return false;
return !layout_descriptor()->IsTagged(index.property_index());
}
bool Map::TooManyFastProperties(StoreOrigin store_origin) const {
if (UnusedPropertyFields() != 0) return false;
if (is_prototype_map()) return false;
if (store_origin == StoreOrigin::kNamed) {
int limit = Max(kMaxFastProperties, GetInObjectProperties());
FieldCounts counts = GetFieldCounts();
// Only count mutable fields so that objects with large numbers of
// constant functions do not go to dictionary mode. That would be bad
// because such objects have often been used as modules.
int external = counts.mutable_count() - GetInObjectProperties();
return external > limit || counts.GetTotal() > kMaxNumberOfDescriptors;
} else {
int limit = Max(kFastPropertiesSoftLimit, GetInObjectProperties());
int external = NumberOfFields() - GetInObjectProperties();
return external > limit;
}
}
PropertyDetails Map::GetLastDescriptorDetails() const {
return instance_descriptors()->GetDetails(LastAdded());
}
int Map::LastAdded() const {
int number_of_own_descriptors = NumberOfOwnDescriptors();
DCHECK_GT(number_of_own_descriptors, 0);
return number_of_own_descriptors - 1;
}
int Map::NumberOfOwnDescriptors() const {
return NumberOfOwnDescriptorsBits::decode(bit_field3());
}
void Map::SetNumberOfOwnDescriptors(int number) {
DCHECK_LE(number, instance_descriptors()->number_of_descriptors());
CHECK_LE(static_cast<unsigned>(number),
static_cast<unsigned>(kMaxNumberOfDescriptors));
set_bit_field3(NumberOfOwnDescriptorsBits::update(bit_field3(), number));
}
int Map::EnumLength() const { return EnumLengthBits::decode(bit_field3()); }
void Map::SetEnumLength(int length) {
if (length != kInvalidEnumCacheSentinel) {
DCHECK_LE(length, NumberOfOwnDescriptors());
CHECK_LE(static_cast<unsigned>(length),
static_cast<unsigned>(kMaxNumberOfDescriptors));
}
set_bit_field3(EnumLengthBits::update(bit_field3(), length));
}
FixedArrayBase* Map::GetInitialElements() const {
FixedArrayBase* result = nullptr;
if (has_fast_elements() || has_fast_string_wrapper_elements()) {
result = GetReadOnlyRoots().empty_fixed_array();
} else if (has_fast_sloppy_arguments_elements()) {
result = GetReadOnlyRoots().empty_sloppy_arguments_elements();
} else if (has_fixed_typed_array_elements()) {
result = GetReadOnlyRoots().EmptyFixedTypedArrayForMap(*this);
} else if (has_dictionary_elements()) {
// TODO(3770): Drop unsightly cast.
result = reinterpret_cast<FixedArrayBase*>(
GetReadOnlyRoots().empty_slow_element_dictionary().ptr());
} else {
UNREACHABLE();
}
DCHECK(!Heap::InNewSpace(result));
return result;
}
VisitorId Map::visitor_id() const {
return static_cast<VisitorId>(
RELAXED_READ_BYTE_FIELD(this, kVisitorIdOffset));
}
void Map::set_visitor_id(VisitorId id) {
CHECK_LT(static_cast<unsigned>(id), 256);
RELAXED_WRITE_BYTE_FIELD(this, kVisitorIdOffset, static_cast<byte>(id));
}
int Map::instance_size_in_words() const {
return RELAXED_READ_BYTE_FIELD(this, kInstanceSizeInWordsOffset);
}
void Map::set_instance_size_in_words(int value) {
RELAXED_WRITE_BYTE_FIELD(this, kInstanceSizeInWordsOffset,
static_cast<byte>(value));
}
int Map::instance_size() const {
return instance_size_in_words() << kTaggedSizeLog2;
}
void Map::set_instance_size(int value) {
CHECK(IsAligned(value, kTaggedSize));
value >>= kTaggedSizeLog2;
CHECK_LT(static_cast<unsigned>(value), 256);
set_instance_size_in_words(value);
}
int Map::inobject_properties_start_or_constructor_function_index() const {
return RELAXED_READ_BYTE_FIELD(
this, kInObjectPropertiesStartOrConstructorFunctionIndexOffset);
}
void Map::set_inobject_properties_start_or_constructor_function_index(
int value) {
CHECK_LT(static_cast<unsigned>(value), 256);
RELAXED_WRITE_BYTE_FIELD(
this, kInObjectPropertiesStartOrConstructorFunctionIndexOffset,
static_cast<byte>(value));
}
int Map::GetInObjectPropertiesStartInWords() const {
DCHECK(IsJSObjectMap());
return inobject_properties_start_or_constructor_function_index();
}
void Map::SetInObjectPropertiesStartInWords(int value) {
CHECK(IsJSObjectMap());
set_inobject_properties_start_or_constructor_function_index(value);
}
int Map::GetInObjectProperties() const {
DCHECK(IsJSObjectMap());
return instance_size_in_words() - GetInObjectPropertiesStartInWords();
}
int Map::GetConstructorFunctionIndex() const {
DCHECK(IsPrimitiveMap());
return inobject_properties_start_or_constructor_function_index();
}
void Map::SetConstructorFunctionIndex(int value) {
CHECK(IsPrimitiveMap());
set_inobject_properties_start_or_constructor_function_index(value);
}
int Map::GetInObjectPropertyOffset(int index) const {
return (GetInObjectPropertiesStartInWords() + index) * kTaggedSize;
}
Handle<Map> Map::AddMissingTransitionsForTesting(
Isolate* isolate, Handle<Map> split_map,
Handle<DescriptorArray> descriptors,
Handle<LayoutDescriptor> full_layout_descriptor) {
return AddMissingTransitions(isolate, split_map, descriptors,
full_layout_descriptor);
}
InstanceType Map::instance_type() const {
return static_cast<InstanceType>(
READ_UINT16_FIELD(this, kInstanceTypeOffset));
}
void Map::set_instance_type(InstanceType value) {
WRITE_UINT16_FIELD(this, kInstanceTypeOffset, value);
}
int Map::UnusedPropertyFields() const {
int value = used_or_unused_instance_size_in_words();
DCHECK_IMPLIES(!IsJSObjectMap(), value == 0);
int unused;
if (value >= JSObject::kFieldsAdded) {
unused = instance_size_in_words() - value;
} else {
// For out of object properties "used_or_unused_instance_size_in_words"
// byte encodes the slack in the property array.
unused = value;
}
return unused;
}
int Map::UnusedInObjectProperties() const {
// Like Map::UnusedPropertyFields(), but returns 0 for out of object
// properties.
int value = used_or_unused_instance_size_in_words();
DCHECK_IMPLIES(!IsJSObjectMap(), value == 0);
if (value >= JSObject::kFieldsAdded) {
return instance_size_in_words() - value;
}
return 0;
}
int Map::used_or_unused_instance_size_in_words() const {
return RELAXED_READ_BYTE_FIELD(this, kUsedOrUnusedInstanceSizeInWordsOffset);
}
void Map::set_used_or_unused_instance_size_in_words(int value) {
CHECK_LE(static_cast<unsigned>(value), 255);
RELAXED_WRITE_BYTE_FIELD(this, kUsedOrUnusedInstanceSizeInWordsOffset,
static_cast<byte>(value));
}
int Map::UsedInstanceSize() const {
int words = used_or_unused_instance_size_in_words();
if (words < JSObject::kFieldsAdded) {
// All in-object properties are used and the words is tracking the slack
// in the property array.
return instance_size();
}
return words * kTaggedSize;
}
void Map::SetInObjectUnusedPropertyFields(int value) {
STATIC_ASSERT(JSObject::kFieldsAdded == JSObject::kHeaderSize / kTaggedSize);
if (!IsJSObjectMap()) {
CHECK_EQ(0, value);
set_used_or_unused_instance_size_in_words(0);
DCHECK_EQ(0, UnusedPropertyFields());
return;
}
CHECK_LE(0, value);
DCHECK_LE(value, GetInObjectProperties());
int used_inobject_properties = GetInObjectProperties() - value;
set_used_or_unused_instance_size_in_words(
GetInObjectPropertyOffset(used_inobject_properties) / kTaggedSize);
DCHECK_EQ(value, UnusedPropertyFields());
}
void Map::SetOutOfObjectUnusedPropertyFields(int value) {
STATIC_ASSERT(JSObject::kFieldsAdded == JSObject::kHeaderSize / kTaggedSize);
CHECK_LT(static_cast<unsigned>(value), JSObject::kFieldsAdded);
// For out of object properties "used_instance_size_in_words" byte encodes
// the slack in the property array.
set_used_or_unused_instance_size_in_words(value);
DCHECK_EQ(value, UnusedPropertyFields());
}
void Map::CopyUnusedPropertyFields(Map map) {
set_used_or_unused_instance_size_in_words(
map->used_or_unused_instance_size_in_words());
DCHECK_EQ(UnusedPropertyFields(), map->UnusedPropertyFields());
}
void Map::CopyUnusedPropertyFieldsAdjustedForInstanceSize(Map map) {
int value = map->used_or_unused_instance_size_in_words();
if (value >= JSValue::kFieldsAdded) {
// Unused in-object fields. Adjust the offset from the object’s start
// so it matches the distance to the object’s end.
value += instance_size_in_words() - map->instance_size_in_words();
}
set_used_or_unused_instance_size_in_words(value);
DCHECK_EQ(UnusedPropertyFields(), map->UnusedPropertyFields());
}
void Map::AccountAddedPropertyField() {
// Update used instance size and unused property fields number.
STATIC_ASSERT(JSObject::kFieldsAdded == JSObject::kHeaderSize / kTaggedSize);
#ifdef DEBUG
int new_unused = UnusedPropertyFields() - 1;
if (new_unused < 0) new_unused += JSObject::kFieldsAdded;
#endif
int value = used_or_unused_instance_size_in_words();
if (value >= JSObject::kFieldsAdded) {
if (value == instance_size_in_words()) {
AccountAddedOutOfObjectPropertyField(0);
} else {
// The property is added in-object, so simply increment the counter.
set_used_or_unused_instance_size_in_words(value + 1);
}
} else {
AccountAddedOutOfObjectPropertyField(value);
}
DCHECK_EQ(new_unused, UnusedPropertyFields());
}
void Map::AccountAddedOutOfObjectPropertyField(int unused_in_property_array) {
unused_in_property_array--;
if (unused_in_property_array < 0) {
unused_in_property_array += JSObject::kFieldsAdded;
}
CHECK_LT(static_cast<unsigned>(unused_in_property_array),
JSObject::kFieldsAdded);
set_used_or_unused_instance_size_in_words(unused_in_property_array);
DCHECK_EQ(unused_in_property_array, UnusedPropertyFields());
}
byte Map::bit_field() const { return READ_BYTE_FIELD(this, kBitFieldOffset); }
void Map::set_bit_field(byte value) {
WRITE_BYTE_FIELD(this, kBitFieldOffset, value);
}
byte Map::bit_field2() const { return READ_BYTE_FIELD(this, kBitField2Offset); }
void Map::set_bit_field2(byte value) {
WRITE_BYTE_FIELD(this, kBitField2Offset, value);
}
bool Map::is_abandoned_prototype_map() const {
return is_prototype_map() && !owns_descriptors();
}
bool Map::should_be_fast_prototype_map() const {
if (!prototype_info()->IsPrototypeInfo()) return false;
return PrototypeInfo::cast(prototype_info())->should_be_fast_map();
}
void Map::set_elements_kind(ElementsKind elements_kind) {
CHECK_LT(static_cast<int>(elements_kind), kElementsKindCount);
set_bit_field2(Map::ElementsKindBits::update(bit_field2(), elements_kind));
}
ElementsKind Map::elements_kind() const {
return Map::ElementsKindBits::decode(bit_field2());
}
bool Map::has_fast_smi_elements() const {
return IsSmiElementsKind(elements_kind());
}
bool Map::has_fast_object_elements() const {
return IsObjectElementsKind(elements_kind());
}
bool Map::has_fast_smi_or_object_elements() const {
return IsSmiOrObjectElementsKind(elements_kind());
}
bool Map::has_fast_double_elements() const {
return IsDoubleElementsKind(elements_kind());
}
bool Map::has_fast_elements() const {
return IsFastElementsKind(elements_kind());
}
bool Map::has_sloppy_arguments_elements() const {
return IsSloppyArgumentsElementsKind(elements_kind());
}
bool Map::has_fast_sloppy_arguments_elements() const {
return elements_kind() == FAST_SLOPPY_ARGUMENTS_ELEMENTS;
}
bool Map::has_fast_string_wrapper_elements() const {
return elements_kind() == FAST_STRING_WRAPPER_ELEMENTS;
}
bool Map::has_fixed_typed_array_elements() const {
return IsFixedTypedArrayElementsKind(elements_kind());
}
bool Map::has_dictionary_elements() const {
return IsDictionaryElementsKind(elements_kind());
}
void Map::set_is_dictionary_map(bool value) {
uint32_t new_bit_field3 = IsDictionaryMapBit::update(bit_field3(), value);
new_bit_field3 = IsUnstableBit::update(new_bit_field3, value);
set_bit_field3(new_bit_field3);
}
bool Map::is_dictionary_map() const {
return IsDictionaryMapBit::decode(bit_field3());
}
void Map::mark_unstable() {
set_bit_field3(IsUnstableBit::update(bit_field3(), true));
}
bool Map::is_stable() const { return !IsUnstableBit::decode(bit_field3()); }
bool Map::CanBeDeprecated() const {
int descriptor = LastAdded();
for (int i = 0; i <= descriptor; i++) {
PropertyDetails details = instance_descriptors()->GetDetails(i);
if (details.representation().IsNone()) return true;
if (details.representation().IsSmi()) return true;
if (details.representation().IsDouble()) return true;
if (details.representation().IsHeapObject()) return true;
if (details.kind() == kData && details.location() == kDescriptor) {
return true;
}
}
return false;
}
void Map::NotifyLeafMapLayoutChange(Isolate* isolate) {
if (is_stable()) {
mark_unstable();
dependent_code()->DeoptimizeDependentCodeGroup(
isolate, DependentCode::kPrototypeCheckGroup);
}
}
bool Map::CanTransition() const {
// Only JSObject and subtypes have map transitions and back pointers.
return InstanceTypeChecker::IsJSObject(instance_type());
}
#define DEF_TESTER(Type, ...) \
bool Map::Is##Type##Map() const { \
return InstanceTypeChecker::Is##Type(instance_type()); \
}
INSTANCE_TYPE_CHECKERS(DEF_TESTER)
#undef DEF_TESTER
bool Map::IsBooleanMap() const {
return *this == GetReadOnlyRoots().boolean_map();
}
bool Map::IsNullOrUndefinedMap() const {
return *this == GetReadOnlyRoots().null_map() ||
*this == GetReadOnlyRoots().undefined_map();
}
bool Map::IsPrimitiveMap() const {
return instance_type() <= LAST_PRIMITIVE_TYPE;
}
Object* Map::prototype() const { return READ_FIELD(this, kPrototypeOffset); }
void Map::set_prototype(Object* value, WriteBarrierMode mode) {
DCHECK(value->IsNull() || value->IsJSReceiver());
WRITE_FIELD(this, kPrototypeOffset, value);
CONDITIONAL_WRITE_BARRIER(this, kPrototypeOffset, value, mode);
}
LayoutDescriptor Map::layout_descriptor_gc_safe() const {
DCHECK(FLAG_unbox_double_fields);
// The loaded value can be dereferenced on background thread to load the
// bitmap. We need acquire load in order to ensure that the bitmap
// initializing stores are also visible to the background thread.
Object* layout_desc = ACQUIRE_READ_FIELD(this, kLayoutDescriptorOffset);
return LayoutDescriptor::cast_gc_safe(layout_desc);
}
bool Map::HasFastPointerLayout() const {
DCHECK(FLAG_unbox_double_fields);
// The loaded value is used for SMI check only and is not dereferenced,
// so relaxed load is safe.
Object* layout_desc = RELAXED_READ_FIELD(this, kLayoutDescriptorOffset);
return LayoutDescriptor::IsFastPointerLayout(layout_desc);
}
void Map::UpdateDescriptors(DescriptorArray* descriptors,
LayoutDescriptor layout_desc) {
set_instance_descriptors(descriptors);
if (FLAG_unbox_double_fields) {
if (layout_descriptor()->IsSlowLayout()) {
set_layout_descriptor(layout_desc);
}
#ifdef VERIFY_HEAP
// TODO(ishell): remove these checks from VERIFY_HEAP mode.
if (FLAG_verify_heap) {
CHECK(layout_descriptor()->IsConsistentWithMap(*this));
CHECK_EQ(Map::GetVisitorId(*this), visitor_id());
}
#else
SLOW_DCHECK(layout_descriptor()->IsConsistentWithMap(*this));
DCHECK(visitor_id() == Map::GetVisitorId(*this));
#endif
}
}
void Map::InitializeDescriptors(DescriptorArray* descriptors,
LayoutDescriptor layout_desc) {
int len = descriptors->number_of_descriptors();
set_instance_descriptors(descriptors);
SetNumberOfOwnDescriptors(len);
if (FLAG_unbox_double_fields) {
set_layout_descriptor(layout_desc);
#ifdef VERIFY_HEAP
// TODO(ishell): remove these checks from VERIFY_HEAP mode.
if (FLAG_verify_heap) {
CHECK(layout_descriptor()->IsConsistentWithMap(*this));
}
#else
SLOW_DCHECK(layout_descriptor()->IsConsistentWithMap(*this));
#endif
set_visitor_id(Map::GetVisitorId(*this));
}
}
void Map::set_bit_field3(uint32_t bits) {
if (kInt32Size != kTaggedSize) {
WRITE_UINT32_FIELD(this, kBitField3Offset + kInt32Size, 0);
}
WRITE_UINT32_FIELD(this, kBitField3Offset, bits);
}
uint32_t Map::bit_field3() const {
return READ_UINT32_FIELD(this, kBitField3Offset);
}
LayoutDescriptor Map::GetLayoutDescriptor() const {
return FLAG_unbox_double_fields ? layout_descriptor()
: LayoutDescriptor::FastPointerLayout();
}
void Map::AppendDescriptor(Descriptor* desc) {
DescriptorArray* descriptors = instance_descriptors();
int number_of_own_descriptors = NumberOfOwnDescriptors();
DCHECK(descriptors->number_of_descriptors() == number_of_own_descriptors);
descriptors->Append(desc);
SetNumberOfOwnDescriptors(number_of_own_descriptors + 1);
// Properly mark the map if the {desc} is an "interesting symbol".
if (desc->GetKey()->IsInterestingSymbol()) {
set_may_have_interesting_symbols(true);
}
PropertyDetails details = desc->GetDetails();
if (details.location() == kField) {
DCHECK_GT(UnusedPropertyFields(), 0);
AccountAddedPropertyField();
}
// This function does not support appending double field descriptors and
// it should never try to (otherwise, layout descriptor must be updated too).
#ifdef DEBUG
DCHECK(details.location() != kField || !details.representation().IsDouble());
#endif
}
Object* Map::GetBackPointer() const {
Object* object = constructor_or_backpointer();
if (object->IsMap()) {
return object;
}
return GetReadOnlyRoots().undefined_value();
}
Map Map::ElementsTransitionMap() {
DisallowHeapAllocation no_gc;
// TODO(delphick): While it's safe to pass nullptr for Isolate* here as
// SearchSpecial doesn't need it, this is really ugly. Perhaps factor out a
// base class for methods not requiring an Isolate?
return TransitionsAccessor(nullptr, *this, &no_gc)
.SearchSpecial(GetReadOnlyRoots().elements_transition_symbol());
}
Object* Map::prototype_info() const {
DCHECK(is_prototype_map());
return READ_FIELD(this, Map::kTransitionsOrPrototypeInfoOffset);
}
void Map::set_prototype_info(Object* value, WriteBarrierMode mode) {
CHECK(is_prototype_map());
WRITE_FIELD(this, Map::kTransitionsOrPrototypeInfoOffset, value);
CONDITIONAL_WRITE_BARRIER(this, Map::kTransitionsOrPrototypeInfoOffset, value,
mode);
}
void Map::SetBackPointer(Object* value, WriteBarrierMode mode) {
CHECK_GE(instance_type(), FIRST_JS_RECEIVER_TYPE);
CHECK(value->IsMap());
CHECK(GetBackPointer()->IsUndefined());
CHECK_IMPLIES(value->IsMap(), Map::cast(value)->GetConstructor() ==
constructor_or_backpointer());
set_constructor_or_backpointer(value, mode);
}
ACCESSORS(Map, dependent_code, DependentCode, kDependentCodeOffset)
ACCESSORS(Map, prototype_validity_cell, Object, kPrototypeValidityCellOffset)
ACCESSORS(Map, constructor_or_backpointer, Object,
kConstructorOrBackPointerOffset)
bool Map::IsPrototypeValidityCellValid() const {
Object* validity_cell = prototype_validity_cell();
Object* value = validity_cell->IsSmi() ? Smi::cast(validity_cell)
: Cell::cast(validity_cell)->value();
return value == Smi::FromInt(Map::kPrototypeChainValid);
}
Object* Map::GetConstructor() const {
Object* maybe_constructor = constructor_or_backpointer();
// Follow any back pointers.
while (maybe_constructor->IsMap()) {
maybe_constructor =
Map::cast(maybe_constructor)->constructor_or_backpointer();
}
return maybe_constructor;
}
FunctionTemplateInfo* Map::GetFunctionTemplateInfo() const {
Object* constructor = GetConstructor();
if (constructor->IsJSFunction()) {
DCHECK(JSFunction::cast(constructor)->shared()->IsApiFunction());
return JSFunction::cast(constructor)->shared()->get_api_func_data();
}
DCHECK(constructor->IsFunctionTemplateInfo());
return FunctionTemplateInfo::cast(constructor);
}
void Map::SetConstructor(Object* constructor, WriteBarrierMode mode) {
// Never overwrite a back pointer with a constructor.
CHECK(!constructor_or_backpointer()->IsMap());
set_constructor_or_backpointer(constructor, mode);
}
Handle<Map> Map::CopyInitialMap(Isolate* isolate, Handle<Map> map) {
return CopyInitialMap(isolate, map, map->instance_size(),
map->GetInObjectProperties(),
map->UnusedPropertyFields());
}
bool Map::IsInobjectSlackTrackingInProgress() const {
return construction_counter() != Map::kNoSlackTracking;
}
void Map::InobjectSlackTrackingStep(Isolate* isolate) {
// Slack tracking should only be performed on an initial map.
DCHECK(GetBackPointer()->IsUndefined());
if (!IsInobjectSlackTrackingInProgress()) return;
int counter = construction_counter();
set_construction_counter(counter - 1);
if (counter == kSlackTrackingCounterEnd) {
CompleteInobjectSlackTracking(isolate);
}
}
int Map::SlackForArraySize(int old_size, int size_limit) {
const int max_slack = size_limit - old_size;
CHECK_LE(0, max_slack);
if (old_size < 4) {
DCHECK_LE(1, max_slack);
return 1;
}
return Min(max_slack, old_size / 4);
}
int NormalizedMapCache::GetIndex(Handle<Map> map) {
return map->Hash() % NormalizedMapCache::kEntries;
}
bool NormalizedMapCache::IsNormalizedMapCache(const HeapObject* obj) {
if (!obj->IsWeakFixedArray()) return false;
if (WeakFixedArray::cast(obj)->length() != NormalizedMapCache::kEntries) {
return false;
}
#ifdef VERIFY_HEAP
if (FLAG_verify_heap) {
NormalizedMapCache* cache =
reinterpret_cast<NormalizedMapCache*>(const_cast<HeapObject*>(obj));
cache->NormalizedMapCacheVerify(cache->GetIsolate());
}
#endif
return true;
}
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_MAP_INL_H_