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