V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
builtins-typed-array-gen.cc
1 // Copyright 2017 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-typed-array-gen.h"
6 
7 #include "src/builtins/builtins-constructor-gen.h"
8 #include "src/builtins/builtins-utils-gen.h"
9 #include "src/builtins/builtins.h"
10 #include "src/builtins/growable-fixed-array-gen.h"
11 #include "src/handles-inl.h"
12 #include "src/heap/factory-inl.h"
13 
14 namespace v8 {
15 namespace internal {
16 
17 using compiler::Node;
18 template <class T>
19 using TNode = compiler::TNode<T>;
20 
21 // This is needed for gc_mole which will compile this file without the full set
22 // of GN defined macros.
23 #ifndef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
24 #define V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP 64
25 #endif
26 
27 // -----------------------------------------------------------------------------
28 // ES6 section 22.2 TypedArray Objects
29 
30 TNode<Map> TypedArrayBuiltinsAssembler::LoadMapForType(
31  TNode<JSTypedArray> array) {
32  TVARIABLE(Map, var_typed_map);
33  TNode<Map> array_map = LoadMap(array);
34  TNode<Int32T> elements_kind = LoadMapElementsKind(array_map);
35  ReadOnlyRoots roots(isolate());
36 
37  DispatchTypedArrayByElementsKind(
38  elements_kind,
39  [&](ElementsKind kind, int size, int typed_array_fun_index) {
40  Handle<Map> map(roots.MapForFixedTypedArray(kind), isolate());
41  var_typed_map = HeapConstant(map);
42  });
43 
44  return var_typed_map.value();
45 }
46 
47 // The byte_offset can be higher than Smi range, in which case to perform the
48 // pointer arithmetic necessary to calculate external_pointer, converting
49 // byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi
50 // on the particular platform. 32 bit platforms are self-limiting, because we
51 // can't allocate an array bigger than our 32-bit arithmetic range anyway. 64
52 // bit platforms could theoretically have an offset up to 2^35 - 1, so we may
53 // need to convert the float heap number to an intptr.
54 TNode<UintPtrT> TypedArrayBuiltinsAssembler::CalculateExternalPointer(
55  TNode<UintPtrT> backing_store, TNode<Number> byte_offset) {
56  return Unsigned(
57  IntPtrAdd(backing_store, ChangeNonnegativeNumberToUintPtr(byte_offset)));
58 }
59 
60 // Setup the TypedArray which is under construction.
61 // - Set the length.
62 // - Set the byte_offset.
63 // - Set the byte_length.
64 // - Set EmbedderFields to 0.
65 void TypedArrayBuiltinsAssembler::SetupTypedArray(TNode<JSTypedArray> holder,
66  TNode<Smi> length,
67  TNode<UintPtrT> byte_offset,
68  TNode<UintPtrT> byte_length) {
69  StoreObjectField(holder, JSTypedArray::kLengthOffset, length);
70  StoreObjectFieldNoWriteBarrier(holder, JSArrayBufferView::kByteOffsetOffset,
71  byte_offset,
72  MachineType::PointerRepresentation());
73  StoreObjectFieldNoWriteBarrier(holder, JSArrayBufferView::kByteLengthOffset,
74  byte_length,
75  MachineType::PointerRepresentation());
76  for (int offset = JSTypedArray::kHeaderSize;
77  offset < JSTypedArray::kSizeWithEmbedderFields; offset += kPointerSize) {
78  StoreObjectField(holder, offset, SmiConstant(0));
79  }
80 }
81 
82 // Attach an off-heap buffer to a TypedArray.
83 void TypedArrayBuiltinsAssembler::AttachBuffer(TNode<JSTypedArray> holder,
84  TNode<JSArrayBuffer> buffer,
85  TNode<Map> map,
86  TNode<Smi> length,
87  TNode<Number> byte_offset) {
88  StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
89 
90  Node* elements = Allocate(FixedTypedArrayBase::kHeaderSize);
91  StoreMapNoWriteBarrier(elements, map);
92  StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
93  StoreObjectFieldNoWriteBarrier(
94  elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0));
95 
96  TNode<UintPtrT> backing_store =
97  LoadObjectField<UintPtrT>(buffer, JSArrayBuffer::kBackingStoreOffset);
98 
99  TNode<UintPtrT> external_pointer =
100  CalculateExternalPointer(backing_store, byte_offset);
101  StoreObjectFieldNoWriteBarrier(
102  elements, FixedTypedArrayBase::kExternalPointerOffset, external_pointer,
103  MachineType::PointerRepresentation());
104 
105  StoreObjectField(holder, JSObject::kElementsOffset, elements);
106 }
107 
108 TF_BUILTIN(TypedArrayInitializeWithBuffer, TypedArrayBuiltinsAssembler) {
109  TNode<JSTypedArray> holder = CAST(Parameter(Descriptor::kHolder));
110  TNode<Smi> length = CAST(Parameter(Descriptor::kLength));
111  TNode<JSArrayBuffer> buffer = CAST(Parameter(Descriptor::kBuffer));
112  TNode<Smi> element_size = CAST(Parameter(Descriptor::kElementSize));
113  TNode<Number> byte_offset = CAST(Parameter(Descriptor::kByteOffset));
114 
115  TNode<Map> fixed_typed_map = LoadMapForType(holder);
116 
117  // SmiMul returns a heap number in case of Smi overflow.
118  TNode<Number> byte_length = SmiMul(length, element_size);
119 
120  SetupTypedArray(holder, length, ChangeNonnegativeNumberToUintPtr(byte_offset),
121  ChangeNonnegativeNumberToUintPtr(byte_length));
122  AttachBuffer(holder, buffer, fixed_typed_map, length, byte_offset);
123  Return(UndefinedConstant());
124 }
125 
126 TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
127  TNode<JSTypedArray> holder = CAST(Parameter(Descriptor::kHolder));
128  TNode<Smi> length = CAST(Parameter(Descriptor::kLength));
129  TNode<Smi> element_size = CAST(Parameter(Descriptor::kElementSize));
130  Node* initialize = Parameter(Descriptor::kInitialize);
131  TNode<JSReceiver> buffer_constructor =
132  CAST(Parameter(Descriptor::kBufferConstructor));
133  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
134 
135  CSA_ASSERT(this, TaggedIsPositiveSmi(length));
136  CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
137  CSA_ASSERT(this, IsBoolean(initialize));
138 
139  TNode<Smi> byte_offset = SmiConstant(0);
140 
141  static const int32_t fta_base_data_offset =
142  FixedTypedArrayBase::kDataOffset - kHeapObjectTag;
143 
144  Label setup_holder(this), allocate_on_heap(this), aligned(this),
145  allocate_elements(this), allocate_off_heap(this),
146  allocate_off_heap_custom_constructor(this),
147  allocate_off_heap_no_init(this), attach_buffer(this), done(this);
148  TVARIABLE(IntPtrT, var_total_size);
149 
150  // SmiMul returns a heap number in case of Smi overflow.
151  TNode<Number> byte_length = SmiMul(length, element_size);
152 
153  TNode<Map> fixed_typed_map = LoadMapForType(holder);
154 
155  // If target and new_target for the buffer differ, allocate off-heap.
156  TNode<JSFunction> default_constructor = CAST(LoadContextElement(
157  LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX));
158  GotoIfNot(WordEqual(buffer_constructor, default_constructor),
159  &allocate_off_heap_custom_constructor);
160 
161  // For buffers with byte_length over the threshold, allocate off-heap.
162  GotoIf(TaggedIsNotSmi(byte_length), &allocate_off_heap);
163  TNode<Smi> smi_byte_length = CAST(byte_length);
164  GotoIf(SmiGreaterThan(smi_byte_length,
165  SmiConstant(V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP)),
166  &allocate_off_heap);
167  TNode<IntPtrT> word_byte_length = SmiToIntPtr(smi_byte_length);
168  Goto(&allocate_on_heap);
169 
170  BIND(&allocate_on_heap);
171  {
172  CSA_ASSERT(this, TaggedIsPositiveSmi(byte_length));
173  // Allocate a new ArrayBuffer and initialize it with empty properties and
174  // elements.
175  Node* native_context = LoadNativeContext(context);
176  Node* map =
177  LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX);
178  Node* empty_fixed_array = LoadRoot(RootIndex::kEmptyFixedArray);
179 
180  Node* buffer = Allocate(JSArrayBuffer::kSizeWithEmbedderFields);
181  StoreMapNoWriteBarrier(buffer, map);
182  StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOrHashOffset,
183  empty_fixed_array);
184  StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
185  empty_fixed_array);
186  // Setup the ArrayBuffer.
187  // - Set BitField to 0.
188  // - Set IsExternal and IsNeuterable bits of BitFieldSlot.
189  // - Set the byte_length field to byte_length.
190  // - Set backing_store to null/Smi(0).
191  // - Set all embedder fields to Smi(0).
192  if (FIELD_SIZE(JSArrayBuffer::kOptionalPaddingOffset)) {
193  DCHECK_EQ(4, FIELD_SIZE(JSArrayBuffer::kOptionalPaddingOffset));
194  StoreObjectFieldNoWriteBarrier(
195  buffer, JSArrayBuffer::kOptionalPaddingOffset, Int32Constant(0),
196  MachineRepresentation::kWord32);
197  }
198  int32_t bitfield_value = (1 << JSArrayBuffer::IsExternalBit::kShift) |
199  (1 << JSArrayBuffer::IsNeuterableBit::kShift);
200  StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
201  Int32Constant(bitfield_value),
202  MachineRepresentation::kWord32);
203 
204  StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
205  SmiToIntPtr(CAST(byte_length)),
206  MachineType::PointerRepresentation());
207  StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
208  SmiConstant(0));
209  for (int offset = JSArrayBuffer::kHeaderSize;
210  offset < JSArrayBuffer::kSizeWithEmbedderFields;
211  offset += kTaggedSize) {
212  StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(0));
213  }
214 
215  StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
216 
217  // Check the alignment.
218  // TODO(ishell): remove <Object, Object>
219  GotoIf(WordEqual<Object, Object>(
220  SmiMod(element_size, SmiConstant(kObjectAlignment)),
221  SmiConstant(0)),
222  &aligned);
223 
224  // Fix alignment if needed.
225  DCHECK_EQ(0, FixedTypedArrayBase::kHeaderSize & kObjectAlignmentMask);
226  TNode<IntPtrT> aligned_header_size =
227  IntPtrConstant(FixedTypedArrayBase::kHeaderSize + kObjectAlignmentMask);
228  TNode<IntPtrT> size = IntPtrAdd(word_byte_length, aligned_header_size);
229  var_total_size = WordAnd(size, IntPtrConstant(~kObjectAlignmentMask));
230  Goto(&allocate_elements);
231  }
232 
233  BIND(&aligned);
234  {
235  TNode<IntPtrT> header_size =
236  IntPtrConstant(FixedTypedArrayBase::kHeaderSize);
237  var_total_size = IntPtrAdd(word_byte_length, header_size);
238  Goto(&allocate_elements);
239  }
240 
241  BIND(&allocate_elements);
242  {
243  // Allocate a FixedTypedArray and set the length, base pointer and external
244  // pointer.
245  CSA_ASSERT(this, IsRegularHeapObjectSize(var_total_size.value()));
246 
247  Node* elements;
248 
249  if (UnalignedLoadSupported(MachineRepresentation::kFloat64) &&
250  UnalignedStoreSupported(MachineRepresentation::kFloat64)) {
251  elements = AllocateInNewSpace(var_total_size.value());
252  } else {
253  elements = AllocateInNewSpace(var_total_size.value(), kDoubleAlignment);
254  }
255 
256  StoreMapNoWriteBarrier(elements, fixed_typed_map);
257  StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
258  StoreObjectFieldNoWriteBarrier(
259  elements, FixedTypedArrayBase::kBasePointerOffset, elements);
260  StoreObjectFieldNoWriteBarrier(elements,
261  FixedTypedArrayBase::kExternalPointerOffset,
262  IntPtrConstant(fta_base_data_offset),
263  MachineType::PointerRepresentation());
264 
265  StoreObjectField(holder, JSObject::kElementsOffset, elements);
266 
267  GotoIf(IsFalse(initialize), &done);
268  // Initialize the backing store by filling it with 0s.
269  Node* backing_store = IntPtrAdd(BitcastTaggedToWord(elements),
270  IntPtrConstant(fta_base_data_offset));
271  // Call out to memset to perform initialization.
272  Node* memset = ExternalConstant(ExternalReference::libc_memset_function());
273  CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
274  MachineType::IntPtr(), MachineType::UintPtr(), memset,
275  backing_store, IntPtrConstant(0), word_byte_length);
276  Goto(&done);
277  }
278 
279  TVARIABLE(JSArrayBuffer, var_buffer);
280 
281  BIND(&allocate_off_heap);
282  {
283  GotoIf(IsFalse(initialize), &allocate_off_heap_no_init);
284  var_buffer = CAST(Construct(context, default_constructor, byte_length));
285  Goto(&attach_buffer);
286  }
287 
288  BIND(&allocate_off_heap_custom_constructor);
289  {
290  var_buffer =
291  CAST(CallStub(CodeFactory::Construct(isolate()), context,
292  default_constructor, buffer_constructor, Int32Constant(1),
293  UndefinedConstant(), byte_length));
294  Goto(&attach_buffer);
295  }
296 
297  BIND(&allocate_off_heap_no_init);
298  {
299  Node* buffer_constructor_noinit = LoadContextElement(
300  LoadNativeContext(context), Context::ARRAY_BUFFER_NOINIT_FUN_INDEX);
301  var_buffer = CAST(CallJS(CodeFactory::Call(isolate()), context,
302  buffer_constructor_noinit, UndefinedConstant(),
303  byte_length));
304  Goto(&attach_buffer);
305  }
306 
307  BIND(&attach_buffer);
308  {
309  AttachBuffer(holder, var_buffer.value(), fixed_typed_map, length,
310  byte_offset);
311  Goto(&done);
312  }
313 
314  BIND(&done);
315  SetupTypedArray(holder, length, ChangeNonnegativeNumberToUintPtr(byte_offset),
316  ChangeNonnegativeNumberToUintPtr(byte_length));
317  Return(UndefinedConstant());
318 }
319 
320 // ES6 #sec-typedarray-length
321 void TypedArrayBuiltinsAssembler::ConstructByLength(TNode<Context> context,
322  TNode<JSTypedArray> holder,
323  TNode<Object> length,
324  TNode<Smi> element_size) {
325  // TODO(7881): support larger-than-smi typed array lengths
326  CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
327 
328  Label invalid_length(this, Label::kDeferred), done(this);
329 
330  TNode<Number> converted_length =
331  ToInteger_Inline(context, length, CodeStubAssembler::kTruncateMinusZero);
332 
333  // The maximum length of a TypedArray is MaxSmi().
334  // Note: this is not per spec, but rather a constraint of our current
335  // representation (which uses Smis).
336  // TODO(7881): support larger-than-smi typed array lengths
337  GotoIf(TaggedIsNotSmi(converted_length), &invalid_length);
338  // The goto above ensures that byte_length is a Smi.
339  TNode<Smi> smi_converted_length = CAST(converted_length);
340  GotoIf(SmiLessThan(smi_converted_length, SmiConstant(0)), &invalid_length);
341 
342  Node* initialize = TrueConstant();
343  TNode<JSFunction> default_constructor = CAST(LoadContextElement(
344  LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX));
345  CallBuiltin(Builtins::kTypedArrayInitialize, context, holder,
346  converted_length, element_size, initialize, default_constructor);
347  Goto(&done);
348 
349  BIND(&invalid_length);
350  {
351  ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
352  converted_length);
353  }
354 
355  BIND(&done);
356 }
357 
358 // ES6 #sec-typedarray-buffer-byteoffset-length
359 void TypedArrayBuiltinsAssembler::ConstructByArrayBuffer(
360  TNode<Context> context, TNode<JSTypedArray> holder,
361  TNode<JSArrayBuffer> buffer, TNode<Object> byte_offset,
362  TNode<Object> length, TNode<Smi> element_size) {
363  CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
364 
365  VARIABLE(new_byte_length, MachineRepresentation::kTagged, SmiConstant(0));
366  VARIABLE(offset, MachineRepresentation::kTagged, SmiConstant(0));
367 
368  Label start_offset_error(this, Label::kDeferred),
369  byte_length_error(this, Label::kDeferred),
370  invalid_offset_error(this, Label::kDeferred);
371  Label offset_is_smi(this), offset_not_smi(this, Label::kDeferred),
372  check_length(this), call_init(this), invalid_length(this),
373  length_undefined(this), length_defined(this), done(this);
374 
375  GotoIf(IsUndefined(byte_offset), &check_length);
376 
377  offset.Bind(ToInteger_Inline(context, byte_offset,
378  CodeStubAssembler::kTruncateMinusZero));
379  Branch(TaggedIsSmi(offset.value()), &offset_is_smi, &offset_not_smi);
380 
381  // Check that the offset is a multiple of the element size.
382  BIND(&offset_is_smi);
383  {
384  TNode<Smi> smi_offset = CAST(offset.value());
385  GotoIf(SmiEqual(smi_offset, SmiConstant(0)), &check_length);
386  GotoIf(SmiLessThan(smi_offset, SmiConstant(0)), &invalid_length);
387  TNode<Number> remainder = SmiMod(smi_offset, element_size);
388  // TODO(ishell): remove <Object, Object>
389  Branch(WordEqual<Object, Object>(remainder, SmiConstant(0)), &check_length,
390  &start_offset_error);
391  }
392  BIND(&offset_not_smi);
393  {
394  GotoIf(IsTrue(CallBuiltin(Builtins::kLessThan, context, offset.value(),
395  SmiConstant(0))),
396  &invalid_length);
397  Node* remainder =
398  CallBuiltin(Builtins::kModulus, context, offset.value(), element_size);
399  // Remainder can be a heap number.
400  Branch(IsTrue(CallBuiltin(Builtins::kEqual, context, remainder,
401  SmiConstant(0))),
402  &check_length, &start_offset_error);
403  }
404 
405  BIND(&check_length);
406  Branch(IsUndefined(length), &length_undefined, &length_defined);
407 
408  BIND(&length_undefined);
409  {
410  ThrowIfArrayBufferIsDetached(context, buffer, "Construct");
411  TNode<Number> buffer_byte_length = ChangeUintPtrToTagged(
412  LoadObjectField<UintPtrT>(buffer, JSArrayBuffer::kByteLengthOffset));
413 
414  Node* remainder = CallBuiltin(Builtins::kModulus, context,
415  buffer_byte_length, element_size);
416  // Remainder can be a heap number.
417  GotoIf(IsFalse(CallBuiltin(Builtins::kEqual, context, remainder,
418  SmiConstant(0))),
419  &byte_length_error);
420 
421  new_byte_length.Bind(CallBuiltin(Builtins::kSubtract, context,
422  buffer_byte_length, offset.value()));
423 
424  Branch(IsTrue(CallBuiltin(Builtins::kLessThan, context,
425  new_byte_length.value(), SmiConstant(0))),
426  &invalid_offset_error, &call_init);
427  }
428 
429  BIND(&length_defined);
430  {
431  TNode<Smi> new_length = ToSmiIndex(length, context, &invalid_length);
432  ThrowIfArrayBufferIsDetached(context, buffer, "Construct");
433  new_byte_length.Bind(SmiMul(new_length, element_size));
434  // Reading the byte length must come after the ToIndex operation, which
435  // could cause the buffer to become detached.
436  TNode<Number> buffer_byte_length = ChangeUintPtrToTagged(
437  LoadObjectField<UintPtrT>(buffer, JSArrayBuffer::kByteLengthOffset));
438 
439  Node* end = CallBuiltin(Builtins::kAdd, context, offset.value(),
440  new_byte_length.value());
441 
442  Branch(IsTrue(CallBuiltin(Builtins::kGreaterThan, context, end,
443  buffer_byte_length)),
444  &invalid_length, &call_init);
445  }
446 
447  BIND(&call_init);
448  {
449  TNode<Object> raw_length = CallBuiltin(
450  Builtins::kDivide, context, new_byte_length.value(), element_size);
451  // Force the result into a Smi, or throw a range error if it doesn't fit.
452  TNode<Smi> new_length = ToSmiIndex(raw_length, context, &invalid_length);
453 
454  CallBuiltin(Builtins::kTypedArrayInitializeWithBuffer, context, holder,
455  new_length, buffer, element_size, offset.value());
456  Goto(&done);
457  }
458 
459  BIND(&invalid_offset_error);
460  { ThrowRangeError(context, MessageTemplate::kInvalidOffset, byte_offset); }
461 
462  BIND(&start_offset_error);
463  {
464  Node* holder_map = LoadMap(holder);
465  Node* problem_string = StringConstant("start offset");
466  CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
467  problem_string);
468 
469  Unreachable();
470  }
471 
472  BIND(&byte_length_error);
473  {
474  Node* holder_map = LoadMap(holder);
475  Node* problem_string = StringConstant("byte length");
476  CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
477  problem_string);
478 
479  Unreachable();
480  }
481 
482  BIND(&invalid_length);
483  {
484  ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength, length);
485  }
486 
487  BIND(&done);
488 }
489 
490 void TypedArrayBuiltinsAssembler::ConstructByTypedArray(
491  TNode<Context> context, TNode<JSTypedArray> holder,
492  TNode<JSTypedArray> typed_array, TNode<Smi> element_size) {
493  CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
494 
495  TNode<JSFunction> const default_constructor = CAST(LoadContextElement(
496  LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX));
497 
498  Label construct(this), if_detached(this), if_notdetached(this),
499  check_for_sab(this), if_buffernotshared(this), check_prototype(this),
500  done(this);
501  TVARIABLE(JSReceiver, buffer_constructor, default_constructor);
502 
503  TNode<JSArrayBuffer> source_buffer = LoadObjectField<JSArrayBuffer>(
504  typed_array, JSArrayBufferView::kBufferOffset);
505  Branch(IsDetachedBuffer(source_buffer), &if_detached, &if_notdetached);
506 
507  // TODO(petermarshall): Throw on detached typedArray.
508  TVARIABLE(Smi, source_length);
509  BIND(&if_detached);
510  source_length = SmiConstant(0);
511  Goto(&check_for_sab);
512 
513  BIND(&if_notdetached);
514  source_length = LoadJSTypedArrayLength(typed_array);
515  Goto(&check_for_sab);
516 
517  // The spec requires that constructing a typed array using a SAB-backed typed
518  // array use the ArrayBuffer constructor, not the species constructor. See
519  // https://tc39.github.io/ecma262/#sec-typedarray-typedarray.
520  BIND(&check_for_sab);
521  TNode<Uint32T> bitfield =
522  LoadObjectField<Uint32T>(source_buffer, JSArrayBuffer::kBitFieldOffset);
523  Branch(IsSetWord32<JSArrayBuffer::IsSharedBit>(bitfield), &construct,
524  &if_buffernotshared);
525 
526  BIND(&if_buffernotshared);
527  {
528  buffer_constructor =
529  SpeciesConstructor(context, source_buffer, default_constructor);
530  // TODO(petermarshall): Throw on detached typedArray.
531  GotoIfNot(IsDetachedBuffer(source_buffer), &construct);
532  source_length = SmiConstant(0);
533  Goto(&construct);
534  }
535 
536  BIND(&construct);
537  {
538  ConstructByArrayLike(context, holder, typed_array, source_length.value(),
539  element_size, buffer_constructor.value());
540  Goto(&done);
541  }
542 
543  BIND(&done);
544 }
545 
546 Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) {
547  CSA_ASSERT(this, IsJSTypedArray(typed_array));
548  Node* elements = LoadElements(typed_array);
549  CSA_ASSERT(this, IsFixedTypedArray(elements));
550  return LoadFixedTypedArrayBackingStore(CAST(elements));
551 }
552 
553 TNode<BoolT> TypedArrayBuiltinsAssembler::ByteLengthIsValid(
554  TNode<Number> byte_length) {
555  Label smi(this), done(this);
556  TVARIABLE(BoolT, is_valid);
557  GotoIf(TaggedIsSmi(byte_length), &smi);
558 
559  TNode<Float64T> float_value = LoadHeapNumberValue(CAST(byte_length));
560  TNode<Float64T> max_byte_length_double =
561  Float64Constant(FixedTypedArrayBase::kMaxByteLength);
562  is_valid = Float64LessThanOrEqual(float_value, max_byte_length_double);
563  Goto(&done);
564 
565  BIND(&smi);
566  TNode<IntPtrT> max_byte_length =
567  IntPtrConstant(FixedTypedArrayBase::kMaxByteLength);
568  is_valid =
569  UintPtrLessThanOrEqual(SmiUntag(CAST(byte_length)), max_byte_length);
570  Goto(&done);
571 
572  BIND(&done);
573  return is_valid.value();
574 }
575 
576 void TypedArrayBuiltinsAssembler::ConstructByArrayLike(
577  TNode<Context> context, TNode<JSTypedArray> holder,
578  TNode<HeapObject> array_like, TNode<Object> initial_length,
579  TNode<Smi> element_size, TNode<JSReceiver> buffer_constructor) {
580  Label invalid_length(this, Label::kDeferred), fill(this), fast_copy(this),
581  detached_check(this), done(this);
582 
583  // The caller has looked up length on array_like, which is observable.
584  TNode<Smi> length = ToSmiLength(initial_length, context, &invalid_length);
585 
586  Node* initialize = FalseConstant();
587  CallBuiltin(Builtins::kTypedArrayInitialize, context, holder, length,
588  element_size, initialize, buffer_constructor);
589 
590  GotoIf(IsJSTypedArray(array_like), &detached_check);
591  Goto(&fill);
592 
593  BIND(&detached_check);
594  ThrowIfArrayBufferViewBufferIsDetached(context, CAST(array_like),
595  "Construct");
596  Goto(&fill);
597 
598  BIND(&fill);
599  GotoIf(SmiEqual(length, SmiConstant(0)), &done);
600  TNode<Int32T> holder_kind = LoadElementsKind(holder);
601  TNode<Int32T> source_kind = LoadElementsKind(array_like);
602  GotoIf(Word32Equal(holder_kind, source_kind), &fast_copy);
603 
604  // Copy using the elements accessor.
605  CallRuntime(Runtime::kTypedArrayCopyElements, context, holder, array_like,
606  length);
607  Goto(&done);
608 
609  BIND(&fast_copy);
610  {
611  Node* holder_data_ptr = LoadDataPtr(holder);
612  Node* source_data_ptr = LoadDataPtr(array_like);
613 
614  // Calculate the byte length. We shouldn't be trying to copy if the typed
615  // array was neutered.
616  CSA_ASSERT(this, SmiNotEqual(length, SmiConstant(0)));
617  CSA_ASSERT(this, Word32Equal(IsDetachedBuffer(LoadObjectField(
618  array_like, JSTypedArray::kBufferOffset)),
619  Int32Constant(0)));
620 
621  TNode<Number> byte_length = SmiMul(length, element_size);
622  CSA_ASSERT(this, ByteLengthIsValid(byte_length));
623  TNode<UintPtrT> byte_length_intptr =
624  ChangeNonnegativeNumberToUintPtr(byte_length);
625  CSA_ASSERT(this, UintPtrLessThanOrEqual(
626  byte_length_intptr,
627  IntPtrConstant(FixedTypedArrayBase::kMaxByteLength)));
628 
629  Node* memcpy = ExternalConstant(ExternalReference::libc_memcpy_function());
630  CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
631  MachineType::Pointer(), MachineType::UintPtr(), memcpy,
632  holder_data_ptr, source_data_ptr, byte_length_intptr);
633  Goto(&done);
634  }
635 
636  BIND(&invalid_length);
637  {
638  ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
639  initial_length);
640  }
641 
642  BIND(&done);
643 }
644 
645 void TypedArrayBuiltinsAssembler::ConstructByIterable(
646  TNode<Context> context, TNode<JSTypedArray> holder,
647  TNode<JSReceiver> iterable, TNode<JSReceiver> iterator_fn,
648  TNode<Smi> element_size) {
649  Label fast_path(this), slow_path(this), done(this);
650  CSA_ASSERT(this, IsCallable(iterator_fn));
651 
652  TNode<JSArray> array_like =
653  CAST(CallBuiltin(Builtins::kIterableToListMayPreserveHoles, context,
654  iterable, iterator_fn));
655  TNode<Object> initial_length = LoadJSArrayLength(array_like);
656 
657  TNode<JSFunction> default_constructor = CAST(LoadContextElement(
658  LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX));
659  ConstructByArrayLike(context, holder, array_like, initial_length,
660  element_size, default_constructor);
661 }
662 
663 TF_BUILTIN(TypedArrayBaseConstructor, TypedArrayBuiltinsAssembler) {
664  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
665  ThrowTypeError(context, MessageTemplate::kConstructAbstractClass,
666  "TypedArray");
667 }
668 
669 // ES #sec-typedarray-constructors
670 TF_BUILTIN(CreateTypedArray, TypedArrayBuiltinsAssembler) {
671  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
672  TNode<JSFunction> target = CAST(Parameter(Descriptor::kTarget));
673  TNode<JSReceiver> new_target = CAST(Parameter(Descriptor::kNewTarget));
674  TNode<Object> arg1 = CAST(Parameter(Descriptor::kArg1));
675  TNode<Object> arg2 = CAST(Parameter(Descriptor::kArg2));
676  TNode<Object> arg3 = CAST(Parameter(Descriptor::kArg3));
677 
678  CSA_ASSERT(this, IsConstructor(target));
679  CSA_ASSERT(this, IsJSReceiver(new_target));
680 
681  Label if_arg1isbuffer(this), if_arg1istypedarray(this),
682  if_arg1isreceiver(this), if_arg1isnumber(this), return_result(this);
683 
684  ConstructorBuiltinsAssembler constructor_assembler(this->state());
685  TNode<JSTypedArray> result = CAST(
686  constructor_assembler.EmitFastNewObject(context, target, new_target));
687  // We need to set the byte_offset / byte_length to some sane values
688  // to keep the heap verifier happy.
689  // TODO(bmeurer): Fix this initialization to not use EmitFastNewObject,
690  // which causes the problem, since it puts Undefined into all slots of
691  // the object even though that doesn't make any sense for these fields.
692  StoreObjectFieldNoWriteBarrier(result, JSTypedArray::kByteOffsetOffset,
693  UintPtrConstant(0),
694  MachineType::PointerRepresentation());
695  StoreObjectFieldNoWriteBarrier(result, JSTypedArray::kByteLengthOffset,
696  UintPtrConstant(0),
697  MachineType::PointerRepresentation());
698 
699  TNode<Smi> element_size =
700  SmiTag(GetTypedArrayElementSize(LoadElementsKind(result)));
701 
702  GotoIf(TaggedIsSmi(arg1), &if_arg1isnumber);
703  TNode<HeapObject> arg1_heap_object = UncheckedCast<HeapObject>(arg1);
704  GotoIf(IsJSArrayBuffer(arg1_heap_object), &if_arg1isbuffer);
705  GotoIf(IsJSTypedArray(arg1_heap_object), &if_arg1istypedarray);
706  GotoIf(IsJSReceiver(arg1_heap_object), &if_arg1isreceiver);
707  Goto(&if_arg1isnumber);
708 
709  // https://tc39.github.io/ecma262/#sec-typedarray-buffer-byteoffset-length
710  BIND(&if_arg1isbuffer);
711  {
712  ConstructByArrayBuffer(context, result, CAST(arg1), arg2, arg3,
713  element_size);
714  Goto(&return_result);
715  }
716 
717  // https://tc39.github.io/ecma262/#sec-typedarray-typedarray
718  BIND(&if_arg1istypedarray);
719  {
720  TNode<JSTypedArray> typed_array = CAST(arg1_heap_object);
721  ConstructByTypedArray(context, result, typed_array, element_size);
722  Goto(&return_result);
723  }
724 
725  // https://tc39.github.io/ecma262/#sec-typedarray-object
726  BIND(&if_arg1isreceiver);
727  {
728  Label if_iteratorundefined(this), if_iteratornotcallable(this);
729  // Get iterator symbol
730  TNode<Object> iteratorFn = CAST(GetMethod(
731  context, arg1_heap_object, isolate()->factory()->iterator_symbol(),
732  &if_iteratorundefined));
733  GotoIf(TaggedIsSmi(iteratorFn), &if_iteratornotcallable);
734  GotoIfNot(IsCallable(CAST(iteratorFn)), &if_iteratornotcallable);
735 
736  ConstructByIterable(context, result, CAST(arg1_heap_object),
737  CAST(iteratorFn), element_size);
738  Goto(&return_result);
739 
740  BIND(&if_iteratorundefined);
741  {
742  TNode<HeapObject> array_like = arg1_heap_object;
743  TNode<Object> initial_length =
744  GetProperty(context, arg1, LengthStringConstant());
745 
746  TNode<JSFunction> default_constructor = CAST(LoadContextElement(
747  LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX));
748  ConstructByArrayLike(context, result, array_like, initial_length,
749  element_size, default_constructor);
750  Goto(&return_result);
751  }
752 
753  BIND(&if_iteratornotcallable);
754  { ThrowTypeError(context, MessageTemplate::kIteratorSymbolNonCallable); }
755  }
756 
757  // The first argument was a number or fell through and is treated as
758  // a number. https://tc39.github.io/ecma262/#sec-typedarray-length
759  BIND(&if_arg1isnumber);
760  {
761  ConstructByLength(context, result, arg1, element_size);
762  Goto(&return_result);
763  }
764 
765  BIND(&return_result);
766  Return(result);
767 }
768 
769 // ES #sec-typedarray-constructors
770 TF_BUILTIN(TypedArrayConstructor, TypedArrayBuiltinsAssembler) {
771  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
772  TNode<JSFunction> target = CAST(Parameter(Descriptor::kJSTarget));
773  TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));
774  Node* argc =
775  ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
776  CodeStubArguments args(this, argc);
777  Node* arg1 = args.GetOptionalArgumentValue(0);
778  Node* arg2 = args.GetOptionalArgumentValue(1);
779  Node* arg3 = args.GetOptionalArgumentValue(2);
780 
781  // If NewTarget is undefined, throw a TypeError exception.
782  // All the TypedArray constructors have this as the first step:
783  // https://tc39.github.io/ecma262/#sec-typedarray-constructors
784  Label throwtypeerror(this, Label::kDeferred);
785  GotoIf(IsUndefined(new_target), &throwtypeerror);
786 
787  Node* result = CallBuiltin(Builtins::kCreateTypedArray, context, target,
788  new_target, arg1, arg2, arg3);
789  args.PopAndReturn(result);
790 
791  BIND(&throwtypeerror);
792  {
793  TNode<String> name =
794  CAST(CallRuntime(Runtime::kGetFunctionName, context, target));
795  ThrowTypeError(context, MessageTemplate::kConstructorNotFunction, name);
796  }
797 }
798 
799 // ES6 #sec-get-%typedarray%.prototype.bytelength
800 TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
801  const char* const kMethodName = "get TypedArray.prototype.byteLength";
802  Node* context = Parameter(Descriptor::kContext);
803  Node* receiver = Parameter(Descriptor::kReceiver);
804 
805  // Check if the {receiver} is actually a JSTypedArray.
806  ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
807 
808  // Default to zero if the {receiver}s buffer was neutered.
809  TNode<JSArrayBuffer> receiver_buffer =
810  LoadJSArrayBufferViewBuffer(CAST(receiver));
811  TNode<UintPtrT> byte_length = Select<UintPtrT>(
812  IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
813  [=] { return LoadJSArrayBufferViewByteLength(CAST(receiver)); });
814  Return(ChangeUintPtrToTagged(byte_length));
815 }
816 
817 // ES6 #sec-get-%typedarray%.prototype.byteoffset
818 TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
819  const char* const kMethodName = "get TypedArray.prototype.byteOffset";
820  Node* context = Parameter(Descriptor::kContext);
821  Node* receiver = Parameter(Descriptor::kReceiver);
822 
823  // Check if the {receiver} is actually a JSTypedArray.
824  ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
825 
826  // Default to zero if the {receiver}s buffer was neutered.
827  TNode<JSArrayBuffer> receiver_buffer =
828  LoadJSArrayBufferViewBuffer(CAST(receiver));
829  TNode<UintPtrT> byte_offset = Select<UintPtrT>(
830  IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
831  [=] { return LoadJSArrayBufferViewByteOffset(CAST(receiver)); });
832  Return(ChangeUintPtrToTagged(byte_offset));
833 }
834 
835 // ES6 #sec-get-%typedarray%.prototype.length
836 TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
837  const char* const kMethodName = "get TypedArray.prototype.length";
838  Node* context = Parameter(Descriptor::kContext);
839  Node* receiver = Parameter(Descriptor::kReceiver);
840 
841  // Check if the {receiver} is actually a JSTypedArray.
842  ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
843 
844  // Default to zero if the {receiver}s buffer was neutered.
845  TNode<JSArrayBuffer> receiver_buffer =
846  LoadJSArrayBufferViewBuffer(CAST(receiver));
847  TNode<Smi> length = Select<Smi>(
848  IsDetachedBuffer(receiver_buffer), [=] { return SmiConstant(0); },
849  [=] { return LoadJSTypedArrayLength(CAST(receiver)); });
850  Return(length);
851 }
852 
853 TNode<Word32T> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
854  TNode<Word32T> kind) {
855  return Word32Or(Word32Equal(kind, Int32Constant(UINT8_ELEMENTS)),
856  Word32Equal(kind, Int32Constant(UINT8_CLAMPED_ELEMENTS)));
857 }
858 
859 TNode<Word32T> TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
860  TNode<Word32T> kind) {
861  return Word32Or(Word32Equal(kind, Int32Constant(BIGINT64_ELEMENTS)),
862  Word32Equal(kind, Int32Constant(BIGUINT64_ELEMENTS)));
863 }
864 
865 TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize(
866  TNode<Word32T> elements_kind) {
867  TVARIABLE(IntPtrT, element_size);
868 
869  DispatchTypedArrayByElementsKind(
870  elements_kind,
871  [&](ElementsKind el_kind, int size, int typed_array_fun_index) {
872  element_size = IntPtrConstant(size);
873  });
874 
875  return element_size.value();
876 }
877 
878 TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
879  TNode<Context> context, TNode<JSTypedArray> exemplar) {
880  TVARIABLE(IntPtrT, context_slot);
881  TNode<Word32T> elements_kind = LoadElementsKind(exemplar);
882 
883  DispatchTypedArrayByElementsKind(
884  elements_kind,
885  [&](ElementsKind el_kind, int size, int typed_array_function_index) {
886  context_slot = IntPtrConstant(typed_array_function_index);
887  });
888 
889  return CAST(
890  LoadContextElement(LoadNativeContext(context), context_slot.value()));
891 }
892 
893 template <class... TArgs>
894 TNode<JSTypedArray> TypedArrayBuiltinsAssembler::TypedArraySpeciesCreate(
895  const char* method_name, TNode<Context> context,
896  TNode<JSTypedArray> exemplar, TArgs... args) {
897  TVARIABLE(JSTypedArray, var_new_typed_array);
898  Label slow(this, Label::kDeferred), done(this);
899 
900  // Let defaultConstructor be the intrinsic object listed in column one of
901  // Table 52 for exemplar.[[TypedArrayName]].
902  TNode<JSFunction> default_constructor =
903  GetDefaultConstructor(context, exemplar);
904 
905  TNode<Map> map = LoadMap(exemplar);
906  GotoIfNot(IsPrototypeTypedArrayPrototype(context, map), &slow);
907  GotoIf(IsTypedArraySpeciesProtectorCellInvalid(), &slow);
908  {
909  const size_t argc = sizeof...(args);
910  static_assert(argc >= 1 && argc <= 3,
911  "TypedArraySpeciesCreate called with unexpected arguments");
912  TNode<Object> arg_list[argc] = {args...};
913  TNode<Object> arg0 = argc < 1 ? UndefinedConstant() : arg_list[0];
914  TNode<Object> arg1 = argc < 2 ? UndefinedConstant() : arg_list[1];
915  TNode<Object> arg2 = argc < 3 ? UndefinedConstant() : arg_list[2];
916  var_new_typed_array = UncheckedCast<JSTypedArray>(
917  CallBuiltin(Builtins::kCreateTypedArray, context, default_constructor,
918  default_constructor, arg0, arg1, arg2));
919 #ifdef DEBUG
920  // It is assumed that the CreateTypedArray builtin does not produce a
921  // typed array that fails ValidateTypedArray.
922  TNode<JSArrayBuffer> buffer =
923  LoadJSArrayBufferViewBuffer(var_new_typed_array.value());
924  CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(buffer)));
925 #endif // DEBUG
926  Goto(&done);
927  }
928  BIND(&slow);
929  {
930  // Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
931  TNode<JSReceiver> constructor =
932  SpeciesConstructor(context, exemplar, default_constructor);
933 
934  // Let newTypedArray be ? Construct(constructor, argumentList).
935  TNode<JSReceiver> new_object = Construct(context, constructor, args...);
936 
937  // Perform ? ValidateTypedArray(newTypedArray).
938  var_new_typed_array = ValidateTypedArray(context, new_object, method_name);
939  Goto(&done);
940  }
941 
942  BIND(&done);
943  return var_new_typed_array.value();
944 }
945 
946 TNode<JSTypedArray>
947 TypedArrayBuiltinsAssembler::TypedArraySpeciesCreateByLength(
948  TNode<Context> context, TNode<JSTypedArray> exemplar, TNode<Smi> len,
949  const char* method_name) {
950  CSA_ASSERT(this, TaggedIsPositiveSmi(len));
951 
952  TNode<JSTypedArray> new_typed_array =
953  TypedArraySpeciesCreate(method_name, context, exemplar, len);
954 
955  ThrowIfLengthLessThan(context, new_typed_array, len);
956  return new_typed_array;
957 }
958 
959 TNode<JSTypedArray> TypedArrayBuiltinsAssembler::TypedArrayCreateByLength(
960  TNode<Context> context, TNode<Object> constructor, TNode<Smi> len,
961  const char* method_name) {
962  CSA_ASSERT(this, TaggedIsPositiveSmi(len));
963 
964  // Let newTypedArray be ? Construct(constructor, argumentList).
965  TNode<Object> new_object = CAST(ConstructJS(CodeFactory::Construct(isolate()),
966  context, constructor, len));
967 
968  // Perform ? ValidateTypedArray(newTypedArray).
969  TNode<JSTypedArray> new_typed_array =
970  ValidateTypedArray(context, new_object, method_name);
971 
972  ThrowIfLengthLessThan(context, new_typed_array, len);
973  return new_typed_array;
974 }
975 
976 void TypedArrayBuiltinsAssembler::ThrowIfLengthLessThan(
977  TNode<Context> context, TNode<JSTypedArray> typed_array,
978  TNode<Smi> min_length) {
979  // If typed_array.[[ArrayLength]] < min_length, throw a TypeError exception.
980  Label if_length_is_not_short(this);
981  TNode<Smi> new_length = LoadJSTypedArrayLength(typed_array);
982  GotoIfNot(SmiLessThan(new_length, min_length), &if_length_is_not_short);
983  ThrowTypeError(context, MessageTemplate::kTypedArrayTooShort);
984 
985  BIND(&if_length_is_not_short);
986 }
987 
988 TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
989  TNode<Context> context, TNode<JSTypedArray> array) {
990  Label call_runtime(this), done(this);
991  TVARIABLE(Object, var_result);
992 
993  TNode<Object> buffer = LoadObjectField(array, JSTypedArray::kBufferOffset);
994  GotoIf(IsDetachedBuffer(buffer), &call_runtime);
995  TNode<UintPtrT> backing_store = LoadObjectField<UintPtrT>(
996  CAST(buffer), JSArrayBuffer::kBackingStoreOffset);
997  GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
998  var_result = buffer;
999  Goto(&done);
1000 
1001  BIND(&call_runtime);
1002  {
1003  var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array);
1004  Goto(&done);
1005  }
1006 
1007  BIND(&done);
1008  return CAST(var_result.value());
1009 }
1010 
1011 TNode<JSTypedArray> TypedArrayBuiltinsAssembler::ValidateTypedArray(
1012  TNode<Context> context, TNode<Object> obj, const char* method_name) {
1013  // If it is not a typed array, throw
1014  ThrowIfNotInstanceType(context, obj, JS_TYPED_ARRAY_TYPE, method_name);
1015 
1016  // If the typed array's buffer is detached, throw
1017  ThrowIfArrayBufferViewBufferIsDetached(context, CAST(obj), method_name);
1018 
1019  return CAST(obj);
1020 }
1021 
1022 void TypedArrayBuiltinsAssembler::SetTypedArraySource(
1023  TNode<Context> context, TNode<JSTypedArray> source,
1024  TNode<JSTypedArray> target, TNode<IntPtrT> offset, Label* call_runtime,
1025  Label* if_source_too_large) {
1026  CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(
1027  LoadObjectField(source, JSTypedArray::kBufferOffset))));
1028  CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(
1029  LoadObjectField(target, JSTypedArray::kBufferOffset))));
1030  CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
1031  CSA_ASSERT(this,
1032  IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
1033 
1034  // Check for possible range errors.
1035 
1036  TNode<IntPtrT> source_length = SmiUntag(LoadJSTypedArrayLength(source));
1037  TNode<IntPtrT> target_length = SmiUntag(LoadJSTypedArrayLength(target));
1038  TNode<IntPtrT> required_target_length = IntPtrAdd(source_length, offset);
1039 
1040  GotoIf(IntPtrGreaterThan(required_target_length, target_length),
1041  if_source_too_large);
1042 
1043  // Grab pointers and byte lengths we need later on.
1044 
1045  TNode<IntPtrT> target_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(target));
1046  TNode<IntPtrT> source_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(source));
1047 
1048  TNode<Word32T> source_el_kind = LoadElementsKind(source);
1049  TNode<Word32T> target_el_kind = LoadElementsKind(target);
1050 
1051  TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind);
1052  TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind);
1053 
1054  // A note on byte lengths: both source- and target byte lengths must be valid,
1055  // i.e. it must be possible to allocate an array of the given length. That
1056  // means we're safe from overflows in the following multiplication.
1057  TNode<IntPtrT> source_byte_length = IntPtrMul(source_length, source_el_size);
1058  CSA_ASSERT(this,
1059  UintPtrGreaterThanOrEqual(source_byte_length, IntPtrConstant(0)));
1060 
1061  Label call_memmove(this), fast_c_call(this), out(this), exception(this);
1062 
1063  // A fast memmove call can be used when the source and target types are are
1064  // the same or either Uint8 or Uint8Clamped.
1065  GotoIf(Word32Equal(source_el_kind, target_el_kind), &call_memmove);
1066  GotoIfNot(IsUint8ElementsKind(source_el_kind), &fast_c_call);
1067  Branch(IsUint8ElementsKind(target_el_kind), &call_memmove, &fast_c_call);
1068 
1069  BIND(&call_memmove);
1070  {
1071  TNode<IntPtrT> target_start =
1072  IntPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size));
1073  CallCMemmove(target_start, source_data_ptr, source_byte_length);
1074  Goto(&out);
1075  }
1076 
1077  BIND(&fast_c_call);
1078  {
1079  CSA_ASSERT(
1080  this, UintPtrGreaterThanOrEqual(
1081  IntPtrMul(target_length, target_el_size), IntPtrConstant(0)));
1082 
1083  GotoIf(Word32NotEqual(IsBigInt64ElementsKind(source_el_kind),
1084  IsBigInt64ElementsKind(target_el_kind)),
1085  &exception);
1086 
1087  TNode<IntPtrT> source_length = SmiUntag(LoadJSTypedArrayLength(source));
1088  CallCCopyTypedArrayElementsToTypedArray(source, target, source_length,
1089  offset);
1090  Goto(&out);
1091  }
1092 
1093  BIND(&exception);
1094  ThrowTypeError(context, MessageTemplate::kBigIntMixedTypes);
1095 
1096  BIND(&out);
1097 }
1098 
1099 void TypedArrayBuiltinsAssembler::SetJSArraySource(
1100  TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> target,
1101  TNode<IntPtrT> offset, Label* call_runtime, Label* if_source_too_large) {
1102  CSA_ASSERT(this, IsFastJSArray(source, context));
1103  CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
1104  CSA_ASSERT(this,
1105  IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
1106 
1107  TNode<IntPtrT> source_length = SmiUntag(LoadFastJSArrayLength(source));
1108  TNode<IntPtrT> target_length = SmiUntag(LoadJSTypedArrayLength(target));
1109 
1110  // Maybe out of bounds?
1111  GotoIf(IntPtrGreaterThan(IntPtrAdd(source_length, offset), target_length),
1112  if_source_too_large);
1113 
1114  // Nothing to do if {source} is empty.
1115  Label out(this), fast_c_call(this);
1116  GotoIf(IntPtrEqual(source_length, IntPtrConstant(0)), &out);
1117 
1118  // Dispatch based on the source elements kind.
1119  {
1120  // These are the supported elements kinds in TryCopyElementsFastNumber.
1121  int32_t values[] = {
1122  PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
1123  HOLEY_DOUBLE_ELEMENTS,
1124  };
1125  Label* labels[] = {
1126  &fast_c_call, &fast_c_call, &fast_c_call, &fast_c_call,
1127  };
1128  STATIC_ASSERT(arraysize(values) == arraysize(labels));
1129 
1130  TNode<Int32T> source_elements_kind = LoadElementsKind(source);
1131  Switch(source_elements_kind, call_runtime, values, labels,
1132  arraysize(values));
1133  }
1134 
1135  BIND(&fast_c_call);
1136  GotoIf(IsBigInt64ElementsKind(LoadElementsKind(target)), call_runtime);
1137  CallCCopyFastNumberJSArrayElementsToTypedArray(context, source, target,
1138  source_length, offset);
1139  Goto(&out);
1140  BIND(&out);
1141 }
1142 
1143 void TypedArrayBuiltinsAssembler::CallCMemmove(TNode<IntPtrT> dest_ptr,
1144  TNode<IntPtrT> src_ptr,
1145  TNode<IntPtrT> byte_length) {
1146  TNode<ExternalReference> memmove =
1147  ExternalConstant(ExternalReference::libc_memmove_function());
1148  CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
1149  MachineType::Pointer(), MachineType::UintPtr(), memmove,
1150  dest_ptr, src_ptr, byte_length);
1151 }
1152 
1153 void TypedArrayBuiltinsAssembler::
1154  CallCCopyFastNumberJSArrayElementsToTypedArray(TNode<Context> context,
1155  TNode<JSArray> source,
1156  TNode<JSTypedArray> dest,
1157  TNode<IntPtrT> source_length,
1158  TNode<IntPtrT> offset) {
1159  CSA_ASSERT(this,
1160  Word32BinaryNot(IsBigInt64ElementsKind(LoadElementsKind(dest))));
1161  TNode<ExternalReference> f = ExternalConstant(
1162  ExternalReference::copy_fast_number_jsarray_elements_to_typed_array());
1163  CallCFunction5(MachineType::AnyTagged(), MachineType::AnyTagged(),
1164  MachineType::AnyTagged(), MachineType::AnyTagged(),
1165  MachineType::UintPtr(), MachineType::UintPtr(), f, context,
1166  source, dest, source_length, offset);
1167 }
1168 
1169 void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
1170  TNode<JSTypedArray> source, TNode<JSTypedArray> dest,
1171  TNode<IntPtrT> source_length, TNode<IntPtrT> offset) {
1172  TNode<ExternalReference> f = ExternalConstant(
1173  ExternalReference::copy_typed_array_elements_to_typed_array());
1174  CallCFunction4(MachineType::AnyTagged(), MachineType::AnyTagged(),
1175  MachineType::AnyTagged(), MachineType::UintPtr(),
1176  MachineType::UintPtr(), f, source, dest, source_length,
1177  offset);
1178 }
1179 
1180 void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsSlice(
1181  TNode<JSTypedArray> source, TNode<JSTypedArray> dest, TNode<IntPtrT> start,
1182  TNode<IntPtrT> end) {
1183  TNode<ExternalReference> f =
1184  ExternalConstant(ExternalReference::copy_typed_array_elements_slice());
1185  CallCFunction4(MachineType::AnyTagged(), MachineType::AnyTagged(),
1186  MachineType::AnyTagged(), MachineType::UintPtr(),
1187  MachineType::UintPtr(), f, source, dest, start, end);
1188 }
1189 
1190 void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
1191  TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function) {
1192  Label next(this), if_unknown_type(this, Label::kDeferred);
1193 
1194  int32_t elements_kinds[] = {
1195 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) TYPE##_ELEMENTS,
1196  TYPED_ARRAYS(TYPED_ARRAY_CASE)
1197 #undef TYPED_ARRAY_CASE
1198  };
1199 
1200 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) Label if_##type##array(this);
1201  TYPED_ARRAYS(TYPED_ARRAY_CASE)
1202 #undef TYPED_ARRAY_CASE
1203 
1204  Label* elements_kind_labels[] = {
1205 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &if_##type##array,
1206  TYPED_ARRAYS(TYPED_ARRAY_CASE)
1207 #undef TYPED_ARRAY_CASE
1208  };
1209  STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels));
1210 
1211  Switch(elements_kind, &if_unknown_type, elements_kinds, elements_kind_labels,
1212  arraysize(elements_kinds));
1213 
1214 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
1215  BIND(&if_##type##array); \
1216  { \
1217  case_function(TYPE##_ELEMENTS, sizeof(ctype), \
1218  Context::TYPE##_ARRAY_FUN_INDEX); \
1219  Goto(&next); \
1220  }
1221  TYPED_ARRAYS(TYPED_ARRAY_CASE)
1222 #undef TYPED_ARRAY_CASE
1223 
1224  BIND(&if_unknown_type);
1225  Unreachable();
1226 
1227  BIND(&next);
1228 }
1229 
1230 // ES #sec-get-%typedarray%.prototype.set
1231 TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
1232  const char* method_name = "%TypedArray%.prototype.set";
1233  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1234  CodeStubArguments args(
1235  this,
1236  ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
1237 
1238  Label if_source_is_typed_array(this), if_source_is_fast_jsarray(this),
1239  if_offset_is_out_of_bounds(this, Label::kDeferred),
1240  if_source_too_large(this, Label::kDeferred),
1241  if_receiver_is_not_typedarray(this, Label::kDeferred);
1242 
1243  // Check the receiver is a typed array.
1244  TNode<Object> receiver = args.GetReceiver();
1245  GotoIf(TaggedIsSmi(receiver), &if_receiver_is_not_typedarray);
1246  GotoIfNot(IsJSTypedArray(CAST(receiver)), &if_receiver_is_not_typedarray);
1247 
1248  // Normalize offset argument (using ToInteger) and handle heap number cases.
1249  TNode<Object> offset = args.GetOptionalArgumentValue(1, SmiConstant(0));
1250  TNode<Number> offset_num =
1251  ToInteger_Inline(context, offset, kTruncateMinusZero);
1252 
1253  // Since ToInteger always returns a Smi if the given value is within Smi
1254  // range, and the only corner case of -0.0 has already been truncated to 0.0,
1255  // we can simply throw unless the offset is a non-negative Smi.
1256  // TODO(jgruber): It's an observable spec violation to throw here if
1257  // {offset_num} is a positive number outside the Smi range. Per spec, we need
1258  // to check for detached buffers and call the observable ToObject/ToLength
1259  // operations first.
1260  GotoIfNot(TaggedIsPositiveSmi(offset_num), &if_offset_is_out_of_bounds);
1261  TNode<Smi> offset_smi = CAST(offset_num);
1262 
1263  // Check the receiver is not neutered.
1264  ThrowIfArrayBufferViewBufferIsDetached(context, CAST(receiver), method_name);
1265 
1266  // Check the source argument is valid and whether a fast path can be taken.
1267  Label call_runtime(this);
1268  TNode<Object> source = args.GetOptionalArgumentValue(0);
1269  GotoIf(TaggedIsSmi(source), &call_runtime);
1270  GotoIf(IsJSTypedArray(CAST(source)), &if_source_is_typed_array);
1271  BranchIfFastJSArray(source, context, &if_source_is_fast_jsarray,
1272  &call_runtime);
1273 
1274  // Fast path for a typed array source argument.
1275  BIND(&if_source_is_typed_array);
1276  {
1277  // Check the source argument is not neutered.
1278  ThrowIfArrayBufferViewBufferIsDetached(context, CAST(source), method_name);
1279 
1280  SetTypedArraySource(context, CAST(source), CAST(receiver),
1281  SmiUntag(offset_smi), &call_runtime,
1282  &if_source_too_large);
1283  args.PopAndReturn(UndefinedConstant());
1284  }
1285 
1286  // Fast path for a fast JSArray source argument.
1287  BIND(&if_source_is_fast_jsarray);
1288  {
1289  SetJSArraySource(context, CAST(source), CAST(receiver),
1290  SmiUntag(offset_smi), &call_runtime, &if_source_too_large);
1291  args.PopAndReturn(UndefinedConstant());
1292  }
1293 
1294  BIND(&call_runtime);
1295  args.PopAndReturn(CallRuntime(Runtime::kTypedArraySet, context, receiver,
1296  source, offset_smi));
1297 
1298  BIND(&if_offset_is_out_of_bounds);
1299  ThrowRangeError(context, MessageTemplate::kTypedArraySetOffsetOutOfBounds);
1300 
1301  BIND(&if_source_too_large);
1302  ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge);
1303 
1304  BIND(&if_receiver_is_not_typedarray);
1305  ThrowTypeError(context, MessageTemplate::kNotTypedArray);
1306 }
1307 
1308 // ES %TypedArray%.prototype.slice
1309 TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
1310  const char* method_name = "%TypedArray%.prototype.slice";
1311  Label call_c(this), call_memmove(this), if_count_is_not_zero(this),
1312  if_bigint_mixed_types(this, Label::kDeferred);
1313 
1314  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1315  CodeStubArguments args(
1316  this,
1317  ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
1318 
1319  TNode<Object> receiver = args.GetReceiver();
1320  TNode<JSTypedArray> source =
1321  ValidateTypedArray(context, receiver, method_name);
1322 
1323  TNode<Smi> source_length = LoadJSTypedArrayLength(source);
1324 
1325  // Convert start offset argument to integer, and calculate relative offset.
1326  TNode<Object> start = args.GetOptionalArgumentValue(0, SmiConstant(0));
1327  TNode<Smi> start_index =
1328  SmiTag(ConvertToRelativeIndex(context, start, SmiUntag(source_length)));
1329 
1330  // Convert end offset argument to integer, and calculate relative offset.
1331  // If end offset is not given or undefined is given, set source_length to
1332  // "end_index".
1333  TNode<Object> end = args.GetOptionalArgumentValue(1, UndefinedConstant());
1334  TNode<Smi> end_index =
1335  Select<Smi>(IsUndefined(end), [=] { return source_length; },
1336  [=] {
1337  return SmiTag(ConvertToRelativeIndex(
1338  context, end, SmiUntag(source_length)));
1339  });
1340 
1341  // Create a result array by invoking TypedArraySpeciesCreate.
1342  TNode<Smi> count = SmiMax(SmiSub(end_index, start_index), SmiConstant(0));
1343  TNode<JSTypedArray> result_array =
1344  TypedArraySpeciesCreateByLength(context, source, count, method_name);
1345 
1346  // If count is zero, return early.
1347  GotoIf(SmiGreaterThan(count, SmiConstant(0)), &if_count_is_not_zero);
1348  args.PopAndReturn(result_array);
1349 
1350  BIND(&if_count_is_not_zero);
1351  // Check the source array is neutered or not. We don't need to check if the
1352  // result array is neutered or not since TypedArraySpeciesCreate checked it.
1353  CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(LoadObjectField(
1354  result_array, JSTypedArray::kBufferOffset))));
1355  TNode<JSArrayBuffer> receiver_buffer =
1356  LoadJSArrayBufferViewBuffer(CAST(receiver));
1357  ThrowIfArrayBufferIsDetached(context, receiver_buffer, method_name);
1358 
1359  // result_array could be a different type from source or share the same
1360  // buffer with the source because of custom species constructor.
1361  // If the types of source and result array are the same and they are not
1362  // sharing the same buffer, use memmove.
1363  TNode<Word32T> source_el_kind = LoadElementsKind(source);
1364  TNode<Word32T> target_el_kind = LoadElementsKind(result_array);
1365  GotoIfNot(Word32Equal(source_el_kind, target_el_kind), &call_c);
1366 
1367  TNode<Object> target_buffer =
1368  LoadObjectField(result_array, JSTypedArray::kBufferOffset);
1369  Branch(WordEqual(receiver_buffer, target_buffer), &call_c, &call_memmove);
1370 
1371  BIND(&call_memmove);
1372  {
1373  GotoIfForceSlowPath(&call_c);
1374 
1375  TNode<IntPtrT> target_data_ptr =
1376  UncheckedCast<IntPtrT>(LoadDataPtr(result_array));
1377  TNode<IntPtrT> source_data_ptr =
1378  UncheckedCast<IntPtrT>(LoadDataPtr(source));
1379 
1380  TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind);
1381  TNode<IntPtrT> source_start_bytes =
1382  IntPtrMul(SmiToIntPtr(start_index), source_el_size);
1383  TNode<IntPtrT> source_start =
1384  IntPtrAdd(source_data_ptr, source_start_bytes);
1385 
1386  TNode<IntPtrT> count_bytes = IntPtrMul(SmiToIntPtr(count), source_el_size);
1387 
1388 #ifdef DEBUG
1389  TNode<UintPtrT> target_byte_length =
1390  LoadJSArrayBufferViewByteLength(result_array);
1391  CSA_ASSERT(this, UintPtrLessThanOrEqual(Unsigned(count_bytes),
1392  target_byte_length));
1393  TNode<UintPtrT> source_byte_length =
1394  LoadJSArrayBufferViewByteLength(source);
1395  TNode<UintPtrT> source_size_in_bytes =
1396  UintPtrSub(source_byte_length, Unsigned(source_start_bytes));
1397  CSA_ASSERT(this, UintPtrLessThanOrEqual(Unsigned(count_bytes),
1398  source_size_in_bytes));
1399 #endif // DEBUG
1400 
1401  CallCMemmove(target_data_ptr, source_start, count_bytes);
1402  args.PopAndReturn(result_array);
1403  }
1404 
1405  BIND(&call_c);
1406  {
1407  GotoIf(Word32NotEqual(IsBigInt64ElementsKind(source_el_kind),
1408  IsBigInt64ElementsKind(target_el_kind)),
1409  &if_bigint_mixed_types);
1410 
1411  CallCCopyTypedArrayElementsSlice(
1412  source, result_array, SmiToIntPtr(start_index), SmiToIntPtr(end_index));
1413  args.PopAndReturn(result_array);
1414  }
1415 
1416  BIND(&if_bigint_mixed_types);
1417  ThrowTypeError(context, MessageTemplate::kBigIntMixedTypes);
1418 }
1419 
1420 // ES %TypedArray%.prototype.subarray
1421 TF_BUILTIN(TypedArrayPrototypeSubArray, TypedArrayBuiltinsAssembler) {
1422  const char* method_name = "%TypedArray%.prototype.subarray";
1423  Label offset_done(this);
1424 
1425  TVARIABLE(Smi, var_begin);
1426  TVARIABLE(Smi, var_end);
1427 
1428  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1429  CodeStubArguments args(
1430  this,
1431  ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
1432 
1433  // 1. Let O be the this value.
1434  // 3. If O does not have a [[TypedArrayName]] internal slot, throw a TypeError
1435  // exception.
1436  TNode<Object> receiver = args.GetReceiver();
1437  ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, method_name);
1438 
1439  TNode<JSTypedArray> source = CAST(receiver);
1440 
1441  // 5. Let buffer be O.[[ViewedArrayBuffer]].
1442  TNode<JSArrayBuffer> buffer = GetBuffer(context, source);
1443  // 6. Let srcLength be O.[[ArrayLength]].
1444  TNode<Smi> source_length = LoadJSTypedArrayLength(source);
1445 
1446  // 7. Let relativeBegin be ? ToInteger(begin).
1447  // 8. If relativeBegin < 0, let beginIndex be max((srcLength + relativeBegin),
1448  // 0); else let beginIndex be min(relativeBegin, srcLength).
1449  TNode<Object> begin = args.GetOptionalArgumentValue(0, SmiConstant(0));
1450  var_begin =
1451  SmiTag(ConvertToRelativeIndex(context, begin, SmiUntag(source_length)));
1452 
1453  TNode<Object> end = args.GetOptionalArgumentValue(1, UndefinedConstant());
1454  // 9. If end is undefined, let relativeEnd be srcLength;
1455  var_end = source_length;
1456  GotoIf(IsUndefined(end), &offset_done);
1457 
1458  // else, let relativeEnd be ? ToInteger(end).
1459  // 10. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd), 0);
1460  // else let endIndex be min(relativeEnd, srcLength).
1461  var_end =
1462  SmiTag(ConvertToRelativeIndex(context, end, SmiUntag(source_length)));
1463  Goto(&offset_done);
1464 
1465  BIND(&offset_done);
1466 
1467  // 11. Let newLength be max(endIndex - beginIndex, 0).
1468  TNode<Smi> new_length =
1469  SmiMax(SmiSub(var_end.value(), var_begin.value()), SmiConstant(0));
1470 
1471  // 12. Let constructorName be the String value of O.[[TypedArrayName]].
1472  // 13. Let elementSize be the Number value of the Element Size value specified
1473  // in Table 52 for constructorName.
1474  TNode<Word32T> element_kind = LoadElementsKind(source);
1475  TNode<IntPtrT> element_size = GetTypedArrayElementSize(element_kind);
1476 
1477  // 14. Let srcByteOffset be O.[[ByteOffset]].
1478  TNode<Number> source_byte_offset =
1479  ChangeUintPtrToTagged(LoadJSArrayBufferViewByteOffset(source));
1480 
1481  // 15. Let beginByteOffset be srcByteOffset + beginIndex × elementSize.
1482  TNode<Number> offset = SmiMul(var_begin.value(), SmiFromIntPtr(element_size));
1483  TNode<Number> begin_byte_offset = NumberAdd(source_byte_offset, offset);
1484 
1485  // 16. Let argumentsList be « buffer, beginByteOffset, newLength ».
1486  // 17. Return ? TypedArraySpeciesCreate(O, argumentsList).
1487  args.PopAndReturn(TypedArraySpeciesCreate(
1488  method_name, context, source, buffer, begin_byte_offset, new_length));
1489 }
1490 
1491 // ES #sec-get-%typedarray%.prototype-@@tostringtag
1492 TF_BUILTIN(TypedArrayPrototypeToStringTag, TypedArrayBuiltinsAssembler) {
1493  Node* receiver = Parameter(Descriptor::kReceiver);
1494  Label if_receiverisheapobject(this), return_undefined(this);
1495  Branch(TaggedIsSmi(receiver), &return_undefined, &if_receiverisheapobject);
1496 
1497  // Dispatch on the elements kind, offset by
1498  // FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND.
1499  size_t const kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
1500  FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
1501  1;
1502 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
1503  Label return_##type##array(this); \
1504  BIND(&return_##type##array); \
1505  Return(StringConstant(#Type "Array"));
1506  TYPED_ARRAYS(TYPED_ARRAY_CASE)
1507 #undef TYPED_ARRAY_CASE
1508  Label* elements_kind_labels[kTypedElementsKindCount] = {
1509 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &return_##type##array,
1510  TYPED_ARRAYS(TYPED_ARRAY_CASE)
1511 #undef TYPED_ARRAY_CASE
1512  };
1513  int32_t elements_kinds[kTypedElementsKindCount] = {
1514 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
1515  TYPE##_ELEMENTS - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
1516  TYPED_ARRAYS(TYPED_ARRAY_CASE)
1517 #undef TYPED_ARRAY_CASE
1518  };
1519 
1520  // We offset the dispatch by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, so
1521  // that this can be turned into a non-sparse table switch for ideal
1522  // performance.
1523  BIND(&if_receiverisheapobject);
1524  Node* elements_kind =
1525  Int32Sub(LoadElementsKind(receiver),
1526  Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND));
1527  Switch(elements_kind, &return_undefined, elements_kinds, elements_kind_labels,
1528  kTypedElementsKindCount);
1529 
1530  BIND(&return_undefined);
1531  Return(UndefinedConstant());
1532 }
1533 
1534 void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
1535  TNode<Context> context, TNode<Object> receiver, const char* method_name,
1536  IterationKind kind) {
1537  Label throw_bad_receiver(this, Label::kDeferred);
1538 
1539  GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
1540  GotoIfNot(IsJSTypedArray(CAST(receiver)), &throw_bad_receiver);
1541 
1542  // Check if the {receiver}'s JSArrayBuffer was neutered.
1543  ThrowIfArrayBufferViewBufferIsDetached(context, CAST(receiver), method_name);
1544 
1545  Return(CreateArrayIterator(context, receiver, kind));
1546 
1547  BIND(&throw_bad_receiver);
1548  ThrowTypeError(context, MessageTemplate::kNotTypedArray, method_name);
1549 }
1550 
1551 // ES #sec-%typedarray%.prototype.values
1552 TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
1553  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1554  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1555  GenerateTypedArrayPrototypeIterationMethod(context, receiver,
1556  "%TypedArray%.prototype.values()",
1557  IterationKind::kValues);
1558 }
1559 
1560 // ES #sec-%typedarray%.prototype.entries
1561 TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
1562  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1563  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1564  GenerateTypedArrayPrototypeIterationMethod(context, receiver,
1565  "%TypedArray%.prototype.entries()",
1566  IterationKind::kEntries);
1567 }
1568 
1569 // ES #sec-%typedarray%.prototype.keys
1570 TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
1571  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1572  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1573  GenerateTypedArrayPrototypeIterationMethod(
1574  context, receiver, "%TypedArray%.prototype.keys()", IterationKind::kKeys);
1575 }
1576 
1577 // ES6 #sec-%typedarray%.of
1578 TF_BUILTIN(TypedArrayOf, TypedArrayBuiltinsAssembler) {
1579  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1580 
1581  // 1. Let len be the actual number of arguments passed to this function.
1582  TNode<IntPtrT> length = ChangeInt32ToIntPtr(
1583  UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
1584  // 2. Let items be the List of arguments passed to this function.
1585  CodeStubArguments args(this, length, nullptr, INTPTR_PARAMETERS,
1586  CodeStubArguments::ReceiverMode::kHasReceiver);
1587 
1588  Label if_not_constructor(this, Label::kDeferred),
1589  if_neutered(this, Label::kDeferred);
1590 
1591  // 3. Let C be the this value.
1592  // 4. If IsConstructor(C) is false, throw a TypeError exception.
1593  TNode<Object> receiver = args.GetReceiver();
1594  GotoIf(TaggedIsSmi(receiver), &if_not_constructor);
1595  GotoIfNot(IsConstructor(CAST(receiver)), &if_not_constructor);
1596 
1597  // 5. Let newObj be ? TypedArrayCreate(C, len).
1598  TNode<JSTypedArray> new_typed_array = TypedArrayCreateByLength(
1599  context, receiver, SmiTag(length), "%TypedArray%.of");
1600 
1601  TNode<Word32T> elements_kind = LoadElementsKind(new_typed_array);
1602 
1603  // 6. Let k be 0.
1604  // 7. Repeat, while k < len
1605  // a. Let kValue be items[k].
1606  // b. Let Pk be ! ToString(k).
1607  // c. Perform ? Set(newObj, Pk, kValue, true).
1608  // d. Increase k by 1.
1609  DispatchTypedArrayByElementsKind(
1610  elements_kind,
1611  [&](ElementsKind kind, int size, int typed_array_fun_index) {
1612  TNode<FixedTypedArrayBase> elements =
1613  CAST(LoadElements(new_typed_array));
1614  BuildFastLoop(
1615  IntPtrConstant(0), length,
1616  [&](Node* index) {
1617  TNode<Object> item = args.AtIndex(index, INTPTR_PARAMETERS);
1618  TNode<IntPtrT> intptr_index = UncheckedCast<IntPtrT>(index);
1619  if (kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS) {
1620  EmitBigTypedArrayElementStore(new_typed_array, elements,
1621  intptr_index, item, context,
1622  &if_neutered);
1623  } else {
1624  Node* value =
1625  PrepareValueForWriteToTypedArray(item, kind, context);
1626 
1627  // ToNumber may execute JavaScript code, which could neuter
1628  // the array's buffer.
1629  Node* buffer = LoadObjectField(new_typed_array,
1630  JSTypedArray::kBufferOffset);
1631  GotoIf(IsDetachedBuffer(buffer), &if_neutered);
1632 
1633  // GC may move backing store in ToNumber, thus load backing
1634  // store everytime in this loop.
1635  TNode<RawPtrT> backing_store =
1636  LoadFixedTypedArrayBackingStore(elements);
1637  StoreElement(backing_store, kind, index, value,
1638  INTPTR_PARAMETERS);
1639  }
1640  },
1641  1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
1642  });
1643 
1644  // 8. Return newObj.
1645  args.PopAndReturn(new_typed_array);
1646 
1647  BIND(&if_not_constructor);
1648  ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
1649 
1650  BIND(&if_neutered);
1651  ThrowTypeError(context, MessageTemplate::kDetachedOperation,
1652  "%TypedArray%.of");
1653 }
1654 
1655 // ES6 #sec-%typedarray%.from
1656 TF_BUILTIN(TypedArrayFrom, TypedArrayBuiltinsAssembler) {
1657  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1658 
1659  Label check_iterator(this), from_array_like(this), fast_path(this),
1660  slow_path(this), create_typed_array(this), check_typedarray(this),
1661  if_not_constructor(this, Label::kDeferred),
1662  if_map_fn_not_callable(this, Label::kDeferred),
1663  if_iterator_fn_not_callable(this, Label::kDeferred),
1664  if_neutered(this, Label::kDeferred);
1665 
1666  CodeStubArguments args(
1667  this,
1668  ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
1669  TNode<Object> source = args.GetOptionalArgumentValue(0);
1670 
1671  // 5. If thisArg is present, let T be thisArg; else let T be undefined.
1672  TNode<Object> this_arg = args.GetOptionalArgumentValue(2);
1673 
1674  // 1. Let C be the this value.
1675  // 2. If IsConstructor(C) is false, throw a TypeError exception.
1676  TNode<Object> receiver = args.GetReceiver();
1677  GotoIf(TaggedIsSmi(receiver), &if_not_constructor);
1678  GotoIfNot(IsConstructor(CAST(receiver)), &if_not_constructor);
1679 
1680  // 3. If mapfn is present and mapfn is not undefined, then
1681  TNode<Object> map_fn = args.GetOptionalArgumentValue(1);
1682  TVARIABLE(BoolT, mapping, Int32FalseConstant());
1683  GotoIf(IsUndefined(map_fn), &check_typedarray);
1684 
1685  // a. If IsCallable(mapfn) is false, throw a TypeError exception.
1686  // b. Let mapping be true.
1687  // 4. Else, let mapping be false.
1688  GotoIf(TaggedIsSmi(map_fn), &if_map_fn_not_callable);
1689  GotoIfNot(IsCallable(CAST(map_fn)), &if_map_fn_not_callable);
1690  mapping = Int32TrueConstant();
1691  Goto(&check_typedarray);
1692 
1693  TVARIABLE(Object, final_source);
1694  TVARIABLE(Smi, final_length);
1695 
1696  // We split up this builtin differently to the way it is written in the spec.
1697  // We already have great code in the elements accessor for copying from a
1698  // JSArray into a TypedArray, so we use that when possible. We only avoid
1699  // calling into the elements accessor when we have a mapping function, because
1700  // we can't handle that. Here, presence of a mapping function is the slow
1701  // path. We also combine the two different loops in the specification
1702  // (starting at 7.e and 13) because they are essentially identical. We also
1703  // save on code-size this way.
1704 
1705  // Get the iterator function
1706  BIND(&check_typedarray);
1707  TNode<Object> iterator_fn =
1708  CAST(GetMethod(context, source, isolate()->factory()->iterator_symbol(),
1709  &from_array_like));
1710  GotoIf(TaggedIsSmi(iterator_fn), &if_iterator_fn_not_callable);
1711 
1712  {
1713  // TypedArrays have iterators, so normally we would go through the
1714  // IterableToList case below, which would convert the TypedArray to a
1715  // JSArray (boxing the values if they won't fit in a Smi).
1716  //
1717  // However, if we can guarantee that the source object has the built-in
1718  // iterator and that the %ArrayIteratorPrototype%.next method has not been
1719  // overridden, then we know the behavior of the iterator: returning the
1720  // values in the TypedArray sequentially from index 0 to length-1.
1721  //
1722  // In this case, we can avoid creating the intermediate array and the
1723  // associated HeapNumbers, and use the fast path in TypedArrayCopyElements
1724  // which uses the same ordering as the default iterator.
1725  //
1726  // Drop through to the default check_iterator behavior if any of these
1727  // checks fail.
1728 
1729  // Check that the source is a TypedArray
1730  GotoIf(TaggedIsSmi(source), &check_iterator);
1731  GotoIfNot(IsJSTypedArray(CAST(source)), &check_iterator);
1732  TNode<JSArrayBuffer> source_buffer =
1733  LoadJSArrayBufferViewBuffer(CAST(source));
1734  GotoIf(IsDetachedBuffer(source_buffer), &check_iterator);
1735 
1736  // Check that the iterator function is Builtins::kTypedArrayPrototypeValues
1737  GotoIfNot(IsJSFunction(CAST(iterator_fn)), &check_iterator);
1738  TNode<SharedFunctionInfo> shared_info = LoadObjectField<SharedFunctionInfo>(
1739  CAST(iterator_fn), JSFunction::kSharedFunctionInfoOffset);
1740  GotoIfNot(
1741  WordEqual(LoadObjectField(shared_info,
1742  SharedFunctionInfo::kFunctionDataOffset),
1743  SmiConstant(Builtins::kTypedArrayPrototypeValues)),
1744  &check_iterator);
1745  // Check that the ArrayIterator prototype's "next" method hasn't been
1746  // overridden
1747  TNode<PropertyCell> protector_cell =
1748  CAST(LoadRoot(RootIndex::kArrayIteratorProtector));
1749  GotoIfNot(
1750  WordEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
1751  SmiConstant(Isolate::kProtectorValid)),
1752  &check_iterator);
1753 
1754  // Source is a TypedArray with unmodified iterator behavior. Use the
1755  // source object directly, taking advantage of the special-case code in
1756  // TypedArrayCopyElements
1757  final_length = LoadJSTypedArrayLength(CAST(source));
1758  final_source = source;
1759  Goto(&create_typed_array);
1760  }
1761 
1762  BIND(&check_iterator);
1763  {
1764  // 6. Let usingIterator be ? GetMethod(source, @@iterator).
1765  GotoIfNot(IsCallable(CAST(iterator_fn)), &if_iterator_fn_not_callable);
1766 
1767  // We are using the iterator.
1768  Label if_length_not_smi(this, Label::kDeferred);
1769  // 7. If usingIterator is not undefined, then
1770  // a. Let values be ? IterableToList(source, usingIterator).
1771  // b. Let len be the number of elements in values.
1772  TNode<JSArray> values = CAST(
1773  CallBuiltin(Builtins::kIterableToList, context, source, iterator_fn));
1774 
1775  // This is not a spec'd limit, so it doesn't particularly matter when we
1776  // throw the range error for typed array length > MaxSmi.
1777  TNode<Object> raw_length = LoadJSArrayLength(values);
1778  GotoIfNot(TaggedIsSmi(raw_length), &if_length_not_smi);
1779 
1780  final_length = CAST(raw_length);
1781  final_source = values;
1782  Goto(&create_typed_array);
1783 
1784  BIND(&if_length_not_smi);
1785  ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
1786  raw_length);
1787  }
1788 
1789  BIND(&from_array_like);
1790  {
1791  // TODO(7881): support larger-than-smi typed array lengths
1792  Label if_length_not_smi(this, Label::kDeferred);
1793  final_source = source;
1794 
1795  // 10. Let len be ? ToLength(? Get(arrayLike, "length")).
1796  TNode<Object> raw_length =
1797  GetProperty(context, final_source.value(), LengthStringConstant());
1798  final_length = ToSmiLength(raw_length, context, &if_length_not_smi);
1799  Goto(&create_typed_array);
1800 
1801  BIND(&if_length_not_smi);
1802  ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
1803  raw_length);
1804  }
1805 
1806  TVARIABLE(JSTypedArray, target_obj);
1807 
1808  BIND(&create_typed_array);
1809  {
1810  // 7c/11. Let targetObj be ? TypedArrayCreate(C, «len»).
1811  target_obj = TypedArrayCreateByLength(
1812  context, receiver, final_length.value(), "%TypedArray%.from");
1813 
1814  Branch(mapping.value(), &slow_path, &fast_path);
1815  }
1816 
1817  BIND(&fast_path);
1818  {
1819  Label done(this);
1820  GotoIf(SmiEqual(final_length.value(), SmiConstant(0)), &done);
1821 
1822  CallRuntime(Runtime::kTypedArrayCopyElements, context, target_obj.value(),
1823  final_source.value(), final_length.value());
1824  Goto(&done);
1825 
1826  BIND(&done);
1827  args.PopAndReturn(target_obj.value());
1828  }
1829 
1830  BIND(&slow_path);
1831  TNode<Word32T> elements_kind = LoadElementsKind(target_obj.value());
1832 
1833  // 7e/13 : Copy the elements
1834  TNode<FixedTypedArrayBase> elements = CAST(LoadElements(target_obj.value()));
1835  BuildFastLoop(
1836  SmiConstant(0), final_length.value(),
1837  [&](Node* index) {
1838  TNode<Object> const k_value =
1839  GetProperty(context, final_source.value(), index);
1840 
1841  TNode<Object> const mapped_value =
1842  CAST(CallJS(CodeFactory::Call(isolate()), context, map_fn, this_arg,
1843  k_value, index));
1844 
1845  TNode<IntPtrT> intptr_index = SmiUntag(index);
1846  DispatchTypedArrayByElementsKind(
1847  elements_kind,
1848  [&](ElementsKind kind, int size, int typed_array_fun_index) {
1849  if (kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS) {
1850  EmitBigTypedArrayElementStore(target_obj.value(), elements,
1851  intptr_index, mapped_value,
1852  context, &if_neutered);
1853  } else {
1854  Node* const final_value = PrepareValueForWriteToTypedArray(
1855  mapped_value, kind, context);
1856 
1857  // ToNumber may execute JavaScript code, which could neuter
1858  // the array's buffer.
1859  Node* buffer = LoadObjectField(target_obj.value(),
1860  JSTypedArray::kBufferOffset);
1861  GotoIf(IsDetachedBuffer(buffer), &if_neutered);
1862 
1863  // GC may move backing store in map_fn, thus load backing
1864  // store in each iteration of this loop.
1865  TNode<RawPtrT> backing_store =
1866  LoadFixedTypedArrayBackingStore(elements);
1867  StoreElement(backing_store, kind, index, final_value,
1868  SMI_PARAMETERS);
1869  }
1870  });
1871  },
1872  1, ParameterMode::SMI_PARAMETERS, IndexAdvanceMode::kPost);
1873 
1874  args.PopAndReturn(target_obj.value());
1875 
1876  BIND(&if_not_constructor);
1877  ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
1878 
1879  BIND(&if_map_fn_not_callable);
1880  ThrowTypeError(context, MessageTemplate::kCalledNonCallable, map_fn);
1881 
1882  BIND(&if_iterator_fn_not_callable);
1883  ThrowTypeError(context, MessageTemplate::kIteratorSymbolNonCallable);
1884 
1885  BIND(&if_neutered);
1886  ThrowTypeError(context, MessageTemplate::kDetachedOperation,
1887  "%TypedArray%.from");
1888 }
1889 
1890 // ES %TypedArray%.prototype.filter
1891 TF_BUILTIN(TypedArrayPrototypeFilter, TypedArrayBuiltinsAssembler) {
1892  const char* method_name = "%TypedArray%.prototype.filter";
1893 
1894  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1895  CodeStubArguments args(
1896  this,
1897  ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
1898 
1899  Label if_callback_not_callable(this, Label::kDeferred),
1900  detached(this, Label::kDeferred);
1901 
1902  // 1. Let O be the this value.
1903  // 2. Perform ? ValidateTypedArray(O).
1904  TNode<Object> receiver = args.GetReceiver();
1905  TNode<JSTypedArray> source =
1906  ValidateTypedArray(context, receiver, method_name);
1907 
1908  // 3. Let len be O.[[ArrayLength]].
1909  TNode<Smi> length = LoadJSTypedArrayLength(source);
1910 
1911  // 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
1912  TNode<Object> callbackfn = args.GetOptionalArgumentValue(0);
1913  GotoIf(TaggedIsSmi(callbackfn), &if_callback_not_callable);
1914  GotoIfNot(IsCallable(CAST(callbackfn)), &if_callback_not_callable);
1915 
1916  // 5. If thisArg is present, let T be thisArg; else let T be undefined.
1917  TNode<Object> this_arg = args.GetOptionalArgumentValue(1);
1918 
1919  TNode<JSArrayBuffer> source_buffer =
1920  LoadObjectField<JSArrayBuffer>(source, JSArrayBufferView::kBufferOffset);
1921  TNode<Word32T> elements_kind = LoadElementsKind(source);
1922  GrowableFixedArray values(state());
1923  VariableList vars(
1924  {values.var_array(), values.var_length(), values.var_capacity()}, zone());
1925 
1926  // 6. Let kept be a new empty List.
1927  // 7. Let k be 0.
1928  // 8. Let captured be 0.
1929  // 9. Repeat, while k < len
1930  BuildFastLoop(
1931  vars, SmiConstant(0), length,
1932  [&](Node* index) {
1933  GotoIf(IsDetachedBuffer(source_buffer), &detached);
1934 
1935  TVARIABLE(Numeric, value);
1936  // a. Let Pk be ! ToString(k).
1937  // b. Let kValue be ? Get(O, Pk).
1938  DispatchTypedArrayByElementsKind(
1939  elements_kind,
1940  [&](ElementsKind kind, int size, int typed_array_fun_index) {
1941  TNode<IntPtrT> backing_store =
1942  UncheckedCast<IntPtrT>(LoadDataPtr(source));
1943  value = CAST(LoadFixedTypedArrayElementAsTagged(
1944  backing_store, index, kind, ParameterMode::SMI_PARAMETERS));
1945  });
1946 
1947  // c. Let selected be ToBoolean(Call(callbackfn, T, kValue, k, O))
1948  Node* selected =
1949  CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg,
1950  value.value(), index, source);
1951 
1952  Label true_continue(this), false_continue(this);
1953  BranchIfToBooleanIsTrue(selected, &true_continue, &false_continue);
1954 
1955  BIND(&true_continue);
1956  // d. If selected is true, then
1957  // i. Append kValue to the end of kept.
1958  // ii. Increase captured by 1.
1959  values.Push(value.value());
1960  Goto(&false_continue);
1961 
1962  BIND(&false_continue);
1963  },
1964  1, ParameterMode::SMI_PARAMETERS, IndexAdvanceMode::kPost);
1965 
1966  TNode<JSArray> values_array = values.ToJSArray(context);
1967  TNode<Smi> captured = LoadFastJSArrayLength(values_array);
1968 
1969  // 10. Let A be ? TypedArraySpeciesCreate(O, captured).
1970  TNode<JSTypedArray> result_array =
1971  TypedArraySpeciesCreateByLength(context, source, captured, method_name);
1972 
1973  // 11. Let n be 0.
1974  // 12. For each element e of kept, do
1975  // a. Perform ! Set(A, ! ToString(n), e, true).
1976  // b. Increment n by 1.
1977  CallRuntime(Runtime::kTypedArrayCopyElements, context, result_array,
1978  values_array, captured);
1979 
1980  // 13. Return A.
1981  args.PopAndReturn(result_array);
1982 
1983  BIND(&if_callback_not_callable);
1984  ThrowTypeError(context, MessageTemplate::kCalledNonCallable, callbackfn);
1985 
1986  BIND(&detached);
1987  ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
1988 }
1989 
1990 #undef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
1991 
1992 } // namespace internal
1993 } // namespace v8
Definition: libplatform.h:13