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