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