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 : #include "torque-generated/builtins-typed-array-createtypedarray-from-dsl-gen.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 : using compiler::Node;
19 : template <class T>
20 : using TNode = compiler::TNode<T>;
21 :
22 : // This is needed for gc_mole which will compile this file without the full set
23 : // of GN defined macros.
24 : #ifndef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
25 : #define V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP 64
26 : #endif
27 :
28 : // -----------------------------------------------------------------------------
29 : // ES6 section 22.2 TypedArray Objects
30 :
31 0 : TNode<Map> TypedArrayBuiltinsAssembler::LoadMapForType(
32 : TNode<JSTypedArray> array) {
33 0 : TVARIABLE(Map, var_typed_map);
34 0 : TNode<Map> array_map = LoadMap(array);
35 0 : TNode<Int32T> elements_kind = LoadMapElementsKind(array_map);
36 0 : ReadOnlyRoots roots(isolate());
37 :
38 0 : DispatchTypedArrayByElementsKind(
39 : elements_kind,
40 0 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
41 0 : Handle<Map> map(roots.MapForFixedTypedArray(kind), isolate());
42 0 : var_typed_map = HeapConstant(map);
43 0 : });
44 :
45 0 : return var_typed_map.value();
46 : }
47 :
48 : // Setup the TypedArray which is under construction.
49 : // - Set the length.
50 : // - Set the byte_offset.
51 : // - Set the byte_length.
52 : // - Set EmbedderFields to 0.
53 168 : void TypedArrayBuiltinsAssembler::SetupTypedArray(TNode<JSTypedArray> holder,
54 : TNode<Smi> length,
55 : TNode<UintPtrT> byte_offset,
56 : TNode<UintPtrT> byte_length) {
57 : CSA_ASSERT(this, TaggedIsPositiveSmi(length));
58 168 : StoreObjectField(holder, JSTypedArray::kLengthOffset, length);
59 : StoreObjectFieldNoWriteBarrier(holder, JSArrayBufferView::kByteOffsetOffset,
60 : byte_offset,
61 168 : MachineType::PointerRepresentation());
62 : StoreObjectFieldNoWriteBarrier(holder, JSArrayBufferView::kByteLengthOffset,
63 : byte_length,
64 168 : MachineType::PointerRepresentation());
65 336 : for (int offset = JSTypedArray::kHeaderSize;
66 504 : offset < JSTypedArray::kSizeWithEmbedderFields; offset += kTaggedSize) {
67 672 : StoreObjectField(holder, offset, SmiConstant(0));
68 : }
69 168 : }
70 :
71 : // Allocate a new ArrayBuffer and initialize it with empty properties and
72 : // elements.
73 112 : TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
74 : TNode<Context> context, TNode<JSTypedArray> holder,
75 : TNode<UintPtrT> byte_length) {
76 112 : TNode<Context> native_context = LoadNativeContext(context);
77 : TNode<Map> map =
78 112 : CAST(LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX));
79 : TNode<FixedArray> empty_fixed_array =
80 112 : CAST(LoadRoot(RootIndex::kEmptyFixedArray));
81 :
82 : TNode<JSArrayBuffer> buffer = UncheckedCast<JSArrayBuffer>(
83 112 : Allocate(JSArrayBuffer::kSizeWithEmbedderFields));
84 112 : StoreMapNoWriteBarrier(buffer, map);
85 : StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOrHashOffset,
86 : empty_fixed_array);
87 : StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
88 : empty_fixed_array);
89 : // Setup the ArrayBuffer.
90 : // - Set BitField to 0.
91 : // - Set IsExternal and IsDetachable bits of BitFieldSlot.
92 : // - Set the byte_length field to byte_length.
93 : // - Set backing_store to null/Smi(0).
94 : // - Set all embedder fields to Smi(0).
95 : if (FIELD_SIZE(JSArrayBuffer::kOptionalPaddingOffset) != 0) {
96 : DCHECK_EQ(4, FIELD_SIZE(JSArrayBuffer::kOptionalPaddingOffset));
97 : StoreObjectFieldNoWriteBarrier(
98 224 : buffer, JSArrayBuffer::kOptionalPaddingOffset, Int32Constant(0),
99 112 : MachineRepresentation::kWord32);
100 : }
101 : int32_t bitfield_value = (1 << JSArrayBuffer::IsExternalBit::kShift) |
102 : (1 << JSArrayBuffer::IsDetachableBit::kShift);
103 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
104 224 : Int32Constant(bitfield_value),
105 112 : MachineRepresentation::kWord32);
106 :
107 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
108 : byte_length,
109 112 : MachineType::PointerRepresentation());
110 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
111 224 : IntPtrConstant(0),
112 112 : MachineType::PointerRepresentation());
113 224 : for (int offset = JSArrayBuffer::kHeaderSize;
114 336 : offset < JSArrayBuffer::kSizeWithEmbedderFields; offset += kTaggedSize) {
115 224 : StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(0));
116 : }
117 :
118 112 : StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
119 112 : return buffer;
120 : }
121 :
122 112 : TNode<FixedTypedArrayBase> TypedArrayBuiltinsAssembler::AllocateOnHeapElements(
123 : TNode<Map> map, TNode<IntPtrT> total_size, TNode<Number> length) {
124 : CSA_ASSERT(this, IntPtrGreaterThanOrEqual(total_size, IntPtrConstant(0)));
125 :
126 : // Allocate a FixedTypedArray and set the length, base pointer and external
127 : // pointer.
128 : CSA_ASSERT(this, IsRegularHeapObjectSize(total_size));
129 :
130 : TNode<Object> elements;
131 :
132 224 : if (UnalignedLoadSupported(MachineRepresentation::kFloat64) &&
133 112 : UnalignedStoreSupported(MachineRepresentation::kFloat64)) {
134 224 : elements = AllocateInNewSpace(total_size);
135 : } else {
136 0 : elements = AllocateInNewSpace(total_size, kDoubleAlignment);
137 : }
138 :
139 112 : StoreMapNoWriteBarrier(elements, map);
140 112 : StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
141 : StoreObjectFieldNoWriteBarrier(
142 112 : elements, FixedTypedArrayBase::kBasePointerOffset, elements);
143 : StoreObjectFieldNoWriteBarrier(
144 : elements, FixedTypedArrayBase::kExternalPointerOffset,
145 224 : IntPtrConstant(FixedTypedArrayBase::ExternalPointerValueForOnHeapArray()),
146 112 : MachineType::PointerRepresentation());
147 112 : return CAST(elements);
148 : }
149 :
150 1568 : TNode<RawPtrT> TypedArrayBuiltinsAssembler::LoadDataPtr(
151 : TNode<JSTypedArray> typed_array) {
152 : TNode<FixedArrayBase> elements = LoadElements(typed_array);
153 : CSA_ASSERT(this, IsFixedTypedArray(elements));
154 1568 : return LoadFixedTypedArrayBackingStore(CAST(elements));
155 : }
156 :
157 224 : TF_BUILTIN(TypedArrayBaseConstructor, TypedArrayBuiltinsAssembler) {
158 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
159 56 : ThrowTypeError(context, MessageTemplate::kConstructAbstractClass,
160 56 : "TypedArray");
161 56 : }
162 :
163 : // ES #sec-typedarray-constructors
164 224 : TF_BUILTIN(TypedArrayConstructor, TypedArrayBuiltinsAssembler) {
165 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
166 : TNode<JSFunction> target = CAST(Parameter(Descriptor::kJSTarget));
167 : TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));
168 : Node* argc =
169 112 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
170 56 : CodeStubArguments args(this, argc);
171 112 : Node* arg1 = args.GetOptionalArgumentValue(0);
172 112 : Node* arg2 = args.GetOptionalArgumentValue(1);
173 112 : Node* arg3 = args.GetOptionalArgumentValue(2);
174 :
175 : // If NewTarget is undefined, throw a TypeError exception.
176 : // All the TypedArray constructors have this as the first step:
177 : // https://tc39.github.io/ecma262/#sec-typedarray-constructors
178 56 : Label throwtypeerror(this, Label::kDeferred);
179 112 : GotoIf(IsUndefined(new_target), &throwtypeerror);
180 :
181 112 : Node* result = CallBuiltin(Builtins::kCreateTypedArray, context, target,
182 56 : new_target, arg1, arg2, arg3);
183 56 : args.PopAndReturn(result);
184 :
185 56 : BIND(&throwtypeerror);
186 : {
187 : TNode<String> name =
188 : CAST(CallRuntime(Runtime::kGetFunctionName, context, target));
189 56 : ThrowTypeError(context, MessageTemplate::kConstructorNotFunction, name);
190 : }
191 56 : }
192 :
193 : // ES6 #sec-get-%typedarray%.prototype.bytelength
194 224 : TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
195 : const char* const kMethodName = "get TypedArray.prototype.byteLength";
196 : Node* context = Parameter(Descriptor::kContext);
197 : Node* receiver = Parameter(Descriptor::kReceiver);
198 :
199 : // Check if the {receiver} is actually a JSTypedArray.
200 56 : ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
201 :
202 : // Default to zero if the {receiver}s buffer was detached.
203 : TNode<JSArrayBuffer> receiver_buffer =
204 56 : LoadJSArrayBufferViewBuffer(CAST(receiver));
205 : TNode<UintPtrT> byte_length = Select<UintPtrT>(
206 56 : IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
207 224 : [=] { return LoadJSArrayBufferViewByteLength(CAST(receiver)); });
208 112 : Return(ChangeUintPtrToTagged(byte_length));
209 56 : }
210 :
211 : // ES6 #sec-get-%typedarray%.prototype.byteoffset
212 224 : TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
213 : const char* const kMethodName = "get TypedArray.prototype.byteOffset";
214 : Node* context = Parameter(Descriptor::kContext);
215 : Node* receiver = Parameter(Descriptor::kReceiver);
216 :
217 : // Check if the {receiver} is actually a JSTypedArray.
218 56 : ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
219 :
220 : // Default to zero if the {receiver}s buffer was detached.
221 : TNode<JSArrayBuffer> receiver_buffer =
222 56 : LoadJSArrayBufferViewBuffer(CAST(receiver));
223 : TNode<UintPtrT> byte_offset = Select<UintPtrT>(
224 56 : IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
225 224 : [=] { return LoadJSArrayBufferViewByteOffset(CAST(receiver)); });
226 112 : Return(ChangeUintPtrToTagged(byte_offset));
227 56 : }
228 :
229 : // ES6 #sec-get-%typedarray%.prototype.length
230 224 : TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
231 : const char* const kMethodName = "get TypedArray.prototype.length";
232 : Node* context = Parameter(Descriptor::kContext);
233 : Node* receiver = Parameter(Descriptor::kReceiver);
234 :
235 : // Check if the {receiver} is actually a JSTypedArray.
236 56 : ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
237 :
238 : // Default to zero if the {receiver}s buffer was detached.
239 : TNode<JSArrayBuffer> receiver_buffer =
240 56 : LoadJSArrayBufferViewBuffer(CAST(receiver));
241 : TNode<Smi> length = Select<Smi>(
242 56 : IsDetachedBuffer(receiver_buffer), [=] { return SmiConstant(0); },
243 224 : [=] { return LoadJSTypedArrayLength(CAST(receiver)); });
244 56 : Return(length);
245 56 : }
246 :
247 112 : TNode<Word32T> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
248 : TNode<Word32T> kind) {
249 336 : return Word32Or(Word32Equal(kind, Int32Constant(UINT8_ELEMENTS)),
250 448 : Word32Equal(kind, Int32Constant(UINT8_CLAMPED_ELEMENTS)));
251 : }
252 :
253 280 : TNode<Word32T> TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
254 : TNode<Word32T> kind) {
255 840 : return Word32Or(Word32Equal(kind, Int32Constant(BIGINT64_ELEMENTS)),
256 1120 : Word32Equal(kind, Int32Constant(BIGUINT64_ELEMENTS)));
257 : }
258 :
259 112 : TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize(
260 : TNode<Word32T> elements_kind) {
261 112 : TVARIABLE(IntPtrT, element_size);
262 :
263 112 : DispatchTypedArrayByElementsKind(
264 : elements_kind,
265 : [&](ElementsKind el_kind, int size, int typed_array_fun_index) {
266 1232 : element_size = IntPtrConstant(size);
267 112 : });
268 :
269 112 : return element_size.value();
270 : }
271 :
272 : TypedArrayBuiltinsFromDSLAssembler::TypedArrayElementsInfo
273 168 : TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
274 : TNode<JSTypedArray> typed_array) {
275 168 : TNode<Int32T> elements_kind = LoadElementsKind(typed_array);
276 168 : TVARIABLE(UintPtrT, var_size_log2);
277 : TVARIABLE(Map, var_map);
278 168 : ReadOnlyRoots roots(isolate());
279 :
280 336 : DispatchTypedArrayByElementsKind(
281 : elements_kind,
282 1848 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
283 : DCHECK_GT(size, 0);
284 7392 : var_size_log2 = UintPtrConstant(ElementsKindToShiftSize(kind));
285 :
286 1848 : Handle<Map> map(roots.MapForFixedTypedArray(kind), isolate());
287 3696 : var_map = HeapConstant(map);
288 2016 : });
289 :
290 : return TypedArrayBuiltinsFromDSLAssembler::TypedArrayElementsInfo{
291 336 : var_size_log2.value(), var_map.value(), elements_kind};
292 : }
293 :
294 224 : TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
295 : TNode<Context> context, TNode<JSTypedArray> exemplar) {
296 224 : TVARIABLE(IntPtrT, context_slot);
297 448 : TNode<Word32T> elements_kind = LoadElementsKind(exemplar);
298 :
299 224 : DispatchTypedArrayByElementsKind(
300 : elements_kind,
301 : [&](ElementsKind el_kind, int size, int typed_array_function_index) {
302 2464 : context_slot = IntPtrConstant(typed_array_function_index);
303 224 : });
304 :
305 672 : return CAST(
306 : LoadContextElement(LoadNativeContext(context), context_slot.value()));
307 : }
308 :
309 112 : TNode<JSTypedArray> TypedArrayBuiltinsAssembler::TypedArrayCreateByLength(
310 : TNode<Context> context, TNode<Object> constructor, TNode<Smi> len,
311 : const char* method_name) {
312 : CSA_ASSERT(this, TaggedIsPositiveSmi(len));
313 :
314 : // Let newTypedArray be ? Construct(constructor, argumentList).
315 336 : TNode<Object> new_object = CAST(ConstructJS(CodeFactory::Construct(isolate()),
316 : context, constructor, len));
317 :
318 : // Perform ? ValidateTypedArray(newTypedArray).
319 : TNode<JSTypedArray> new_typed_array =
320 112 : ValidateTypedArray(context, new_object, method_name);
321 :
322 112 : ThrowIfLengthLessThan(context, new_typed_array, len);
323 112 : return new_typed_array;
324 : }
325 :
326 112 : void TypedArrayBuiltinsAssembler::ThrowIfLengthLessThan(
327 : TNode<Context> context, TNode<JSTypedArray> typed_array,
328 : TNode<Smi> min_length) {
329 : // If typed_array.[[ArrayLength]] < min_length, throw a TypeError exception.
330 224 : Label if_length_is_not_short(this);
331 112 : TNode<Smi> new_length = LoadJSTypedArrayLength(typed_array);
332 224 : GotoIfNot(SmiLessThan(new_length, min_length), &if_length_is_not_short);
333 112 : ThrowTypeError(context, MessageTemplate::kTypedArrayTooShort);
334 :
335 112 : BIND(&if_length_is_not_short);
336 112 : }
337 :
338 56 : TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
339 : TNode<Context> context, TNode<JSTypedArray> array) {
340 112 : Label call_runtime(this), done(this);
341 : TVARIABLE(Object, var_result);
342 :
343 56 : TNode<Object> buffer = LoadObjectField(array, JSTypedArray::kBufferOffset);
344 112 : GotoIf(IsDetachedBuffer(buffer), &call_runtime);
345 : TNode<UintPtrT> backing_store = LoadObjectField<UintPtrT>(
346 : CAST(buffer), JSArrayBuffer::kBackingStoreOffset);
347 168 : GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
348 : var_result = buffer;
349 56 : Goto(&done);
350 :
351 56 : BIND(&call_runtime);
352 : {
353 : var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array);
354 56 : Goto(&done);
355 : }
356 :
357 56 : BIND(&done);
358 56 : return CAST(var_result.value());
359 : }
360 :
361 560 : TNode<JSTypedArray> TypedArrayBuiltinsAssembler::ValidateTypedArray(
362 : TNode<Context> context, TNode<Object> obj, const char* method_name) {
363 : // If it is not a typed array, throw
364 560 : ThrowIfNotInstanceType(context, obj, JS_TYPED_ARRAY_TYPE, method_name);
365 :
366 : // If the typed array's buffer is detached, throw
367 560 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(obj), method_name);
368 :
369 560 : return CAST(obj);
370 : }
371 :
372 56 : void TypedArrayBuiltinsAssembler::SetTypedArraySource(
373 : TNode<Context> context, TNode<JSTypedArray> source,
374 : TNode<JSTypedArray> target, TNode<IntPtrT> offset, Label* call_runtime,
375 : Label* if_source_too_large) {
376 : CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(
377 : LoadObjectField(source, JSTypedArray::kBufferOffset))));
378 : CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(
379 : LoadObjectField(target, JSTypedArray::kBufferOffset))));
380 : CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
381 : CSA_ASSERT(this,
382 : IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
383 :
384 : // Check for possible range errors.
385 :
386 112 : TNode<IntPtrT> source_length = SmiUntag(LoadJSTypedArrayLength(source));
387 112 : TNode<IntPtrT> target_length = SmiUntag(LoadJSTypedArrayLength(target));
388 56 : TNode<IntPtrT> required_target_length = IntPtrAdd(source_length, offset);
389 :
390 112 : GotoIf(IntPtrGreaterThan(required_target_length, target_length),
391 56 : if_source_too_large);
392 :
393 : // Grab pointers and byte lengths we need later on.
394 :
395 56 : TNode<RawPtrT> target_data_ptr = LoadDataPtr(target);
396 56 : TNode<RawPtrT> source_data_ptr = LoadDataPtr(source);
397 :
398 112 : TNode<Word32T> source_el_kind = LoadElementsKind(source);
399 112 : TNode<Word32T> target_el_kind = LoadElementsKind(target);
400 :
401 56 : TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind);
402 56 : TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind);
403 :
404 : // A note on byte lengths: both source- and target byte lengths must be valid,
405 : // i.e. it must be possible to allocate an array of the given length. That
406 : // means we're safe from overflows in the following multiplication.
407 : TNode<IntPtrT> source_byte_length = IntPtrMul(source_length, source_el_size);
408 : CSA_ASSERT(this,
409 : UintPtrGreaterThanOrEqual(source_byte_length, IntPtrConstant(0)));
410 :
411 56 : Label call_memmove(this), fast_c_call(this), out(this), exception(this);
412 :
413 : // A fast memmove call can be used when the source and target types are are
414 : // the same or either Uint8 or Uint8Clamped.
415 112 : GotoIf(Word32Equal(source_el_kind, target_el_kind), &call_memmove);
416 112 : GotoIfNot(IsUint8ElementsKind(source_el_kind), &fast_c_call);
417 112 : Branch(IsUint8ElementsKind(target_el_kind), &call_memmove, &fast_c_call);
418 :
419 56 : BIND(&call_memmove);
420 : {
421 : TNode<RawPtrT> target_start =
422 56 : RawPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size));
423 56 : CallCMemmove(target_start, source_data_ptr, Unsigned(source_byte_length));
424 56 : Goto(&out);
425 : }
426 :
427 56 : BIND(&fast_c_call);
428 : {
429 : CSA_ASSERT(
430 : this, UintPtrGreaterThanOrEqual(
431 : IntPtrMul(target_length, target_el_size), IntPtrConstant(0)));
432 :
433 224 : GotoIf(Word32NotEqual(IsBigInt64ElementsKind(source_el_kind),
434 168 : IsBigInt64ElementsKind(target_el_kind)),
435 56 : &exception);
436 :
437 112 : TNode<IntPtrT> source_length = SmiUntag(LoadJSTypedArrayLength(source));
438 : CallCCopyTypedArrayElementsToTypedArray(source, target, source_length,
439 56 : offset);
440 56 : Goto(&out);
441 : }
442 :
443 56 : BIND(&exception);
444 56 : ThrowTypeError(context, MessageTemplate::kBigIntMixedTypes);
445 :
446 56 : BIND(&out);
447 56 : }
448 :
449 56 : void TypedArrayBuiltinsAssembler::SetJSArraySource(
450 : TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> target,
451 : TNode<IntPtrT> offset, Label* call_runtime, Label* if_source_too_large) {
452 : CSA_ASSERT(this, IsFastJSArray(source, context));
453 : CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
454 : CSA_ASSERT(this,
455 : IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
456 :
457 112 : TNode<IntPtrT> source_length = SmiUntag(LoadFastJSArrayLength(source));
458 112 : TNode<IntPtrT> target_length = SmiUntag(LoadJSTypedArrayLength(target));
459 :
460 : // Maybe out of bounds?
461 168 : GotoIf(IntPtrGreaterThan(IntPtrAdd(source_length, offset), target_length),
462 56 : if_source_too_large);
463 :
464 : // Nothing to do if {source} is empty.
465 56 : Label out(this), fast_c_call(this);
466 168 : GotoIf(IntPtrEqual(source_length, IntPtrConstant(0)), &out);
467 :
468 : // Dispatch based on the source elements kind.
469 : {
470 : // These are the supported elements kinds in TryCopyElementsFastNumber.
471 : int32_t values[] = {
472 : PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
473 : HOLEY_DOUBLE_ELEMENTS,
474 56 : };
475 : Label* labels[] = {
476 : &fast_c_call, &fast_c_call, &fast_c_call, &fast_c_call,
477 56 : };
478 : STATIC_ASSERT(arraysize(values) == arraysize(labels));
479 :
480 56 : TNode<Int32T> source_elements_kind = LoadElementsKind(source);
481 : Switch(source_elements_kind, call_runtime, values, labels,
482 56 : arraysize(values));
483 : }
484 :
485 56 : BIND(&fast_c_call);
486 168 : GotoIf(IsBigInt64ElementsKind(LoadElementsKind(target)), call_runtime);
487 : CallCCopyFastNumberJSArrayElementsToTypedArray(context, source, target,
488 56 : source_length, offset);
489 56 : Goto(&out);
490 56 : BIND(&out);
491 56 : }
492 :
493 112 : void TypedArrayBuiltinsAssembler::CallCMemmove(TNode<RawPtrT> dest_ptr,
494 : TNode<RawPtrT> src_ptr,
495 : TNode<UintPtrT> byte_length) {
496 : TNode<ExternalReference> memmove =
497 112 : ExternalConstant(ExternalReference::libc_memmove_function());
498 : CallCFunction(memmove, MachineType::AnyTagged(),
499 : std::make_pair(MachineType::Pointer(), dest_ptr),
500 : std::make_pair(MachineType::Pointer(), src_ptr),
501 112 : std::make_pair(MachineType::UintPtr(), byte_length));
502 112 : }
503 :
504 56 : void TypedArrayBuiltinsAssembler::CallCMemcpy(TNode<RawPtrT> dest_ptr,
505 : TNode<RawPtrT> src_ptr,
506 : TNode<UintPtrT> byte_length) {
507 : TNode<ExternalReference> memcpy =
508 56 : ExternalConstant(ExternalReference::libc_memcpy_function());
509 : CallCFunction(memcpy, MachineType::AnyTagged(),
510 : std::make_pair(MachineType::Pointer(), dest_ptr),
511 : std::make_pair(MachineType::Pointer(), src_ptr),
512 56 : std::make_pair(MachineType::UintPtr(), byte_length));
513 56 : }
514 :
515 56 : void TypedArrayBuiltinsAssembler::CallCMemset(TNode<RawPtrT> dest_ptr,
516 : TNode<IntPtrT> value,
517 : TNode<UintPtrT> length) {
518 : TNode<ExternalReference> memset =
519 56 : ExternalConstant(ExternalReference::libc_memset_function());
520 : CallCFunction(memset, MachineType::AnyTagged(),
521 : std::make_pair(MachineType::Pointer(), dest_ptr),
522 : std::make_pair(MachineType::IntPtr(), value),
523 56 : std::make_pair(MachineType::UintPtr(), length));
524 56 : }
525 :
526 56 : void TypedArrayBuiltinsAssembler::
527 : CallCCopyFastNumberJSArrayElementsToTypedArray(TNode<Context> context,
528 : TNode<JSArray> source,
529 : TNode<JSTypedArray> dest,
530 : TNode<IntPtrT> source_length,
531 : TNode<IntPtrT> offset) {
532 : CSA_ASSERT(this,
533 : Word32BinaryNot(IsBigInt64ElementsKind(LoadElementsKind(dest))));
534 : TNode<ExternalReference> f = ExternalConstant(
535 56 : ExternalReference::copy_fast_number_jsarray_elements_to_typed_array());
536 : CallCFunction(f, MachineType::AnyTagged(),
537 : std::make_pair(MachineType::AnyTagged(), context),
538 : std::make_pair(MachineType::AnyTagged(), source),
539 : std::make_pair(MachineType::AnyTagged(), dest),
540 : std::make_pair(MachineType::UintPtr(), source_length),
541 56 : std::make_pair(MachineType::UintPtr(), offset));
542 56 : }
543 :
544 56 : void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
545 : TNode<JSTypedArray> source, TNode<JSTypedArray> dest,
546 : TNode<IntPtrT> source_length, TNode<IntPtrT> offset) {
547 : TNode<ExternalReference> f = ExternalConstant(
548 56 : ExternalReference::copy_typed_array_elements_to_typed_array());
549 : CallCFunction(f, MachineType::AnyTagged(),
550 : std::make_pair(MachineType::AnyTagged(), source),
551 : std::make_pair(MachineType::AnyTagged(), dest),
552 : std::make_pair(MachineType::UintPtr(), source_length),
553 56 : std::make_pair(MachineType::UintPtr(), offset));
554 56 : }
555 :
556 56 : void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsSlice(
557 : TNode<JSTypedArray> source, TNode<JSTypedArray> dest, TNode<IntPtrT> start,
558 : TNode<IntPtrT> end) {
559 : TNode<ExternalReference> f =
560 56 : ExternalConstant(ExternalReference::copy_typed_array_elements_slice());
561 : CallCFunction(f, MachineType::AnyTagged(),
562 : std::make_pair(MachineType::AnyTagged(), source),
563 : std::make_pair(MachineType::AnyTagged(), dest),
564 : std::make_pair(MachineType::UintPtr(), start),
565 56 : std::make_pair(MachineType::UintPtr(), end));
566 56 : }
567 :
568 616 : void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
569 : TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function) {
570 1232 : Label next(this), if_unknown_type(this, Label::kDeferred);
571 :
572 : int32_t elements_kinds[] = {
573 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) TYPE##_ELEMENTS,
574 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
575 : #undef TYPED_ARRAY_CASE
576 616 : };
577 :
578 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) Label if_##type##array(this);
579 616 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
580 : #undef TYPED_ARRAY_CASE
581 :
582 : Label* elements_kind_labels[] = {
583 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &if_##type##array,
584 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
585 : #undef TYPED_ARRAY_CASE
586 616 : };
587 : STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels));
588 :
589 : Switch(elements_kind, &if_unknown_type, elements_kinds, elements_kind_labels,
590 616 : arraysize(elements_kinds));
591 :
592 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
593 : BIND(&if_##type##array); \
594 : { \
595 : case_function(TYPE##_ELEMENTS, sizeof(ctype), \
596 : Context::TYPE##_ARRAY_FUN_INDEX); \
597 : Goto(&next); \
598 : }
599 7392 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
600 : #undef TYPED_ARRAY_CASE
601 :
602 616 : BIND(&if_unknown_type);
603 616 : Unreachable();
604 :
605 616 : BIND(&next);
606 616 : }
607 :
608 56 : TNode<BoolT> TypedArrayBuiltinsAssembler::IsSharedArrayBuffer(
609 : TNode<JSArrayBuffer> buffer) {
610 : TNode<Uint32T> bitfield =
611 56 : LoadObjectField<Uint32T>(buffer, JSArrayBuffer::kBitFieldOffset);
612 56 : return IsSetWord32<JSArrayBuffer::IsSharedBit>(bitfield);
613 : }
614 :
615 : // ES #sec-get-%typedarray%.prototype.set
616 224 : TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
617 : const char* method_name = "%TypedArray%.prototype.set";
618 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
619 : CodeStubArguments args(
620 : this,
621 168 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
622 :
623 56 : Label if_source_is_typed_array(this), if_source_is_fast_jsarray(this),
624 56 : if_offset_is_out_of_bounds(this, Label::kDeferred),
625 56 : if_source_too_large(this, Label::kDeferred),
626 56 : if_receiver_is_not_typedarray(this, Label::kDeferred);
627 :
628 : // Check the receiver is a typed array.
629 56 : TNode<Object> receiver = args.GetReceiver();
630 112 : GotoIf(TaggedIsSmi(receiver), &if_receiver_is_not_typedarray);
631 112 : GotoIfNot(IsJSTypedArray(CAST(receiver)), &if_receiver_is_not_typedarray);
632 :
633 : // Normalize offset argument (using ToInteger) and handle heap number cases.
634 112 : TNode<Object> offset = args.GetOptionalArgumentValue(1, SmiConstant(0));
635 : TNode<Number> offset_num =
636 56 : ToInteger_Inline(context, offset, kTruncateMinusZero);
637 :
638 : // Since ToInteger always returns a Smi if the given value is within Smi
639 : // range, and the only corner case of -0.0 has already been truncated to 0.0,
640 : // we can simply throw unless the offset is a non-negative Smi.
641 : // TODO(jgruber): It's an observable spec violation to throw here if
642 : // {offset_num} is a positive number outside the Smi range. Per spec, we need
643 : // to check for detached buffers and call the observable ToObject/ToLength
644 : // operations first.
645 112 : GotoIfNot(TaggedIsPositiveSmi(offset_num), &if_offset_is_out_of_bounds);
646 : TNode<Smi> offset_smi = CAST(offset_num);
647 :
648 : // Check the receiver is not detached.
649 56 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(receiver), method_name);
650 :
651 : // Check the source argument is valid and whether a fast path can be taken.
652 56 : Label call_runtime(this);
653 56 : TNode<Object> source = args.GetOptionalArgumentValue(0);
654 112 : GotoIf(TaggedIsSmi(source), &call_runtime);
655 112 : GotoIf(IsJSTypedArray(CAST(source)), &if_source_is_typed_array);
656 56 : BranchIfFastJSArray(source, context, &if_source_is_fast_jsarray,
657 56 : &call_runtime);
658 :
659 : // Fast path for a typed array source argument.
660 56 : BIND(&if_source_is_typed_array);
661 : {
662 : // Check the source argument is not detached.
663 56 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(source), method_name);
664 :
665 112 : SetTypedArraySource(context, CAST(source), CAST(receiver),
666 : SmiUntag(offset_smi), &call_runtime,
667 56 : &if_source_too_large);
668 112 : args.PopAndReturn(UndefinedConstant());
669 : }
670 :
671 : // Fast path for a fast JSArray source argument.
672 56 : BIND(&if_source_is_fast_jsarray);
673 : {
674 112 : SetJSArraySource(context, CAST(source), CAST(receiver),
675 56 : SmiUntag(offset_smi), &call_runtime, &if_source_too_large);
676 112 : args.PopAndReturn(UndefinedConstant());
677 : }
678 :
679 56 : BIND(&call_runtime);
680 : args.PopAndReturn(CallRuntime(Runtime::kTypedArraySet, context, receiver,
681 56 : source, offset_smi));
682 :
683 56 : BIND(&if_offset_is_out_of_bounds);
684 56 : ThrowRangeError(context, MessageTemplate::kTypedArraySetOffsetOutOfBounds);
685 :
686 56 : BIND(&if_source_too_large);
687 56 : ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge);
688 :
689 56 : BIND(&if_receiver_is_not_typedarray);
690 56 : ThrowTypeError(context, MessageTemplate::kNotTypedArray);
691 56 : }
692 :
693 : // ES #sec-get-%typedarray%.prototype-@@tostringtag
694 224 : TF_BUILTIN(TypedArrayPrototypeToStringTag, TypedArrayBuiltinsAssembler) {
695 : Node* receiver = Parameter(Descriptor::kReceiver);
696 56 : Label if_receiverisheapobject(this), return_undefined(this);
697 112 : Branch(TaggedIsSmi(receiver), &return_undefined, &if_receiverisheapobject);
698 :
699 : // Dispatch on the elements kind, offset by
700 : // FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND.
701 : size_t const kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
702 : FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
703 : 1;
704 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
705 : Label return_##type##array(this); \
706 : BIND(&return_##type##array); \
707 : Return(StringConstant(#Type "Array"));
708 1288 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
709 : #undef TYPED_ARRAY_CASE
710 : Label* elements_kind_labels[kTypedElementsKindCount] = {
711 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &return_##type##array,
712 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
713 : #undef TYPED_ARRAY_CASE
714 56 : };
715 : int32_t elements_kinds[kTypedElementsKindCount] = {
716 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
717 : TYPE##_ELEMENTS - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
718 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
719 : #undef TYPED_ARRAY_CASE
720 56 : };
721 :
722 : // We offset the dispatch by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, so
723 : // that this can be turned into a non-sparse table switch for ideal
724 : // performance.
725 56 : BIND(&if_receiverisheapobject);
726 : Node* elements_kind =
727 224 : Int32Sub(LoadElementsKind(receiver),
728 168 : Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND));
729 : Switch(elements_kind, &return_undefined, elements_kinds, elements_kind_labels,
730 56 : kTypedElementsKindCount);
731 :
732 56 : BIND(&return_undefined);
733 112 : Return(UndefinedConstant());
734 56 : }
735 :
736 168 : void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
737 : TNode<Context> context, TNode<Object> receiver, const char* method_name,
738 : IterationKind kind) {
739 336 : Label throw_bad_receiver(this, Label::kDeferred);
740 :
741 336 : GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
742 336 : GotoIfNot(IsJSTypedArray(CAST(receiver)), &throw_bad_receiver);
743 :
744 : // Check if the {receiver}'s JSArrayBuffer was detached.
745 168 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(receiver), method_name);
746 :
747 336 : Return(CreateArrayIterator(context, receiver, kind));
748 :
749 168 : BIND(&throw_bad_receiver);
750 168 : ThrowTypeError(context, MessageTemplate::kNotTypedArray, method_name);
751 168 : }
752 :
753 : // ES #sec-%typedarray%.prototype.values
754 280 : TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
755 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
756 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
757 56 : GenerateTypedArrayPrototypeIterationMethod(context, receiver,
758 : "%TypedArray%.prototype.values()",
759 56 : IterationKind::kValues);
760 56 : }
761 :
762 : // ES #sec-%typedarray%.prototype.entries
763 280 : TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
764 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
765 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
766 56 : GenerateTypedArrayPrototypeIterationMethod(context, receiver,
767 : "%TypedArray%.prototype.entries()",
768 56 : IterationKind::kEntries);
769 56 : }
770 :
771 : // ES #sec-%typedarray%.prototype.keys
772 280 : TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
773 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
774 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
775 56 : GenerateTypedArrayPrototypeIterationMethod(
776 56 : context, receiver, "%TypedArray%.prototype.keys()", IterationKind::kKeys);
777 56 : }
778 :
779 : // ES6 #sec-%typedarray%.of
780 280 : TF_BUILTIN(TypedArrayOf, TypedArrayBuiltinsAssembler) {
781 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
782 :
783 : // 1. Let len be the actual number of arguments passed to this function.
784 : TNode<IntPtrT> length = ChangeInt32ToIntPtr(
785 56 : UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
786 : // 2. Let items be the List of arguments passed to this function.
787 : CodeStubArguments args(this, length, nullptr, INTPTR_PARAMETERS,
788 56 : CodeStubArguments::ReceiverMode::kHasReceiver);
789 :
790 56 : Label if_not_constructor(this, Label::kDeferred),
791 56 : if_detached(this, Label::kDeferred);
792 :
793 : // 3. Let C be the this value.
794 : // 4. If IsConstructor(C) is false, throw a TypeError exception.
795 56 : TNode<Object> receiver = args.GetReceiver();
796 112 : GotoIf(TaggedIsSmi(receiver), &if_not_constructor);
797 112 : GotoIfNot(IsConstructor(CAST(receiver)), &if_not_constructor);
798 :
799 : // 5. Let newObj be ? TypedArrayCreate(C, len).
800 : TNode<JSTypedArray> new_typed_array = TypedArrayCreateByLength(
801 56 : context, receiver, SmiTag(length), "%TypedArray%.of");
802 :
803 112 : TNode<Word32T> elements_kind = LoadElementsKind(new_typed_array);
804 :
805 : // 6. Let k be 0.
806 : // 7. Repeat, while k < len
807 : // a. Let kValue be items[k].
808 : // b. Let Pk be ! ToString(k).
809 : // c. Perform ? Set(newObj, Pk, kValue, true).
810 : // d. Increase k by 1.
811 56 : DispatchTypedArrayByElementsKind(
812 : elements_kind,
813 616 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
814 : TNode<FixedTypedArrayBase> elements =
815 3696 : CAST(LoadElements(new_typed_array));
816 1232 : BuildFastLoop(
817 1848 : IntPtrConstant(0), length,
818 616 : [&](Node* index) {
819 1232 : TNode<Object> item = args.AtIndex(index, INTPTR_PARAMETERS);
820 3136 : TNode<IntPtrT> intptr_index = UncheckedCast<IntPtrT>(index);
821 1120 : if (kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS) {
822 1120 : EmitBigTypedArrayElementStore(new_typed_array, elements,
823 1232 : intptr_index, item, context,
824 1344 : &if_detached);
825 : } else {
826 : Node* value =
827 504 : PrepareValueForWriteToTypedArray(item, kind, context);
828 :
829 : // ToNumber may execute JavaScript code, which could detach
830 : // the array's buffer.
831 : Node* buffer = LoadObjectField(new_typed_array,
832 504 : JSTypedArray::kBufferOffset);
833 1008 : GotoIf(IsDetachedBuffer(buffer), &if_detached);
834 :
835 : // GC may move backing store in ToNumber, thus load backing
836 : // store everytime in this loop.
837 : TNode<RawPtrT> backing_store =
838 504 : LoadFixedTypedArrayBackingStore(elements);
839 1008 : StoreElement(backing_store, kind, index, value,
840 504 : INTPTR_PARAMETERS);
841 : }
842 616 : },
843 616 : 1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
844 672 : });
845 :
846 : // 8. Return newObj.
847 56 : args.PopAndReturn(new_typed_array);
848 :
849 56 : BIND(&if_not_constructor);
850 56 : ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
851 :
852 56 : BIND(&if_detached);
853 : ThrowTypeError(context, MessageTemplate::kDetachedOperation,
854 56 : "%TypedArray%.of");
855 56 : }
856 :
857 : // ES6 #sec-%typedarray%.from
858 280 : TF_BUILTIN(TypedArrayFrom, TypedArrayBuiltinsAssembler) {
859 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
860 :
861 56 : Label check_iterator(this), from_array_like(this), fast_path(this),
862 56 : slow_path(this), create_typed_array(this), check_typedarray(this),
863 56 : if_not_constructor(this, Label::kDeferred),
864 56 : if_map_fn_not_callable(this, Label::kDeferred),
865 56 : if_iterator_fn_not_callable(this, Label::kDeferred),
866 56 : if_detached(this, Label::kDeferred);
867 :
868 : CodeStubArguments args(
869 : this,
870 168 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
871 56 : TNode<Object> source = args.GetOptionalArgumentValue(0);
872 :
873 : // 5. If thisArg is present, let T be thisArg; else let T be undefined.
874 56 : TNode<Object> this_arg = args.GetOptionalArgumentValue(2);
875 :
876 : // 1. Let C be the this value.
877 : // 2. If IsConstructor(C) is false, throw a TypeError exception.
878 56 : TNode<Object> receiver = args.GetReceiver();
879 112 : GotoIf(TaggedIsSmi(receiver), &if_not_constructor);
880 112 : GotoIfNot(IsConstructor(CAST(receiver)), &if_not_constructor);
881 :
882 : // 3. If mapfn is present and mapfn is not undefined, then
883 56 : TNode<Object> map_fn = args.GetOptionalArgumentValue(1);
884 : TVARIABLE(BoolT, mapping, Int32FalseConstant());
885 112 : GotoIf(IsUndefined(map_fn), &check_typedarray);
886 :
887 : // a. If IsCallable(mapfn) is false, throw a TypeError exception.
888 : // b. Let mapping be true.
889 : // 4. Else, let mapping be false.
890 112 : GotoIf(TaggedIsSmi(map_fn), &if_map_fn_not_callable);
891 112 : GotoIfNot(IsCallable(CAST(map_fn)), &if_map_fn_not_callable);
892 : mapping = Int32TrueConstant();
893 56 : Goto(&check_typedarray);
894 :
895 : TVARIABLE(Object, final_source);
896 : TVARIABLE(Smi, final_length);
897 :
898 : // We split up this builtin differently to the way it is written in the spec.
899 : // We already have great code in the elements accessor for copying from a
900 : // JSArray into a TypedArray, so we use that when possible. We only avoid
901 : // calling into the elements accessor when we have a mapping function, because
902 : // we can't handle that. Here, presence of a mapping function is the slow
903 : // path. We also combine the two different loops in the specification
904 : // (starting at 7.e and 13) because they are essentially identical. We also
905 : // save on code-size this way.
906 :
907 : // Get the iterator function
908 56 : BIND(&check_typedarray);
909 : TNode<Object> iterator_fn =
910 112 : CAST(GetMethod(context, source, isolate()->factory()->iterator_symbol(),
911 : &from_array_like));
912 112 : GotoIf(TaggedIsSmi(iterator_fn), &if_iterator_fn_not_callable);
913 :
914 : {
915 : // TypedArrays have iterators, so normally we would go through the
916 : // IterableToList case below, which would convert the TypedArray to a
917 : // JSArray (boxing the values if they won't fit in a Smi).
918 : //
919 : // However, if we can guarantee that the source object has the built-in
920 : // iterator and that the %ArrayIteratorPrototype%.next method has not been
921 : // overridden, then we know the behavior of the iterator: returning the
922 : // values in the TypedArray sequentially from index 0 to length-1.
923 : //
924 : // In this case, we can avoid creating the intermediate array and the
925 : // associated HeapNumbers, and use the fast path in TypedArrayCopyElements
926 : // which uses the same ordering as the default iterator.
927 : //
928 : // Drop through to the default check_iterator behavior if any of these
929 : // checks fail.
930 :
931 : // Check that the source is a TypedArray
932 112 : GotoIf(TaggedIsSmi(source), &check_iterator);
933 112 : GotoIfNot(IsJSTypedArray(CAST(source)), &check_iterator);
934 : TNode<JSArrayBuffer> source_buffer =
935 56 : LoadJSArrayBufferViewBuffer(CAST(source));
936 112 : GotoIf(IsDetachedBuffer(source_buffer), &check_iterator);
937 :
938 : // Check that the iterator function is Builtins::kTypedArrayPrototypeValues
939 112 : GotoIfNot(IsJSFunction(CAST(iterator_fn)), &check_iterator);
940 : TNode<SharedFunctionInfo> shared_info = LoadObjectField<SharedFunctionInfo>(
941 : CAST(iterator_fn), JSFunction::kSharedFunctionInfoOffset);
942 56 : GotoIfNot(
943 : WordEqual(LoadObjectField(shared_info,
944 : SharedFunctionInfo::kFunctionDataOffset),
945 : SmiConstant(Builtins::kTypedArrayPrototypeValues)),
946 56 : &check_iterator);
947 : // Check that the ArrayIterator prototype's "next" method hasn't been
948 : // overridden
949 : TNode<PropertyCell> protector_cell =
950 56 : CAST(LoadRoot(RootIndex::kArrayIteratorProtector));
951 56 : GotoIfNot(
952 : WordEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
953 56 : SmiConstant(Isolate::kProtectorValid)),
954 56 : &check_iterator);
955 :
956 : // Source is a TypedArray with unmodified iterator behavior. Use the
957 : // source object directly, taking advantage of the special-case code in
958 : // TypedArrayCopyElements
959 56 : final_length = LoadJSTypedArrayLength(CAST(source));
960 : final_source = source;
961 56 : Goto(&create_typed_array);
962 : }
963 :
964 56 : BIND(&check_iterator);
965 : {
966 : // 6. Let usingIterator be ? GetMethod(source, @@iterator).
967 112 : GotoIfNot(IsCallable(CAST(iterator_fn)), &if_iterator_fn_not_callable);
968 :
969 : // We are using the iterator.
970 56 : Label if_length_not_smi(this, Label::kDeferred);
971 : // 7. If usingIterator is not undefined, then
972 : // a. Let values be ? IterableToList(source, usingIterator).
973 : // b. Let len be the number of elements in values.
974 56 : TNode<JSArray> values = CAST(
975 : CallBuiltin(Builtins::kIterableToList, context, source, iterator_fn));
976 :
977 : // This is not a spec'd limit, so it doesn't particularly matter when we
978 : // throw the range error for typed array length > MaxSmi.
979 112 : TNode<Object> raw_length = LoadJSArrayLength(values);
980 112 : GotoIfNot(TaggedIsSmi(raw_length), &if_length_not_smi);
981 :
982 : final_length = CAST(raw_length);
983 : final_source = values;
984 56 : Goto(&create_typed_array);
985 :
986 56 : BIND(&if_length_not_smi);
987 : ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
988 56 : raw_length);
989 : }
990 :
991 56 : BIND(&from_array_like);
992 : {
993 : // TODO(7881): support larger-than-smi typed array lengths
994 56 : Label if_length_not_smi(this, Label::kDeferred);
995 : final_source = source;
996 :
997 : // 10. Let len be ? ToLength(? Get(arrayLike, "length")).
998 : TNode<Object> raw_length =
999 112 : GetProperty(context, final_source.value(), LengthStringConstant());
1000 56 : final_length = ToSmiLength(context, raw_length, &if_length_not_smi);
1001 56 : Goto(&create_typed_array);
1002 :
1003 56 : BIND(&if_length_not_smi);
1004 : ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
1005 56 : raw_length);
1006 : }
1007 :
1008 : TVARIABLE(JSTypedArray, target_obj);
1009 :
1010 56 : BIND(&create_typed_array);
1011 : {
1012 : // 7c/11. Let targetObj be ? TypedArrayCreate(C, «len»).
1013 56 : target_obj = TypedArrayCreateByLength(
1014 : context, receiver, final_length.value(), "%TypedArray%.from");
1015 :
1016 56 : Branch(mapping.value(), &slow_path, &fast_path);
1017 : }
1018 :
1019 56 : BIND(&fast_path);
1020 : {
1021 56 : Label done(this);
1022 168 : GotoIf(SmiEqual(final_length.value(), SmiConstant(0)), &done);
1023 :
1024 : CallRuntime(Runtime::kTypedArrayCopyElements, context, target_obj.value(),
1025 : final_source.value(), final_length.value());
1026 56 : Goto(&done);
1027 :
1028 56 : BIND(&done);
1029 56 : args.PopAndReturn(target_obj.value());
1030 : }
1031 :
1032 56 : BIND(&slow_path);
1033 112 : TNode<Word32T> elements_kind = LoadElementsKind(target_obj.value());
1034 :
1035 : // 7e/13 : Copy the elements
1036 56 : TNode<FixedTypedArrayBase> elements = CAST(LoadElements(target_obj.value()));
1037 56 : BuildFastLoop(
1038 112 : SmiConstant(0), final_length.value(),
1039 56 : [&](Node* index) {
1040 : TNode<Object> const k_value =
1041 616 : GetProperty(context, final_source.value(), index);
1042 :
1043 : TNode<Object> const mapped_value =
1044 336 : CAST(CallJS(CodeFactory::Call(isolate()), context, map_fn, this_arg,
1045 : k_value, index));
1046 :
1047 112 : TNode<IntPtrT> intptr_index = SmiUntag(index);
1048 112 : DispatchTypedArrayByElementsKind(
1049 56 : elements_kind,
1050 616 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
1051 616 : if (kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS) {
1052 1288 : EmitBigTypedArrayElementStore(target_obj.value(), elements,
1053 616 : intptr_index, mapped_value,
1054 3416 : context, &if_detached);
1055 : } else {
1056 1008 : Node* const final_value = PrepareValueForWriteToTypedArray(
1057 504 : mapped_value, kind, context);
1058 :
1059 : // ToNumber may execute JavaScript code, which could detach
1060 : // the array's buffer.
1061 : Node* buffer = LoadObjectField(target_obj.value(),
1062 504 : JSTypedArray::kBufferOffset);
1063 1008 : GotoIf(IsDetachedBuffer(buffer), &if_detached);
1064 :
1065 : // GC may move backing store in map_fn, thus load backing
1066 : // store in each iteration of this loop.
1067 : TNode<RawPtrT> backing_store =
1068 504 : LoadFixedTypedArrayBackingStore(elements);
1069 1008 : StoreElement(backing_store, kind, index, final_value,
1070 504 : SMI_PARAMETERS);
1071 : }
1072 672 : });
1073 56 : },
1074 56 : 1, ParameterMode::SMI_PARAMETERS, IndexAdvanceMode::kPost);
1075 :
1076 56 : args.PopAndReturn(target_obj.value());
1077 :
1078 56 : BIND(&if_not_constructor);
1079 56 : ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
1080 :
1081 56 : BIND(&if_map_fn_not_callable);
1082 56 : ThrowTypeError(context, MessageTemplate::kCalledNonCallable, map_fn);
1083 :
1084 56 : BIND(&if_iterator_fn_not_callable);
1085 56 : ThrowTypeError(context, MessageTemplate::kIteratorSymbolNonCallable);
1086 :
1087 56 : BIND(&if_detached);
1088 : ThrowTypeError(context, MessageTemplate::kDetachedOperation,
1089 56 : "%TypedArray%.from");
1090 56 : }
1091 :
1092 : #undef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
1093 :
1094 : } // namespace internal
1095 59456 : } // namespace v8
|