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_SLOTS_H_ |
6 | #define V8_OBJECTS_SLOTS_H_ |
7 | |
8 | #include "src/globals.h" |
9 | #include "src/v8memory.h" |
10 | |
11 | namespace v8 { |
12 | namespace internal { |
13 | |
14 | class Object; |
15 | |
16 | template <typename Subclass, typename Data, |
17 | size_t SlotDataAlignment = sizeof(Data)> |
18 | class SlotBase { |
19 | public: |
20 | using TData = Data; |
21 | |
22 | static constexpr size_t kSlotDataSize = sizeof(Data); |
23 | static constexpr size_t kSlotDataAlignment = SlotDataAlignment; |
24 | |
25 | Subclass& operator++() { // Prefix increment. |
26 | ptr_ += kSlotDataSize; |
27 | return *static_cast<Subclass*>(this); |
28 | } |
29 | Subclass operator++(int) { // Postfix increment. |
30 | Subclass result = *static_cast<Subclass*>(this); |
31 | ptr_ += kSlotDataSize; |
32 | return result; |
33 | } |
34 | Subclass& operator--() { // Prefix decrement. |
35 | ptr_ -= kSlotDataSize; |
36 | return *static_cast<Subclass*>(this); |
37 | } |
38 | Subclass operator--(int) { // Postfix decrement. |
39 | Subclass result = *static_cast<Subclass*>(this); |
40 | ptr_ -= kSlotDataSize; |
41 | return result; |
42 | } |
43 | |
44 | bool operator<(const SlotBase& other) const { return ptr_ < other.ptr_; } |
45 | bool operator<=(const SlotBase& other) const { return ptr_ <= other.ptr_; } |
46 | bool operator>(const SlotBase& other) const { return ptr_ > other.ptr_; } |
47 | bool operator>=(const SlotBase& other) const { return ptr_ >= other.ptr_; } |
48 | bool operator==(const SlotBase& other) const { return ptr_ == other.ptr_; } |
49 | bool operator!=(const SlotBase& other) const { return ptr_ != other.ptr_; } |
50 | size_t operator-(const SlotBase& other) const { |
51 | DCHECK_GE(ptr_, other.ptr_); |
52 | return static_cast<size_t>((ptr_ - other.ptr_) / kSlotDataSize); |
53 | } |
54 | Subclass operator-(int i) const { return Subclass(ptr_ - i * kSlotDataSize); } |
55 | Subclass operator+(int i) const { return Subclass(ptr_ + i * kSlotDataSize); } |
56 | friend Subclass operator+(int i, const Subclass& slot) { |
57 | return Subclass(slot.ptr_ + i * kSlotDataSize); |
58 | } |
59 | Subclass& operator+=(int i) { |
60 | ptr_ += i * kSlotDataSize; |
61 | return *static_cast<Subclass*>(this); |
62 | } |
63 | Subclass operator-(int i) { return Subclass(ptr_ - i * kSlotDataSize); } |
64 | Subclass& operator-=(int i) { |
65 | ptr_ -= i * kSlotDataSize; |
66 | return *static_cast<Subclass*>(this); |
67 | } |
68 | |
69 | void* ToVoidPtr() const { return reinterpret_cast<void*>(address()); } |
70 | |
71 | Address address() const { return ptr_; } |
72 | // For symmetry with Handle. |
73 | TData* location() const { return reinterpret_cast<TData*>(ptr_); } |
74 | |
75 | protected: |
76 | explicit SlotBase(Address ptr) : ptr_(ptr) { |
77 | DCHECK(IsAligned(ptr, kSlotDataAlignment)); |
78 | } |
79 | |
80 | private: |
81 | // This field usually describes an on-heap address (a slot within an object), |
82 | // so its type should not be a pointer to another C++ wrapper class. |
83 | // Type safety is provided by well-defined conversion operations. |
84 | Address ptr_; |
85 | }; |
86 | |
87 | // An FullObjectSlot instance describes a kSystemPointerSize-sized field |
88 | // ("slot") holding a tagged pointer (smi or strong heap object). |
89 | // Its address() is the address of the slot. |
90 | // The slot's contents can be read and written using operator* and store(). |
91 | class FullObjectSlot : public SlotBase<FullObjectSlot, Address> { |
92 | public: |
93 | using TObject = Object; |
94 | using THeapObjectSlot = FullHeapObjectSlot; |
95 | |
96 | // Tagged value stored in this slot is guaranteed to never be a weak pointer. |
97 | static constexpr bool kCanBeWeak = false; |
98 | |
99 | FullObjectSlot() : SlotBase(kNullAddress) {} |
100 | explicit FullObjectSlot(Address ptr) : SlotBase(ptr) {} |
101 | explicit FullObjectSlot(const Address* ptr) |
102 | : SlotBase(reinterpret_cast<Address>(ptr)) {} |
103 | inline explicit FullObjectSlot(Object* object); |
104 | template <typename T> |
105 | explicit FullObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot) |
106 | : SlotBase(slot.address()) {} |
107 | |
108 | // Compares memory representation of a value stored in the slot with given |
109 | // raw value. |
110 | inline bool contains_value(Address raw_value) const; |
111 | |
112 | inline const Object operator*() const; |
113 | inline void store(Object value) const; |
114 | |
115 | inline Object Acquire_Load() const; |
116 | inline Object Relaxed_Load() const; |
117 | inline void Relaxed_Store(Object value) const; |
118 | inline void Release_Store(Object value) const; |
119 | inline Object Release_CompareAndSwap(Object old, Object target) const; |
120 | }; |
121 | |
122 | // A FullMaybeObjectSlot instance describes a kSystemPointerSize-sized field |
123 | // ("slot") holding a possibly-weak tagged pointer (think: MaybeObject). |
124 | // Its address() is the address of the slot. |
125 | // The slot's contents can be read and written using operator* and store(). |
126 | class FullMaybeObjectSlot |
127 | : public SlotBase<FullMaybeObjectSlot, Address, kSystemPointerSize> { |
128 | public: |
129 | using TObject = MaybeObject; |
130 | using THeapObjectSlot = FullHeapObjectSlot; |
131 | |
132 | // Tagged value stored in this slot can be a weak pointer. |
133 | static constexpr bool kCanBeWeak = true; |
134 | |
135 | FullMaybeObjectSlot() : SlotBase(kNullAddress) {} |
136 | explicit FullMaybeObjectSlot(Address ptr) : SlotBase(ptr) {} |
137 | explicit FullMaybeObjectSlot(Object* ptr) |
138 | : SlotBase(reinterpret_cast<Address>(ptr)) {} |
139 | explicit FullMaybeObjectSlot(MaybeObject* ptr) |
140 | : SlotBase(reinterpret_cast<Address>(ptr)) {} |
141 | template <typename T> |
142 | explicit FullMaybeObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot) |
143 | : SlotBase(slot.address()) {} |
144 | |
145 | inline const MaybeObject operator*() const; |
146 | inline void store(MaybeObject value) const; |
147 | |
148 | inline MaybeObject Relaxed_Load() const; |
149 | inline void Relaxed_Store(MaybeObject value) const; |
150 | inline void Release_CompareAndSwap(MaybeObject old, MaybeObject target) const; |
151 | }; |
152 | |
153 | // A FullHeapObjectSlot instance describes a kSystemPointerSize-sized field |
154 | // ("slot") holding a weak or strong pointer to a heap object (think: |
155 | // HeapObjectReference). |
156 | // Its address() is the address of the slot. |
157 | // The slot's contents can be read and written using operator* and store(). |
158 | // In case it is known that that slot contains a strong heap object pointer, |
159 | // ToHeapObject() can be used to retrieve that heap object. |
160 | class FullHeapObjectSlot : public SlotBase<FullHeapObjectSlot, Address> { |
161 | public: |
162 | FullHeapObjectSlot() : SlotBase(kNullAddress) {} |
163 | explicit FullHeapObjectSlot(Address ptr) : SlotBase(ptr) {} |
164 | explicit FullHeapObjectSlot(Object* ptr) |
165 | : SlotBase(reinterpret_cast<Address>(ptr)) {} |
166 | template <typename T> |
167 | explicit FullHeapObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot) |
168 | : SlotBase(slot.address()) {} |
169 | |
170 | inline const HeapObjectReference operator*() const; |
171 | inline void store(HeapObjectReference value) const; |
172 | |
173 | inline HeapObject ToHeapObject() const; |
174 | |
175 | inline void StoreHeapObject(HeapObject value) const; |
176 | }; |
177 | |
178 | // TODO(ishell, v8:8875): When pointer compression is enabled the [u]intptr_t |
179 | // and double fields are only kTaggedSize aligned so in order to avoid undefined |
180 | // behavior in C++ code we use this iterator adaptor when using STL algorithms |
181 | // with unaligned pointers. |
182 | // It will be removed once all v8:8875 is fixed and all the full pointer and |
183 | // double values in compressed V8 heap are properly aligned. |
184 | template <typename T> |
185 | class UnalignedSlot : public SlotBase<UnalignedSlot<T>, T, 1> { |
186 | public: |
187 | // This class is a stand-in for "T&" that uses custom read/write operations |
188 | // for the actual memory accesses. |
189 | class Reference { |
190 | public: |
191 | explicit Reference(Address address) : address_(address) {} |
192 | Reference(const Reference&) V8_NOEXCEPT = default; |
193 | |
194 | Reference& operator=(const Reference& other) V8_NOEXCEPT { |
195 | WriteUnalignedValue<T>(address_, other.value()); |
196 | return *this; |
197 | } |
198 | Reference& operator=(T value) { |
199 | WriteUnalignedValue<T>(address_, value); |
200 | return *this; |
201 | } |
202 | |
203 | // Values of type UnalignedSlot::reference must be implicitly convertible |
204 | // to UnalignedSlot::value_type. |
205 | operator T() const { return value(); } |
206 | |
207 | void swap(Reference& other) { |
208 | T tmp = value(); |
209 | WriteUnalignedValue<T>(address_, other.value()); |
210 | WriteUnalignedValue<T>(other.address_, tmp); |
211 | } |
212 | |
213 | bool operator<(const Reference& other) const { |
214 | return value() < other.value(); |
215 | } |
216 | |
217 | bool operator==(const Reference& other) const { |
218 | return value() == other.value(); |
219 | } |
220 | |
221 | private: |
222 | T value() const { return ReadUnalignedValue<T>(address_); } |
223 | |
224 | Address address_; |
225 | }; |
226 | |
227 | // The rest of this class follows C++'s "RandomAccessIterator" requirements. |
228 | // Most of the heavy lifting is inherited from SlotBase. |
229 | using difference_type = int; |
230 | using value_type = T; |
231 | using reference = Reference; |
232 | using pointer = T*; |
233 | using iterator_category = std::random_access_iterator_tag; |
234 | |
235 | UnalignedSlot() : SlotBase<UnalignedSlot<T>, T, 1>(kNullAddress) {} |
236 | explicit UnalignedSlot(Address address) |
237 | : SlotBase<UnalignedSlot<T>, T, 1>(address) {} |
238 | explicit UnalignedSlot(T* address) |
239 | : SlotBase<UnalignedSlot<T>, T, 1>(reinterpret_cast<Address>(address)) {} |
240 | |
241 | Reference operator*() const { |
242 | return Reference(SlotBase<UnalignedSlot<T>, T, 1>::address()); |
243 | } |
244 | Reference operator[](difference_type i) const { |
245 | return Reference(SlotBase<UnalignedSlot<T>, T, 1>::address() + |
246 | i * sizeof(T)); |
247 | } |
248 | |
249 | friend void swap(Reference lhs, Reference rhs) { lhs.swap(rhs); } |
250 | |
251 | friend difference_type operator-(UnalignedSlot a, UnalignedSlot b) { |
252 | return static_cast<int>(a.address() - b.address()) / sizeof(T); |
253 | } |
254 | }; |
255 | |
256 | } // namespace internal |
257 | } // namespace v8 |
258 | |
259 | #endif // V8_OBJECTS_SLOTS_H_ |
260 | |