1 // Copyright 2018 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_INSTANCE_TYPE_H_
6 #define V8_OBJECTS_INSTANCE_TYPE_H_
7 
8 #include "src/objects/elements-kind.h"
9 #include "src/objects/objects-definitions.h"
10 
11 // Has to be the last include (doesn't have include guards):
12 #include "src/objects/object-macros.h"
13 
14 #include "torque-generated/instance-types-tq.h"
15 
16 namespace v8 {
17 namespace internal {
18 
19 // We use the full 16 bits of the instance_type field to encode heap object
20 // instance types. All the high-order bits (bits 6-15) are cleared if the object
21 // is a string, and contain set bits if it is not a string.
22 const uint32_t kIsNotStringMask = ~((1 << 6) - 1);
23 const uint32_t kStringTag = 0x0;
24 
25 // For strings, bits 0-2 indicate the representation of the string. In
26 // particular, bit 0 indicates whether the string is direct or indirect.
27 const uint32_t kStringRepresentationMask = (1 << 3) - 1;
28 enum StringRepresentationTag {
29   kSeqStringTag = 0x0,
30   kConsStringTag = 0x1,
31   kExternalStringTag = 0x2,
32   kSlicedStringTag = 0x3,
33   kThinStringTag = 0x5
34 };
35 const uint32_t kIsIndirectStringMask = 1 << 0;
36 const uint32_t kIsIndirectStringTag = 1 << 0;
37 // NOLINTNEXTLINE(runtime/references) (false positive)
38 STATIC_ASSERT((kSeqStringTag & kIsIndirectStringMask) == 0);
39 // NOLINTNEXTLINE(runtime/references) (false positive)
40 STATIC_ASSERT((kExternalStringTag & kIsIndirectStringMask) == 0);
41 // NOLINTNEXTLINE(runtime/references) (false positive)
42 STATIC_ASSERT((kConsStringTag & kIsIndirectStringMask) == kIsIndirectStringTag);
43 // NOLINTNEXTLINE(runtime/references) (false positive)
44 STATIC_ASSERT((kSlicedStringTag & kIsIndirectStringMask) ==
45               kIsIndirectStringTag);
46 // NOLINTNEXTLINE(runtime/references) (false positive)
47 STATIC_ASSERT((kThinStringTag & kIsIndirectStringMask) == kIsIndirectStringTag);
48 
49 // For strings, bit 3 indicates whether the string consists of two-byte
50 // characters or one-byte characters.
51 const uint32_t kStringEncodingMask = 1 << 3;
52 const uint32_t kTwoByteStringTag = 0;
53 const uint32_t kOneByteStringTag = 1 << 3;
54 
55 // For strings, bit 4 indicates whether the data pointer of an external string
56 // is cached. Note that the string representation is expected to be
57 // kExternalStringTag.
58 const uint32_t kUncachedExternalStringMask = 1 << 4;
59 const uint32_t kUncachedExternalStringTag = 1 << 4;
60 
61 // For strings, bit 5 indicates that the string is internalized (if not set) or
62 // isn't (if set).
63 const uint32_t kIsNotInternalizedMask = 1 << 5;
64 const uint32_t kNotInternalizedTag = 1 << 5;
65 const uint32_t kInternalizedTag = 0;
66 
67 // A ConsString with an empty string as the right side is a candidate
68 // for being shortcut by the garbage collector. We don't allocate any
69 // non-flat internalized strings, so we do not shortcut them thereby
70 // avoiding turning internalized strings into strings. The bit-masks
71 // below contain the internalized bit as additional safety.
72 // See heap.cc, mark-compact.cc and objects-visiting.cc.
73 const uint32_t kShortcutTypeMask =
74     kIsNotStringMask | kIsNotInternalizedMask | kStringRepresentationMask;
75 const uint32_t kShortcutTypeTag = kConsStringTag | kNotInternalizedTag;
76 
IsShortcutCandidate(int type)77 static inline bool IsShortcutCandidate(int type) {
78   return ((type & kShortcutTypeMask) == kShortcutTypeTag);
79 }
80 
81 enum InstanceType : uint16_t {
82   // String types.
83   INTERNALIZED_STRING_TYPE =
84       kTwoByteStringTag | kSeqStringTag | kInternalizedTag,
85   ONE_BYTE_INTERNALIZED_STRING_TYPE =
86       kOneByteStringTag | kSeqStringTag | kInternalizedTag,
87   EXTERNAL_INTERNALIZED_STRING_TYPE =
88       kTwoByteStringTag | kExternalStringTag | kInternalizedTag,
89   EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE =
90       kOneByteStringTag | kExternalStringTag | kInternalizedTag,
91   UNCACHED_EXTERNAL_INTERNALIZED_STRING_TYPE =
92       EXTERNAL_INTERNALIZED_STRING_TYPE | kUncachedExternalStringTag |
93       kInternalizedTag,
94   UNCACHED_EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE =
95       EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE | kUncachedExternalStringTag |
96       kInternalizedTag,
97   STRING_TYPE = INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
98   ONE_BYTE_STRING_TYPE =
99       ONE_BYTE_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
100   CONS_STRING_TYPE = kTwoByteStringTag | kConsStringTag | kNotInternalizedTag,
101   CONS_ONE_BYTE_STRING_TYPE =
102       kOneByteStringTag | kConsStringTag | kNotInternalizedTag,
103   SLICED_STRING_TYPE =
104       kTwoByteStringTag | kSlicedStringTag | kNotInternalizedTag,
105   SLICED_ONE_BYTE_STRING_TYPE =
106       kOneByteStringTag | kSlicedStringTag | kNotInternalizedTag,
107   EXTERNAL_STRING_TYPE =
108       EXTERNAL_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
109   EXTERNAL_ONE_BYTE_STRING_TYPE =
110       EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
111   UNCACHED_EXTERNAL_STRING_TYPE =
112       UNCACHED_EXTERNAL_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
113   UNCACHED_EXTERNAL_ONE_BYTE_STRING_TYPE =
114       UNCACHED_EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
115   THIN_STRING_TYPE = kTwoByteStringTag | kThinStringTag | kNotInternalizedTag,
116   THIN_ONE_BYTE_STRING_TYPE =
117       kOneByteStringTag | kThinStringTag | kNotInternalizedTag,
118 
119 // Most instance types are defined in Torque, with the exception of the string
120 // types above. They are ordered by inheritance hierarchy so that we can easily
121 // use range checks to determine whether an object is an instance of a subclass
122 // of any type. There are a few more constraints specified in the Torque type
123 // definitions:
124 // - Some instance types are exposed in v8.h, so they are locked to specific
125 //   values to not unnecessarily change the ABI.
126 // - JSSpecialObject and JSCustomElementsObject are aligned with the beginning
127 //   of the JSObject range, so that we can use a larger range check from
128 //   FIRST_JS_RECEIVER_TYPE to the end of those ranges and include JSProxy too.
129 // - JSFunction is last, meaning we can use a single inequality check to
130 //   determine whether an instance type is within the range for any class in the
131 //   inheritance hierarchy of JSFunction. This includes commonly-checked classes
132 //   JSObject and JSReceiver.
133 #define MAKE_TORQUE_INSTANCE_TYPE(TYPE, value) TYPE = value,
134   TORQUE_ASSIGNED_INSTANCE_TYPES(MAKE_TORQUE_INSTANCE_TYPE)
135 #undef MAKE_TORQUE_INSTANCE_TYPE
136 
137   // Pseudo-types
138   FIRST_UNIQUE_NAME_TYPE = INTERNALIZED_STRING_TYPE,
139   LAST_UNIQUE_NAME_TYPE = SYMBOL_TYPE,
140   FIRST_NONSTRING_TYPE = SYMBOL_TYPE,
141   // Boundary for testing JSReceivers that need special property lookup handling
142   LAST_SPECIAL_RECEIVER_TYPE = LAST_JS_SPECIAL_OBJECT_TYPE,
143   // Boundary case for testing JSReceivers that may have elements while having
144   // an empty fixed array as elements backing store. This is true for string
145   // wrappers.
146   LAST_CUSTOM_ELEMENTS_RECEIVER = LAST_JS_CUSTOM_ELEMENTS_OBJECT_TYPE,
147 
148   // Convenient names for things where the generated name is awkward:
149   FIRST_TYPE = FIRST_HEAP_OBJECT_TYPE,
150   LAST_TYPE = LAST_HEAP_OBJECT_TYPE,
151   FIRST_FUNCTION_TYPE = FIRST_JS_FUNCTION_OR_BOUND_FUNCTION_TYPE,
152   LAST_FUNCTION_TYPE = LAST_JS_FUNCTION_OR_BOUND_FUNCTION_TYPE,
153   BIGINT_TYPE = BIG_INT_BASE_TYPE,
154 };
155 
156 // This constant is defined outside of the InstanceType enum because the
157 // string instance types are sparce and there's no such a string instance type.
158 // But it's still useful for range checks to have such a value.
159 constexpr InstanceType LAST_STRING_TYPE =
160     static_cast<InstanceType>(FIRST_NONSTRING_TYPE - 1);
161 
162 // NOLINTNEXTLINE(runtime/references) (false positive)
163 STATIC_ASSERT((FIRST_NONSTRING_TYPE & kIsNotStringMask) != kStringTag);
164 STATIC_ASSERT(JS_OBJECT_TYPE == Internals::kJSObjectType);
165 STATIC_ASSERT(JS_API_OBJECT_TYPE == Internals::kJSApiObjectType);
166 STATIC_ASSERT(JS_SPECIAL_API_OBJECT_TYPE == Internals::kJSSpecialApiObjectType);
167 STATIC_ASSERT(FIRST_NONSTRING_TYPE == Internals::kFirstNonstringType);
168 STATIC_ASSERT(ODDBALL_TYPE == Internals::kOddballType);
169 STATIC_ASSERT(FOREIGN_TYPE == Internals::kForeignType);
170 
171 // Verify that string types are all less than other types.
172 #define CHECK_STRING_RANGE(TYPE, ...) \
173   STATIC_ASSERT(TYPE < FIRST_NONSTRING_TYPE);
174 STRING_TYPE_LIST(CHECK_STRING_RANGE)
175 #undef CHECK_STRING_RANGE
176 #define CHECK_NONSTRING_RANGE(TYPE) STATIC_ASSERT(TYPE >= FIRST_NONSTRING_TYPE);
177 TORQUE_ASSIGNED_INSTANCE_TYPE_LIST(CHECK_NONSTRING_RANGE)
178 #undef CHECK_NONSTRING_RANGE
179 
180 // Two ranges don't cleanly follow the inheritance hierarchy. Here we ensure
181 // that only expected types fall within these ranges.
182 // - From FIRST_JS_RECEIVER_TYPE to LAST_SPECIAL_RECEIVER_TYPE should correspond
183 //   to the union type JSProxy | JSSpecialObject.
184 // - From FIRST_JS_RECEIVER_TYPE to LAST_CUSTOM_ELEMENTS_RECEIVER should
185 //   correspond to the union type JSProxy | JSCustomElementsObject.
186 // Note in particular that these ranges include all subclasses of JSReceiver
187 // that are not also subclasses of JSObject (currently only JSProxy).
188 #define CHECK_INSTANCE_TYPE(TYPE)                                          \
189   STATIC_ASSERT((TYPE >= FIRST_JS_RECEIVER_TYPE &&                         \
190                  TYPE <= LAST_SPECIAL_RECEIVER_TYPE) ==                    \
191                 (TYPE == JS_PROXY_TYPE || TYPE == JS_GLOBAL_OBJECT_TYPE || \
192                  TYPE == JS_GLOBAL_PROXY_TYPE ||                           \
193                  TYPE == JS_MODULE_NAMESPACE_TYPE ||                       \
194                  TYPE == JS_SPECIAL_API_OBJECT_TYPE));                     \
195   STATIC_ASSERT((TYPE >= FIRST_JS_RECEIVER_TYPE &&                         \
196                  TYPE <= LAST_CUSTOM_ELEMENTS_RECEIVER) ==                 \
197                 (TYPE == JS_PROXY_TYPE || TYPE == JS_GLOBAL_OBJECT_TYPE || \
198                  TYPE == JS_GLOBAL_PROXY_TYPE ||                           \
199                  TYPE == JS_MODULE_NAMESPACE_TYPE ||                       \
200                  TYPE == JS_SPECIAL_API_OBJECT_TYPE ||                     \
201                  TYPE == JS_PRIMITIVE_WRAPPER_TYPE));
202 TORQUE_ASSIGNED_INSTANCE_TYPE_LIST(CHECK_INSTANCE_TYPE)
203 #undef CHECK_INSTANCE_TYPE
204 
205 // Make sure it doesn't matter whether we sign-extend or zero-extend these
206 // values, because Torque treats InstanceType as signed.
207 STATIC_ASSERT(LAST_TYPE < 1 << 15);
208 
209 V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
210                                            InstanceType instance_type);
211 
212 // List of object types that have a single unique instance type.
213 #define INSTANCE_TYPE_CHECKERS_SINGLE(V)           \
214   TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(V) \
215   TORQUE_INSTANCE_CHECKERS_SINGLE_ONLY_DECLARED(V) \
216   V(BigInt, BIGINT_TYPE)                           \
217   V(FixedArrayExact, FIXED_ARRAY_TYPE)
218 
219 #define INSTANCE_TYPE_CHECKERS_RANGE(V)           \
220   TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(V) \
221   TORQUE_INSTANCE_CHECKERS_RANGE_ONLY_DECLARED(V)
222 
223 #define INSTANCE_TYPE_CHECKERS_CUSTOM(V) \
224   V(ExternalString)                      \
225   V(InternalizedString)
226 
227 #define INSTANCE_TYPE_CHECKERS(V)  \
228   INSTANCE_TYPE_CHECKERS_SINGLE(V) \
229   INSTANCE_TYPE_CHECKERS_RANGE(V)  \
230   INSTANCE_TYPE_CHECKERS_CUSTOM(V)
231 
232 namespace InstanceTypeChecker {
233 #define IS_TYPE_FUNCTION_DECL(Type, ...) \
234   V8_INLINE bool Is##Type(InstanceType instance_type);
235 
236 INSTANCE_TYPE_CHECKERS(IS_TYPE_FUNCTION_DECL)
237 
238 #define TYPED_ARRAY_IS_TYPE_FUNCTION_DECL(Type, ...) \
239   IS_TYPE_FUNCTION_DECL(Fixed##Type##Array)
240 TYPED_ARRAYS(TYPED_ARRAY_IS_TYPE_FUNCTION_DECL)
241 #undef TYPED_ARRAY_IS_TYPE_FUNCTION_DECL
242 
243 #undef IS_TYPE_FUNCTION_DECL
244 }  // namespace InstanceTypeChecker
245 
246 }  // namespace internal
247 }  // namespace v8
248 
249 #include "src/objects/object-macros-undef.h"
250 
251 #endif  // V8_OBJECTS_INSTANCE_TYPE_H_
252