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