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