v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
js-array-buffer-inl.h
Go to the documentation of this file.
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
9// Include the non-inl header before the rest of the headers.
10
14
15// Has to be the last include (doesn't have include guards):
17
18namespace v8 {
19namespace internal {
20
21#include "torque-generated/src/objects/js-array-buffer-tq-inl.inc"
22
23TQ_OBJECT_CONSTRUCTORS_IMPL(JSArrayBuffer)
24TQ_OBJECT_CONSTRUCTORS_IMPL(JSArrayBufferView)
26TQ_OBJECT_CONSTRUCTORS_IMPL(JSDataViewOrRabGsabDataView)
28TQ_OBJECT_CONSTRUCTORS_IMPL(JSRabGsabDataView)
29
30ACCESSORS(JSTypedArray, base_pointer, Tagged<Object>, kBasePointerOffset)
32 kBasePointerOffset)
33
34size_t JSArrayBuffer::byte_length() const {
35 // Use GetByteLength() for growable SharedArrayBuffers.
36 DCHECK(!is_shared() || !is_resizable_by_js());
37 return byte_length_unchecked();
38}
39
41 return ReadBoundedSizeField(kRawByteLengthOffset);
42}
43
44void JSArrayBuffer::set_byte_length(size_t value) {
45 WriteBoundedSizeField(kRawByteLengthOffset, value);
46}
47
48size_t JSArrayBuffer::max_byte_length() const {
49 return ReadBoundedSizeField(kRawMaxByteLengthOffset);
50}
51
52void JSArrayBuffer::set_max_byte_length(size_t value) {
53 WriteBoundedSizeField(kRawMaxByteLengthOffset, value);
54}
55
56DEF_GETTER(JSArrayBuffer, backing_store, void*) {
57 Address value = ReadSandboxedPointerField(kBackingStoreOffset, cage_base);
58 return reinterpret_cast<void*>(value);
59}
60
61void JSArrayBuffer::set_backing_store(Isolate* isolate, void* value) {
62 Address addr = reinterpret_cast<Address>(value);
63 WriteSandboxedPointerField(kBackingStoreOffset, isolate, addr);
64}
65
66std::shared_ptr<BackingStore> JSArrayBuffer::GetBackingStore() const {
67 if (!extension()) return nullptr;
68 return extension()->backing_store();
69}
70
72 if (V8_UNLIKELY(is_shared() && is_resizable_by_js())) {
73 // Invariant: byte_length for GSAB is 0 (it needs to be read from the
74 // BackingStore). Don't use the byte_length getter, which DCHECKs that it's
75 // not used on growable SharedArrayBuffers.
77
78 // If the byte length is read after the JSArrayBuffer object is allocated
79 // but before it's attached to the backing store, GetBackingStore returns
80 // nullptr. This is rare, but can happen e.g., when memory measurements
81 // are enabled (via performance.measureMemory()).
82 auto backing_store = GetBackingStore();
83 if (!backing_store) {
84 return 0;
85 }
86
87 return backing_store->byte_length(std::memory_order_seq_cst);
88 }
89 return byte_length();
90}
91
93 return static_cast<uint32_t>(ReadField<Address>(kBackingStoreOffset));
94}
95
97 WriteField<Address>(kBackingStoreOffset, static_cast<Address>(ref));
98}
99
101#if V8_COMPRESS_POINTERS
102 // The extension field is lazily-initialized, so set it to null initially.
103 base::AsAtomic32::Release_Store(extension_handle_location(),
105#else
107#endif // V8_COMPRESS_POINTERS
108}
109
110ArrayBufferExtension* JSArrayBuffer::extension() const {
111#if V8_COMPRESS_POINTERS
112 // We need Acquire semantics here when loading the entry, see below.
113 // Consider adding respective external pointer accessors if non-relaxed
114 // ordering semantics are ever needed in other places as well.
115 Isolate* isolate = GetIsolateFromWritableObject(*this);
117 base::AsAtomic32::Acquire_Load(extension_handle_location());
118 return reinterpret_cast<ArrayBufferExtension*>(
119 isolate->external_pointer_table().Get(handle, kArrayBufferExtensionTag));
120#else
122#endif // V8_COMPRESS_POINTERS
123}
124
125void JSArrayBuffer::set_extension(ArrayBufferExtension* extension) {
126#if V8_COMPRESS_POINTERS
127 // TODO(saelo): if we ever use the external pointer table for all external
128 // pointer fields in the no-sandbox-ptr-compression config, replace this code
129 // here and above with the respective external pointer accessors.
130 IsolateForPointerCompression isolate = GetIsolateFromWritableObject(*this);
132 Address value = reinterpret_cast<Address>(extension);
133 ExternalPointerTable& table = isolate.GetExternalPointerTableFor(tag);
134
135 ExternalPointerHandle current_handle =
136 base::AsAtomic32::Relaxed_Load(extension_handle_location());
137 if (current_handle == kNullExternalPointerHandle) {
138 // We need Release semantics here, see above.
139 ExternalPointerHandle handle = table.AllocateAndInitializeEntry(
140 isolate.GetExternalPointerTableSpaceFor(tag, address()), value, tag);
141 base::AsAtomic32::Release_Store(extension_handle_location(), handle);
142 EXTERNAL_POINTER_WRITE_BARRIER(*this, kExtensionOffset, tag);
143 } else {
144 table.Set(current_handle, value, tag);
145 }
146#else
148#endif // V8_COMPRESS_POINTERS
150}
151
152#if V8_COMPRESS_POINTERS
153ExternalPointerHandle* JSArrayBuffer::extension_handle_location() const {
154 Address location = field_address(kExtensionOffset);
155 return reinterpret_cast<ExternalPointerHandle*>(location);
156}
157#else
159 Address location = field_address(kExtensionOffset);
160 return reinterpret_cast<ArrayBufferExtension**>(location);
161}
162#endif // V8_COMPRESS_POINTERS
163
165 if (FIELD_SIZE(kOptionalPaddingOffset) != 0) {
166 DCHECK_EQ(4, FIELD_SIZE(kOptionalPaddingOffset));
167 memset(reinterpret_cast<void*>(address() + kOptionalPaddingOffset), 0,
168 FIELD_SIZE(kOptionalPaddingOffset));
169 }
170}
171
172ACCESSORS(JSArrayBuffer, detach_key, Tagged<Object>, kDetachKeyOffset)
173
174void JSArrayBuffer::set_bit_field(uint32_t bits) {
175 RELAXED_WRITE_UINT32_FIELD(*this, kBitFieldOffset, bits);
176}
177
178uint32_t JSArrayBuffer::bit_field() const {
179 return RELAXED_READ_UINT32_FIELD(*this, kBitFieldOffset);
180}
181
182// |bit_field| fields.
184 JSArrayBuffer::IsExternalBit)
186 JSArrayBuffer::IsDetachableBit)
188 JSArrayBuffer::WasDetachedBit)
190 JSArrayBuffer::IsSharedBit)
192 JSArrayBuffer::IsResizableByJsBit)
193
194bool JSArrayBuffer::IsEmpty() const {
195 auto backing_store = GetBackingStore();
196 bool is_empty = !backing_store || backing_store->IsEmpty();
197 DCHECK_IMPLIES(is_empty, byte_length() == 0);
198 return is_empty;
199}
200
201size_t JSArrayBufferView::byte_offset() const {
202 return ReadBoundedSizeField(kRawByteOffsetOffset);
203}
204
205void JSArrayBufferView::set_byte_offset(size_t value) {
206 WriteBoundedSizeField(kRawByteOffsetOffset, value);
207}
208
209size_t JSArrayBufferView::byte_length() const {
210 return ReadBoundedSizeField(kRawByteLengthOffset);
211}
212
213void JSArrayBufferView::set_byte_length(size_t value) {
214 WriteBoundedSizeField(kRawByteLengthOffset, value);
215}
216
218 return Cast<JSArrayBuffer>(buffer())->was_detached();
219}
220
222 JSArrayBufferView::IsLengthTrackingBit)
224 JSArrayBufferView::IsBackedByRabBit)
225
226bool JSArrayBufferView::IsVariableLength() const {
227 return is_length_tracking() || is_backed_by_rab();
228}
229
230size_t JSTypedArray::GetLengthOrOutOfBounds(bool& out_of_bounds) const {
231 DCHECK(!out_of_bounds);
232 if (WasDetached()) return 0;
233 if (IsVariableLength()) {
234 return GetVariableLengthOrOutOfBounds(out_of_bounds);
235 }
236 return LengthUnchecked();
237}
238
240 bool out_of_bounds = false;
241 return GetLengthOrOutOfBounds(out_of_bounds);
242}
243
245 return GetLength() * element_size();
246}
247
249 bool out_of_bounds = false;
250 GetLengthOrOutOfBounds(out_of_bounds);
251 return out_of_bounds;
252}
253
255 if (WasDetached()) {
256 return true;
257 }
258 if (!is_backed_by_rab()) {
259 // TypedArrays backed by GSABs or regular AB/SABs are never out of bounds.
260 // This shortcut is load-bearing; this enables determining
261 // IsDetachedOrOutOfBounds without consulting the BackingStore.
262 return false;
263 }
264 return IsOutOfBounds();
265}
266
267// static
269 size_t* element_size,
270 ElementsKind* element_kind) {
271 switch (array_type) {
272#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
273 case kExternal##Type##Array: \
274 *element_size = sizeof(ctype); \
275 *element_kind = TYPE##_ELEMENTS; \
276 return;
277
279#undef TYPED_ARRAY_CASE
280 }
281 UNREACHABLE();
282}
283
284size_t JSTypedArray::length() const {
285 DCHECK(!is_length_tracking());
287 return ReadBoundedSizeField(kRawLengthOffset);
288}
289
291 return ReadBoundedSizeField(kRawLengthOffset);
292}
293
294void JSTypedArray::set_length(size_t value) {
295 WriteBoundedSizeField(kRawLengthOffset, value);
296}
297
298DEF_GETTER(JSTypedArray, external_pointer, Address) {
299 return ReadSandboxedPointerField(kExternalPointerOffset, cage_base);
300}
301
303 WriteSandboxedPointerField(kExternalPointerOffset, isolate, value);
304}
305
307 PtrComprCageBase cage_base) {
308#ifdef V8_COMPRESS_POINTERS
309 return cage_base.address();
310#else
311 return 0;
312#endif
313}
314
316 DCHECK(!is_on_heap());
317 return static_cast<uint32_t>(ReadField<Address>(kExternalPointerOffset));
318}
319
321 DCHECK(!is_on_heap());
322 WriteField<Address>(kExternalPointerOffset, static_cast<Address>(ref));
323}
324
326 Isolate* isolate) {
329 external_pointer() - ExternalPointerCompensationForOnHeapArray(isolate);
330 WriteField<Address>(kExternalPointerOffset, offset);
331}
332
334 Isolate* isolate) {
336 Address pointer = ReadField<Address>(kExternalPointerOffset) +
338 set_external_pointer(isolate, pointer);
339}
340
342 // Zero-extend Tagged_t to Address according to current compression scheme
343 // so that the addition with |external_pointer| (which already contains
344 // compensated offset value) will decompress the tagged value.
345 // See JSTypedArray::ExternalPointerCompensationForOnHeapArray() for details.
347 return reinterpret_cast<void*>(external_pointer() +
348 static_cast<Tagged_t>(base_pointer().ptr()));
349}
350
351void JSTypedArray::SetOffHeapDataPtr(Isolate* isolate, void* base,
352 Address offset) {
353 Address address = reinterpret_cast<Address>(base) + offset;
354 set_external_pointer(isolate, address);
355 // This is the only spot in which the `base_pointer` field can be mutated
356 // after object initialization. Note this can happen at most once, when
357 // `JSTypedArray::GetBuffer` transitions from an on- to off-heap
358 // representation.
359 // To play well with Turbofan concurrency requirements, `base_pointer` is set
360 // with a release store, after external_pointer has been set.
361 set_base_pointer(Smi::zero(), kReleaseStore, SKIP_WRITE_BARRIER);
362 DCHECK_EQ(address, reinterpret_cast<Address>(DataPtr()));
363}
364
366 // Keep synced with `is_on_heap(AcquireLoadTag)`.
368 return base_pointer() != Smi::zero();
369}
370
372 // Keep synced with `is_on_heap()`.
373 // Note: For Turbofan concurrency requirements, it's important that this
374 // function reads only `base_pointer`.
376 return base_pointer(tag) != Smi::zero();
377}
378
379// static
381 Isolate* isolate, DirectHandle<Object> receiver, const char* method_name) {
382 if (V8_UNLIKELY(!IsJSTypedArray(*receiver))) {
383 const MessageTemplate message = MessageTemplate::kNotTypedArray;
384 THROW_NEW_ERROR(isolate, NewTypeError(message));
385 }
386
388 if (V8_UNLIKELY(array->WasDetached())) {
389 const MessageTemplate message = MessageTemplate::kDetachedOperation;
390 DirectHandle<String> operation =
391 isolate->factory()->NewStringFromAsciiChecked(method_name);
392 THROW_NEW_ERROR(isolate, NewTypeError(message, operation));
393 }
394
395 if (V8_UNLIKELY(array->IsVariableLength() && array->IsOutOfBounds())) {
396 const MessageTemplate message = MessageTemplate::kDetachedOperation;
397 DirectHandle<String> operation =
398 isolate->factory()->NewStringFromAsciiChecked(method_name);
399 THROW_NEW_ERROR(isolate, NewTypeError(message, operation));
400 }
401
402 // spec describes to return `buffer`, but it may disrupt current
403 // implementations, and it's much useful to return array for now.
404 return array;
405}
406
408 Address value = ReadSandboxedPointerField(kDataPointerOffset, cage_base);
409 return reinterpret_cast<void*>(value);
410}
411
413 void* ptr) {
414 Address value = reinterpret_cast<Address>(ptr);
415 WriteSandboxedPointerField(kDataPointerOffset, isolate, value);
416}
417
419 if (IsOutOfBounds()) {
420 return 0;
421 }
422 if (is_length_tracking()) {
423 // Invariant: byte_length of length tracking DataViews is 0.
424 DCHECK_EQ(0, byte_length());
425 return buffer()->GetByteLength() - byte_offset();
426 }
427 return byte_length();
428}
429
431 if (!is_backed_by_rab()) {
432 return false;
433 }
434 if (is_length_tracking()) {
435 return byte_offset() > buffer()->GetByteLength();
436 }
437 return byte_offset() + byte_length() > buffer()->GetByteLength();
438}
439
440} // namespace internal
441} // namespace v8
442
444
445#endif // V8_OBJECTS_JS_ARRAY_BUFFER_INL_H_
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype)
static void Release_Store(T *addr, typename std::remove_reference< T >::type new_value)
static T Acquire_Load(T *addr)
static T Relaxed_Load(T *addr)
size_t byte_length_unchecked() const
void SetBackingStoreRefForSerialization(uint32_t ref)
uint32_t GetBackingStoreRefForDeserialization() const
void set_backing_store(Isolate *isolate, void *value)
ArrayBufferExtension ** extension_location() const
std::shared_ptr< BackingStore > GetBackingStore() const
void set_data_pointer(Isolate *isolate, void *value)
void SetOffHeapDataPtr(Isolate *isolate, void *base, Address offset)
static Address ExternalPointerCompensationForOnHeapArray(PtrComprCageBase cage_base)
void RemoveExternalPointerCompensationForSerialization(Isolate *isolate)
size_t GetLengthOrOutOfBounds(bool &out_of_bounds) const
uint32_t GetExternalBackingStoreRefForDeserialization() const
size_t GetVariableLengthOrOutOfBounds(bool &out_of_bounds) const
static constexpr bool kOffHeapDataPtrEqualsExternalPointer
V8_EXPORT_PRIVATE size_t element_size() const
static MaybeDirectHandle< JSTypedArray > Validate(Isolate *isolate, DirectHandle< Object > receiver, const char *method_name)
void set_external_pointer(Isolate *isolate, Address value)
void AddExternalPointerCompensationForDeserialization(Isolate *isolate)
void SetExternalBackingStoreRefForSerialization(uint32_t ref)
static void ForFixedTypedArray(ExternalArrayType array_type, size_t *element_size, ElementsKind *element_kind)
static constexpr Tagged< Smi > zero()
Definition smi.h:99
static void ForArrayBufferExtension(Tagged< JSArrayBuffer > host, ArrayBufferExtension *extension)
bool is_empty
Definition sweeper.cc:229
#define TYPED_ARRAYS(V)
#define THROW_NEW_ERROR(isolate, call)
Definition isolate.h:307
int32_t offset
std::string extension
TNode< Object > receiver
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
Definition graph.h:1231
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
@ SKIP_WRITE_BARRIER
Definition objects.h:52
V8_INLINE size_t ReadBoundedSizeField(Address field_address)
V8_INLINE Address ReadSandboxedPointerField(Address field_address, PtrComprCageBase cage_base)
V8_INLINE void WriteBoundedSizeField(Address field_address, size_t value)
V8_INLINE Isolate * GetIsolateFromWritableObject(Tagged< HeapObject > object)
Address Tagged_t
Definition globals.h:547
constexpr ExternalPointerHandle kNullExternalPointerHandle
@ kArrayBufferExtensionTag
uint32_t ExternalPointerHandle
V8_INLINE void WriteSandboxedPointerField(Address field_address, PtrComprCageBase cage_base, Address pointer)
return value
Definition map-inl.h:893
JSArrayBuffer::IsDetachableBit is_shared
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
static constexpr ReleaseStoreTag kReleaseStore
Definition globals.h:2910
#define RELAXED_READ_UINT32_FIELD(p, offset)
#define ACCESSORS(holder, name, type, offset)
#define EXTERNAL_POINTER_WRITE_BARRIER(object, offset, tag)
#define TQ_OBJECT_CONSTRUCTORS_IMPL(Type)
#define RELAXED_WRITE_UINT32_FIELD(p, offset, value)
#define BIT_FIELD_ACCESSORS(holder, field, name, BitField)
#define RELEASE_ACQUIRE_ACCESSORS(holder, name, type, offset)
#define DEF_GETTER(Camel, Lower, Bit)
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define FIELD_SIZE(Name)
Definition utils.h:259
#define V8_UNLIKELY(condition)
Definition v8config.h:660