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_JS_ARRAY_BUFFER_INL_H_
6 #define V8_OBJECTS_JS_ARRAY_BUFFER_INL_H_
7
8 #include "src/common/external-pointer.h"
9 #include "src/objects/js-array-buffer.h"
10
11 #include "src/common/external-pointer-inl.h"
12 #include "src/heap/heap-write-barrier-inl.h"
13 #include "src/objects/js-objects-inl.h"
14 #include "src/objects/objects-inl.h"
15 #include "src/wasm/wasm-engine.h"
16
17 // Has to be the last include (doesn't have include guards):
18 #include "src/objects/object-macros.h"
19
20 namespace v8 {
21 namespace internal {
22
23 #include "torque-generated/src/objects/js-array-buffer-tq-inl.inc"
24
25 TQ_OBJECT_CONSTRUCTORS_IMPL(JSArrayBuffer)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSArrayBufferView)26 TQ_OBJECT_CONSTRUCTORS_IMPL(JSArrayBufferView)
27 TQ_OBJECT_CONSTRUCTORS_IMPL(JSTypedArray)
28 TQ_OBJECT_CONSTRUCTORS_IMPL(JSDataView)
29
30 void JSArrayBuffer::AllocateExternalPointerEntries(Isolate* isolate) {
31 InitExternalPointerField(kBackingStoreOffset, isolate);
32 }
33
byte_length()34 size_t JSArrayBuffer::byte_length() const {
35 return ReadField<size_t>(kByteLengthOffset);
36 }
37
set_byte_length(size_t value)38 void JSArrayBuffer::set_byte_length(size_t value) {
39 WriteField<size_t>(kByteLengthOffset, value);
40 }
41
DEF_GETTER(JSArrayBuffer,backing_store,void *)42 DEF_GETTER(JSArrayBuffer, backing_store, void*) {
43 Address value = ReadExternalPointerField(kBackingStoreOffset, isolate,
44 kArrayBufferBackingStoreTag);
45 return reinterpret_cast<void*>(value);
46 }
47
set_backing_store(Isolate * isolate,void * value)48 void JSArrayBuffer::set_backing_store(Isolate* isolate, void* value) {
49 WriteExternalPointerField(kBackingStoreOffset, isolate,
50 reinterpret_cast<Address>(value),
51 kArrayBufferBackingStoreTag);
52 }
53
GetBackingStoreRefForDeserialization()54 uint32_t JSArrayBuffer::GetBackingStoreRefForDeserialization() const {
55 return static_cast<uint32_t>(
56 ReadField<ExternalPointer_t>(kBackingStoreOffset));
57 }
58
SetBackingStoreRefForSerialization(uint32_t ref)59 void JSArrayBuffer::SetBackingStoreRefForSerialization(uint32_t ref) {
60 WriteField<ExternalPointer_t>(kBackingStoreOffset,
61 static_cast<ExternalPointer_t>(ref));
62 }
63
extension()64 ArrayBufferExtension* JSArrayBuffer::extension() const {
65 #if V8_COMPRESS_POINTERS
66 // With pointer compression the extension-field might not be
67 // pointer-aligned. However on ARM64 this field needs to be aligned to
68 // perform atomic operations on it. Therefore we split the pointer into two
69 // 32-bit words that we update atomically. We don't have an ABA problem here
70 // since there can never be an Attach() after Detach() (transitions only
71 // from NULL --> some ptr --> NULL).
72
73 // Synchronize with publishing release store of non-null extension
74 uint32_t lo = base::AsAtomic32::Acquire_Load(extension_lo());
75 if (lo & kUninitializedTagMask) return nullptr;
76
77 // Synchronize with release store of null extension
78 uint32_t hi = base::AsAtomic32::Acquire_Load(extension_hi());
79 uint32_t verify_lo = base::AsAtomic32::Relaxed_Load(extension_lo());
80 if (lo != verify_lo) return nullptr;
81
82 uintptr_t address = static_cast<uintptr_t>(lo);
83 address |= static_cast<uintptr_t>(hi) << 32;
84 return reinterpret_cast<ArrayBufferExtension*>(address);
85 #else
86 return base::AsAtomicPointer::Acquire_Load(extension_location());
87 #endif
88 }
89
set_extension(ArrayBufferExtension * extension)90 void JSArrayBuffer::set_extension(ArrayBufferExtension* extension) {
91 #if V8_COMPRESS_POINTERS
92 if (extension != nullptr) {
93 uintptr_t address = reinterpret_cast<uintptr_t>(extension);
94 base::AsAtomic32::Relaxed_Store(extension_hi(),
95 static_cast<uint32_t>(address >> 32));
96 base::AsAtomic32::Release_Store(extension_lo(),
97 static_cast<uint32_t>(address));
98 } else {
99 base::AsAtomic32::Relaxed_Store(extension_lo(),
100 0 | kUninitializedTagMask);
101 base::AsAtomic32::Release_Store(extension_hi(), 0);
102 }
103 #else
104 base::AsAtomicPointer::Release_Store(extension_location(), extension);
105 #endif
106 WriteBarrier::Marking(*this, extension);
107 }
108
extension_location()109 ArrayBufferExtension** JSArrayBuffer::extension_location() const {
110 Address location = field_address(kExtensionOffset);
111 return reinterpret_cast<ArrayBufferExtension**>(location);
112 }
113
114 #if V8_COMPRESS_POINTERS
extension_lo()115 uint32_t* JSArrayBuffer::extension_lo() const {
116 Address location = field_address(kExtensionOffset);
117 return reinterpret_cast<uint32_t*>(location);
118 }
119
extension_hi()120 uint32_t* JSArrayBuffer::extension_hi() const {
121 Address location = field_address(kExtensionOffset) + sizeof(uint32_t);
122 return reinterpret_cast<uint32_t*>(location);
123 }
124 #endif
125
allocation_length()126 size_t JSArrayBuffer::allocation_length() const {
127 if (backing_store() == nullptr) {
128 return 0;
129 }
130 return byte_length();
131 }
132
allocation_base()133 void* JSArrayBuffer::allocation_base() const {
134 if (backing_store() == nullptr) {
135 return nullptr;
136 }
137 return backing_store();
138 }
139
clear_padding()140 void JSArrayBuffer::clear_padding() {
141 if (FIELD_SIZE(kOptionalPaddingOffset) != 0) {
142 DCHECK_EQ(4, FIELD_SIZE(kOptionalPaddingOffset));
143 memset(reinterpret_cast<void*>(address() + kOptionalPaddingOffset), 0,
144 FIELD_SIZE(kOptionalPaddingOffset));
145 }
146 }
147
set_bit_field(uint32_t bits)148 void JSArrayBuffer::set_bit_field(uint32_t bits) {
149 RELAXED_WRITE_UINT32_FIELD(*this, kBitFieldOffset, bits);
150 }
151
bit_field()152 uint32_t JSArrayBuffer::bit_field() const {
153 return RELAXED_READ_UINT32_FIELD(*this, kBitFieldOffset);
154 }
155
156 // |bit_field| fields.
BIT_FIELD_ACCESSORS(JSArrayBuffer,bit_field,is_external,JSArrayBuffer::IsExternalBit)157 BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_external,
158 JSArrayBuffer::IsExternalBit)
159 BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_detachable,
160 JSArrayBuffer::IsDetachableBit)
161 BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, was_detached,
162 JSArrayBuffer::WasDetachedBit)
163 BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_asmjs_memory,
164 JSArrayBuffer::IsAsmJsMemoryBit)
165 BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_shared,
166 JSArrayBuffer::IsSharedBit)
167
168 size_t JSArrayBufferView::byte_offset() const {
169 return ReadField<size_t>(kByteOffsetOffset);
170 }
171
set_byte_offset(size_t value)172 void JSArrayBufferView::set_byte_offset(size_t value) {
173 WriteField<size_t>(kByteOffsetOffset, value);
174 }
175
byte_length()176 size_t JSArrayBufferView::byte_length() const {
177 return ReadField<size_t>(kByteLengthOffset);
178 }
179
set_byte_length(size_t value)180 void JSArrayBufferView::set_byte_length(size_t value) {
181 WriteField<size_t>(kByteLengthOffset, value);
182 }
183
WasDetached()184 bool JSArrayBufferView::WasDetached() const {
185 return JSArrayBuffer::cast(buffer()).was_detached();
186 }
187
AllocateExternalPointerEntries(Isolate * isolate)188 void JSTypedArray::AllocateExternalPointerEntries(Isolate* isolate) {
189 InitExternalPointerField(kExternalPointerOffset, isolate);
190 }
191
length()192 size_t JSTypedArray::length() const { return ReadField<size_t>(kLengthOffset); }
193
set_length(size_t value)194 void JSTypedArray::set_length(size_t value) {
195 WriteField<size_t>(kLengthOffset, value);
196 }
197
DEF_GETTER(JSTypedArray,external_pointer,Address)198 DEF_GETTER(JSTypedArray, external_pointer, Address) {
199 return ReadExternalPointerField(kExternalPointerOffset, isolate,
200 kTypedArrayExternalPointerTag);
201 }
202
DEF_GETTER(JSTypedArray,external_pointer_raw,ExternalPointer_t)203 DEF_GETTER(JSTypedArray, external_pointer_raw, ExternalPointer_t) {
204 return ReadField<ExternalPointer_t>(kExternalPointerOffset);
205 }
206
set_external_pointer(Isolate * isolate,Address value)207 void JSTypedArray::set_external_pointer(Isolate* isolate, Address value) {
208 WriteExternalPointerField(kExternalPointerOffset, isolate, value,
209 kTypedArrayExternalPointerTag);
210 }
211
ExternalPointerCompensationForOnHeapArray(IsolateRoot isolate)212 Address JSTypedArray::ExternalPointerCompensationForOnHeapArray(
213 IsolateRoot isolate) {
214 #ifdef V8_COMPRESS_POINTERS
215 return isolate.address();
216 #else
217 return 0;
218 #endif
219 }
220
GetExternalBackingStoreRefForDeserialization()221 uint32_t JSTypedArray::GetExternalBackingStoreRefForDeserialization() const {
222 DCHECK(!is_on_heap());
223 return static_cast<uint32_t>(
224 ReadField<ExternalPointer_t>(kExternalPointerOffset));
225 }
226
SetExternalBackingStoreRefForSerialization(uint32_t ref)227 void JSTypedArray::SetExternalBackingStoreRefForSerialization(uint32_t ref) {
228 DCHECK(!is_on_heap());
229 WriteField<ExternalPointer_t>(kExternalPointerOffset,
230 static_cast<ExternalPointer_t>(ref));
231 }
232
RemoveExternalPointerCompensationForSerialization(Isolate * isolate)233 void JSTypedArray::RemoveExternalPointerCompensationForSerialization(
234 Isolate* isolate) {
235 DCHECK(is_on_heap());
236 // TODO(v8:10391): once we have an external table, avoid the need for
237 // compensation by replacing external_pointer and base_pointer fields
238 // with one data_pointer field which can point to either external data
239 // backing store or into on-heap backing store.
240 Address offset =
241 external_pointer() - ExternalPointerCompensationForOnHeapArray(isolate);
242 #ifdef V8_HEAP_SANDBOX
243 // Write decompensated offset directly to the external pointer field, thus
244 // allowing the offset to be propagated through serialization-deserialization.
245 WriteField<ExternalPointer_t>(kExternalPointerOffset, offset);
246 #else
247 set_external_pointer(isolate, offset);
248 #endif
249 }
250
DataPtr()251 void* JSTypedArray::DataPtr() {
252 // Zero-extend Tagged_t to Address according to current compression scheme
253 // so that the addition with |external_pointer| (which already contains
254 // compensated offset value) will decompress the tagged value.
255 // See JSTypedArray::ExternalPointerCompensationForOnHeapArray() for details.
256 return reinterpret_cast<void*>(external_pointer() +
257 static_cast<Tagged_t>(base_pointer().ptr()));
258 }
259
SetOffHeapDataPtr(Isolate * isolate,void * base,Address offset)260 void JSTypedArray::SetOffHeapDataPtr(Isolate* isolate, void* base,
261 Address offset) {
262 set_base_pointer(Smi::zero(), SKIP_WRITE_BARRIER);
263 Address address = reinterpret_cast<Address>(base) + offset;
264 set_external_pointer(isolate, address);
265 DCHECK_EQ(address, reinterpret_cast<Address>(DataPtr()));
266 }
267
SetOnHeapDataPtr(Isolate * isolate,HeapObject base,Address offset)268 void JSTypedArray::SetOnHeapDataPtr(Isolate* isolate, HeapObject base,
269 Address offset) {
270 set_base_pointer(base);
271 set_external_pointer(
272 isolate, offset + ExternalPointerCompensationForOnHeapArray(isolate));
273 DCHECK_EQ(base.ptr() + offset, reinterpret_cast<Address>(DataPtr()));
274 }
275
is_on_heap()276 bool JSTypedArray::is_on_heap() const {
277 DisallowHeapAllocation no_gc;
278 // Checking that buffer()->backing_store() is not nullptr is not sufficient;
279 // it will be nullptr when byte_length is 0 as well.
280 return base_pointer() == elements();
281 }
282
283 // static
Validate(Isolate * isolate,Handle<Object> receiver,const char * method_name)284 MaybeHandle<JSTypedArray> JSTypedArray::Validate(Isolate* isolate,
285 Handle<Object> receiver,
286 const char* method_name) {
287 if (V8_UNLIKELY(!receiver->IsJSTypedArray())) {
288 const MessageTemplate message = MessageTemplate::kNotTypedArray;
289 THROW_NEW_ERROR(isolate, NewTypeError(message), JSTypedArray);
290 }
291
292 Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(receiver);
293 if (V8_UNLIKELY(array->WasDetached())) {
294 const MessageTemplate message = MessageTemplate::kDetachedOperation;
295 Handle<String> operation =
296 isolate->factory()->NewStringFromAsciiChecked(method_name);
297 THROW_NEW_ERROR(isolate, NewTypeError(message, operation), JSTypedArray);
298 }
299
300 // spec describes to return `buffer`, but it may disrupt current
301 // implementations, and it's much useful to return array for now.
302 return array;
303 }
304
DEF_GETTER(JSDataView,data_pointer,void *)305 DEF_GETTER(JSDataView, data_pointer, void*) {
306 return reinterpret_cast<void*>(ReadExternalPointerField(
307 kDataPointerOffset, isolate, kDataViewDataPointerTag));
308 }
309
AllocateExternalPointerEntries(Isolate * isolate)310 void JSDataView::AllocateExternalPointerEntries(Isolate* isolate) {
311 InitExternalPointerField(kDataPointerOffset, isolate);
312 }
313
set_data_pointer(Isolate * isolate,void * value)314 void JSDataView::set_data_pointer(Isolate* isolate, void* value) {
315 WriteExternalPointerField(kDataPointerOffset, isolate,
316 reinterpret_cast<Address>(value),
317 kDataViewDataPointerTag);
318 }
319
320 } // namespace internal
321 } // namespace v8
322
323 #include "src/objects/object-macros-undef.h"
324
325 #endif // V8_OBJECTS_JS_ARRAY_BUFFER_INL_H_
326