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 168 : StoreObjectFieldNoWriteBarrier(holder, JSArrayBufferView::kByteOffsetOffset,
60 : byte_offset,
61 168 : MachineType::PointerRepresentation());
62 168 : StoreObjectFieldNoWriteBarrier(holder, JSArrayBufferView::kByteLengthOffset,
63 : byte_length,
64 168 : MachineType::PointerRepresentation());
65 840 : for (int offset = JSTypedArray::kHeaderSize;
66 840 : 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 224 : StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOrHashOffset,
86 112 : empty_fixed_array);
87 224 : StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
88 112 : 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 : buffer, JSArrayBuffer::kOptionalPaddingOffset, Int32Constant(0),
99 : MachineRepresentation::kWord32);
100 : }
101 : int32_t bitfield_value = (1 << JSArrayBuffer::IsExternalBit::kShift) |
102 112 : (1 << JSArrayBuffer::IsDetachableBit::kShift);
103 112 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
104 224 : Int32Constant(bitfield_value),
105 112 : MachineRepresentation::kWord32);
106 :
107 112 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
108 : byte_length,
109 112 : MachineType::PointerRepresentation());
110 224 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
111 224 : IntPtrConstant(0),
112 112 : MachineType::PointerRepresentation());
113 560 : for (int offset = JSArrayBuffer::kHeaderSize;
114 560 : offset < JSArrayBuffer::kSizeWithEmbedderFields; offset += kTaggedSize) {
115 448 : 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 112 : TNode<Object> elements;
131 :
132 224 : if (UnalignedLoadSupported(MachineRepresentation::kFloat64) &&
133 112 : UnalignedStoreSupported(MachineRepresentation::kFloat64)) {
134 112 : 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 112 : StoreObjectFieldNoWriteBarrier(
142 112 : elements, FixedTypedArrayBase::kBasePointerOffset, elements);
143 224 : 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 1568 : TNode<FixedArrayBase> elements = LoadElements(typed_array);
153 : CSA_ASSERT(this, IsFixedTypedArray(elements));
154 1568 : return LoadFixedTypedArrayBackingStore(CAST(elements));
155 : }
156 :
157 280 : TF_BUILTIN(TypedArrayBaseConstructor, TypedArrayBuiltinsAssembler) {
158 56 : 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 448 : TF_BUILTIN(TypedArrayConstructor, TypedArrayBuiltinsAssembler) {
165 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
166 56 : TNode<JSFunction> target = CAST(Parameter(Descriptor::kJSTarget));
167 56 : TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));
168 : Node* argc =
169 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
170 56 : CodeStubArguments args(this, argc);
171 56 : Node* arg1 = args.GetOptionalArgumentValue(0);
172 56 : Node* arg2 = args.GetOptionalArgumentValue(1);
173 56 : 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 112 : Label throwtypeerror(this, Label::kDeferred);
179 56 : GotoIf(IsUndefined(new_target), &throwtypeerror);
180 :
181 112 : Node* result = CallBuiltin(Builtins::kCreateTypedArray, context, target,
182 168 : new_target, arg1, arg2, arg3);
183 56 : args.PopAndReturn(result);
184 :
185 56 : BIND(&throwtypeerror);
186 : {
187 : TNode<String> name =
188 56 : 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 336 : TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
195 56 : const char* const kMethodName = "get TypedArray.prototype.byteLength";
196 56 : Node* context = Parameter(Descriptor::kContext);
197 56 : 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 112 : [=] { return LoadJSArrayBufferViewByteLength(CAST(receiver)); });
208 56 : Return(ChangeUintPtrToTagged(byte_length));
209 56 : }
210 :
211 : // ES6 #sec-get-%typedarray%.prototype.byteoffset
212 336 : TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
213 56 : const char* const kMethodName = "get TypedArray.prototype.byteOffset";
214 56 : Node* context = Parameter(Descriptor::kContext);
215 56 : 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 112 : [=] { return LoadJSArrayBufferViewByteOffset(CAST(receiver)); });
226 56 : Return(ChangeUintPtrToTagged(byte_offset));
227 56 : }
228 :
229 : // ES6 #sec-get-%typedarray%.prototype.length
230 336 : TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
231 56 : const char* const kMethodName = "get TypedArray.prototype.length";
232 56 : Node* context = Parameter(Descriptor::kContext);
233 56 : 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 112 : [=] { return LoadJSTypedArrayLength(CAST(receiver)); });
244 56 : Return(length);
245 56 : }
246 :
247 112 : TNode<Word32T> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
248 : TNode<Word32T> kind) {
249 224 : return Word32Or(Word32Equal(kind, Int32Constant(UINT8_ELEMENTS)),
250 336 : Word32Equal(kind, Int32Constant(UINT8_CLAMPED_ELEMENTS)));
251 : }
252 :
253 280 : TNode<Word32T> TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
254 : TNode<Word32T> kind) {
255 560 : return Word32Or(Word32Equal(kind, Int32Constant(BIGINT64_ELEMENTS)),
256 840 : Word32Equal(kind, Int32Constant(BIGUINT64_ELEMENTS)));
257 : }
258 :
259 112 : TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize(
260 : TNode<Word32T> elements_kind) {
261 224 : TVARIABLE(IntPtrT, element_size);
262 :
263 224 : DispatchTypedArrayByElementsKind(
264 : elements_kind,
265 1232 : [&](ElementsKind el_kind, int size, int typed_array_fun_index) {
266 1232 : element_size = IntPtrConstant(size);
267 1344 : });
268 :
269 224 : 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 336 : TVARIABLE(UintPtrT, var_size_log2);
277 336 : 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 3696 : var_size_log2 = UintPtrConstant(ElementsKindToShiftSize(kind));
285 :
286 1848 : Handle<Map> map(roots.MapForFixedTypedArray(kind), isolate());
287 1848 : 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 448 : TVARIABLE(IntPtrT, context_slot);
297 224 : TNode<Word32T> elements_kind = LoadElementsKind(exemplar);
298 :
299 448 : DispatchTypedArrayByElementsKind(
300 : elements_kind,
301 2464 : [&](ElementsKind el_kind, int size, int typed_array_function_index) {
302 2464 : context_slot = IntPtrConstant(typed_array_function_index);
303 2688 : });
304 :
305 448 : 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 112 : 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 112 : 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 112 : TVARIABLE(Object, var_result);
342 :
343 56 : TNode<Object> buffer = LoadObjectField(array, JSTypedArray::kBufferOffset);
344 56 : GotoIf(IsDetachedBuffer(buffer), &call_runtime);
345 : TNode<UintPtrT> backing_store = LoadObjectField<UintPtrT>(
346 56 : CAST(buffer), JSArrayBuffer::kBackingStoreOffset);
347 56 : GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
348 56 : var_result = buffer;
349 56 : Goto(&done);
350 :
351 56 : BIND(&call_runtime);
352 : {
353 56 : var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array);
354 56 : Goto(&done);
355 : }
356 :
357 56 : BIND(&done);
358 112 : 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 56 : TNode<IntPtrT> source_length = SmiUntag(LoadJSTypedArrayLength(source));
387 56 : 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 56 : TNode<Word32T> source_el_kind = LoadElementsKind(source);
399 56 : 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 56 : TNode<IntPtrT> source_byte_length = IntPtrMul(source_length, source_el_size);
408 : CSA_ASSERT(this,
409 : UintPtrGreaterThanOrEqual(source_byte_length, IntPtrConstant(0)));
410 :
411 112 : 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 56 : GotoIf(Word32Equal(source_el_kind, target_el_kind), &call_memmove);
416 56 : GotoIfNot(IsUint8ElementsKind(source_el_kind), &fast_c_call);
417 56 : 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 224 : IsBigInt64ElementsKind(target_el_kind)),
435 56 : &exception);
436 :
437 56 : 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 56 : TNode<IntPtrT> source_length = SmiUntag(LoadFastJSArrayLength(source));
458 56 : TNode<IntPtrT> target_length = SmiUntag(LoadJSTypedArrayLength(target));
459 :
460 : // Maybe out of bounds?
461 112 : GotoIf(IntPtrGreaterThan(IntPtrAdd(source_length, offset), target_length),
462 56 : if_source_too_large);
463 :
464 : // Nothing to do if {source} is empty.
465 112 : Label out(this), fast_c_call(this);
466 56 : 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 56 : Switch(source_elements_kind, call_runtime, values, labels,
482 56 : arraysize(values));
483 : }
484 :
485 56 : BIND(&fast_c_call);
486 56 : 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 112 : CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
499 : MachineType::Pointer(), MachineType::UintPtr(), memmove,
500 112 : dest_ptr, src_ptr, byte_length);
501 112 : }
502 :
503 56 : void TypedArrayBuiltinsAssembler::CallCMemcpy(TNode<RawPtrT> dest_ptr,
504 : TNode<RawPtrT> src_ptr,
505 : TNode<UintPtrT> byte_length) {
506 : TNode<ExternalReference> memcpy =
507 56 : ExternalConstant(ExternalReference::libc_memcpy_function());
508 56 : CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
509 : MachineType::Pointer(), MachineType::UintPtr(), memcpy,
510 56 : dest_ptr, src_ptr, byte_length);
511 56 : }
512 :
513 56 : void TypedArrayBuiltinsAssembler::CallCMemset(TNode<RawPtrT> dest_ptr,
514 : TNode<IntPtrT> value,
515 : TNode<UintPtrT> length) {
516 : TNode<ExternalReference> memset =
517 56 : ExternalConstant(ExternalReference::libc_memset_function());
518 56 : CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
519 : MachineType::IntPtr(), MachineType::UintPtr(), memset,
520 56 : dest_ptr, value, length);
521 56 : }
522 :
523 56 : void TypedArrayBuiltinsAssembler::
524 : CallCCopyFastNumberJSArrayElementsToTypedArray(TNode<Context> context,
525 : TNode<JSArray> source,
526 : TNode<JSTypedArray> dest,
527 : TNode<IntPtrT> source_length,
528 : TNode<IntPtrT> offset) {
529 : CSA_ASSERT(this,
530 : Word32BinaryNot(IsBigInt64ElementsKind(LoadElementsKind(dest))));
531 : TNode<ExternalReference> f = ExternalConstant(
532 56 : ExternalReference::copy_fast_number_jsarray_elements_to_typed_array());
533 56 : CallCFunction5(MachineType::AnyTagged(), MachineType::AnyTagged(),
534 : MachineType::AnyTagged(), MachineType::AnyTagged(),
535 : MachineType::UintPtr(), MachineType::UintPtr(), f, context,
536 56 : source, dest, source_length, offset);
537 56 : }
538 :
539 56 : void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
540 : TNode<JSTypedArray> source, TNode<JSTypedArray> dest,
541 : TNode<IntPtrT> source_length, TNode<IntPtrT> offset) {
542 : TNode<ExternalReference> f = ExternalConstant(
543 56 : ExternalReference::copy_typed_array_elements_to_typed_array());
544 56 : CallCFunction4(MachineType::AnyTagged(), MachineType::AnyTagged(),
545 : MachineType::AnyTagged(), MachineType::UintPtr(),
546 : MachineType::UintPtr(), f, source, dest, source_length,
547 56 : offset);
548 56 : }
549 :
550 56 : void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsSlice(
551 : TNode<JSTypedArray> source, TNode<JSTypedArray> dest, TNode<IntPtrT> start,
552 : TNode<IntPtrT> end) {
553 : TNode<ExternalReference> f =
554 56 : ExternalConstant(ExternalReference::copy_typed_array_elements_slice());
555 56 : CallCFunction4(MachineType::AnyTagged(), MachineType::AnyTagged(),
556 : MachineType::AnyTagged(), MachineType::UintPtr(),
557 56 : MachineType::UintPtr(), f, source, dest, start, end);
558 56 : }
559 :
560 616 : void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
561 : TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function) {
562 1232 : Label next(this), if_unknown_type(this, Label::kDeferred);
563 :
564 : int32_t elements_kinds[] = {
565 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) TYPE##_ELEMENTS,
566 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
567 : #undef TYPED_ARRAY_CASE
568 616 : };
569 :
570 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) Label if_##type##array(this);
571 1232 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
572 : #undef TYPED_ARRAY_CASE
573 :
574 : Label* elements_kind_labels[] = {
575 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &if_##type##array,
576 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
577 : #undef TYPED_ARRAY_CASE
578 616 : };
579 : STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels));
580 :
581 616 : Switch(elements_kind, &if_unknown_type, elements_kinds, elements_kind_labels,
582 616 : arraysize(elements_kinds));
583 :
584 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
585 : BIND(&if_##type##array); \
586 : { \
587 : case_function(TYPE##_ELEMENTS, sizeof(ctype), \
588 : Context::TYPE##_ARRAY_FUN_INDEX); \
589 : Goto(&next); \
590 : }
591 616 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
592 : #undef TYPED_ARRAY_CASE
593 :
594 616 : BIND(&if_unknown_type);
595 616 : Unreachable();
596 :
597 616 : BIND(&next);
598 616 : }
599 :
600 56 : TNode<BoolT> TypedArrayBuiltinsAssembler::IsSharedArrayBuffer(
601 : TNode<JSArrayBuffer> buffer) {
602 : TNode<Uint32T> bitfield =
603 56 : LoadObjectField<Uint32T>(buffer, JSArrayBuffer::kBitFieldOffset);
604 56 : return IsSetWord32<JSArrayBuffer::IsSharedBit>(bitfield);
605 : }
606 :
607 : // ES #sec-get-%typedarray%.prototype.set
608 336 : TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
609 56 : const char* method_name = "%TypedArray%.prototype.set";
610 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
611 : CodeStubArguments args(
612 : this,
613 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
614 :
615 112 : Label if_source_is_typed_array(this), if_source_is_fast_jsarray(this),
616 112 : if_offset_is_out_of_bounds(this, Label::kDeferred),
617 112 : if_source_too_large(this, Label::kDeferred),
618 112 : if_receiver_is_not_typedarray(this, Label::kDeferred);
619 :
620 : // Check the receiver is a typed array.
621 56 : TNode<Object> receiver = args.GetReceiver();
622 56 : GotoIf(TaggedIsSmi(receiver), &if_receiver_is_not_typedarray);
623 56 : GotoIfNot(IsJSTypedArray(CAST(receiver)), &if_receiver_is_not_typedarray);
624 :
625 : // Normalize offset argument (using ToInteger) and handle heap number cases.
626 56 : TNode<Object> offset = args.GetOptionalArgumentValue(1, SmiConstant(0));
627 : TNode<Number> offset_num =
628 56 : ToInteger_Inline(context, offset, kTruncateMinusZero);
629 :
630 : // Since ToInteger always returns a Smi if the given value is within Smi
631 : // range, and the only corner case of -0.0 has already been truncated to 0.0,
632 : // we can simply throw unless the offset is a non-negative Smi.
633 : // TODO(jgruber): It's an observable spec violation to throw here if
634 : // {offset_num} is a positive number outside the Smi range. Per spec, we need
635 : // to check for detached buffers and call the observable ToObject/ToLength
636 : // operations first.
637 56 : GotoIfNot(TaggedIsPositiveSmi(offset_num), &if_offset_is_out_of_bounds);
638 56 : TNode<Smi> offset_smi = CAST(offset_num);
639 :
640 : // Check the receiver is not detached.
641 56 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(receiver), method_name);
642 :
643 : // Check the source argument is valid and whether a fast path can be taken.
644 112 : Label call_runtime(this);
645 56 : TNode<Object> source = args.GetOptionalArgumentValue(0);
646 56 : GotoIf(TaggedIsSmi(source), &call_runtime);
647 56 : GotoIf(IsJSTypedArray(CAST(source)), &if_source_is_typed_array);
648 56 : BranchIfFastJSArray(source, context, &if_source_is_fast_jsarray,
649 56 : &call_runtime);
650 :
651 : // Fast path for a typed array source argument.
652 56 : BIND(&if_source_is_typed_array);
653 : {
654 : // Check the source argument is not detached.
655 56 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(source), method_name);
656 :
657 112 : SetTypedArraySource(context, CAST(source), CAST(receiver),
658 : SmiUntag(offset_smi), &call_runtime,
659 56 : &if_source_too_large);
660 56 : args.PopAndReturn(UndefinedConstant());
661 : }
662 :
663 : // Fast path for a fast JSArray source argument.
664 56 : BIND(&if_source_is_fast_jsarray);
665 : {
666 112 : SetJSArraySource(context, CAST(source), CAST(receiver),
667 56 : SmiUntag(offset_smi), &call_runtime, &if_source_too_large);
668 56 : args.PopAndReturn(UndefinedConstant());
669 : }
670 :
671 56 : BIND(&call_runtime);
672 112 : args.PopAndReturn(CallRuntime(Runtime::kTypedArraySet, context, receiver,
673 168 : source, offset_smi));
674 :
675 56 : BIND(&if_offset_is_out_of_bounds);
676 56 : ThrowRangeError(context, MessageTemplate::kTypedArraySetOffsetOutOfBounds);
677 :
678 56 : BIND(&if_source_too_large);
679 56 : ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge);
680 :
681 56 : BIND(&if_receiver_is_not_typedarray);
682 56 : ThrowTypeError(context, MessageTemplate::kNotTypedArray);
683 56 : }
684 :
685 : // ES #sec-get-%typedarray%.prototype-@@tostringtag
686 280 : TF_BUILTIN(TypedArrayPrototypeToStringTag, TypedArrayBuiltinsAssembler) {
687 56 : Node* receiver = Parameter(Descriptor::kReceiver);
688 112 : Label if_receiverisheapobject(this), return_undefined(this);
689 56 : Branch(TaggedIsSmi(receiver), &return_undefined, &if_receiverisheapobject);
690 :
691 : // Dispatch on the elements kind, offset by
692 : // FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND.
693 : size_t const kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
694 : FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
695 56 : 1;
696 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
697 : Label return_##type##array(this); \
698 : BIND(&return_##type##array); \
699 : Return(StringConstant(#Type "Array"));
700 112 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
701 : #undef TYPED_ARRAY_CASE
702 : Label* elements_kind_labels[kTypedElementsKindCount] = {
703 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &return_##type##array,
704 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
705 : #undef TYPED_ARRAY_CASE
706 56 : };
707 : int32_t elements_kinds[kTypedElementsKindCount] = {
708 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
709 : TYPE##_ELEMENTS - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
710 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
711 : #undef TYPED_ARRAY_CASE
712 56 : };
713 :
714 : // We offset the dispatch by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, so
715 : // that this can be turned into a non-sparse table switch for ideal
716 : // performance.
717 56 : BIND(&if_receiverisheapobject);
718 : Node* elements_kind =
719 224 : Int32Sub(LoadElementsKind(receiver),
720 280 : Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND));
721 56 : Switch(elements_kind, &return_undefined, elements_kinds, elements_kind_labels,
722 56 : kTypedElementsKindCount);
723 :
724 56 : BIND(&return_undefined);
725 56 : Return(UndefinedConstant());
726 56 : }
727 :
728 168 : void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
729 : TNode<Context> context, TNode<Object> receiver, const char* method_name,
730 : IterationKind kind) {
731 336 : Label throw_bad_receiver(this, Label::kDeferred);
732 :
733 168 : GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
734 168 : GotoIfNot(IsJSTypedArray(CAST(receiver)), &throw_bad_receiver);
735 :
736 : // Check if the {receiver}'s JSArrayBuffer was detached.
737 168 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(receiver), method_name);
738 :
739 168 : Return(CreateArrayIterator(context, receiver, kind));
740 :
741 168 : BIND(&throw_bad_receiver);
742 168 : ThrowTypeError(context, MessageTemplate::kNotTypedArray, method_name);
743 168 : }
744 :
745 : // ES #sec-%typedarray%.prototype.values
746 336 : TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
747 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
748 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
749 56 : GenerateTypedArrayPrototypeIterationMethod(context, receiver,
750 : "%TypedArray%.prototype.values()",
751 56 : IterationKind::kValues);
752 56 : }
753 :
754 : // ES #sec-%typedarray%.prototype.entries
755 336 : TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
756 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
757 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
758 56 : GenerateTypedArrayPrototypeIterationMethod(context, receiver,
759 : "%TypedArray%.prototype.entries()",
760 56 : IterationKind::kEntries);
761 56 : }
762 :
763 : // ES #sec-%typedarray%.prototype.keys
764 336 : TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
765 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
766 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
767 56 : GenerateTypedArrayPrototypeIterationMethod(
768 56 : context, receiver, "%TypedArray%.prototype.keys()", IterationKind::kKeys);
769 56 : }
770 :
771 : // ES6 #sec-%typedarray%.of
772 336 : TF_BUILTIN(TypedArrayOf, TypedArrayBuiltinsAssembler) {
773 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
774 :
775 : // 1. Let len be the actual number of arguments passed to this function.
776 : TNode<IntPtrT> length = ChangeInt32ToIntPtr(
777 56 : UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
778 : // 2. Let items be the List of arguments passed to this function.
779 : CodeStubArguments args(this, length, nullptr, INTPTR_PARAMETERS,
780 56 : CodeStubArguments::ReceiverMode::kHasReceiver);
781 :
782 112 : Label if_not_constructor(this, Label::kDeferred),
783 112 : if_detached(this, Label::kDeferred);
784 :
785 : // 3. Let C be the this value.
786 : // 4. If IsConstructor(C) is false, throw a TypeError exception.
787 56 : TNode<Object> receiver = args.GetReceiver();
788 56 : GotoIf(TaggedIsSmi(receiver), &if_not_constructor);
789 56 : GotoIfNot(IsConstructor(CAST(receiver)), &if_not_constructor);
790 :
791 : // 5. Let newObj be ? TypedArrayCreate(C, len).
792 : TNode<JSTypedArray> new_typed_array = TypedArrayCreateByLength(
793 56 : context, receiver, SmiTag(length), "%TypedArray%.of");
794 :
795 56 : TNode<Word32T> elements_kind = LoadElementsKind(new_typed_array);
796 :
797 : // 6. Let k be 0.
798 : // 7. Repeat, while k < len
799 : // a. Let kValue be items[k].
800 : // b. Let Pk be ! ToString(k).
801 : // c. Perform ? Set(newObj, Pk, kValue, true).
802 : // d. Increase k by 1.
803 112 : DispatchTypedArrayByElementsKind(
804 : elements_kind,
805 616 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
806 : TNode<FixedTypedArrayBase> elements =
807 3080 : CAST(LoadElements(new_typed_array));
808 5544 : BuildFastLoop(
809 1848 : IntPtrConstant(0), length,
810 616 : [&](Node* index) {
811 1232 : TNode<Object> item = args.AtIndex(index, INTPTR_PARAMETERS);
812 3752 : TNode<IntPtrT> intptr_index = UncheckedCast<IntPtrT>(index);
813 1624 : if (kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS) {
814 1232 : EmitBigTypedArrayElementStore(new_typed_array, elements,
815 1232 : intptr_index, item, context,
816 1344 : &if_detached);
817 : } else {
818 : Node* value =
819 1008 : PrepareValueForWriteToTypedArray(item, kind, context);
820 :
821 : // ToNumber may execute JavaScript code, which could detach
822 : // the array's buffer.
823 1008 : Node* buffer = LoadObjectField(new_typed_array,
824 2016 : JSTypedArray::kBufferOffset);
825 1008 : GotoIf(IsDetachedBuffer(buffer), &if_detached);
826 :
827 : // GC may move backing store in ToNumber, thus load backing
828 : // store everytime in this loop.
829 : TNode<RawPtrT> backing_store =
830 504 : LoadFixedTypedArrayBackingStore(elements);
831 1008 : StoreElement(backing_store, kind, index, value,
832 504 : INTPTR_PARAMETERS);
833 : }
834 616 : },
835 616 : 1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
836 672 : });
837 :
838 : // 8. Return newObj.
839 56 : args.PopAndReturn(new_typed_array);
840 :
841 56 : BIND(&if_not_constructor);
842 56 : ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
843 :
844 56 : BIND(&if_detached);
845 56 : ThrowTypeError(context, MessageTemplate::kDetachedOperation,
846 56 : "%TypedArray%.of");
847 56 : }
848 :
849 : // ES6 #sec-%typedarray%.from
850 336 : TF_BUILTIN(TypedArrayFrom, TypedArrayBuiltinsAssembler) {
851 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
852 :
853 112 : Label check_iterator(this), from_array_like(this), fast_path(this),
854 112 : slow_path(this), create_typed_array(this), check_typedarray(this),
855 112 : if_not_constructor(this, Label::kDeferred),
856 112 : if_map_fn_not_callable(this, Label::kDeferred),
857 112 : if_iterator_fn_not_callable(this, Label::kDeferred),
858 112 : if_detached(this, Label::kDeferred);
859 :
860 : CodeStubArguments args(
861 : this,
862 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
863 56 : TNode<Object> source = args.GetOptionalArgumentValue(0);
864 :
865 : // 5. If thisArg is present, let T be thisArg; else let T be undefined.
866 56 : TNode<Object> this_arg = args.GetOptionalArgumentValue(2);
867 :
868 : // 1. Let C be the this value.
869 : // 2. If IsConstructor(C) is false, throw a TypeError exception.
870 56 : TNode<Object> receiver = args.GetReceiver();
871 56 : GotoIf(TaggedIsSmi(receiver), &if_not_constructor);
872 56 : GotoIfNot(IsConstructor(CAST(receiver)), &if_not_constructor);
873 :
874 : // 3. If mapfn is present and mapfn is not undefined, then
875 56 : TNode<Object> map_fn = args.GetOptionalArgumentValue(1);
876 112 : TVARIABLE(BoolT, mapping, Int32FalseConstant());
877 56 : GotoIf(IsUndefined(map_fn), &check_typedarray);
878 :
879 : // a. If IsCallable(mapfn) is false, throw a TypeError exception.
880 : // b. Let mapping be true.
881 : // 4. Else, let mapping be false.
882 56 : GotoIf(TaggedIsSmi(map_fn), &if_map_fn_not_callable);
883 56 : GotoIfNot(IsCallable(CAST(map_fn)), &if_map_fn_not_callable);
884 56 : mapping = Int32TrueConstant();
885 56 : Goto(&check_typedarray);
886 :
887 112 : TVARIABLE(Object, final_source);
888 112 : TVARIABLE(Smi, final_length);
889 :
890 : // We split up this builtin differently to the way it is written in the spec.
891 : // We already have great code in the elements accessor for copying from a
892 : // JSArray into a TypedArray, so we use that when possible. We only avoid
893 : // calling into the elements accessor when we have a mapping function, because
894 : // we can't handle that. Here, presence of a mapping function is the slow
895 : // path. We also combine the two different loops in the specification
896 : // (starting at 7.e and 13) because they are essentially identical. We also
897 : // save on code-size this way.
898 :
899 : // Get the iterator function
900 56 : BIND(&check_typedarray);
901 : TNode<Object> iterator_fn =
902 112 : CAST(GetMethod(context, source, isolate()->factory()->iterator_symbol(),
903 : &from_array_like));
904 56 : GotoIf(TaggedIsSmi(iterator_fn), &if_iterator_fn_not_callable);
905 :
906 : {
907 : // TypedArrays have iterators, so normally we would go through the
908 : // IterableToList case below, which would convert the TypedArray to a
909 : // JSArray (boxing the values if they won't fit in a Smi).
910 : //
911 : // However, if we can guarantee that the source object has the built-in
912 : // iterator and that the %ArrayIteratorPrototype%.next method has not been
913 : // overridden, then we know the behavior of the iterator: returning the
914 : // values in the TypedArray sequentially from index 0 to length-1.
915 : //
916 : // In this case, we can avoid creating the intermediate array and the
917 : // associated HeapNumbers, and use the fast path in TypedArrayCopyElements
918 : // which uses the same ordering as the default iterator.
919 : //
920 : // Drop through to the default check_iterator behavior if any of these
921 : // checks fail.
922 :
923 : // Check that the source is a TypedArray
924 56 : GotoIf(TaggedIsSmi(source), &check_iterator);
925 56 : GotoIfNot(IsJSTypedArray(CAST(source)), &check_iterator);
926 : TNode<JSArrayBuffer> source_buffer =
927 56 : LoadJSArrayBufferViewBuffer(CAST(source));
928 56 : GotoIf(IsDetachedBuffer(source_buffer), &check_iterator);
929 :
930 : // Check that the iterator function is Builtins::kTypedArrayPrototypeValues
931 56 : GotoIfNot(IsJSFunction(CAST(iterator_fn)), &check_iterator);
932 : TNode<SharedFunctionInfo> shared_info = LoadObjectField<SharedFunctionInfo>(
933 56 : CAST(iterator_fn), JSFunction::kSharedFunctionInfoOffset);
934 112 : GotoIfNot(
935 112 : WordEqual(LoadObjectField(shared_info,
936 : SharedFunctionInfo::kFunctionDataOffset),
937 112 : SmiConstant(Builtins::kTypedArrayPrototypeValues)),
938 56 : &check_iterator);
939 : // Check that the ArrayIterator prototype's "next" method hasn't been
940 : // overridden
941 : TNode<PropertyCell> protector_cell =
942 56 : CAST(LoadRoot(RootIndex::kArrayIteratorProtector));
943 112 : GotoIfNot(
944 112 : WordEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
945 112 : SmiConstant(Isolate::kProtectorValid)),
946 56 : &check_iterator);
947 :
948 : // Source is a TypedArray with unmodified iterator behavior. Use the
949 : // source object directly, taking advantage of the special-case code in
950 : // TypedArrayCopyElements
951 56 : final_length = LoadJSTypedArrayLength(CAST(source));
952 56 : final_source = source;
953 56 : Goto(&create_typed_array);
954 : }
955 :
956 56 : BIND(&check_iterator);
957 : {
958 : // 6. Let usingIterator be ? GetMethod(source, @@iterator).
959 56 : GotoIfNot(IsCallable(CAST(iterator_fn)), &if_iterator_fn_not_callable);
960 :
961 : // We are using the iterator.
962 112 : Label if_length_not_smi(this, Label::kDeferred);
963 : // 7. If usingIterator is not undefined, then
964 : // a. Let values be ? IterableToList(source, usingIterator).
965 : // b. Let len be the number of elements in values.
966 56 : TNode<JSArray> values = CAST(
967 : CallBuiltin(Builtins::kIterableToList, context, source, iterator_fn));
968 :
969 : // This is not a spec'd limit, so it doesn't particularly matter when we
970 : // throw the range error for typed array length > MaxSmi.
971 56 : TNode<Object> raw_length = LoadJSArrayLength(values);
972 56 : GotoIfNot(TaggedIsSmi(raw_length), &if_length_not_smi);
973 :
974 56 : final_length = CAST(raw_length);
975 56 : final_source = values;
976 56 : Goto(&create_typed_array);
977 :
978 56 : BIND(&if_length_not_smi);
979 56 : ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
980 56 : raw_length);
981 : }
982 :
983 56 : BIND(&from_array_like);
984 : {
985 : // TODO(7881): support larger-than-smi typed array lengths
986 112 : Label if_length_not_smi(this, Label::kDeferred);
987 56 : final_source = source;
988 :
989 : // 10. Let len be ? ToLength(? Get(arrayLike, "length")).
990 : TNode<Object> raw_length =
991 56 : GetProperty(context, final_source.value(), LengthStringConstant());
992 56 : final_length = ToSmiLength(context, raw_length, &if_length_not_smi);
993 56 : Goto(&create_typed_array);
994 :
995 56 : BIND(&if_length_not_smi);
996 56 : ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
997 56 : raw_length);
998 : }
999 :
1000 112 : TVARIABLE(JSTypedArray, target_obj);
1001 :
1002 56 : BIND(&create_typed_array);
1003 : {
1004 : // 7c/11. Let targetObj be ? TypedArrayCreate(C, «len»).
1005 56 : target_obj = TypedArrayCreateByLength(
1006 56 : context, receiver, final_length.value(), "%TypedArray%.from");
1007 :
1008 56 : Branch(mapping.value(), &slow_path, &fast_path);
1009 : }
1010 :
1011 56 : BIND(&fast_path);
1012 : {
1013 112 : Label done(this);
1014 56 : GotoIf(SmiEqual(final_length.value(), SmiConstant(0)), &done);
1015 :
1016 : CallRuntime(Runtime::kTypedArrayCopyElements, context, target_obj.value(),
1017 56 : final_source.value(), final_length.value());
1018 56 : Goto(&done);
1019 :
1020 56 : BIND(&done);
1021 56 : args.PopAndReturn(target_obj.value());
1022 : }
1023 :
1024 56 : BIND(&slow_path);
1025 56 : TNode<Word32T> elements_kind = LoadElementsKind(target_obj.value());
1026 :
1027 : // 7e/13 : Copy the elements
1028 56 : TNode<FixedTypedArrayBase> elements = CAST(LoadElements(target_obj.value()));
1029 224 : BuildFastLoop(
1030 168 : SmiConstant(0), final_length.value(),
1031 56 : [&](Node* index) {
1032 : TNode<Object> const k_value =
1033 504 : GetProperty(context, final_source.value(), index);
1034 :
1035 : TNode<Object> const mapped_value =
1036 280 : CAST(CallJS(CodeFactory::Call(isolate()), context, map_fn, this_arg,
1037 : k_value, index));
1038 :
1039 112 : TNode<IntPtrT> intptr_index = SmiUntag(index);
1040 392 : DispatchTypedArrayByElementsKind(
1041 56 : elements_kind,
1042 616 : [&](ElementsKind kind, int size, int typed_array_fun_index) {
1043 616 : if (kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS) {
1044 1344 : EmitBigTypedArrayElementStore(target_obj.value(), elements,
1045 616 : intptr_index, mapped_value,
1046 3416 : context, &if_detached);
1047 : } else {
1048 1008 : Node* const final_value = PrepareValueForWriteToTypedArray(
1049 504 : mapped_value, kind, context);
1050 :
1051 : // ToNumber may execute JavaScript code, which could detach
1052 : // the array's buffer.
1053 2016 : Node* buffer = LoadObjectField(target_obj.value(),
1054 2016 : JSTypedArray::kBufferOffset);
1055 1008 : GotoIf(IsDetachedBuffer(buffer), &if_detached);
1056 :
1057 : // GC may move backing store in map_fn, thus load backing
1058 : // store in each iteration of this loop.
1059 : TNode<RawPtrT> backing_store =
1060 504 : LoadFixedTypedArrayBackingStore(elements);
1061 1008 : StoreElement(backing_store, kind, index, final_value,
1062 504 : SMI_PARAMETERS);
1063 : }
1064 672 : });
1065 56 : },
1066 56 : 1, ParameterMode::SMI_PARAMETERS, IndexAdvanceMode::kPost);
1067 :
1068 56 : args.PopAndReturn(target_obj.value());
1069 :
1070 56 : BIND(&if_not_constructor);
1071 56 : ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
1072 :
1073 56 : BIND(&if_map_fn_not_callable);
1074 56 : ThrowTypeError(context, MessageTemplate::kCalledNonCallable, map_fn);
1075 :
1076 56 : BIND(&if_iterator_fn_not_callable);
1077 56 : ThrowTypeError(context, MessageTemplate::kIteratorSymbolNonCallable);
1078 :
1079 56 : BIND(&if_detached);
1080 56 : ThrowTypeError(context, MessageTemplate::kDetachedOperation,
1081 56 : "%TypedArray%.from");
1082 56 : }
1083 :
1084 : #undef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
1085 :
1086 : } // namespace internal
1087 87414 : } // namespace v8
|