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/execution/protectors.h"
12 #include "src/handles/handles-inl.h"
13 #include "src/heap/factory-inl.h"
14 
15 namespace v8 {
16 namespace internal {
17 
18 using compiler::Node;
19 
20 // -----------------------------------------------------------------------------
21 // ES6 section 22.2 TypedArray Objects
22 
23 // Sets the embedder fields to 0 for a TypedArray which is under construction.
SetupTypedArrayEmbedderFields(TNode<JSTypedArray> holder)24 void TypedArrayBuiltinsAssembler::SetupTypedArrayEmbedderFields(
25     TNode<JSTypedArray> holder) {
26   for (int offset = JSTypedArray::kHeaderSize;
27        offset < JSTypedArray::kSizeWithEmbedderFields; offset += kTaggedSize) {
28     StoreObjectField(holder, offset, SmiConstant(0));
29   }
30 }
31 
32 // Allocate a new ArrayBuffer and initialize it with empty properties and
33 // elements.
34 // TODO(bmeurer,v8:4153): Rename this and maybe fix up the implementation a bit.
AllocateEmptyOnHeapBuffer(TNode<Context> context,TNode<UintPtrT> byte_length)35 TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
36     TNode<Context> context, TNode<UintPtrT> byte_length) {
37   TNode<NativeContext> native_context = LoadNativeContext(context);
38   TNode<Map> map =
39       CAST(LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX));
40   TNode<FixedArray> empty_fixed_array = EmptyFixedArrayConstant();
41 
42   TNode<JSArrayBuffer> buffer = UncheckedCast<JSArrayBuffer>(
43       Allocate(JSArrayBuffer::kSizeWithEmbedderFields));
44   StoreMapNoWriteBarrier(buffer, map);
45   StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOrHashOffset,
46                                  empty_fixed_array);
47   StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
48                                  empty_fixed_array);
49   // Setup the ArrayBuffer.
50   //  - Set BitField to 0.
51   //  - Set IsExternal and IsDetachable bits of BitFieldSlot.
52   //  - Set the byte_length field to byte_length.
53   //  - Set backing_store to null/Smi(0).
54   //  - Set extension to null.
55   //  - Set all embedder fields to Smi(0).
56   if (FIELD_SIZE(JSArrayBuffer::kOptionalPaddingOffset) != 0) {
57     DCHECK_EQ(4, FIELD_SIZE(JSArrayBuffer::kOptionalPaddingOffset));
58     StoreObjectFieldNoWriteBarrier(
59         buffer, JSArrayBuffer::kOptionalPaddingOffset, Int32Constant(0));
60   }
61   int32_t bitfield_value = (1 << JSArrayBuffer::IsExternalBit::kShift) |
62                            (1 << JSArrayBuffer::IsDetachableBit::kShift);
63   StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
64                                  Int32Constant(bitfield_value));
65 
66   StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
67                                  byte_length);
68   StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
69                                  IntPtrConstant(0));
70   if (V8_ARRAY_BUFFER_EXTENSION_BOOL) {
71     StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kExtensionOffset,
72                                    IntPtrConstant(0));
73   }
74   for (int offset = JSArrayBuffer::kHeaderSize;
75        offset < JSArrayBuffer::kSizeWithEmbedderFields; offset += kTaggedSize) {
76     StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(0));
77   }
78   return buffer;
79 }
80 
TF_BUILTIN(TypedArrayBaseConstructor,TypedArrayBuiltinsAssembler)81 TF_BUILTIN(TypedArrayBaseConstructor, TypedArrayBuiltinsAssembler) {
82   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
83   ThrowTypeError(context, MessageTemplate::kConstructAbstractClass,
84                  "TypedArray");
85 }
86 
87 // ES #sec-typedarray-constructors
TF_BUILTIN(TypedArrayConstructor,TypedArrayBuiltinsAssembler)88 TF_BUILTIN(TypedArrayConstructor, TypedArrayBuiltinsAssembler) {
89   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
90   TNode<JSFunction> target = CAST(Parameter(Descriptor::kJSTarget));
91   TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));
92   TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
93       UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
94   CodeStubArguments args(this, argc);
95   TNode<Object> arg1 = args.GetOptionalArgumentValue(0);
96   TNode<Object> arg2 = args.GetOptionalArgumentValue(1);
97   TNode<Object> arg3 = args.GetOptionalArgumentValue(2);
98 
99   // If NewTarget is undefined, throw a TypeError exception.
100   // All the TypedArray constructors have this as the first step:
101   // https://tc39.github.io/ecma262/#sec-typedarray-constructors
102   Label throwtypeerror(this, Label::kDeferred);
103   GotoIf(IsUndefined(new_target), &throwtypeerror);
104 
105   TNode<Object> result = CallBuiltin(Builtins::kCreateTypedArray, context,
106                                      target, new_target, arg1, arg2, arg3);
107   args.PopAndReturn(result);
108 
109   BIND(&throwtypeerror);
110   {
111     TNode<String> name =
112         CAST(CallRuntime(Runtime::kGetFunctionName, context, target));
113     ThrowTypeError(context, MessageTemplate::kConstructorNotFunction, name);
114   }
115 }
116 
117 // ES6 #sec-get-%typedarray%.prototype.bytelength
TF_BUILTIN(TypedArrayPrototypeByteLength,TypedArrayBuiltinsAssembler)118 TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
119   const char* const kMethodName = "get TypedArray.prototype.byteLength";
120   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
121   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
122 
123   // Check if the {receiver} is actually a JSTypedArray.
124   ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
125 
126   // Default to zero if the {receiver}s buffer was detached.
127   TNode<JSArrayBuffer> receiver_buffer =
128       LoadJSArrayBufferViewBuffer(CAST(receiver));
129   TNode<UintPtrT> byte_length = Select<UintPtrT>(
130       IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
131       [=] { return LoadJSArrayBufferViewByteLength(CAST(receiver)); });
132   Return(ChangeUintPtrToTagged(byte_length));
133 }
134 
135 // ES6 #sec-get-%typedarray%.prototype.byteoffset
TF_BUILTIN(TypedArrayPrototypeByteOffset,TypedArrayBuiltinsAssembler)136 TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
137   const char* const kMethodName = "get TypedArray.prototype.byteOffset";
138   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
139   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
140 
141   // Check if the {receiver} is actually a JSTypedArray.
142   ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
143 
144   // Default to zero if the {receiver}s buffer was detached.
145   TNode<JSArrayBuffer> receiver_buffer =
146       LoadJSArrayBufferViewBuffer(CAST(receiver));
147   TNode<UintPtrT> byte_offset = Select<UintPtrT>(
148       IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
149       [=] { return LoadJSArrayBufferViewByteOffset(CAST(receiver)); });
150   Return(ChangeUintPtrToTagged(byte_offset));
151 }
152 
153 // ES6 #sec-get-%typedarray%.prototype.length
TF_BUILTIN(TypedArrayPrototypeLength,TypedArrayBuiltinsAssembler)154 TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
155   const char* const kMethodName = "get TypedArray.prototype.length";
156   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
157   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
158 
159   // Check if the {receiver} is actually a JSTypedArray.
160   ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
161 
162   // Default to zero if the {receiver}s buffer was detached.
163   TNode<JSArrayBuffer> receiver_buffer =
164       LoadJSArrayBufferViewBuffer(CAST(receiver));
165   TNode<UintPtrT> length = Select<UintPtrT>(
166       IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
167       [=] { return LoadJSTypedArrayLength(CAST(receiver)); });
168   Return(ChangeUintPtrToTagged(length));
169 }
170 
IsUint8ElementsKind(TNode<Int32T> kind)171 TNode<BoolT> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
172     TNode<Int32T> kind) {
173   return Word32Or(Word32Equal(kind, Int32Constant(UINT8_ELEMENTS)),
174                   Word32Equal(kind, Int32Constant(UINT8_CLAMPED_ELEMENTS)));
175 }
176 
IsBigInt64ElementsKind(TNode<Int32T> kind)177 TNode<BoolT> TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
178     TNode<Int32T> kind) {
179   STATIC_ASSERT(BIGUINT64_ELEMENTS + 1 == BIGINT64_ELEMENTS);
180   return IsElementsKindInRange(kind, BIGUINT64_ELEMENTS, BIGINT64_ELEMENTS);
181 }
182 
GetTypedArrayElementSize(TNode<Int32T> elements_kind)183 TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize(
184     TNode<Int32T> elements_kind) {
185   TVARIABLE(IntPtrT, element_size);
186 
187   DispatchTypedArrayByElementsKind(
188       elements_kind,
189       [&](ElementsKind el_kind, int size, int typed_array_fun_index) {
190         element_size = IntPtrConstant(size);
191       });
192 
193   return element_size.value();
194 }
195 
196 TorqueStructTypedArrayElementsInfo
GetTypedArrayElementsInfo(TNode<JSTypedArray> typed_array)197 TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
198     TNode<JSTypedArray> typed_array) {
199   return GetTypedArrayElementsInfo(LoadMap(typed_array));
200 }
201 
202 TorqueStructTypedArrayElementsInfo
GetTypedArrayElementsInfo(TNode<Map> map)203 TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(TNode<Map> map) {
204   TNode<Int32T> elements_kind = LoadMapElementsKind(map);
205   TVARIABLE(UintPtrT, var_size_log2);
206   TVARIABLE(Map, var_map);
207   ReadOnlyRoots roots(isolate());
208 
209   DispatchTypedArrayByElementsKind(
210       elements_kind,
211       [&](ElementsKind kind, int size, int typed_array_fun_index) {
212         DCHECK_GT(size, 0);
213         var_size_log2 = UintPtrConstant(ElementsKindToShiftSize(kind));
214       });
215 
216   return TorqueStructTypedArrayElementsInfo{var_size_log2.value(),
217                                             elements_kind};
218 }
219 
GetDefaultConstructor(TNode<Context> context,TNode<JSTypedArray> exemplar)220 TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
221     TNode<Context> context, TNode<JSTypedArray> exemplar) {
222   TVARIABLE(IntPtrT, context_slot);
223   TNode<Int32T> elements_kind = LoadElementsKind(exemplar);
224 
225   DispatchTypedArrayByElementsKind(
226       elements_kind,
227       [&](ElementsKind el_kind, int size, int typed_array_function_index) {
228         context_slot = IntPtrConstant(typed_array_function_index);
229       });
230 
231   return CAST(
232       LoadContextElement(LoadNativeContext(context), context_slot.value()));
233 }
234 
GetBuffer(TNode<Context> context,TNode<JSTypedArray> array)235 TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
236     TNode<Context> context, TNode<JSTypedArray> array) {
237   Label call_runtime(this), done(this);
238   TVARIABLE(Object, var_result);
239 
240   TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array);
241   GotoIf(IsDetachedBuffer(buffer), &call_runtime);
242   TNode<RawPtrT> backing_store = LoadJSArrayBufferBackingStore(buffer);
243   GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
244   var_result = buffer;
245   Goto(&done);
246 
247   BIND(&call_runtime);
248   {
249     var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array);
250     Goto(&done);
251   }
252 
253   BIND(&done);
254   return CAST(var_result.value());
255 }
256 
ValidateTypedArray(TNode<Context> context,TNode<Object> obj,const char * method_name)257 TNode<JSTypedArray> TypedArrayBuiltinsAssembler::ValidateTypedArray(
258     TNode<Context> context, TNode<Object> obj, const char* method_name) {
259   // If it is not a typed array, throw
260   ThrowIfNotInstanceType(context, obj, JS_TYPED_ARRAY_TYPE, method_name);
261 
262   // If the typed array's buffer is detached, throw
263   ThrowIfArrayBufferViewBufferIsDetached(context, CAST(obj), method_name);
264 
265   return CAST(obj);
266 }
267 
CallCMemmove(TNode<RawPtrT> dest_ptr,TNode<RawPtrT> src_ptr,TNode<UintPtrT> byte_length)268 void TypedArrayBuiltinsAssembler::CallCMemmove(TNode<RawPtrT> dest_ptr,
269                                                TNode<RawPtrT> src_ptr,
270                                                TNode<UintPtrT> byte_length) {
271   TNode<ExternalReference> memmove =
272       ExternalConstant(ExternalReference::libc_memmove_function());
273   CallCFunction(memmove, MachineType::AnyTagged(),
274                 std::make_pair(MachineType::Pointer(), dest_ptr),
275                 std::make_pair(MachineType::Pointer(), src_ptr),
276                 std::make_pair(MachineType::UintPtr(), byte_length));
277 }
278 
CallCMemcpy(TNode<RawPtrT> dest_ptr,TNode<RawPtrT> src_ptr,TNode<UintPtrT> byte_length)279 void TypedArrayBuiltinsAssembler::CallCMemcpy(TNode<RawPtrT> dest_ptr,
280                                               TNode<RawPtrT> src_ptr,
281                                               TNode<UintPtrT> byte_length) {
282   TNode<ExternalReference> memcpy =
283       ExternalConstant(ExternalReference::libc_memcpy_function());
284   CallCFunction(memcpy, MachineType::AnyTagged(),
285                 std::make_pair(MachineType::Pointer(), dest_ptr),
286                 std::make_pair(MachineType::Pointer(), src_ptr),
287                 std::make_pair(MachineType::UintPtr(), byte_length));
288 }
289 
CallCMemset(TNode<RawPtrT> dest_ptr,TNode<IntPtrT> value,TNode<UintPtrT> length)290 void TypedArrayBuiltinsAssembler::CallCMemset(TNode<RawPtrT> dest_ptr,
291                                               TNode<IntPtrT> value,
292                                               TNode<UintPtrT> length) {
293   TNode<ExternalReference> memset =
294       ExternalConstant(ExternalReference::libc_memset_function());
295   CallCFunction(memset, MachineType::AnyTagged(),
296                 std::make_pair(MachineType::Pointer(), dest_ptr),
297                 std::make_pair(MachineType::IntPtr(), value),
298                 std::make_pair(MachineType::UintPtr(), length));
299 }
300 
301 void TypedArrayBuiltinsAssembler::
CallCCopyFastNumberJSArrayElementsToTypedArray(TNode<Context> context,TNode<JSArray> source,TNode<JSTypedArray> dest,TNode<UintPtrT> source_length,TNode<UintPtrT> offset)302     CallCCopyFastNumberJSArrayElementsToTypedArray(
303         TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> dest,
304         TNode<UintPtrT> source_length, TNode<UintPtrT> offset) {
305   CSA_ASSERT(this,
306              Word32BinaryNot(IsBigInt64ElementsKind(LoadElementsKind(dest))));
307   TNode<ExternalReference> f = ExternalConstant(
308       ExternalReference::copy_fast_number_jsarray_elements_to_typed_array());
309   CallCFunction(f, MachineType::AnyTagged(),
310                 std::make_pair(MachineType::AnyTagged(), context),
311                 std::make_pair(MachineType::AnyTagged(), source),
312                 std::make_pair(MachineType::AnyTagged(), dest),
313                 std::make_pair(MachineType::UintPtr(), source_length),
314                 std::make_pair(MachineType::UintPtr(), offset));
315 }
316 
CallCCopyTypedArrayElementsToTypedArray(TNode<JSTypedArray> source,TNode<JSTypedArray> dest,TNode<UintPtrT> source_length,TNode<UintPtrT> offset)317 void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
318     TNode<JSTypedArray> source, TNode<JSTypedArray> dest,
319     TNode<UintPtrT> source_length, TNode<UintPtrT> offset) {
320   TNode<ExternalReference> f = ExternalConstant(
321       ExternalReference::copy_typed_array_elements_to_typed_array());
322   CallCFunction(f, MachineType::AnyTagged(),
323                 std::make_pair(MachineType::AnyTagged(), source),
324                 std::make_pair(MachineType::AnyTagged(), dest),
325                 std::make_pair(MachineType::UintPtr(), source_length),
326                 std::make_pair(MachineType::UintPtr(), offset));
327 }
328 
CallCCopyTypedArrayElementsSlice(TNode<JSTypedArray> source,TNode<JSTypedArray> dest,TNode<UintPtrT> start,TNode<UintPtrT> end)329 void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsSlice(
330     TNode<JSTypedArray> source, TNode<JSTypedArray> dest, TNode<UintPtrT> start,
331     TNode<UintPtrT> end) {
332   TNode<ExternalReference> f =
333       ExternalConstant(ExternalReference::copy_typed_array_elements_slice());
334   CallCFunction(f, MachineType::AnyTagged(),
335                 std::make_pair(MachineType::AnyTagged(), source),
336                 std::make_pair(MachineType::AnyTagged(), dest),
337                 std::make_pair(MachineType::UintPtr(), start),
338                 std::make_pair(MachineType::UintPtr(), end));
339 }
340 
DispatchTypedArrayByElementsKind(TNode<Word32T> elements_kind,const TypedArraySwitchCase & case_function)341 void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
342     TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function) {
343   Label next(this), if_unknown_type(this, Label::kDeferred);
344 
345   int32_t elements_kinds[] = {
346 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) TYPE##_ELEMENTS,
347       TYPED_ARRAYS(TYPED_ARRAY_CASE)
348 #undef TYPED_ARRAY_CASE
349   };
350 
351 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) Label if_##type##array(this);
352   TYPED_ARRAYS(TYPED_ARRAY_CASE)
353 #undef TYPED_ARRAY_CASE
354 
355   Label* elements_kind_labels[] = {
356 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &if_##type##array,
357       TYPED_ARRAYS(TYPED_ARRAY_CASE)
358 #undef TYPED_ARRAY_CASE
359   };
360   STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels));
361 
362   Switch(elements_kind, &if_unknown_type, elements_kinds, elements_kind_labels,
363          arraysize(elements_kinds));
364 
365 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype)   \
366   BIND(&if_##type##array);                          \
367   {                                                 \
368     case_function(TYPE##_ELEMENTS, sizeof(ctype),   \
369                   Context::TYPE##_ARRAY_FUN_INDEX); \
370     Goto(&next);                                    \
371   }
372   TYPED_ARRAYS(TYPED_ARRAY_CASE)
373 #undef TYPED_ARRAY_CASE
374 
375   BIND(&if_unknown_type);
376   Unreachable();
377 
378   BIND(&next);
379 }
380 
SetJSTypedArrayOnHeapDataPtr(TNode<JSTypedArray> holder,TNode<ByteArray> base,TNode<UintPtrT> offset)381 void TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
382     TNode<JSTypedArray> holder, TNode<ByteArray> base, TNode<UintPtrT> offset) {
383   offset = UintPtrAdd(UintPtrConstant(ByteArray::kHeaderSize - kHeapObjectTag),
384                       offset);
385   if (COMPRESS_POINTERS_BOOL) {
386     TNode<IntPtrT> full_base = Signed(BitcastTaggedToWord(base));
387     TNode<Int32T> compressed_base = TruncateIntPtrToInt32(full_base);
388     // TODO(v8:9706): Add a way to directly use kRootRegister value.
389     TNode<IntPtrT> isolate_root =
390         IntPtrSub(full_base, Signed(ChangeUint32ToWord(compressed_base)));
391     // Add JSTypedArray::ExternalPointerCompensationForOnHeapArray() to offset.
392     DCHECK_EQ(
393         isolate()->isolate_root(),
394         JSTypedArray::ExternalPointerCompensationForOnHeapArray(isolate()));
395     // See JSTypedArray::SetOnHeapDataPtr() for details.
396     offset = Unsigned(IntPtrAdd(offset, isolate_root));
397   }
398 
399   StoreObjectField(holder, JSTypedArray::kBasePointerOffset, base);
400   StoreObjectFieldNoWriteBarrier<UintPtrT>(
401       holder, JSTypedArray::kExternalPointerOffset, offset);
402 }
403 
SetJSTypedArrayOffHeapDataPtr(TNode<JSTypedArray> holder,TNode<RawPtrT> base,TNode<UintPtrT> offset)404 void TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr(
405     TNode<JSTypedArray> holder, TNode<RawPtrT> base, TNode<UintPtrT> offset) {
406   StoreObjectFieldNoWriteBarrier(holder, JSTypedArray::kBasePointerOffset,
407                                  SmiConstant(0));
408 
409   base = RawPtrAdd(base, Signed(offset));
410   StoreObjectFieldNoWriteBarrier<RawPtrT>(
411       holder, JSTypedArray::kExternalPointerOffset, base);
412 }
413 
StoreJSTypedArrayElementFromNumeric(TNode<Context> context,TNode<JSTypedArray> typed_array,TNode<UintPtrT> index,TNode<Numeric> value,ElementsKind elements_kind)414 void TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric(
415     TNode<Context> context, TNode<JSTypedArray> typed_array,
416     TNode<UintPtrT> index, TNode<Numeric> value, ElementsKind elements_kind) {
417   TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(typed_array);
418   switch (elements_kind) {
419     case UINT8_ELEMENTS:
420     case UINT8_CLAMPED_ELEMENTS:
421     case INT8_ELEMENTS:
422     case UINT16_ELEMENTS:
423     case INT16_ELEMENTS:
424       StoreElement(data_ptr, elements_kind, index, SmiToInt32(CAST(value)));
425       break;
426     case UINT32_ELEMENTS:
427     case INT32_ELEMENTS:
428       StoreElement(data_ptr, elements_kind, index,
429                    TruncateTaggedToWord32(context, value));
430       break;
431     case FLOAT32_ELEMENTS:
432       StoreElement(data_ptr, elements_kind, index,
433                    TruncateFloat64ToFloat32(LoadHeapNumberValue(CAST(value))));
434       break;
435     case FLOAT64_ELEMENTS:
436       StoreElement(data_ptr, elements_kind, index,
437                    LoadHeapNumberValue(CAST(value)));
438       break;
439     case BIGUINT64_ELEMENTS:
440     case BIGINT64_ELEMENTS:
441       StoreElement(data_ptr, elements_kind, index,
442                    UncheckedCast<BigInt>(value));
443       break;
444     default:
445       UNREACHABLE();
446   }
447 }
448 
StoreJSTypedArrayElementFromTagged(TNode<Context> context,TNode<JSTypedArray> typed_array,TNode<UintPtrT> index,TNode<Object> value,ElementsKind elements_kind,Label * if_detached)449 void TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged(
450     TNode<Context> context, TNode<JSTypedArray> typed_array,
451     TNode<UintPtrT> index, TNode<Object> value, ElementsKind elements_kind,
452     Label* if_detached) {
453   // |prepared_value| is Word32T or Float64T or Float32T or BigInt.
454   Node* prepared_value =
455       PrepareValueForWriteToTypedArray(value, elements_kind, context);
456 
457   // ToNumber/ToBigInt may execute JavaScript code, which could detach
458   // the array's buffer.
459   TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(typed_array);
460   GotoIf(IsDetachedBuffer(buffer), if_detached);
461 
462   TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(typed_array);
463   StoreElement(data_ptr, elements_kind, index, prepared_value);
464 }
465 
466 // ES #sec-get-%typedarray%.prototype-@@tostringtag
TF_BUILTIN(TypedArrayPrototypeToStringTag,TypedArrayBuiltinsAssembler)467 TF_BUILTIN(TypedArrayPrototypeToStringTag, TypedArrayBuiltinsAssembler) {
468   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
469   Label if_receiverisheapobject(this), return_undefined(this);
470   Branch(TaggedIsSmi(receiver), &return_undefined, &if_receiverisheapobject);
471 
472   // Dispatch on the elements kind, offset by
473   // FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND.
474   size_t const kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
475                                          FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
476                                          1;
477 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
478   Label return_##type##array(this);               \
479   BIND(&return_##type##array);                    \
480   Return(StringConstant(#Type "Array"));
481   TYPED_ARRAYS(TYPED_ARRAY_CASE)
482 #undef TYPED_ARRAY_CASE
483   Label* elements_kind_labels[kTypedElementsKindCount] = {
484 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &return_##type##array,
485       TYPED_ARRAYS(TYPED_ARRAY_CASE)
486 #undef TYPED_ARRAY_CASE
487   };
488   int32_t elements_kinds[kTypedElementsKindCount] = {
489 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
490   TYPE##_ELEMENTS - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
491       TYPED_ARRAYS(TYPED_ARRAY_CASE)
492 #undef TYPED_ARRAY_CASE
493   };
494 
495   // We offset the dispatch by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, so that
496   // this can be turned into a non-sparse table switch for ideal performance.
497   BIND(&if_receiverisheapobject);
498   TNode<HeapObject> receiver_heap_object = CAST(receiver);
499   TNode<Int32T> elements_kind =
500       Int32Sub(LoadElementsKind(receiver_heap_object),
501                Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND));
502   Switch(elements_kind, &return_undefined, elements_kinds, elements_kind_labels,
503          kTypedElementsKindCount);
504 
505   BIND(&return_undefined);
506   Return(UndefinedConstant());
507 }
508 
GenerateTypedArrayPrototypeIterationMethod(TNode<Context> context,TNode<Object> receiver,const char * method_name,IterationKind kind)509 void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
510     TNode<Context> context, TNode<Object> receiver, const char* method_name,
511     IterationKind kind) {
512   Label throw_bad_receiver(this, Label::kDeferred);
513 
514   GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
515   GotoIfNot(IsJSTypedArray(CAST(receiver)), &throw_bad_receiver);
516 
517   // Check if the {receiver}'s JSArrayBuffer was detached.
518   ThrowIfArrayBufferViewBufferIsDetached(context, CAST(receiver), method_name);
519 
520   Return(CreateArrayIterator(context, receiver, kind));
521 
522   BIND(&throw_bad_receiver);
523   ThrowTypeError(context, MessageTemplate::kNotTypedArray, method_name);
524 }
525 
526 // ES #sec-%typedarray%.prototype.values
TF_BUILTIN(TypedArrayPrototypeValues,TypedArrayBuiltinsAssembler)527 TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
528   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
529   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
530   GenerateTypedArrayPrototypeIterationMethod(context, receiver,
531                                              "%TypedArray%.prototype.values()",
532                                              IterationKind::kValues);
533 }
534 
535 // ES #sec-%typedarray%.prototype.entries
TF_BUILTIN(TypedArrayPrototypeEntries,TypedArrayBuiltinsAssembler)536 TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
537   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
538   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
539   GenerateTypedArrayPrototypeIterationMethod(context, receiver,
540                                              "%TypedArray%.prototype.entries()",
541                                              IterationKind::kEntries);
542 }
543 
544 // ES #sec-%typedarray%.prototype.keys
TF_BUILTIN(TypedArrayPrototypeKeys,TypedArrayBuiltinsAssembler)545 TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
546   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
547   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
548   GenerateTypedArrayPrototypeIterationMethod(
549       context, receiver, "%TypedArray%.prototype.keys()", IterationKind::kKeys);
550 }
551 
552 }  // namespace internal
553 }  // namespace v8
554