• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_OBJECTS_MAP_INL_H_
6 #define V8_OBJECTS_MAP_INL_H_
7 
8 #include "src/heap/heap-write-barrier-inl.h"
9 #include "src/objects/api-callbacks-inl.h"
10 #include "src/objects/cell-inl.h"
11 #include "src/objects/descriptor-array-inl.h"
12 #include "src/objects/field-type.h"
13 #include "src/objects/instance-type-inl.h"
14 #include "src/objects/js-function-inl.h"
15 #include "src/objects/map-updater.h"
16 #include "src/objects/map.h"
17 #include "src/objects/objects-inl.h"
18 #include "src/objects/property.h"
19 #include "src/objects/prototype-info-inl.h"
20 #include "src/objects/shared-function-info-inl.h"
21 #include "src/objects/templates-inl.h"
22 #include "src/objects/transitions-inl.h"
23 #include "src/objects/transitions.h"
24 
25 #if V8_ENABLE_WEBASSEMBLY
26 #include "src/wasm/wasm-objects-inl.h"
27 #endif  // V8_ENABLE_WEBASSEMBLY
28 
29 // Has to be the last include (doesn't have include guards):
30 #include "src/objects/object-macros.h"
31 
32 namespace v8 {
33 namespace internal {
34 
35 #include "torque-generated/src/objects/map-tq-inl.inc"
36 
37 TQ_OBJECT_CONSTRUCTORS_IMPL(Map)
38 
ACCESSORS(Map,instance_descriptors,DescriptorArray,kInstanceDescriptorsOffset)39 ACCESSORS(Map, instance_descriptors, DescriptorArray,
40           kInstanceDescriptorsOffset)
41 RELAXED_ACCESSORS(Map, instance_descriptors, DescriptorArray,
42                   kInstanceDescriptorsOffset)
43 RELEASE_ACQUIRE_ACCESSORS(Map, instance_descriptors, DescriptorArray,
44                           kInstanceDescriptorsOffset)
45 
46 // A freshly allocated layout descriptor can be set on an existing map.
47 // We need to use release-store and acquire-load accessor pairs to ensure
48 // that the concurrent marking thread observes initializing stores of the
49 // layout descriptor.
50 WEAK_ACCESSORS(Map, raw_transitions, kTransitionsOrPrototypeInfoOffset)
51 RELEASE_ACQUIRE_WEAK_ACCESSORS(Map, raw_transitions,
52                                kTransitionsOrPrototypeInfoOffset)
53 
54 ACCESSORS_CHECKED2(Map, prototype, HeapObject, kPrototypeOffset, true,
55                    value.IsNull() || value.IsJSReceiver())
56 
57 DEF_GETTER(Map, prototype_info, Object) {
58   Object value = TaggedField<Object, kTransitionsOrPrototypeInfoOffset>::load(
59       cage_base, *this);
60   DCHECK(this->is_prototype_map());
61   return value;
62 }
RELEASE_ACQUIRE_ACCESSORS(Map,prototype_info,Object,kTransitionsOrPrototypeInfoOffset)63 RELEASE_ACQUIRE_ACCESSORS(Map, prototype_info, Object,
64                           kTransitionsOrPrototypeInfoOffset)
65 
66 // |bit_field| fields.
67 // Concurrent access to |has_prototype_slot| and |has_non_instance_prototype|
68 // is explicitly allowlisted here. The former is never modified after the map
69 // is setup but it's being read by concurrent marker when pointer compression
70 // is enabled. The latter bit can be modified on a live objects.
71 BIT_FIELD_ACCESSORS(Map, relaxed_bit_field, has_non_instance_prototype,
72                     Map::Bits1::HasNonInstancePrototypeBit)
73 BIT_FIELD_ACCESSORS(Map, relaxed_bit_field, has_prototype_slot,
74                     Map::Bits1::HasPrototypeSlotBit)
75 
76 // These are fine to be written as non-atomic since we don't have data races.
77 // However, they have to be read atomically from the background since the
78 // |bit_field| as a whole can mutate when using the above setters.
79 BIT_FIELD_ACCESSORS2(Map, relaxed_bit_field, bit_field, is_callable,
80                      Map::Bits1::IsCallableBit)
81 BIT_FIELD_ACCESSORS2(Map, relaxed_bit_field, bit_field, has_named_interceptor,
82                      Map::Bits1::HasNamedInterceptorBit)
83 BIT_FIELD_ACCESSORS2(Map, relaxed_bit_field, bit_field, has_indexed_interceptor,
84                      Map::Bits1::HasIndexedInterceptorBit)
85 BIT_FIELD_ACCESSORS2(Map, relaxed_bit_field, bit_field, is_undetectable,
86                      Map::Bits1::IsUndetectableBit)
87 BIT_FIELD_ACCESSORS2(Map, relaxed_bit_field, bit_field, is_access_check_needed,
88                      Map::Bits1::IsAccessCheckNeededBit)
89 BIT_FIELD_ACCESSORS2(Map, relaxed_bit_field, bit_field, is_constructor,
90                      Map::Bits1::IsConstructorBit)
91 
92 // |bit_field2| fields.
93 BIT_FIELD_ACCESSORS(Map, bit_field2, new_target_is_base,
94                     Map::Bits2::NewTargetIsBaseBit)
95 BIT_FIELD_ACCESSORS(Map, bit_field2, is_immutable_proto,
96                     Map::Bits2::IsImmutablePrototypeBit)
97 
98 // |bit_field3| fields.
99 BIT_FIELD_ACCESSORS(Map, relaxed_bit_field3, owns_descriptors,
100                     Map::Bits3::OwnsDescriptorsBit)
101 BIT_FIELD_ACCESSORS(Map, release_acquire_bit_field3, is_deprecated,
102                     Map::Bits3::IsDeprecatedBit)
103 BIT_FIELD_ACCESSORS(Map, relaxed_bit_field3, is_in_retained_map_list,
104                     Map::Bits3::IsInRetainedMapListBit)
105 BIT_FIELD_ACCESSORS(Map, release_acquire_bit_field3, is_prototype_map,
106                     Map::Bits3::IsPrototypeMapBit)
107 BIT_FIELD_ACCESSORS(Map, relaxed_bit_field3, is_migration_target,
108                     Map::Bits3::IsMigrationTargetBit)
109 BIT_FIELD_ACCESSORS2(Map, relaxed_bit_field3, bit_field3, is_extensible,
110                      Map::Bits3::IsExtensibleBit)
111 BIT_FIELD_ACCESSORS(Map, bit_field3, may_have_interesting_symbols,
112                     Map::Bits3::MayHaveInterestingSymbolsBit)
113 BIT_FIELD_ACCESSORS(Map, relaxed_bit_field3, construction_counter,
114                     Map::Bits3::ConstructionCounterBits)
115 
116 DEF_GETTER(Map, GetNamedInterceptor, InterceptorInfo) {
117   DCHECK(has_named_interceptor());
118   FunctionTemplateInfo info = GetFunctionTemplateInfo(cage_base);
119   return InterceptorInfo::cast(info.GetNamedPropertyHandler(cage_base));
120 }
121 
DEF_GETTER(Map,GetIndexedInterceptor,InterceptorInfo)122 DEF_GETTER(Map, GetIndexedInterceptor, InterceptorInfo) {
123   DCHECK(has_indexed_interceptor());
124   FunctionTemplateInfo info = GetFunctionTemplateInfo(cage_base);
125   return InterceptorInfo::cast(info.GetIndexedPropertyHandler(cage_base));
126 }
127 
128 // static
IsMostGeneralFieldType(Representation representation,FieldType field_type)129 bool Map::IsMostGeneralFieldType(Representation representation,
130                                  FieldType field_type) {
131   return !representation.IsHeapObject() || field_type.IsAny();
132 }
133 
134 // static
FieldTypeIsCleared(Representation rep,FieldType type)135 bool Map::FieldTypeIsCleared(Representation rep, FieldType type) {
136   return type.IsNone() && rep.IsHeapObject();
137 }
138 
139 // static
CanHaveFastTransitionableElementsKind(InstanceType instance_type)140 bool Map::CanHaveFastTransitionableElementsKind(InstanceType instance_type) {
141   return instance_type == JS_ARRAY_TYPE ||
142          instance_type == JS_PRIMITIVE_WRAPPER_TYPE ||
143          instance_type == JS_ARGUMENTS_OBJECT_TYPE;
144 }
145 
CanHaveFastTransitionableElementsKind()146 bool Map::CanHaveFastTransitionableElementsKind() const {
147   return CanHaveFastTransitionableElementsKind(instance_type());
148 }
149 
IsDetached(Isolate * isolate)150 bool Map::IsDetached(Isolate* isolate) const {
151   if (is_prototype_map()) return true;
152   return instance_type() == JS_OBJECT_TYPE && NumberOfOwnDescriptors() > 0 &&
153          GetBackPointer().IsUndefined(isolate);
154 }
155 
156 // static
GeneralizeIfCanHaveTransitionableFastElementsKind(Isolate * isolate,InstanceType instance_type,Representation * representation,Handle<FieldType> * field_type)157 void Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
158     Isolate* isolate, InstanceType instance_type,
159     Representation* representation, Handle<FieldType>* field_type) {
160   if (CanHaveFastTransitionableElementsKind(instance_type)) {
161     // We don't support propagation of field generalization through elements
162     // kind transitions because they are inserted into the transition tree
163     // before field transitions. In order to avoid complexity of handling
164     // such a case we ensure that all maps with transitionable elements kinds
165     // have the most general field representation and type.
166     *field_type = FieldType::Any(isolate);
167     *representation = Representation::Tagged();
168   }
169 }
170 
Normalize(Isolate * isolate,Handle<Map> fast_map,PropertyNormalizationMode mode,const char * reason)171 Handle<Map> Map::Normalize(Isolate* isolate, Handle<Map> fast_map,
172                            PropertyNormalizationMode mode, const char* reason) {
173   return Normalize(isolate, fast_map, fast_map->elements_kind(), mode, reason);
174 }
175 
EquivalentToForNormalization(const Map other,PropertyNormalizationMode mode)176 bool Map::EquivalentToForNormalization(const Map other,
177                                        PropertyNormalizationMode mode) const {
178   return EquivalentToForNormalization(other, elements_kind(), mode);
179 }
180 
TooManyFastProperties(StoreOrigin store_origin)181 bool Map::TooManyFastProperties(StoreOrigin store_origin) const {
182   if (UnusedPropertyFields() != 0) return false;
183   if (is_prototype_map()) return false;
184   if (store_origin == StoreOrigin::kNamed) {
185     int limit = std::max({kMaxFastProperties, GetInObjectProperties()});
186     FieldCounts counts = GetFieldCounts();
187     // Only count mutable fields so that objects with large numbers of
188     // constant functions do not go to dictionary mode. That would be bad
189     // because such objects have often been used as modules.
190     int external = counts.mutable_count() - GetInObjectProperties();
191     return external > limit || counts.GetTotal() > kMaxNumberOfDescriptors;
192   } else {
193     int limit = std::max({kFastPropertiesSoftLimit, GetInObjectProperties()});
194     int external =
195         NumberOfFields(ConcurrencyMode::kSynchronous) - GetInObjectProperties();
196     return external > limit;
197   }
198 }
199 
GetLastDescriptorName(Isolate * isolate)200 Name Map::GetLastDescriptorName(Isolate* isolate) const {
201   return instance_descriptors(isolate).GetKey(LastAdded());
202 }
203 
GetLastDescriptorDetails(Isolate * isolate)204 PropertyDetails Map::GetLastDescriptorDetails(Isolate* isolate) const {
205   return instance_descriptors(isolate).GetDetails(LastAdded());
206 }
207 
LastAdded()208 InternalIndex Map::LastAdded() const {
209   int number_of_own_descriptors = NumberOfOwnDescriptors();
210   DCHECK_GT(number_of_own_descriptors, 0);
211   return InternalIndex(number_of_own_descriptors - 1);
212 }
213 
NumberOfOwnDescriptors()214 int Map::NumberOfOwnDescriptors() const {
215   return Bits3::NumberOfOwnDescriptorsBits::decode(
216       release_acquire_bit_field3());
217 }
218 
SetNumberOfOwnDescriptors(int number)219 void Map::SetNumberOfOwnDescriptors(int number) {
220   DCHECK_LE(number, instance_descriptors().number_of_descriptors());
221   CHECK_LE(static_cast<unsigned>(number),
222            static_cast<unsigned>(kMaxNumberOfDescriptors));
223   set_release_acquire_bit_field3(
224       Bits3::NumberOfOwnDescriptorsBits::update(bit_field3(), number));
225 }
226 
IterateOwnDescriptors()227 InternalIndex::Range Map::IterateOwnDescriptors() const {
228   return InternalIndex::Range(NumberOfOwnDescriptors());
229 }
230 
EnumLength()231 int Map::EnumLength() const {
232   return Bits3::EnumLengthBits::decode(bit_field3());
233 }
234 
SetEnumLength(int length)235 void Map::SetEnumLength(int length) {
236   if (length != kInvalidEnumCacheSentinel) {
237     DCHECK_LE(length, NumberOfOwnDescriptors());
238     CHECK_LE(static_cast<unsigned>(length),
239              static_cast<unsigned>(kMaxNumberOfDescriptors));
240   }
241   set_relaxed_bit_field3(Bits3::EnumLengthBits::update(bit_field3(), length));
242 }
243 
GetInitialElements()244 FixedArrayBase Map::GetInitialElements() const {
245   FixedArrayBase result;
246   if (has_fast_elements() || has_fast_string_wrapper_elements() ||
247       has_any_nonextensible_elements()) {
248     result = GetReadOnlyRoots().empty_fixed_array();
249   } else if (has_typed_array_or_rab_gsab_typed_array_elements()) {
250     result = GetReadOnlyRoots().empty_byte_array();
251   } else if (has_dictionary_elements()) {
252     result = GetReadOnlyRoots().empty_slow_element_dictionary();
253   } else {
254     UNREACHABLE();
255   }
256   DCHECK(!ObjectInYoungGeneration(result));
257   return result;
258 }
259 
visitor_id()260 VisitorId Map::visitor_id() const {
261   return static_cast<VisitorId>(
262       RELAXED_READ_BYTE_FIELD(*this, kVisitorIdOffset));
263 }
264 
set_visitor_id(VisitorId id)265 void Map::set_visitor_id(VisitorId id) {
266   CHECK_LT(static_cast<unsigned>(id), 256);
267   RELAXED_WRITE_BYTE_FIELD(*this, kVisitorIdOffset, static_cast<byte>(id));
268 }
269 
instance_size_in_words()270 int Map::instance_size_in_words() const {
271   return RELAXED_READ_BYTE_FIELD(*this, kInstanceSizeInWordsOffset);
272 }
273 
set_instance_size_in_words(int value)274 void Map::set_instance_size_in_words(int value) {
275   RELAXED_WRITE_BYTE_FIELD(*this, kInstanceSizeInWordsOffset,
276                            static_cast<byte>(value));
277 }
278 
instance_size()279 int Map::instance_size() const {
280   return instance_size_in_words() << kTaggedSizeLog2;
281 }
282 
set_instance_size(int value)283 void Map::set_instance_size(int value) {
284   CHECK(IsAligned(value, kTaggedSize));
285   value >>= kTaggedSizeLog2;
286   CHECK_LT(static_cast<unsigned>(value), 256);
287   set_instance_size_in_words(value);
288 }
289 
inobject_properties_start_or_constructor_function_index()290 int Map::inobject_properties_start_or_constructor_function_index() const {
291   // TODO(solanes, v8:7790, v8:11353): Make this and the setter non-atomic
292   // when TSAN sees the map's store synchronization.
293   return RELAXED_READ_BYTE_FIELD(
294       *this, kInobjectPropertiesStartOrConstructorFunctionIndexOffset);
295 }
296 
set_inobject_properties_start_or_constructor_function_index(int value)297 void Map::set_inobject_properties_start_or_constructor_function_index(
298     int value) {
299   CHECK_LT(static_cast<unsigned>(value), 256);
300   RELAXED_WRITE_BYTE_FIELD(
301       *this, kInobjectPropertiesStartOrConstructorFunctionIndexOffset,
302       static_cast<byte>(value));
303 }
304 
GetInObjectPropertiesStartInWords()305 int Map::GetInObjectPropertiesStartInWords() const {
306   DCHECK(IsJSObjectMap());
307   return inobject_properties_start_or_constructor_function_index();
308 }
309 
SetInObjectPropertiesStartInWords(int value)310 void Map::SetInObjectPropertiesStartInWords(int value) {
311   CHECK(IsJSObjectMap());
312   set_inobject_properties_start_or_constructor_function_index(value);
313 }
314 
GetInObjectProperties()315 int Map::GetInObjectProperties() const {
316   DCHECK(IsJSObjectMap());
317   return instance_size_in_words() - GetInObjectPropertiesStartInWords();
318 }
319 
GetConstructorFunctionIndex()320 int Map::GetConstructorFunctionIndex() const {
321   DCHECK(IsPrimitiveMap());
322   return inobject_properties_start_or_constructor_function_index();
323 }
324 
SetConstructorFunctionIndex(int value)325 void Map::SetConstructorFunctionIndex(int value) {
326   CHECK(IsPrimitiveMap());
327   set_inobject_properties_start_or_constructor_function_index(value);
328 }
329 
GetInObjectPropertyOffset(int index)330 int Map::GetInObjectPropertyOffset(int index) const {
331   return (GetInObjectPropertiesStartInWords() + index) * kTaggedSize;
332 }
333 
AddMissingTransitionsForTesting(Isolate * isolate,Handle<Map> split_map,Handle<DescriptorArray> descriptors)334 Handle<Map> Map::AddMissingTransitionsForTesting(
335     Isolate* isolate, Handle<Map> split_map,
336     Handle<DescriptorArray> descriptors) {
337   return AddMissingTransitions(isolate, split_map, descriptors);
338 }
339 
instance_type()340 InstanceType Map::instance_type() const {
341   // TODO(solanes, v8:7790, v8:11353, v8:11945): Make this and the setter
342   // non-atomic when TSAN sees the map's store synchronization.
343   return static_cast<InstanceType>(
344       RELAXED_READ_UINT16_FIELD(*this, kInstanceTypeOffset));
345 }
346 
set_instance_type(InstanceType value)347 void Map::set_instance_type(InstanceType value) {
348   RELAXED_WRITE_UINT16_FIELD(*this, kInstanceTypeOffset, value);
349 }
350 
UnusedPropertyFields()351 int Map::UnusedPropertyFields() const {
352   int value = used_or_unused_instance_size_in_words();
353   DCHECK_IMPLIES(!IsJSObjectMap(), value == 0);
354   int unused;
355   if (value >= JSObject::kFieldsAdded) {
356     unused = instance_size_in_words() - value;
357   } else {
358     // For out of object properties "used_or_unused_instance_size_in_words"
359     // byte encodes the slack in the property array.
360     unused = value;
361   }
362   return unused;
363 }
364 
UnusedInObjectProperties()365 int Map::UnusedInObjectProperties() const {
366   // Like Map::UnusedPropertyFields(), but returns 0 for out of object
367   // properties.
368   int value = used_or_unused_instance_size_in_words();
369   DCHECK_IMPLIES(!IsJSObjectMap(), value == 0);
370   if (value >= JSObject::kFieldsAdded) {
371     return instance_size_in_words() - value;
372   }
373   return 0;
374 }
375 
used_or_unused_instance_size_in_words()376 int Map::used_or_unused_instance_size_in_words() const {
377   return RELAXED_READ_BYTE_FIELD(*this, kUsedOrUnusedInstanceSizeInWordsOffset);
378 }
379 
set_used_or_unused_instance_size_in_words(int value)380 void Map::set_used_or_unused_instance_size_in_words(int value) {
381   CHECK_LE(static_cast<unsigned>(value), 255);
382   RELAXED_WRITE_BYTE_FIELD(*this, kUsedOrUnusedInstanceSizeInWordsOffset,
383                            static_cast<byte>(value));
384 }
385 
UsedInstanceSize()386 int Map::UsedInstanceSize() const {
387   int words = used_or_unused_instance_size_in_words();
388   if (words < JSObject::kFieldsAdded) {
389     // All in-object properties are used and the words is tracking the slack
390     // in the property array.
391     return instance_size();
392   }
393   return words * kTaggedSize;
394 }
395 
SetInObjectUnusedPropertyFields(int value)396 void Map::SetInObjectUnusedPropertyFields(int value) {
397   STATIC_ASSERT(JSObject::kFieldsAdded == JSObject::kHeaderSize / kTaggedSize);
398   if (!IsJSObjectMap()) {
399     CHECK_EQ(0, value);
400     set_used_or_unused_instance_size_in_words(0);
401     DCHECK_EQ(0, UnusedPropertyFields());
402     return;
403   }
404   CHECK_LE(0, value);
405   DCHECK_LE(value, GetInObjectProperties());
406   int used_inobject_properties = GetInObjectProperties() - value;
407   set_used_or_unused_instance_size_in_words(
408       GetInObjectPropertyOffset(used_inobject_properties) / kTaggedSize);
409   DCHECK_EQ(value, UnusedPropertyFields());
410 }
411 
SetOutOfObjectUnusedPropertyFields(int value)412 void Map::SetOutOfObjectUnusedPropertyFields(int value) {
413   STATIC_ASSERT(JSObject::kFieldsAdded == JSObject::kHeaderSize / kTaggedSize);
414   CHECK_LT(static_cast<unsigned>(value), JSObject::kFieldsAdded);
415   // For out of object properties "used_instance_size_in_words" byte encodes
416   // the slack in the property array.
417   set_used_or_unused_instance_size_in_words(value);
418   DCHECK_EQ(value, UnusedPropertyFields());
419 }
420 
CopyUnusedPropertyFields(Map map)421 void Map::CopyUnusedPropertyFields(Map map) {
422   set_used_or_unused_instance_size_in_words(
423       map.used_or_unused_instance_size_in_words());
424   DCHECK_EQ(UnusedPropertyFields(), map.UnusedPropertyFields());
425 }
426 
CopyUnusedPropertyFieldsAdjustedForInstanceSize(Map map)427 void Map::CopyUnusedPropertyFieldsAdjustedForInstanceSize(Map map) {
428   int value = map.used_or_unused_instance_size_in_words();
429   if (value >= JSPrimitiveWrapper::kFieldsAdded) {
430     // Unused in-object fields. Adjust the offset from the object’s start
431     // so it matches the distance to the object’s end.
432     value += instance_size_in_words() - map.instance_size_in_words();
433   }
434   set_used_or_unused_instance_size_in_words(value);
435   DCHECK_EQ(UnusedPropertyFields(), map.UnusedPropertyFields());
436 }
437 
AccountAddedPropertyField()438 void Map::AccountAddedPropertyField() {
439   // Update used instance size and unused property fields number.
440   STATIC_ASSERT(JSObject::kFieldsAdded == JSObject::kHeaderSize / kTaggedSize);
441 #ifdef DEBUG
442   int new_unused = UnusedPropertyFields() - 1;
443   if (new_unused < 0) new_unused += JSObject::kFieldsAdded;
444 #endif
445   int value = used_or_unused_instance_size_in_words();
446   if (value >= JSObject::kFieldsAdded) {
447     if (value == instance_size_in_words()) {
448       AccountAddedOutOfObjectPropertyField(0);
449     } else {
450       // The property is added in-object, so simply increment the counter.
451       set_used_or_unused_instance_size_in_words(value + 1);
452     }
453   } else {
454     AccountAddedOutOfObjectPropertyField(value);
455   }
456   DCHECK_EQ(new_unused, UnusedPropertyFields());
457 }
458 
AccountAddedOutOfObjectPropertyField(int unused_in_property_array)459 void Map::AccountAddedOutOfObjectPropertyField(int unused_in_property_array) {
460   unused_in_property_array--;
461   if (unused_in_property_array < 0) {
462     unused_in_property_array += JSObject::kFieldsAdded;
463   }
464   CHECK_LT(static_cast<unsigned>(unused_in_property_array),
465            JSObject::kFieldsAdded);
466   set_used_or_unused_instance_size_in_words(unused_in_property_array);
467   DCHECK_EQ(unused_in_property_array, UnusedPropertyFields());
468 }
469 
470 #if V8_ENABLE_WEBASSEMBLY
WasmByte1()471 uint8_t Map::WasmByte1() const {
472   DCHECK(IsWasmObjectMap());
473   return inobject_properties_start_or_constructor_function_index();
474 }
475 
WasmByte2()476 uint8_t Map::WasmByte2() const {
477   DCHECK(IsWasmObjectMap());
478   return used_or_unused_instance_size_in_words();
479 }
480 
SetWasmByte1(uint8_t value)481 void Map::SetWasmByte1(uint8_t value) {
482   CHECK(IsWasmObjectMap());
483   set_inobject_properties_start_or_constructor_function_index(value);
484 }
485 
SetWasmByte2(uint8_t value)486 void Map::SetWasmByte2(uint8_t value) {
487   CHECK(IsWasmObjectMap());
488   set_used_or_unused_instance_size_in_words(value);
489 }
490 #endif  // V8_ENABLE_WEBASSEMBLY
491 
bit_field()492 byte Map::bit_field() const {
493   // TODO(solanes, v8:7790, v8:11353): Make this non-atomic when TSAN sees the
494   // map's store synchronization.
495   return relaxed_bit_field();
496 }
497 
set_bit_field(byte value)498 void Map::set_bit_field(byte value) {
499   // TODO(solanes, v8:7790, v8:11353): Make this non-atomic when TSAN sees the
500   // map's store synchronization.
501   set_relaxed_bit_field(value);
502 }
503 
relaxed_bit_field()504 byte Map::relaxed_bit_field() const {
505   return RELAXED_READ_BYTE_FIELD(*this, kBitFieldOffset);
506 }
507 
set_relaxed_bit_field(byte value)508 void Map::set_relaxed_bit_field(byte value) {
509   RELAXED_WRITE_BYTE_FIELD(*this, kBitFieldOffset, value);
510 }
511 
bit_field2()512 byte Map::bit_field2() const { return ReadField<byte>(kBitField2Offset); }
513 
set_bit_field2(byte value)514 void Map::set_bit_field2(byte value) {
515   WriteField<byte>(kBitField2Offset, value);
516 }
517 
bit_field3()518 uint32_t Map::bit_field3() const {
519   // TODO(solanes, v8:7790, v8:11353): Make this and the setter non-atomic
520   // when TSAN sees the map's store synchronization.
521   return relaxed_bit_field3();
522 }
523 
set_bit_field3(uint32_t value)524 void Map::set_bit_field3(uint32_t value) { set_relaxed_bit_field3(value); }
525 
relaxed_bit_field3()526 uint32_t Map::relaxed_bit_field3() const {
527   return RELAXED_READ_UINT32_FIELD(*this, kBitField3Offset);
528 }
529 
set_relaxed_bit_field3(uint32_t value)530 void Map::set_relaxed_bit_field3(uint32_t value) {
531   RELAXED_WRITE_UINT32_FIELD(*this, kBitField3Offset, value);
532 }
533 
release_acquire_bit_field3()534 uint32_t Map::release_acquire_bit_field3() const {
535   return ACQUIRE_READ_UINT32_FIELD(*this, kBitField3Offset);
536 }
537 
set_release_acquire_bit_field3(uint32_t value)538 void Map::set_release_acquire_bit_field3(uint32_t value) {
539   RELEASE_WRITE_UINT32_FIELD(*this, kBitField3Offset, value);
540 }
541 
is_abandoned_prototype_map()542 bool Map::is_abandoned_prototype_map() const {
543   return is_prototype_map() && !owns_descriptors();
544 }
545 
should_be_fast_prototype_map()546 bool Map::should_be_fast_prototype_map() const {
547   if (!prototype_info().IsPrototypeInfo()) return false;
548   return PrototypeInfo::cast(prototype_info()).should_be_fast_map();
549 }
550 
set_elements_kind(ElementsKind elements_kind)551 void Map::set_elements_kind(ElementsKind elements_kind) {
552   CHECK_LT(static_cast<int>(elements_kind), kElementsKindCount);
553   set_bit_field2(
554       Map::Bits2::ElementsKindBits::update(bit_field2(), elements_kind));
555 }
556 
elements_kind()557 ElementsKind Map::elements_kind() const {
558   return Map::Bits2::ElementsKindBits::decode(bit_field2());
559 }
560 
has_fast_smi_elements()561 bool Map::has_fast_smi_elements() const {
562   return IsSmiElementsKind(elements_kind());
563 }
564 
has_fast_object_elements()565 bool Map::has_fast_object_elements() const {
566   return IsObjectElementsKind(elements_kind());
567 }
568 
has_fast_smi_or_object_elements()569 bool Map::has_fast_smi_or_object_elements() const {
570   return IsSmiOrObjectElementsKind(elements_kind());
571 }
572 
has_fast_double_elements()573 bool Map::has_fast_double_elements() const {
574   return IsDoubleElementsKind(elements_kind());
575 }
576 
has_fast_elements()577 bool Map::has_fast_elements() const {
578   return IsFastElementsKind(elements_kind());
579 }
580 
has_sloppy_arguments_elements()581 bool Map::has_sloppy_arguments_elements() const {
582   return IsSloppyArgumentsElementsKind(elements_kind());
583 }
584 
has_fast_sloppy_arguments_elements()585 bool Map::has_fast_sloppy_arguments_elements() const {
586   return elements_kind() == FAST_SLOPPY_ARGUMENTS_ELEMENTS;
587 }
588 
has_fast_string_wrapper_elements()589 bool Map::has_fast_string_wrapper_elements() const {
590   return elements_kind() == FAST_STRING_WRAPPER_ELEMENTS;
591 }
592 
has_typed_array_or_rab_gsab_typed_array_elements()593 bool Map::has_typed_array_or_rab_gsab_typed_array_elements() const {
594   return IsTypedArrayOrRabGsabTypedArrayElementsKind(elements_kind());
595 }
596 
has_any_typed_array_or_wasm_array_elements()597 bool Map::has_any_typed_array_or_wasm_array_elements() const {
598   ElementsKind kind = elements_kind();
599   return IsTypedArrayOrRabGsabTypedArrayElementsKind(kind) ||
600 #if V8_ENABLE_WEBASSEMBLY
601          IsWasmArrayElementsKind(kind) ||
602 #endif  // V8_ENABLE_WEBASSEMBLY
603          false;
604 }
605 
has_dictionary_elements()606 bool Map::has_dictionary_elements() const {
607   return IsDictionaryElementsKind(elements_kind());
608 }
609 
has_any_nonextensible_elements()610 bool Map::has_any_nonextensible_elements() const {
611   return IsAnyNonextensibleElementsKind(elements_kind());
612 }
613 
has_nonextensible_elements()614 bool Map::has_nonextensible_elements() const {
615   return IsNonextensibleElementsKind(elements_kind());
616 }
617 
has_sealed_elements()618 bool Map::has_sealed_elements() const {
619   return IsSealedElementsKind(elements_kind());
620 }
621 
has_frozen_elements()622 bool Map::has_frozen_elements() const {
623   return IsFrozenElementsKind(elements_kind());
624 }
625 
set_is_dictionary_map(bool value)626 void Map::set_is_dictionary_map(bool value) {
627   uint32_t new_bit_field3 =
628       Bits3::IsDictionaryMapBit::update(bit_field3(), value);
629   new_bit_field3 = Bits3::IsUnstableBit::update(new_bit_field3, value);
630   set_bit_field3(new_bit_field3);
631 }
632 
is_dictionary_map()633 bool Map::is_dictionary_map() const {
634   return Bits3::IsDictionaryMapBit::decode(relaxed_bit_field3());
635 }
636 
mark_unstable()637 void Map::mark_unstable() {
638   set_release_acquire_bit_field3(
639       Bits3::IsUnstableBit::update(bit_field3(), true));
640 }
641 
is_stable()642 bool Map::is_stable() const {
643   return !Bits3::IsUnstableBit::decode(release_acquire_bit_field3());
644 }
645 
CanBeDeprecated()646 bool Map::CanBeDeprecated() const {
647   for (InternalIndex i : IterateOwnDescriptors()) {
648     PropertyDetails details = instance_descriptors(kRelaxedLoad).GetDetails(i);
649     if (details.representation().MightCauseMapDeprecation()) return true;
650     if (details.kind() == PropertyKind::kData &&
651         details.location() == PropertyLocation::kDescriptor) {
652       return true;
653     }
654   }
655   return false;
656 }
657 
NotifyLeafMapLayoutChange(Isolate * isolate)658 void Map::NotifyLeafMapLayoutChange(Isolate* isolate) {
659   if (is_stable()) {
660     mark_unstable();
661     dependent_code().DeoptimizeDependentCodeGroup(
662         isolate, DependentCode::kPrototypeCheckGroup);
663   }
664 }
665 
CanTransition()666 bool Map::CanTransition() const {
667   // Only JSObject and subtypes have map transitions and back pointers.
668   return InstanceTypeChecker::IsJSObject(instance_type());
669 }
670 
671 #define DEF_TESTER(Type, ...)                              \
672   bool Map::Is##Type##Map() const {                        \
673     return InstanceTypeChecker::Is##Type(instance_type()); \
674   }
INSTANCE_TYPE_CHECKERS(DEF_TESTER)675 INSTANCE_TYPE_CHECKERS(DEF_TESTER)
676 #undef DEF_TESTER
677 
678 bool Map::IsBooleanMap() const {
679   return *this == GetReadOnlyRoots().boolean_map();
680 }
681 
IsNullOrUndefinedMap()682 bool Map::IsNullOrUndefinedMap() const {
683   auto roots = GetReadOnlyRoots();
684   return *this == roots.null_map() || *this == roots.undefined_map();
685 }
686 
IsPrimitiveMap()687 bool Map::IsPrimitiveMap() const {
688   return instance_type() <= LAST_PRIMITIVE_HEAP_OBJECT_TYPE;
689 }
690 
UpdateDescriptors(Isolate * isolate,DescriptorArray descriptors,int number_of_own_descriptors)691 void Map::UpdateDescriptors(Isolate* isolate, DescriptorArray descriptors,
692                             int number_of_own_descriptors) {
693   SetInstanceDescriptors(isolate, descriptors, number_of_own_descriptors);
694 }
695 
InitializeDescriptors(Isolate * isolate,DescriptorArray descriptors)696 void Map::InitializeDescriptors(Isolate* isolate, DescriptorArray descriptors) {
697   SetInstanceDescriptors(isolate, descriptors,
698                          descriptors.number_of_descriptors());
699 }
700 
clear_padding()701 void Map::clear_padding() {
702   if (FIELD_SIZE(kOptionalPaddingOffset) == 0) return;
703   DCHECK_EQ(4, FIELD_SIZE(kOptionalPaddingOffset));
704   memset(reinterpret_cast<void*>(address() + kOptionalPaddingOffset), 0,
705          FIELD_SIZE(kOptionalPaddingOffset));
706 }
707 
AppendDescriptor(Isolate * isolate,Descriptor * desc)708 void Map::AppendDescriptor(Isolate* isolate, Descriptor* desc) {
709   DescriptorArray descriptors = instance_descriptors(isolate);
710   int number_of_own_descriptors = NumberOfOwnDescriptors();
711   DCHECK(descriptors.number_of_descriptors() == number_of_own_descriptors);
712   {
713     // The following two operations need to happen before the marking write
714     // barrier.
715     descriptors.Append(desc);
716     SetNumberOfOwnDescriptors(number_of_own_descriptors + 1);
717 #ifndef V8_DISABLE_WRITE_BARRIERS
718     WriteBarrier::Marking(descriptors, number_of_own_descriptors + 1);
719 #endif
720   }
721   // Properly mark the map if the {desc} is an "interesting symbol".
722   if (desc->GetKey()->IsInterestingSymbol()) {
723     set_may_have_interesting_symbols(true);
724   }
725   PropertyDetails details = desc->GetDetails();
726   if (details.location() == PropertyLocation::kField) {
727     DCHECK_GT(UnusedPropertyFields(), 0);
728     AccountAddedPropertyField();
729   }
730 
731 // This function does not support appending double field descriptors and
732 // it should never try to (otherwise, layout descriptor must be updated too).
733 #ifdef DEBUG
734   DCHECK(details.location() != PropertyLocation::kField ||
735          !details.representation().IsDouble());
736 #endif
737 }
738 
ConcurrentIsMap(PtrComprCageBase cage_base,const Object & object)739 bool Map::ConcurrentIsMap(PtrComprCageBase cage_base,
740                           const Object& object) const {
741   return object.IsHeapObject() && HeapObject::cast(object).map(cage_base) ==
742                                       GetReadOnlyRoots(cage_base).meta_map();
743 }
744 
DEF_GETTER(Map,GetBackPointer,HeapObject)745 DEF_GETTER(Map, GetBackPointer, HeapObject) {
746   Object object = constructor_or_back_pointer(cage_base, kRelaxedLoad);
747   if (ConcurrentIsMap(cage_base, object)) {
748     return Map::cast(object);
749   }
750   return GetReadOnlyRoots(cage_base).undefined_value();
751 }
752 
SetBackPointer(HeapObject value,WriteBarrierMode mode)753 void Map::SetBackPointer(HeapObject value, WriteBarrierMode mode) {
754   CHECK_GE(instance_type(), FIRST_JS_RECEIVER_TYPE);
755   CHECK(value.IsMap());
756   CHECK(GetBackPointer().IsUndefined());
757   CHECK_EQ(Map::cast(value).GetConstructor(), constructor_or_back_pointer());
758   set_constructor_or_back_pointer(value, mode);
759 }
760 
761 // static
ElementsTransitionMap(Isolate * isolate,ConcurrencyMode cmode)762 Map Map::ElementsTransitionMap(Isolate* isolate, ConcurrencyMode cmode) {
763   return TransitionsAccessor(isolate, *this, IsConcurrent(cmode))
764       .SearchSpecial(ReadOnlyRoots(isolate).elements_transition_symbol());
765 }
766 
ACCESSORS(Map,dependent_code,DependentCode,kDependentCodeOffset)767 ACCESSORS(Map, dependent_code, DependentCode, kDependentCodeOffset)
768 ACCESSORS(Map, prototype_validity_cell, Object, kPrototypeValidityCellOffset)
769 ACCESSORS_CHECKED2(Map, constructor_or_back_pointer, Object,
770                    kConstructorOrBackPointerOrNativeContextOffset,
771                    !IsContextMap(), value.IsNull() || !IsContextMap())
772 RELAXED_ACCESSORS_CHECKED2(Map, constructor_or_back_pointer, Object,
773                            kConstructorOrBackPointerOrNativeContextOffset,
774                            !IsContextMap(), value.IsNull() || !IsContextMap())
775 ACCESSORS_CHECKED(Map, native_context, NativeContext,
776                   kConstructorOrBackPointerOrNativeContextOffset,
777                   IsContextMap())
778 ACCESSORS_CHECKED(Map, native_context_or_null, Object,
779                   kConstructorOrBackPointerOrNativeContextOffset,
780                   (value.IsNull() || value.IsNativeContext()) && IsContextMap())
781 #if V8_ENABLE_WEBASSEMBLY
782 ACCESSORS_CHECKED(Map, wasm_type_info, WasmTypeInfo,
783                   kConstructorOrBackPointerOrNativeContextOffset,
784                   IsWasmStructMap() || IsWasmArrayMap() ||
785                       IsWasmInternalFunctionMap())
786 #endif  // V8_ENABLE_WEBASSEMBLY
787 
788 bool Map::IsPrototypeValidityCellValid() const {
789   Object validity_cell = prototype_validity_cell();
790   Object value = validity_cell.IsSmi() ? Smi::cast(validity_cell)
791                                        : Cell::cast(validity_cell).value();
792   return value == Smi::FromInt(Map::kPrototypeChainValid);
793 }
794 
DEF_GETTER(Map,GetConstructor,Object)795 DEF_GETTER(Map, GetConstructor, Object) {
796   Object maybe_constructor = constructor_or_back_pointer(cage_base);
797   // Follow any back pointers.
798   while (ConcurrentIsMap(cage_base, maybe_constructor)) {
799     maybe_constructor =
800         Map::cast(maybe_constructor).constructor_or_back_pointer(cage_base);
801   }
802   return maybe_constructor;
803 }
804 
TryGetConstructor(Isolate * isolate,int max_steps)805 Object Map::TryGetConstructor(Isolate* isolate, int max_steps) {
806   Object maybe_constructor = constructor_or_back_pointer(isolate);
807   // Follow any back pointers.
808   while (maybe_constructor.IsMap(isolate)) {
809     if (max_steps-- == 0) return Smi::FromInt(0);
810     maybe_constructor =
811         Map::cast(maybe_constructor).constructor_or_back_pointer(isolate);
812   }
813   return maybe_constructor;
814 }
815 
DEF_GETTER(Map,GetFunctionTemplateInfo,FunctionTemplateInfo)816 DEF_GETTER(Map, GetFunctionTemplateInfo, FunctionTemplateInfo) {
817   Object constructor = GetConstructor(cage_base);
818   if (constructor.IsJSFunction(cage_base)) {
819     // TODO(ishell): IsApiFunction(isolate) and get_api_func_data(isolate)
820     DCHECK(JSFunction::cast(constructor).shared(cage_base).IsApiFunction());
821     return JSFunction::cast(constructor).shared(cage_base).get_api_func_data();
822   }
823   DCHECK(constructor.IsFunctionTemplateInfo(cage_base));
824   return FunctionTemplateInfo::cast(constructor);
825 }
826 
SetConstructor(Object constructor,WriteBarrierMode mode)827 void Map::SetConstructor(Object constructor, WriteBarrierMode mode) {
828   // Never overwrite a back pointer with a constructor.
829   CHECK(!constructor_or_back_pointer().IsMap());
830   set_constructor_or_back_pointer(constructor, mode);
831 }
832 
CopyInitialMap(Isolate * isolate,Handle<Map> map)833 Handle<Map> Map::CopyInitialMap(Isolate* isolate, Handle<Map> map) {
834   return CopyInitialMap(isolate, map, map->instance_size(),
835                         map->GetInObjectProperties(),
836                         map->UnusedPropertyFields());
837 }
838 
IsInobjectSlackTrackingInProgress()839 bool Map::IsInobjectSlackTrackingInProgress() const {
840   return construction_counter() != Map::kNoSlackTracking;
841 }
842 
InobjectSlackTrackingStep(Isolate * isolate)843 void Map::InobjectSlackTrackingStep(Isolate* isolate) {
844   DisallowGarbageCollection no_gc;
845   // Slack tracking should only be performed on an initial map.
846   DCHECK(GetBackPointer().IsUndefined());
847   if (!IsInobjectSlackTrackingInProgress()) return;
848   int counter = construction_counter();
849   set_construction_counter(counter - 1);
850   if (counter == kSlackTrackingCounterEnd) {
851     MapUpdater::CompleteInobjectSlackTracking(isolate, *this);
852   }
853 }
854 
SlackForArraySize(int old_size,int size_limit)855 int Map::SlackForArraySize(int old_size, int size_limit) {
856   const int max_slack = size_limit - old_size;
857   CHECK_LE(0, max_slack);
858   if (old_size < 4) {
859     DCHECK_LE(1, max_slack);
860     return 1;
861   }
862   return std::min(max_slack, old_size / 4);
863 }
864 
InstanceSizeFromSlack(int slack)865 int Map::InstanceSizeFromSlack(int slack) const {
866   return instance_size() - slack * kTaggedSize;
867 }
868 
OBJECT_CONSTRUCTORS_IMPL(NormalizedMapCache,WeakFixedArray)869 OBJECT_CONSTRUCTORS_IMPL(NormalizedMapCache, WeakFixedArray)
870 CAST_ACCESSOR(NormalizedMapCache)
871 NEVER_READ_ONLY_SPACE_IMPL(NormalizedMapCache)
872 
873 int NormalizedMapCache::GetIndex(Handle<Map> map) {
874   return map->Hash() % NormalizedMapCache::kEntries;
875 }
876 
DEF_GETTER(HeapObject,IsNormalizedMapCache,bool)877 DEF_GETTER(HeapObject, IsNormalizedMapCache, bool) {
878   if (!IsWeakFixedArray(cage_base)) return false;
879   if (WeakFixedArray::cast(*this).length() != NormalizedMapCache::kEntries) {
880     return false;
881   }
882   return true;
883 }
884 
885 }  // namespace internal
886 }  // namespace v8
887 
888 #include "src/objects/object-macros-undef.h"
889 
890 #endif  // V8_OBJECTS_MAP_INL_H_
891