// 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_H_
|
#define V8_OBJECTS_MAP_H_
|
|
#include "src/objects.h"
|
#include "src/objects/code.h"
|
|
#include "src/globals.h"
|
|
// Has to be the last include (doesn't have include guards):
|
#include "src/objects/object-macros.h"
|
|
namespace v8 {
|
namespace internal {
|
|
#define VISITOR_ID_LIST(V) \
|
V(AllocationSite) \
|
V(BigInt) \
|
V(ByteArray) \
|
V(BytecodeArray) \
|
V(Cell) \
|
V(Code) \
|
V(CodeDataContainer) \
|
V(ConsString) \
|
V(DataHandler) \
|
V(DataObject) \
|
V(EphemeronHashTable) \
|
V(FeedbackCell) \
|
V(FeedbackVector) \
|
V(FixedArray) \
|
V(FixedDoubleArray) \
|
V(FixedFloat64Array) \
|
V(FixedTypedArrayBase) \
|
V(FreeSpace) \
|
V(JSApiObject) \
|
V(JSArrayBuffer) \
|
V(JSFunction) \
|
V(JSObject) \
|
V(JSObjectFast) \
|
V(JSWeakCollection) \
|
V(Map) \
|
V(NativeContext) \
|
V(Oddball) \
|
V(PreParsedScopeData) \
|
V(PropertyArray) \
|
V(PropertyCell) \
|
V(PrototypeInfo) \
|
V(SeqOneByteString) \
|
V(SeqTwoByteString) \
|
V(SharedFunctionInfo) \
|
V(ShortcutCandidate) \
|
V(SlicedString) \
|
V(SmallOrderedHashMap) \
|
V(SmallOrderedHashSet) \
|
V(Struct) \
|
V(Symbol) \
|
V(ThinString) \
|
V(TransitionArray) \
|
V(UncompiledDataWithoutPreParsedScope) \
|
V(UncompiledDataWithPreParsedScope) \
|
V(WasmInstanceObject) \
|
V(WeakArray)
|
|
// For data objects, JS objects and structs along with generic visitor which
|
// can visit object of any size we provide visitors specialized by
|
// object size in words.
|
// Ids of specialized visitors are declared in a linear order (without
|
// holes) starting from the id of visitor specialized for 2 words objects
|
// (base visitor id) and ending with the id of generic visitor.
|
// Method GetVisitorIdForSize depends on this ordering to calculate visitor
|
// id of specialized visitor from given instance size, base visitor id and
|
// generic visitor's id.
|
enum VisitorId {
|
#define VISITOR_ID_ENUM_DECL(id) kVisit##id,
|
VISITOR_ID_LIST(VISITOR_ID_ENUM_DECL)
|
#undef VISITOR_ID_ENUM_DECL
|
kVisitorIdCount
|
};
|
|
typedef std::vector<Handle<Map>> MapHandles;
|
|
// All heap objects have a Map that describes their structure.
|
// A Map contains information about:
|
// - Size information about the object
|
// - How to iterate over an object (for garbage collection)
|
//
|
// Map layout:
|
// +---------------+---------------------------------------------+
|
// | _ Type _ | _ Description _ |
|
// +---------------+---------------------------------------------+
|
// | TaggedPointer | map - Always a pointer to the MetaMap root |
|
// +---------------+---------------------------------------------+
|
// | Int | The first int field |
|
// `---+----------+---------------------------------------------+
|
// | Byte | [instance_size] |
|
// +----------+---------------------------------------------+
|
// | Byte | If Map for a primitive type: |
|
// | | native context index for constructor fn |
|
// | | If Map for an Object type: |
|
// | | inobject properties start offset in words |
|
// +----------+---------------------------------------------+
|
// | Byte | [used_or_unused_instance_size_in_words] |
|
// | | For JSObject in fast mode this byte encodes |
|
// | | the size of the object that includes only |
|
// | | the used property fields or the slack size |
|
// | | in properties backing store. |
|
// +----------+---------------------------------------------+
|
// | Byte | [visitor_id] |
|
// +----+----------+---------------------------------------------+
|
// | Int | The second int field |
|
// `---+----------+---------------------------------------------+
|
// | Short | [instance_type] |
|
// +----------+---------------------------------------------+
|
// | Byte | [bit_field] |
|
// | | - has_non_instance_prototype (bit 0) |
|
// | | - is_callable (bit 1) |
|
// | | - has_named_interceptor (bit 2) |
|
// | | - has_indexed_interceptor (bit 3) |
|
// | | - is_undetectable (bit 4) |
|
// | | - is_access_check_needed (bit 5) |
|
// | | - is_constructor (bit 6) |
|
// | | - has_prototype_slot (bit 7) |
|
// +----------+---------------------------------------------+
|
// | Byte | [bit_field2] |
|
// | | - is_extensible (bit 0) |
|
// | | - is_prototype_map (bit 1) |
|
// | | - is_in_retained_map_list (bit 2) |
|
// | | - elements_kind (bits 3..7) |
|
// +----+----------+---------------------------------------------+
|
// | Int | [bit_field3] |
|
// | | - enum_length (bit 0..9) |
|
// | | - number_of_own_descriptors (bit 10..19) |
|
// | | - is_dictionary_map (bit 20) |
|
// | | - owns_descriptors (bit 21) |
|
// | | - has_hidden_prototype (bit 22) |
|
// | | - is_deprecated (bit 23) |
|
// | | - is_unstable (bit 24) |
|
// | | - is_migration_target (bit 25) |
|
// | | - is_immutable_proto (bit 26) |
|
// | | - new_target_is_base (bit 27) |
|
// | | - may_have_interesting_symbols (bit 28) |
|
// | | - construction_counter (bit 29..31) |
|
// | | |
|
// +*************************************************************+
|
// | Int | On systems with 64bit pointer types, there |
|
// | | is an unused 32bits after bit_field3 |
|
// +*************************************************************+
|
// | TaggedPointer | [prototype] |
|
// +---------------+---------------------------------------------+
|
// | TaggedPointer | [constructor_or_backpointer] |
|
// +---------------+---------------------------------------------+
|
// | TaggedPointer | If Map is a prototype map: |
|
// | | [prototype_info] |
|
// | | Else: |
|
// | | [raw_transitions] |
|
// +---------------+---------------------------------------------+
|
// | TaggedPointer | [instance_descriptors] |
|
// +*************************************************************+
|
// ! TaggedPointer ! [layout_descriptors] !
|
// ! ! Field is only present if compile-time flag !
|
// ! ! FLAG_unbox_double_fields is enabled !
|
// ! ! (basically on 64 bit architectures) !
|
// +*************************************************************+
|
// | TaggedPointer | [dependent_code] |
|
// +---------------+---------------------------------------------+
|
|
class Map : public HeapObject {
|
public:
|
// Instance size.
|
// Size in bytes or kVariableSizeSentinel if instances do not have
|
// a fixed size.
|
DECL_INT_ACCESSORS(instance_size)
|
// Size in words or kVariableSizeSentinel if instances do not have
|
// a fixed size.
|
DECL_INT_ACCESSORS(instance_size_in_words)
|
|
// [inobject_properties_start_or_constructor_function_index]:
|
// Provides access to the inobject properties start offset in words in case of
|
// JSObject maps, or the constructor function index in case of primitive maps.
|
DECL_INT_ACCESSORS(inobject_properties_start_or_constructor_function_index)
|
|
// Get/set the in-object property area start offset in words in the object.
|
inline int GetInObjectPropertiesStartInWords() const;
|
inline void SetInObjectPropertiesStartInWords(int value);
|
// Count of properties allocated in the object (JSObject only).
|
inline int GetInObjectProperties() const;
|
// Index of the constructor function in the native context (primitives only),
|
// or the special sentinel value to indicate that there is no object wrapper
|
// for the primitive (i.e. in case of null or undefined).
|
static const int kNoConstructorFunctionIndex = 0;
|
inline int GetConstructorFunctionIndex() const;
|
inline void SetConstructorFunctionIndex(int value);
|
static MaybeHandle<JSFunction> GetConstructorFunction(
|
Handle<Map> map, Handle<Context> native_context);
|
|
// Retrieve interceptors.
|
inline InterceptorInfo* GetNamedInterceptor();
|
inline InterceptorInfo* GetIndexedInterceptor();
|
|
// Instance type.
|
DECL_PRIMITIVE_ACCESSORS(instance_type, InstanceType)
|
|
// Returns the size of the used in-object area including object header
|
// (only used for JSObject in fast mode, for the other kinds of objects it
|
// is equal to the instance size).
|
inline int UsedInstanceSize() const;
|
|
// Tells how many unused property fields (in-object or out-of object) are
|
// available in the instance (only used for JSObject in fast mode).
|
inline int UnusedPropertyFields() const;
|
// Tells how many unused in-object property words are present.
|
inline int UnusedInObjectProperties() const;
|
// Updates the counters tracking unused fields in the object.
|
inline void SetInObjectUnusedPropertyFields(int unused_property_fields);
|
// Updates the counters tracking unused fields in the property array.
|
inline void SetOutOfObjectUnusedPropertyFields(int unused_property_fields);
|
inline void CopyUnusedPropertyFields(Map* map);
|
inline void CopyUnusedPropertyFieldsAdjustedForInstanceSize(Map* map);
|
inline void AccountAddedPropertyField();
|
inline void AccountAddedOutOfObjectPropertyField(
|
int unused_in_property_array);
|
|
//
|
// Bit field.
|
//
|
DECL_PRIMITIVE_ACCESSORS(bit_field, byte)
|
|
// Bit positions for |bit_field|.
|
#define MAP_BIT_FIELD_FIELDS(V, _) \
|
V(HasNonInstancePrototypeBit, bool, 1, _) \
|
V(IsCallableBit, bool, 1, _) \
|
V(HasNamedInterceptorBit, bool, 1, _) \
|
V(HasIndexedInterceptorBit, bool, 1, _) \
|
V(IsUndetectableBit, bool, 1, _) \
|
V(IsAccessCheckNeededBit, bool, 1, _) \
|
V(IsConstructorBit, bool, 1, _) \
|
V(HasPrototypeSlotBit, bool, 1, _)
|
|
DEFINE_BIT_FIELDS(MAP_BIT_FIELD_FIELDS)
|
#undef MAP_BIT_FIELD_FIELDS
|
|
//
|
// Bit field 2.
|
//
|
DECL_PRIMITIVE_ACCESSORS(bit_field2, byte)
|
|
// Bit positions for |bit_field2|.
|
#define MAP_BIT_FIELD2_FIELDS(V, _) \
|
V(IsExtensibleBit, bool, 1, _) \
|
V(IsPrototypeMapBit, bool, 1, _) \
|
V(IsInRetainedMapListBit, bool, 1, _) \
|
V(ElementsKindBits, ElementsKind, 5, _)
|
|
DEFINE_BIT_FIELDS(MAP_BIT_FIELD2_FIELDS)
|
#undef MAP_BIT_FIELD2_FIELDS
|
|
//
|
// Bit field 3.
|
//
|
DECL_PRIMITIVE_ACCESSORS(bit_field3, uint32_t)
|
|
// Bit positions for |bit_field3|.
|
#define MAP_BIT_FIELD3_FIELDS(V, _) \
|
V(EnumLengthBits, int, kDescriptorIndexBitCount, _) \
|
V(NumberOfOwnDescriptorsBits, int, kDescriptorIndexBitCount, _) \
|
V(IsDictionaryMapBit, bool, 1, _) \
|
V(OwnsDescriptorsBit, bool, 1, _) \
|
V(HasHiddenPrototypeBit, bool, 1, _) \
|
V(IsDeprecatedBit, bool, 1, _) \
|
V(IsUnstableBit, bool, 1, _) \
|
V(IsMigrationTargetBit, bool, 1, _) \
|
V(IsImmutablePrototypeBit, bool, 1, _) \
|
V(NewTargetIsBaseBit, bool, 1, _) \
|
V(MayHaveInterestingSymbolsBit, bool, 1, _) \
|
V(ConstructionCounterBits, int, 3, _)
|
|
DEFINE_BIT_FIELDS(MAP_BIT_FIELD3_FIELDS)
|
#undef MAP_BIT_FIELD3_FIELDS
|
|
STATIC_ASSERT(NumberOfOwnDescriptorsBits::kMax >= kMaxNumberOfDescriptors);
|
|
static const int kSlackTrackingCounterStart = 7;
|
static const int kSlackTrackingCounterEnd = 1;
|
static const int kNoSlackTracking = 0;
|
STATIC_ASSERT(kSlackTrackingCounterStart <= ConstructionCounterBits::kMax);
|
|
// Inobject slack tracking is the way to reclaim unused inobject space.
|
//
|
// The instance size is initially determined by adding some slack to
|
// expected_nof_properties (to allow for a few extra properties added
|
// after the constructor). There is no guarantee that the extra space
|
// will not be wasted.
|
//
|
// Here is the algorithm to reclaim the unused inobject space:
|
// - Detect the first constructor call for this JSFunction.
|
// When it happens enter the "in progress" state: initialize construction
|
// counter in the initial_map.
|
// - While the tracking is in progress initialize unused properties of a new
|
// object with one_pointer_filler_map instead of undefined_value (the "used"
|
// part is initialized with undefined_value as usual). This way they can
|
// be resized quickly and safely.
|
// - Once enough objects have been created compute the 'slack'
|
// (traverse the map transition tree starting from the
|
// initial_map and find the lowest value of unused_property_fields).
|
// - Traverse the transition tree again and decrease the instance size
|
// of every map. Existing objects will resize automatically (they are
|
// filled with one_pointer_filler_map). All further allocations will
|
// use the adjusted instance size.
|
// - SharedFunctionInfo's expected_nof_properties left unmodified since
|
// allocations made using different closures could actually create different
|
// kind of objects (see prototype inheritance pattern).
|
//
|
// Important: inobject slack tracking is not attempted during the snapshot
|
// creation.
|
|
static const int kGenerousAllocationCount =
|
kSlackTrackingCounterStart - kSlackTrackingCounterEnd + 1;
|
|
// Starts the tracking by initializing object constructions countdown counter.
|
void StartInobjectSlackTracking();
|
|
// True if the object constructions countdown counter is a range
|
// [kSlackTrackingCounterEnd, kSlackTrackingCounterStart].
|
inline bool IsInobjectSlackTrackingInProgress() const;
|
|
// Does the tracking step.
|
inline void InobjectSlackTrackingStep(Isolate* isolate);
|
|
// Computes inobject slack for the transition tree starting at this initial
|
// map.
|
int ComputeMinObjectSlack(Isolate* isolate);
|
inline int InstanceSizeFromSlack(int slack) const;
|
|
// Completes inobject slack tracking for the transition tree starting at this
|
// initial map.
|
void CompleteInobjectSlackTracking(Isolate* isolate);
|
|
// Tells whether the object in the prototype property will be used
|
// for instances created from this function. If the prototype
|
// property is set to a value that is not a JSObject, the prototype
|
// property will not be used to create instances of the function.
|
// See ECMA-262, 13.2.2.
|
DECL_BOOLEAN_ACCESSORS(has_non_instance_prototype)
|
|
// Tells whether the instance has a [[Construct]] internal method.
|
// This property is implemented according to ES6, section 7.2.4.
|
DECL_BOOLEAN_ACCESSORS(is_constructor)
|
|
// Tells whether the instance with this map may have properties for
|
// interesting symbols on it.
|
// An "interesting symbol" is one for which Name::IsInterestingSymbol()
|
// returns true, i.e. a well-known symbol like @@toStringTag.
|
DECL_BOOLEAN_ACCESSORS(may_have_interesting_symbols)
|
|
DECL_BOOLEAN_ACCESSORS(has_prototype_slot)
|
|
// Tells whether the instance with this map has a hidden prototype.
|
DECL_BOOLEAN_ACCESSORS(has_hidden_prototype)
|
|
// Records and queries whether the instance has a named interceptor.
|
DECL_BOOLEAN_ACCESSORS(has_named_interceptor)
|
|
// Records and queries whether the instance has an indexed interceptor.
|
DECL_BOOLEAN_ACCESSORS(has_indexed_interceptor)
|
|
// Tells whether the instance is undetectable.
|
// An undetectable object is a special class of JSObject: 'typeof' operator
|
// returns undefined, ToBoolean returns false. Otherwise it behaves like
|
// a normal JS object. It is useful for implementing undetectable
|
// document.all in Firefox & Safari.
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=248549.
|
DECL_BOOLEAN_ACCESSORS(is_undetectable)
|
|
// Tells whether the instance has a [[Call]] internal method.
|
// This property is implemented according to ES6, section 7.2.3.
|
DECL_BOOLEAN_ACCESSORS(is_callable)
|
|
DECL_BOOLEAN_ACCESSORS(new_target_is_base)
|
DECL_BOOLEAN_ACCESSORS(is_extensible)
|
DECL_BOOLEAN_ACCESSORS(is_prototype_map)
|
inline bool is_abandoned_prototype_map() const;
|
|
// Whether the instance has been added to the retained map list by
|
// Heap::AddRetainedMap.
|
DECL_BOOLEAN_ACCESSORS(is_in_retained_map_list)
|
|
DECL_PRIMITIVE_ACCESSORS(elements_kind, ElementsKind)
|
|
// Tells whether the instance has fast elements that are only Smis.
|
inline bool has_fast_smi_elements() const;
|
|
// Tells whether the instance has fast elements.
|
inline bool has_fast_object_elements() const;
|
inline bool has_fast_smi_or_object_elements() const;
|
inline bool has_fast_double_elements() const;
|
inline bool has_fast_elements() const;
|
inline bool has_sloppy_arguments_elements() const;
|
inline bool has_fast_sloppy_arguments_elements() const;
|
inline bool has_fast_string_wrapper_elements() const;
|
inline bool has_fixed_typed_array_elements() const;
|
inline bool has_dictionary_elements() const;
|
|
static bool IsValidElementsTransition(ElementsKind from_kind,
|
ElementsKind to_kind);
|
|
// Returns true if the current map doesn't have DICTIONARY_ELEMENTS but if a
|
// map with DICTIONARY_ELEMENTS was found in the prototype chain.
|
bool DictionaryElementsInPrototypeChainOnly(Isolate* isolate);
|
|
inline Map* ElementsTransitionMap();
|
|
inline FixedArrayBase* GetInitialElements() const;
|
|
// [raw_transitions]: Provides access to the transitions storage field.
|
// Don't call set_raw_transitions() directly to overwrite transitions, use
|
// the TransitionArray::ReplaceTransitions() wrapper instead!
|
DECL_ACCESSORS(raw_transitions, MaybeObject)
|
// [prototype_info]: Per-prototype metadata. Aliased with transitions
|
// (which prototype maps don't have).
|
DECL_ACCESSORS(prototype_info, Object)
|
// PrototypeInfo is created lazily using this helper (which installs it on
|
// the given prototype's map).
|
static Handle<PrototypeInfo> GetOrCreatePrototypeInfo(
|
Handle<JSObject> prototype, Isolate* isolate);
|
static Handle<PrototypeInfo> GetOrCreatePrototypeInfo(
|
Handle<Map> prototype_map, Isolate* isolate);
|
inline bool should_be_fast_prototype_map() const;
|
static void SetShouldBeFastPrototypeMap(Handle<Map> map, bool value,
|
Isolate* isolate);
|
|
// [prototype chain validity cell]: Associated with a prototype object,
|
// stored in that object's map, indicates that prototype chains through this
|
// object are currently valid. The cell will be invalidated and replaced when
|
// the prototype chain changes. When there's nothing to guard (for example,
|
// when direct prototype is null or Proxy) this function returns Smi with
|
// |kPrototypeChainValid| sentinel value.
|
static Handle<Object> GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
|
Isolate* isolate);
|
static const int kPrototypeChainValid = 0;
|
static const int kPrototypeChainInvalid = 1;
|
|
static bool IsPrototypeChainInvalidated(Map* map);
|
|
// Return the map of the root of object's prototype chain.
|
Map* GetPrototypeChainRootMap(Isolate* isolate) const;
|
|
Map* FindRootMap(Isolate* isolate) const;
|
Map* FindFieldOwner(Isolate* isolate, int descriptor) const;
|
|
inline int GetInObjectPropertyOffset(int index) const;
|
|
int NumberOfFields() const;
|
|
bool HasOutOfObjectProperties() const;
|
|
// Returns true if transition to the given map requires special
|
// synchronization with the concurrent marker.
|
bool TransitionRequiresSynchronizationWithGC(Map* target) const;
|
// Returns true if transition to the given map removes a tagged in-object
|
// field.
|
bool TransitionRemovesTaggedField(Map* target) const;
|
// Returns true if transition to the given map replaces a tagged in-object
|
// field with an untagged in-object field.
|
bool TransitionChangesTaggedFieldToUntaggedField(Map* target) const;
|
|
// TODO(ishell): candidate with JSObject::MigrateToMap().
|
bool InstancesNeedRewriting(Map* target) const;
|
bool InstancesNeedRewriting(Map* target, int target_number_of_fields,
|
int target_inobject, int target_unused,
|
int* old_number_of_fields) const;
|
// TODO(ishell): moveit!
|
static Handle<Map> GeneralizeAllFields(Isolate* isolate, Handle<Map> map);
|
V8_WARN_UNUSED_RESULT static Handle<FieldType> GeneralizeFieldType(
|
Representation rep1, Handle<FieldType> type1, Representation rep2,
|
Handle<FieldType> type2, Isolate* isolate);
|
static void GeneralizeField(Isolate* isolate, Handle<Map> map,
|
int modify_index, PropertyConstness new_constness,
|
Representation new_representation,
|
Handle<FieldType> new_field_type);
|
// Returns true if |descriptor|'th property is a field that may be generalized
|
// by just updating current map.
|
static inline bool IsInplaceGeneralizableField(PropertyConstness constness,
|
Representation representation,
|
FieldType* field_type);
|
|
// Generalizes constness, representation and field_type if objects with given
|
// instance type can have fast elements that can be transitioned by stubs or
|
// optimized code to more general elements kind.
|
// This generalization is necessary in order to ensure that elements kind
|
// transitions performed by stubs / optimized code don't silently transition
|
// PropertyConstness::kMutable fields back to VariableMode::kConst state or
|
// fields with HeapObject representation and "Any" type back to "Class" type.
|
static inline void GeneralizeIfCanHaveTransitionableFastElementsKind(
|
Isolate* isolate, InstanceType instance_type,
|
PropertyConstness* constness, Representation* representation,
|
Handle<FieldType>* field_type);
|
|
static Handle<Map> ReconfigureProperty(Isolate* isolate, Handle<Map> map,
|
int modify_index,
|
PropertyKind new_kind,
|
PropertyAttributes new_attributes,
|
Representation new_representation,
|
Handle<FieldType> new_field_type);
|
|
static Handle<Map> ReconfigureElementsKind(Isolate* isolate, Handle<Map> map,
|
ElementsKind new_elements_kind);
|
|
static Handle<Map> PrepareForDataProperty(Isolate* isolate,
|
Handle<Map> old_map,
|
int descriptor_number,
|
PropertyConstness constness,
|
Handle<Object> value);
|
|
static Handle<Map> Normalize(Isolate* isolate, Handle<Map> map,
|
PropertyNormalizationMode mode,
|
const char* reason);
|
|
// Tells whether the map is used for JSObjects in dictionary mode (ie
|
// normalized objects, ie objects for which HasFastProperties returns false).
|
// A map can never be used for both dictionary mode and fast mode JSObjects.
|
// False by default and for HeapObjects that are not JSObjects.
|
DECL_BOOLEAN_ACCESSORS(is_dictionary_map)
|
|
// Tells whether the instance needs security checks when accessing its
|
// properties.
|
DECL_BOOLEAN_ACCESSORS(is_access_check_needed)
|
|
// [prototype]: implicit prototype object.
|
DECL_ACCESSORS(prototype, Object)
|
// TODO(jkummerow): make set_prototype private.
|
static void SetPrototype(Isolate* isolate, Handle<Map> map,
|
Handle<Object> prototype,
|
bool enable_prototype_setup_mode = true);
|
|
// [constructor]: points back to the function or FunctionTemplateInfo
|
// responsible for this map.
|
// The field overlaps with the back pointer. All maps in a transition tree
|
// have the same constructor, so maps with back pointers can walk the
|
// back pointer chain until they find the map holding their constructor.
|
// Returns null_value if there's neither a constructor function nor a
|
// FunctionTemplateInfo available.
|
DECL_ACCESSORS(constructor_or_backpointer, Object)
|
inline Object* GetConstructor() const;
|
inline FunctionTemplateInfo* GetFunctionTemplateInfo() const;
|
inline void SetConstructor(Object* constructor,
|
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
|
// [back pointer]: points back to the parent map from which a transition
|
// leads to this map. The field overlaps with the constructor (see above).
|
inline Object* GetBackPointer() const;
|
inline void SetBackPointer(Object* value,
|
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
|
|
// [instance descriptors]: describes the object.
|
DECL_ACCESSORS(instance_descriptors, DescriptorArray)
|
|
// [layout descriptor]: describes the object layout.
|
DECL_ACCESSORS(layout_descriptor, LayoutDescriptor)
|
// |layout descriptor| accessor which can be used from GC.
|
inline LayoutDescriptor* layout_descriptor_gc_safe() const;
|
inline bool HasFastPointerLayout() const;
|
|
// |layout descriptor| accessor that is safe to call even when
|
// FLAG_unbox_double_fields is disabled (in this case Map does not contain
|
// |layout_descriptor| field at all).
|
inline LayoutDescriptor* GetLayoutDescriptor() const;
|
|
inline void UpdateDescriptors(DescriptorArray* descriptors,
|
LayoutDescriptor* layout_descriptor);
|
inline void InitializeDescriptors(DescriptorArray* descriptors,
|
LayoutDescriptor* layout_descriptor);
|
|
// [dependent code]: list of optimized codes that weakly embed this map.
|
DECL_ACCESSORS(dependent_code, DependentCode)
|
|
// [prototype_validity_cell]: Cell containing the validity bit for prototype
|
// chains or Smi(0) if uninitialized.
|
// The meaning of this validity cell is different for prototype maps and
|
// non-prototype maps.
|
// For prototype maps the validity bit "guards" modifications of prototype
|
// chains going through this object. When a prototype object changes, both its
|
// own validity cell and those of all "downstream" prototypes are invalidated;
|
// handlers for a given receiver embed the currently valid cell for that
|
// receiver's prototype during their creation and check it on execution.
|
// For non-prototype maps which are used as transitioning store handlers this
|
// field contains the validity cell which guards modifications of this map's
|
// prototype.
|
DECL_ACCESSORS(prototype_validity_cell, Object)
|
|
// Returns true if prototype validity cell value represents "valid" prototype
|
// chain state.
|
inline bool IsPrototypeValidityCellValid() const;
|
|
inline PropertyDetails GetLastDescriptorDetails() const;
|
|
inline int LastAdded() const;
|
|
inline int NumberOfOwnDescriptors() const;
|
inline void SetNumberOfOwnDescriptors(int number);
|
|
inline Cell* RetrieveDescriptorsPointer();
|
|
// Checks whether all properties are stored either in the map or on the object
|
// (inobject, properties, or elements backing store), requiring no special
|
// checks.
|
bool OnlyHasSimpleProperties() const;
|
inline int EnumLength() const;
|
inline void SetEnumLength(int length);
|
|
DECL_BOOLEAN_ACCESSORS(owns_descriptors)
|
|
inline void mark_unstable();
|
inline bool is_stable() const;
|
|
DECL_BOOLEAN_ACCESSORS(is_migration_target)
|
|
DECL_BOOLEAN_ACCESSORS(is_immutable_proto)
|
|
// This counter is used for in-object slack tracking.
|
// The in-object slack tracking is considered enabled when the counter is
|
// non zero. The counter only has a valid count for initial maps. For
|
// transitioned maps only kNoSlackTracking has a meaning, namely that inobject
|
// slack tracking already finished for the transition tree. Any other value
|
// indicates that either inobject slack tracking is still in progress, or that
|
// the map isn't part of the transition tree anymore.
|
DECL_INT_ACCESSORS(construction_counter)
|
|
DECL_BOOLEAN_ACCESSORS(is_deprecated)
|
inline bool CanBeDeprecated() const;
|
// Returns a non-deprecated version of the input. If the input was not
|
// deprecated, it is directly returned. Otherwise, the non-deprecated version
|
// is found by re-transitioning from the root of the transition tree using the
|
// descriptor array of the map. Returns MaybeHandle<Map>() if no updated map
|
// is found.
|
static MaybeHandle<Map> TryUpdate(Isolate* isolate,
|
Handle<Map> map) V8_WARN_UNUSED_RESULT;
|
|
// Returns a non-deprecated version of the input. This method may deprecate
|
// existing maps along the way if encodings conflict. Not for use while
|
// gathering type feedback. Use TryUpdate in those cases instead.
|
static Handle<Map> Update(Isolate* isolate, Handle<Map> map);
|
|
static inline Handle<Map> CopyInitialMap(Isolate* isolate, Handle<Map> map);
|
static Handle<Map> CopyInitialMap(Isolate* isolate, Handle<Map> map,
|
int instance_size, int in_object_properties,
|
int unused_property_fields);
|
static Handle<Map> CopyInitialMapNormalized(
|
Isolate* isolate, Handle<Map> map,
|
PropertyNormalizationMode mode = CLEAR_INOBJECT_PROPERTIES);
|
static Handle<Map> CopyDropDescriptors(Isolate* isolate, Handle<Map> map);
|
static Handle<Map> CopyInsertDescriptor(Isolate* isolate, Handle<Map> map,
|
Descriptor* descriptor,
|
TransitionFlag flag);
|
|
static MaybeObjectHandle WrapFieldType(Isolate* isolate,
|
Handle<FieldType> type);
|
static FieldType* UnwrapFieldType(MaybeObject* wrapped_type);
|
|
V8_WARN_UNUSED_RESULT static MaybeHandle<Map> CopyWithField(
|
Isolate* isolate, Handle<Map> map, Handle<Name> name,
|
Handle<FieldType> type, PropertyAttributes attributes,
|
PropertyConstness constness, Representation representation,
|
TransitionFlag flag);
|
|
V8_WARN_UNUSED_RESULT static MaybeHandle<Map> CopyWithConstant(
|
Isolate* isolate, Handle<Map> map, Handle<Name> name,
|
Handle<Object> constant, PropertyAttributes attributes,
|
TransitionFlag flag);
|
|
// Returns a new map with all transitions dropped from the given map and
|
// the ElementsKind set.
|
static Handle<Map> TransitionElementsTo(Isolate* isolate, Handle<Map> map,
|
ElementsKind to_kind);
|
|
static Handle<Map> AsElementsKind(Isolate* isolate, Handle<Map> map,
|
ElementsKind kind);
|
|
static Handle<Map> CopyAsElementsKind(Isolate* isolate, Handle<Map> map,
|
ElementsKind kind, TransitionFlag flag);
|
|
static Handle<Map> AsLanguageMode(Isolate* isolate, Handle<Map> initial_map,
|
Handle<SharedFunctionInfo> shared_info);
|
|
static Handle<Map> CopyForPreventExtensions(Isolate* isolate, Handle<Map> map,
|
PropertyAttributes attrs_to_add,
|
Handle<Symbol> transition_marker,
|
const char* reason);
|
|
static Handle<Map> FixProxy(Handle<Map> map, InstanceType type, int size);
|
|
// Maximal number of fast properties. Used to restrict the number of map
|
// transitions to avoid an explosion in the number of maps for objects used as
|
// dictionaries.
|
inline bool TooManyFastProperties(StoreFromKeyed store_mode) const;
|
static Handle<Map> TransitionToDataProperty(Isolate* isolate, Handle<Map> map,
|
Handle<Name> name,
|
Handle<Object> value,
|
PropertyAttributes attributes,
|
PropertyConstness constness,
|
StoreFromKeyed store_mode);
|
static Handle<Map> TransitionToAccessorProperty(
|
Isolate* isolate, Handle<Map> map, Handle<Name> name, int descriptor,
|
Handle<Object> getter, Handle<Object> setter,
|
PropertyAttributes attributes);
|
static Handle<Map> ReconfigureExistingProperty(Isolate* isolate,
|
Handle<Map> map,
|
int descriptor,
|
PropertyKind kind,
|
PropertyAttributes attributes);
|
|
inline void AppendDescriptor(Descriptor* desc);
|
|
// Returns a copy of the map, prepared for inserting into the transition
|
// tree (if the |map| owns descriptors then the new one will share
|
// descriptors with |map|).
|
static Handle<Map> CopyForTransition(Isolate* isolate, Handle<Map> map,
|
const char* reason);
|
|
// Returns a copy of the map, with all transitions dropped from the
|
// instance descriptors.
|
static Handle<Map> Copy(Isolate* isolate, Handle<Map> map,
|
const char* reason);
|
static Handle<Map> Create(Isolate* isolate, int inobject_properties);
|
|
// Returns the next free property index (only valid for FAST MODE).
|
int NextFreePropertyIndex() const;
|
|
// Returns the number of enumerable properties.
|
int NumberOfEnumerableProperties() const;
|
|
DECL_CAST(Map)
|
|
static inline int SlackForArraySize(int old_size, int size_limit);
|
|
static void EnsureDescriptorSlack(Isolate* isolate, Handle<Map> map,
|
int slack);
|
|
// Returns the map to be used for instances when the given {prototype} is
|
// passed to an Object.create call. Might transition the given {prototype}.
|
static Handle<Map> GetObjectCreateMap(Isolate* isolate,
|
Handle<HeapObject> prototype);
|
|
// Similar to {GetObjectCreateMap} but does not transition {prototype} and
|
// fails gracefully by returning an empty handle instead.
|
static MaybeHandle<Map> TryGetObjectCreateMap(Isolate* isolate,
|
Handle<HeapObject> prototype);
|
|
// Computes a hash value for this map, to be used in HashTables and such.
|
int Hash();
|
|
// Returns the transitioned map for this map with the most generic
|
// elements_kind that's found in |candidates|, or |nullptr| if no match is
|
// found at all.
|
Map* FindElementsKindTransitionedMap(Isolate* isolate,
|
MapHandles const& candidates);
|
|
inline static bool IsJSObject(InstanceType type);
|
|
inline bool CanTransition() const;
|
|
inline bool IsBooleanMap() const;
|
inline bool IsNullMap() const;
|
inline bool IsUndefinedMap() const;
|
inline bool IsNullOrUndefinedMap() const;
|
inline bool IsPrimitiveMap() const;
|
inline bool IsJSReceiverMap() const;
|
inline bool IsJSObjectMap() const;
|
inline bool IsJSPromiseMap() const;
|
inline bool IsJSArrayMap() const;
|
inline bool IsJSFunctionMap() const;
|
inline bool IsStringMap() const;
|
inline bool IsJSProxyMap() const;
|
inline bool IsModuleMap() const;
|
inline bool IsJSGlobalProxyMap() const;
|
inline bool IsJSGlobalObjectMap() const;
|
inline bool IsJSTypedArrayMap() const;
|
inline bool IsJSDataViewMap() const;
|
inline bool IsSpecialReceiverMap() const;
|
inline bool IsCustomElementsReceiverMap() const;
|
|
bool IsMapInArrayPrototypeChain(Isolate* isolate) const;
|
|
// Dispatched behavior.
|
DECL_PRINTER(Map)
|
DECL_VERIFIER(Map)
|
|
#ifdef VERIFY_HEAP
|
void DictionaryMapVerify(Isolate* isolate);
|
#endif
|
|
DECL_PRIMITIVE_ACCESSORS(visitor_id, VisitorId)
|
|
static Handle<Map> TransitionToPrototype(Isolate* isolate, Handle<Map> map,
|
Handle<Object> prototype);
|
|
static Handle<Map> TransitionToImmutableProto(Isolate* isolate,
|
Handle<Map> map);
|
|
static const int kMaxPreAllocatedPropertyFields = 255;
|
|
// Layout description.
|
#define MAP_FIELDS(V) \
|
/* Raw data fields. */ \
|
V(kInstanceSizeInWordsOffset, kUInt8Size) \
|
V(kInObjectPropertiesStartOrConstructorFunctionIndexOffset, kUInt8Size) \
|
V(kUsedOrUnusedInstanceSizeInWordsOffset, kUInt8Size) \
|
V(kVisitorIdOffset, kUInt8Size) \
|
V(kInstanceTypeOffset, kUInt16Size) \
|
V(kBitFieldOffset, kUInt8Size) \
|
V(kBitField2Offset, kUInt8Size) \
|
V(kBitField3Offset, kUInt32Size) \
|
V(k64BitArchPaddingOffset, kPointerSize == kUInt32Size ? 0 : kUInt32Size) \
|
/* Pointer fields. */ \
|
V(kPointerFieldsBeginOffset, 0) \
|
V(kPrototypeOffset, kPointerSize) \
|
V(kConstructorOrBackPointerOffset, kPointerSize) \
|
V(kTransitionsOrPrototypeInfoOffset, kPointerSize) \
|
V(kDescriptorsOffset, kPointerSize) \
|
V(kLayoutDescriptorOffset, FLAG_unbox_double_fields ? kPointerSize : 0) \
|
V(kDependentCodeOffset, kPointerSize) \
|
V(kPrototypeValidityCellOffset, kPointerSize) \
|
V(kPointerFieldsEndOffset, 0) \
|
/* Total size. */ \
|
V(kSize, 0)
|
|
DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, MAP_FIELDS)
|
#undef MAP_FIELDS
|
|
STATIC_ASSERT(kInstanceTypeOffset == Internals::kMapInstanceTypeOffset);
|
|
class BodyDescriptor;
|
|
// Compares this map to another to see if they describe equivalent objects.
|
// If |mode| is set to CLEAR_INOBJECT_PROPERTIES, |other| is treated as if
|
// it had exactly zero inobject properties.
|
// The "shared" flags of both this map and |other| are ignored.
|
bool EquivalentToForNormalization(const Map* other,
|
PropertyNormalizationMode mode) const;
|
|
// Returns true if given field is unboxed double.
|
inline bool IsUnboxedDoubleField(FieldIndex index) const;
|
|
void PrintMapDetails(std::ostream& os);
|
|
static inline Handle<Map> AddMissingTransitionsForTesting(
|
Isolate* isolate, Handle<Map> split_map,
|
Handle<DescriptorArray> descriptors,
|
Handle<LayoutDescriptor> full_layout_descriptor);
|
|
// Fires when the layout of an object with a leaf map changes.
|
// This includes adding transitions to the leaf map or changing
|
// the descriptor array.
|
inline void NotifyLeafMapLayoutChange(Isolate* isolate);
|
|
static VisitorId GetVisitorId(Map* map);
|
|
// Returns true if objects with given instance type are allowed to have
|
// fast transitionable elements kinds. This predicate is used to ensure
|
// that objects that can have transitionable fast elements kind will not
|
// get in-place generalizable fields because the elements kind transition
|
// performed by stubs or optimized code can't properly generalize such
|
// fields.
|
static inline bool CanHaveFastTransitionableElementsKind(
|
InstanceType instance_type);
|
inline bool CanHaveFastTransitionableElementsKind() const;
|
|
private:
|
// This byte encodes either the instance size without the in-object slack or
|
// the slack size in properties backing store.
|
// Let H be JSObject::kHeaderSize / kPointerSize.
|
// If value >= H then:
|
// - all field properties are stored in the object.
|
// - there is no property array.
|
// - value * kPointerSize is the actual object size without the slack.
|
// Otherwise:
|
// - there is no slack in the object.
|
// - the property array has value slack slots.
|
// Note that this encoding requires that H = JSObject::kFieldsAdded.
|
DECL_INT_ACCESSORS(used_or_unused_instance_size_in_words)
|
|
// Returns the map that this (root) map transitions to if its elements_kind
|
// is changed to |elements_kind|, or |nullptr| if no such map is cached yet.
|
Map* LookupElementsTransitionMap(Isolate* isolate,
|
ElementsKind elements_kind);
|
|
// Tries to replay property transitions starting from this (root) map using
|
// the descriptor array of the |map|. The |root_map| is expected to have
|
// proper elements kind and therefore elements kinds transitions are not
|
// taken by this function. Returns |nullptr| if matching transition map is
|
// not found.
|
Map* TryReplayPropertyTransitions(Isolate* isolate, Map* map);
|
|
static void ConnectTransition(Isolate* isolate, Handle<Map> parent,
|
Handle<Map> child, Handle<Name> name,
|
SimpleTransitionFlag flag);
|
|
bool EquivalentToForTransition(const Map* other) const;
|
bool EquivalentToForElementsKindTransition(const Map* other) const;
|
static Handle<Map> RawCopy(Isolate* isolate, Handle<Map> map,
|
int instance_size, int inobject_properties);
|
static Handle<Map> ShareDescriptor(Isolate* isolate, Handle<Map> map,
|
Handle<DescriptorArray> descriptors,
|
Descriptor* descriptor);
|
static Handle<Map> AddMissingTransitions(
|
Isolate* isolate, Handle<Map> map, Handle<DescriptorArray> descriptors,
|
Handle<LayoutDescriptor> full_layout_descriptor);
|
static void InstallDescriptors(
|
Isolate* isolate, Handle<Map> parent_map, Handle<Map> child_map,
|
int new_descriptor, Handle<DescriptorArray> descriptors,
|
Handle<LayoutDescriptor> full_layout_descriptor);
|
static Handle<Map> CopyAddDescriptor(Isolate* isolate, Handle<Map> map,
|
Descriptor* descriptor,
|
TransitionFlag flag);
|
static Handle<Map> CopyReplaceDescriptors(
|
Isolate* isolate, Handle<Map> map, Handle<DescriptorArray> descriptors,
|
Handle<LayoutDescriptor> layout_descriptor, TransitionFlag flag,
|
MaybeHandle<Name> maybe_name, const char* reason,
|
SimpleTransitionFlag simple_flag);
|
|
static Handle<Map> CopyReplaceDescriptor(Isolate* isolate, Handle<Map> map,
|
Handle<DescriptorArray> descriptors,
|
Descriptor* descriptor, int index,
|
TransitionFlag flag);
|
static Handle<Map> CopyNormalized(Isolate* isolate, Handle<Map> map,
|
PropertyNormalizationMode mode);
|
|
// TODO(ishell): Move to MapUpdater.
|
static Handle<Map> CopyGeneralizeAllFields(Isolate* isolate, Handle<Map> map,
|
ElementsKind elements_kind,
|
int modify_index,
|
PropertyKind kind,
|
PropertyAttributes attributes,
|
const char* reason);
|
|
void DeprecateTransitionTree(Isolate* isolate);
|
|
void ReplaceDescriptors(Isolate* isolate, DescriptorArray* new_descriptors,
|
LayoutDescriptor* new_layout_descriptor);
|
|
// Update field type of the given descriptor to new representation and new
|
// type. The type must be prepared for storing in descriptor array:
|
// it must be either a simple type or a map wrapped in a weak cell.
|
void UpdateFieldType(Isolate* isolate, int descriptor_number,
|
Handle<Name> name, PropertyConstness new_constness,
|
Representation new_representation,
|
MaybeObjectHandle new_wrapped_type);
|
|
// TODO(ishell): Move to MapUpdater.
|
void PrintReconfiguration(Isolate* isolate, FILE* file, int modify_index,
|
PropertyKind kind, PropertyAttributes attributes);
|
// TODO(ishell): Move to MapUpdater.
|
void PrintGeneralization(
|
Isolate* isolate, FILE* file, const char* reason, int modify_index,
|
int split, int descriptors, bool constant_to_field,
|
Representation old_representation, Representation new_representation,
|
MaybeHandle<FieldType> old_field_type, MaybeHandle<Object> old_value,
|
MaybeHandle<FieldType> new_field_type, MaybeHandle<Object> new_value);
|
static const int kFastPropertiesSoftLimit = 12;
|
static const int kMaxFastProperties = 128;
|
|
friend class MapUpdater;
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(Map);
|
};
|
|
// The cache for maps used by normalized (dictionary mode) objects.
|
// Such maps do not have property descriptors, so a typical program
|
// needs very limited number of distinct normalized maps.
|
class NormalizedMapCache : public WeakFixedArray,
|
public NeverReadOnlySpaceObject {
|
public:
|
using NeverReadOnlySpaceObject::GetHeap;
|
using NeverReadOnlySpaceObject::GetIsolate;
|
|
static Handle<NormalizedMapCache> New(Isolate* isolate);
|
|
V8_WARN_UNUSED_RESULT MaybeHandle<Map> Get(Handle<Map> fast_map,
|
PropertyNormalizationMode mode);
|
void Set(Handle<Map> fast_map, Handle<Map> normalized_map);
|
|
DECL_CAST(NormalizedMapCache)
|
|
static inline bool IsNormalizedMapCache(const HeapObject* obj);
|
|
DECL_VERIFIER(NormalizedMapCache)
|
|
private:
|
static const int kEntries = 64;
|
|
static inline int GetIndex(Handle<Map> map);
|
|
// The following declarations hide base class methods.
|
Object* get(int index);
|
void set(int index, Object* value);
|
};
|
|
} // namespace internal
|
} // namespace v8
|
|
#include "src/objects/object-macros-undef.h"
|
|
#endif // V8_OBJECTS_MAP_H_
|