1 | // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #ifndef RUNTIME_VM_TAGGED_POINTER_H_ |
6 | #define RUNTIME_VM_TAGGED_POINTER_H_ |
7 | |
8 | #include "platform/assert.h" |
9 | #include "platform/utils.h" |
10 | #include "vm/class_id.h" |
11 | #include "vm/pointer_tagging.h" |
12 | |
13 | namespace dart { |
14 | |
15 | class IsolateGroup; |
16 | class ObjectLayout; |
17 | |
18 | class ObjectPtr { |
19 | public: |
20 | ObjectPtr* operator->() { return this; } |
21 | const ObjectPtr* operator->() const { return this; } |
22 | ObjectLayout* ptr() const { |
23 | return reinterpret_cast<ObjectLayout*>(UntaggedPointer()); |
24 | } |
25 | |
26 | bool IsWellFormed() const { |
27 | uword value = tagged_pointer_; |
28 | return (value & kSmiTagMask) == 0 || |
29 | Utils::IsAligned(value - kHeapObjectTag, kWordSize); |
30 | } |
31 | bool IsHeapObject() const { |
32 | ASSERT(IsWellFormed()); |
33 | uword value = tagged_pointer_; |
34 | return (value & kSmiTagMask) == kHeapObjectTag; |
35 | } |
36 | // Assumes this is a heap object. |
37 | bool IsNewObject() const { |
38 | ASSERT(IsHeapObject()); |
39 | uword addr = tagged_pointer_; |
40 | return (addr & kNewObjectAlignmentOffset) == kNewObjectAlignmentOffset; |
41 | } |
42 | bool IsNewObjectMayBeSmi() const { |
43 | static const uword kNewObjectBits = |
44 | (kNewObjectAlignmentOffset | kHeapObjectTag); |
45 | const uword addr = tagged_pointer_; |
46 | return (addr & kObjectAlignmentMask) == kNewObjectBits; |
47 | } |
48 | // Assumes this is a heap object. |
49 | bool IsOldObject() const { |
50 | ASSERT(IsHeapObject()); |
51 | uword addr = tagged_pointer_; |
52 | return (addr & kNewObjectAlignmentOffset) == kOldObjectAlignmentOffset; |
53 | } |
54 | |
55 | // Like !IsHeapObject() || IsOldObject(), but compiles to a single branch. |
56 | bool IsSmiOrOldObject() const { |
57 | ASSERT(IsWellFormed()); |
58 | static const uword kNewObjectBits = |
59 | (kNewObjectAlignmentOffset | kHeapObjectTag); |
60 | const uword addr = tagged_pointer_; |
61 | return (addr & kObjectAlignmentMask) != kNewObjectBits; |
62 | } |
63 | |
64 | // Like !IsHeapObject() || IsNewObject(), but compiles to a single branch. |
65 | bool IsSmiOrNewObject() const { |
66 | ASSERT(IsWellFormed()); |
67 | static const uword kOldObjectBits = |
68 | (kOldObjectAlignmentOffset | kHeapObjectTag); |
69 | const uword addr = tagged_pointer_; |
70 | return (addr & kObjectAlignmentMask) != kOldObjectBits; |
71 | } |
72 | |
73 | #define DEFINE_IS_CID(clazz) \ |
74 | bool Is##clazz() const { return ((GetClassId() == k##clazz##Cid)); } |
75 | CLASS_LIST(DEFINE_IS_CID) |
76 | #undef DEFINE_IS_CID |
77 | |
78 | #define DEFINE_IS_CID(clazz) \ |
79 | bool IsTypedData##clazz() const { \ |
80 | return ((GetClassId() == kTypedData##clazz##Cid)); \ |
81 | } \ |
82 | bool IsTypedDataView##clazz() const { \ |
83 | return ((GetClassId() == kTypedData##clazz##ViewCid)); \ |
84 | } \ |
85 | bool IsExternalTypedData##clazz() const { \ |
86 | return ((GetClassId() == kExternalTypedData##clazz##Cid)); \ |
87 | } |
88 | CLASS_LIST_TYPED_DATA(DEFINE_IS_CID) |
89 | #undef DEFINE_IS_CID |
90 | |
91 | #define DEFINE_IS_CID(clazz) \ |
92 | bool IsFfi##clazz() const { return ((GetClassId() == kFfi##clazz##Cid)); } |
93 | CLASS_LIST_FFI(DEFINE_IS_CID) |
94 | #undef DEFINE_IS_CID |
95 | |
96 | bool IsStringInstance() const { return IsStringClassId(GetClassId()); } |
97 | bool IsRawNull() const { return GetClassId() == kNullCid; } |
98 | bool IsDartInstance() const { |
99 | return (!IsHeapObject() || (GetClassId() >= kInstanceCid)); |
100 | } |
101 | bool IsFreeListElement() const { |
102 | return ((GetClassId() == kFreeListElement)); |
103 | } |
104 | bool IsForwardingCorpse() const { |
105 | return ((GetClassId() == kForwardingCorpse)); |
106 | } |
107 | bool IsPseudoObject() const { |
108 | return IsFreeListElement() || IsForwardingCorpse(); |
109 | } |
110 | |
111 | intptr_t GetClassId() const; |
112 | intptr_t GetClassIdMayBeSmi() const { |
113 | return IsHeapObject() ? GetClassId() : static_cast<intptr_t>(kSmiCid); |
114 | } |
115 | |
116 | void Validate(IsolateGroup* isolate_group) const; |
117 | |
118 | bool operator==(const ObjectPtr& other) { |
119 | return tagged_pointer_ == other.tagged_pointer_; |
120 | } |
121 | bool operator!=(const ObjectPtr& other) { |
122 | return tagged_pointer_ != other.tagged_pointer_; |
123 | } |
124 | constexpr bool operator==(const ObjectPtr& other) const { |
125 | return tagged_pointer_ == other.tagged_pointer_; |
126 | } |
127 | constexpr bool operator!=(const ObjectPtr& other) const { |
128 | return tagged_pointer_ != other.tagged_pointer_; |
129 | } |
130 | bool operator==(const nullptr_t& other) { return tagged_pointer_ == 0; } |
131 | bool operator!=(const nullptr_t& other) { return tagged_pointer_ != 0; } |
132 | constexpr bool operator==(const nullptr_t& other) const { |
133 | return tagged_pointer_ == 0; |
134 | } |
135 | constexpr bool operator!=(const nullptr_t& other) const { |
136 | return tagged_pointer_ != 0; |
137 | } |
138 | |
139 | // Use explicit null comparisons instead. |
140 | operator bool() const = delete; |
141 | |
142 | // The underlying types of int32_t/int64_t and intptr_t are sometimes |
143 | // different and sometimes the same, depending on the platform. With |
144 | // only a conversion operator for intptr_t, on 64-bit Mac a static_cast |
145 | // to int64_t fails because it tries conversion to bool (!) rather than |
146 | // intptr_t. So we exhaustive define all the valid conversions based on |
147 | // the underlying types. |
148 | #if INT_MAX == INTPTR_MAX |
149 | explicit operator int() const { // NOLINT |
150 | return static_cast<int>(tagged_pointer_); // NOLINT |
151 | } |
152 | #endif |
153 | #if LONG_MAX == INTPTR_MAX |
154 | explicit operator long() const { // NOLINT |
155 | return static_cast<long>(tagged_pointer_); // NOLINT |
156 | } |
157 | #endif |
158 | #if LLONG_MAX == INTPTR_MAX |
159 | explicit operator long long() const { // NOLINT |
160 | return static_cast<long long>(tagged_pointer_); // NOLINT |
161 | } |
162 | #endif |
163 | #if UINT_MAX == UINTPTR_MAX |
164 | explicit operator unsigned int() const { // NOLINT |
165 | return static_cast<unsigned int>(tagged_pointer_); // NOLINT |
166 | } |
167 | #endif |
168 | #if ULONG_MAX == UINTPTR_MAX |
169 | explicit operator unsigned long() const { // NOLINT |
170 | return static_cast<unsigned long>(tagged_pointer_); // NOLINT |
171 | } |
172 | #endif |
173 | #if ULLONG_MAX == UINTPTR_MAX |
174 | explicit operator unsigned long long() const { // NOLINT |
175 | return static_cast<unsigned long long>(tagged_pointer_); // NOLINT |
176 | } |
177 | #endif |
178 | |
179 | // Must be trivially copyable for std::atomic. |
180 | ObjectPtr& operator=(const ObjectPtr& other) = default; |
181 | constexpr ObjectPtr(const ObjectPtr& other) = default; |
182 | |
183 | ObjectPtr() : tagged_pointer_(0) {} |
184 | explicit constexpr ObjectPtr(uword tagged) : tagged_pointer_(tagged) {} |
185 | explicit constexpr ObjectPtr(intptr_t tagged) : tagged_pointer_(tagged) {} |
186 | constexpr ObjectPtr(nullptr_t) : tagged_pointer_(0) {} // NOLINT |
187 | explicit ObjectPtr(ObjectLayout* heap_object) |
188 | : tagged_pointer_(reinterpret_cast<uword>(heap_object) + kHeapObjectTag) { |
189 | } |
190 | |
191 | protected: |
192 | uword UntaggedPointer() const { |
193 | ASSERT(IsHeapObject()); |
194 | return tagged_pointer_ - kHeapObjectTag; |
195 | } |
196 | |
197 | uword tagged_pointer_; |
198 | }; |
199 | |
200 | // Needed by the printing in the EXPECT macros. |
201 | #if defined(DEBUG) || defined(TESTING) |
202 | inline std::ostream& operator<<(std::ostream& os, const ObjectPtr& obj) { |
203 | os << reinterpret_cast<void*>(static_cast<uword>(obj)); |
204 | return os; |
205 | } |
206 | #endif |
207 | |
208 | #define DEFINE_TAGGED_POINTER(klass, base) \ |
209 | class klass##Layout; \ |
210 | class klass##Ptr : public base##Ptr { \ |
211 | public: \ |
212 | klass##Ptr* operator->() { return this; } \ |
213 | const klass##Ptr* operator->() const { return this; } \ |
214 | klass##Layout* ptr() { \ |
215 | return reinterpret_cast<klass##Layout*>(UntaggedPointer()); \ |
216 | } \ |
217 | /* TODO: Return const pointer */ \ |
218 | klass##Layout* ptr() const { \ |
219 | return reinterpret_cast<klass##Layout*>(UntaggedPointer()); \ |
220 | } \ |
221 | klass##Ptr& operator=(const klass##Ptr& other) = default; \ |
222 | constexpr klass##Ptr(const klass##Ptr& other) = default; \ |
223 | explicit constexpr klass##Ptr(const ObjectPtr& other) \ |
224 | : base##Ptr(other) {} \ |
225 | klass##Ptr() : base##Ptr() {} \ |
226 | explicit constexpr klass##Ptr(uword tagged) : base##Ptr(tagged) {} \ |
227 | explicit constexpr klass##Ptr(intptr_t tagged) : base##Ptr(tagged) {} \ |
228 | constexpr klass##Ptr(nullptr_t) : base##Ptr(nullptr) {} /* NOLINT */ \ |
229 | explicit klass##Ptr(const ObjectLayout* untagged) \ |
230 | : base##Ptr(reinterpret_cast<uword>(untagged) + kHeapObjectTag) {} \ |
231 | }; |
232 | |
233 | DEFINE_TAGGED_POINTER(Class, Object) |
234 | DEFINE_TAGGED_POINTER(PatchClass, Object) |
235 | DEFINE_TAGGED_POINTER(Function, Object) |
236 | DEFINE_TAGGED_POINTER(ClosureData, Object) |
237 | DEFINE_TAGGED_POINTER(SignatureData, Object) |
238 | DEFINE_TAGGED_POINTER(RedirectionData, Object) |
239 | DEFINE_TAGGED_POINTER(FfiTrampolineData, Object) |
240 | DEFINE_TAGGED_POINTER(Field, Object) |
241 | DEFINE_TAGGED_POINTER(Script, Object) |
242 | DEFINE_TAGGED_POINTER(Library, Object) |
243 | DEFINE_TAGGED_POINTER(Namespace, Object) |
244 | DEFINE_TAGGED_POINTER(KernelProgramInfo, Object) |
245 | DEFINE_TAGGED_POINTER(WeakSerializationReference, Object) |
246 | DEFINE_TAGGED_POINTER(Code, Object) |
247 | DEFINE_TAGGED_POINTER(Bytecode, Object) |
248 | DEFINE_TAGGED_POINTER(ObjectPool, Object) |
249 | DEFINE_TAGGED_POINTER(Instructions, Object) |
250 | DEFINE_TAGGED_POINTER(InstructionsSection, Object) |
251 | DEFINE_TAGGED_POINTER(PcDescriptors, Object) |
252 | DEFINE_TAGGED_POINTER(CodeSourceMap, Object) |
253 | DEFINE_TAGGED_POINTER(CompressedStackMaps, Object) |
254 | DEFINE_TAGGED_POINTER(LocalVarDescriptors, Object) |
255 | DEFINE_TAGGED_POINTER(ExceptionHandlers, Object) |
256 | DEFINE_TAGGED_POINTER(Context, Object) |
257 | DEFINE_TAGGED_POINTER(ContextScope, Object) |
258 | DEFINE_TAGGED_POINTER(ParameterTypeCheck, Object) |
259 | DEFINE_TAGGED_POINTER(SingleTargetCache, Object) |
260 | DEFINE_TAGGED_POINTER(UnlinkedCall, Object) |
261 | DEFINE_TAGGED_POINTER(MonomorphicSmiableCall, Object) |
262 | DEFINE_TAGGED_POINTER(CallSiteData, Object) |
263 | DEFINE_TAGGED_POINTER(ICData, CallSiteData) |
264 | DEFINE_TAGGED_POINTER(MegamorphicCache, CallSiteData) |
265 | DEFINE_TAGGED_POINTER(SubtypeTestCache, Object) |
266 | DEFINE_TAGGED_POINTER(LoadingUnit, Object) |
267 | DEFINE_TAGGED_POINTER(Error, Object) |
268 | DEFINE_TAGGED_POINTER(ApiError, Error) |
269 | DEFINE_TAGGED_POINTER(LanguageError, Error) |
270 | DEFINE_TAGGED_POINTER(UnhandledException, Error) |
271 | DEFINE_TAGGED_POINTER(UnwindError, Error) |
272 | DEFINE_TAGGED_POINTER(Instance, Object) |
273 | DEFINE_TAGGED_POINTER(LibraryPrefix, Instance) |
274 | DEFINE_TAGGED_POINTER(TypeArguments, Instance) |
275 | DEFINE_TAGGED_POINTER(AbstractType, Instance) |
276 | DEFINE_TAGGED_POINTER(Type, AbstractType) |
277 | DEFINE_TAGGED_POINTER(TypeRef, AbstractType) |
278 | DEFINE_TAGGED_POINTER(TypeParameter, AbstractType) |
279 | DEFINE_TAGGED_POINTER(Closure, Instance) |
280 | DEFINE_TAGGED_POINTER(Number, Instance) |
281 | DEFINE_TAGGED_POINTER(Integer, Number) |
282 | DEFINE_TAGGED_POINTER(Smi, Integer) |
283 | DEFINE_TAGGED_POINTER(Mint, Integer) |
284 | DEFINE_TAGGED_POINTER(Double, Number) |
285 | DEFINE_TAGGED_POINTER(String, Instance) |
286 | DEFINE_TAGGED_POINTER(OneByteString, String) |
287 | DEFINE_TAGGED_POINTER(TwoByteString, String) |
288 | DEFINE_TAGGED_POINTER(PointerBase, Instance) |
289 | DEFINE_TAGGED_POINTER(TypedDataBase, PointerBase) |
290 | DEFINE_TAGGED_POINTER(TypedData, TypedDataBase) |
291 | DEFINE_TAGGED_POINTER(TypedDataView, TypedDataBase) |
292 | DEFINE_TAGGED_POINTER(ExternalOneByteString, String) |
293 | DEFINE_TAGGED_POINTER(ExternalTwoByteString, String) |
294 | DEFINE_TAGGED_POINTER(Bool, Instance) |
295 | DEFINE_TAGGED_POINTER(Array, Instance) |
296 | DEFINE_TAGGED_POINTER(ImmutableArray, Array) |
297 | DEFINE_TAGGED_POINTER(GrowableObjectArray, Instance) |
298 | DEFINE_TAGGED_POINTER(LinkedHashMap, Instance) |
299 | DEFINE_TAGGED_POINTER(Float32x4, Instance) |
300 | DEFINE_TAGGED_POINTER(Int32x4, Instance) |
301 | DEFINE_TAGGED_POINTER(Float64x2, Instance) |
302 | DEFINE_TAGGED_POINTER(ExternalTypedData, TypedDataBase) |
303 | DEFINE_TAGGED_POINTER(Pointer, PointerBase) |
304 | DEFINE_TAGGED_POINTER(DynamicLibrary, Instance) |
305 | DEFINE_TAGGED_POINTER(Capability, Instance) |
306 | DEFINE_TAGGED_POINTER(SendPort, Instance) |
307 | DEFINE_TAGGED_POINTER(ReceivePort, Instance) |
308 | DEFINE_TAGGED_POINTER(TransferableTypedData, Instance) |
309 | DEFINE_TAGGED_POINTER(StackTrace, Instance) |
310 | DEFINE_TAGGED_POINTER(RegExp, Instance) |
311 | DEFINE_TAGGED_POINTER(WeakProperty, Instance) |
312 | DEFINE_TAGGED_POINTER(MirrorReference, Instance) |
313 | DEFINE_TAGGED_POINTER(UserTag, Instance) |
314 | DEFINE_TAGGED_POINTER(FutureOr, Instance) |
315 | #undef DEFINE_TAGGED_POINTER |
316 | |
317 | inline intptr_t RawSmiValue(const SmiPtr raw_value) { |
318 | const intptr_t value = static_cast<intptr_t>(raw_value); |
319 | ASSERT((value & kSmiTagMask) == kSmiTag); |
320 | return (value >> kSmiTagShift); |
321 | } |
322 | |
323 | } // namespace dart |
324 | |
325 | #endif // RUNTIME_VM_TAGGED_POINTER_H_ |
326 | |