1 // Copyright 2012 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_PROPERTY_DETAILS_H_
6 #define V8_OBJECTS_PROPERTY_DETAILS_H_
7
8 #include "include/v8-object.h"
9 #include "src/base/bit-field.h"
10 #include "src/common/globals.h"
11 #include "src/flags/flags.h"
12 #include "src/utils/allocation.h"
13
14 namespace v8 {
15 namespace internal {
16
17 // ES6 6.1.7.1
18 enum PropertyAttributes {
19 NONE = ::v8::None,
20 READ_ONLY = ::v8::ReadOnly,
21 DONT_ENUM = ::v8::DontEnum,
22 DONT_DELETE = ::v8::DontDelete,
23
24 ALL_ATTRIBUTES_MASK = READ_ONLY | DONT_ENUM | DONT_DELETE,
25
26 SEALED = DONT_DELETE,
27 FROZEN = SEALED | READ_ONLY,
28
29 ABSENT = 64, // Used in runtime to indicate a property is absent.
30 // ABSENT can never be stored in or returned from a descriptor's attributes
31 // bitfield. It is only used as a return value meaning the attributes of
32 // a non-existent property.
33 };
34
PropertyAttributesFromInt(int value)35 V8_INLINE PropertyAttributes PropertyAttributesFromInt(int value) {
36 DCHECK_EQ(value & ~PropertyAttributes::ALL_ATTRIBUTES_MASK, 0);
37 return static_cast<PropertyAttributes>(value);
38 }
39
40 // Number of distinct bits in PropertyAttributes.
41 static const int kPropertyAttributesBitsCount = 3;
42
43 static const int kPropertyAttributesCombinationsCount =
44 1 << kPropertyAttributesBitsCount;
45
46 enum PropertyFilter {
47 ALL_PROPERTIES = 0,
48 ONLY_WRITABLE = 1,
49 ONLY_ENUMERABLE = 2,
50 ONLY_CONFIGURABLE = 4,
51 SKIP_STRINGS = 8,
52 SKIP_SYMBOLS = 16,
53 ONLY_ALL_CAN_READ = 32,
54 PRIVATE_NAMES_ONLY = 64,
55 ENUMERABLE_STRINGS = ONLY_ENUMERABLE | SKIP_SYMBOLS,
56 };
57 // Enable fast comparisons of PropertyAttributes against PropertyFilters.
58 STATIC_ASSERT(ALL_PROPERTIES == static_cast<PropertyFilter>(NONE));
59 STATIC_ASSERT(ONLY_WRITABLE == static_cast<PropertyFilter>(READ_ONLY));
60 STATIC_ASSERT(ONLY_ENUMERABLE == static_cast<PropertyFilter>(DONT_ENUM));
61 STATIC_ASSERT(ONLY_CONFIGURABLE == static_cast<PropertyFilter>(DONT_DELETE));
62 STATIC_ASSERT(((SKIP_STRINGS | SKIP_SYMBOLS | ONLY_ALL_CAN_READ) &
63 ALL_ATTRIBUTES_MASK) == 0);
64 STATIC_ASSERT(ALL_PROPERTIES ==
65 static_cast<PropertyFilter>(v8::PropertyFilter::ALL_PROPERTIES));
66 STATIC_ASSERT(ONLY_WRITABLE ==
67 static_cast<PropertyFilter>(v8::PropertyFilter::ONLY_WRITABLE));
68 STATIC_ASSERT(ONLY_ENUMERABLE ==
69 static_cast<PropertyFilter>(v8::PropertyFilter::ONLY_ENUMERABLE));
70 STATIC_ASSERT(ONLY_CONFIGURABLE == static_cast<PropertyFilter>(
71 v8::PropertyFilter::ONLY_CONFIGURABLE));
72 STATIC_ASSERT(SKIP_STRINGS ==
73 static_cast<PropertyFilter>(v8::PropertyFilter::SKIP_STRINGS));
74 STATIC_ASSERT(SKIP_SYMBOLS ==
75 static_cast<PropertyFilter>(v8::PropertyFilter::SKIP_SYMBOLS));
76
77 // Assert that kPropertyAttributesBitsCount value matches the definition of
78 // ALL_ATTRIBUTES_MASK.
79 STATIC_ASSERT((ALL_ATTRIBUTES_MASK == (READ_ONLY | DONT_ENUM | DONT_DELETE)) ==
80 (kPropertyAttributesBitsCount == 3));
81
82 class Smi;
83 class TypeInfo;
84
85 // Order of kinds is significant.
86 // Must fit in the BitField PropertyDetails::KindField.
87 enum class PropertyKind { kData = 0, kAccessor = 1 };
88
89 // Order of modes is significant.
90 // Must fit in the BitField PropertyDetails::LocationField.
91 enum class PropertyLocation { kField = 0, kDescriptor = 1 };
92
93 // Order of modes is significant.
94 // Must fit in the BitField PropertyDetails::ConstnessField.
95 enum class PropertyConstness { kMutable = 0, kConst = 1 };
96
97 class Representation {
98 public:
99 enum Kind {
100 kNone,
101 kSmi,
102 kDouble,
103 kHeapObject,
104 kTagged,
105 // This representation is used for WasmObject fields and basically means
106 // that the actual field type information must be taken from the Wasm RTT
107 // associated with the map.
108 kWasmValue,
109 kNumRepresentations
110 };
111
Representation()112 constexpr Representation() : kind_(kNone) {}
113
None()114 static constexpr Representation None() { return Representation(kNone); }
Tagged()115 static constexpr Representation Tagged() { return Representation(kTagged); }
Smi()116 static constexpr Representation Smi() { return Representation(kSmi); }
Double()117 static constexpr Representation Double() { return Representation(kDouble); }
HeapObject()118 static constexpr Representation HeapObject() {
119 return Representation(kHeapObject);
120 }
WasmValue()121 static constexpr Representation WasmValue() {
122 return Representation(kWasmValue);
123 }
124
FromKind(Kind kind)125 static constexpr Representation FromKind(Kind kind) {
126 return Representation(kind);
127 }
128
Equals(const Representation & other)129 bool Equals(const Representation& other) const {
130 return kind_ == other.kind_;
131 }
132
IsCompatibleForLoad(const Representation & other)133 bool IsCompatibleForLoad(const Representation& other) const {
134 return IsDouble() == other.IsDouble();
135 }
136
IsCompatibleForStore(const Representation & other)137 bool IsCompatibleForStore(const Representation& other) const {
138 return Equals(other);
139 }
140
141 // Returns true if a change from this representation to a more general one
142 // might cause a map deprecation.
MightCauseMapDeprecation()143 bool MightCauseMapDeprecation() const {
144 // HeapObject to tagged representation change can be done in-place.
145 // Boxed double to tagged transition is always done in-place.
146 // Note that WasmValue is not supposed to be changed at all (the only
147 // representation it fits into is WasmValue), so for the sake of predicate
148 // correctness we treat it as in-place "changeable".
149 if (IsTagged() || IsHeapObject() || IsDouble() || IsWasmValue()) {
150 return false;
151 }
152 // None to double and smi to double representation changes require
153 // deprecation, because doubles might require box allocation, see
154 // CanBeInPlaceChangedTo().
155 DCHECK(IsNone() || IsSmi());
156 return true;
157 }
158
CanBeInPlaceChangedTo(const Representation & other)159 bool CanBeInPlaceChangedTo(const Representation& other) const {
160 if (Equals(other)) return true;
161 if (IsWasmValue() || other.IsWasmValue()) return false;
162 // If it's just a representation generalization case (i.e. property kind and
163 // attributes stays unchanged) it's fine to transition from None to anything
164 // but double without any modification to the object, because the default
165 // uninitialized value for representation None can be overwritten by both
166 // smi and tagged values. Doubles, however, would require a box allocation.
167 if (IsNone()) return !other.IsDouble();
168 if (!other.IsTagged()) return false;
169 DCHECK(IsSmi() || IsDouble() || IsHeapObject());
170 return true;
171 }
172
173 // Return the most generic representation that this representation can be
174 // changed to in-place. If an in-place representation change is not allowed,
175 // then this will return the current representation.
MostGenericInPlaceChange()176 Representation MostGenericInPlaceChange() const {
177 if (IsWasmValue()) return Representation::WasmValue();
178 return Representation::Tagged();
179 }
180
is_more_general_than(const Representation & other)181 bool is_more_general_than(const Representation& other) const {
182 if (IsWasmValue()) return false;
183 if (IsHeapObject()) return other.IsNone();
184 return kind_ > other.kind_;
185 }
186
fits_into(const Representation & other)187 bool fits_into(const Representation& other) const {
188 return other.is_more_general_than(*this) || other.Equals(*this);
189 }
190
generalize(Representation other)191 Representation generalize(Representation other) {
192 if (other.fits_into(*this)) return *this;
193 if (other.is_more_general_than(*this)) return other;
194 return Representation::Tagged();
195 }
196
size()197 int size() const {
198 DCHECK(!IsNone());
199 if (IsDouble()) return kDoubleSize;
200 DCHECK(IsTagged() || IsSmi() || IsHeapObject());
201 return kTaggedSize;
202 }
203
kind()204 constexpr Kind kind() const { return static_cast<Kind>(kind_); }
IsNone()205 constexpr bool IsNone() const { return kind_ == kNone; }
IsWasmValue()206 constexpr bool IsWasmValue() const { return kind_ == kWasmValue; }
IsTagged()207 constexpr bool IsTagged() const { return kind_ == kTagged; }
IsSmi()208 constexpr bool IsSmi() const { return kind_ == kSmi; }
IsSmiOrTagged()209 constexpr bool IsSmiOrTagged() const { return IsSmi() || IsTagged(); }
IsDouble()210 constexpr bool IsDouble() const { return kind_ == kDouble; }
IsHeapObject()211 constexpr bool IsHeapObject() const { return kind_ == kHeapObject; }
212
Mnemonic()213 const char* Mnemonic() const {
214 switch (kind_) {
215 case kNone:
216 return "v";
217 case kTagged:
218 return "t";
219 case kSmi:
220 return "s";
221 case kDouble:
222 return "d";
223 case kHeapObject:
224 return "h";
225 case kWasmValue:
226 return "w";
227 }
228 UNREACHABLE();
229 }
230
231 private:
Representation(Kind k)232 explicit constexpr Representation(Kind k) : kind_(k) {}
233
234 // Make sure kind fits in int8.
235 STATIC_ASSERT(kNumRepresentations <= (1 << kBitsPerByte));
236
237 int8_t kind_;
238 };
239
240 static const int kDescriptorIndexBitCount = 10;
241 static const int kFirstInobjectPropertyOffsetBitCount = 7;
242 // The maximum number of descriptors we want in a descriptor array. It should
243 // fit in a page and also the following should hold:
244 // kMaxNumberOfDescriptors + kFieldsAdded <= PropertyArray::kMaxLength.
245 static const int kMaxNumberOfDescriptors = (1 << kDescriptorIndexBitCount) - 4;
246 static const int kInvalidEnumCacheSentinel =
247 (1 << kDescriptorIndexBitCount) - 1;
248
249 // A PropertyCell's property details contains a cell type that is meaningful if
250 // the cell is still valid (does not hold the hole).
251 enum class PropertyCellType {
252 kMutable, // Cell will no longer be tracked as constant.
253 kUndefined, // The PREMONOMORPHIC of property cells.
254 kConstant, // Cell has been assigned only once.
255 kConstantType, // Cell has been assigned only one type.
256 // Temporary value indicating an ongoing property cell state transition. Only
257 // observable by a background thread.
258 kInTransition,
259 // Value for dictionaries not holding cells, must be 0:
260 kNoCell = kMutable,
261 };
262
263 // PropertyDetails captures type and attributes for a property.
264 // They are used both in property dictionaries and instance descriptors.
265 class PropertyDetails {
266 public:
267 // Property details for global dictionary properties.
268 constexpr PropertyDetails(PropertyKind kind, PropertyAttributes attributes,
269 PropertyCellType cell_type,
270 int dictionary_index = 0)
271 : value_(KindField::encode(kind) |
272 LocationField::encode(PropertyLocation::kField) |
273 AttributesField::encode(attributes) |
274 // We track PropertyCell constness via PropertyCellTypeField,
275 // so we set ConstnessField to kMutable to simplify DCHECKs
276 // related to non-global property constness tracking.
277 ConstnessField::encode(PropertyConstness::kMutable) |
278 DictionaryStorageField::encode(dictionary_index) |
279 PropertyCellTypeField::encode(cell_type)) {}
280
281 // Property details for dictionary mode properties/elements.
282 constexpr PropertyDetails(PropertyKind kind, PropertyAttributes attributes,
283 PropertyConstness constness,
284 int dictionary_index = 0)
285 : value_(KindField::encode(kind) |
286 LocationField::encode(PropertyLocation::kField) |
287 AttributesField::encode(attributes) |
288 ConstnessField::encode(constness) |
289 DictionaryStorageField::encode(dictionary_index) |
290 PropertyCellTypeField::encode(PropertyCellType::kNoCell)) {}
291
292 // Property details for fast mode properties.
293 constexpr PropertyDetails(PropertyKind kind, PropertyAttributes attributes,
294 PropertyLocation location,
295 PropertyConstness constness,
296 Representation representation, int field_index = 0)
297 : value_(
298 KindField::encode(kind) | AttributesField::encode(attributes) |
299 LocationField::encode(location) |
300 ConstnessField::encode(constness) |
301 RepresentationField::encode(EncodeRepresentation(representation)) |
302 FieldIndexField::encode(field_index)) {}
303
304 static constexpr PropertyDetails Empty(
305 PropertyCellType cell_type = PropertyCellType::kNoCell) {
306 return PropertyDetails(PropertyKind::kData, NONE, cell_type);
307 }
308
309 bool operator==(PropertyDetails const& other) {
310 return value_ == other.value_;
311 }
312
313 bool operator!=(PropertyDetails const& other) {
314 return value_ != other.value_;
315 }
316
pointer()317 int pointer() const { return DescriptorPointer::decode(value_); }
318
set_pointer(int i)319 PropertyDetails set_pointer(int i) const {
320 return PropertyDetails(value_, i);
321 }
322
set_cell_type(PropertyCellType type)323 PropertyDetails set_cell_type(PropertyCellType type) const {
324 PropertyDetails details = *this;
325 details.value_ = PropertyCellTypeField::update(details.value_, type);
326 return details;
327 }
328
set_index(int index)329 PropertyDetails set_index(int index) const {
330 PropertyDetails details = *this;
331 details.value_ = DictionaryStorageField::update(details.value_, index);
332 return details;
333 }
334
CopyWithRepresentation(Representation representation)335 PropertyDetails CopyWithRepresentation(Representation representation) const {
336 return PropertyDetails(value_, representation);
337 }
CopyWithConstness(PropertyConstness constness)338 PropertyDetails CopyWithConstness(PropertyConstness constness) const {
339 return PropertyDetails(value_, constness);
340 }
CopyAddAttributes(PropertyAttributes new_attributes)341 PropertyDetails CopyAddAttributes(PropertyAttributes new_attributes) const {
342 new_attributes =
343 static_cast<PropertyAttributes>(attributes() | new_attributes);
344 return PropertyDetails(value_, new_attributes);
345 }
346
347 // Conversion for storing details as Object.
348 explicit inline PropertyDetails(Smi smi);
349 inline Smi AsSmi() const;
350
EncodeRepresentation(Representation representation)351 static constexpr uint8_t EncodeRepresentation(Representation representation) {
352 return representation.kind();
353 }
354
DecodeRepresentation(uint32_t bits)355 static Representation DecodeRepresentation(uint32_t bits) {
356 return Representation::FromKind(static_cast<Representation::Kind>(bits));
357 }
358
kind()359 PropertyKind kind() const { return KindField::decode(value_); }
location()360 PropertyLocation location() const { return LocationField::decode(value_); }
constness()361 PropertyConstness constness() const { return ConstnessField::decode(value_); }
362
attributes()363 PropertyAttributes attributes() const {
364 return AttributesField::decode(value_);
365 }
366
HasKindAndAttributes(PropertyKind kind,PropertyAttributes attributes)367 bool HasKindAndAttributes(PropertyKind kind, PropertyAttributes attributes) {
368 return (value_ & (KindField::kMask | AttributesField::kMask)) ==
369 (KindField::encode(kind) | AttributesField::encode(attributes));
370 }
371
dictionary_index()372 int dictionary_index() const {
373 return DictionaryStorageField::decode(value_);
374 }
375
representation()376 Representation representation() const {
377 return DecodeRepresentation(RepresentationField::decode(value_));
378 }
379
field_index()380 int field_index() const { return FieldIndexField::decode(value_); }
381
382 inline int field_width_in_words() const;
383
IsValidIndex(int index)384 static bool IsValidIndex(int index) {
385 return DictionaryStorageField::is_valid(index);
386 }
387
IsReadOnly()388 bool IsReadOnly() const { return (attributes() & READ_ONLY) != 0; }
IsConfigurable()389 bool IsConfigurable() const { return (attributes() & DONT_DELETE) == 0; }
IsDontEnum()390 bool IsDontEnum() const { return (attributes() & DONT_ENUM) != 0; }
IsEnumerable()391 bool IsEnumerable() const { return !IsDontEnum(); }
cell_type()392 PropertyCellType cell_type() const {
393 return PropertyCellTypeField::decode(value_);
394 }
395
396 bool operator==(const PropertyDetails& b) const { return value_ == b.value_; }
397
398 // Bit fields in value_ (type, shift, size). Must be public so the
399 // constants can be embedded in generated code.
400 using KindField = base::BitField<PropertyKind, 0, 1>;
401 using ConstnessField = KindField::Next<PropertyConstness, 1>;
402 using AttributesField = ConstnessField::Next<PropertyAttributes, 3>;
403 static const int kAttributesReadOnlyMask =
404 (READ_ONLY << AttributesField::kShift);
405 static const int kAttributesDontDeleteMask =
406 (DONT_DELETE << AttributesField::kShift);
407 static const int kAttributesDontEnumMask =
408 (DONT_ENUM << AttributesField::kShift);
409
410 // Bit fields for normalized/dictionary mode objects.
411 using PropertyCellTypeField = AttributesField::Next<PropertyCellType, 3>;
412 using DictionaryStorageField = PropertyCellTypeField::Next<uint32_t, 23>;
413
414 // Bit fields for fast objects.
415 using LocationField = AttributesField::Next<PropertyLocation, 1>;
416 using RepresentationField = LocationField::Next<uint32_t, 3>;
417 using DescriptorPointer =
418 RepresentationField::Next<uint32_t, kDescriptorIndexBitCount>;
419 using FieldIndexField =
420 DescriptorPointer::Next<uint32_t, kDescriptorIndexBitCount>;
421
422 // All bits for both fast and slow objects must fit in a smi.
423 STATIC_ASSERT(DictionaryStorageField::kLastUsedBit < 31);
424 STATIC_ASSERT(FieldIndexField::kLastUsedBit < 31);
425
426 // DictionaryStorageField must be the last field, so that overflowing it
427 // doesn't overwrite other fields.
428 STATIC_ASSERT(DictionaryStorageField::kLastUsedBit == 30);
429
430 // All bits for non-global dictionary mode objects except enumeration index
431 // must fit in a byte.
432 STATIC_ASSERT(KindField::kLastUsedBit < 8);
433 STATIC_ASSERT(ConstnessField::kLastUsedBit < 8);
434 STATIC_ASSERT(AttributesField::kLastUsedBit < 8);
435
436 static const int kInitialIndex = 1;
437
438 static constexpr PropertyConstness kConstIfDictConstnessTracking =
439 V8_DICT_PROPERTY_CONST_TRACKING_BOOL ? PropertyConstness::kConst
440 : PropertyConstness::kMutable;
441
442 #ifdef OBJECT_PRINT
443 // For our gdb macros, we should perhaps change these in the future.
444 void Print(bool dictionary_mode);
445 #endif
446
447 enum PrintMode {
448 kPrintAttributes = 1 << 0,
449 kPrintFieldIndex = 1 << 1,
450 kPrintRepresentation = 1 << 2,
451 kPrintPointer = 1 << 3,
452
453 kForProperties = kPrintFieldIndex,
454 kForTransitions = kPrintAttributes,
455 kPrintFull = -1,
456 };
457 void PrintAsSlowTo(std::ostream& out, bool print_dict_index);
458 void PrintAsFastTo(std::ostream& out, PrintMode mode = kPrintFull);
459
460 // Encodes those property details for non-global dictionary properties
461 // with an enumeration index of 0 as a single byte.
ToByte()462 uint8_t ToByte() {
463 // We only care about the value of KindField, ConstnessField, and
464 // AttributesField. We've statically asserted earlier that these fields fit
465 // into a byte together.
466
467 DCHECK_EQ(PropertyLocation::kField, location());
468 STATIC_ASSERT(static_cast<int>(PropertyLocation::kField) == 0);
469
470 DCHECK_EQ(PropertyCellType::kNoCell, cell_type());
471 STATIC_ASSERT(static_cast<int>(PropertyCellType::kNoCell) == 0);
472
473 // Only to be used when the enum index isn't actually maintained
474 // by the PropertyDetails:
475 DCHECK_EQ(0, dictionary_index());
476
477 return value_;
478 }
479
480 // Only to be used for bytes obtained by ToByte. In particular, only used for
481 // non-global dictionary properties.
FromByte(uint8_t encoded_details)482 static PropertyDetails FromByte(uint8_t encoded_details) {
483 // The 0-extension to 32bit sets PropertyLocation to kField,
484 // PropertyCellType to kNoCell, and enumeration index to 0, as intended.
485 // Everything else is obtained from |encoded_details|.
486 PropertyDetails details(encoded_details);
487 DCHECK_EQ(PropertyLocation::kField, details.location());
488 DCHECK_EQ(PropertyCellType::kNoCell, details.cell_type());
489 DCHECK_EQ(0, details.dictionary_index());
490 return details;
491 }
492
493 private:
PropertyDetails(int value,int pointer)494 PropertyDetails(int value, int pointer) {
495 value_ = DescriptorPointer::update(value, pointer);
496 }
PropertyDetails(int value,Representation representation)497 PropertyDetails(int value, Representation representation) {
498 value_ = RepresentationField::update(value,
499 EncodeRepresentation(representation));
500 }
PropertyDetails(int value,PropertyConstness constness)501 PropertyDetails(int value, PropertyConstness constness) {
502 value_ = ConstnessField::update(value, constness);
503 }
PropertyDetails(int value,PropertyAttributes attributes)504 PropertyDetails(int value, PropertyAttributes attributes) {
505 value_ = AttributesField::update(value, attributes);
506 }
507
PropertyDetails(uint32_t value)508 explicit PropertyDetails(uint32_t value) : value_{value} {}
509
510 uint32_t value_;
511 };
512
513 // kField location is more general than kDescriptor, kDescriptor generalizes
514 // only to itself.
IsGeneralizableTo(PropertyLocation a,PropertyLocation b)515 inline bool IsGeneralizableTo(PropertyLocation a, PropertyLocation b) {
516 return b == PropertyLocation::kField || a == PropertyLocation::kDescriptor;
517 }
518
519 // PropertyConstness::kMutable constness is more general than
520 // VariableMode::kConst, VariableMode::kConst generalizes only to itself.
IsGeneralizableTo(PropertyConstness a,PropertyConstness b)521 inline bool IsGeneralizableTo(PropertyConstness a, PropertyConstness b) {
522 return b == PropertyConstness::kMutable || a == PropertyConstness::kConst;
523 }
524
GeneralizeConstness(PropertyConstness a,PropertyConstness b)525 inline PropertyConstness GeneralizeConstness(PropertyConstness a,
526 PropertyConstness b) {
527 return a == PropertyConstness::kMutable ? PropertyConstness::kMutable : b;
528 }
529
530 V8_EXPORT_PRIVATE std::ostream& operator<<(
531 std::ostream& os, const Representation& representation);
532 V8_EXPORT_PRIVATE std::ostream& operator<<(
533 std::ostream& os, const PropertyAttributes& attributes);
534 V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
535 PropertyConstness constness);
536 V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
537 PropertyCellType type);
538 } // namespace internal
539 } // namespace v8
540
541 #endif // V8_OBJECTS_PROPERTY_DETAILS_H_
542