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 0 : TNode<Map> TypedArrayBuiltinsAssembler::LoadMapForType(
31 : TNode<JSTypedArray> array) {
32 0 : TVARIABLE(Map, var_typed_map);
33 0 : TNode<Map> array_map = LoadMap(array);
34 0 : TNode<Int32T> elements_kind = LoadMapElementsKind(array_map);
35 0 : ReadOnlyRoots roots(isolate());
36 :
37 : DispatchTypedArrayByElementsKind(
38 : elements_kind,
39 0 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
40 0 : Handle<Map> map(roots.MapForFixedTypedArray(kind), isolate());
41 0 : var_typed_map = HeapConstant(map);
42 0 : });
43 :
44 0 : 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 168 : TNode<UintPtrT> TypedArrayBuiltinsAssembler::CalculateExternalPointer(
55 : TNode<UintPtrT> backing_store, TNode<Number> byte_offset) {
56 : return Unsigned(
57 168 : 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 168 : void TypedArrayBuiltinsAssembler::SetupTypedArray(TNode<JSTypedArray> holder,
66 : TNode<Smi> length,
67 : TNode<UintPtrT> byte_offset,
68 : TNode<UintPtrT> byte_length) {
69 : CSA_ASSERT(this, TaggedIsPositiveSmi(length));
70 168 : StoreObjectField(holder, JSTypedArray::kLengthOffset, length);
71 : StoreObjectFieldNoWriteBarrier(holder, JSArrayBufferView::kByteOffsetOffset,
72 : byte_offset,
73 168 : MachineType::PointerRepresentation());
74 : StoreObjectFieldNoWriteBarrier(holder, JSArrayBufferView::kByteLengthOffset,
75 : byte_length,
76 168 : MachineType::PointerRepresentation());
77 504 : for (int offset = JSTypedArray::kHeaderSize;
78 : offset < JSTypedArray::kSizeWithEmbedderFields; offset += kTaggedSize) {
79 336 : StoreObjectField(holder, offset, SmiConstant(0));
80 : }
81 168 : }
82 :
83 : // Attach an off-heap buffer to a TypedArray.
84 168 : void TypedArrayBuiltinsAssembler::AttachBuffer(TNode<JSTypedArray> holder,
85 : TNode<JSArrayBuffer> buffer,
86 : TNode<Map> map,
87 : TNode<Smi> length,
88 : TNode<Number> byte_offset) {
89 : CSA_ASSERT(this, TaggedIsPositiveSmi(length));
90 168 : StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
91 :
92 168 : Node* elements = Allocate(FixedTypedArrayBase::kHeaderSize);
93 168 : StoreMapNoWriteBarrier(elements, map);
94 168 : StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
95 : StoreObjectFieldNoWriteBarrier(
96 168 : elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0));
97 :
98 : TNode<UintPtrT> backing_store =
99 168 : LoadObjectField<UintPtrT>(buffer, JSArrayBuffer::kBackingStoreOffset);
100 :
101 : TNode<UintPtrT> external_pointer =
102 168 : CalculateExternalPointer(backing_store, byte_offset);
103 : StoreObjectFieldNoWriteBarrier(
104 : elements, FixedTypedArrayBase::kExternalPointerOffset, external_pointer,
105 168 : MachineType::PointerRepresentation());
106 :
107 168 : StoreObjectField(holder, JSObject::kElementsOffset, elements);
108 168 : }
109 :
110 : // Allocate a new ArrayBuffer and initialize it with empty properties and
111 : // elements.
112 112 : TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
113 : TNode<Context> context, TNode<JSTypedArray> holder,
114 : TNode<UintPtrT> byte_length) {
115 112 : TNode<Context> native_context = LoadNativeContext(context);
116 : TNode<Map> map =
117 112 : CAST(LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX));
118 : TNode<FixedArray> empty_fixed_array =
119 112 : CAST(LoadRoot(RootIndex::kEmptyFixedArray));
120 :
121 : TNode<JSArrayBuffer> buffer = UncheckedCast<JSArrayBuffer>(
122 112 : Allocate(JSArrayBuffer::kSizeWithEmbedderFields));
123 112 : StoreMapNoWriteBarrier(buffer, map);
124 : StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOrHashOffset,
125 112 : empty_fixed_array);
126 : StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
127 112 : empty_fixed_array);
128 : // Setup the ArrayBuffer.
129 : // - Set BitField to 0.
130 : // - Set IsExternal and IsDetachable bits of BitFieldSlot.
131 : // - Set the byte_length field to byte_length.
132 : // - Set backing_store to null/Smi(0).
133 : // - Set all embedder fields to Smi(0).
134 : if (FIELD_SIZE(JSArrayBuffer::kOptionalPaddingOffset) != 0) {
135 : DCHECK_EQ(4, FIELD_SIZE(JSArrayBuffer::kOptionalPaddingOffset));
136 : StoreObjectFieldNoWriteBarrier(
137 112 : buffer, JSArrayBuffer::kOptionalPaddingOffset, Int32Constant(0),
138 112 : MachineRepresentation::kWord32);
139 : }
140 : int32_t bitfield_value = (1 << JSArrayBuffer::IsExternalBit::kShift) |
141 112 : (1 << JSArrayBuffer::IsDetachableBit::kShift);
142 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
143 112 : Int32Constant(bitfield_value),
144 112 : MachineRepresentation::kWord32);
145 :
146 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
147 : byte_length,
148 112 : MachineType::PointerRepresentation());
149 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
150 112 : SmiConstant(0));
151 336 : for (int offset = JSArrayBuffer::kHeaderSize;
152 : offset < JSArrayBuffer::kSizeWithEmbedderFields; offset += kTaggedSize) {
153 224 : StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(0));
154 : }
155 :
156 112 : StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
157 112 : return buffer;
158 : }
159 :
160 112 : TNode<FixedTypedArrayBase> TypedArrayBuiltinsAssembler::AllocateOnHeapElements(
161 : TNode<Map> map, TNode<IntPtrT> total_size, TNode<Number> length) {
162 : static const intptr_t fta_base_data_offset =
163 : FixedTypedArrayBase::kDataOffset - kHeapObjectTag;
164 :
165 : CSA_ASSERT(this, IntPtrGreaterThanOrEqual(total_size, IntPtrConstant(0)));
166 :
167 : // Allocate a FixedTypedArray and set the length, base pointer and external
168 : // pointer.
169 : CSA_ASSERT(this, IsRegularHeapObjectSize(total_size));
170 :
171 112 : TNode<Object> elements;
172 :
173 224 : if (UnalignedLoadSupported(MachineRepresentation::kFloat64) &&
174 112 : UnalignedStoreSupported(MachineRepresentation::kFloat64)) {
175 112 : elements = AllocateInNewSpace(total_size);
176 : } else {
177 0 : elements = AllocateInNewSpace(total_size, kDoubleAlignment);
178 : }
179 :
180 112 : StoreMapNoWriteBarrier(elements, map);
181 112 : StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
182 : StoreObjectFieldNoWriteBarrier(
183 112 : elements, FixedTypedArrayBase::kBasePointerOffset, elements);
184 : StoreObjectFieldNoWriteBarrier(elements,
185 : FixedTypedArrayBase::kExternalPointerOffset,
186 112 : IntPtrConstant(fta_base_data_offset),
187 224 : MachineType::PointerRepresentation());
188 112 : return CAST(elements);
189 : }
190 :
191 2184 : Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) {
192 : CSA_ASSERT(this, IsJSTypedArray(typed_array));
193 2184 : Node* elements = LoadElements(typed_array);
194 : CSA_ASSERT(this, IsFixedTypedArray(elements));
195 2184 : return LoadFixedTypedArrayBackingStore(CAST(elements));
196 : }
197 :
198 0 : TNode<BoolT> TypedArrayBuiltinsAssembler::ByteLengthIsValid(
199 : TNode<Number> byte_length) {
200 0 : Label smi(this), done(this);
201 0 : TVARIABLE(BoolT, is_valid);
202 0 : GotoIf(TaggedIsSmi(byte_length), &smi);
203 :
204 0 : TNode<Float64T> float_value = LoadHeapNumberValue(CAST(byte_length));
205 : TNode<Float64T> max_byte_length_double =
206 0 : Float64Constant(FixedTypedArrayBase::kMaxByteLength);
207 0 : is_valid = Float64LessThanOrEqual(float_value, max_byte_length_double);
208 0 : Goto(&done);
209 :
210 0 : BIND(&smi);
211 : TNode<IntPtrT> max_byte_length =
212 0 : IntPtrConstant(FixedTypedArrayBase::kMaxByteLength);
213 0 : is_valid =
214 0 : UintPtrLessThanOrEqual(SmiUntag(CAST(byte_length)), max_byte_length);
215 0 : Goto(&done);
216 :
217 0 : BIND(&done);
218 0 : return is_valid.value();
219 : }
220 :
221 280 : TF_BUILTIN(TypedArrayBaseConstructor, TypedArrayBuiltinsAssembler) {
222 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
223 : ThrowTypeError(context, MessageTemplate::kConstructAbstractClass,
224 56 : "TypedArray");
225 56 : }
226 :
227 : // ES #sec-typedarray-constructors
228 448 : TF_BUILTIN(TypedArrayConstructor, TypedArrayBuiltinsAssembler) {
229 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
230 56 : TNode<JSFunction> target = CAST(Parameter(Descriptor::kJSTarget));
231 56 : TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));
232 : Node* argc =
233 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
234 56 : CodeStubArguments args(this, argc);
235 56 : Node* arg1 = args.GetOptionalArgumentValue(0);
236 56 : Node* arg2 = args.GetOptionalArgumentValue(1);
237 56 : Node* arg3 = args.GetOptionalArgumentValue(2);
238 :
239 : // If NewTarget is undefined, throw a TypeError exception.
240 : // All the TypedArray constructors have this as the first step:
241 : // https://tc39.github.io/ecma262/#sec-typedarray-constructors
242 56 : Label throwtypeerror(this, Label::kDeferred);
243 56 : GotoIf(IsUndefined(new_target), &throwtypeerror);
244 :
245 : Node* result = CallBuiltin(Builtins::kCreateTypedArray, context, target,
246 56 : new_target, arg1, arg2, arg3);
247 56 : args.PopAndReturn(result);
248 :
249 56 : BIND(&throwtypeerror);
250 : {
251 : TNode<String> name =
252 56 : CAST(CallRuntime(Runtime::kGetFunctionName, context, target));
253 56 : ThrowTypeError(context, MessageTemplate::kConstructorNotFunction, name);
254 56 : }
255 56 : }
256 :
257 : // ES6 #sec-get-%typedarray%.prototype.bytelength
258 336 : TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
259 56 : const char* const kMethodName = "get TypedArray.prototype.byteLength";
260 56 : Node* context = Parameter(Descriptor::kContext);
261 56 : Node* receiver = Parameter(Descriptor::kReceiver);
262 :
263 : // Check if the {receiver} is actually a JSTypedArray.
264 56 : ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
265 :
266 : // Default to zero if the {receiver}s buffer was detached.
267 : TNode<JSArrayBuffer> receiver_buffer =
268 56 : LoadJSArrayBufferViewBuffer(CAST(receiver));
269 : TNode<UintPtrT> byte_length = Select<UintPtrT>(
270 56 : IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
271 112 : [=] { return LoadJSArrayBufferViewByteLength(CAST(receiver)); });
272 56 : Return(ChangeUintPtrToTagged(byte_length));
273 56 : }
274 :
275 : // ES6 #sec-get-%typedarray%.prototype.byteoffset
276 336 : TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
277 56 : const char* const kMethodName = "get TypedArray.prototype.byteOffset";
278 56 : Node* context = Parameter(Descriptor::kContext);
279 56 : Node* receiver = Parameter(Descriptor::kReceiver);
280 :
281 : // Check if the {receiver} is actually a JSTypedArray.
282 56 : ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
283 :
284 : // Default to zero if the {receiver}s buffer was detached.
285 : TNode<JSArrayBuffer> receiver_buffer =
286 56 : LoadJSArrayBufferViewBuffer(CAST(receiver));
287 : TNode<UintPtrT> byte_offset = Select<UintPtrT>(
288 56 : IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
289 112 : [=] { return LoadJSArrayBufferViewByteOffset(CAST(receiver)); });
290 56 : Return(ChangeUintPtrToTagged(byte_offset));
291 56 : }
292 :
293 : // ES6 #sec-get-%typedarray%.prototype.length
294 336 : TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
295 56 : const char* const kMethodName = "get TypedArray.prototype.length";
296 56 : Node* context = Parameter(Descriptor::kContext);
297 56 : Node* receiver = Parameter(Descriptor::kReceiver);
298 :
299 : // Check if the {receiver} is actually a JSTypedArray.
300 56 : ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
301 :
302 : // Default to zero if the {receiver}s buffer was detached.
303 : TNode<JSArrayBuffer> receiver_buffer =
304 56 : LoadJSArrayBufferViewBuffer(CAST(receiver));
305 : TNode<Smi> length = Select<Smi>(
306 56 : IsDetachedBuffer(receiver_buffer), [=] { return SmiConstant(0); },
307 112 : [=] { return LoadJSTypedArrayLength(CAST(receiver)); });
308 56 : Return(length);
309 56 : }
310 :
311 112 : TNode<Word32T> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
312 : TNode<Word32T> kind) {
313 224 : return Word32Or(Word32Equal(kind, Int32Constant(UINT8_ELEMENTS)),
314 336 : Word32Equal(kind, Int32Constant(UINT8_CLAMPED_ELEMENTS)));
315 : }
316 :
317 280 : TNode<Word32T> TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
318 : TNode<Word32T> kind) {
319 560 : return Word32Or(Word32Equal(kind, Int32Constant(BIGINT64_ELEMENTS)),
320 840 : Word32Equal(kind, Int32Constant(BIGUINT64_ELEMENTS)));
321 : }
322 :
323 224 : TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize(
324 : TNode<Word32T> elements_kind) {
325 224 : TVARIABLE(IntPtrT, element_size);
326 :
327 : DispatchTypedArrayByElementsKind(
328 : elements_kind,
329 2464 : [&](ElementsKind el_kind, int size, int typed_array_fun_index) {
330 2464 : element_size = IntPtrConstant(size);
331 2688 : });
332 :
333 224 : return element_size.value();
334 : }
335 :
336 : TypedArrayBuiltinsFromDSLAssembler::TypedArrayElementsInfo
337 56 : TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
338 : TNode<JSTypedArray> typed_array) {
339 56 : TNode<Int32T> elements_kind = LoadElementsKind(typed_array);
340 56 : TVARIABLE(Smi, var_element_size);
341 112 : TVARIABLE(Map, var_map);
342 56 : ReadOnlyRoots roots(isolate());
343 :
344 : DispatchTypedArrayByElementsKind(
345 : elements_kind,
346 616 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
347 : DCHECK_GT(size, 0);
348 616 : var_element_size = SmiConstant(size);
349 :
350 616 : Handle<Map> map(roots.MapForFixedTypedArray(kind), isolate());
351 616 : var_map = HeapConstant(map);
352 672 : });
353 :
354 : return TypedArrayBuiltinsFromDSLAssembler::TypedArrayElementsInfo{
355 112 : var_element_size.value(), var_map.value(), elements_kind};
356 : }
357 :
358 224 : TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
359 : TNode<Context> context, TNode<JSTypedArray> exemplar) {
360 224 : TVARIABLE(IntPtrT, context_slot);
361 224 : TNode<Word32T> elements_kind = LoadElementsKind(exemplar);
362 :
363 : DispatchTypedArrayByElementsKind(
364 : elements_kind,
365 2464 : [&](ElementsKind el_kind, int size, int typed_array_function_index) {
366 2464 : context_slot = IntPtrConstant(typed_array_function_index);
367 2688 : });
368 :
369 448 : return CAST(
370 224 : LoadContextElement(LoadNativeContext(context), context_slot.value()));
371 : }
372 :
373 : template <class... TArgs>
374 224 : TNode<JSTypedArray> TypedArrayBuiltinsAssembler::TypedArraySpeciesCreate(
375 : const char* method_name, TNode<Context> context,
376 : TNode<JSTypedArray> exemplar, TArgs... args) {
377 224 : TVARIABLE(JSTypedArray, var_new_typed_array);
378 448 : Label slow(this, Label::kDeferred), done(this);
379 :
380 : // Let defaultConstructor be the intrinsic object listed in column one of
381 : // Table 52 for exemplar.[[TypedArrayName]].
382 : TNode<JSFunction> default_constructor =
383 224 : GetDefaultConstructor(context, exemplar);
384 :
385 224 : TNode<Map> map = LoadMap(exemplar);
386 224 : GotoIfNot(IsPrototypeTypedArrayPrototype(context, map), &slow);
387 224 : GotoIf(IsTypedArraySpeciesProtectorCellInvalid(), &slow);
388 : {
389 224 : const size_t argc = sizeof...(args);
390 : static_assert(argc >= 1 && argc <= 3,
391 : "TypedArraySpeciesCreate called with unexpected arguments");
392 224 : TNode<Object> arg_list[argc] = {args...};
393 224 : TNode<Object> arg0 = argc < 1 ? UndefinedConstant() : arg_list[0];
394 224 : TNode<Object> arg1 = argc < 2 ? UndefinedConstant() : arg_list[1];
395 224 : TNode<Object> arg2 = argc < 3 ? UndefinedConstant() : arg_list[2];
396 224 : var_new_typed_array = UncheckedCast<JSTypedArray>(
397 : CallBuiltin(Builtins::kCreateTypedArray, context, default_constructor,
398 : default_constructor, arg0, arg1, arg2));
399 : #ifdef DEBUG
400 : // It is assumed that the CreateTypedArray builtin does not produce a
401 : // typed array that fails ValidateTypedArray.
402 : TNode<JSArrayBuffer> buffer =
403 : LoadJSArrayBufferViewBuffer(var_new_typed_array.value());
404 : CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(buffer)));
405 : #endif // DEBUG
406 224 : Goto(&done);
407 : }
408 224 : BIND(&slow);
409 : {
410 : // Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
411 : TNode<JSReceiver> constructor =
412 224 : SpeciesConstructor(context, exemplar, default_constructor);
413 :
414 : // Let newTypedArray be ? Construct(constructor, argumentList).
415 224 : TNode<JSReceiver> new_object = Construct(context, constructor, args...);
416 :
417 : // Perform ? ValidateTypedArray(newTypedArray).
418 224 : var_new_typed_array = ValidateTypedArray(context, new_object, method_name);
419 224 : Goto(&done);
420 : }
421 :
422 224 : BIND(&done);
423 448 : return var_new_typed_array.value();
424 : }
425 :
426 : TNode<JSTypedArray>
427 168 : TypedArrayBuiltinsAssembler::TypedArraySpeciesCreateByLength(
428 : TNode<Context> context, TNode<JSTypedArray> exemplar, TNode<Smi> len,
429 : const char* method_name) {
430 : CSA_ASSERT(this, TaggedIsPositiveSmi(len));
431 :
432 : TNode<JSTypedArray> new_typed_array =
433 168 : TypedArraySpeciesCreate(method_name, context, exemplar, len);
434 :
435 168 : ThrowIfLengthLessThan(context, new_typed_array, len);
436 168 : return new_typed_array;
437 : }
438 :
439 112 : TNode<JSTypedArray> TypedArrayBuiltinsAssembler::TypedArrayCreateByLength(
440 : TNode<Context> context, TNode<Object> constructor, TNode<Smi> len,
441 : const char* method_name) {
442 : CSA_ASSERT(this, TaggedIsPositiveSmi(len));
443 :
444 : // Let newTypedArray be ? Construct(constructor, argumentList).
445 112 : TNode<Object> new_object = CAST(ConstructJS(CodeFactory::Construct(isolate()),
446 : context, constructor, len));
447 :
448 : // Perform ? ValidateTypedArray(newTypedArray).
449 : TNode<JSTypedArray> new_typed_array =
450 112 : ValidateTypedArray(context, new_object, method_name);
451 :
452 112 : ThrowIfLengthLessThan(context, new_typed_array, len);
453 112 : return new_typed_array;
454 : }
455 :
456 280 : void TypedArrayBuiltinsAssembler::ThrowIfLengthLessThan(
457 : TNode<Context> context, TNode<JSTypedArray> typed_array,
458 : TNode<Smi> min_length) {
459 : // If typed_array.[[ArrayLength]] < min_length, throw a TypeError exception.
460 280 : Label if_length_is_not_short(this);
461 280 : TNode<Smi> new_length = LoadJSTypedArrayLength(typed_array);
462 280 : GotoIfNot(SmiLessThan(new_length, min_length), &if_length_is_not_short);
463 280 : ThrowTypeError(context, MessageTemplate::kTypedArrayTooShort);
464 :
465 280 : BIND(&if_length_is_not_short);
466 280 : }
467 :
468 56 : TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
469 : TNode<Context> context, TNode<JSTypedArray> array) {
470 112 : Label call_runtime(this), done(this);
471 112 : TVARIABLE(Object, var_result);
472 :
473 56 : TNode<Object> buffer = LoadObjectField(array, JSTypedArray::kBufferOffset);
474 56 : GotoIf(IsDetachedBuffer(buffer), &call_runtime);
475 : TNode<UintPtrT> backing_store = LoadObjectField<UintPtrT>(
476 56 : CAST(buffer), JSArrayBuffer::kBackingStoreOffset);
477 56 : GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
478 56 : var_result = buffer;
479 56 : Goto(&done);
480 :
481 56 : BIND(&call_runtime);
482 : {
483 56 : var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array);
484 56 : Goto(&done);
485 : }
486 :
487 56 : BIND(&done);
488 112 : return CAST(var_result.value());
489 : }
490 :
491 616 : TNode<JSTypedArray> TypedArrayBuiltinsAssembler::ValidateTypedArray(
492 : TNode<Context> context, TNode<Object> obj, const char* method_name) {
493 : // If it is not a typed array, throw
494 616 : ThrowIfNotInstanceType(context, obj, JS_TYPED_ARRAY_TYPE, method_name);
495 :
496 : // If the typed array's buffer is detached, throw
497 616 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(obj), method_name);
498 :
499 616 : return CAST(obj);
500 : }
501 :
502 56 : void TypedArrayBuiltinsAssembler::SetTypedArraySource(
503 : TNode<Context> context, TNode<JSTypedArray> source,
504 : TNode<JSTypedArray> target, TNode<IntPtrT> offset, Label* call_runtime,
505 : Label* if_source_too_large) {
506 : CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(
507 : LoadObjectField(source, JSTypedArray::kBufferOffset))));
508 : CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(
509 : LoadObjectField(target, JSTypedArray::kBufferOffset))));
510 : CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
511 : CSA_ASSERT(this,
512 : IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
513 :
514 : // Check for possible range errors.
515 :
516 56 : TNode<IntPtrT> source_length = SmiUntag(LoadJSTypedArrayLength(source));
517 56 : TNode<IntPtrT> target_length = SmiUntag(LoadJSTypedArrayLength(target));
518 56 : TNode<IntPtrT> required_target_length = IntPtrAdd(source_length, offset);
519 :
520 112 : GotoIf(IntPtrGreaterThan(required_target_length, target_length),
521 56 : if_source_too_large);
522 :
523 : // Grab pointers and byte lengths we need later on.
524 :
525 56 : TNode<IntPtrT> target_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(target));
526 56 : TNode<IntPtrT> source_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(source));
527 :
528 56 : TNode<Word32T> source_el_kind = LoadElementsKind(source);
529 56 : TNode<Word32T> target_el_kind = LoadElementsKind(target);
530 :
531 56 : TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind);
532 56 : TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind);
533 :
534 : // A note on byte lengths: both source- and target byte lengths must be valid,
535 : // i.e. it must be possible to allocate an array of the given length. That
536 : // means we're safe from overflows in the following multiplication.
537 56 : TNode<IntPtrT> source_byte_length = IntPtrMul(source_length, source_el_size);
538 : CSA_ASSERT(this,
539 : UintPtrGreaterThanOrEqual(source_byte_length, IntPtrConstant(0)));
540 :
541 112 : Label call_memmove(this), fast_c_call(this), out(this), exception(this);
542 :
543 : // A fast memmove call can be used when the source and target types are are
544 : // the same or either Uint8 or Uint8Clamped.
545 56 : GotoIf(Word32Equal(source_el_kind, target_el_kind), &call_memmove);
546 56 : GotoIfNot(IsUint8ElementsKind(source_el_kind), &fast_c_call);
547 56 : Branch(IsUint8ElementsKind(target_el_kind), &call_memmove, &fast_c_call);
548 :
549 56 : BIND(&call_memmove);
550 : {
551 : TNode<IntPtrT> target_start =
552 56 : IntPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size));
553 56 : CallCMemmove(target_start, source_data_ptr, source_byte_length);
554 56 : Goto(&out);
555 : }
556 :
557 56 : BIND(&fast_c_call);
558 : {
559 : CSA_ASSERT(
560 : this, UintPtrGreaterThanOrEqual(
561 : IntPtrMul(target_length, target_el_size), IntPtrConstant(0)));
562 :
563 : GotoIf(Word32NotEqual(IsBigInt64ElementsKind(source_el_kind),
564 112 : IsBigInt64ElementsKind(target_el_kind)),
565 56 : &exception);
566 :
567 56 : TNode<IntPtrT> source_length = SmiUntag(LoadJSTypedArrayLength(source));
568 : CallCCopyTypedArrayElementsToTypedArray(source, target, source_length,
569 56 : offset);
570 56 : Goto(&out);
571 : }
572 :
573 56 : BIND(&exception);
574 56 : ThrowTypeError(context, MessageTemplate::kBigIntMixedTypes);
575 :
576 112 : BIND(&out);
577 56 : }
578 :
579 56 : void TypedArrayBuiltinsAssembler::SetJSArraySource(
580 : TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> target,
581 : TNode<IntPtrT> offset, Label* call_runtime, Label* if_source_too_large) {
582 : CSA_ASSERT(this, IsFastJSArray(source, context));
583 : CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
584 : CSA_ASSERT(this,
585 : IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
586 :
587 56 : TNode<IntPtrT> source_length = SmiUntag(LoadFastJSArrayLength(source));
588 56 : TNode<IntPtrT> target_length = SmiUntag(LoadJSTypedArrayLength(target));
589 :
590 : // Maybe out of bounds?
591 112 : GotoIf(IntPtrGreaterThan(IntPtrAdd(source_length, offset), target_length),
592 56 : if_source_too_large);
593 :
594 : // Nothing to do if {source} is empty.
595 112 : Label out(this), fast_c_call(this);
596 56 : GotoIf(IntPtrEqual(source_length, IntPtrConstant(0)), &out);
597 :
598 : // Dispatch based on the source elements kind.
599 : {
600 : // These are the supported elements kinds in TryCopyElementsFastNumber.
601 : int32_t values[] = {
602 : PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
603 : HOLEY_DOUBLE_ELEMENTS,
604 56 : };
605 : Label* labels[] = {
606 : &fast_c_call, &fast_c_call, &fast_c_call, &fast_c_call,
607 56 : };
608 : STATIC_ASSERT(arraysize(values) == arraysize(labels));
609 :
610 56 : TNode<Int32T> source_elements_kind = LoadElementsKind(source);
611 : Switch(source_elements_kind, call_runtime, values, labels,
612 56 : arraysize(values));
613 : }
614 :
615 56 : BIND(&fast_c_call);
616 56 : GotoIf(IsBigInt64ElementsKind(LoadElementsKind(target)), call_runtime);
617 : CallCCopyFastNumberJSArrayElementsToTypedArray(context, source, target,
618 56 : source_length, offset);
619 56 : Goto(&out);
620 112 : BIND(&out);
621 56 : }
622 :
623 112 : void TypedArrayBuiltinsAssembler::CallCMemmove(TNode<IntPtrT> dest_ptr,
624 : TNode<IntPtrT> src_ptr,
625 : TNode<IntPtrT> byte_length) {
626 : TNode<ExternalReference> memmove =
627 112 : ExternalConstant(ExternalReference::libc_memmove_function());
628 : CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
629 : MachineType::Pointer(), MachineType::UintPtr(), memmove,
630 112 : dest_ptr, src_ptr, byte_length);
631 112 : }
632 :
633 56 : void TypedArrayBuiltinsAssembler::CallCMemcpy(TNode<RawPtrT> dest_ptr,
634 : TNode<RawPtrT> src_ptr,
635 : TNode<UintPtrT> byte_length) {
636 : TNode<ExternalReference> memcpy =
637 56 : ExternalConstant(ExternalReference::libc_memcpy_function());
638 : CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
639 : MachineType::Pointer(), MachineType::UintPtr(), memcpy,
640 56 : dest_ptr, src_ptr, byte_length);
641 56 : }
642 :
643 56 : void TypedArrayBuiltinsAssembler::CallCMemset(TNode<RawPtrT> dest_ptr,
644 : TNode<IntPtrT> value,
645 : TNode<UintPtrT> length) {
646 : TNode<ExternalReference> memset =
647 56 : ExternalConstant(ExternalReference::libc_memset_function());
648 : CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
649 : MachineType::IntPtr(), MachineType::UintPtr(), memset,
650 56 : dest_ptr, value, length);
651 56 : }
652 :
653 56 : void TypedArrayBuiltinsAssembler::
654 : CallCCopyFastNumberJSArrayElementsToTypedArray(TNode<Context> context,
655 : TNode<JSArray> source,
656 : TNode<JSTypedArray> dest,
657 : TNode<IntPtrT> source_length,
658 : TNode<IntPtrT> offset) {
659 : CSA_ASSERT(this,
660 : Word32BinaryNot(IsBigInt64ElementsKind(LoadElementsKind(dest))));
661 : TNode<ExternalReference> f = ExternalConstant(
662 56 : ExternalReference::copy_fast_number_jsarray_elements_to_typed_array());
663 : CallCFunction5(MachineType::AnyTagged(), MachineType::AnyTagged(),
664 : MachineType::AnyTagged(), MachineType::AnyTagged(),
665 : MachineType::UintPtr(), MachineType::UintPtr(), f, context,
666 56 : source, dest, source_length, offset);
667 56 : }
668 :
669 56 : void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
670 : TNode<JSTypedArray> source, TNode<JSTypedArray> dest,
671 : TNode<IntPtrT> source_length, TNode<IntPtrT> offset) {
672 : TNode<ExternalReference> f = ExternalConstant(
673 56 : ExternalReference::copy_typed_array_elements_to_typed_array());
674 : CallCFunction4(MachineType::AnyTagged(), MachineType::AnyTagged(),
675 : MachineType::AnyTagged(), MachineType::UintPtr(),
676 : MachineType::UintPtr(), f, source, dest, source_length,
677 56 : offset);
678 56 : }
679 :
680 56 : void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsSlice(
681 : TNode<JSTypedArray> source, TNode<JSTypedArray> dest, TNode<IntPtrT> start,
682 : TNode<IntPtrT> end) {
683 : TNode<ExternalReference> f =
684 56 : ExternalConstant(ExternalReference::copy_typed_array_elements_slice());
685 : CallCFunction4(MachineType::AnyTagged(), MachineType::AnyTagged(),
686 : MachineType::AnyTagged(), MachineType::UintPtr(),
687 56 : MachineType::UintPtr(), f, source, dest, start, end);
688 56 : }
689 :
690 672 : void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
691 : TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function) {
692 1344 : Label next(this), if_unknown_type(this, Label::kDeferred);
693 :
694 : int32_t elements_kinds[] = {
695 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) TYPE##_ELEMENTS,
696 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
697 : #undef TYPED_ARRAY_CASE
698 672 : };
699 :
700 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) Label if_##type##array(this);
701 1344 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
702 : #undef TYPED_ARRAY_CASE
703 :
704 : Label* elements_kind_labels[] = {
705 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &if_##type##array,
706 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
707 : #undef TYPED_ARRAY_CASE
708 672 : };
709 : STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels));
710 :
711 : Switch(elements_kind, &if_unknown_type, elements_kinds, elements_kind_labels,
712 672 : arraysize(elements_kinds));
713 :
714 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
715 : BIND(&if_##type##array); \
716 : { \
717 : case_function(TYPE##_ELEMENTS, sizeof(ctype), \
718 : Context::TYPE##_ARRAY_FUN_INDEX); \
719 : Goto(&next); \
720 : }
721 672 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
722 : #undef TYPED_ARRAY_CASE
723 :
724 672 : BIND(&if_unknown_type);
725 672 : Unreachable();
726 :
727 1344 : BIND(&next);
728 672 : }
729 :
730 56 : TNode<BoolT> TypedArrayBuiltinsAssembler::IsSharedArrayBuffer(
731 : TNode<JSArrayBuffer> buffer) {
732 : TNode<Uint32T> bitfield =
733 56 : LoadObjectField<Uint32T>(buffer, JSArrayBuffer::kBitFieldOffset);
734 56 : return IsSetWord32<JSArrayBuffer::IsSharedBit>(bitfield);
735 : }
736 :
737 : // ES #sec-get-%typedarray%.prototype.set
738 336 : TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
739 56 : const char* method_name = "%TypedArray%.prototype.set";
740 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
741 : CodeStubArguments args(
742 : this,
743 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
744 :
745 112 : Label if_source_is_typed_array(this), if_source_is_fast_jsarray(this),
746 112 : if_offset_is_out_of_bounds(this, Label::kDeferred),
747 112 : if_source_too_large(this, Label::kDeferred),
748 112 : if_receiver_is_not_typedarray(this, Label::kDeferred);
749 :
750 : // Check the receiver is a typed array.
751 56 : TNode<Object> receiver = args.GetReceiver();
752 56 : GotoIf(TaggedIsSmi(receiver), &if_receiver_is_not_typedarray);
753 56 : GotoIfNot(IsJSTypedArray(CAST(receiver)), &if_receiver_is_not_typedarray);
754 :
755 : // Normalize offset argument (using ToInteger) and handle heap number cases.
756 56 : TNode<Object> offset = args.GetOptionalArgumentValue(1, SmiConstant(0));
757 : TNode<Number> offset_num =
758 56 : ToInteger_Inline(context, offset, kTruncateMinusZero);
759 :
760 : // Since ToInteger always returns a Smi if the given value is within Smi
761 : // range, and the only corner case of -0.0 has already been truncated to 0.0,
762 : // we can simply throw unless the offset is a non-negative Smi.
763 : // TODO(jgruber): It's an observable spec violation to throw here if
764 : // {offset_num} is a positive number outside the Smi range. Per spec, we need
765 : // to check for detached buffers and call the observable ToObject/ToLength
766 : // operations first.
767 56 : GotoIfNot(TaggedIsPositiveSmi(offset_num), &if_offset_is_out_of_bounds);
768 56 : TNode<Smi> offset_smi = CAST(offset_num);
769 :
770 : // Check the receiver is not detached.
771 56 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(receiver), method_name);
772 :
773 : // Check the source argument is valid and whether a fast path can be taken.
774 112 : Label call_runtime(this);
775 56 : TNode<Object> source = args.GetOptionalArgumentValue(0);
776 56 : GotoIf(TaggedIsSmi(source), &call_runtime);
777 56 : GotoIf(IsJSTypedArray(CAST(source)), &if_source_is_typed_array);
778 : BranchIfFastJSArray(source, context, &if_source_is_fast_jsarray,
779 56 : &call_runtime);
780 :
781 : // Fast path for a typed array source argument.
782 56 : BIND(&if_source_is_typed_array);
783 : {
784 : // Check the source argument is not detached.
785 56 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(source), method_name);
786 :
787 112 : SetTypedArraySource(context, CAST(source), CAST(receiver),
788 : SmiUntag(offset_smi), &call_runtime,
789 168 : &if_source_too_large);
790 56 : args.PopAndReturn(UndefinedConstant());
791 : }
792 :
793 : // Fast path for a fast JSArray source argument.
794 56 : BIND(&if_source_is_fast_jsarray);
795 : {
796 112 : SetJSArraySource(context, CAST(source), CAST(receiver),
797 168 : SmiUntag(offset_smi), &call_runtime, &if_source_too_large);
798 56 : args.PopAndReturn(UndefinedConstant());
799 : }
800 :
801 56 : BIND(&call_runtime);
802 : args.PopAndReturn(CallRuntime(Runtime::kTypedArraySet, context, receiver,
803 56 : source, offset_smi));
804 :
805 56 : BIND(&if_offset_is_out_of_bounds);
806 56 : ThrowRangeError(context, MessageTemplate::kTypedArraySetOffsetOutOfBounds);
807 :
808 56 : BIND(&if_source_too_large);
809 56 : ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge);
810 :
811 56 : BIND(&if_receiver_is_not_typedarray);
812 112 : ThrowTypeError(context, MessageTemplate::kNotTypedArray);
813 56 : }
814 :
815 : // ES %TypedArray%.prototype.slice
816 336 : TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
817 56 : const char* method_name = "%TypedArray%.prototype.slice";
818 112 : Label call_c(this), call_memmove(this), if_count_is_not_zero(this),
819 112 : if_bigint_mixed_types(this, Label::kDeferred);
820 :
821 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
822 : CodeStubArguments args(
823 : this,
824 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
825 :
826 56 : TNode<Object> receiver = args.GetReceiver();
827 : TNode<JSTypedArray> source =
828 56 : ValidateTypedArray(context, receiver, method_name);
829 :
830 56 : TNode<Smi> source_length = LoadJSTypedArrayLength(source);
831 :
832 : // Convert start offset argument to integer, and calculate relative offset.
833 56 : TNode<Object> start = args.GetOptionalArgumentValue(0, SmiConstant(0));
834 : TNode<Smi> start_index =
835 56 : SmiTag(ConvertToRelativeIndex(context, start, SmiUntag(source_length)));
836 :
837 : // Convert end offset argument to integer, and calculate relative offset.
838 : // If end offset is not given or undefined is given, set source_length to
839 : // "end_index".
840 56 : TNode<Object> end = args.GetOptionalArgumentValue(1, UndefinedConstant());
841 : TNode<Smi> end_index =
842 168 : Select<Smi>(IsUndefined(end), [=] { return source_length; },
843 56 : [=] {
844 : return SmiTag(ConvertToRelativeIndex(
845 112 : context, end, SmiUntag(source_length)));
846 280 : });
847 :
848 : // Create a result array by invoking TypedArraySpeciesCreate.
849 56 : TNode<Smi> count = SmiMax(SmiSub(end_index, start_index), SmiConstant(0));
850 : TNode<JSTypedArray> result_array =
851 56 : TypedArraySpeciesCreateByLength(context, source, count, method_name);
852 :
853 : // If count is zero, return early.
854 56 : GotoIf(SmiGreaterThan(count, SmiConstant(0)), &if_count_is_not_zero);
855 56 : args.PopAndReturn(result_array);
856 :
857 56 : BIND(&if_count_is_not_zero);
858 : // Check the source array is detached or not. We don't need to check if the
859 : // result array is detached or not since TypedArraySpeciesCreate checked it.
860 : CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(LoadObjectField(
861 : result_array, JSTypedArray::kBufferOffset))));
862 : TNode<JSArrayBuffer> receiver_buffer =
863 56 : LoadJSArrayBufferViewBuffer(CAST(receiver));
864 56 : ThrowIfArrayBufferIsDetached(context, receiver_buffer, method_name);
865 :
866 : // result_array could be a different type from source or share the same
867 : // buffer with the source because of custom species constructor.
868 : // If the types of source and result array are the same and they are not
869 : // sharing the same buffer, use memmove.
870 56 : TNode<Word32T> source_el_kind = LoadElementsKind(source);
871 56 : TNode<Word32T> target_el_kind = LoadElementsKind(result_array);
872 56 : GotoIfNot(Word32Equal(source_el_kind, target_el_kind), &call_c);
873 :
874 : TNode<Object> target_buffer =
875 56 : LoadObjectField(result_array, JSTypedArray::kBufferOffset);
876 56 : Branch(WordEqual(receiver_buffer, target_buffer), &call_c, &call_memmove);
877 :
878 56 : BIND(&call_memmove);
879 : {
880 56 : GotoIfForceSlowPath(&call_c);
881 :
882 : TNode<IntPtrT> target_data_ptr =
883 56 : UncheckedCast<IntPtrT>(LoadDataPtr(result_array));
884 : TNode<IntPtrT> source_data_ptr =
885 56 : UncheckedCast<IntPtrT>(LoadDataPtr(source));
886 :
887 56 : TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind);
888 : TNode<IntPtrT> source_start_bytes =
889 56 : IntPtrMul(SmiToIntPtr(start_index), source_el_size);
890 : TNode<IntPtrT> source_start =
891 56 : IntPtrAdd(source_data_ptr, source_start_bytes);
892 :
893 56 : TNode<IntPtrT> count_bytes = IntPtrMul(SmiToIntPtr(count), source_el_size);
894 :
895 : #ifdef DEBUG
896 : TNode<UintPtrT> target_byte_length =
897 : LoadJSArrayBufferViewByteLength(result_array);
898 : CSA_ASSERT(this, UintPtrLessThanOrEqual(Unsigned(count_bytes),
899 : target_byte_length));
900 : TNode<UintPtrT> source_byte_length =
901 : LoadJSArrayBufferViewByteLength(source);
902 : TNode<UintPtrT> source_size_in_bytes =
903 : UintPtrSub(source_byte_length, Unsigned(source_start_bytes));
904 : CSA_ASSERT(this, UintPtrLessThanOrEqual(Unsigned(count_bytes),
905 : source_size_in_bytes));
906 : #endif // DEBUG
907 :
908 56 : CallCMemmove(target_data_ptr, source_start, count_bytes);
909 56 : args.PopAndReturn(result_array);
910 : }
911 :
912 56 : BIND(&call_c);
913 : {
914 56 : GotoIf(Word32NotEqual(IsBigInt64ElementsKind(source_el_kind),
915 168 : IsBigInt64ElementsKind(target_el_kind)),
916 56 : &if_bigint_mixed_types);
917 :
918 : CallCCopyTypedArrayElementsSlice(
919 56 : source, result_array, SmiToIntPtr(start_index), SmiToIntPtr(end_index));
920 56 : args.PopAndReturn(result_array);
921 : }
922 :
923 56 : BIND(&if_bigint_mixed_types);
924 112 : ThrowTypeError(context, MessageTemplate::kBigIntMixedTypes);
925 56 : }
926 :
927 : // ES %TypedArray%.prototype.subarray
928 336 : TF_BUILTIN(TypedArrayPrototypeSubArray, TypedArrayBuiltinsAssembler) {
929 56 : const char* method_name = "%TypedArray%.prototype.subarray";
930 56 : Label offset_done(this);
931 :
932 112 : TVARIABLE(Smi, var_begin);
933 112 : TVARIABLE(Smi, var_end);
934 :
935 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
936 : CodeStubArguments args(
937 : this,
938 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
939 :
940 : // 1. Let O be the this value.
941 : // 3. If O does not have a [[TypedArrayName]] internal slot, throw a TypeError
942 : // exception.
943 56 : TNode<Object> receiver = args.GetReceiver();
944 56 : ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, method_name);
945 :
946 56 : TNode<JSTypedArray> source = CAST(receiver);
947 :
948 : // 5. Let buffer be O.[[ViewedArrayBuffer]].
949 56 : TNode<JSArrayBuffer> buffer = GetBuffer(context, source);
950 : // 6. Let srcLength be O.[[ArrayLength]].
951 56 : TNode<Smi> source_length = LoadJSTypedArrayLength(source);
952 :
953 : // 7. Let relativeBegin be ? ToInteger(begin).
954 : // 8. If relativeBegin < 0, let beginIndex be max((srcLength + relativeBegin),
955 : // 0); else let beginIndex be min(relativeBegin, srcLength).
956 56 : TNode<Object> begin = args.GetOptionalArgumentValue(0, SmiConstant(0));
957 112 : var_begin =
958 168 : SmiTag(ConvertToRelativeIndex(context, begin, SmiUntag(source_length)));
959 :
960 56 : TNode<Object> end = args.GetOptionalArgumentValue(1, UndefinedConstant());
961 : // 9. If end is undefined, let relativeEnd be srcLength;
962 56 : var_end = source_length;
963 56 : GotoIf(IsUndefined(end), &offset_done);
964 :
965 : // else, let relativeEnd be ? ToInteger(end).
966 : // 10. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd), 0);
967 : // else let endIndex be min(relativeEnd, srcLength).
968 112 : var_end =
969 168 : SmiTag(ConvertToRelativeIndex(context, end, SmiUntag(source_length)));
970 56 : Goto(&offset_done);
971 :
972 56 : BIND(&offset_done);
973 :
974 : // 11. Let newLength be max(endIndex - beginIndex, 0).
975 : TNode<Smi> new_length =
976 56 : SmiMax(SmiSub(var_end.value(), var_begin.value()), SmiConstant(0));
977 :
978 : // 12. Let constructorName be the String value of O.[[TypedArrayName]].
979 : // 13. Let elementSize be the Number value of the Element Size value specified
980 : // in Table 52 for constructorName.
981 56 : TNode<Word32T> element_kind = LoadElementsKind(source);
982 56 : TNode<IntPtrT> element_size = GetTypedArrayElementSize(element_kind);
983 :
984 : // 14. Let srcByteOffset be O.[[ByteOffset]].
985 : TNode<Number> source_byte_offset =
986 56 : ChangeUintPtrToTagged(LoadJSArrayBufferViewByteOffset(source));
987 :
988 : // 15. Let beginByteOffset be srcByteOffset + beginIndex × elementSize.
989 56 : TNode<Number> offset = SmiMul(var_begin.value(), SmiFromIntPtr(element_size));
990 56 : TNode<Number> begin_byte_offset = NumberAdd(source_byte_offset, offset);
991 :
992 : // 16. Let argumentsList be « buffer, beginByteOffset, newLength ».
993 : // 17. Return ? TypedArraySpeciesCreate(O, argumentsList).
994 : args.PopAndReturn(TypedArraySpeciesCreate(
995 112 : method_name, context, source, buffer, begin_byte_offset, new_length));
996 56 : }
997 :
998 : // ES #sec-get-%typedarray%.prototype-@@tostringtag
999 280 : TF_BUILTIN(TypedArrayPrototypeToStringTag, TypedArrayBuiltinsAssembler) {
1000 56 : Node* receiver = Parameter(Descriptor::kReceiver);
1001 112 : Label if_receiverisheapobject(this), return_undefined(this);
1002 56 : Branch(TaggedIsSmi(receiver), &return_undefined, &if_receiverisheapobject);
1003 :
1004 : // Dispatch on the elements kind, offset by
1005 : // FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND.
1006 : size_t const kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
1007 : FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
1008 56 : 1;
1009 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
1010 : Label return_##type##array(this); \
1011 : BIND(&return_##type##array); \
1012 : Return(StringConstant(#Type "Array"));
1013 112 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
1014 : #undef TYPED_ARRAY_CASE
1015 : Label* elements_kind_labels[kTypedElementsKindCount] = {
1016 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &return_##type##array,
1017 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
1018 : #undef TYPED_ARRAY_CASE
1019 56 : };
1020 : int32_t elements_kinds[kTypedElementsKindCount] = {
1021 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
1022 : TYPE##_ELEMENTS - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
1023 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
1024 : #undef TYPED_ARRAY_CASE
1025 56 : };
1026 :
1027 : // We offset the dispatch by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, so
1028 : // that this can be turned into a non-sparse table switch for ideal
1029 : // performance.
1030 56 : BIND(&if_receiverisheapobject);
1031 : Node* elements_kind =
1032 112 : Int32Sub(LoadElementsKind(receiver),
1033 168 : Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND));
1034 : Switch(elements_kind, &return_undefined, elements_kinds, elements_kind_labels,
1035 56 : kTypedElementsKindCount);
1036 :
1037 56 : BIND(&return_undefined);
1038 112 : Return(UndefinedConstant());
1039 56 : }
1040 :
1041 168 : void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
1042 : TNode<Context> context, TNode<Object> receiver, const char* method_name,
1043 : IterationKind kind) {
1044 168 : Label throw_bad_receiver(this, Label::kDeferred);
1045 :
1046 168 : GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
1047 168 : GotoIfNot(IsJSTypedArray(CAST(receiver)), &throw_bad_receiver);
1048 :
1049 : // Check if the {receiver}'s JSArrayBuffer was detached.
1050 168 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(receiver), method_name);
1051 :
1052 168 : Return(CreateArrayIterator(context, receiver, kind));
1053 :
1054 168 : BIND(&throw_bad_receiver);
1055 168 : ThrowTypeError(context, MessageTemplate::kNotTypedArray, method_name);
1056 168 : }
1057 :
1058 : // ES #sec-%typedarray%.prototype.values
1059 336 : TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
1060 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1061 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1062 : GenerateTypedArrayPrototypeIterationMethod(context, receiver,
1063 : "%TypedArray%.prototype.values()",
1064 56 : IterationKind::kValues);
1065 56 : }
1066 :
1067 : // ES #sec-%typedarray%.prototype.entries
1068 336 : TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
1069 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1070 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1071 : GenerateTypedArrayPrototypeIterationMethod(context, receiver,
1072 : "%TypedArray%.prototype.entries()",
1073 56 : IterationKind::kEntries);
1074 56 : }
1075 :
1076 : // ES #sec-%typedarray%.prototype.keys
1077 336 : TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
1078 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1079 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1080 : GenerateTypedArrayPrototypeIterationMethod(
1081 56 : context, receiver, "%TypedArray%.prototype.keys()", IterationKind::kKeys);
1082 56 : }
1083 :
1084 : // ES6 #sec-%typedarray%.of
1085 336 : TF_BUILTIN(TypedArrayOf, TypedArrayBuiltinsAssembler) {
1086 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1087 :
1088 : // 1. Let len be the actual number of arguments passed to this function.
1089 : TNode<IntPtrT> length = ChangeInt32ToIntPtr(
1090 56 : UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
1091 : // 2. Let items be the List of arguments passed to this function.
1092 : CodeStubArguments args(this, length, nullptr, INTPTR_PARAMETERS,
1093 56 : CodeStubArguments::ReceiverMode::kHasReceiver);
1094 :
1095 56 : Label if_not_constructor(this, Label::kDeferred),
1096 112 : if_detached(this, Label::kDeferred);
1097 :
1098 : // 3. Let C be the this value.
1099 : // 4. If IsConstructor(C) is false, throw a TypeError exception.
1100 56 : TNode<Object> receiver = args.GetReceiver();
1101 56 : GotoIf(TaggedIsSmi(receiver), &if_not_constructor);
1102 56 : GotoIfNot(IsConstructor(CAST(receiver)), &if_not_constructor);
1103 :
1104 : // 5. Let newObj be ? TypedArrayCreate(C, len).
1105 : TNode<JSTypedArray> new_typed_array = TypedArrayCreateByLength(
1106 56 : context, receiver, SmiTag(length), "%TypedArray%.of");
1107 :
1108 56 : TNode<Word32T> elements_kind = LoadElementsKind(new_typed_array);
1109 :
1110 : // 6. Let k be 0.
1111 : // 7. Repeat, while k < len
1112 : // a. Let kValue be items[k].
1113 : // b. Let Pk be ! ToString(k).
1114 : // c. Perform ? Set(newObj, Pk, kValue, true).
1115 : // d. Increase k by 1.
1116 : DispatchTypedArrayByElementsKind(
1117 : elements_kind,
1118 616 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
1119 : TNode<FixedTypedArrayBase> elements =
1120 616 : CAST(LoadElements(new_typed_array));
1121 : BuildFastLoop(
1122 616 : IntPtrConstant(0), length,
1123 616 : [&](Node* index) {
1124 616 : TNode<Object> item = args.AtIndex(index, INTPTR_PARAMETERS);
1125 616 : TNode<IntPtrT> intptr_index = UncheckedCast<IntPtrT>(index);
1126 616 : if (kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS) {
1127 : EmitBigTypedArrayElementStore(new_typed_array, elements,
1128 : intptr_index, item, context,
1129 112 : &if_detached);
1130 : } else {
1131 : Node* value =
1132 504 : PrepareValueForWriteToTypedArray(item, kind, context);
1133 :
1134 : // ToNumber may execute JavaScript code, which could detach
1135 : // the array's buffer.
1136 : Node* buffer = LoadObjectField(new_typed_array,
1137 504 : JSTypedArray::kBufferOffset);
1138 504 : GotoIf(IsDetachedBuffer(buffer), &if_detached);
1139 :
1140 : // GC may move backing store in ToNumber, thus load backing
1141 : // store everytime in this loop.
1142 : TNode<RawPtrT> backing_store =
1143 504 : LoadFixedTypedArrayBackingStore(elements);
1144 : StoreElement(backing_store, kind, index, value,
1145 504 : INTPTR_PARAMETERS);
1146 : }
1147 616 : },
1148 1232 : 1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
1149 672 : });
1150 :
1151 : // 8. Return newObj.
1152 56 : args.PopAndReturn(new_typed_array);
1153 :
1154 56 : BIND(&if_not_constructor);
1155 56 : ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
1156 :
1157 56 : BIND(&if_detached);
1158 : ThrowTypeError(context, MessageTemplate::kDetachedOperation,
1159 112 : "%TypedArray%.of");
1160 56 : }
1161 :
1162 : // ES6 #sec-%typedarray%.from
1163 336 : TF_BUILTIN(TypedArrayFrom, TypedArrayBuiltinsAssembler) {
1164 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1165 :
1166 112 : Label check_iterator(this), from_array_like(this), fast_path(this),
1167 112 : slow_path(this), create_typed_array(this), check_typedarray(this),
1168 112 : if_not_constructor(this, Label::kDeferred),
1169 112 : if_map_fn_not_callable(this, Label::kDeferred),
1170 112 : if_iterator_fn_not_callable(this, Label::kDeferred),
1171 112 : if_detached(this, Label::kDeferred);
1172 :
1173 : CodeStubArguments args(
1174 : this,
1175 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
1176 56 : TNode<Object> source = args.GetOptionalArgumentValue(0);
1177 :
1178 : // 5. If thisArg is present, let T be thisArg; else let T be undefined.
1179 56 : TNode<Object> this_arg = args.GetOptionalArgumentValue(2);
1180 :
1181 : // 1. Let C be the this value.
1182 : // 2. If IsConstructor(C) is false, throw a TypeError exception.
1183 56 : TNode<Object> receiver = args.GetReceiver();
1184 56 : GotoIf(TaggedIsSmi(receiver), &if_not_constructor);
1185 56 : GotoIfNot(IsConstructor(CAST(receiver)), &if_not_constructor);
1186 :
1187 : // 3. If mapfn is present and mapfn is not undefined, then
1188 56 : TNode<Object> map_fn = args.GetOptionalArgumentValue(1);
1189 112 : TVARIABLE(BoolT, mapping, Int32FalseConstant());
1190 56 : GotoIf(IsUndefined(map_fn), &check_typedarray);
1191 :
1192 : // a. If IsCallable(mapfn) is false, throw a TypeError exception.
1193 : // b. Let mapping be true.
1194 : // 4. Else, let mapping be false.
1195 56 : GotoIf(TaggedIsSmi(map_fn), &if_map_fn_not_callable);
1196 56 : GotoIfNot(IsCallable(CAST(map_fn)), &if_map_fn_not_callable);
1197 56 : mapping = Int32TrueConstant();
1198 56 : Goto(&check_typedarray);
1199 :
1200 112 : TVARIABLE(Object, final_source);
1201 112 : TVARIABLE(Smi, final_length);
1202 :
1203 : // We split up this builtin differently to the way it is written in the spec.
1204 : // We already have great code in the elements accessor for copying from a
1205 : // JSArray into a TypedArray, so we use that when possible. We only avoid
1206 : // calling into the elements accessor when we have a mapping function, because
1207 : // we can't handle that. Here, presence of a mapping function is the slow
1208 : // path. We also combine the two different loops in the specification
1209 : // (starting at 7.e and 13) because they are essentially identical. We also
1210 : // save on code-size this way.
1211 :
1212 : // Get the iterator function
1213 56 : BIND(&check_typedarray);
1214 : TNode<Object> iterator_fn =
1215 112 : CAST(GetMethod(context, source, isolate()->factory()->iterator_symbol(),
1216 : &from_array_like));
1217 56 : GotoIf(TaggedIsSmi(iterator_fn), &if_iterator_fn_not_callable);
1218 :
1219 : {
1220 : // TypedArrays have iterators, so normally we would go through the
1221 : // IterableToList case below, which would convert the TypedArray to a
1222 : // JSArray (boxing the values if they won't fit in a Smi).
1223 : //
1224 : // However, if we can guarantee that the source object has the built-in
1225 : // iterator and that the %ArrayIteratorPrototype%.next method has not been
1226 : // overridden, then we know the behavior of the iterator: returning the
1227 : // values in the TypedArray sequentially from index 0 to length-1.
1228 : //
1229 : // In this case, we can avoid creating the intermediate array and the
1230 : // associated HeapNumbers, and use the fast path in TypedArrayCopyElements
1231 : // which uses the same ordering as the default iterator.
1232 : //
1233 : // Drop through to the default check_iterator behavior if any of these
1234 : // checks fail.
1235 :
1236 : // Check that the source is a TypedArray
1237 56 : GotoIf(TaggedIsSmi(source), &check_iterator);
1238 56 : GotoIfNot(IsJSTypedArray(CAST(source)), &check_iterator);
1239 : TNode<JSArrayBuffer> source_buffer =
1240 56 : LoadJSArrayBufferViewBuffer(CAST(source));
1241 56 : GotoIf(IsDetachedBuffer(source_buffer), &check_iterator);
1242 :
1243 : // Check that the iterator function is Builtins::kTypedArrayPrototypeValues
1244 56 : GotoIfNot(IsJSFunction(CAST(iterator_fn)), &check_iterator);
1245 : TNode<SharedFunctionInfo> shared_info = LoadObjectField<SharedFunctionInfo>(
1246 56 : CAST(iterator_fn), JSFunction::kSharedFunctionInfoOffset);
1247 : GotoIfNot(
1248 : WordEqual(LoadObjectField(shared_info,
1249 : SharedFunctionInfo::kFunctionDataOffset),
1250 112 : SmiConstant(Builtins::kTypedArrayPrototypeValues)),
1251 56 : &check_iterator);
1252 : // Check that the ArrayIterator prototype's "next" method hasn't been
1253 : // overridden
1254 : TNode<PropertyCell> protector_cell =
1255 56 : CAST(LoadRoot(RootIndex::kArrayIteratorProtector));
1256 : GotoIfNot(
1257 : WordEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
1258 112 : SmiConstant(Isolate::kProtectorValid)),
1259 56 : &check_iterator);
1260 :
1261 : // Source is a TypedArray with unmodified iterator behavior. Use the
1262 : // source object directly, taking advantage of the special-case code in
1263 : // TypedArrayCopyElements
1264 56 : final_length = LoadJSTypedArrayLength(CAST(source));
1265 56 : final_source = source;
1266 56 : Goto(&create_typed_array);
1267 : }
1268 :
1269 56 : BIND(&check_iterator);
1270 : {
1271 : // 6. Let usingIterator be ? GetMethod(source, @@iterator).
1272 56 : GotoIfNot(IsCallable(CAST(iterator_fn)), &if_iterator_fn_not_callable);
1273 :
1274 : // We are using the iterator.
1275 56 : Label if_length_not_smi(this, Label::kDeferred);
1276 : // 7. If usingIterator is not undefined, then
1277 : // a. Let values be ? IterableToList(source, usingIterator).
1278 : // b. Let len be the number of elements in values.
1279 56 : TNode<JSArray> values = CAST(
1280 : CallBuiltin(Builtins::kIterableToList, context, source, iterator_fn));
1281 :
1282 : // This is not a spec'd limit, so it doesn't particularly matter when we
1283 : // throw the range error for typed array length > MaxSmi.
1284 56 : TNode<Object> raw_length = LoadJSArrayLength(values);
1285 56 : GotoIfNot(TaggedIsSmi(raw_length), &if_length_not_smi);
1286 :
1287 56 : final_length = CAST(raw_length);
1288 56 : final_source = values;
1289 56 : Goto(&create_typed_array);
1290 :
1291 56 : BIND(&if_length_not_smi);
1292 : ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
1293 56 : raw_length);
1294 : }
1295 :
1296 56 : BIND(&from_array_like);
1297 : {
1298 : // TODO(7881): support larger-than-smi typed array lengths
1299 56 : Label if_length_not_smi(this, Label::kDeferred);
1300 56 : final_source = source;
1301 :
1302 : // 10. Let len be ? ToLength(? Get(arrayLike, "length")).
1303 : TNode<Object> raw_length =
1304 56 : GetProperty(context, final_source.value(), LengthStringConstant());
1305 56 : final_length = ToSmiLength(context, raw_length, &if_length_not_smi);
1306 56 : Goto(&create_typed_array);
1307 :
1308 56 : BIND(&if_length_not_smi);
1309 : ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
1310 56 : raw_length);
1311 : }
1312 :
1313 112 : TVARIABLE(JSTypedArray, target_obj);
1314 :
1315 56 : BIND(&create_typed_array);
1316 : {
1317 : // 7c/11. Let targetObj be ? TypedArrayCreate(C, «len»).
1318 56 : target_obj = TypedArrayCreateByLength(
1319 56 : context, receiver, final_length.value(), "%TypedArray%.from");
1320 :
1321 56 : Branch(mapping.value(), &slow_path, &fast_path);
1322 : }
1323 :
1324 56 : BIND(&fast_path);
1325 : {
1326 56 : Label done(this);
1327 56 : GotoIf(SmiEqual(final_length.value(), SmiConstant(0)), &done);
1328 :
1329 : CallRuntime(Runtime::kTypedArrayCopyElements, context, target_obj.value(),
1330 56 : final_source.value(), final_length.value());
1331 56 : Goto(&done);
1332 :
1333 56 : BIND(&done);
1334 56 : args.PopAndReturn(target_obj.value());
1335 : }
1336 :
1337 56 : BIND(&slow_path);
1338 56 : TNode<Word32T> elements_kind = LoadElementsKind(target_obj.value());
1339 :
1340 : // 7e/13 : Copy the elements
1341 56 : TNode<FixedTypedArrayBase> elements = CAST(LoadElements(target_obj.value()));
1342 : BuildFastLoop(
1343 56 : SmiConstant(0), final_length.value(),
1344 56 : [&](Node* index) {
1345 : TNode<Object> const k_value =
1346 56 : GetProperty(context, final_source.value(), index);
1347 :
1348 : TNode<Object> const mapped_value =
1349 56 : CAST(CallJS(CodeFactory::Call(isolate()), context, map_fn, this_arg,
1350 : k_value, index));
1351 :
1352 56 : TNode<IntPtrT> intptr_index = SmiUntag(index);
1353 : DispatchTypedArrayByElementsKind(
1354 : elements_kind,
1355 616 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
1356 616 : if (kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS) {
1357 : EmitBigTypedArrayElementStore(target_obj.value(), elements,
1358 : intptr_index, mapped_value,
1359 112 : context, &if_detached);
1360 : } else {
1361 : Node* const final_value = PrepareValueForWriteToTypedArray(
1362 504 : mapped_value, kind, context);
1363 :
1364 : // ToNumber may execute JavaScript code, which could detach
1365 : // the array's buffer.
1366 504 : Node* buffer = LoadObjectField(target_obj.value(),
1367 504 : JSTypedArray::kBufferOffset);
1368 504 : GotoIf(IsDetachedBuffer(buffer), &if_detached);
1369 :
1370 : // GC may move backing store in map_fn, thus load backing
1371 : // store in each iteration of this loop.
1372 : TNode<RawPtrT> backing_store =
1373 504 : LoadFixedTypedArrayBackingStore(elements);
1374 : StoreElement(backing_store, kind, index, final_value,
1375 504 : SMI_PARAMETERS);
1376 : }
1377 672 : });
1378 56 : },
1379 112 : 1, ParameterMode::SMI_PARAMETERS, IndexAdvanceMode::kPost);
1380 :
1381 56 : args.PopAndReturn(target_obj.value());
1382 :
1383 56 : BIND(&if_not_constructor);
1384 56 : ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
1385 :
1386 56 : BIND(&if_map_fn_not_callable);
1387 56 : ThrowTypeError(context, MessageTemplate::kCalledNonCallable, map_fn);
1388 :
1389 56 : BIND(&if_iterator_fn_not_callable);
1390 56 : ThrowTypeError(context, MessageTemplate::kIteratorSymbolNonCallable);
1391 :
1392 56 : BIND(&if_detached);
1393 : ThrowTypeError(context, MessageTemplate::kDetachedOperation,
1394 112 : "%TypedArray%.from");
1395 56 : }
1396 :
1397 : // ES %TypedArray%.prototype.filter
1398 336 : TF_BUILTIN(TypedArrayPrototypeFilter, TypedArrayBuiltinsAssembler) {
1399 56 : const char* method_name = "%TypedArray%.prototype.filter";
1400 :
1401 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1402 : CodeStubArguments args(
1403 : this,
1404 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
1405 :
1406 56 : Label if_callback_not_callable(this, Label::kDeferred),
1407 112 : detached(this, Label::kDeferred);
1408 :
1409 : // 1. Let O be the this value.
1410 : // 2. Perform ? ValidateTypedArray(O).
1411 56 : TNode<Object> receiver = args.GetReceiver();
1412 : TNode<JSTypedArray> source =
1413 56 : ValidateTypedArray(context, receiver, method_name);
1414 :
1415 : // 3. Let len be O.[[ArrayLength]].
1416 56 : TNode<Smi> length = LoadJSTypedArrayLength(source);
1417 :
1418 : // 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
1419 56 : TNode<Object> callbackfn = args.GetOptionalArgumentValue(0);
1420 56 : GotoIf(TaggedIsSmi(callbackfn), &if_callback_not_callable);
1421 56 : GotoIfNot(IsCallable(CAST(callbackfn)), &if_callback_not_callable);
1422 :
1423 : // 5. If thisArg is present, let T be thisArg; else let T be undefined.
1424 56 : TNode<Object> this_arg = args.GetOptionalArgumentValue(1);
1425 :
1426 : TNode<JSArrayBuffer> source_buffer =
1427 56 : LoadObjectField<JSArrayBuffer>(source, JSArrayBufferView::kBufferOffset);
1428 56 : TNode<Word32T> elements_kind = LoadElementsKind(source);
1429 112 : GrowableFixedArray values(state());
1430 : VariableList vars(
1431 112 : {values.var_array(), values.var_length(), values.var_capacity()}, zone());
1432 :
1433 : // 6. Let kept be a new empty List.
1434 : // 7. Let k be 0.
1435 : // 8. Let captured be 0.
1436 : // 9. Repeat, while k < len
1437 : BuildFastLoop(
1438 56 : vars, SmiConstant(0), length,
1439 56 : [&](Node* index) {
1440 56 : GotoIf(IsDetachedBuffer(source_buffer), &detached);
1441 :
1442 56 : TVARIABLE(Numeric, value);
1443 : // a. Let Pk be ! ToString(k).
1444 : // b. Let kValue be ? Get(O, Pk).
1445 : DispatchTypedArrayByElementsKind(
1446 : elements_kind,
1447 616 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
1448 : TNode<IntPtrT> backing_store =
1449 616 : UncheckedCast<IntPtrT>(LoadDataPtr(source));
1450 1232 : value = CAST(LoadFixedTypedArrayElementAsTagged(
1451 616 : backing_store, index, kind, ParameterMode::SMI_PARAMETERS));
1452 672 : });
1453 :
1454 : // c. Let selected be ToBoolean(Call(callbackfn, T, kValue, k, O))
1455 : Node* selected =
1456 : CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg,
1457 56 : value.value(), index, source);
1458 :
1459 112 : Label true_continue(this), false_continue(this);
1460 56 : BranchIfToBooleanIsTrue(selected, &true_continue, &false_continue);
1461 :
1462 56 : BIND(&true_continue);
1463 : // d. If selected is true, then
1464 : // i. Append kValue to the end of kept.
1465 : // ii. Increase captured by 1.
1466 56 : values.Push(value.value());
1467 56 : Goto(&false_continue);
1468 :
1469 56 : BIND(&false_continue);
1470 56 : },
1471 112 : 1, ParameterMode::SMI_PARAMETERS, IndexAdvanceMode::kPost);
1472 :
1473 56 : TNode<JSArray> values_array = values.ToJSArray(context);
1474 56 : TNode<Smi> captured = LoadFastJSArrayLength(values_array);
1475 :
1476 : // 10. Let A be ? TypedArraySpeciesCreate(O, captured).
1477 : TNode<JSTypedArray> result_array =
1478 56 : TypedArraySpeciesCreateByLength(context, source, captured, method_name);
1479 :
1480 : // 11. Let n be 0.
1481 : // 12. For each element e of kept, do
1482 : // a. Perform ! Set(A, ! ToString(n), e, true).
1483 : // b. Increment n by 1.
1484 : CallRuntime(Runtime::kTypedArrayCopyElements, context, result_array,
1485 56 : values_array, captured);
1486 :
1487 : // 13. Return A.
1488 56 : args.PopAndReturn(result_array);
1489 :
1490 56 : BIND(&if_callback_not_callable);
1491 56 : ThrowTypeError(context, MessageTemplate::kCalledNonCallable, callbackfn);
1492 :
1493 56 : BIND(&detached);
1494 112 : ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
1495 56 : }
1496 :
1497 : #undef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
1498 :
1499 : } // namespace internal
1500 86739 : } // namespace v8
|