v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-arraybuffer.cc
Go to the documentation of this file.
1// Copyright 2016 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
10#include "src/heap/heap-inl.h" // For ToBoolean. TODO(jkummerow): Drop.
15
16namespace v8 {
17namespace internal {
18
19#define CHECK_SHARED(expected, name, method) \
20 if (name->is_shared() != expected) { \
21 THROW_NEW_ERROR_RETURN_FAILURE( \
22 isolate, \
23 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, \
24 isolate->factory()->NewStringFromAsciiChecked(method), \
25 name)); \
26 }
27
28#define CHECK_RESIZABLE(expected, name, method) \
29 if (name->is_resizable_by_js() != expected) { \
30 THROW_NEW_ERROR_RETURN_FAILURE( \
31 isolate, \
32 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, \
33 isolate->factory()->NewStringFromAsciiChecked(method), \
34 name)); \
35 }
36
37// -----------------------------------------------------------------------------
38// ES#sec-arraybuffer-objects
39
40namespace {
41
42// Tries to allocate a BackingStore given the input configuration. Either
43// returns the BackingStore or a message template that should be thrown as
44// RangeError.
45std::pair<std::unique_ptr<BackingStore>, std::optional<MessageTemplate>>
46TryAllocateBackingStore(Isolate* isolate, SharedFlag shared,
47 ResizableFlag resizable, DirectHandle<Object> length,
48 DirectHandle<Object> max_length,
49 InitializedFlag initialized) {
50 DisallowJavascriptExecution no_js(isolate);
51
52 size_t byte_length;
53 size_t max_byte_length = 0;
54 std::unique_ptr<BackingStore> backing_store;
55
56 size_t max_allocatable =
57 isolate->array_buffer_allocator()->MaxAllocationSize();
58 DCHECK(max_allocatable <= JSArrayBuffer::kMaxByteLength);
60 if (!TryNumberToSize(*length, &byte_length) ||
61 byte_length > max_allocatable) {
62 return {nullptr, MessageTemplate::kInvalidArrayBufferLength};
63 }
64
65 switch (resizable) {
67 backing_store =
68 BackingStore::Allocate(isolate, byte_length, shared, initialized);
69 break;
71 if (!TryNumberToSize(*max_length, &max_byte_length) ||
72 max_byte_length > max_allocatable) {
73 return {nullptr, MessageTemplate::kInvalidArrayBufferMaxLength};
74 }
75 if (byte_length > max_byte_length) {
76 return {nullptr, MessageTemplate::kInvalidArrayBufferMaxLength};
77 }
78
79 size_t page_size, initial_pages, max_pages;
80 const auto maybe_range_error =
82 isolate, byte_length, max_byte_length, &page_size, &initial_pages,
83 &max_pages);
84 if (maybe_range_error.has_value()) {
85 return {nullptr, maybe_range_error.value()};
86 }
88 isolate, byte_length, max_byte_length, page_size, initial_pages,
89 max_pages, WasmMemoryFlag::kNotWasm, shared);
90 break;
91 }
92
93 // Range errors bailed out earlier; only the failing allocation needs to be
94 // caught here.
95 if (!backing_store) {
96 return {nullptr, MessageTemplate::kArrayBufferAllocationFailed};
97 }
98 return {std::move(backing_store), std::nullopt};
99}
100
101Tagged<Object> ConstructBuffer(Isolate* isolate,
102 DirectHandle<JSFunction> target,
103 DirectHandle<JSReceiver> new_target,
104 DirectHandle<Object> length,
105 DirectHandle<Object> max_length,
106 InitializedFlag initialized) {
107 // We first try to convert the sizes and collect any possible range errors. If
108 // no errors are observable we create the BackingStore before the
109 // JSArrayBuffer to avoid a complex dance during setup. We then always create
110 // the AB before throwing a possible error as the creation is observable.
111 const SharedFlag shared =
112 *target != target->native_context()->array_buffer_fun()
115 const ResizableFlag resizable = max_length.is_null()
118 // BackingStore allocation may GC which is not observable itself.
119 auto [backing_store, range_error] = TryAllocateBackingStore(
120 isolate, shared, resizable, length, max_length, initialized);
121 DirectHandle<JSObject> result;
123 isolate, result,
125 auto array_buffer = Cast<JSArrayBuffer>(result);
126 const bool backing_store_creation_failed = !backing_store;
127 array_buffer->Setup(shared, resizable, std::move(backing_store), isolate);
128 if (backing_store_creation_failed) {
129 CHECK(range_error.has_value());
130 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewRangeError(range_error.value()));
131 }
132 return *array_buffer;
133}
134
135} // namespace
136
137// ES #sec-arraybuffer-constructor
138BUILTIN(ArrayBufferConstructor) {
139 HandleScope scope(isolate);
140 DirectHandle<JSFunction> target = args.target();
141 DCHECK(*target == target->native_context()->array_buffer_fun() ||
142 *target == target->native_context()->shared_array_buffer_fun());
143 if (IsUndefined(*args.new_target(), isolate)) { // [[Call]]
145 isolate,
146 NewTypeError(MessageTemplate::kConstructorNotFunction,
147 direct_handle(target->shared()->Name(), isolate)));
148 }
149 // [[Construct]]
151 DirectHandle<Object> length = args.atOrUndefined(isolate, 1);
152
153 DirectHandle<Object> number_length;
154 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_length,
155 Object::ToInteger(isolate, length));
156 if (Object::NumberValue(*number_length) < 0.0) {
158 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
159 }
160
161 DirectHandle<Object> number_max_length;
162 DirectHandle<Object> max_length;
163 DirectHandle<Object> options = args.atOrUndefined(isolate, 2);
165 isolate, max_length,
167 options, isolate->factory()->max_byte_length_string(), isolate));
168
169 if (!IsUndefined(*max_length, isolate)) {
170 if (*target == target->native_context()->array_buffer_fun()) {
171 isolate->CountUsage(
173 } else {
174 isolate->CountUsage(
176 }
177 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_max_length,
178 Object::ToInteger(isolate, max_length));
179 }
180 return ConstructBuffer(isolate, target, new_target, number_length,
181 number_max_length, InitializedFlag::kZeroInitialized);
182}
183
184// This is a helper to construct an ArrayBuffer with uinitialized memory.
185// This means the caller must ensure the buffer is totally initialized in
186// all cases, or we will expose uinitialized memory to user code.
187BUILTIN(ArrayBufferConstructor_DoNotInitialize) {
188 HandleScope scope(isolate);
189 DirectHandle<JSFunction> target(isolate->native_context()->array_buffer_fun(),
190 isolate);
191 DirectHandle<Object> length = args.atOrUndefined(isolate, 1);
192 return ConstructBuffer(isolate, target, target, length, {},
194}
195
197 const char* kMethodName, bool is_shared) {
198 HandleScope scope(isolate);
200 DirectHandle<Object> end = args.atOrUndefined(isolate, 2);
201
202 // * If Type(O) is not Object, throw a TypeError exception.
203 // * If O does not have an [[ArrayBufferData]] internal slot, throw a
204 // TypeError exception.
205 CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
206 // * [AB] If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
207 // * [SAB] If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
208 CHECK_SHARED(is_shared, array_buffer, kMethodName);
209
210 // * [AB] If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
211 if (!is_shared && array_buffer->was_detached()) {
213 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
214 isolate->factory()->NewStringFromAsciiChecked(
215 kMethodName)));
216 }
217
218 // * [AB] Let len be O.[[ArrayBufferByteLength]].
219 // * [SAB] Let len be O.[[ArrayBufferByteLength]].
220 double const len = array_buffer->GetByteLength();
221
222 // * Let relativeStart be ? ToInteger(start).
223 double relative_start;
225 isolate, relative_start, Object::IntegerValue(isolate, start));
226
227 // * If relativeStart < 0, let first be max((len + relativeStart), 0); else
228 // let first be min(relativeStart, len).
229 double const first = (relative_start < 0)
230 ? std::max(len + relative_start, 0.0)
231 : std::min(relative_start, len);
232
233 // * If end is undefined, let relativeEnd be len; else let relativeEnd be ?
234 // ToInteger(end).
235 double relative_end;
236 if (IsUndefined(*end, isolate)) {
237 relative_end = len;
238 } else {
240 isolate, relative_end, Object::IntegerValue(isolate, end));
241 }
242
243 // * If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let
244 // final be min(relativeEnd, len).
245 double const final_ = (relative_end < 0) ? std::max(len + relative_end, 0.0)
246 : std::min(relative_end, len);
247
248 // * Let newLen be max(final-first, 0).
249 double const new_len = std::max(final_ - first, 0.0);
250 DirectHandle<Object> new_len_obj = isolate->factory()->NewNumber(new_len);
251
252 // * [AB] Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%).
253 // * [SAB] Let ctor be ? SpeciesConstructor(O, %SharedArrayBuffer%).
254 DirectHandle<JSFunction> constructor_fun =
255 is_shared ? isolate->shared_array_buffer_fun()
256 : isolate->array_buffer_fun();
259 isolate, ctor,
261 constructor_fun));
262
263 // * Let new be ? Construct(ctor, newLen).
265 {
266 constexpr int argc = 1;
267 std::array<DirectHandle<Object>, argc> ctor_args = {new_len_obj};
268
269 DirectHandle<Object> new_obj;
271 isolate, new_obj,
272 Execution::New(isolate, ctor, base::VectorOf(ctor_args)));
273
274 new_ = Cast<JSReceiver>(new_obj);
275 }
276
277 // * If new does not have an [[ArrayBufferData]] internal slot, throw a
278 // TypeError exception.
279 if (!IsJSArrayBuffer(*new_)) {
281 isolate,
282 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
283 isolate->factory()->NewStringFromAsciiChecked(kMethodName),
284 new_));
285 }
286
287 // * [AB] If IsSharedArrayBuffer(new) is true, throw a TypeError exception.
288 // * [SAB] If IsSharedArrayBuffer(new) is false, throw a TypeError exception.
289 DirectHandle<JSArrayBuffer> new_array_buffer = Cast<JSArrayBuffer>(new_);
290 CHECK_SHARED(is_shared, new_array_buffer, kMethodName);
291
292 // The created ArrayBuffer might or might not be resizable, since the species
293 // constructor might return a non-resizable or a resizable buffer.
294
295 // * [AB] If IsDetachedBuffer(new) is true, throw a TypeError exception.
296 if (!is_shared && new_array_buffer->was_detached()) {
298 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
299 isolate->factory()->NewStringFromAsciiChecked(
300 kMethodName)));
301 }
302
303 // * [AB] If SameValue(new, O) is true, throw a TypeError exception.
304 if (!is_shared && Object::SameValue(*new_, *args.receiver())) {
306 isolate, NewTypeError(MessageTemplate::kArrayBufferSpeciesThis));
307 }
308
309 // * [SAB] If new.[[ArrayBufferData]] and O.[[ArrayBufferData]] are the same
310 // Shared Data Block values, throw a TypeError exception.
311 if (is_shared &&
312 new_array_buffer->backing_store() == array_buffer->backing_store()) {
314 isolate, NewTypeError(MessageTemplate::kSharedArrayBufferSpeciesThis));
315 }
316
317 // * If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception.
318 size_t new_array_buffer_byte_length = new_array_buffer->GetByteLength();
319 if (new_array_buffer_byte_length < new_len) {
321 isolate,
322 NewTypeError(is_shared ? MessageTemplate::kSharedArrayBufferTooShort
323 : MessageTemplate::kArrayBufferTooShort));
324 }
325
326 // * [AB] NOTE: Side-effects of the above steps may have detached O.
327 // * [AB] If IsDetachedBuffer(O) is true, throw a TypeError exception.
328 if (!is_shared && array_buffer->was_detached()) {
330 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
331 isolate->factory()->NewStringFromAsciiChecked(
332 kMethodName)));
333 }
334
335 // * Let fromBuf be O.[[ArrayBufferData]].
336 // * Let toBuf be new.[[ArrayBufferData]].
337 // * Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
338 size_t first_size = first;
339 size_t new_len_size = new_len;
340 DCHECK(new_array_buffer_byte_length >= new_len_size);
341
342 if (new_len_size != 0) {
343 size_t from_byte_length = array_buffer->GetByteLength();
344 if (V8_UNLIKELY(!is_shared && array_buffer->is_resizable_by_js())) {
345 // The above steps might have resized the underlying buffer. In that case,
346 // only copy the still-accessible portion of the underlying data.
347 if (first_size > from_byte_length) {
348 return *new_; // Nothing to copy.
349 }
350 if (new_len_size > from_byte_length - first_size) {
351 new_len_size = from_byte_length - first_size;
352 }
353 }
354 DCHECK(first_size <= from_byte_length);
355 DCHECK(from_byte_length - first_size >= new_len_size);
356 uint8_t* from_data =
357 reinterpret_cast<uint8_t*>(array_buffer->backing_store()) + first_size;
358 uint8_t* to_data =
359 reinterpret_cast<uint8_t*>(new_array_buffer->backing_store());
360 if (is_shared) {
361 base::Relaxed_Memcpy(reinterpret_cast<base::Atomic8*>(to_data),
362 reinterpret_cast<base::Atomic8*>(from_data),
363 new_len_size);
364 } else {
365 CopyBytes(to_data, from_data, new_len_size);
366 }
367 }
368
369 return *new_;
370}
371
372// ES #sec-sharedarraybuffer.prototype.slice
373BUILTIN(SharedArrayBufferPrototypeSlice) {
374 const char* const kMethodName = "SharedArrayBuffer.prototype.slice";
375 return SliceHelper(args, isolate, kMethodName, true);
376}
377
378// ES #sec-arraybuffer.prototype.slice
379// ArrayBuffer.prototype.slice ( start, end )
380BUILTIN(ArrayBufferPrototypeSlice) {
381 const char* const kMethodName = "ArrayBuffer.prototype.slice";
382 return SliceHelper(args, isolate, kMethodName, false);
383}
384
386 const char* kMethodName, bool is_shared) {
387 HandleScope scope(isolate);
388
389 // 1 Let O be the this value.
390 // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
391 CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
392 CHECK_RESIZABLE(true, array_buffer, kMethodName);
393
394 // [RAB] 3. If IsSharedArrayBuffer(O) is true, throw a *TypeError* exception
395 // [GSAB] 3. If IsSharedArrayBuffer(O) is false, throw a *TypeError* exception
396 CHECK_SHARED(is_shared, array_buffer, kMethodName);
397
398 // Let newByteLength to ? ToIntegerOrInfinity(newLength).
400 DirectHandle<Object> number_new_byte_length;
401 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_new_byte_length,
402 Object::ToInteger(isolate, new_length));
403
404 // [RAB] If IsDetachedBuffer(O) is true, throw a TypeError exception.
405 if (!is_shared && array_buffer->was_detached()) {
407 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
408 isolate->factory()->NewStringFromAsciiChecked(
409 kMethodName)));
410 }
411
412 // [RAB] If newByteLength < 0 or newByteLength >
413 // O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
414
415 // [GSAB] If newByteLength < currentByteLength or newByteLength >
416 // O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
417 size_t new_byte_length;
418 if (!TryNumberToSize(*number_new_byte_length, &new_byte_length)) {
420 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength,
421 isolate->factory()->NewStringFromAsciiChecked(
422 kMethodName)));
423 }
424
425 if (is_shared && new_byte_length < array_buffer->GetByteLength()) {
426 // GrowableSharedArrayBuffer is only allowed to grow.
428 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength,
429 isolate->factory()->NewStringFromAsciiChecked(
430 kMethodName)));
431 }
432
433 if (new_byte_length > array_buffer->max_byte_length()) {
435 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength,
436 isolate->factory()->NewStringFromAsciiChecked(
437 kMethodName)));
438 }
439
440 // [RAB] Let hostHandled be ? HostResizeArrayBuffer(O, newByteLength).
441 // [GSAB] Let hostHandled be ? HostGrowArrayBuffer(O, newByteLength).
442 // If hostHandled is handled, return undefined.
443
444#ifdef V8_ENABLE_WEBASSEMBLY
445 auto backing_store = array_buffer->GetBackingStore();
446 if (backing_store->is_wasm_memory()) {
447 size_t old_byte_length =
448 backing_store->byte_length(std::memory_order_seq_cst);
449 // WebAssembly memories cannot shrink, and must be a multiple of the page
450 // size.
451 if (new_byte_length < old_byte_length ||
452 (new_byte_length % wasm::kWasmPageSize) != 0) {
454 isolate,
455 NewRangeError(
456 MessageTemplate::kInvalidArrayBufferResizeLength,
457 isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
458 }
459 Handle<Object> memory =
461 isolate, array_buffer,
462 isolate->factory()->array_buffer_wasm_memory_symbol())
463 .ToHandleChecked();
464 CHECK(IsWasmMemoryObject(*memory));
465 // WasmMemoryObject::Grow handles updating byte_length, as it's used by both
466 // ArrayBuffer.prototype.resize and WebAssembly.Memory.prototype.grow.
467 uint32_t delta_pages =
468 static_cast<uint32_t>(new_byte_length - old_byte_length) /
471 delta_pages) == -1) {
473 isolate, NewRangeError(MessageTemplate::kOutOfMemory,
474 isolate->factory()->NewStringFromAsciiChecked(
475 kMethodName)));
476 }
477 return ReadOnlyRoots(isolate).undefined_value();
478 }
479#endif // V8_ENABLE_WEBASSEMBLY
480
481 if (!is_shared) {
482 // [RAB] Let oldBlock be O.[[ArrayBufferData]].
483 // [RAB] Let newBlock be ? CreateByteDataBlock(newByteLength).
484 // [RAB] Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).
485 // [RAB] Perform CopyDataBlockBytes(newBlock, 0, oldBlock, 0, copyLength).
486 // [RAB] NOTE: Neither creation of the new Data Block nor copying from the
487 // old Data Block are observable. Implementations reserve the right to
488 // implement this method as in-place growth or shrinkage.
489 if (array_buffer->GetBackingStore()->ResizeInPlace(isolate,
490 new_byte_length) !=
493 isolate, NewRangeError(MessageTemplate::kOutOfMemory,
494 isolate->factory()->NewStringFromAsciiChecked(
495 kMethodName)));
496 }
497
498 // TypedsArrays in optimized code may go out of bounds. Trigger deopts
499 // through the ArrayBufferDetaching protector.
500 if (new_byte_length < array_buffer->byte_length()) {
501 if (Protectors::IsArrayBufferDetachingIntact(isolate)) {
502 Protectors::InvalidateArrayBufferDetaching(isolate);
503 }
504 }
505
506 isolate->heap()->ResizeArrayBufferExtension(
507 array_buffer->extension(),
508 static_cast<int64_t>(new_byte_length) - array_buffer->byte_length());
509
510 // [RAB] Set O.[[ArrayBufferByteLength]] to newLength.
511 array_buffer->set_byte_length(new_byte_length);
512 } else {
513 // [GSAB] (Detailed description of the algorithm omitted.)
514 auto result =
515 array_buffer->GetBackingStore()->GrowInPlace(isolate, new_byte_length);
518 isolate, NewRangeError(MessageTemplate::kOutOfMemory,
519 isolate->factory()->NewStringFromAsciiChecked(
520 kMethodName)));
521 }
524 isolate,
525 NewRangeError(
526 MessageTemplate::kInvalidArrayBufferResizeLength,
527 isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
528 }
529 // Invariant: byte_length for a GSAB is 0 (it needs to be read from the
530 // BackingStore). Don't use the byte_length getter, which DCHECKs that it's
531 // not used on growable SharedArrayBuffers
532 CHECK_EQ(0, array_buffer->byte_length_unchecked());
533 }
534 return ReadOnlyRoots(isolate).undefined_value();
535}
536
537// ES #sec-get-sharedarraybuffer.prototype.bytelength
538// get SharedArrayBuffer.prototype.byteLength
539BUILTIN(SharedArrayBufferPrototypeGetByteLength) {
540 const char* const kMethodName = "get SharedArrayBuffer.prototype.byteLength";
541 HandleScope scope(isolate);
542 // 1. Let O be the this value.
543 // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
544 CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
545 // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
546 CHECK_SHARED(true, array_buffer, kMethodName);
547
548 DCHECK_IMPLIES(!array_buffer->GetBackingStore()->is_wasm_memory(),
549 array_buffer->max_byte_length() ==
550 array_buffer->GetBackingStore()->max_byte_length());
551
552 // 4. Let length be ArrayBufferByteLength(O, SeqCst).
553 size_t byte_length = array_buffer->GetByteLength();
554 // 5. Return F(length).
555 return *isolate->factory()->NewNumberFromSize(byte_length);
556}
557
558// ES #sec-arraybuffer.prototype.resize
559// ArrayBuffer.prototype.resize(new_size)
560BUILTIN(ArrayBufferPrototypeResize) {
561 const char* const kMethodName = "ArrayBuffer.prototype.resize";
562 constexpr bool kIsShared = false;
563 return ResizeHelper(args, isolate, kMethodName, kIsShared);
564}
565
566namespace {
567
568enum PreserveResizability { kToFixedLength, kPreserveResizability };
569
570Tagged<Object> ArrayBufferTransfer(Isolate* isolate,
571 DirectHandle<JSArrayBuffer> array_buffer,
572 DirectHandle<Object> new_length,
573 PreserveResizability preserve_resizability,
574 const char* method_name) {
575 size_t max_allocatable =
576 isolate->array_buffer_allocator()->MaxAllocationSize();
577 DCHECK(max_allocatable <= JSArrayBuffer::kMaxByteLength);
578 // 2. If IsSharedArrayBuffer(arrayBuffer) is true, throw a TypeError
579 // exception.
580 CHECK_SHARED(false, array_buffer, method_name);
581
582 size_t new_byte_length;
583 if (IsUndefined(*new_length, isolate)) {
584 // 3. If newLength is undefined, then
585 // a. Let newByteLength be arrayBuffer.[[ArrayBufferByteLength]].
586 new_byte_length = array_buffer->GetByteLength();
587 } else {
588 // 4. Else,
589 // a. Let newByteLength be ? ToIndex(newLength).
590 DirectHandle<Object> number_new_byte_length;
591 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_new_byte_length,
592 Object::ToInteger(isolate, new_length));
593 if (Object::NumberValue(*number_new_byte_length) < 0.0) {
595 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
596 }
597 if (!TryNumberToSize(*number_new_byte_length, &new_byte_length) ||
598 new_byte_length > max_allocatable) {
600 isolate,
601 NewRangeError(
602 MessageTemplate::kInvalidArrayBufferResizeLength,
603 isolate->factory()->NewStringFromAsciiChecked(method_name)));
604 }
605 }
606
607 // 5. If IsDetachedBuffer(arrayBuffer) is true, throw a TypeError exception.
608 if (array_buffer->was_detached()) {
610 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
611 isolate->factory()->NewStringFromAsciiChecked(
612 method_name)));
613 }
614
615 ResizableFlag resizable;
616 size_t new_max_byte_length;
617 if (preserve_resizability == kPreserveResizability &&
618 array_buffer->is_resizable_by_js()) {
619 // 6. If preserveResizability is preserve-resizability and
620 // IsResizableArrayBuffer(arrayBuffer) is true, then
621 // a. Let newMaxByteLength be arrayBuffer.[[ArrayBufferMaxByteLength]].
622 new_max_byte_length = array_buffer->max_byte_length();
623 resizable = ResizableFlag::kResizable;
624 } else {
625 // 7. Else,
626 // a. Let newMaxByteLength be empty.
627 new_max_byte_length = new_byte_length;
629 }
630
631 // 8. If arrayBuffer.[[ArrayBufferDetachKey]] is not undefined, throw a
632 // TypeError exception.
633
634 if (!array_buffer->is_detachable()) {
636 isolate,
637 NewTypeError(MessageTemplate::kDataCloneErrorNonDetachableArrayBuffer));
638 }
639
640 // After this point the steps are not observable and are performed out of
641 // spec order.
642
643 // Case 1: We don't need a BackingStore.
644 if (new_byte_length == 0) {
645 // 15. Perform ! DetachArrayBuffer(arrayBuffer).
646 JSArrayBuffer::Detach(array_buffer).Check();
647
648 // 9. Let newBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, newByteLength,
649 // newMaxByteLength).
650 //
651 // Nothing to do for steps 10-14.
652 //
653 // 16. Return newBuffer.
654 return *isolate->factory()
655 ->NewJSArrayBufferAndBackingStore(
656 0, new_max_byte_length, InitializedFlag::kUninitialized,
657 resizable)
658 .ToHandleChecked();
659 }
660
661 // Case 2: We can reuse the same BackingStore.
662 auto from_backing_store = array_buffer->GetBackingStore();
663 if (from_backing_store && !from_backing_store->is_resizable_by_js() &&
664 resizable == ResizableFlag::kNotResizable &&
665 new_byte_length == array_buffer->GetByteLength()) {
666 // TODO(syg): Consider realloc when the default ArrayBuffer allocator's
667 // Reallocate does better than copy.
668 //
669 // See https://crbug.com/330575496#comment27
670
671 // 15. Perform ! DetachArrayBuffer(arrayBuffer).
672 JSArrayBuffer::Detach(array_buffer).Check();
673
674 // 9. Let newBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, newByteLength,
675 // newMaxByteLength).
676 // 16. Return newBuffer.
677 return *isolate->factory()->NewJSArrayBuffer(std::move(from_backing_store));
678 }
679
680 // Case 3: We can't reuse the same BackingStore. Copy the buffer.
681
682 if (new_byte_length > new_max_byte_length) {
684 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
685 }
686
687 // 9. Let newBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, newByteLength,
688 // newMaxByteLength).
689 DirectHandle<JSArrayBuffer> new_buffer;
690 MaybeDirectHandle<JSArrayBuffer> result =
691 isolate->factory()->NewJSArrayBufferAndBackingStore(
692 new_byte_length, new_max_byte_length, InitializedFlag::kUninitialized,
693 resizable);
694 if (!result.ToHandle(&new_buffer)) {
696 isolate, NewRangeError(MessageTemplate::kArrayBufferAllocationFailed));
697 }
698
699 // 10. Let copyLength be min(newByteLength,
700 // arrayBuffer.[[ArrayBufferByteLength]]).
701 //
702 // (Size comparison is done manually below instead of using min.)
703
704 // 11. Let fromBlock be arrayBuffer.[[ArrayBufferData]].
705 uint8_t* from_data =
706 reinterpret_cast<uint8_t*>(array_buffer->backing_store());
707
708 // 12. Let toBlock be newBuffer.[[ArrayBufferData]].
709 uint8_t* to_data = reinterpret_cast<uint8_t*>(new_buffer->backing_store());
710
711 // 13. Perform CopyDataBlockBytes(toBlock, 0, fromBlock, 0, copyLength).
712 // 14. NOTE: Neither creation of the new Data Block nor copying from the old
713 // Data Block are observable. Implementations reserve the right to
714 // implement this method as a zero-copy move or a realloc.
715 size_t from_byte_length = array_buffer->GetByteLength();
716 if (new_byte_length <= from_byte_length) {
717 CopyBytes(to_data, from_data, new_byte_length);
718 } else {
719 CopyBytes(to_data, from_data, from_byte_length);
720 memset(to_data + from_byte_length, 0, new_byte_length - from_byte_length);
721 }
722
723 // 15. Perform ! DetachArrayBuffer(arrayBuffer).
724 JSArrayBuffer::Detach(array_buffer).Check();
725
726 // 16. Return newBuffer.
727 return *new_buffer;
728}
729
730} // namespace
731
732// ES #sec-arraybuffer.prototype.transfer
733// ArrayBuffer.prototype.transfer([new_length])
734BUILTIN(ArrayBufferPrototypeTransfer) {
735 const char kMethodName[] = "ArrayBuffer.prototype.transfer";
736 HandleScope scope(isolate);
737 isolate->CountUsage(v8::Isolate::kArrayBufferTransfer);
738
739 // 1. Perform ? RequireInternalSlot(arrayBuffer, [[ArrayBufferData]]).
740 CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
741 DirectHandle<Object> new_length = args.atOrUndefined(isolate, 1);
742 return ArrayBufferTransfer(isolate, array_buffer, new_length,
743 kPreserveResizability, kMethodName);
744}
745
746// ES #sec-arraybuffer.prototype.transferToFixedLength
747// ArrayBuffer.prototype.transferToFixedLength([new_length])
748BUILTIN(ArrayBufferPrototypeTransferToFixedLength) {
749 const char kMethodName[] = "ArrayBuffer.prototype.transferToFixedLength";
750 HandleScope scope(isolate);
751 isolate->CountUsage(v8::Isolate::kArrayBufferTransfer);
752
753 // 1. Perform ? RequireInternalSlot(arrayBuffer, [[ArrayBufferData]]).
754 CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
755 DirectHandle<Object> new_length = args.atOrUndefined(isolate, 1);
756 return ArrayBufferTransfer(isolate, array_buffer, new_length, kToFixedLength,
757 kMethodName);
758}
759
760// ES #sec-sharedarraybuffer.prototype.grow
761// SharedArrayBuffer.prototype.grow(new_size)
762BUILTIN(SharedArrayBufferPrototypeGrow) {
763 const char* const kMethodName = "SharedArrayBuffer.prototype.grow";
764 constexpr bool kIsShared = true;
765 return ResizeHelper(args, isolate, kMethodName, kIsShared);
766}
767
768} // namespace internal
769} // namespace v8
#define CHECK_RESIZABLE(expected, name, method)
#define CHECK_SHARED(expected, name, method)
#define CHECK_RECEIVER(Type, name, method)
#define BUILTIN(name)
@ kGrowableSharedArrayBuffer
Definition v8-isolate.h:615
@ kResizableArrayBuffer
Definition v8-isolate.h:614
@ kArrayBufferTransfer
Definition v8-isolate.h:623
static std::unique_ptr< BackingStore > TryAllocateAndPartiallyCommitMemory(Isolate *isolate, size_t byte_length, size_t max_byte_length, size_t page_size, size_t initial_pages, size_t maximum_pages, WasmMemoryFlag wasm_memory, SharedFlag shared, bool has_guard_regions=false)
static std::unique_ptr< BackingStore > Allocate(Isolate *isolate, size_t byte_length, SharedFlag shared, InitializedFlag initialized)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSReceiver > New(Isolate *isolate, DirectHandle< Object > constructor, base::Vector< const DirectHandle< Object > > args)
Definition execution.cc:556
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT Maybe< bool > Detach(DirectHandle< JSArrayBuffer > buffer, bool force_for_wasm_memory=false, DirectHandle< Object > key={})
static constexpr size_t kMaxByteLength
static std::optional< MessageTemplate > GetResizableBackingStorePageConfigurationImpl(Isolate *isolate, size_t byte_length, size_t max_byte_length, size_t *page_size, size_t *initial_pages, size_t *max_pages)
static V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT MaybeHandle< JSObject > New(DirectHandle< JSFunction > constructor, DirectHandle< JSReceiver > new_target, DirectHandle< AllocationSite > site, NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
static MaybeDirectHandle< Object > ReadFromOptionsBag(DirectHandle< Object > options, DirectHandle< String > option_name, Isolate *isolate)
static constexpr size_t kMaxByteLength
static V8_WARN_UNUSED_RESULT HandleType< Number >::MaybeType ToInteger(Isolate *isolate, HandleType< T > input)
static double NumberValue(Tagged< Number > obj)
static V8_WARN_UNUSED_RESULT Maybe< double > IntegerValue(Isolate *isolate, HandleType< T > input)
static V8_EXPORT_PRIVATE bool SameValue(Tagged< Object > obj, Tagged< Object > other)
Definition objects.cc:1706
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< Object > SpeciesConstructor(Isolate *isolate, DirectHandle< JSReceiver > recv, DirectHandle< JSFunction > default_ctor)
Definition objects.cc:1791
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetProperty(LookupIterator *it, bool is_global_reference=false)
Definition objects.cc:1248
static V8_EXPORT_PRIVATE int32_t Grow(Isolate *, DirectHandle< WasmMemoryObject >, uint32_t pages)
int start
int end
#define ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:284
#define THROW_NEW_ERROR_RETURN_FAILURE(isolate, call)
Definition isolate.h:294
#define MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:448
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
DirectHandle< Object > new_target
Definition execution.cc:75
TNode< Object > target
ZoneVector< RpoNumber > & result
void Relaxed_Memcpy(volatile Atomic8 *dst, volatile const Atomic8 *src, size_t bytes)
Definition atomicops.h:363
char Atomic8
Definition atomicops.h:57
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
constexpr size_t kWasmPageSize
void CopyBytes(T *dst, const T *src, size_t num_bytes)
Definition memcopy.h:261
static Tagged< Object > SliceHelper(BuiltinArguments args, Isolate *isolate, const char *kMethodName, bool is_shared)
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
JSArrayBuffer::IsDetachableBit is_shared
bool TryNumberToSize(Tagged< Object > number, size_t *result)
static Tagged< Object > ResizeHelper(BuiltinArguments args, Isolate *isolate, const char *kMethodName, bool is_shared)
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define V8_UNLIKELY(condition)
Definition v8config.h:660