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