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
5#include "src/builtins/builtins-utils-inl.h"
6#include "src/builtins/builtins.h"
7#include "src/handles/maybe-handles-inl.h"
8#include "src/heap/heap-inl.h"  // For ToBoolean. TODO(jkummerow): Drop.
9#include "src/logging/counters.h"
10#include "src/numbers/conversions.h"
11#include "src/objects/js-array-buffer-inl.h"
12#include "src/objects/objects-inl.h"
13
14namespace v8 {
15namespace internal {
16
17#define CHECK_SHARED(expected, name, method)                                \
18  if (name->is_shared() != expected) {                                      \
19    THROW_NEW_ERROR_RETURN_FAILURE(                                         \
20        isolate,                                                            \
21        NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,          \
22                     isolate->factory()->NewStringFromAsciiChecked(method), \
23                     name));                                                \
24  }
25
26#define CHECK_RESIZABLE(expected, name, method)                             \
27  if (name->is_resizable() != expected) {                                   \
28    THROW_NEW_ERROR_RETURN_FAILURE(                                         \
29        isolate,                                                            \
30        NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,          \
31                     isolate->factory()->NewStringFromAsciiChecked(method), \
32                     name));                                                \
33  }
34
35// -----------------------------------------------------------------------------
36// ES#sec-arraybuffer-objects
37
38namespace {
39
40Object ConstructBuffer(Isolate* isolate, Handle<JSFunction> target,
41                       Handle<JSReceiver> new_target, Handle<Object> length,
42                       Handle<Object> max_length, InitializedFlag initialized) {
43  SharedFlag shared = *target != target->native_context().array_buffer_fun()
44                          ? SharedFlag::kShared
45                          : SharedFlag::kNotShared;
46  ResizableFlag resizable = max_length.is_null() ? ResizableFlag::kNotResizable
47                                                 : ResizableFlag::kResizable;
48  Handle<JSObject> result;
49  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
50      isolate, result,
51      JSObject::New(target, new_target, Handle<AllocationSite>::null()));
52  auto array_buffer = Handle<JSArrayBuffer>::cast(result);
53  // Ensure that all fields are initialized because BackingStore::Allocate is
54  // allowed to GC. Note that we cannot move the allocation of the ArrayBuffer
55  // after BackingStore::Allocate because of the spec.
56  array_buffer->Setup(shared, resizable, nullptr);
57
58  size_t byte_length;
59  size_t max_byte_length = 0;
60  if (!TryNumberToSize(*length, &byte_length) ||
61      byte_length > JSArrayBuffer::kMaxByteLength) {
62    // ToNumber failed.
63    THROW_NEW_ERROR_RETURN_FAILURE(
64        isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
65  }
66
67  std::unique_ptr<BackingStore> backing_store;
68  if (resizable == ResizableFlag::kNotResizable) {
69    backing_store =
70        BackingStore::Allocate(isolate, byte_length, shared, initialized);
71    max_byte_length = byte_length;
72  } else {
73    if (!TryNumberToSize(*max_length, &max_byte_length)) {
74      THROW_NEW_ERROR_RETURN_FAILURE(
75          isolate,
76          NewRangeError(MessageTemplate::kInvalidArrayBufferMaxLength));
77    }
78    if (byte_length > max_byte_length) {
79      THROW_NEW_ERROR_RETURN_FAILURE(
80          isolate,
81          NewRangeError(MessageTemplate::kInvalidArrayBufferMaxLength));
82    }
83
84    size_t page_size, initial_pages, max_pages;
85    MAYBE_RETURN(JSArrayBuffer::GetResizableBackingStorePageConfiguration(
86                     isolate, byte_length, max_byte_length, kThrowOnError,
87                     &page_size, &initial_pages, &max_pages),
88                 ReadOnlyRoots(isolate).exception());
89
90    constexpr bool kIsWasmMemory = false;
91    backing_store = BackingStore::TryAllocateAndPartiallyCommitMemory(
92        isolate, byte_length, max_byte_length, page_size, initial_pages,
93        max_pages, kIsWasmMemory, shared);
94  }
95  if (!backing_store) {
96    // Allocation of backing store failed.
97    THROW_NEW_ERROR_RETURN_FAILURE(
98        isolate, NewRangeError(MessageTemplate::kArrayBufferAllocationFailed));
99  }
100
101  array_buffer->Attach(std::move(backing_store));
102  array_buffer->set_max_byte_length(max_byte_length);
103  return *array_buffer;
104}
105
106}  // namespace
107
108// ES #sec-arraybuffer-constructor
109BUILTIN(ArrayBufferConstructor) {
110  HandleScope scope(isolate);
111  Handle<JSFunction> target = args.target();
112  DCHECK(*target == target->native_context().array_buffer_fun() ||
113         *target == target->native_context().shared_array_buffer_fun());
114  if (args.new_target()->IsUndefined(isolate)) {  // [[Call]]
115    THROW_NEW_ERROR_RETURN_FAILURE(
116        isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
117                              handle(target->shared().Name(), isolate)));
118  }
119  // [[Construct]]
120  Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
121  Handle<Object> length = args.atOrUndefined(isolate, 1);
122
123  Handle<Object> number_length;
124  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_length,
125                                     Object::ToInteger(isolate, length));
126  if (number_length->Number() < 0.0) {
127    THROW_NEW_ERROR_RETURN_FAILURE(
128        isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
129  }
130
131  Handle<Object> number_max_length;
132  if (FLAG_harmony_rab_gsab) {
133    Handle<Object> max_length;
134    Handle<Object> options = args.atOrUndefined(isolate, 2);
135    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
136        isolate, max_length,
137        JSObject::ReadFromOptionsBag(
138            options, isolate->factory()->max_byte_length_string(), isolate));
139
140    if (!max_length->IsUndefined(isolate)) {
141      ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
142          isolate, number_max_length, Object::ToInteger(isolate, max_length));
143    }
144  }
145  return ConstructBuffer(isolate, target, new_target, number_length,
146                         number_max_length, InitializedFlag::kZeroInitialized);
147}
148
149// This is a helper to construct an ArrayBuffer with uinitialized memory.
150// This means the caller must ensure the buffer is totally initialized in
151// all cases, or we will expose uinitialized memory to user code.
152BUILTIN(ArrayBufferConstructor_DoNotInitialize) {
153  HandleScope scope(isolate);
154  Handle<JSFunction> target(isolate->native_context()->array_buffer_fun(),
155                            isolate);
156  Handle<Object> length = args.atOrUndefined(isolate, 1);
157  return ConstructBuffer(isolate, target, target, length, Handle<Object>(),
158                         InitializedFlag::kUninitialized);
159}
160
161static Object SliceHelper(BuiltinArguments args, Isolate* isolate,
162                          const char* kMethodName, bool is_shared) {
163  HandleScope scope(isolate);
164  Handle<Object> start = args.at(1);
165  Handle<Object> end = args.atOrUndefined(isolate, 2);
166
167  // * If Type(O) is not Object, throw a TypeError exception.
168  // * If O does not have an [[ArrayBufferData]] internal slot, throw a
169  //   TypeError exception.
170  CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
171  // * [AB] If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
172  // * [SAB] If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
173  CHECK_SHARED(is_shared, array_buffer, kMethodName);
174
175  // * [AB] If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
176  if (!is_shared && array_buffer->was_detached()) {
177    THROW_NEW_ERROR_RETURN_FAILURE(
178        isolate, NewTypeError(MessageTemplate::kDetachedOperation,
179                              isolate->factory()->NewStringFromAsciiChecked(
180                                  kMethodName)));
181  }
182
183  // * [AB] Let len be O.[[ArrayBufferByteLength]].
184  // * [SAB] Let len be O.[[ArrayBufferByteLength]].
185  double const len = array_buffer->GetByteLength();
186
187  // * Let relativeStart be ? ToInteger(start).
188  Handle<Object> relative_start;
189  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, relative_start,
190                                     Object::ToInteger(isolate, start));
191
192  // * If relativeStart < 0, let first be max((len + relativeStart), 0); else
193  //   let first be min(relativeStart, len).
194  double const first = (relative_start->Number() < 0)
195                           ? std::max(len + relative_start->Number(), 0.0)
196                           : std::min(relative_start->Number(), len);
197
198  // * If end is undefined, let relativeEnd be len; else let relativeEnd be ?
199  //   ToInteger(end).
200  double relative_end;
201  if (end->IsUndefined(isolate)) {
202    relative_end = len;
203  } else {
204    Handle<Object> relative_end_obj;
205    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, relative_end_obj,
206                                       Object::ToInteger(isolate, end));
207    relative_end = relative_end_obj->Number();
208  }
209
210  // * If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let
211  //   final be min(relativeEnd, len).
212  double const final_ = (relative_end < 0) ? std::max(len + relative_end, 0.0)
213                                           : std::min(relative_end, len);
214
215  // * Let newLen be max(final-first, 0).
216  double const new_len = std::max(final_ - first, 0.0);
217  Handle<Object> new_len_obj = isolate->factory()->NewNumber(new_len);
218
219  // * [AB] Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%).
220  // * [SAB] Let ctor be ? SpeciesConstructor(O, %SharedArrayBuffer%).
221  Handle<JSFunction> constructor_fun = is_shared
222                                           ? isolate->shared_array_buffer_fun()
223                                           : isolate->array_buffer_fun();
224  Handle<Object> ctor;
225  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
226      isolate, ctor,
227      Object::SpeciesConstructor(
228          isolate, Handle<JSReceiver>::cast(args.receiver()), constructor_fun));
229
230  // * Let new be ? Construct(ctor, newLen).
231  Handle<JSReceiver> new_;
232  {
233    const int argc = 1;
234
235    base::ScopedVector<Handle<Object>> argv(argc);
236    argv[0] = new_len_obj;
237
238    Handle<Object> new_obj;
239    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
240        isolate, new_obj, Execution::New(isolate, ctor, argc, argv.begin()));
241
242    new_ = Handle<JSReceiver>::cast(new_obj);
243  }
244
245  // * If new does not have an [[ArrayBufferData]] internal slot, throw a
246  //   TypeError exception.
247  if (!new_->IsJSArrayBuffer()) {
248    THROW_NEW_ERROR_RETURN_FAILURE(
249        isolate,
250        NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
251                     isolate->factory()->NewStringFromAsciiChecked(kMethodName),
252                     new_));
253  }
254
255  // * [AB] If IsSharedArrayBuffer(new) is true, throw a TypeError exception.
256  // * [SAB] If IsSharedArrayBuffer(new) is false, throw a TypeError exception.
257  Handle<JSArrayBuffer> new_array_buffer = Handle<JSArrayBuffer>::cast(new_);
258  CHECK_SHARED(is_shared, new_array_buffer, kMethodName);
259
260  // The created ArrayBuffer might or might not be resizable, since the species
261  // constructor might return a non-resizable or a resizable buffer.
262
263  // * [AB] If IsDetachedBuffer(new) is true, throw a TypeError exception.
264  if (!is_shared && new_array_buffer->was_detached()) {
265    THROW_NEW_ERROR_RETURN_FAILURE(
266        isolate, NewTypeError(MessageTemplate::kDetachedOperation,
267                              isolate->factory()->NewStringFromAsciiChecked(
268                                  kMethodName)));
269  }
270
271  // * [AB] If SameValue(new, O) is true, throw a TypeError exception.
272  if (!is_shared && new_->SameValue(*args.receiver())) {
273    THROW_NEW_ERROR_RETURN_FAILURE(
274        isolate, NewTypeError(MessageTemplate::kArrayBufferSpeciesThis));
275  }
276
277  // * [SAB] If new.[[ArrayBufferData]] and O.[[ArrayBufferData]] are the same
278  //         Shared Data Block values, throw a TypeError exception.
279  if (is_shared &&
280      new_array_buffer->backing_store() == array_buffer->backing_store()) {
281    THROW_NEW_ERROR_RETURN_FAILURE(
282        isolate, NewTypeError(MessageTemplate::kSharedArrayBufferSpeciesThis));
283  }
284
285  // * If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception.
286  size_t new_array_buffer_byte_length = new_array_buffer->GetByteLength();
287  if (new_array_buffer_byte_length < new_len) {
288    THROW_NEW_ERROR_RETURN_FAILURE(
289        isolate,
290        NewTypeError(is_shared ? MessageTemplate::kSharedArrayBufferTooShort
291                               : MessageTemplate::kArrayBufferTooShort));
292  }
293
294  // * [AB] NOTE: Side-effects of the above steps may have detached O.
295  // * [AB] If IsDetachedBuffer(O) is true, throw a TypeError exception.
296  if (!is_shared && array_buffer->was_detached()) {
297    THROW_NEW_ERROR_RETURN_FAILURE(
298        isolate, NewTypeError(MessageTemplate::kDetachedOperation,
299                              isolate->factory()->NewStringFromAsciiChecked(
300                                  kMethodName)));
301  }
302
303  // * Let fromBuf be O.[[ArrayBufferData]].
304  // * Let toBuf be new.[[ArrayBufferData]].
305  // * Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
306  size_t first_size = first;
307  size_t new_len_size = new_len;
308  DCHECK(new_array_buffer_byte_length >= new_len_size);
309
310  if (new_len_size != 0) {
311    size_t from_byte_length = array_buffer->GetByteLength();
312    if (V8_UNLIKELY(!is_shared && array_buffer->is_resizable())) {
313      // The above steps might have resized the underlying buffer. In that case,
314      // only copy the still-accessible portion of the underlying data.
315      if (first_size > from_byte_length) {
316        return *new_;  // Nothing to copy.
317      }
318      if (new_len_size > from_byte_length - first_size) {
319        new_len_size = from_byte_length - first_size;
320      }
321    }
322    DCHECK(first_size <= from_byte_length);
323    DCHECK(from_byte_length - first_size >= new_len_size);
324    uint8_t* from_data =
325        reinterpret_cast<uint8_t*>(array_buffer->backing_store()) + first_size;
326    uint8_t* to_data =
327        reinterpret_cast<uint8_t*>(new_array_buffer->backing_store());
328    if (is_shared) {
329      base::Relaxed_Memcpy(reinterpret_cast<base::Atomic8*>(to_data),
330                           reinterpret_cast<base::Atomic8*>(from_data),
331                           new_len_size);
332    } else {
333      CopyBytes(to_data, from_data, new_len_size);
334    }
335  }
336
337  return *new_;
338}
339
340// ES #sec-sharedarraybuffer.prototype.slice
341BUILTIN(SharedArrayBufferPrototypeSlice) {
342  const char* const kMethodName = "SharedArrayBuffer.prototype.slice";
343  return SliceHelper(args, isolate, kMethodName, true);
344}
345
346// ES #sec-arraybuffer.prototype.slice
347// ArrayBuffer.prototype.slice ( start, end )
348BUILTIN(ArrayBufferPrototypeSlice) {
349  const char* const kMethodName = "ArrayBuffer.prototype.slice";
350  return SliceHelper(args, isolate, kMethodName, false);
351}
352
353static Object ResizeHelper(BuiltinArguments args, Isolate* isolate,
354                           const char* kMethodName, bool is_shared) {
355  HandleScope scope(isolate);
356
357  // 1 Let O be the this value.
358  // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
359  CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
360  CHECK_RESIZABLE(true, array_buffer, kMethodName);
361
362  // [RAB] 3. If IsSharedArrayBuffer(O) is true, throw a *TypeError* exception
363  // [GSAB] 3. If IsSharedArrayBuffer(O) is false, throw a *TypeError* exception
364  CHECK_SHARED(is_shared, array_buffer, kMethodName);
365
366  // Let newByteLength to ? ToIntegerOrInfinity(newLength).
367  Handle<Object> new_length = args.at(1);
368  Handle<Object> number_new_byte_length;
369  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_new_byte_length,
370                                     Object::ToInteger(isolate, new_length));
371
372  // [RAB] If IsDetachedBuffer(O) is true, throw a TypeError exception.
373  if (!is_shared && array_buffer->was_detached()) {
374    THROW_NEW_ERROR_RETURN_FAILURE(
375        isolate, NewTypeError(MessageTemplate::kDetachedOperation,
376                              isolate->factory()->NewStringFromAsciiChecked(
377                                  kMethodName)));
378  }
379
380  // [RAB] If newByteLength < 0 or newByteLength >
381  // O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
382
383  // [GSAB] If newByteLength < currentByteLength or newByteLength >
384  // O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
385  size_t new_byte_length;
386  if (!TryNumberToSize(*number_new_byte_length, &new_byte_length)) {
387    THROW_NEW_ERROR_RETURN_FAILURE(
388        isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength,
389                               isolate->factory()->NewStringFromAsciiChecked(
390                                   kMethodName)));
391  }
392
393  if (is_shared && new_byte_length < array_buffer->byte_length()) {
394    // GrowableSharedArrayBuffer is only allowed to grow.
395    THROW_NEW_ERROR_RETURN_FAILURE(
396        isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength,
397                               isolate->factory()->NewStringFromAsciiChecked(
398                                   kMethodName)));
399  }
400
401  if (new_byte_length > array_buffer->max_byte_length()) {
402    THROW_NEW_ERROR_RETURN_FAILURE(
403        isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength,
404                               isolate->factory()->NewStringFromAsciiChecked(
405                                   kMethodName)));
406  }
407
408  size_t page_size = AllocatePageSize();
409  size_t new_committed_pages;
410  bool round_return_value =
411      RoundUpToPageSize(new_byte_length, page_size,
412                        JSArrayBuffer::kMaxByteLength, &new_committed_pages);
413  CHECK(round_return_value);
414
415  // [RAB] Let hostHandled be ? HostResizeArrayBuffer(O, newByteLength).
416  // [GSAB] Let hostHandled be ? HostGrowArrayBuffer(O, newByteLength).
417  // If hostHandled is handled, return undefined.
418
419  // TODO(v8:11111, v8:12746): Wasm integration.
420
421  if (!is_shared) {
422    // [RAB] Let oldBlock be O.[[ArrayBufferData]].
423    // [RAB] Let newBlock be ? CreateByteDataBlock(newByteLength).
424    // [RAB] Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).
425    // [RAB] Perform CopyDataBlockBytes(newBlock, 0, oldBlock, 0, copyLength).
426    // [RAB] NOTE: Neither creation of the new Data Block nor copying from the
427    // old Data Block are observable. Implementations reserve the right to
428    // implement this method as in-place growth or shrinkage.
429    if (array_buffer->GetBackingStore()->ResizeInPlace(
430            isolate, new_byte_length, new_committed_pages * page_size) !=
431        BackingStore::ResizeOrGrowResult::kSuccess) {
432      THROW_NEW_ERROR_RETURN_FAILURE(
433          isolate, NewRangeError(MessageTemplate::kOutOfMemory,
434                                 isolate->factory()->NewStringFromAsciiChecked(
435                                     kMethodName)));
436    }
437    // [RAB] Set O.[[ArrayBufferByteLength]] to newLength.
438    array_buffer->set_byte_length(new_byte_length);
439  } else {
440    // [GSAB] (Detailed description of the algorithm omitted.)
441    auto result = array_buffer->GetBackingStore()->GrowInPlace(
442        isolate, new_byte_length, new_committed_pages * page_size);
443    if (result == BackingStore::ResizeOrGrowResult::kFailure) {
444      THROW_NEW_ERROR_RETURN_FAILURE(
445          isolate, NewRangeError(MessageTemplate::kOutOfMemory,
446                                 isolate->factory()->NewStringFromAsciiChecked(
447                                     kMethodName)));
448    }
449    if (result == BackingStore::ResizeOrGrowResult::kRace) {
450      THROW_NEW_ERROR_RETURN_FAILURE(
451          isolate,
452          NewRangeError(
453              MessageTemplate::kInvalidArrayBufferResizeLength,
454              isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
455    }
456    // Invariant: byte_length for a GSAB is 0 (it needs to be read from the
457    // BackingStore).
458    CHECK_EQ(0, array_buffer->byte_length());
459  }
460  return ReadOnlyRoots(isolate).undefined_value();
461}
462
463// ES #sec-get-sharedarraybuffer.prototype.bytelength
464// get SharedArrayBuffer.prototype.byteLength
465BUILTIN(SharedArrayBufferPrototypeGetByteLength) {
466  const char* const kMethodName = "get SharedArrayBuffer.prototype.byteLength";
467  HandleScope scope(isolate);
468  // 1. Let O be the this value.
469  // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
470  CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
471  // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
472  CHECK_SHARED(true, array_buffer, kMethodName);
473
474  DCHECK_EQ(array_buffer->max_byte_length(),
475            array_buffer->GetBackingStore()->max_byte_length());
476
477  // 4. Let length be ArrayBufferByteLength(O, SeqCst).
478  size_t byte_length = array_buffer->GetByteLength();
479  // 5. Return F(length).
480  return *isolate->factory()->NewNumberFromSize(byte_length);
481}
482
483// ES #sec-arraybuffer.prototype.resize
484// ArrayBuffer.prototype.resize(new_size))
485BUILTIN(ArrayBufferPrototypeResize) {
486  const char* const kMethodName = "ArrayBuffer.prototype.resize";
487  constexpr bool kIsShared = false;
488  return ResizeHelper(args, isolate, kMethodName, kIsShared);
489}
490
491// ES #sec-sharedarraybuffer.prototype.grow
492// SharedArrayBuffer.prototype.grow(new_size))
493BUILTIN(SharedArrayBufferPrototypeGrow) {
494  const char* const kMethodName = "SharedArrayBuffer.prototype.grow";
495  constexpr bool kIsShared = true;
496  return ResizeHelper(args, isolate, kMethodName, kIsShared);
497}
498
499}  // namespace internal
500}  // namespace v8
501