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-array-gen.h" 6 7 #include "src/builtins/builtins-iterator-gen.h" 8 #include "src/builtins/builtins-string-gen.h" 9 #include "src/builtins/builtins-typed-array-gen.h" 10 #include "src/builtins/builtins-utils-gen.h" 11 #include "src/builtins/builtins.h" 12 #include "src/code-stub-assembler.h" 13 #include "src/frame-constants.h" 14 #include "src/heap/factory-inl.h" 15 #include "src/objects/arguments-inl.h" 16 17 namespace v8 { 18 namespace internal { 19 20 using Node = compiler::Node; 21 22 ArrayBuiltinsAssembler::ArrayBuiltinsAssembler( 23 compiler::CodeAssemblerState* state) 24 : BaseBuiltinsFromDSLAssembler(state), 25 k_(this, MachineRepresentation::kTagged), 26 a_(this, MachineRepresentation::kTagged), 27 to_(this, MachineRepresentation::kTagged, SmiConstant(0)), 28 fully_spec_compliant_(this, {&k_, &a_, &to_}) {} 29 30 void ArrayBuiltinsAssembler::FindResultGenerator() { 31 a_.Bind(UndefinedConstant()); 32 } 33 34 Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { 35 Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), 36 this_arg(), k_value, k, o()); 37 Label false_continue(this), return_true(this); 38 BranchIfToBooleanIsTrue(value, &return_true, &false_continue); 39 BIND(&return_true); 40 ReturnFromBuiltin(k_value); 41 BIND(&false_continue); 42 return a(); 43 } 44 45 void ArrayBuiltinsAssembler::FindIndexResultGenerator() { 46 a_.Bind(SmiConstant(-1)); 47 } 48 49 Node* ArrayBuiltinsAssembler::FindIndexProcessor(Node* k_value, Node* k) { 50 Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), 51 this_arg(), k_value, k, o()); 52 Label false_continue(this), return_true(this); 53 BranchIfToBooleanIsTrue(value, &return_true, &false_continue); 54 BIND(&return_true); 55 ReturnFromBuiltin(k); 56 BIND(&false_continue); 57 return a(); 58 } 59 60 void ArrayBuiltinsAssembler::ForEachResultGenerator() { 61 a_.Bind(UndefinedConstant()); 62 } 63 64 Node* ArrayBuiltinsAssembler::ForEachProcessor(Node* k_value, Node* k) { 65 CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), this_arg(), 66 k_value, k, o()); 67 return a(); 68 } 69 70 void ArrayBuiltinsAssembler::SomeResultGenerator() { 71 a_.Bind(FalseConstant()); 72 } 73 74 Node* ArrayBuiltinsAssembler::SomeProcessor(Node* k_value, Node* k) { 75 Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), 76 this_arg(), k_value, k, o()); 77 Label false_continue(this), return_true(this); 78 BranchIfToBooleanIsTrue(value, &return_true, &false_continue); 79 BIND(&return_true); 80 ReturnFromBuiltin(TrueConstant()); 81 BIND(&false_continue); 82 return a(); 83 } 84 85 void ArrayBuiltinsAssembler::EveryResultGenerator() { 86 a_.Bind(TrueConstant()); 87 } 88 89 Node* ArrayBuiltinsAssembler::EveryProcessor(Node* k_value, Node* k) { 90 Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), 91 this_arg(), k_value, k, o()); 92 Label true_continue(this), return_false(this); 93 BranchIfToBooleanIsTrue(value, &true_continue, &return_false); 94 BIND(&return_false); 95 ReturnFromBuiltin(FalseConstant()); 96 BIND(&true_continue); 97 return a(); 98 } 99 100 void ArrayBuiltinsAssembler::ReduceResultGenerator() { 101 return a_.Bind(this_arg()); 102 } 103 104 Node* ArrayBuiltinsAssembler::ReduceProcessor(Node* k_value, Node* k) { 105 VARIABLE(result, MachineRepresentation::kTagged); 106 Label done(this, {&result}), initial(this); 107 GotoIf(WordEqual(a(), TheHoleConstant()), &initial); 108 result.Bind(CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), 109 UndefinedConstant(), a(), k_value, k, o())); 110 Goto(&done); 111 112 BIND(&initial); 113 result.Bind(k_value); 114 Goto(&done); 115 116 BIND(&done); 117 return result.value(); 118 } 119 120 void ArrayBuiltinsAssembler::ReducePostLoopAction() { 121 Label ok(this); 122 GotoIf(WordNotEqual(a(), TheHoleConstant()), &ok); 123 ThrowTypeError(context(), MessageTemplate::kReduceNoInitial); 124 BIND(&ok); 125 } 126 127 void ArrayBuiltinsAssembler::FilterResultGenerator() { 128 // 7. Let A be ArraySpeciesCreate(O, 0). 129 // This version of ArraySpeciesCreate will create with the correct 130 // ElementsKind in the fast case. 131 GenerateArraySpeciesCreate(); 132 } 133 134 Node* ArrayBuiltinsAssembler::FilterProcessor(Node* k_value, Node* k) { 135 // ii. Let selected be ToBoolean(? Call(callbackfn, T, kValue, k, O)). 136 Node* selected = CallJS(CodeFactory::Call(isolate()), context(), 137 callbackfn(), this_arg(), k_value, k, o()); 138 Label true_continue(this, &to_), false_continue(this); 139 BranchIfToBooleanIsTrue(selected, &true_continue, &false_continue); 140 BIND(&true_continue); 141 // iii. If selected is true, then... 142 { 143 Label after_work(this, &to_); 144 Node* kind = nullptr; 145 146 // If a() is a JSArray, we can have a fast path. 147 Label fast(this); 148 Label runtime(this); 149 Label object_push_pre(this), object_push(this), double_push(this); 150 BranchIfFastJSArray(a(), context(), &fast, &runtime); 151 152 BIND(&fast); 153 { 154 GotoIf(WordNotEqual(LoadJSArrayLength(a()), to_.value()), &runtime); 155 kind = EnsureArrayPushable(LoadMap(a()), &runtime); 156 GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS), 157 &object_push_pre); 158 159 BuildAppendJSArray(HOLEY_SMI_ELEMENTS, a(), k_value, &runtime); 160 Goto(&after_work); 161 } 162 163 BIND(&object_push_pre); 164 { 165 Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), &double_push, 166 &object_push); 167 } 168 169 BIND(&object_push); 170 { 171 BuildAppendJSArray(HOLEY_ELEMENTS, a(), k_value, &runtime); 172 Goto(&after_work); 173 } 174 175 BIND(&double_push); 176 { 177 BuildAppendJSArray(HOLEY_DOUBLE_ELEMENTS, a(), k_value, &runtime); 178 Goto(&after_work); 179 } 180 181 BIND(&runtime); 182 { 183 // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue). 184 CallRuntime(Runtime::kCreateDataProperty, context(), a(), to_.value(), 185 k_value); 186 Goto(&after_work); 187 } 188 189 BIND(&after_work); 190 { 191 // 2. Increase to by 1. 192 to_.Bind(NumberInc(to_.value())); 193 Goto(&false_continue); 194 } 195 } 196 BIND(&false_continue); 197 return a(); 198 } 199 200 void ArrayBuiltinsAssembler::MapResultGenerator() { 201 GenerateArraySpeciesCreate(len_); 202 } 203 204 void ArrayBuiltinsAssembler::TypedArrayMapResultGenerator() { 205 // 6. Let A be ? TypedArraySpeciesCreate(O, len). 206 TNode<JSTypedArray> original_array = CAST(o()); 207 TNode<Smi> length = CAST(len_); 208 const char* method_name = "%TypedArray%.prototype.map"; 209 210 TypedArrayBuiltinsAssembler typedarray_asm(state()); 211 TNode<JSTypedArray> a = typedarray_asm.SpeciesCreateByLength( 212 context(), original_array, length, method_name); 213 // In the Spec and our current implementation, the length check is already 214 // performed in TypedArraySpeciesCreate. 215 CSA_ASSERT(this, SmiLessThanOrEqual(CAST(len_), LoadTypedArrayLength(a))); 216 fast_typed_array_target_ = 217 Word32Equal(LoadInstanceType(LoadElements(original_array)), 218 LoadInstanceType(LoadElements(a))); 219 a_.Bind(a); 220 } 221 222 Node* ArrayBuiltinsAssembler::SpecCompliantMapProcessor(Node* k_value, 223 Node* k) { 224 // i. Let kValue be ? Get(O, Pk). Performed by the caller of 225 // SpecCompliantMapProcessor. 226 // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O). 227 Node* mapped_value = CallJS(CodeFactory::Call(isolate()), context(), 228 callbackfn(), this_arg(), k_value, k, o()); 229 230 // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). 231 CallRuntime(Runtime::kCreateDataProperty, context(), a(), k, mapped_value); 232 return a(); 233 } 234 235 Node* ArrayBuiltinsAssembler::FastMapProcessor(Node* k_value, Node* k) { 236 // i. Let kValue be ? Get(O, Pk). Performed by the caller of 237 // FastMapProcessor. 238 // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O). 239 Node* mapped_value = CallJS(CodeFactory::Call(isolate()), context(), 240 callbackfn(), this_arg(), k_value, k, o()); 241 242 // mode is SMI_PARAMETERS because k has tagged representation. 243 ParameterMode mode = SMI_PARAMETERS; 244 Label runtime(this), finished(this); 245 Label transition_pre(this), transition_smi_fast(this), 246 transition_smi_double(this); 247 Label array_not_smi(this), array_fast(this), array_double(this); 248 249 TNode<Int32T> kind = LoadElementsKind(a()); 250 Node* elements = LoadElements(a()); 251 GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS), &array_not_smi); 252 TryStoreArrayElement(HOLEY_SMI_ELEMENTS, mode, &transition_pre, elements, k, 253 mapped_value); 254 Goto(&finished); 255 256 BIND(&transition_pre); 257 { 258 // array is smi. Value is either tagged or a heap number. 259 CSA_ASSERT(this, TaggedIsNotSmi(mapped_value)); 260 GotoIf(IsHeapNumberMap(LoadMap(mapped_value)), &transition_smi_double); 261 Goto(&transition_smi_fast); 262 } 263 264 BIND(&array_not_smi); 265 { 266 Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), &array_double, 267 &array_fast); 268 } 269 270 BIND(&transition_smi_fast); 271 { 272 // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). 273 Node* const native_context = LoadNativeContext(context()); 274 Node* const fast_map = LoadContextElement( 275 native_context, Context::JS_ARRAY_HOLEY_ELEMENTS_MAP_INDEX); 276 277 // Since this transition is only a map change, just do it right here. 278 // Since a() doesn't have an allocation site, it's safe to do the 279 // map store directly, otherwise I'd call TransitionElementsKind(). 280 StoreMap(a(), fast_map); 281 Goto(&array_fast); 282 } 283 284 BIND(&array_fast); 285 { 286 TryStoreArrayElement(HOLEY_ELEMENTS, mode, &runtime, elements, k, 287 mapped_value); 288 Goto(&finished); 289 } 290 291 BIND(&transition_smi_double); 292 { 293 // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). 294 Node* const native_context = LoadNativeContext(context()); 295 Node* const double_map = LoadContextElement( 296 native_context, Context::JS_ARRAY_HOLEY_DOUBLE_ELEMENTS_MAP_INDEX); 297 298 const ElementsKind kFromKind = HOLEY_SMI_ELEMENTS; 299 const ElementsKind kToKind = HOLEY_DOUBLE_ELEMENTS; 300 const bool kIsJSArray = true; 301 302 Label transition_in_runtime(this, Label::kDeferred); 303 TransitionElementsKind(a(), double_map, kFromKind, kToKind, kIsJSArray, 304 &transition_in_runtime); 305 Goto(&array_double); 306 307 BIND(&transition_in_runtime); 308 CallRuntime(Runtime::kTransitionElementsKind, context(), a(), double_map); 309 Goto(&array_double); 310 } 311 312 BIND(&array_double); 313 { 314 // TODO(mvstanton): If we use a variable for elements and bind it 315 // appropriately, we can avoid an extra load of elements by binding the 316 // value only after a transition from smi to double. 317 elements = LoadElements(a()); 318 // If the mapped_value isn't a number, this will bail out to the runtime 319 // to make the transition. 320 TryStoreArrayElement(HOLEY_DOUBLE_ELEMENTS, mode, &runtime, elements, k, 321 mapped_value); 322 Goto(&finished); 323 } 324 325 BIND(&runtime); 326 { 327 // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). 328 CallRuntime(Runtime::kCreateDataProperty, context(), a(), k, 329 mapped_value); 330 Goto(&finished); 331 } 332 333 BIND(&finished); 334 return a(); 335 } 336 337 // See tc39.github.io/ecma262/#sec-%typedarray%.prototype.map. 338 Node* ArrayBuiltinsAssembler::TypedArrayMapProcessor(Node* k_value, Node* k) { 339 // 8. c. Let mapped_value be ? Call(callbackfn, T, kValue, k, O ). 340 Node* mapped_value = CallJS(CodeFactory::Call(isolate()), context(), 341 callbackfn(), this_arg(), k_value, k, o()); 342 Label fast(this), slow(this), done(this), detached(this, Label::kDeferred); 343 344 // 8. d. Perform ? Set(A, Pk, mapped_value, true). 345 // Since we know that A is a TypedArray, this always ends up in 346 // #sec-integer-indexed-exotic-objects-set-p-v-receiver and then 347 // tc39.github.io/ecma262/#sec-integerindexedelementset . 348 Branch(fast_typed_array_target_, &fast, &slow); 349 350 BIND(&fast); 351 // #sec-integerindexedelementset 352 // 5. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let 353 // numValue be ? ToBigInt(v). 354 // 6. Otherwise, let numValue be ? ToNumber(value). 355 Node* num_value; 356 if (source_elements_kind_ == BIGINT64_ELEMENTS || 357 source_elements_kind_ == BIGUINT64_ELEMENTS) { 358 num_value = ToBigInt(context(), mapped_value); 359 } else { 360 num_value = ToNumber_Inline(context(), mapped_value); 361 } 362 // The only way how this can bailout is because of a detached buffer. 363 EmitElementStore(a(), k, num_value, false, source_elements_kind_, 364 KeyedAccessStoreMode::STANDARD_STORE, &detached, 365 context()); 366 Goto(&done); 367 368 BIND(&slow); 369 SetPropertyStrict(context(), CAST(a()), CAST(k), CAST(mapped_value)); 370 Goto(&done); 371 372 BIND(&detached); 373 // tc39.github.io/ecma262/#sec-integerindexedelementset 374 // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. 375 ThrowTypeError(context_, MessageTemplate::kDetachedOperation, name_); 376 377 BIND(&done); 378 return a(); 379 } 380 381 void ArrayBuiltinsAssembler::NullPostLoopAction() {} 382 383 void ArrayBuiltinsAssembler::FillFixedArrayWithSmiZero( 384 TNode<FixedArray> array, TNode<Smi> smi_length) { 385 CSA_ASSERT(this, Word32BinaryNot(IsFixedDoubleArray(array))); 386 387 TNode<IntPtrT> length = SmiToIntPtr(smi_length); 388 TNode<WordT> byte_length = TimesPointerSize(length); 389 CSA_ASSERT(this, UintPtrLessThan(length, byte_length)); 390 391 static const int32_t fa_base_data_offset = 392 FixedArray::kHeaderSize - kHeapObjectTag; 393 TNode<IntPtrT> backing_store = IntPtrAdd( 394 BitcastTaggedToWord(array), IntPtrConstant(fa_base_data_offset)); 395 396 // Call out to memset to perform initialization. 397 TNode<ExternalReference> memset = 398 ExternalConstant(ExternalReference::libc_memset_function()); 399 STATIC_ASSERT(kSizetSize == kIntptrSize); 400 CallCFunction3(MachineType::Pointer(), MachineType::Pointer(), 401 MachineType::IntPtr(), MachineType::UintPtr(), memset, 402 backing_store, IntPtrConstant(0), byte_length); 403 } 404 405 void ArrayBuiltinsAssembler::ReturnFromBuiltin(Node* value) { 406 if (argc_ == nullptr) { 407 Return(value); 408 } else { 409 // argc_ doesn't include the receiver, so it has to be added back in 410 // manually. 411 PopAndReturn(IntPtrAdd(argc_, IntPtrConstant(1)), value); 412 } 413 } 414 415 void ArrayBuiltinsAssembler::InitIteratingArrayBuiltinBody( 416 TNode<Context> context, TNode<Object> receiver, Node* callbackfn, 417 Node* this_arg, TNode<IntPtrT> argc) { 418 context_ = context; 419 receiver_ = receiver; 420 callbackfn_ = callbackfn; 421 this_arg_ = this_arg; 422 argc_ = argc; 423 } 424 425 void ArrayBuiltinsAssembler::GenerateIteratingArrayBuiltinBody( 426 const char* name, const BuiltinResultGenerator& generator, 427 const CallResultProcessor& processor, const PostLoopAction& action, 428 const Callable& slow_case_continuation, 429 MissingPropertyMode missing_property_mode, ForEachDirection direction) { 430 Label non_array(this), array_changes(this, {&k_, &a_, &to_}); 431 432 // TODO(danno): Seriously? Do we really need to throw the exact error 433 // message on null and undefined so that the webkit tests pass? 434 Label throw_null_undefined_exception(this, Label::kDeferred); 435 GotoIf(IsNullOrUndefined(receiver()), &throw_null_undefined_exception); 436 437 // By the book: taken directly from the ECMAScript 2015 specification 438 439 // 1. Let O be ToObject(this value). 440 // 2. ReturnIfAbrupt(O) 441 o_ = ToObject_Inline(context(), receiver()); 442 443 // 3. Let len be ToLength(Get(O, "length")). 444 // 4. ReturnIfAbrupt(len). 445 TVARIABLE(Number, merged_length); 446 Label has_length(this, &merged_length), not_js_array(this); 447 GotoIf(DoesntHaveInstanceType(o(), JS_ARRAY_TYPE), ¬_js_array); 448 merged_length = LoadJSArrayLength(CAST(o())); 449 Goto(&has_length); 450 451 BIND(¬_js_array); 452 { 453 Node* len_property = 454 GetProperty(context(), o(), isolate()->factory()->length_string()); 455 merged_length = ToLength_Inline(context(), len_property); 456 Goto(&has_length); 457 } 458 BIND(&has_length); 459 { 460 len_ = merged_length.value(); 461 462 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. 463 Label type_exception(this, Label::kDeferred); 464 Label done(this); 465 GotoIf(TaggedIsSmi(callbackfn()), &type_exception); 466 Branch(IsCallableMap(LoadMap(callbackfn())), &done, &type_exception); 467 468 BIND(&throw_null_undefined_exception); 469 ThrowTypeError(context(), MessageTemplate::kCalledOnNullOrUndefined, 470 name); 471 472 BIND(&type_exception); 473 ThrowTypeError(context(), MessageTemplate::kCalledNonCallable, 474 callbackfn()); 475 476 BIND(&done); 477 } 478 479 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. 480 // [Already done by the arguments adapter] 481 482 if (direction == ForEachDirection::kForward) { 483 // 7. Let k be 0. 484 k_.Bind(SmiConstant(0)); 485 } else { 486 k_.Bind(NumberDec(len())); 487 } 488 489 generator(this); 490 491 HandleFastElements(processor, action, &fully_spec_compliant_, direction, 492 missing_property_mode); 493 494 BIND(&fully_spec_compliant_); 495 496 Node* result = 497 CallStub(slow_case_continuation, context(), receiver(), callbackfn(), 498 this_arg(), a_.value(), o(), k_.value(), len(), to_.value()); 499 ReturnFromBuiltin(result); 500 } 501 502 void ArrayBuiltinsAssembler::InitIteratingArrayBuiltinLoopContinuation( 503 TNode<Context> context, TNode<Object> receiver, Node* callbackfn, 504 Node* this_arg, Node* a, TNode<JSReceiver> o, Node* initial_k, 505 TNode<Number> len, Node* to) { 506 context_ = context; 507 this_arg_ = this_arg; 508 callbackfn_ = callbackfn; 509 a_.Bind(a); 510 k_.Bind(initial_k); 511 o_ = o; 512 len_ = len; 513 to_.Bind(to); 514 } 515 516 void ArrayBuiltinsAssembler::GenerateIteratingTypedArrayBuiltinBody( 517 const char* name, const BuiltinResultGenerator& generator, 518 const CallResultProcessor& processor, const PostLoopAction& action, 519 ForEachDirection direction) { 520 name_ = name; 521 522 // ValidateTypedArray: tc39.github.io/ecma262/#sec-validatetypedarray 523 524 Label throw_not_typed_array(this, Label::kDeferred); 525 526 GotoIf(TaggedIsSmi(receiver_), &throw_not_typed_array); 527 GotoIfNot(HasInstanceType(CAST(receiver_), JS_TYPED_ARRAY_TYPE), 528 &throw_not_typed_array); 529 530 TNode<JSTypedArray> typed_array = CAST(receiver_); 531 o_ = typed_array; 532 533 TNode<JSArrayBuffer> array_buffer = LoadArrayBufferViewBuffer(typed_array); 534 ThrowIfArrayBufferIsDetached(context_, array_buffer, name_); 535 536 len_ = LoadTypedArrayLength(typed_array); 537 538 Label throw_not_callable(this, Label::kDeferred); 539 Label distinguish_types(this); 540 GotoIf(TaggedIsSmi(callbackfn_), &throw_not_callable); 541 Branch(IsCallableMap(LoadMap(callbackfn_)), &distinguish_types, 542 &throw_not_callable); 543 544 BIND(&throw_not_typed_array); 545 ThrowTypeError(context_, MessageTemplate::kNotTypedArray); 546 547 BIND(&throw_not_callable); 548 ThrowTypeError(context_, MessageTemplate::kCalledNonCallable, callbackfn_); 549 550 Label unexpected_instance_type(this); 551 BIND(&unexpected_instance_type); 552 Unreachable(); 553 554 std::vector<int32_t> instance_types = { 555 #define INSTANCE_TYPE(Type, type, TYPE, ctype) FIXED_##TYPE##_ARRAY_TYPE, 556 TYPED_ARRAYS(INSTANCE_TYPE) 557 #undef INSTANCE_TYPE 558 }; 559 std::vector<Label> labels; 560 for (size_t i = 0; i < instance_types.size(); ++i) { 561 labels.push_back(Label(this)); 562 } 563 std::vector<Label*> label_ptrs; 564 for (Label& label : labels) { 565 label_ptrs.push_back(&label); 566 } 567 568 BIND(&distinguish_types); 569 570 generator(this); 571 572 if (direction == ForEachDirection::kForward) { 573 k_.Bind(SmiConstant(0)); 574 } else { 575 k_.Bind(NumberDec(len())); 576 } 577 CSA_ASSERT(this, IsSafeInteger(k())); 578 Node* instance_type = LoadInstanceType(LoadElements(typed_array)); 579 Switch(instance_type, &unexpected_instance_type, instance_types.data(), 580 label_ptrs.data(), labels.size()); 581 582 for (size_t i = 0; i < labels.size(); ++i) { 583 BIND(&labels[i]); 584 Label done(this); 585 source_elements_kind_ = ElementsKindForInstanceType( 586 static_cast<InstanceType>(instance_types[i])); 587 // TODO(tebbi): Silently cancelling the loop on buffer detachment is a 588 // spec violation. Should go to &throw_detached and throw a TypeError 589 // instead. 590 VisitAllTypedArrayElements(array_buffer, processor, &done, direction, 591 typed_array); 592 Goto(&done); 593 // No exception, return success 594 BIND(&done); 595 action(this); 596 ReturnFromBuiltin(a_.value()); 597 } 598 } 599 600 void ArrayBuiltinsAssembler::GenerateIteratingArrayBuiltinLoopContinuation( 601 const CallResultProcessor& processor, const PostLoopAction& action, 602 MissingPropertyMode missing_property_mode, ForEachDirection direction) { 603 Label loop(this, {&k_, &a_, &to_}); 604 Label after_loop(this); 605 Goto(&loop); 606 BIND(&loop); 607 { 608 if (direction == ForEachDirection::kForward) { 609 // 8. Repeat, while k < len 610 GotoIfNumberGreaterThanOrEqual(k(), len_, &after_loop); 611 } else { 612 // OR 613 // 10. Repeat, while k >= 0 614 GotoIfNumberGreaterThanOrEqual(SmiConstant(-1), k(), &after_loop); 615 } 616 617 Label done_element(this, &to_); 618 // a. Let Pk be ToString(k). 619 // k() is guaranteed to be a positive integer, hence ToString is 620 // side-effect free and HasProperty/GetProperty do the conversion inline. 621 CSA_ASSERT(this, IsSafeInteger(k())); 622 623 if (missing_property_mode == MissingPropertyMode::kSkip) { 624 // b. Let kPresent be HasProperty(O, Pk). 625 // c. ReturnIfAbrupt(kPresent). 626 TNode<Oddball> k_present = 627 HasProperty(context(), o(), k(), kHasProperty); 628 629 // d. If kPresent is true, then 630 GotoIf(IsFalse(k_present), &done_element); 631 } 632 633 // i. Let kValue be Get(O, Pk). 634 // ii. ReturnIfAbrupt(kValue). 635 Node* k_value = GetProperty(context(), o(), k()); 636 637 // iii. Let funcResult be Call(callbackfn, T, kValue, k, O). 638 // iv. ReturnIfAbrupt(funcResult). 639 a_.Bind(processor(this, k_value, k())); 640 Goto(&done_element); 641 642 BIND(&done_element); 643 644 if (direction == ForEachDirection::kForward) { 645 // e. Increase k by 1. 646 k_.Bind(NumberInc(k())); 647 } else { 648 // e. Decrease k by 1. 649 k_.Bind(NumberDec(k())); 650 } 651 Goto(&loop); 652 } 653 BIND(&after_loop); 654 655 action(this); 656 Return(a_.value()); 657 } 658 659 ElementsKind ArrayBuiltinsAssembler::ElementsKindForInstanceType( 660 InstanceType type) { 661 switch (type) { 662 #define INSTANCE_TYPE_TO_ELEMENTS_KIND(Type, type, TYPE, ctype) \ 663 case FIXED_##TYPE##_ARRAY_TYPE: \ 664 return TYPE##_ELEMENTS; 665 666 TYPED_ARRAYS(INSTANCE_TYPE_TO_ELEMENTS_KIND) 667 #undef INSTANCE_TYPE_TO_ELEMENTS_KIND 668 669 default: 670 UNREACHABLE(); 671 } 672 } 673 674 void ArrayBuiltinsAssembler::VisitAllTypedArrayElements( 675 Node* array_buffer, const CallResultProcessor& processor, Label* detached, 676 ForEachDirection direction, TNode<JSTypedArray> typed_array) { 677 VariableList list({&a_, &k_, &to_}, zone()); 678 679 FastLoopBody body = [&](Node* index) { 680 GotoIf(IsDetachedBuffer(array_buffer), detached); 681 Node* elements = LoadElements(typed_array); 682 Node* base_ptr = 683 LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset); 684 Node* external_ptr = 685 LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset, 686 MachineType::Pointer()); 687 Node* data_ptr = IntPtrAdd(BitcastTaggedToWord(base_ptr), external_ptr); 688 Node* value = LoadFixedTypedArrayElementAsTagged( 689 data_ptr, index, source_elements_kind_, SMI_PARAMETERS); 690 k_.Bind(index); 691 a_.Bind(processor(this, value, index)); 692 }; 693 Node* start = SmiConstant(0); 694 Node* end = len_; 695 IndexAdvanceMode advance_mode = IndexAdvanceMode::kPost; 696 int incr = 1; 697 if (direction == ForEachDirection::kReverse) { 698 std::swap(start, end); 699 advance_mode = IndexAdvanceMode::kPre; 700 incr = -1; 701 } 702 BuildFastLoop(list, start, end, body, incr, ParameterMode::SMI_PARAMETERS, 703 advance_mode); 704 } 705 706 void ArrayBuiltinsAssembler::VisitAllFastElementsOneKind( 707 ElementsKind kind, const CallResultProcessor& processor, 708 Label* array_changed, ParameterMode mode, ForEachDirection direction, 709 MissingPropertyMode missing_property_mode, TNode<Smi> length) { 710 Comment("begin VisitAllFastElementsOneKind"); 711 // We only use this kind of processing if the no-elements protector is 712 // in place at the start. We'll continue checking during array iteration. 713 CSA_ASSERT(this, Word32BinaryNot(IsNoElementsProtectorCellInvalid())); 714 VARIABLE(original_map, MachineRepresentation::kTagged); 715 original_map.Bind(LoadMap(o())); 716 VariableList list({&original_map, &a_, &k_, &to_}, zone()); 717 Node* start = IntPtrOrSmiConstant(0, mode); 718 Node* end = TaggedToParameter(length, mode); 719 IndexAdvanceMode advance_mode = direction == ForEachDirection::kReverse 720 ? IndexAdvanceMode::kPre 721 : IndexAdvanceMode::kPost; 722 if (direction == ForEachDirection::kReverse) std::swap(start, end); 723 BuildFastLoop( 724 list, start, end, 725 [=, &original_map](Node* index) { 726 k_.Bind(ParameterToTagged(index, mode)); 727 Label one_element_done(this), hole_element(this), 728 process_element(this); 729 730 // Check if o's map has changed during the callback. If so, we have to 731 // fall back to the slower spec implementation for the rest of the 732 // iteration. 733 Node* o_map = LoadMap(o()); 734 GotoIf(WordNotEqual(o_map, original_map.value()), array_changed); 735 736 TNode<JSArray> o_array = CAST(o()); 737 // Check if o's length has changed during the callback and if the 738 // index is now out of range of the new length. 739 GotoIf(SmiGreaterThanOrEqual(CAST(k_.value()), 740 CAST(LoadJSArrayLength(o_array))), 741 array_changed); 742 743 // Re-load the elements array. If may have been resized. 744 Node* elements = LoadElements(o_array); 745 746 // Fast case: load the element directly from the elements FixedArray 747 // and call the callback if the element is not the hole. 748 DCHECK(kind == PACKED_ELEMENTS || kind == PACKED_DOUBLE_ELEMENTS); 749 int base_size = kind == PACKED_ELEMENTS 750 ? FixedArray::kHeaderSize 751 : (FixedArray::kHeaderSize - kHeapObjectTag); 752 Node* offset = ElementOffsetFromIndex(index, kind, mode, base_size); 753 VARIABLE(value, MachineRepresentation::kTagged); 754 if (kind == PACKED_ELEMENTS) { 755 value.Bind(LoadObjectField(elements, offset)); 756 GotoIf(WordEqual(value.value(), TheHoleConstant()), &hole_element); 757 } else { 758 Node* double_value = 759 LoadDoubleWithHoleCheck(elements, offset, &hole_element); 760 value.Bind(AllocateHeapNumberWithValue(double_value)); 761 } 762 Goto(&process_element); 763 764 BIND(&hole_element); 765 if (missing_property_mode == MissingPropertyMode::kSkip) { 766 // The NoElementsProtectorCell could go invalid during callbacks. 767 Branch(IsNoElementsProtectorCellInvalid(), array_changed, 768 &one_element_done); 769 } else { 770 value.Bind(UndefinedConstant()); 771 Goto(&process_element); 772 } 773 BIND(&process_element); 774 { 775 a_.Bind(processor(this, value.value(), k())); 776 Goto(&one_element_done); 777 } 778 BIND(&one_element_done); 779 }, 780 1, mode, advance_mode); 781 Comment("end VisitAllFastElementsOneKind"); 782 } 783 784 void ArrayBuiltinsAssembler::HandleFastElements( 785 const CallResultProcessor& processor, const PostLoopAction& action, 786 Label* slow, ForEachDirection direction, 787 MissingPropertyMode missing_property_mode) { 788 Label switch_on_elements_kind(this), fast_elements(this), 789 maybe_double_elements(this), fast_double_elements(this); 790 791 Comment("begin HandleFastElements"); 792 // Non-smi lengths must use the slow path. 793 GotoIf(TaggedIsNotSmi(len()), slow); 794 795 BranchIfFastJSArray(o(), context(), 796 &switch_on_elements_kind, slow); 797 798 BIND(&switch_on_elements_kind); 799 TNode<Smi> smi_len = CAST(len()); 800 // Select by ElementsKind 801 Node* o_map = LoadMap(o()); 802 Node* bit_field2 = LoadMapBitField2(o_map); 803 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); 804 Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), 805 &maybe_double_elements, &fast_elements); 806 807 ParameterMode mode = OptimalParameterMode(); 808 BIND(&fast_elements); 809 { 810 VisitAllFastElementsOneKind(PACKED_ELEMENTS, processor, slow, mode, 811 direction, missing_property_mode, smi_len); 812 813 action(this); 814 815 // No exception, return success 816 ReturnFromBuiltin(a_.value()); 817 } 818 819 BIND(&maybe_double_elements); 820 Branch(IsElementsKindGreaterThan(kind, HOLEY_DOUBLE_ELEMENTS), slow, 821 &fast_double_elements); 822 823 BIND(&fast_double_elements); 824 { 825 VisitAllFastElementsOneKind(PACKED_DOUBLE_ELEMENTS, processor, slow, mode, 826 direction, missing_property_mode, smi_len); 827 828 action(this); 829 830 // No exception, return success 831 ReturnFromBuiltin(a_.value()); 832 } 833 } 834 835 // Perform ArraySpeciesCreate (ES6 #sec-arrayspeciescreate). 836 // This version is specialized to create a zero length array 837 // of the elements kind of the input array. 838 void ArrayBuiltinsAssembler::GenerateArraySpeciesCreate() { 839 Label runtime(this, Label::kDeferred), done(this); 840 841 TNode<Smi> len = SmiConstant(0); 842 TNode<Map> original_map = LoadMap(o()); 843 GotoIfNot( 844 InstanceTypeEqual(LoadMapInstanceType(original_map), JS_ARRAY_TYPE), 845 &runtime); 846 847 GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map), 848 &runtime); 849 850 Node* species_protector = ArraySpeciesProtectorConstant(); 851 Node* value = 852 LoadObjectField(species_protector, PropertyCell::kValueOffset); 853 TNode<Smi> const protector_invalid = 854 SmiConstant(Isolate::kProtectorInvalid); 855 GotoIf(WordEqual(value, protector_invalid), &runtime); 856 857 // Respect the ElementsKind of the input array. 858 TNode<Int32T> elements_kind = LoadMapElementsKind(original_map); 859 GotoIfNot(IsFastElementsKind(elements_kind), &runtime); 860 TNode<Context> native_context = LoadNativeContext(context()); 861 TNode<Map> array_map = 862 LoadJSArrayElementsMap(elements_kind, native_context); 863 TNode<JSArray> array = 864 CAST(AllocateJSArray(GetInitialFastElementsKind(), array_map, len, len, 865 nullptr, CodeStubAssembler::SMI_PARAMETERS)); 866 a_.Bind(array); 867 868 Goto(&done); 869 870 BIND(&runtime); 871 { 872 // 5. Let A be ? ArraySpeciesCreate(O, len). 873 Node* constructor = 874 CallRuntime(Runtime::kArraySpeciesConstructor, context(), o()); 875 a_.Bind(ConstructJS(CodeFactory::Construct(isolate()), context(), 876 constructor, len)); 877 Goto(&fully_spec_compliant_); 878 } 879 880 BIND(&done); 881 } 882 883 // Perform ArraySpeciesCreate (ES6 #sec-arrayspeciescreate). 884 void ArrayBuiltinsAssembler::GenerateArraySpeciesCreate(TNode<Number> len) { 885 Label runtime(this, Label::kDeferred), done(this); 886 887 Node* const original_map = LoadMap(o()); 888 GotoIfNot( 889 InstanceTypeEqual(LoadMapInstanceType(original_map), JS_ARRAY_TYPE), 890 &runtime); 891 892 GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map), 893 &runtime); 894 895 Node* species_protector = ArraySpeciesProtectorConstant(); 896 Node* value = 897 LoadObjectField(species_protector, PropertyCell::kValueOffset); 898 Node* const protector_invalid = SmiConstant(Isolate::kProtectorInvalid); 899 GotoIf(WordEqual(value, protector_invalid), &runtime); 900 901 GotoIfNot(TaggedIsPositiveSmi(len), &runtime); 902 GotoIf( 903 SmiAbove(CAST(len), SmiConstant(JSArray::kInitialMaxFastElementArray)), 904 &runtime); 905 906 // We need to be conservative and start with holey because the builtins 907 // that create output arrays aren't guaranteed to be called for every 908 // element in the input array (maybe the callback deletes an element). 909 const ElementsKind elements_kind = 910 GetHoleyElementsKind(GetInitialFastElementsKind()); 911 TNode<Context> native_context = LoadNativeContext(context()); 912 TNode<Map> array_map = 913 LoadJSArrayElementsMap(elements_kind, native_context); 914 a_.Bind(AllocateJSArray(PACKED_SMI_ELEMENTS, array_map, len, len, nullptr, 915 CodeStubAssembler::SMI_PARAMETERS)); 916 917 Goto(&done); 918 919 BIND(&runtime); 920 { 921 // 5. Let A be ? ArraySpeciesCreate(O, len). 922 Node* constructor = 923 CallRuntime(Runtime::kArraySpeciesConstructor, context(), o()); 924 a_.Bind(ConstructJS(CodeFactory::Construct(isolate()), context(), 925 constructor, len)); 926 Goto(&fully_spec_compliant_); 927 } 928 929 BIND(&done); 930 } 931 932 TF_BUILTIN(ArrayPrototypePop, CodeStubAssembler) { 933 TNode<Int32T> argc = 934 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)); 935 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 936 CSA_ASSERT(this, IsUndefined(Parameter(Descriptor::kJSNewTarget))); 937 938 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc)); 939 TNode<Object> receiver = args.GetReceiver(); 940 941 Label runtime(this, Label::kDeferred); 942 Label fast(this); 943 944 // Only pop in this stub if 945 // 1) the array has fast elements 946 // 2) the length is writable, 947 // 3) the elements backing store isn't copy-on-write, 948 // 4) we aren't supposed to shrink the backing store. 949 950 // 1) Check that the array has fast elements. 951 BranchIfFastJSArray(receiver, context, &fast, &runtime); 952 953 BIND(&fast); 954 { 955 TNode<JSArray> array_receiver = CAST(receiver); 956 CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array_receiver))); 957 Node* length = 958 LoadAndUntagObjectField(array_receiver, JSArray::kLengthOffset); 959 Label return_undefined(this), fast_elements(this); 960 GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &return_undefined); 961 962 // 2) Ensure that the length is writable. 963 EnsureArrayLengthWritable(LoadMap(array_receiver), &runtime); 964 965 // 3) Check that the elements backing store isn't copy-on-write. 966 Node* elements = LoadElements(array_receiver); 967 GotoIf(WordEqual(LoadMap(elements), 968 LoadRoot(Heap::kFixedCOWArrayMapRootIndex)), 969 &runtime); 970 971 Node* new_length = IntPtrSub(length, IntPtrConstant(1)); 972 973 // 4) Check that we're not supposed to shrink the backing store, as 974 // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl. 975 Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements)); 976 GotoIf(IntPtrLessThan( 977 IntPtrAdd(IntPtrAdd(new_length, new_length), 978 IntPtrConstant(JSObject::kMinAddedElementsCapacity)), 979 capacity), 980 &runtime); 981 982 StoreObjectFieldNoWriteBarrier(array_receiver, JSArray::kLengthOffset, 983 SmiTag(new_length)); 984 985 TNode<Int32T> elements_kind = LoadElementsKind(array_receiver); 986 GotoIf(Int32LessThanOrEqual(elements_kind, 987 Int32Constant(TERMINAL_FAST_ELEMENTS_KIND)), 988 &fast_elements); 989 990 Node* value = LoadFixedDoubleArrayElement( 991 elements, new_length, MachineType::Float64(), 0, INTPTR_PARAMETERS, 992 &return_undefined); 993 994 int32_t header_size = FixedDoubleArray::kHeaderSize - kHeapObjectTag; 995 Node* offset = ElementOffsetFromIndex(new_length, HOLEY_DOUBLE_ELEMENTS, 996 INTPTR_PARAMETERS, header_size); 997 if (Is64()) { 998 Node* double_hole = Int64Constant(kHoleNanInt64); 999 StoreNoWriteBarrier(MachineRepresentation::kWord64, elements, offset, 1000 double_hole); 1001 } else { 1002 STATIC_ASSERT(kHoleNanLower32 == kHoleNanUpper32); 1003 Node* double_hole = Int32Constant(kHoleNanLower32); 1004 StoreNoWriteBarrier(MachineRepresentation::kWord32, elements, offset, 1005 double_hole); 1006 StoreNoWriteBarrier(MachineRepresentation::kWord32, elements, 1007 IntPtrAdd(offset, IntPtrConstant(kPointerSize)), 1008 double_hole); 1009 } 1010 args.PopAndReturn(AllocateHeapNumberWithValue(value)); 1011 1012 BIND(&fast_elements); 1013 { 1014 Node* value = LoadFixedArrayElement(CAST(elements), new_length); 1015 StoreFixedArrayElement(CAST(elements), new_length, TheHoleConstant()); 1016 GotoIf(WordEqual(value, TheHoleConstant()), &return_undefined); 1017 args.PopAndReturn(value); 1018 } 1019 1020 BIND(&return_undefined); 1021 { args.PopAndReturn(UndefinedConstant()); } 1022 } 1023 1024 BIND(&runtime); 1025 { 1026 // We are not using Parameter(Descriptor::kJSTarget) and loading the value 1027 // from the current frame here in order to reduce register pressure on the 1028 // fast path. 1029 TNode<JSFunction> target = LoadTargetFromFrame(); 1030 TailCallBuiltin(Builtins::kArrayPop, context, target, UndefinedConstant(), 1031 argc); 1032 } 1033 } 1034 1035 TF_BUILTIN(ArrayPrototypePush, CodeStubAssembler) { 1036 TVARIABLE(IntPtrT, arg_index); 1037 Label default_label(this, &arg_index); 1038 Label smi_transition(this); 1039 Label object_push_pre(this); 1040 Label object_push(this, &arg_index); 1041 Label double_push(this, &arg_index); 1042 Label double_transition(this); 1043 Label runtime(this, Label::kDeferred); 1044 1045 // TODO(ishell): use constants from Descriptor once the JSFunction linkage 1046 // arguments are reordered. 1047 TNode<Int32T> argc = 1048 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)); 1049 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1050 CSA_ASSERT(this, IsUndefined(Parameter(Descriptor::kJSNewTarget))); 1051 1052 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc)); 1053 TNode<Object> receiver = args.GetReceiver(); 1054 TNode<JSArray> array_receiver; 1055 Node* kind = nullptr; 1056 1057 Label fast(this); 1058 BranchIfFastJSArray(receiver, context, &fast, &runtime); 1059 1060 BIND(&fast); 1061 { 1062 array_receiver = CAST(receiver); 1063 arg_index = IntPtrConstant(0); 1064 kind = EnsureArrayPushable(LoadMap(array_receiver), &runtime); 1065 GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS), 1066 &object_push_pre); 1067 1068 Node* new_length = BuildAppendJSArray(PACKED_SMI_ELEMENTS, array_receiver, 1069 &args, &arg_index, &smi_transition); 1070 args.PopAndReturn(new_length); 1071 } 1072 1073 // If the argument is not a smi, then use a heavyweight SetProperty to 1074 // transition the array for only the single next element. If the argument is 1075 // a smi, the failure is due to some other reason and we should fall back on 1076 // the most generic implementation for the rest of the array. 1077 BIND(&smi_transition); 1078 { 1079 Node* arg = args.AtIndex(arg_index.value()); 1080 GotoIf(TaggedIsSmi(arg), &default_label); 1081 Node* length = LoadJSArrayLength(array_receiver); 1082 // TODO(danno): Use the KeyedStoreGeneric stub here when possible, 1083 // calling into the runtime to do the elements transition is overkill. 1084 SetPropertyStrict(context, array_receiver, CAST(length), CAST(arg)); 1085 Increment(&arg_index); 1086 // The runtime SetProperty call could have converted the array to dictionary 1087 // mode, which must be detected to abort the fast-path. 1088 Node* map = LoadMap(array_receiver); 1089 Node* bit_field2 = LoadMapBitField2(map); 1090 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); 1091 GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)), 1092 &default_label); 1093 1094 GotoIfNotNumber(arg, &object_push); 1095 Goto(&double_push); 1096 } 1097 1098 BIND(&object_push_pre); 1099 { 1100 Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), &double_push, 1101 &object_push); 1102 } 1103 1104 BIND(&object_push); 1105 { 1106 Node* new_length = BuildAppendJSArray(PACKED_ELEMENTS, array_receiver, 1107 &args, &arg_index, &default_label); 1108 args.PopAndReturn(new_length); 1109 } 1110 1111 BIND(&double_push); 1112 { 1113 Node* new_length = 1114 BuildAppendJSArray(PACKED_DOUBLE_ELEMENTS, array_receiver, &args, 1115 &arg_index, &double_transition); 1116 args.PopAndReturn(new_length); 1117 } 1118 1119 // If the argument is not a double, then use a heavyweight SetProperty to 1120 // transition the array for only the single next element. If the argument is 1121 // a double, the failure is due to some other reason and we should fall back 1122 // on the most generic implementation for the rest of the array. 1123 BIND(&double_transition); 1124 { 1125 Node* arg = args.AtIndex(arg_index.value()); 1126 GotoIfNumber(arg, &default_label); 1127 Node* length = LoadJSArrayLength(array_receiver); 1128 // TODO(danno): Use the KeyedStoreGeneric stub here when possible, 1129 // calling into the runtime to do the elements transition is overkill. 1130 SetPropertyStrict(context, array_receiver, CAST(length), CAST(arg)); 1131 Increment(&arg_index); 1132 // The runtime SetProperty call could have converted the array to dictionary 1133 // mode, which must be detected to abort the fast-path. 1134 Node* map = LoadMap(array_receiver); 1135 Node* bit_field2 = LoadMapBitField2(map); 1136 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); 1137 GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)), 1138 &default_label); 1139 Goto(&object_push); 1140 } 1141 1142 // Fallback that stores un-processed arguments using the full, heavyweight 1143 // SetProperty machinery. 1144 BIND(&default_label); 1145 { 1146 args.ForEach( 1147 [this, array_receiver, context](Node* arg) { 1148 Node* length = LoadJSArrayLength(array_receiver); 1149 SetPropertyStrict(context, array_receiver, CAST(length), CAST(arg)); 1150 }, 1151 arg_index.value()); 1152 args.PopAndReturn(LoadJSArrayLength(array_receiver)); 1153 } 1154 1155 BIND(&runtime); 1156 { 1157 // We are not using Parameter(Descriptor::kJSTarget) and loading the value 1158 // from the current frame here in order to reduce register pressure on the 1159 // fast path. 1160 TNode<JSFunction> target = LoadTargetFromFrame(); 1161 TailCallBuiltin(Builtins::kArrayPush, context, target, UndefinedConstant(), 1162 argc); 1163 } 1164 } 1165 1166 class ArrayPrototypeSliceCodeStubAssembler : public CodeStubAssembler { 1167 public: 1168 explicit ArrayPrototypeSliceCodeStubAssembler( 1169 compiler::CodeAssemblerState* state) 1170 : CodeStubAssembler(state) {} 1171 1172 Node* HandleFastSlice(TNode<Context> context, Node* array, Node* from, 1173 Node* count, Label* slow) { 1174 VARIABLE(result, MachineRepresentation::kTagged); 1175 Label done(this); 1176 1177 GotoIf(TaggedIsNotSmi(from), slow); 1178 GotoIf(TaggedIsNotSmi(count), slow); 1179 1180 Label try_fast_arguments(this), try_simple_slice(this); 1181 1182 Node* map = LoadMap(array); 1183 GotoIfNot(IsJSArrayMap(map), &try_fast_arguments); 1184 1185 // Check prototype chain if receiver does not have packed elements 1186 GotoIfNot(IsPrototypeInitialArrayPrototype(context, map), slow); 1187 1188 GotoIf(IsNoElementsProtectorCellInvalid(), slow); 1189 1190 GotoIf(IsArraySpeciesProtectorCellInvalid(), slow); 1191 1192 // Bailout if receiver has slow elements. 1193 Node* elements_kind = LoadMapElementsKind(map); 1194 GotoIfNot(IsFastElementsKind(elements_kind), &try_simple_slice); 1195 1196 // Make sure that the length hasn't been changed by side-effect. 1197 Node* array_length = LoadJSArrayLength(array); 1198 GotoIf(TaggedIsNotSmi(array_length), slow); 1199 GotoIf(SmiAbove(SmiAdd(CAST(from), CAST(count)), CAST(array_length)), slow); 1200 1201 CSA_ASSERT(this, SmiGreaterThanOrEqual(CAST(from), SmiConstant(0))); 1202 1203 result.Bind(CallBuiltin(Builtins::kExtractFastJSArray, context, array, from, 1204 count)); 1205 Goto(&done); 1206 1207 BIND(&try_fast_arguments); 1208 1209 Node* const native_context = LoadNativeContext(context); 1210 Node* const fast_aliasted_arguments_map = LoadContextElement( 1211 native_context, Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX); 1212 GotoIf(WordNotEqual(map, fast_aliasted_arguments_map), &try_simple_slice); 1213 1214 TNode<SloppyArgumentsElements> sloppy_elements = CAST(LoadElements(array)); 1215 TNode<Smi> sloppy_elements_length = 1216 LoadFixedArrayBaseLength(sloppy_elements); 1217 TNode<Smi> parameter_map_length = 1218 SmiSub(sloppy_elements_length, 1219 SmiConstant(SloppyArgumentsElements::kParameterMapStart)); 1220 VARIABLE(index_out, MachineType::PointerRepresentation()); 1221 1222 int max_fast_elements = 1223 (kMaxRegularHeapObjectSize - FixedArray::kHeaderSize - JSArray::kSize - 1224 AllocationMemento::kSize) / 1225 kPointerSize; 1226 GotoIf(SmiAboveOrEqual(CAST(count), SmiConstant(max_fast_elements)), 1227 &try_simple_slice); 1228 1229 GotoIf(SmiLessThan(CAST(from), SmiConstant(0)), slow); 1230 1231 TNode<Smi> end = SmiAdd(CAST(from), CAST(count)); 1232 1233 TNode<FixedArray> unmapped_elements = CAST(LoadFixedArrayElement( 1234 sloppy_elements, SloppyArgumentsElements::kArgumentsIndex)); 1235 TNode<Smi> unmapped_elements_length = 1236 LoadFixedArrayBaseLength(unmapped_elements); 1237 1238 GotoIf(SmiAbove(end, unmapped_elements_length), slow); 1239 1240 Node* array_map = LoadJSArrayElementsMap(HOLEY_ELEMENTS, native_context); 1241 result.Bind(AllocateJSArray(HOLEY_ELEMENTS, array_map, count, count, 1242 nullptr, SMI_PARAMETERS)); 1243 1244 index_out.Bind(IntPtrConstant(0)); 1245 TNode<FixedArray> result_elements = CAST(LoadElements(result.value())); 1246 TNode<Smi> from_mapped = SmiMin(parameter_map_length, CAST(from)); 1247 TNode<Smi> to = SmiMin(parameter_map_length, end); 1248 Node* arguments_context = LoadFixedArrayElement( 1249 sloppy_elements, SloppyArgumentsElements::kContextIndex); 1250 VariableList var_list({&index_out}, zone()); 1251 BuildFastLoop( 1252 var_list, from_mapped, to, 1253 [this, result_elements, arguments_context, sloppy_elements, 1254 unmapped_elements, &index_out](Node* current) { 1255 Node* context_index = LoadFixedArrayElement( 1256 sloppy_elements, current, 1257 kPointerSize * SloppyArgumentsElements::kParameterMapStart, 1258 SMI_PARAMETERS); 1259 Label is_the_hole(this), done(this); 1260 GotoIf(IsTheHole(context_index), &is_the_hole); 1261 Node* mapped_argument = 1262 LoadContextElement(arguments_context, SmiUntag(context_index)); 1263 StoreFixedArrayElement(result_elements, index_out.value(), 1264 mapped_argument, SKIP_WRITE_BARRIER); 1265 Goto(&done); 1266 BIND(&is_the_hole); 1267 Node* argument = LoadFixedArrayElement(unmapped_elements, current, 0, 1268 SMI_PARAMETERS); 1269 StoreFixedArrayElement(result_elements, index_out.value(), argument, 1270 SKIP_WRITE_BARRIER); 1271 Goto(&done); 1272 BIND(&done); 1273 index_out.Bind(IntPtrAdd(index_out.value(), IntPtrConstant(1))); 1274 }, 1275 1, SMI_PARAMETERS, IndexAdvanceMode::kPost); 1276 1277 TNode<Smi> unmapped_from = 1278 SmiMin(SmiMax(parameter_map_length, CAST(from)), end); 1279 1280 BuildFastLoop( 1281 var_list, unmapped_from, end, 1282 [this, unmapped_elements, result_elements, &index_out](Node* current) { 1283 Node* argument = LoadFixedArrayElement(unmapped_elements, current, 0, 1284 SMI_PARAMETERS); 1285 StoreFixedArrayElement(result_elements, index_out.value(), argument, 1286 SKIP_WRITE_BARRIER); 1287 index_out.Bind(IntPtrAdd(index_out.value(), IntPtrConstant(1))); 1288 }, 1289 1, SMI_PARAMETERS, IndexAdvanceMode::kPost); 1290 1291 Goto(&done); 1292 1293 BIND(&try_simple_slice); 1294 Node* simple_result = CallRuntime(Runtime::kTrySliceSimpleNonFastElements, 1295 context, array, from, count); 1296 GotoIfNumber(simple_result, slow); 1297 result.Bind(simple_result); 1298 1299 Goto(&done); 1300 1301 BIND(&done); 1302 return result.value(); 1303 } 1304 1305 void CopyOneElement(TNode<Context> context, Node* o, Node* a, Node* p_k, 1306 Variable& n) { 1307 // b. Let kPresent be HasProperty(O, Pk). 1308 // c. ReturnIfAbrupt(kPresent). 1309 TNode<Oddball> k_present = HasProperty(context, o, p_k, kHasProperty); 1310 1311 // d. If kPresent is true, then 1312 Label done_element(this); 1313 GotoIf(IsFalse(k_present), &done_element); 1314 1315 // i. Let kValue be Get(O, Pk). 1316 // ii. ReturnIfAbrupt(kValue). 1317 Node* k_value = GetProperty(context, o, p_k); 1318 1319 // iii. Let status be CreateDataPropertyOrThrow(A, ToString(n), kValue). 1320 // iv. ReturnIfAbrupt(status). 1321 CallRuntime(Runtime::kCreateDataProperty, context, a, n.value(), k_value); 1322 1323 Goto(&done_element); 1324 BIND(&done_element); 1325 } 1326 }; 1327 1328 TF_BUILTIN(ArrayPrototypeSlice, ArrayPrototypeSliceCodeStubAssembler) { 1329 Node* const argc = 1330 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 1331 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1332 Label slow(this, Label::kDeferred), fast_elements_kind(this); 1333 1334 CodeStubArguments args(this, argc); 1335 TNode<Object> receiver = args.GetReceiver(); 1336 1337 TVARIABLE(JSReceiver, o); 1338 VARIABLE(len, MachineRepresentation::kTagged); 1339 Label length_done(this), generic_length(this), check_arguments_length(this), 1340 load_arguments_length(this); 1341 1342 GotoIf(TaggedIsSmi(receiver), &generic_length); 1343 GotoIfNot(IsJSArray(CAST(receiver)), &check_arguments_length); 1344 1345 TNode<JSArray> array_receiver = CAST(receiver); 1346 o = array_receiver; 1347 len.Bind(LoadJSArrayLength(array_receiver)); 1348 1349 // Check for the array clone case. There can be no arguments to slice, the 1350 // array prototype chain must be intact and have no elements, the array has to 1351 // have fast elements. 1352 GotoIf(WordNotEqual(argc, IntPtrConstant(0)), &length_done); 1353 1354 Label clone(this); 1355 BranchIfFastJSArrayForCopy(receiver, context, &clone, &length_done); 1356 BIND(&clone); 1357 1358 args.PopAndReturn( 1359 CallBuiltin(Builtins::kCloneFastJSArray, context, receiver)); 1360 1361 BIND(&check_arguments_length); 1362 1363 Node* map = LoadMap(array_receiver); 1364 Node* native_context = LoadNativeContext(context); 1365 GotoIfContextElementEqual(map, native_context, 1366 Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX, 1367 &load_arguments_length); 1368 GotoIfContextElementEqual(map, native_context, 1369 Context::SLOW_ALIASED_ARGUMENTS_MAP_INDEX, 1370 &load_arguments_length); 1371 GotoIfContextElementEqual(map, native_context, 1372 Context::STRICT_ARGUMENTS_MAP_INDEX, 1373 &load_arguments_length); 1374 GotoIfContextElementEqual(map, native_context, 1375 Context::SLOPPY_ARGUMENTS_MAP_INDEX, 1376 &load_arguments_length); 1377 1378 Goto(&generic_length); 1379 1380 BIND(&load_arguments_length); 1381 Node* arguments_length = 1382 LoadObjectField(array_receiver, JSArgumentsObject::kLengthOffset); 1383 GotoIf(TaggedIsNotSmi(arguments_length), &generic_length); 1384 o = CAST(receiver); 1385 len.Bind(arguments_length); 1386 Goto(&length_done); 1387 1388 BIND(&generic_length); 1389 // 1. Let O be ToObject(this value). 1390 // 2. ReturnIfAbrupt(O). 1391 o = ToObject_Inline(context, receiver); 1392 1393 // 3. Let len be ToLength(Get(O, "length")). 1394 // 4. ReturnIfAbrupt(len). 1395 len.Bind(ToLength_Inline( 1396 context, 1397 GetProperty(context, o.value(), isolate()->factory()->length_string()))); 1398 Goto(&length_done); 1399 1400 BIND(&length_done); 1401 1402 // 5. Let relativeStart be ToInteger(start). 1403 // 6. ReturnIfAbrupt(relativeStart). 1404 TNode<Object> arg0 = args.GetOptionalArgumentValue(0, SmiConstant(0)); 1405 Node* relative_start = ToInteger_Inline(context, arg0); 1406 1407 // 7. If relativeStart < 0, let k be max((len + relativeStart),0); 1408 // else let k be min(relativeStart, len.value()). 1409 VARIABLE(k, MachineRepresentation::kTagged); 1410 Label relative_start_positive(this), relative_start_done(this); 1411 GotoIfNumberGreaterThanOrEqual(relative_start, SmiConstant(0), 1412 &relative_start_positive); 1413 k.Bind(NumberMax(NumberAdd(len.value(), relative_start), NumberConstant(0))); 1414 Goto(&relative_start_done); 1415 BIND(&relative_start_positive); 1416 k.Bind(NumberMin(relative_start, len.value())); 1417 Goto(&relative_start_done); 1418 BIND(&relative_start_done); 1419 1420 // 8. If end is undefined, let relativeEnd be len; 1421 // else let relativeEnd be ToInteger(end). 1422 // 9. ReturnIfAbrupt(relativeEnd). 1423 TNode<Object> end = args.GetOptionalArgumentValue(1, UndefinedConstant()); 1424 Label end_undefined(this), end_done(this); 1425 VARIABLE(relative_end, MachineRepresentation::kTagged); 1426 GotoIf(WordEqual(end, UndefinedConstant()), &end_undefined); 1427 relative_end.Bind(ToInteger_Inline(context, end)); 1428 Goto(&end_done); 1429 BIND(&end_undefined); 1430 relative_end.Bind(len.value()); 1431 Goto(&end_done); 1432 BIND(&end_done); 1433 1434 // 10. If relativeEnd < 0, let final be max((len + relativeEnd),0); 1435 // else let final be min(relativeEnd, len). 1436 VARIABLE(final, MachineRepresentation::kTagged); 1437 Label relative_end_positive(this), relative_end_done(this); 1438 GotoIfNumberGreaterThanOrEqual(relative_end.value(), NumberConstant(0), 1439 &relative_end_positive); 1440 final.Bind(NumberMax(NumberAdd(len.value(), relative_end.value()), 1441 NumberConstant(0))); 1442 Goto(&relative_end_done); 1443 BIND(&relative_end_positive); 1444 final.Bind(NumberMin(relative_end.value(), len.value())); 1445 Goto(&relative_end_done); 1446 BIND(&relative_end_done); 1447 1448 // 11. Let count be max(final k, 0). 1449 Node* count = 1450 NumberMax(NumberSub(final.value(), k.value()), NumberConstant(0)); 1451 1452 // Handle FAST_ELEMENTS 1453 Label non_fast(this); 1454 Node* fast_result = 1455 HandleFastSlice(context, o.value(), k.value(), count, &non_fast); 1456 args.PopAndReturn(fast_result); 1457 1458 // 12. Let A be ArraySpeciesCreate(O, count). 1459 // 13. ReturnIfAbrupt(A). 1460 BIND(&non_fast); 1461 1462 Node* constructor = 1463 CallRuntime(Runtime::kArraySpeciesConstructor, context, o.value()); 1464 Node* a = ConstructJS(CodeFactory::Construct(isolate()), context, constructor, 1465 count); 1466 1467 // 14. Let n be 0. 1468 VARIABLE(n, MachineRepresentation::kTagged); 1469 n.Bind(SmiConstant(0)); 1470 1471 Label loop(this, {&k, &n}); 1472 Label after_loop(this); 1473 Goto(&loop); 1474 BIND(&loop); 1475 { 1476 // 15. Repeat, while k < final 1477 GotoIfNumberGreaterThanOrEqual(k.value(), final.value(), &after_loop); 1478 1479 Node* p_k = k.value(); // ToString(context, k.value()) is no-op 1480 1481 CopyOneElement(context, o.value(), a, p_k, n); 1482 1483 // e. Increase k by 1. 1484 k.Bind(NumberInc(k.value())); 1485 1486 // f. Increase n by 1. 1487 n.Bind(NumberInc(n.value())); 1488 1489 Goto(&loop); 1490 } 1491 1492 BIND(&after_loop); 1493 1494 // 16. Let setStatus be Set(A, "length", n, true). 1495 // 17. ReturnIfAbrupt(setStatus). 1496 SetPropertyStrict(context, CAST(a), CodeStubAssembler::LengthStringConstant(), 1497 CAST(n.value())); 1498 args.PopAndReturn(a); 1499 } 1500 1501 TF_BUILTIN(ArrayPrototypeShift, CodeStubAssembler) { 1502 TNode<Int32T> argc = 1503 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)); 1504 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1505 CSA_ASSERT(this, IsUndefined(Parameter(Descriptor::kJSNewTarget))); 1506 1507 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc)); 1508 TNode<Object> receiver = args.GetReceiver(); 1509 1510 Label runtime(this, Label::kDeferred); 1511 Label fast(this); 1512 1513 // Only shift in this stub if 1514 // 1) the array has fast elements 1515 // 2) the length is writable, 1516 // 3) the elements backing store isn't copy-on-write, 1517 // 4) we aren't supposed to shrink the backing store, 1518 // 5) we aren't supposed to left-trim the backing store. 1519 1520 // 1) Check that the array has fast elements. 1521 BranchIfFastJSArray(receiver, context, &fast, &runtime); 1522 1523 BIND(&fast); 1524 { 1525 TNode<JSArray> array_receiver = CAST(receiver); 1526 CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array_receiver))); 1527 Node* length = 1528 LoadAndUntagObjectField(array_receiver, JSArray::kLengthOffset); 1529 Label return_undefined(this), fast_elements_tagged(this), 1530 fast_elements_smi(this); 1531 GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &return_undefined); 1532 1533 // 2) Ensure that the length is writable. 1534 EnsureArrayLengthWritable(LoadMap(array_receiver), &runtime); 1535 1536 // 3) Check that the elements backing store isn't copy-on-write. 1537 Node* elements = LoadElements(array_receiver); 1538 GotoIf(WordEqual(LoadMap(elements), 1539 LoadRoot(Heap::kFixedCOWArrayMapRootIndex)), 1540 &runtime); 1541 1542 Node* new_length = IntPtrSub(length, IntPtrConstant(1)); 1543 1544 // 4) Check that we're not supposed to right-trim the backing store, as 1545 // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl. 1546 Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements)); 1547 GotoIf(IntPtrLessThan( 1548 IntPtrAdd(IntPtrAdd(new_length, new_length), 1549 IntPtrConstant(JSObject::kMinAddedElementsCapacity)), 1550 capacity), 1551 &runtime); 1552 1553 // 5) Check that we're not supposed to left-trim the backing store, as 1554 // implemented in elements.cc:FastElementsAccessor::MoveElements. 1555 GotoIf(IntPtrGreaterThan(new_length, 1556 IntPtrConstant(JSArray::kMaxCopyElements)), 1557 &runtime); 1558 1559 StoreObjectFieldNoWriteBarrier(array_receiver, JSArray::kLengthOffset, 1560 SmiTag(new_length)); 1561 1562 TNode<Int32T> elements_kind = LoadElementsKind(array_receiver); 1563 GotoIf( 1564 Int32LessThanOrEqual(elements_kind, Int32Constant(HOLEY_SMI_ELEMENTS)), 1565 &fast_elements_smi); 1566 GotoIf(Int32LessThanOrEqual(elements_kind, Int32Constant(HOLEY_ELEMENTS)), 1567 &fast_elements_tagged); 1568 1569 // Fast double elements kind: 1570 { 1571 CSA_ASSERT(this, 1572 Int32LessThanOrEqual(elements_kind, 1573 Int32Constant(HOLEY_DOUBLE_ELEMENTS))); 1574 1575 VARIABLE(result, MachineRepresentation::kTagged, UndefinedConstant()); 1576 1577 Label move_elements(this); 1578 result.Bind(AllocateHeapNumberWithValue(LoadFixedDoubleArrayElement( 1579 elements, IntPtrConstant(0), MachineType::Float64(), 0, 1580 INTPTR_PARAMETERS, &move_elements))); 1581 Goto(&move_elements); 1582 BIND(&move_elements); 1583 1584 int32_t header_size = FixedDoubleArray::kHeaderSize - kHeapObjectTag; 1585 Node* memmove = 1586 ExternalConstant(ExternalReference::libc_memmove_function()); 1587 Node* start = IntPtrAdd( 1588 BitcastTaggedToWord(elements), 1589 ElementOffsetFromIndex(IntPtrConstant(0), HOLEY_DOUBLE_ELEMENTS, 1590 INTPTR_PARAMETERS, header_size)); 1591 CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(), 1592 MachineType::Pointer(), MachineType::UintPtr(), memmove, 1593 start, IntPtrAdd(start, IntPtrConstant(kDoubleSize)), 1594 IntPtrMul(new_length, IntPtrConstant(kDoubleSize))); 1595 Node* offset = ElementOffsetFromIndex(new_length, HOLEY_DOUBLE_ELEMENTS, 1596 INTPTR_PARAMETERS, header_size); 1597 if (Is64()) { 1598 Node* double_hole = Int64Constant(kHoleNanInt64); 1599 StoreNoWriteBarrier(MachineRepresentation::kWord64, elements, offset, 1600 double_hole); 1601 } else { 1602 STATIC_ASSERT(kHoleNanLower32 == kHoleNanUpper32); 1603 Node* double_hole = Int32Constant(kHoleNanLower32); 1604 StoreNoWriteBarrier(MachineRepresentation::kWord32, elements, offset, 1605 double_hole); 1606 StoreNoWriteBarrier(MachineRepresentation::kWord32, elements, 1607 IntPtrAdd(offset, IntPtrConstant(kPointerSize)), 1608 double_hole); 1609 } 1610 args.PopAndReturn(result.value()); 1611 } 1612 1613 BIND(&fast_elements_tagged); 1614 { 1615 TNode<FixedArray> elements_fixed_array = CAST(elements); 1616 Node* value = LoadFixedArrayElement(elements_fixed_array, 0); 1617 BuildFastLoop( 1618 IntPtrConstant(0), new_length, 1619 [&](Node* index) { 1620 StoreFixedArrayElement( 1621 elements_fixed_array, index, 1622 LoadFixedArrayElement(elements_fixed_array, 1623 IntPtrAdd(index, IntPtrConstant(1)))); 1624 }, 1625 1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost); 1626 StoreFixedArrayElement(elements_fixed_array, new_length, 1627 TheHoleConstant()); 1628 GotoIf(WordEqual(value, TheHoleConstant()), &return_undefined); 1629 args.PopAndReturn(value); 1630 } 1631 1632 BIND(&fast_elements_smi); 1633 { 1634 TNode<FixedArray> elements_fixed_array = CAST(elements); 1635 Node* value = LoadFixedArrayElement(elements_fixed_array, 0); 1636 BuildFastLoop( 1637 IntPtrConstant(0), new_length, 1638 [&](Node* index) { 1639 StoreFixedArrayElement( 1640 elements_fixed_array, index, 1641 LoadFixedArrayElement(elements_fixed_array, 1642 IntPtrAdd(index, IntPtrConstant(1))), 1643 SKIP_WRITE_BARRIER); 1644 }, 1645 1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost); 1646 StoreFixedArrayElement(elements_fixed_array, new_length, 1647 TheHoleConstant()); 1648 GotoIf(WordEqual(value, TheHoleConstant()), &return_undefined); 1649 args.PopAndReturn(value); 1650 } 1651 1652 BIND(&return_undefined); 1653 { args.PopAndReturn(UndefinedConstant()); } 1654 } 1655 1656 BIND(&runtime); 1657 { 1658 // We are not using Parameter(Descriptor::kJSTarget) and loading the value 1659 // from the current frame here in order to reduce register pressure on the 1660 // fast path. 1661 TNode<JSFunction> target = LoadTargetFromFrame(); 1662 TailCallBuiltin(Builtins::kArrayShift, context, target, UndefinedConstant(), 1663 argc); 1664 } 1665 } 1666 1667 TF_BUILTIN(ExtractFastJSArray, ArrayBuiltinsAssembler) { 1668 ParameterMode mode = OptimalParameterMode(); 1669 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1670 Node* array = Parameter(Descriptor::kSource); 1671 Node* begin = TaggedToParameter(Parameter(Descriptor::kBegin), mode); 1672 Node* count = TaggedToParameter(Parameter(Descriptor::kCount), mode); 1673 1674 CSA_ASSERT(this, IsJSArray(array)); 1675 CSA_ASSERT(this, Word32BinaryNot(IsNoElementsProtectorCellInvalid())); 1676 1677 Return(ExtractFastJSArray(context, array, begin, count, mode)); 1678 } 1679 1680 TF_BUILTIN(CloneFastJSArray, ArrayBuiltinsAssembler) { 1681 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1682 Node* array = Parameter(Descriptor::kSource); 1683 1684 CSA_ASSERT(this, IsJSArray(array)); 1685 CSA_ASSERT(this, Word32BinaryNot(IsNoElementsProtectorCellInvalid())); 1686 1687 ParameterMode mode = OptimalParameterMode(); 1688 Return(CloneFastJSArray(context, array, mode)); 1689 } 1690 1691 TF_BUILTIN(ArrayFindLoopContinuation, ArrayBuiltinsAssembler) { 1692 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1693 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 1694 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 1695 Node* this_arg = Parameter(Descriptor::kThisArg); 1696 Node* array = Parameter(Descriptor::kArray); 1697 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); 1698 Node* initial_k = Parameter(Descriptor::kInitialK); 1699 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 1700 Node* to = Parameter(Descriptor::kTo); 1701 1702 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, 1703 this_arg, array, object, initial_k, 1704 len, to); 1705 1706 GenerateIteratingArrayBuiltinLoopContinuation( 1707 &ArrayBuiltinsAssembler::FindProcessor, 1708 &ArrayBuiltinsAssembler::NullPostLoopAction, 1709 MissingPropertyMode::kUseUndefined, ForEachDirection::kForward); 1710 } 1711 1712 // Continuation that is called after an eager deoptimization from TF (ex. the 1713 // array changes during iteration). 1714 TF_BUILTIN(ArrayFindLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { 1715 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1716 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 1717 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 1718 Node* this_arg = Parameter(Descriptor::kThisArg); 1719 Node* initial_k = Parameter(Descriptor::kInitialK); 1720 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 1721 1722 Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver, 1723 callbackfn, this_arg, UndefinedConstant(), receiver, 1724 initial_k, len, UndefinedConstant())); 1725 } 1726 1727 // Continuation that is called after a lazy deoptimization from TF (ex. the 1728 // callback function is no longer callable). 1729 TF_BUILTIN(ArrayFindLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { 1730 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1731 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 1732 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 1733 Node* this_arg = Parameter(Descriptor::kThisArg); 1734 Node* initial_k = Parameter(Descriptor::kInitialK); 1735 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 1736 1737 Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver, 1738 callbackfn, this_arg, UndefinedConstant(), receiver, 1739 initial_k, len, UndefinedConstant())); 1740 } 1741 1742 // Continuation that is called after a lazy deoptimization from TF that happens 1743 // right after the callback and it's returned value must be handled before 1744 // iteration continues. 1745 TF_BUILTIN(ArrayFindLoopAfterCallbackLazyDeoptContinuation, 1746 ArrayBuiltinsAssembler) { 1747 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1748 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 1749 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 1750 Node* this_arg = Parameter(Descriptor::kThisArg); 1751 Node* initial_k = Parameter(Descriptor::kInitialK); 1752 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 1753 Node* found_value = Parameter(Descriptor::kFoundValue); 1754 Node* is_found = Parameter(Descriptor::kIsFound); 1755 1756 // This custom lazy deopt point is right after the callback. find() needs 1757 // to pick up at the next step, which is returning the element if the callback 1758 // value is truthy. Otherwise, continue the search by calling the 1759 // continuation. 1760 Label if_true(this), if_false(this); 1761 BranchIfToBooleanIsTrue(is_found, &if_true, &if_false); 1762 BIND(&if_true); 1763 Return(found_value); 1764 BIND(&if_false); 1765 Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver, 1766 callbackfn, this_arg, UndefinedConstant(), receiver, 1767 initial_k, len, UndefinedConstant())); 1768 } 1769 1770 // ES #sec-get-%typedarray%.prototype.find 1771 TF_BUILTIN(ArrayPrototypeFind, ArrayBuiltinsAssembler) { 1772 TNode<IntPtrT> argc = 1773 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 1774 CodeStubArguments args(this, argc); 1775 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1776 TNode<Object> receiver = args.GetReceiver(); 1777 Node* callbackfn = args.GetOptionalArgumentValue(0); 1778 Node* this_arg = args.GetOptionalArgumentValue(1); 1779 1780 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 1781 1782 GenerateIteratingArrayBuiltinBody( 1783 "Array.prototype.find", &ArrayBuiltinsAssembler::FindResultGenerator, 1784 &ArrayBuiltinsAssembler::FindProcessor, 1785 &ArrayBuiltinsAssembler::NullPostLoopAction, 1786 Builtins::CallableFor(isolate(), Builtins::kArrayFindLoopContinuation), 1787 MissingPropertyMode::kUseUndefined, ForEachDirection::kForward); 1788 } 1789 1790 TF_BUILTIN(ArrayFindIndexLoopContinuation, ArrayBuiltinsAssembler) { 1791 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1792 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 1793 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 1794 Node* this_arg = Parameter(Descriptor::kThisArg); 1795 Node* array = Parameter(Descriptor::kArray); 1796 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); 1797 Node* initial_k = Parameter(Descriptor::kInitialK); 1798 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 1799 Node* to = Parameter(Descriptor::kTo); 1800 1801 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, 1802 this_arg, array, object, initial_k, 1803 len, to); 1804 1805 GenerateIteratingArrayBuiltinLoopContinuation( 1806 &ArrayBuiltinsAssembler::FindIndexProcessor, 1807 &ArrayBuiltinsAssembler::NullPostLoopAction, 1808 MissingPropertyMode::kUseUndefined, ForEachDirection::kForward); 1809 } 1810 1811 TF_BUILTIN(ArrayFindIndexLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { 1812 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1813 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 1814 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 1815 Node* this_arg = Parameter(Descriptor::kThisArg); 1816 Node* initial_k = Parameter(Descriptor::kInitialK); 1817 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 1818 1819 Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context, 1820 receiver, callbackfn, this_arg, SmiConstant(-1), receiver, 1821 initial_k, len, UndefinedConstant())); 1822 } 1823 1824 TF_BUILTIN(ArrayFindIndexLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { 1825 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1826 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 1827 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 1828 Node* this_arg = Parameter(Descriptor::kThisArg); 1829 Node* initial_k = Parameter(Descriptor::kInitialK); 1830 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 1831 1832 Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context, 1833 receiver, callbackfn, this_arg, SmiConstant(-1), receiver, 1834 initial_k, len, UndefinedConstant())); 1835 } 1836 1837 TF_BUILTIN(ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation, 1838 ArrayBuiltinsAssembler) { 1839 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1840 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 1841 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 1842 Node* this_arg = Parameter(Descriptor::kThisArg); 1843 Node* initial_k = Parameter(Descriptor::kInitialK); 1844 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 1845 Node* found_value = Parameter(Descriptor::kFoundValue); 1846 Node* is_found = Parameter(Descriptor::kIsFound); 1847 1848 // This custom lazy deopt point is right after the callback. find() needs 1849 // to pick up at the next step, which is returning the element if the callback 1850 // value is truthy. Otherwise, continue the search by calling the 1851 // continuation. 1852 Label if_true(this), if_false(this); 1853 BranchIfToBooleanIsTrue(is_found, &if_true, &if_false); 1854 BIND(&if_true); 1855 Return(found_value); 1856 BIND(&if_false); 1857 Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context, 1858 receiver, callbackfn, this_arg, SmiConstant(-1), receiver, 1859 initial_k, len, UndefinedConstant())); 1860 } 1861 1862 // ES #sec-get-%typedarray%.prototype.findIndex 1863 TF_BUILTIN(ArrayPrototypeFindIndex, ArrayBuiltinsAssembler) { 1864 TNode<IntPtrT> argc = 1865 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 1866 CodeStubArguments args(this, argc); 1867 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 1868 TNode<Object> receiver = args.GetReceiver(); 1869 Node* callbackfn = args.GetOptionalArgumentValue(0); 1870 Node* this_arg = args.GetOptionalArgumentValue(1); 1871 1872 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 1873 1874 GenerateIteratingArrayBuiltinBody( 1875 "Array.prototype.findIndex", 1876 &ArrayBuiltinsAssembler::FindIndexResultGenerator, 1877 &ArrayBuiltinsAssembler::FindIndexProcessor, 1878 &ArrayBuiltinsAssembler::NullPostLoopAction, 1879 Builtins::CallableFor(isolate(), 1880 Builtins::kArrayFindIndexLoopContinuation), 1881 MissingPropertyMode::kUseUndefined, ForEachDirection::kForward); 1882 } 1883 1884 class ArrayPopulatorAssembler : public CodeStubAssembler { 1885 public: 1886 explicit ArrayPopulatorAssembler(compiler::CodeAssemblerState* state) 1887 : CodeStubAssembler(state) {} 1888 1889 TNode<Object> ConstructArrayLike(TNode<Context> context, 1890 TNode<Object> receiver) { 1891 TVARIABLE(Object, array); 1892 Label is_constructor(this), is_not_constructor(this), done(this); 1893 GotoIf(TaggedIsSmi(receiver), &is_not_constructor); 1894 Branch(IsConstructor(CAST(receiver)), &is_constructor, &is_not_constructor); 1895 1896 BIND(&is_constructor); 1897 { 1898 array = CAST( 1899 ConstructJS(CodeFactory::Construct(isolate()), context, receiver)); 1900 Goto(&done); 1901 } 1902 1903 BIND(&is_not_constructor); 1904 { 1905 Label allocate_js_array(this); 1906 1907 TNode<Map> array_map = CAST(LoadContextElement( 1908 context, Context::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX)); 1909 1910 array = CAST(AllocateJSArray(PACKED_SMI_ELEMENTS, array_map, 1911 SmiConstant(0), SmiConstant(0), nullptr, 1912 ParameterMode::SMI_PARAMETERS)); 1913 Goto(&done); 1914 } 1915 1916 BIND(&done); 1917 return array.value(); 1918 } 1919 1920 TNode<Object> ConstructArrayLike(TNode<Context> context, 1921 TNode<Object> receiver, 1922 TNode<Number> length) { 1923 TVARIABLE(Object, array); 1924 Label is_constructor(this), is_not_constructor(this), done(this); 1925 CSA_ASSERT(this, IsNumberNormalized(length)); 1926 GotoIf(TaggedIsSmi(receiver), &is_not_constructor); 1927 Branch(IsConstructor(CAST(receiver)), &is_constructor, &is_not_constructor); 1928 1929 BIND(&is_constructor); 1930 { 1931 array = CAST(ConstructJS(CodeFactory::Construct(isolate()), context, 1932 receiver, length)); 1933 Goto(&done); 1934 } 1935 1936 BIND(&is_not_constructor); 1937 { 1938 Label allocate_js_array(this); 1939 1940 Label next(this), runtime(this, Label::kDeferred); 1941 TNode<Smi> limit = SmiConstant(JSArray::kInitialMaxFastElementArray); 1942 CSA_ASSERT_BRANCH(this, [=](Label* ok, Label* not_ok) { 1943 BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual, 1944 length, SmiConstant(0), ok, not_ok); 1945 }); 1946 // This check also transitively covers the case where length is too big 1947 // to be representable by a SMI and so is not usable with 1948 // AllocateJSArray. 1949 BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual, length, 1950 limit, &runtime, &next); 1951 1952 BIND(&runtime); 1953 { 1954 TNode<Context> native_context = LoadNativeContext(context); 1955 TNode<JSFunction> array_function = CAST( 1956 LoadContextElement(native_context, Context::ARRAY_FUNCTION_INDEX)); 1957 array = CallRuntime(Runtime::kNewArray, context, array_function, length, 1958 array_function, UndefinedConstant()); 1959 Goto(&done); 1960 } 1961 1962 BIND(&next); 1963 CSA_ASSERT(this, TaggedIsSmi(length)); 1964 1965 TNode<Map> array_map = CAST(LoadContextElement( 1966 context, Context::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX)); 1967 1968 // TODO(delphick): Consider using 1969 // AllocateUninitializedJSArrayWithElements to avoid initializing an 1970 // array and then writing over it. 1971 array = CAST(AllocateJSArray(PACKED_SMI_ELEMENTS, array_map, length, 1972 SmiConstant(0), nullptr, 1973 ParameterMode::SMI_PARAMETERS)); 1974 Goto(&done); 1975 } 1976 1977 BIND(&done); 1978 return array.value(); 1979 } 1980 1981 void GenerateSetLength(TNode<Context> context, TNode<Object> array, 1982 TNode<Number> length) { 1983 Label fast(this), runtime(this), done(this); 1984 // There's no need to set the length, if 1985 // 1) the array is a fast JS array and 1986 // 2) the new length is equal to the old length. 1987 // as the set is not observable. Otherwise fall back to the run-time. 1988 1989 // 1) Check that the array has fast elements. 1990 // TODO(delphick): Consider changing this since it does an an unnecessary 1991 // check for SMIs. 1992 // TODO(delphick): Also we could hoist this to after the array construction 1993 // and copy the args into array in the same way as the Array constructor. 1994 BranchIfFastJSArray(array, context, &fast, &runtime); 1995 1996 BIND(&fast); 1997 { 1998 TNode<JSArray> fast_array = CAST(array); 1999 2000 TNode<Smi> length_smi = CAST(length); 2001 TNode<Smi> old_length = LoadFastJSArrayLength(fast_array); 2002 CSA_ASSERT(this, TaggedIsPositiveSmi(old_length)); 2003 2004 // 2) If the created array's length matches the required length, then 2005 // there's nothing else to do. Otherwise use the runtime to set the 2006 // property as that will insert holes into excess elements or shrink 2007 // the backing store as appropriate. 2008 Branch(SmiNotEqual(length_smi, old_length), &runtime, &done); 2009 } 2010 2011 BIND(&runtime); 2012 { 2013 SetPropertyStrict(context, array, 2014 CodeStubAssembler::LengthStringConstant(), length); 2015 Goto(&done); 2016 } 2017 2018 BIND(&done); 2019 } 2020 }; 2021 2022 // ES #sec-array.from 2023 TF_BUILTIN(ArrayFrom, ArrayPopulatorAssembler) { 2024 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2025 TNode<Int32T> argc = 2026 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)); 2027 2028 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc)); 2029 2030 TNode<Object> map_function = args.GetOptionalArgumentValue(1); 2031 2032 // If map_function is not undefined, then ensure it's callable else throw. 2033 { 2034 Label no_error(this), error(this); 2035 GotoIf(IsUndefined(map_function), &no_error); 2036 GotoIf(TaggedIsSmi(map_function), &error); 2037 Branch(IsCallable(CAST(map_function)), &no_error, &error); 2038 2039 BIND(&error); 2040 ThrowTypeError(context, MessageTemplate::kCalledNonCallable, map_function); 2041 2042 BIND(&no_error); 2043 } 2044 2045 Label iterable(this), not_iterable(this), finished(this), if_exception(this); 2046 2047 TNode<Object> this_arg = args.GetOptionalArgumentValue(2); 2048 TNode<Object> items = args.GetOptionalArgumentValue(0); 2049 // The spec doesn't require ToObject to be called directly on the iterable 2050 // branch, but it's part of GetMethod that is in the spec. 2051 TNode<JSReceiver> array_like = ToObject_Inline(context, items); 2052 2053 TVARIABLE(Object, array); 2054 TVARIABLE(Number, length); 2055 2056 // Determine whether items[Symbol.iterator] is defined: 2057 IteratorBuiltinsAssembler iterator_assembler(state()); 2058 Node* iterator_method = 2059 iterator_assembler.GetIteratorMethod(context, array_like); 2060 Branch(IsNullOrUndefined(iterator_method), ¬_iterable, &iterable); 2061 2062 BIND(&iterable); 2063 { 2064 TVARIABLE(Number, index, SmiConstant(0)); 2065 TVARIABLE(Object, var_exception); 2066 Label loop(this, &index), loop_done(this), 2067 on_exception(this, Label::kDeferred), 2068 index_overflow(this, Label::kDeferred); 2069 2070 // Check that the method is callable. 2071 { 2072 Label get_method_not_callable(this, Label::kDeferred), next(this); 2073 GotoIf(TaggedIsSmi(iterator_method), &get_method_not_callable); 2074 GotoIfNot(IsCallable(CAST(iterator_method)), &get_method_not_callable); 2075 Goto(&next); 2076 2077 BIND(&get_method_not_callable); 2078 ThrowTypeError(context, MessageTemplate::kCalledNonCallable, 2079 iterator_method); 2080 2081 BIND(&next); 2082 } 2083 2084 // Construct the output array with empty length. 2085 array = ConstructArrayLike(context, args.GetReceiver()); 2086 2087 // Actually get the iterator and throw if the iterator method does not yield 2088 // one. 2089 IteratorRecord iterator_record = 2090 iterator_assembler.GetIterator(context, items, iterator_method); 2091 2092 TNode<Context> native_context = LoadNativeContext(context); 2093 TNode<Object> fast_iterator_result_map = 2094 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX); 2095 2096 Goto(&loop); 2097 2098 BIND(&loop); 2099 { 2100 // Loop while iterator is not done. 2101 TNode<Object> next = CAST(iterator_assembler.IteratorStep( 2102 context, iterator_record, &loop_done, fast_iterator_result_map)); 2103 TVARIABLE(Object, value, 2104 CAST(iterator_assembler.IteratorValue( 2105 context, next, fast_iterator_result_map))); 2106 2107 // If a map_function is supplied then call it (using this_arg as 2108 // receiver), on the value returned from the iterator. Exceptions are 2109 // caught so the iterator can be closed. 2110 { 2111 Label next(this); 2112 GotoIf(IsUndefined(map_function), &next); 2113 2114 CSA_ASSERT(this, IsCallable(CAST(map_function))); 2115 Node* v = CallJS(CodeFactory::Call(isolate()), context, map_function, 2116 this_arg, value.value(), index.value()); 2117 GotoIfException(v, &on_exception, &var_exception); 2118 value = CAST(v); 2119 Goto(&next); 2120 BIND(&next); 2121 } 2122 2123 // Store the result in the output object (catching any exceptions so the 2124 // iterator can be closed). 2125 Node* define_status = 2126 CallRuntime(Runtime::kCreateDataProperty, context, array.value(), 2127 index.value(), value.value()); 2128 GotoIfException(define_status, &on_exception, &var_exception); 2129 2130 index = NumberInc(index.value()); 2131 2132 // The spec requires that we throw an exception if index reaches 2^53-1, 2133 // but an empty loop would take >100 days to do this many iterations. To 2134 // actually run for that long would require an iterator that never set 2135 // done to true and a target array which somehow never ran out of memory, 2136 // e.g. a proxy that discarded the values. Ignoring this case just means 2137 // we would repeatedly call CreateDataProperty with index = 2^53. 2138 CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) { 2139 BranchIfNumberRelationalComparison(Operation::kLessThan, index.value(), 2140 NumberConstant(kMaxSafeInteger), ok, 2141 not_ok); 2142 }); 2143 Goto(&loop); 2144 } 2145 2146 BIND(&loop_done); 2147 { 2148 length = index; 2149 Goto(&finished); 2150 } 2151 2152 BIND(&on_exception); 2153 { 2154 // Close the iterator, rethrowing either the passed exception or 2155 // exceptions thrown during the close. 2156 iterator_assembler.IteratorCloseOnException(context, iterator_record, 2157 &var_exception); 2158 } 2159 } 2160 2161 BIND(¬_iterable); 2162 { 2163 // Treat array_like as an array and try to get its length. 2164 length = ToLength_Inline( 2165 context, GetProperty(context, array_like, factory()->length_string())); 2166 2167 // Construct an array using the receiver as constructor with the same length 2168 // as the input array. 2169 array = ConstructArrayLike(context, args.GetReceiver(), length.value()); 2170 2171 TVARIABLE(Number, index, SmiConstant(0)); 2172 2173 // TODO(ishell): remove <Object, Object> 2174 GotoIf(WordEqual<Object, Object>(length.value(), SmiConstant(0)), 2175 &finished); 2176 2177 // Loop from 0 to length-1. 2178 { 2179 Label loop(this, &index); 2180 Goto(&loop); 2181 BIND(&loop); 2182 TVARIABLE(Object, value); 2183 2184 value = GetProperty(context, array_like, index.value()); 2185 2186 // If a map_function is supplied then call it (using this_arg as 2187 // receiver), on the value retrieved from the array. 2188 { 2189 Label next(this); 2190 GotoIf(IsUndefined(map_function), &next); 2191 2192 CSA_ASSERT(this, IsCallable(CAST(map_function))); 2193 value = CAST(CallJS(CodeFactory::Call(isolate()), context, map_function, 2194 this_arg, value.value(), index.value())); 2195 Goto(&next); 2196 BIND(&next); 2197 } 2198 2199 // Store the result in the output object. 2200 CallRuntime(Runtime::kCreateDataProperty, context, array.value(), 2201 index.value(), value.value()); 2202 index = NumberInc(index.value()); 2203 BranchIfNumberRelationalComparison(Operation::kLessThan, index.value(), 2204 length.value(), &loop, &finished); 2205 } 2206 } 2207 2208 BIND(&finished); 2209 2210 // Finally set the length on the output and return it. 2211 GenerateSetLength(context, array.value(), length.value()); 2212 args.PopAndReturn(array.value()); 2213 } 2214 2215 // ES #sec-array.of 2216 TF_BUILTIN(ArrayOf, ArrayPopulatorAssembler) { 2217 TNode<Int32T> argc = 2218 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)); 2219 TNode<Smi> length = SmiFromInt32(argc); 2220 2221 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2222 2223 CodeStubArguments args(this, length, nullptr, ParameterMode::SMI_PARAMETERS); 2224 2225 TNode<Object> array = ConstructArrayLike(context, args.GetReceiver(), length); 2226 2227 // TODO(delphick): Avoid using CreateDataProperty on the fast path. 2228 BuildFastLoop(SmiConstant(0), length, 2229 [=](Node* index) { 2230 CallRuntime( 2231 Runtime::kCreateDataProperty, context, 2232 static_cast<Node*>(array), index, 2233 args.AtIndex(index, ParameterMode::SMI_PARAMETERS)); 2234 }, 2235 1, ParameterMode::SMI_PARAMETERS, IndexAdvanceMode::kPost); 2236 2237 GenerateSetLength(context, array, length); 2238 args.PopAndReturn(array); 2239 } 2240 2241 // ES #sec-get-%typedarray%.prototype.find 2242 TF_BUILTIN(TypedArrayPrototypeFind, ArrayBuiltinsAssembler) { 2243 TNode<IntPtrT> argc = 2244 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2245 CodeStubArguments args(this, argc); 2246 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2247 TNode<Object> receiver = args.GetReceiver(); 2248 Node* callbackfn = args.GetOptionalArgumentValue(0); 2249 Node* this_arg = args.GetOptionalArgumentValue(1); 2250 2251 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 2252 2253 GenerateIteratingTypedArrayBuiltinBody( 2254 "%TypedArray%.prototype.find", 2255 &ArrayBuiltinsAssembler::FindResultGenerator, 2256 &ArrayBuiltinsAssembler::FindProcessor, 2257 &ArrayBuiltinsAssembler::NullPostLoopAction); 2258 } 2259 2260 // ES #sec-get-%typedarray%.prototype.findIndex 2261 TF_BUILTIN(TypedArrayPrototypeFindIndex, ArrayBuiltinsAssembler) { 2262 TNode<IntPtrT> argc = 2263 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2264 CodeStubArguments args(this, argc); 2265 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2266 TNode<Object> receiver = args.GetReceiver(); 2267 Node* callbackfn = args.GetOptionalArgumentValue(0); 2268 Node* this_arg = args.GetOptionalArgumentValue(1); 2269 2270 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 2271 2272 GenerateIteratingTypedArrayBuiltinBody( 2273 "%TypedArray%.prototype.findIndex", 2274 &ArrayBuiltinsAssembler::FindIndexResultGenerator, 2275 &ArrayBuiltinsAssembler::FindIndexProcessor, 2276 &ArrayBuiltinsAssembler::NullPostLoopAction); 2277 } 2278 2279 TF_BUILTIN(TypedArrayPrototypeForEach, ArrayBuiltinsAssembler) { 2280 TNode<IntPtrT> argc = 2281 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2282 CodeStubArguments args(this, argc); 2283 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2284 TNode<Object> receiver = args.GetReceiver(); 2285 Node* callbackfn = args.GetOptionalArgumentValue(0); 2286 Node* this_arg = args.GetOptionalArgumentValue(1); 2287 2288 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 2289 2290 GenerateIteratingTypedArrayBuiltinBody( 2291 "%TypedArray%.prototype.forEach", 2292 &ArrayBuiltinsAssembler::ForEachResultGenerator, 2293 &ArrayBuiltinsAssembler::ForEachProcessor, 2294 &ArrayBuiltinsAssembler::NullPostLoopAction); 2295 } 2296 2297 TF_BUILTIN(ArraySomeLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { 2298 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2299 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2300 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2301 Node* this_arg = Parameter(Descriptor::kThisArg); 2302 Node* initial_k = Parameter(Descriptor::kInitialK); 2303 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2304 Node* result = Parameter(Descriptor::kResult); 2305 2306 // This custom lazy deopt point is right after the callback. every() needs 2307 // to pick up at the next step, which is either continuing to the next 2308 // array element or returning false if {result} is false. 2309 Label true_continue(this), false_continue(this); 2310 2311 // iii. If selected is true, then... 2312 BranchIfToBooleanIsTrue(result, &true_continue, &false_continue); 2313 BIND(&true_continue); 2314 { Return(TrueConstant()); } 2315 BIND(&false_continue); 2316 { 2317 // Increment k. 2318 initial_k = NumberInc(initial_k); 2319 2320 Return(CallBuiltin(Builtins::kArraySomeLoopContinuation, context, receiver, 2321 callbackfn, this_arg, FalseConstant(), receiver, 2322 initial_k, len, UndefinedConstant())); 2323 } 2324 } 2325 2326 TF_BUILTIN(ArraySomeLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { 2327 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2328 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2329 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2330 Node* this_arg = Parameter(Descriptor::kThisArg); 2331 Node* initial_k = Parameter(Descriptor::kInitialK); 2332 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2333 2334 Return(CallBuiltin(Builtins::kArraySomeLoopContinuation, context, receiver, 2335 callbackfn, this_arg, FalseConstant(), receiver, initial_k, 2336 len, UndefinedConstant())); 2337 } 2338 2339 TF_BUILTIN(ArraySomeLoopContinuation, ArrayBuiltinsAssembler) { 2340 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2341 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2342 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2343 Node* this_arg = Parameter(Descriptor::kThisArg); 2344 Node* array = Parameter(Descriptor::kArray); 2345 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); 2346 Node* initial_k = Parameter(Descriptor::kInitialK); 2347 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2348 Node* to = Parameter(Descriptor::kTo); 2349 2350 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, 2351 this_arg, array, object, initial_k, 2352 len, to); 2353 2354 GenerateIteratingArrayBuiltinLoopContinuation( 2355 &ArrayBuiltinsAssembler::SomeProcessor, 2356 &ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip); 2357 } 2358 2359 TF_BUILTIN(ArraySome, ArrayBuiltinsAssembler) { 2360 TNode<IntPtrT> argc = 2361 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2362 CodeStubArguments args(this, argc); 2363 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2364 TNode<Object> receiver = args.GetReceiver(); 2365 Node* callbackfn = args.GetOptionalArgumentValue(0); 2366 Node* this_arg = args.GetOptionalArgumentValue(1); 2367 2368 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 2369 2370 GenerateIteratingArrayBuiltinBody( 2371 "Array.prototype.some", &ArrayBuiltinsAssembler::SomeResultGenerator, 2372 &ArrayBuiltinsAssembler::SomeProcessor, 2373 &ArrayBuiltinsAssembler::NullPostLoopAction, 2374 Builtins::CallableFor(isolate(), Builtins::kArraySomeLoopContinuation), 2375 MissingPropertyMode::kSkip); 2376 } 2377 2378 TF_BUILTIN(TypedArrayPrototypeSome, ArrayBuiltinsAssembler) { 2379 TNode<IntPtrT> argc = 2380 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2381 CodeStubArguments args(this, argc); 2382 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2383 TNode<Object> receiver = args.GetReceiver(); 2384 Node* callbackfn = args.GetOptionalArgumentValue(0); 2385 Node* this_arg = args.GetOptionalArgumentValue(1); 2386 2387 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 2388 2389 GenerateIteratingTypedArrayBuiltinBody( 2390 "%TypedArray%.prototype.some", 2391 &ArrayBuiltinsAssembler::SomeResultGenerator, 2392 &ArrayBuiltinsAssembler::SomeProcessor, 2393 &ArrayBuiltinsAssembler::NullPostLoopAction); 2394 } 2395 2396 TF_BUILTIN(ArrayEveryLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { 2397 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2398 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2399 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2400 Node* this_arg = Parameter(Descriptor::kThisArg); 2401 Node* initial_k = Parameter(Descriptor::kInitialK); 2402 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2403 Node* result = Parameter(Descriptor::kResult); 2404 2405 // This custom lazy deopt point is right after the callback. every() needs 2406 // to pick up at the next step, which is either continuing to the next 2407 // array element or returning false if {result} is false. 2408 Label true_continue(this), false_continue(this); 2409 2410 // iii. If selected is true, then... 2411 BranchIfToBooleanIsTrue(result, &true_continue, &false_continue); 2412 BIND(&true_continue); 2413 { 2414 // Increment k. 2415 initial_k = NumberInc(initial_k); 2416 2417 Return(CallBuiltin(Builtins::kArrayEveryLoopContinuation, context, receiver, 2418 callbackfn, this_arg, TrueConstant(), receiver, 2419 initial_k, len, UndefinedConstant())); 2420 } 2421 BIND(&false_continue); 2422 { Return(FalseConstant()); } 2423 } 2424 2425 TF_BUILTIN(ArrayEveryLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { 2426 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2427 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2428 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2429 Node* this_arg = Parameter(Descriptor::kThisArg); 2430 Node* initial_k = Parameter(Descriptor::kInitialK); 2431 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2432 2433 Return(CallBuiltin(Builtins::kArrayEveryLoopContinuation, context, receiver, 2434 callbackfn, this_arg, TrueConstant(), receiver, initial_k, 2435 len, UndefinedConstant())); 2436 } 2437 2438 TF_BUILTIN(ArrayEveryLoopContinuation, ArrayBuiltinsAssembler) { 2439 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2440 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2441 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2442 Node* this_arg = Parameter(Descriptor::kThisArg); 2443 Node* array = Parameter(Descriptor::kArray); 2444 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); 2445 Node* initial_k = Parameter(Descriptor::kInitialK); 2446 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2447 Node* to = Parameter(Descriptor::kTo); 2448 2449 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, 2450 this_arg, array, object, initial_k, 2451 len, to); 2452 2453 GenerateIteratingArrayBuiltinLoopContinuation( 2454 &ArrayBuiltinsAssembler::EveryProcessor, 2455 &ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip); 2456 } 2457 2458 TF_BUILTIN(ArrayEvery, ArrayBuiltinsAssembler) { 2459 TNode<IntPtrT> argc = 2460 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2461 CodeStubArguments args(this, argc); 2462 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2463 TNode<Object> receiver = args.GetReceiver(); 2464 Node* callbackfn = args.GetOptionalArgumentValue(0); 2465 Node* this_arg = args.GetOptionalArgumentValue(1); 2466 2467 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 2468 2469 GenerateIteratingArrayBuiltinBody( 2470 "Array.prototype.every", &ArrayBuiltinsAssembler::EveryResultGenerator, 2471 &ArrayBuiltinsAssembler::EveryProcessor, 2472 &ArrayBuiltinsAssembler::NullPostLoopAction, 2473 Builtins::CallableFor(isolate(), Builtins::kArrayEveryLoopContinuation), 2474 MissingPropertyMode::kSkip); 2475 } 2476 2477 TF_BUILTIN(TypedArrayPrototypeEvery, ArrayBuiltinsAssembler) { 2478 TNode<IntPtrT> argc = 2479 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2480 CodeStubArguments args(this, argc); 2481 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2482 TNode<Object> receiver = args.GetReceiver(); 2483 Node* callbackfn = args.GetOptionalArgumentValue(0); 2484 Node* this_arg = args.GetOptionalArgumentValue(1); 2485 2486 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 2487 2488 GenerateIteratingTypedArrayBuiltinBody( 2489 "%TypedArray%.prototype.every", 2490 &ArrayBuiltinsAssembler::EveryResultGenerator, 2491 &ArrayBuiltinsAssembler::EveryProcessor, 2492 &ArrayBuiltinsAssembler::NullPostLoopAction); 2493 } 2494 2495 TF_BUILTIN(ArrayReduceLoopContinuation, ArrayBuiltinsAssembler) { 2496 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2497 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2498 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2499 Node* this_arg = Parameter(Descriptor::kThisArg); 2500 Node* accumulator = Parameter(Descriptor::kAccumulator); 2501 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); 2502 Node* initial_k = Parameter(Descriptor::kInitialK); 2503 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2504 Node* to = Parameter(Descriptor::kTo); 2505 2506 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, 2507 this_arg, accumulator, object, 2508 initial_k, len, to); 2509 2510 GenerateIteratingArrayBuiltinLoopContinuation( 2511 &ArrayBuiltinsAssembler::ReduceProcessor, 2512 &ArrayBuiltinsAssembler::ReducePostLoopAction, 2513 MissingPropertyMode::kSkip); 2514 } 2515 2516 TF_BUILTIN(ArrayReducePreLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { 2517 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2518 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2519 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2520 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2521 2522 // Simulate starting the loop at 0, but ensuring that the accumulator is 2523 // the hole. The continuation stub will search for the initial non-hole 2524 // element, rightly throwing an exception if not found. 2525 Return(CallBuiltin(Builtins::kArrayReduceLoopContinuation, context, receiver, 2526 callbackfn, UndefinedConstant(), TheHoleConstant(), 2527 receiver, SmiConstant(0), len, UndefinedConstant())); 2528 } 2529 2530 TF_BUILTIN(ArrayReduceLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { 2531 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2532 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2533 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2534 Node* accumulator = Parameter(Descriptor::kAccumulator); 2535 Node* initial_k = Parameter(Descriptor::kInitialK); 2536 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2537 2538 Return(CallBuiltin(Builtins::kArrayReduceLoopContinuation, context, receiver, 2539 callbackfn, UndefinedConstant(), accumulator, receiver, 2540 initial_k, len, UndefinedConstant())); 2541 } 2542 2543 TF_BUILTIN(ArrayReduceLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { 2544 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2545 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2546 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2547 Node* initial_k = Parameter(Descriptor::kInitialK); 2548 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2549 Node* result = Parameter(Descriptor::kResult); 2550 2551 Return(CallBuiltin(Builtins::kArrayReduceLoopContinuation, context, receiver, 2552 callbackfn, UndefinedConstant(), result, receiver, 2553 initial_k, len, UndefinedConstant())); 2554 } 2555 2556 TF_BUILTIN(ArrayReduce, ArrayBuiltinsAssembler) { 2557 TNode<IntPtrT> argc = 2558 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2559 CodeStubArguments args(this, argc); 2560 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2561 TNode<Object> receiver = args.GetReceiver(); 2562 Node* callbackfn = args.GetOptionalArgumentValue(0); 2563 Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant()); 2564 2565 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value, 2566 argc); 2567 2568 GenerateIteratingArrayBuiltinBody( 2569 "Array.prototype.reduce", &ArrayBuiltinsAssembler::ReduceResultGenerator, 2570 &ArrayBuiltinsAssembler::ReduceProcessor, 2571 &ArrayBuiltinsAssembler::ReducePostLoopAction, 2572 Builtins::CallableFor(isolate(), Builtins::kArrayReduceLoopContinuation), 2573 MissingPropertyMode::kSkip); 2574 } 2575 2576 TF_BUILTIN(TypedArrayPrototypeReduce, ArrayBuiltinsAssembler) { 2577 TNode<IntPtrT> argc = 2578 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2579 CodeStubArguments args(this, argc); 2580 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2581 TNode<Object> receiver = args.GetReceiver(); 2582 Node* callbackfn = args.GetOptionalArgumentValue(0); 2583 Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant()); 2584 2585 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value, 2586 argc); 2587 2588 GenerateIteratingTypedArrayBuiltinBody( 2589 "%TypedArray%.prototype.reduce", 2590 &ArrayBuiltinsAssembler::ReduceResultGenerator, 2591 &ArrayBuiltinsAssembler::ReduceProcessor, 2592 &ArrayBuiltinsAssembler::ReducePostLoopAction); 2593 } 2594 2595 TF_BUILTIN(ArrayReduceRightLoopContinuation, ArrayBuiltinsAssembler) { 2596 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2597 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2598 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2599 Node* this_arg = Parameter(Descriptor::kThisArg); 2600 Node* accumulator = Parameter(Descriptor::kAccumulator); 2601 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); 2602 Node* initial_k = Parameter(Descriptor::kInitialK); 2603 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2604 Node* to = Parameter(Descriptor::kTo); 2605 2606 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, 2607 this_arg, accumulator, object, 2608 initial_k, len, to); 2609 2610 GenerateIteratingArrayBuiltinLoopContinuation( 2611 &ArrayBuiltinsAssembler::ReduceProcessor, 2612 &ArrayBuiltinsAssembler::ReducePostLoopAction, MissingPropertyMode::kSkip, 2613 ForEachDirection::kReverse); 2614 } 2615 2616 TF_BUILTIN(ArrayReduceRightPreLoopEagerDeoptContinuation, 2617 ArrayBuiltinsAssembler) { 2618 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2619 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2620 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2621 TNode<Smi> len = CAST(Parameter(Descriptor::kLength)); 2622 2623 // Simulate starting the loop at 0, but ensuring that the accumulator is 2624 // the hole. The continuation stub will search for the initial non-hole 2625 // element, rightly throwing an exception if not found. 2626 Return(CallBuiltin(Builtins::kArrayReduceRightLoopContinuation, context, 2627 receiver, callbackfn, UndefinedConstant(), 2628 TheHoleConstant(), receiver, SmiSub(len, SmiConstant(1)), 2629 len, UndefinedConstant())); 2630 } 2631 2632 TF_BUILTIN(ArrayReduceRightLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { 2633 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2634 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2635 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2636 Node* accumulator = Parameter(Descriptor::kAccumulator); 2637 Node* initial_k = Parameter(Descriptor::kInitialK); 2638 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2639 2640 Return(CallBuiltin(Builtins::kArrayReduceRightLoopContinuation, context, 2641 receiver, callbackfn, UndefinedConstant(), accumulator, 2642 receiver, initial_k, len, UndefinedConstant())); 2643 } 2644 2645 TF_BUILTIN(ArrayReduceRightLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { 2646 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2647 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2648 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2649 Node* initial_k = Parameter(Descriptor::kInitialK); 2650 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2651 Node* result = Parameter(Descriptor::kResult); 2652 2653 Return(CallBuiltin(Builtins::kArrayReduceRightLoopContinuation, context, 2654 receiver, callbackfn, UndefinedConstant(), result, 2655 receiver, initial_k, len, UndefinedConstant())); 2656 } 2657 2658 TF_BUILTIN(ArrayReduceRight, ArrayBuiltinsAssembler) { 2659 TNode<IntPtrT> argc = 2660 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2661 CodeStubArguments args(this, argc); 2662 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2663 TNode<Object> receiver = args.GetReceiver(); 2664 Node* callbackfn = args.GetOptionalArgumentValue(0); 2665 Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant()); 2666 2667 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value, 2668 argc); 2669 2670 GenerateIteratingArrayBuiltinBody( 2671 "Array.prototype.reduceRight", 2672 &ArrayBuiltinsAssembler::ReduceResultGenerator, 2673 &ArrayBuiltinsAssembler::ReduceProcessor, 2674 &ArrayBuiltinsAssembler::ReducePostLoopAction, 2675 Builtins::CallableFor(isolate(), 2676 Builtins::kArrayReduceRightLoopContinuation), 2677 MissingPropertyMode::kSkip, ForEachDirection::kReverse); 2678 } 2679 2680 TF_BUILTIN(TypedArrayPrototypeReduceRight, ArrayBuiltinsAssembler) { 2681 TNode<IntPtrT> argc = 2682 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2683 CodeStubArguments args(this, argc); 2684 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2685 TNode<Object> receiver = args.GetReceiver(); 2686 Node* callbackfn = args.GetOptionalArgumentValue(0); 2687 Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant()); 2688 2689 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value, 2690 argc); 2691 2692 GenerateIteratingTypedArrayBuiltinBody( 2693 "%TypedArray%.prototype.reduceRight", 2694 &ArrayBuiltinsAssembler::ReduceResultGenerator, 2695 &ArrayBuiltinsAssembler::ReduceProcessor, 2696 &ArrayBuiltinsAssembler::ReducePostLoopAction, 2697 ForEachDirection::kReverse); 2698 } 2699 2700 TF_BUILTIN(ArrayFilterLoopContinuation, ArrayBuiltinsAssembler) { 2701 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2702 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2703 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2704 Node* this_arg = Parameter(Descriptor::kThisArg); 2705 Node* array = Parameter(Descriptor::kArray); 2706 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); 2707 Node* initial_k = Parameter(Descriptor::kInitialK); 2708 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2709 Node* to = Parameter(Descriptor::kTo); 2710 2711 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, 2712 this_arg, array, object, initial_k, 2713 len, to); 2714 2715 GenerateIteratingArrayBuiltinLoopContinuation( 2716 &ArrayBuiltinsAssembler::FilterProcessor, 2717 &ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip); 2718 } 2719 2720 TF_BUILTIN(ArrayFilterLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { 2721 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2722 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2723 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2724 Node* this_arg = Parameter(Descriptor::kThisArg); 2725 Node* array = Parameter(Descriptor::kArray); 2726 Node* initial_k = Parameter(Descriptor::kInitialK); 2727 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2728 Node* to = Parameter(Descriptor::kTo); 2729 2730 Return(CallBuiltin(Builtins::kArrayFilterLoopContinuation, context, receiver, 2731 callbackfn, this_arg, array, receiver, initial_k, len, 2732 to)); 2733 } 2734 2735 TF_BUILTIN(ArrayFilterLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { 2736 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2737 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2738 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2739 Node* this_arg = Parameter(Descriptor::kThisArg); 2740 Node* array = Parameter(Descriptor::kArray); 2741 Node* initial_k = Parameter(Descriptor::kInitialK); 2742 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2743 Node* value_k = Parameter(Descriptor::kValueK); 2744 Node* result = Parameter(Descriptor::kResult); 2745 2746 VARIABLE(to, MachineRepresentation::kTagged, Parameter(Descriptor::kTo)); 2747 2748 // This custom lazy deopt point is right after the callback. filter() needs 2749 // to pick up at the next step, which is setting the callback result in 2750 // the output array. After incrementing k and to, we can glide into the loop 2751 // continuation builtin. 2752 2753 Label true_continue(this, &to), false_continue(this); 2754 2755 // iii. If selected is true, then... 2756 BranchIfToBooleanIsTrue(result, &true_continue, &false_continue); 2757 BIND(&true_continue); 2758 { 2759 // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue). 2760 CallRuntime(Runtime::kCreateDataProperty, context, array, to.value(), 2761 value_k); 2762 // 2. Increase to by 1. 2763 to.Bind(NumberInc(to.value())); 2764 Goto(&false_continue); 2765 } 2766 BIND(&false_continue); 2767 2768 // Increment k. 2769 initial_k = NumberInc(initial_k); 2770 2771 Return(CallBuiltin(Builtins::kArrayFilterLoopContinuation, context, receiver, 2772 callbackfn, this_arg, array, receiver, initial_k, len, 2773 to.value())); 2774 } 2775 2776 TF_BUILTIN(ArrayFilter, ArrayBuiltinsAssembler) { 2777 TNode<IntPtrT> argc = 2778 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2779 CodeStubArguments args(this, argc); 2780 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2781 TNode<Object> receiver = args.GetReceiver(); 2782 Node* callbackfn = args.GetOptionalArgumentValue(0); 2783 Node* this_arg = args.GetOptionalArgumentValue(1); 2784 2785 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 2786 2787 GenerateIteratingArrayBuiltinBody( 2788 "Array.prototype.filter", &ArrayBuiltinsAssembler::FilterResultGenerator, 2789 &ArrayBuiltinsAssembler::FilterProcessor, 2790 &ArrayBuiltinsAssembler::NullPostLoopAction, 2791 Builtins::CallableFor(isolate(), Builtins::kArrayFilterLoopContinuation), 2792 MissingPropertyMode::kSkip); 2793 } 2794 2795 TF_BUILTIN(ArrayMapLoopContinuation, ArrayBuiltinsAssembler) { 2796 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2797 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2798 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2799 Node* this_arg = Parameter(Descriptor::kThisArg); 2800 Node* array = Parameter(Descriptor::kArray); 2801 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); 2802 Node* initial_k = Parameter(Descriptor::kInitialK); 2803 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2804 Node* to = Parameter(Descriptor::kTo); 2805 2806 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, 2807 this_arg, array, object, initial_k, 2808 len, to); 2809 2810 GenerateIteratingArrayBuiltinLoopContinuation( 2811 &ArrayBuiltinsAssembler::SpecCompliantMapProcessor, 2812 &ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip); 2813 } 2814 2815 TF_BUILTIN(ArrayMapLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { 2816 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2817 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2818 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2819 Node* this_arg = Parameter(Descriptor::kThisArg); 2820 Node* array = Parameter(Descriptor::kArray); 2821 Node* initial_k = Parameter(Descriptor::kInitialK); 2822 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2823 2824 Return(CallBuiltin(Builtins::kArrayMapLoopContinuation, context, receiver, 2825 callbackfn, this_arg, array, receiver, initial_k, len, 2826 UndefinedConstant())); 2827 } 2828 2829 TF_BUILTIN(ArrayMapLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { 2830 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2831 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 2832 Node* callbackfn = Parameter(Descriptor::kCallbackFn); 2833 Node* this_arg = Parameter(Descriptor::kThisArg); 2834 Node* array = Parameter(Descriptor::kArray); 2835 Node* initial_k = Parameter(Descriptor::kInitialK); 2836 TNode<Number> len = CAST(Parameter(Descriptor::kLength)); 2837 Node* result = Parameter(Descriptor::kResult); 2838 2839 // This custom lazy deopt point is right after the callback. map() needs 2840 // to pick up at the next step, which is setting the callback result in 2841 // the output array. After incrementing k, we can glide into the loop 2842 // continuation builtin. 2843 2844 // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). 2845 CallRuntime(Runtime::kCreateDataProperty, context, array, initial_k, result); 2846 // Then we have to increment k before going on. 2847 initial_k = NumberInc(initial_k); 2848 2849 Return(CallBuiltin(Builtins::kArrayMapLoopContinuation, context, receiver, 2850 callbackfn, this_arg, array, receiver, initial_k, len, 2851 UndefinedConstant())); 2852 } 2853 2854 TF_BUILTIN(ArrayMap, ArrayBuiltinsAssembler) { 2855 TNode<IntPtrT> argc = 2856 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2857 CodeStubArguments args(this, argc); 2858 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2859 TNode<Object> receiver = args.GetReceiver(); 2860 Node* callbackfn = args.GetOptionalArgumentValue(0); 2861 Node* this_arg = args.GetOptionalArgumentValue(1); 2862 2863 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 2864 2865 GenerateIteratingArrayBuiltinBody( 2866 "Array.prototype.map", &ArrayBuiltinsAssembler::MapResultGenerator, 2867 &ArrayBuiltinsAssembler::FastMapProcessor, 2868 &ArrayBuiltinsAssembler::NullPostLoopAction, 2869 Builtins::CallableFor(isolate(), Builtins::kArrayMapLoopContinuation), 2870 MissingPropertyMode::kSkip); 2871 } 2872 2873 TF_BUILTIN(TypedArrayPrototypeMap, ArrayBuiltinsAssembler) { 2874 TNode<IntPtrT> argc = 2875 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 2876 CodeStubArguments args(this, argc); 2877 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2878 TNode<Object> receiver = args.GetReceiver(); 2879 Node* callbackfn = args.GetOptionalArgumentValue(0); 2880 Node* this_arg = args.GetOptionalArgumentValue(1); 2881 2882 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); 2883 2884 GenerateIteratingTypedArrayBuiltinBody( 2885 "%TypedArray%.prototype.map", 2886 &ArrayBuiltinsAssembler::TypedArrayMapResultGenerator, 2887 &ArrayBuiltinsAssembler::TypedArrayMapProcessor, 2888 &ArrayBuiltinsAssembler::NullPostLoopAction); 2889 } 2890 2891 TF_BUILTIN(ArrayIsArray, CodeStubAssembler) { 2892 TNode<Object> object = CAST(Parameter(Descriptor::kArg)); 2893 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2894 2895 Label call_runtime(this), return_true(this), return_false(this); 2896 2897 GotoIf(TaggedIsSmi(object), &return_false); 2898 TNode<Int32T> instance_type = LoadInstanceType(CAST(object)); 2899 2900 GotoIf(InstanceTypeEqual(instance_type, JS_ARRAY_TYPE), &return_true); 2901 2902 // TODO(verwaest): Handle proxies in-place. 2903 Branch(InstanceTypeEqual(instance_type, JS_PROXY_TYPE), &call_runtime, 2904 &return_false); 2905 2906 BIND(&return_true); 2907 Return(TrueConstant()); 2908 2909 BIND(&return_false); 2910 Return(FalseConstant()); 2911 2912 BIND(&call_runtime); 2913 Return(CallRuntime(Runtime::kArrayIsArray, context, object)); 2914 } 2915 2916 class ArrayIncludesIndexofAssembler : public CodeStubAssembler { 2917 public: 2918 explicit ArrayIncludesIndexofAssembler(compiler::CodeAssemblerState* state) 2919 : CodeStubAssembler(state) {} 2920 2921 enum SearchVariant { kIncludes, kIndexOf }; 2922 2923 void Generate(SearchVariant variant, TNode<IntPtrT> argc, 2924 TNode<Context> context); 2925 void GenerateSmiOrObject(SearchVariant variant, Node* context, Node* elements, 2926 Node* search_element, Node* array_length, 2927 Node* from_index); 2928 void GeneratePackedDoubles(SearchVariant variant, Node* elements, 2929 Node* search_element, Node* array_length, 2930 Node* from_index); 2931 void GenerateHoleyDoubles(SearchVariant variant, Node* elements, 2932 Node* search_element, Node* array_length, 2933 Node* from_index); 2934 }; 2935 2936 void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant, 2937 TNode<IntPtrT> argc, 2938 TNode<Context> context) { 2939 const int kSearchElementArg = 0; 2940 const int kFromIndexArg = 1; 2941 2942 CodeStubArguments args(this, argc); 2943 2944 TNode<Object> receiver = args.GetReceiver(); 2945 TNode<Object> search_element = 2946 args.GetOptionalArgumentValue(kSearchElementArg); 2947 2948 Node* intptr_zero = IntPtrConstant(0); 2949 2950 Label init_index(this), return_not_found(this), call_runtime(this); 2951 2952 // Take slow path if not a JSArray, if retrieving elements requires 2953 // traversing prototype, or if access checks are required. 2954 BranchIfFastJSArray(receiver, context, &init_index, &call_runtime); 2955 2956 BIND(&init_index); 2957 VARIABLE(index_var, MachineType::PointerRepresentation(), intptr_zero); 2958 TNode<JSArray> array = CAST(receiver); 2959 2960 // JSArray length is always a positive Smi for fast arrays. 2961 CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array))); 2962 Node* array_length = LoadFastJSArrayLength(array); 2963 Node* array_length_untagged = SmiUntag(array_length); 2964 2965 { 2966 // Initialize fromIndex. 2967 Label is_smi(this), is_nonsmi(this), done(this); 2968 2969 // If no fromIndex was passed, default to 0. 2970 GotoIf(IntPtrLessThanOrEqual(argc, IntPtrConstant(kFromIndexArg)), &done); 2971 2972 Node* start_from = args.AtIndex(kFromIndexArg); 2973 // Handle Smis and undefined here and everything else in runtime. 2974 // We must be very careful with side effects from the ToInteger conversion, 2975 // as the side effects might render previously checked assumptions about 2976 // the receiver being a fast JSArray and its length invalid. 2977 Branch(TaggedIsSmi(start_from), &is_smi, &is_nonsmi); 2978 2979 BIND(&is_nonsmi); 2980 { 2981 GotoIfNot(IsUndefined(start_from), &call_runtime); 2982 Goto(&done); 2983 } 2984 BIND(&is_smi); 2985 { 2986 Node* intptr_start_from = SmiUntag(start_from); 2987 index_var.Bind(intptr_start_from); 2988 2989 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done); 2990 // The fromIndex is negative: add it to the array's length. 2991 index_var.Bind(IntPtrAdd(array_length_untagged, index_var.value())); 2992 // Clamp negative results at zero. 2993 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done); 2994 index_var.Bind(intptr_zero); 2995 Goto(&done); 2996 } 2997 BIND(&done); 2998 } 2999 3000 // Fail early if startIndex >= array.length. 3001 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length_untagged), 3002 &return_not_found); 3003 3004 Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this); 3005 3006 TNode<Int32T> elements_kind = LoadElementsKind(array); 3007 Node* elements = LoadElements(array); 3008 STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0); 3009 STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1); 3010 STATIC_ASSERT(PACKED_ELEMENTS == 2); 3011 STATIC_ASSERT(HOLEY_ELEMENTS == 3); 3012 GotoIf(Uint32LessThanOrEqual(elements_kind, Int32Constant(HOLEY_ELEMENTS)), 3013 &if_smiorobjects); 3014 GotoIf(Word32Equal(elements_kind, Int32Constant(PACKED_DOUBLE_ELEMENTS)), 3015 &if_packed_doubles); 3016 GotoIf(Word32Equal(elements_kind, Int32Constant(HOLEY_DOUBLE_ELEMENTS)), 3017 &if_holey_doubles); 3018 Goto(&return_not_found); 3019 3020 BIND(&if_smiorobjects); 3021 { 3022 Callable callable = 3023 (variant == kIncludes) 3024 ? Builtins::CallableFor(isolate(), 3025 Builtins::kArrayIncludesSmiOrObject) 3026 : Builtins::CallableFor(isolate(), 3027 Builtins::kArrayIndexOfSmiOrObject); 3028 Node* result = CallStub(callable, context, elements, search_element, 3029 array_length, SmiTag(index_var.value())); 3030 args.PopAndReturn(result); 3031 } 3032 3033 BIND(&if_packed_doubles); 3034 { 3035 Callable callable = 3036 (variant == kIncludes) 3037 ? Builtins::CallableFor(isolate(), 3038 Builtins::kArrayIncludesPackedDoubles) 3039 : Builtins::CallableFor(isolate(), 3040 Builtins::kArrayIndexOfPackedDoubles); 3041 Node* result = CallStub(callable, context, elements, search_element, 3042 array_length, SmiTag(index_var.value())); 3043 args.PopAndReturn(result); 3044 } 3045 3046 BIND(&if_holey_doubles); 3047 { 3048 Callable callable = 3049 (variant == kIncludes) 3050 ? Builtins::CallableFor(isolate(), 3051 Builtins::kArrayIncludesHoleyDoubles) 3052 : Builtins::CallableFor(isolate(), 3053 Builtins::kArrayIndexOfHoleyDoubles); 3054 Node* result = CallStub(callable, context, elements, search_element, 3055 array_length, SmiTag(index_var.value())); 3056 args.PopAndReturn(result); 3057 } 3058 3059 BIND(&return_not_found); 3060 if (variant == kIncludes) { 3061 args.PopAndReturn(FalseConstant()); 3062 } else { 3063 args.PopAndReturn(NumberConstant(-1)); 3064 } 3065 3066 BIND(&call_runtime); 3067 { 3068 Node* start_from = 3069 args.GetOptionalArgumentValue(kFromIndexArg, UndefinedConstant()); 3070 Runtime::FunctionId function = variant == kIncludes 3071 ? Runtime::kArrayIncludes_Slow 3072 : Runtime::kArrayIndexOf; 3073 args.PopAndReturn( 3074 CallRuntime(function, context, array, search_element, start_from)); 3075 } 3076 } 3077 3078 void ArrayIncludesIndexofAssembler::GenerateSmiOrObject( 3079 SearchVariant variant, Node* context, Node* elements, Node* search_element, 3080 Node* array_length, Node* from_index) { 3081 VARIABLE(index_var, MachineType::PointerRepresentation(), 3082 SmiUntag(from_index)); 3083 VARIABLE(search_num, MachineRepresentation::kFloat64); 3084 Node* array_length_untagged = SmiUntag(array_length); 3085 3086 Label ident_loop(this, &index_var), heap_num_loop(this, &search_num), 3087 string_loop(this), bigint_loop(this, &index_var), 3088 undef_loop(this, &index_var), not_smi(this), not_heap_num(this), 3089 return_found(this), return_not_found(this); 3090 3091 GotoIfNot(TaggedIsSmi(search_element), ¬_smi); 3092 search_num.Bind(SmiToFloat64(search_element)); 3093 Goto(&heap_num_loop); 3094 3095 BIND(¬_smi); 3096 if (variant == kIncludes) { 3097 GotoIf(IsUndefined(search_element), &undef_loop); 3098 } 3099 Node* map = LoadMap(search_element); 3100 GotoIfNot(IsHeapNumberMap(map), ¬_heap_num); 3101 search_num.Bind(LoadHeapNumberValue(search_element)); 3102 Goto(&heap_num_loop); 3103 3104 BIND(¬_heap_num); 3105 Node* search_type = LoadMapInstanceType(map); 3106 GotoIf(IsStringInstanceType(search_type), &string_loop); 3107 GotoIf(IsBigIntInstanceType(search_type), &bigint_loop); 3108 Goto(&ident_loop); 3109 3110 BIND(&ident_loop); 3111 { 3112 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3113 &return_not_found); 3114 Node* element_k = LoadFixedArrayElement(CAST(elements), index_var.value()); 3115 GotoIf(WordEqual(element_k, search_element), &return_found); 3116 3117 Increment(&index_var); 3118 Goto(&ident_loop); 3119 } 3120 3121 if (variant == kIncludes) { 3122 BIND(&undef_loop); 3123 3124 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3125 &return_not_found); 3126 Node* element_k = LoadFixedArrayElement(CAST(elements), index_var.value()); 3127 GotoIf(IsUndefined(element_k), &return_found); 3128 GotoIf(IsTheHole(element_k), &return_found); 3129 3130 Increment(&index_var); 3131 Goto(&undef_loop); 3132 } 3133 3134 BIND(&heap_num_loop); 3135 { 3136 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var); 3137 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found; 3138 BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); 3139 3140 BIND(¬_nan_loop); 3141 { 3142 Label continue_loop(this), not_smi(this); 3143 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3144 &return_not_found); 3145 Node* element_k = 3146 LoadFixedArrayElement(CAST(elements), index_var.value()); 3147 GotoIfNot(TaggedIsSmi(element_k), ¬_smi); 3148 Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)), 3149 &return_found, &continue_loop); 3150 3151 BIND(¬_smi); 3152 GotoIfNot(IsHeapNumber(element_k), &continue_loop); 3153 Branch(Float64Equal(search_num.value(), LoadHeapNumberValue(element_k)), 3154 &return_found, &continue_loop); 3155 3156 BIND(&continue_loop); 3157 Increment(&index_var); 3158 Goto(¬_nan_loop); 3159 } 3160 3161 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN. 3162 if (variant == kIncludes) { 3163 BIND(&nan_loop); 3164 Label continue_loop(this); 3165 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3166 &return_not_found); 3167 Node* element_k = 3168 LoadFixedArrayElement(CAST(elements), index_var.value()); 3169 GotoIf(TaggedIsSmi(element_k), &continue_loop); 3170 GotoIfNot(IsHeapNumber(CAST(element_k)), &continue_loop); 3171 BranchIfFloat64IsNaN(LoadHeapNumberValue(element_k), &return_found, 3172 &continue_loop); 3173 3174 BIND(&continue_loop); 3175 Increment(&index_var); 3176 Goto(&nan_loop); 3177 } 3178 } 3179 3180 BIND(&string_loop); 3181 { 3182 TNode<String> search_element_string = CAST(search_element); 3183 Label continue_loop(this), next_iteration(this, &index_var), 3184 slow_compare(this), runtime(this, Label::kDeferred); 3185 TNode<IntPtrT> search_length = 3186 LoadStringLengthAsWord(search_element_string); 3187 Goto(&next_iteration); 3188 BIND(&next_iteration); 3189 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3190 &return_not_found); 3191 Node* element_k = LoadFixedArrayElement(CAST(elements), index_var.value()); 3192 GotoIf(TaggedIsSmi(element_k), &continue_loop); 3193 GotoIf(WordEqual(search_element_string, element_k), &return_found); 3194 Node* element_k_type = LoadInstanceType(element_k); 3195 GotoIfNot(IsStringInstanceType(element_k_type), &continue_loop); 3196 Branch(WordEqual(search_length, LoadStringLengthAsWord(element_k)), 3197 &slow_compare, &continue_loop); 3198 3199 BIND(&slow_compare); 3200 StringBuiltinsAssembler string_asm(state()); 3201 string_asm.StringEqual_Core(context, search_element_string, search_type, 3202 element_k, element_k_type, search_length, 3203 &return_found, &continue_loop, &runtime); 3204 BIND(&runtime); 3205 TNode<Object> result = CallRuntime(Runtime::kStringEqual, context, 3206 search_element_string, element_k); 3207 Branch(WordEqual(result, TrueConstant()), &return_found, &continue_loop); 3208 3209 BIND(&continue_loop); 3210 Increment(&index_var); 3211 Goto(&next_iteration); 3212 } 3213 3214 BIND(&bigint_loop); 3215 { 3216 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3217 &return_not_found); 3218 3219 Node* element_k = LoadFixedArrayElement(CAST(elements), index_var.value()); 3220 Label continue_loop(this); 3221 GotoIf(TaggedIsSmi(element_k), &continue_loop); 3222 GotoIfNot(IsBigInt(CAST(element_k)), &continue_loop); 3223 TNode<Object> result = CallRuntime(Runtime::kBigIntEqualToBigInt, context, 3224 search_element, element_k); 3225 Branch(WordEqual(result, TrueConstant()), &return_found, &continue_loop); 3226 3227 BIND(&continue_loop); 3228 Increment(&index_var); 3229 Goto(&bigint_loop); 3230 } 3231 BIND(&return_found); 3232 if (variant == kIncludes) { 3233 Return(TrueConstant()); 3234 } else { 3235 Return(SmiTag(index_var.value())); 3236 } 3237 3238 BIND(&return_not_found); 3239 if (variant == kIncludes) { 3240 Return(FalseConstant()); 3241 } else { 3242 Return(NumberConstant(-1)); 3243 } 3244 } 3245 3246 void ArrayIncludesIndexofAssembler::GeneratePackedDoubles(SearchVariant variant, 3247 Node* elements, 3248 Node* search_element, 3249 Node* array_length, 3250 Node* from_index) { 3251 VARIABLE(index_var, MachineType::PointerRepresentation(), 3252 SmiUntag(from_index)); 3253 Node* array_length_untagged = SmiUntag(array_length); 3254 3255 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var), 3256 hole_loop(this, &index_var), search_notnan(this), return_found(this), 3257 return_not_found(this); 3258 VARIABLE(search_num, MachineRepresentation::kFloat64); 3259 search_num.Bind(Float64Constant(0)); 3260 3261 GotoIfNot(TaggedIsSmi(search_element), &search_notnan); 3262 search_num.Bind(SmiToFloat64(search_element)); 3263 Goto(¬_nan_loop); 3264 3265 BIND(&search_notnan); 3266 GotoIfNot(IsHeapNumber(search_element), &return_not_found); 3267 3268 search_num.Bind(LoadHeapNumberValue(search_element)); 3269 3270 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found; 3271 BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); 3272 3273 BIND(¬_nan_loop); 3274 { 3275 Label continue_loop(this); 3276 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3277 &return_not_found); 3278 Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), 3279 MachineType::Float64()); 3280 Branch(Float64Equal(element_k, search_num.value()), &return_found, 3281 &continue_loop); 3282 BIND(&continue_loop); 3283 Increment(&index_var); 3284 Goto(¬_nan_loop); 3285 } 3286 3287 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN. 3288 if (variant == kIncludes) { 3289 BIND(&nan_loop); 3290 Label continue_loop(this); 3291 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3292 &return_not_found); 3293 Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), 3294 MachineType::Float64()); 3295 BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop); 3296 BIND(&continue_loop); 3297 Increment(&index_var); 3298 Goto(&nan_loop); 3299 } 3300 3301 BIND(&return_found); 3302 if (variant == kIncludes) { 3303 Return(TrueConstant()); 3304 } else { 3305 Return(SmiTag(index_var.value())); 3306 } 3307 3308 BIND(&return_not_found); 3309 if (variant == kIncludes) { 3310 Return(FalseConstant()); 3311 } else { 3312 Return(NumberConstant(-1)); 3313 } 3314 } 3315 3316 void ArrayIncludesIndexofAssembler::GenerateHoleyDoubles(SearchVariant variant, 3317 Node* elements, 3318 Node* search_element, 3319 Node* array_length, 3320 Node* from_index) { 3321 VARIABLE(index_var, MachineType::PointerRepresentation(), 3322 SmiUntag(from_index)); 3323 Node* array_length_untagged = SmiUntag(array_length); 3324 3325 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var), 3326 hole_loop(this, &index_var), search_notnan(this), return_found(this), 3327 return_not_found(this); 3328 VARIABLE(search_num, MachineRepresentation::kFloat64); 3329 search_num.Bind(Float64Constant(0)); 3330 3331 GotoIfNot(TaggedIsSmi(search_element), &search_notnan); 3332 search_num.Bind(SmiToFloat64(search_element)); 3333 Goto(¬_nan_loop); 3334 3335 BIND(&search_notnan); 3336 if (variant == kIncludes) { 3337 GotoIf(IsUndefined(search_element), &hole_loop); 3338 } 3339 GotoIfNot(IsHeapNumber(search_element), &return_not_found); 3340 3341 search_num.Bind(LoadHeapNumberValue(search_element)); 3342 3343 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found; 3344 BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); 3345 3346 BIND(¬_nan_loop); 3347 { 3348 Label continue_loop(this); 3349 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3350 &return_not_found); 3351 3352 // No need for hole checking here; the following Float64Equal will 3353 // return 'not equal' for holes anyway. 3354 Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), 3355 MachineType::Float64()); 3356 3357 Branch(Float64Equal(element_k, search_num.value()), &return_found, 3358 &continue_loop); 3359 BIND(&continue_loop); 3360 Increment(&index_var); 3361 Goto(¬_nan_loop); 3362 } 3363 3364 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN. 3365 if (variant == kIncludes) { 3366 BIND(&nan_loop); 3367 Label continue_loop(this); 3368 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3369 &return_not_found); 3370 3371 // Load double value or continue if it's the hole NaN. 3372 Node* element_k = LoadFixedDoubleArrayElement( 3373 elements, index_var.value(), MachineType::Float64(), 0, 3374 INTPTR_PARAMETERS, &continue_loop); 3375 3376 BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop); 3377 BIND(&continue_loop); 3378 Increment(&index_var); 3379 Goto(&nan_loop); 3380 } 3381 3382 // Array.p.includes treats the hole as undefined. 3383 if (variant == kIncludes) { 3384 BIND(&hole_loop); 3385 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), 3386 &return_not_found); 3387 3388 // Check if the element is a double hole, but don't load it. 3389 LoadFixedDoubleArrayElement(elements, index_var.value(), 3390 MachineType::None(), 0, INTPTR_PARAMETERS, 3391 &return_found); 3392 3393 Increment(&index_var); 3394 Goto(&hole_loop); 3395 } 3396 3397 BIND(&return_found); 3398 if (variant == kIncludes) { 3399 Return(TrueConstant()); 3400 } else { 3401 Return(SmiTag(index_var.value())); 3402 } 3403 3404 BIND(&return_not_found); 3405 if (variant == kIncludes) { 3406 Return(FalseConstant()); 3407 } else { 3408 Return(NumberConstant(-1)); 3409 } 3410 } 3411 3412 TF_BUILTIN(ArrayIncludes, ArrayIncludesIndexofAssembler) { 3413 TNode<IntPtrT> argc = 3414 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 3415 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 3416 3417 Generate(kIncludes, argc, context); 3418 } 3419 3420 TF_BUILTIN(ArrayIncludesSmiOrObject, ArrayIncludesIndexofAssembler) { 3421 Node* context = Parameter(Descriptor::kContext); 3422 Node* elements = Parameter(Descriptor::kElements); 3423 Node* search_element = Parameter(Descriptor::kSearchElement); 3424 Node* array_length = Parameter(Descriptor::kLength); 3425 Node* from_index = Parameter(Descriptor::kFromIndex); 3426 3427 GenerateSmiOrObject(kIncludes, context, elements, search_element, 3428 array_length, from_index); 3429 } 3430 3431 TF_BUILTIN(ArrayIncludesPackedDoubles, ArrayIncludesIndexofAssembler) { 3432 Node* elements = Parameter(Descriptor::kElements); 3433 Node* search_element = Parameter(Descriptor::kSearchElement); 3434 Node* array_length = Parameter(Descriptor::kLength); 3435 Node* from_index = Parameter(Descriptor::kFromIndex); 3436 3437 GeneratePackedDoubles(kIncludes, elements, search_element, array_length, 3438 from_index); 3439 } 3440 3441 TF_BUILTIN(ArrayIncludesHoleyDoubles, ArrayIncludesIndexofAssembler) { 3442 Node* elements = Parameter(Descriptor::kElements); 3443 Node* search_element = Parameter(Descriptor::kSearchElement); 3444 Node* array_length = Parameter(Descriptor::kLength); 3445 Node* from_index = Parameter(Descriptor::kFromIndex); 3446 3447 GenerateHoleyDoubles(kIncludes, elements, search_element, array_length, 3448 from_index); 3449 } 3450 3451 TF_BUILTIN(ArrayIndexOf, ArrayIncludesIndexofAssembler) { 3452 TNode<IntPtrT> argc = 3453 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 3454 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 3455 3456 Generate(kIndexOf, argc, context); 3457 } 3458 3459 TF_BUILTIN(ArrayIndexOfSmiOrObject, ArrayIncludesIndexofAssembler) { 3460 Node* context = Parameter(Descriptor::kContext); 3461 Node* elements = Parameter(Descriptor::kElements); 3462 Node* search_element = Parameter(Descriptor::kSearchElement); 3463 Node* array_length = Parameter(Descriptor::kLength); 3464 Node* from_index = Parameter(Descriptor::kFromIndex); 3465 3466 GenerateSmiOrObject(kIndexOf, context, elements, search_element, array_length, 3467 from_index); 3468 } 3469 3470 TF_BUILTIN(ArrayIndexOfPackedDoubles, ArrayIncludesIndexofAssembler) { 3471 Node* elements = Parameter(Descriptor::kElements); 3472 Node* search_element = Parameter(Descriptor::kSearchElement); 3473 Node* array_length = Parameter(Descriptor::kLength); 3474 Node* from_index = Parameter(Descriptor::kFromIndex); 3475 3476 GeneratePackedDoubles(kIndexOf, elements, search_element, array_length, 3477 from_index); 3478 } 3479 3480 TF_BUILTIN(ArrayIndexOfHoleyDoubles, ArrayIncludesIndexofAssembler) { 3481 Node* elements = Parameter(Descriptor::kElements); 3482 Node* search_element = Parameter(Descriptor::kSearchElement); 3483 Node* array_length = Parameter(Descriptor::kLength); 3484 Node* from_index = Parameter(Descriptor::kFromIndex); 3485 3486 GenerateHoleyDoubles(kIndexOf, elements, search_element, array_length, 3487 from_index); 3488 } 3489 3490 // ES #sec-array.prototype.values 3491 TF_BUILTIN(ArrayPrototypeValues, CodeStubAssembler) { 3492 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 3493 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 3494 Return(CreateArrayIterator(context, ToObject_Inline(context, receiver), 3495 IterationKind::kValues)); 3496 } 3497 3498 // ES #sec-array.prototype.entries 3499 TF_BUILTIN(ArrayPrototypeEntries, CodeStubAssembler) { 3500 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 3501 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 3502 Return(CreateArrayIterator(context, ToObject_Inline(context, receiver), 3503 IterationKind::kEntries)); 3504 } 3505 3506 // ES #sec-array.prototype.keys 3507 TF_BUILTIN(ArrayPrototypeKeys, CodeStubAssembler) { 3508 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 3509 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); 3510 Return(CreateArrayIterator(context, ToObject_Inline(context, receiver), 3511 IterationKind::kKeys)); 3512 } 3513 3514 // ES #sec-%arrayiteratorprototype%.next 3515 TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) { 3516 const char* method_name = "Array Iterator.prototype.next"; 3517 3518 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 3519 Node* iterator = Parameter(Descriptor::kReceiver); 3520 3521 VARIABLE(var_done, MachineRepresentation::kTagged, TrueConstant()); 3522 VARIABLE(var_value, MachineRepresentation::kTagged, UndefinedConstant()); 3523 3524 Label allocate_entry_if_needed(this); 3525 Label allocate_iterator_result(this); 3526 Label if_typedarray(this), if_other(this, Label::kDeferred), if_array(this), 3527 if_generic(this, Label::kDeferred); 3528 Label set_done(this, Label::kDeferred); 3529 3530 // If O does not have all of the internal slots of an Array Iterator Instance 3531 // (22.1.5.3), throw a TypeError exception 3532 ThrowIfNotInstanceType(context, iterator, JS_ARRAY_ITERATOR_TYPE, 3533 method_name); 3534 3535 // Let a be O.[[IteratedObject]]. 3536 TNode<JSReceiver> array = 3537 CAST(LoadObjectField(iterator, JSArrayIterator::kIteratedObjectOffset)); 3538 3539 // Let index be O.[[ArrayIteratorNextIndex]]. 3540 TNode<Number> index = 3541 CAST(LoadObjectField(iterator, JSArrayIterator::kNextIndexOffset)); 3542 CSA_ASSERT(this, IsNumberNonNegativeSafeInteger(index)); 3543 3544 // Dispatch based on the type of the {array}. 3545 TNode<Map> array_map = LoadMap(array); 3546 TNode<Int32T> array_type = LoadMapInstanceType(array_map); 3547 GotoIf(InstanceTypeEqual(array_type, JS_ARRAY_TYPE), &if_array); 3548 Branch(InstanceTypeEqual(array_type, JS_TYPED_ARRAY_TYPE), &if_typedarray, 3549 &if_other); 3550 3551 BIND(&if_array); 3552 { 3553 // If {array} is a JSArray, then the {index} must be in Unsigned32 range. 3554 CSA_ASSERT(this, IsNumberArrayIndex(index)); 3555 3556 // Check that the {index} is within range for the {array}. We handle all 3557 // kinds of JSArray's here, so we do the computation on Uint32. 3558 TNode<Uint32T> index32 = ChangeNumberToUint32(index); 3559 TNode<Uint32T> length32 = 3560 ChangeNumberToUint32(LoadJSArrayLength(CAST(array))); 3561 GotoIfNot(Uint32LessThan(index32, length32), &set_done); 3562 StoreObjectField( 3563 iterator, JSArrayIterator::kNextIndexOffset, 3564 ChangeUint32ToTagged(Unsigned(Int32Add(index32, Int32Constant(1))))); 3565 3566 var_done.Bind(FalseConstant()); 3567 var_value.Bind(index); 3568 3569 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField( 3570 iterator, JSArrayIterator::kKindOffset), 3571 Int32Constant(static_cast<int>(IterationKind::kKeys))), 3572 &allocate_iterator_result); 3573 3574 Label if_hole(this, Label::kDeferred); 3575 TNode<Int32T> elements_kind = LoadMapElementsKind(array_map); 3576 TNode<FixedArrayBase> elements = LoadElements(CAST(array)); 3577 var_value.Bind(LoadFixedArrayBaseElementAsTagged( 3578 elements, Signed(ChangeUint32ToWord(index32)), elements_kind, 3579 &if_generic, &if_hole)); 3580 Goto(&allocate_entry_if_needed); 3581 3582 BIND(&if_hole); 3583 { 3584 GotoIf(IsNoElementsProtectorCellInvalid(), &if_generic); 3585 var_value.Bind(UndefinedConstant()); 3586 Goto(&allocate_entry_if_needed); 3587 } 3588 } 3589 3590 BIND(&if_other); 3591 { 3592 // We cannot enter here with either JSArray's or JSTypedArray's. 3593 CSA_ASSERT(this, Word32BinaryNot(IsJSArray(array))); 3594 CSA_ASSERT(this, Word32BinaryNot(IsJSTypedArray(array))); 3595 3596 // Check that the {index} is within the bounds of the {array}s "length". 3597 TNode<Number> length = CAST( 3598 CallBuiltin(Builtins::kToLength, context, 3599 GetProperty(context, array, factory()->length_string()))); 3600 GotoIfNumberGreaterThanOrEqual(index, length, &set_done); 3601 StoreObjectField(iterator, JSArrayIterator::kNextIndexOffset, 3602 NumberInc(index)); 3603 3604 var_done.Bind(FalseConstant()); 3605 var_value.Bind(index); 3606 3607 Branch(Word32Equal(LoadAndUntagToWord32ObjectField( 3608 iterator, JSArrayIterator::kKindOffset), 3609 Int32Constant(static_cast<int>(IterationKind::kKeys))), 3610 &allocate_iterator_result, &if_generic); 3611 } 3612 3613 BIND(&set_done); 3614 { 3615 // Change the [[ArrayIteratorNextIndex]] such that the {iterator} will 3616 // never produce values anymore, because it will always fail the bounds 3617 // check. Note that this is different from what the specification does, 3618 // which is changing the [[IteratedObject]] to undefined, because leaving 3619 // [[IteratedObject]] alone helps TurboFan to generate better code with 3620 // the inlining in JSCallReducer::ReduceArrayIteratorPrototypeNext(). 3621 // 3622 // The terminal value we chose here depends on the type of the {array}, 3623 // for JSArray's we use kMaxUInt32 so that TurboFan can always use 3624 // Word32 representation for fast-path indices (and this is safe since 3625 // the "length" of JSArray's is limited to Unsigned32 range). For other 3626 // JSReceiver's we have to use kMaxSafeInteger, since the "length" can 3627 // be any arbitrary value in the safe integer range. 3628 // 3629 // Note specifically that JSTypedArray's will never take this path, so 3630 // we don't need to worry about their maximum value. 3631 CSA_ASSERT(this, Word32BinaryNot(IsJSTypedArray(array))); 3632 TNode<Number> max_length = 3633 SelectConstant(IsJSArray(array), NumberConstant(kMaxUInt32), 3634 NumberConstant(kMaxSafeInteger)); 3635 StoreObjectField(iterator, JSArrayIterator::kNextIndexOffset, max_length); 3636 Goto(&allocate_iterator_result); 3637 } 3638 3639 BIND(&if_generic); 3640 { 3641 var_value.Bind(GetProperty(context, array, index)); 3642 Goto(&allocate_entry_if_needed); 3643 } 3644 3645 BIND(&if_typedarray); 3646 { 3647 // If {array} is a JSTypedArray, the {index} must always be a Smi. 3648 CSA_ASSERT(this, TaggedIsSmi(index)); 3649 3650 // Check that the {array}s buffer wasn't neutered. 3651 ThrowIfArrayBufferViewBufferIsDetached(context, CAST(array), method_name); 3652 3653 // If we go outside of the {length}, we don't need to update the 3654 // [[ArrayIteratorNextIndex]] anymore, since a JSTypedArray's 3655 // length cannot change anymore, so this {iterator} will never 3656 // produce values again anyways. 3657 TNode<Smi> length = LoadTypedArrayLength(CAST(array)); 3658 GotoIfNot(SmiBelow(CAST(index), length), &allocate_iterator_result); 3659 StoreObjectFieldNoWriteBarrier(iterator, JSArrayIterator::kNextIndexOffset, 3660 SmiInc(CAST(index))); 3661 3662 var_done.Bind(FalseConstant()); 3663 var_value.Bind(index); 3664 3665 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField( 3666 iterator, JSArrayIterator::kKindOffset), 3667 Int32Constant(static_cast<int>(IterationKind::kKeys))), 3668 &allocate_iterator_result); 3669 3670 TNode<Int32T> elements_kind = LoadMapElementsKind(array_map); 3671 Node* elements = LoadElements(CAST(array)); 3672 Node* base_ptr = 3673 LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset); 3674 Node* external_ptr = 3675 LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset, 3676 MachineType::Pointer()); 3677 TNode<WordT> data_ptr = 3678 IntPtrAdd(BitcastTaggedToWord(base_ptr), external_ptr); 3679 var_value.Bind(LoadFixedTypedArrayElementAsTagged(data_ptr, CAST(index), 3680 elements_kind)); 3681 Goto(&allocate_entry_if_needed); 3682 } 3683 3684 BIND(&allocate_entry_if_needed); 3685 { 3686 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField( 3687 iterator, JSArrayIterator::kKindOffset), 3688 Int32Constant(static_cast<int>(IterationKind::kValues))), 3689 &allocate_iterator_result); 3690 3691 Node* result = 3692 AllocateJSIteratorResultForEntry(context, index, var_value.value()); 3693 Return(result); 3694 } 3695 3696 BIND(&allocate_iterator_result); 3697 { 3698 Node* result = 3699 AllocateJSIteratorResult(context, var_value.value(), var_done.value()); 3700 Return(result); 3701 } 3702 } 3703 3704 namespace { 3705 3706 class ArrayFlattenAssembler : public CodeStubAssembler { 3707 public: 3708 explicit ArrayFlattenAssembler(compiler::CodeAssemblerState* state) 3709 : CodeStubAssembler(state) {} 3710 3711 // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray 3712 Node* FlattenIntoArray(Node* context, Node* target, Node* source, 3713 Node* source_length, Node* start, Node* depth, 3714 Node* mapper_function = nullptr, 3715 Node* this_arg = nullptr) { 3716 CSA_ASSERT(this, IsJSReceiver(target)); 3717 CSA_ASSERT(this, IsJSReceiver(source)); 3718 CSA_ASSERT(this, IsNumberPositive(source_length)); 3719 CSA_ASSERT(this, IsNumberPositive(start)); 3720 CSA_ASSERT(this, IsNumber(depth)); 3721 3722 // 1. Let targetIndex be start. 3723 VARIABLE(var_target_index, MachineRepresentation::kTagged, start); 3724 3725 // 2. Let sourceIndex be 0. 3726 VARIABLE(var_source_index, MachineRepresentation::kTagged, SmiConstant(0)); 3727 3728 // 3. Repeat... 3729 Label loop(this, {&var_target_index, &var_source_index}), done_loop(this); 3730 Goto(&loop); 3731 BIND(&loop); 3732 { 3733 Node* const source_index = var_source_index.value(); 3734 Node* const target_index = var_target_index.value(); 3735 3736 // ...while sourceIndex < sourceLen 3737 GotoIfNumberGreaterThanOrEqual(source_index, source_length, &done_loop); 3738 3739 // a. Let P be ! ToString(sourceIndex). 3740 // b. Let exists be ? HasProperty(source, P). 3741 CSA_ASSERT(this, 3742 SmiGreaterThanOrEqual(CAST(source_index), SmiConstant(0))); 3743 Node* const exists = 3744 HasProperty(context, source, source_index, kHasProperty); 3745 3746 // c. If exists is true, then 3747 Label next(this); 3748 GotoIfNot(IsTrue(exists), &next); 3749 { 3750 // i. Let element be ? Get(source, P). 3751 Node* element = GetProperty(context, source, source_index); 3752 3753 // ii. If mapperFunction is present, then 3754 if (mapper_function != nullptr) { 3755 CSA_ASSERT(this, Word32Or(IsUndefined(mapper_function), 3756 IsCallable(mapper_function))); 3757 DCHECK_NOT_NULL(this_arg); 3758 3759 // 1. Set element to ? Call(mapperFunction, thisArg , element, 3760 // sourceIndex, source ). 3761 element = 3762 CallJS(CodeFactory::Call(isolate()), context, mapper_function, 3763 this_arg, element, source_index, source); 3764 } 3765 3766 // iii. Let shouldFlatten be false. 3767 Label if_flatten_array(this), if_flatten_proxy(this, Label::kDeferred), 3768 if_noflatten(this); 3769 // iv. If depth > 0, then 3770 GotoIfNumberGreaterThanOrEqual(SmiConstant(0), depth, &if_noflatten); 3771 // 1. Set shouldFlatten to ? IsArray(element). 3772 GotoIf(TaggedIsSmi(element), &if_noflatten); 3773 GotoIf(IsJSArray(element), &if_flatten_array); 3774 GotoIfNot(IsJSProxy(element), &if_noflatten); 3775 Branch(IsTrue(CallRuntime(Runtime::kArrayIsArray, context, element)), 3776 &if_flatten_proxy, &if_noflatten); 3777 3778 BIND(&if_flatten_array); 3779 { 3780 CSA_ASSERT(this, IsJSArray(element)); 3781 3782 // 1. Let elementLen be ? ToLength(? Get(element, "length")). 3783 Node* const element_length = 3784 LoadObjectField(element, JSArray::kLengthOffset); 3785 3786 // 2. Set targetIndex to ? FlattenIntoArray(target, element, 3787 // elementLen, targetIndex, 3788 // depth - 1). 3789 var_target_index.Bind( 3790 CallBuiltin(Builtins::kFlattenIntoArray, context, target, element, 3791 element_length, target_index, NumberDec(depth))); 3792 Goto(&next); 3793 } 3794 3795 BIND(&if_flatten_proxy); 3796 { 3797 CSA_ASSERT(this, IsJSProxy(element)); 3798 3799 // 1. Let elementLen be ? ToLength(? Get(element, "length")). 3800 Node* const element_length = ToLength_Inline( 3801 context, GetProperty(context, element, LengthStringConstant())); 3802 3803 // 2. Set targetIndex to ? FlattenIntoArray(target, element, 3804 // elementLen, targetIndex, 3805 // depth - 1). 3806 var_target_index.Bind( 3807 CallBuiltin(Builtins::kFlattenIntoArray, context, target, element, 3808 element_length, target_index, NumberDec(depth))); 3809 Goto(&next); 3810 } 3811 3812 BIND(&if_noflatten); 3813 { 3814 // 1. If targetIndex >= 2^53-1, throw a TypeError exception. 3815 Label throw_error(this, Label::kDeferred); 3816 GotoIfNumberGreaterThanOrEqual( 3817 target_index, NumberConstant(kMaxSafeInteger), &throw_error); 3818 3819 // 2. Perform ? CreateDataPropertyOrThrow(target, 3820 // ! ToString(targetIndex), 3821 // element). 3822 CallRuntime(Runtime::kCreateDataProperty, context, target, 3823 target_index, element); 3824 3825 // 3. Increase targetIndex by 1. 3826 var_target_index.Bind(NumberInc(target_index)); 3827 Goto(&next); 3828 3829 BIND(&throw_error); 3830 ThrowTypeError(context, MessageTemplate::kFlattenPastSafeLength, 3831 source_length, target_index); 3832 } 3833 } 3834 BIND(&next); 3835 3836 // d. Increase sourceIndex by 1. 3837 var_source_index.Bind(NumberInc(source_index)); 3838 Goto(&loop); 3839 } 3840 3841 BIND(&done_loop); 3842 return var_target_index.value(); 3843 } 3844 }; 3845 3846 } // namespace 3847 3848 // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray 3849 TF_BUILTIN(FlattenIntoArray, ArrayFlattenAssembler) { 3850 Node* const context = Parameter(Descriptor::kContext); 3851 Node* const target = Parameter(Descriptor::kTarget); 3852 Node* const source = Parameter(Descriptor::kSource); 3853 Node* const source_length = Parameter(Descriptor::kSourceLength); 3854 Node* const start = Parameter(Descriptor::kStart); 3855 Node* const depth = Parameter(Descriptor::kDepth); 3856 3857 Return( 3858 FlattenIntoArray(context, target, source, source_length, start, depth)); 3859 } 3860 3861 // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray 3862 TF_BUILTIN(FlatMapIntoArray, ArrayFlattenAssembler) { 3863 Node* const context = Parameter(Descriptor::kContext); 3864 Node* const target = Parameter(Descriptor::kTarget); 3865 Node* const source = Parameter(Descriptor::kSource); 3866 Node* const source_length = Parameter(Descriptor::kSourceLength); 3867 Node* const start = Parameter(Descriptor::kStart); 3868 Node* const depth = Parameter(Descriptor::kDepth); 3869 Node* const mapper_function = Parameter(Descriptor::kMapperFunction); 3870 Node* const this_arg = Parameter(Descriptor::kThisArg); 3871 3872 Return(FlattenIntoArray(context, target, source, source_length, start, depth, 3873 mapper_function, this_arg)); 3874 } 3875 3876 // https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flat 3877 TF_BUILTIN(ArrayPrototypeFlat, CodeStubAssembler) { 3878 Node* const argc = 3879 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 3880 CodeStubArguments args(this, argc); 3881 Node* const context = Parameter(Descriptor::kContext); 3882 Node* const receiver = args.GetReceiver(); 3883 Node* const depth = args.GetOptionalArgumentValue(0); 3884 3885 // 1. Let O be ? ToObject(this value). 3886 Node* const o = ToObject_Inline(CAST(context), CAST(receiver)); 3887 3888 // 2. Let sourceLen be ? ToLength(? Get(O, "length")). 3889 Node* const source_length = 3890 ToLength_Inline(context, GetProperty(context, o, LengthStringConstant())); 3891 3892 // 3. Let depthNum be 1. 3893 VARIABLE(var_depth_num, MachineRepresentation::kTagged, SmiConstant(1)); 3894 3895 // 4. If depth is not undefined, then 3896 Label done(this); 3897 GotoIf(IsUndefined(depth), &done); 3898 { 3899 // a. Set depthNum to ? ToInteger(depth). 3900 var_depth_num.Bind(ToInteger_Inline(context, depth)); 3901 Goto(&done); 3902 } 3903 BIND(&done); 3904 3905 // 5. Let A be ? ArraySpeciesCreate(O, 0). 3906 Node* const constructor = 3907 CallRuntime(Runtime::kArraySpeciesConstructor, context, o); 3908 Node* const a = ConstructJS(CodeFactory::Construct(isolate()), context, 3909 constructor, SmiConstant(0)); 3910 3911 // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum). 3912 CallBuiltin(Builtins::kFlattenIntoArray, context, a, o, source_length, 3913 SmiConstant(0), var_depth_num.value()); 3914 3915 // 7. Return A. 3916 args.PopAndReturn(a); 3917 } 3918 3919 // https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap 3920 TF_BUILTIN(ArrayPrototypeFlatMap, CodeStubAssembler) { 3921 Node* const argc = 3922 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); 3923 CodeStubArguments args(this, argc); 3924 Node* const context = Parameter(Descriptor::kContext); 3925 Node* const receiver = args.GetReceiver(); 3926 Node* const mapper_function = args.GetOptionalArgumentValue(0); 3927 3928 // 1. Let O be ? ToObject(this value). 3929 Node* const o = ToObject_Inline(CAST(context), CAST(receiver)); 3930 3931 // 2. Let sourceLen be ? ToLength(? Get(O, "length")). 3932 Node* const source_length = 3933 ToLength_Inline(context, GetProperty(context, o, LengthStringConstant())); 3934 3935 // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception. 3936 Label if_not_callable(this, Label::kDeferred); 3937 GotoIf(TaggedIsSmi(mapper_function), &if_not_callable); 3938 GotoIfNot(IsCallable(mapper_function), &if_not_callable); 3939 3940 // 4. If thisArg is present, let T be thisArg; else let T be undefined. 3941 Node* const t = args.GetOptionalArgumentValue(1); 3942 3943 // 5. Let A be ? ArraySpeciesCreate(O, 0). 3944 Node* const constructor = 3945 CallRuntime(Runtime::kArraySpeciesConstructor, context, o); 3946 Node* const a = ConstructJS(CodeFactory::Construct(isolate()), context, 3947 constructor, SmiConstant(0)); 3948 3949 // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T). 3950 CallBuiltin(Builtins::kFlatMapIntoArray, context, a, o, source_length, 3951 SmiConstant(0), SmiConstant(1), mapper_function, t); 3952 3953 // 7. Return A. 3954 args.PopAndReturn(a); 3955 3956 BIND(&if_not_callable); 3957 { ThrowTypeError(context, MessageTemplate::kMapperFunctionNonCallable); } 3958 } 3959 3960 TF_BUILTIN(ArrayConstructor, ArrayBuiltinsAssembler) { 3961 // This is a trampoline to ArrayConstructorImpl which just adds 3962 // allocation_site parameter value and sets new_target if necessary. 3963 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 3964 TNode<JSFunction> function = CAST(Parameter(Descriptor::kTarget)); 3965 TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget)); 3966 TNode<Int32T> argc = 3967 UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount)); 3968 3969 // If new_target is undefined, then this is the 'Call' case, so set new_target 3970 // to function. 3971 new_target = 3972 SelectConstant<Object>(IsUndefined(new_target), function, new_target); 3973 3974 // Run the native code for the Array function called as a normal function. 3975 TNode<Object> no_allocation_site = UndefinedConstant(); 3976 TailCallBuiltin(Builtins::kArrayConstructorImpl, context, function, 3977 new_target, argc, no_allocation_site); 3978 } 3979 3980 void ArrayBuiltinsAssembler::TailCallArrayConstructorStub( 3981 const Callable& callable, TNode<Context> context, TNode<JSFunction> target, 3982 TNode<HeapObject> allocation_site_or_undefined, TNode<Int32T> argc) { 3983 TNode<Code> code = HeapConstant(callable.code()); 3984 3985 // We are going to call here ArrayNoArgumentsConstructor or 3986 // ArraySingleArgumentsConstructor which in addition to the register arguments 3987 // also expect some number of arguments on the expression stack. 3988 // Since 3989 // 1) incoming JS arguments are still on the stack, 3990 // 2) the ArrayNoArgumentsConstructor, ArraySingleArgumentsConstructor and 3991 // ArrayNArgumentsConstructor are defined so that the register arguments 3992 // are passed on the same registers, 3993 // in order to be able to generate a tail call to those builtins we do the 3994 // following trick here: we tail call to the constructor builtin using 3995 // ArrayNArgumentsConstructorDescriptor, so the tail call instruction 3996 // pops the current frame but leaves all the incoming JS arguments on the 3997 // expression stack so that the target builtin can still find them where it 3998 // expects. 3999 TailCallStub(ArrayNArgumentsConstructorDescriptor{}, code, context, target, 4000 allocation_site_or_undefined, argc); 4001 } 4002 4003 void ArrayBuiltinsAssembler::CreateArrayDispatchNoArgument( 4004 TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc, 4005 AllocationSiteOverrideMode mode, TNode<AllocationSite> allocation_site) { 4006 if (mode == DISABLE_ALLOCATION_SITES) { 4007 Callable callable = CodeFactory::ArrayNoArgumentConstructor( 4008 isolate(), GetInitialFastElementsKind(), mode); 4009 4010 TailCallArrayConstructorStub(callable, context, target, UndefinedConstant(), 4011 argc); 4012 } else { 4013 DCHECK_EQ(mode, DONT_OVERRIDE); 4014 TNode<Int32T> elements_kind = LoadElementsKind(allocation_site); 4015 4016 // TODO(ishell): Compute the builtin index dynamically instead of 4017 // iterating over all expected elements kinds. 4018 int last_index = 4019 GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); 4020 for (int i = 0; i <= last_index; ++i) { 4021 Label next(this); 4022 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i); 4023 GotoIfNot(Word32Equal(elements_kind, Int32Constant(kind)), &next); 4024 4025 Callable callable = 4026 CodeFactory::ArrayNoArgumentConstructor(isolate(), kind, mode); 4027 4028 TailCallArrayConstructorStub(callable, context, target, allocation_site, 4029 argc); 4030 4031 BIND(&next); 4032 } 4033 4034 // If we reached this point there is a problem. 4035 Abort(AbortReason::kUnexpectedElementsKindInArrayConstructor); 4036 } 4037 } 4038 4039 void ArrayBuiltinsAssembler::CreateArrayDispatchSingleArgument( 4040 TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc, 4041 AllocationSiteOverrideMode mode, TNode<AllocationSite> allocation_site) { 4042 if (mode == DISABLE_ALLOCATION_SITES) { 4043 ElementsKind initial = GetInitialFastElementsKind(); 4044 ElementsKind holey_initial = GetHoleyElementsKind(initial); 4045 Callable callable = CodeFactory::ArraySingleArgumentConstructor( 4046 isolate(), holey_initial, mode); 4047 4048 TailCallArrayConstructorStub(callable, context, target, UndefinedConstant(), 4049 argc); 4050 } else { 4051 DCHECK_EQ(mode, DONT_OVERRIDE); 4052 TNode<Smi> transition_info = LoadTransitionInfo(allocation_site); 4053 4054 // Least significant bit in fast array elements kind means holeyness. 4055 STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0); 4056 STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1); 4057 STATIC_ASSERT(PACKED_ELEMENTS == 2); 4058 STATIC_ASSERT(HOLEY_ELEMENTS == 3); 4059 STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4); 4060 STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5); 4061 4062 Label normal_sequence(this); 4063 TVARIABLE(Int32T, var_elements_kind, 4064 Signed(DecodeWord32<AllocationSite::ElementsKindBits>( 4065 SmiToInt32(transition_info)))); 4066 // Is the low bit set? If so, we are holey and that is good. 4067 int fast_elements_kind_holey_mask = 4068 AllocationSite::ElementsKindBits::encode(static_cast<ElementsKind>(1)); 4069 GotoIf(IsSetSmi(transition_info, fast_elements_kind_holey_mask), 4070 &normal_sequence); 4071 { 4072 // Make elements kind holey and update elements kind in the type info. 4073 var_elements_kind = 4074 Signed(Word32Or(var_elements_kind.value(), Int32Constant(1))); 4075 StoreObjectFieldNoWriteBarrier( 4076 allocation_site, AllocationSite::kTransitionInfoOrBoilerplateOffset, 4077 SmiOr(transition_info, SmiConstant(fast_elements_kind_holey_mask))); 4078 Goto(&normal_sequence); 4079 } 4080 BIND(&normal_sequence); 4081 4082 // TODO(ishell): Compute the builtin index dynamically instead of 4083 // iterating over all expected elements kinds. 4084 // TODO(ishell): Given that the code above ensures that the elements kind 4085 // is holey we can skip checking with non-holey elements kinds. 4086 int last_index = 4087 GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); 4088 for (int i = 0; i <= last_index; ++i) { 4089 Label next(this); 4090 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i); 4091 GotoIfNot(Word32Equal(var_elements_kind.value(), Int32Constant(kind)), 4092 &next); 4093 4094 Callable callable = 4095 CodeFactory::ArraySingleArgumentConstructor(isolate(), kind, mode); 4096 4097 TailCallArrayConstructorStub(callable, context, target, allocation_site, 4098 argc); 4099 4100 BIND(&next); 4101 } 4102 4103 // If we reached this point there is a problem. 4104 Abort(AbortReason::kUnexpectedElementsKindInArrayConstructor); 4105 } 4106 } 4107 4108 void ArrayBuiltinsAssembler::GenerateDispatchToArrayStub( 4109 TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc, 4110 AllocationSiteOverrideMode mode, TNode<AllocationSite> allocation_site) { 4111 Label check_one_case(this), fallthrough(this); 4112 GotoIfNot(Word32Equal(argc, Int32Constant(0)), &check_one_case); 4113 CreateArrayDispatchNoArgument(context, target, argc, mode, allocation_site); 4114 4115 BIND(&check_one_case); 4116 GotoIfNot(Word32Equal(argc, Int32Constant(1)), &fallthrough); 4117 CreateArrayDispatchSingleArgument(context, target, argc, mode, 4118 allocation_site); 4119 4120 BIND(&fallthrough); 4121 } 4122 4123 TF_BUILTIN(ArrayConstructorImpl, ArrayBuiltinsAssembler) { 4124 TNode<JSFunction> target = CAST(Parameter(Descriptor::kTarget)); 4125 TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget)); 4126 TNode<Int32T> argc = 4127 UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount)); 4128 TNode<HeapObject> maybe_allocation_site = 4129 CAST(Parameter(Descriptor::kAllocationSite)); 4130 4131 // Initial map for the builtin Array functions should be Map. 4132 CSA_ASSERT(this, IsMap(CAST(LoadObjectField( 4133 target, JSFunction::kPrototypeOrInitialMapOffset)))); 4134 4135 // We should either have undefined or a valid AllocationSite 4136 CSA_ASSERT(this, Word32Or(IsUndefined(maybe_allocation_site), 4137 IsAllocationSite(maybe_allocation_site))); 4138 4139 // "Enter" the context of the Array function. 4140 TNode<Context> context = 4141 CAST(LoadObjectField(target, JSFunction::kContextOffset)); 4142 4143 Label runtime(this, Label::kDeferred); 4144 GotoIf(WordNotEqual(target, new_target), &runtime); 4145 4146 Label no_info(this); 4147 // If the feedback vector is the undefined value call an array constructor 4148 // that doesn't use AllocationSites. 4149 GotoIf(IsUndefined(maybe_allocation_site), &no_info); 4150 4151 GenerateDispatchToArrayStub(context, target, argc, DONT_OVERRIDE, 4152 CAST(maybe_allocation_site)); 4153 Goto(&runtime); 4154 4155 BIND(&no_info); 4156 GenerateDispatchToArrayStub(context, target, argc, DISABLE_ALLOCATION_SITES); 4157 Goto(&runtime); 4158 4159 BIND(&runtime); 4160 GenerateArrayNArgumentsConstructor(context, target, new_target, argc, 4161 maybe_allocation_site); 4162 } 4163 4164 void ArrayBuiltinsAssembler::GenerateConstructor( 4165 Node* context, Node* array_function, Node* array_map, Node* array_size, 4166 Node* allocation_site, ElementsKind elements_kind, 4167 AllocationSiteMode mode) { 4168 Label ok(this); 4169 Label smi_size(this); 4170 Label small_smi_size(this); 4171 Label call_runtime(this, Label::kDeferred); 4172 4173 Branch(TaggedIsSmi(array_size), &smi_size, &call_runtime); 4174 4175 BIND(&smi_size); 4176 4177 if (IsFastPackedElementsKind(elements_kind)) { 4178 Label abort(this, Label::kDeferred); 4179 Branch(SmiEqual(CAST(array_size), SmiConstant(0)), &small_smi_size, &abort); 4180 4181 BIND(&abort); 4182 Node* reason = SmiConstant(AbortReason::kAllocatingNonEmptyPackedArray); 4183 TailCallRuntime(Runtime::kAbort, context, reason); 4184 } else { 4185 int element_size = 4186 IsDoubleElementsKind(elements_kind) ? kDoubleSize : kPointerSize; 4187 int max_fast_elements = 4188 (kMaxRegularHeapObjectSize - FixedArray::kHeaderSize - JSArray::kSize - 4189 AllocationMemento::kSize) / 4190 element_size; 4191 Branch(SmiAboveOrEqual(CAST(array_size), SmiConstant(max_fast_elements)), 4192 &call_runtime, &small_smi_size); 4193 } 4194 4195 BIND(&small_smi_size); 4196 { 4197 Node* array = AllocateJSArray( 4198 elements_kind, array_map, array_size, array_size, 4199 mode == DONT_TRACK_ALLOCATION_SITE ? nullptr : allocation_site, 4200 CodeStubAssembler::SMI_PARAMETERS); 4201 Return(array); 4202 } 4203 4204 BIND(&call_runtime); 4205 { 4206 TailCallRuntime(Runtime::kNewArray, context, array_function, array_size, 4207 array_function, allocation_site); 4208 } 4209 } 4210 4211 void ArrayBuiltinsAssembler::GenerateArrayNoArgumentConstructor( 4212 ElementsKind kind, AllocationSiteOverrideMode mode) { 4213 typedef ArrayNoArgumentConstructorDescriptor Descriptor; 4214 Node* native_context = LoadObjectField(Parameter(Descriptor::kFunction), 4215 JSFunction::kContextOffset); 4216 bool track_allocation_site = 4217 AllocationSite::ShouldTrack(kind) && mode != DISABLE_ALLOCATION_SITES; 4218 Node* allocation_site = 4219 track_allocation_site ? Parameter(Descriptor::kAllocationSite) : nullptr; 4220 Node* array_map = LoadJSArrayElementsMap(kind, native_context); 4221 Node* array = AllocateJSArray( 4222 kind, array_map, IntPtrConstant(JSArray::kPreallocatedArrayElements), 4223 SmiConstant(0), allocation_site); 4224 Return(array); 4225 } 4226 4227 void ArrayBuiltinsAssembler::GenerateArraySingleArgumentConstructor( 4228 ElementsKind kind, AllocationSiteOverrideMode mode) { 4229 typedef ArraySingleArgumentConstructorDescriptor Descriptor; 4230 Node* context = Parameter(Descriptor::kContext); 4231 Node* function = Parameter(Descriptor::kFunction); 4232 Node* native_context = LoadObjectField(function, JSFunction::kContextOffset); 4233 Node* array_map = LoadJSArrayElementsMap(kind, native_context); 4234 4235 AllocationSiteMode allocation_site_mode = DONT_TRACK_ALLOCATION_SITE; 4236 if (mode == DONT_OVERRIDE) { 4237 allocation_site_mode = AllocationSite::ShouldTrack(kind) 4238 ? TRACK_ALLOCATION_SITE 4239 : DONT_TRACK_ALLOCATION_SITE; 4240 } 4241 4242 Node* array_size = Parameter(Descriptor::kArraySizeSmiParameter); 4243 Node* allocation_site = Parameter(Descriptor::kAllocationSite); 4244 4245 GenerateConstructor(context, function, array_map, array_size, allocation_site, 4246 kind, allocation_site_mode); 4247 } 4248 4249 void ArrayBuiltinsAssembler::GenerateArrayNArgumentsConstructor( 4250 TNode<Context> context, TNode<JSFunction> target, TNode<Object> new_target, 4251 TNode<Int32T> argc, TNode<HeapObject> maybe_allocation_site) { 4252 // Replace incoming JS receiver argument with the target. 4253 // TODO(ishell): Avoid replacing the target on the stack and just add it 4254 // as another additional parameter for Runtime::kNewArray. 4255 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc)); 4256 args.SetReceiver(target); 4257 4258 // Adjust arguments count for the runtime call: +1 for implicit receiver 4259 // and +2 for new_target and maybe_allocation_site. 4260 argc = Int32Add(argc, Int32Constant(3)); 4261 TailCallRuntime(Runtime::kNewArray, argc, context, new_target, 4262 maybe_allocation_site); 4263 } 4264 4265 TF_BUILTIN(ArrayNArgumentsConstructor, ArrayBuiltinsAssembler) { 4266 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 4267 TNode<JSFunction> target = CAST(Parameter(Descriptor::kFunction)); 4268 TNode<Int32T> argc = 4269 UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount)); 4270 TNode<HeapObject> maybe_allocation_site = 4271 CAST(Parameter(Descriptor::kAllocationSite)); 4272 4273 GenerateArrayNArgumentsConstructor(context, target, target, argc, 4274 maybe_allocation_site); 4275 } 4276 4277 void ArrayBuiltinsAssembler::GenerateInternalArrayNoArgumentConstructor( 4278 ElementsKind kind) { 4279 typedef ArrayNoArgumentConstructorDescriptor Descriptor; 4280 Node* array_map = LoadObjectField(Parameter(Descriptor::kFunction), 4281 JSFunction::kPrototypeOrInitialMapOffset); 4282 Node* array = AllocateJSArray( 4283 kind, array_map, IntPtrConstant(JSArray::kPreallocatedArrayElements), 4284 SmiConstant(0)); 4285 Return(array); 4286 } 4287 4288 void ArrayBuiltinsAssembler::GenerateInternalArraySingleArgumentConstructor( 4289 ElementsKind kind) { 4290 typedef ArraySingleArgumentConstructorDescriptor Descriptor; 4291 Node* context = Parameter(Descriptor::kContext); 4292 Node* function = Parameter(Descriptor::kFunction); 4293 Node* array_map = 4294 LoadObjectField(function, JSFunction::kPrototypeOrInitialMapOffset); 4295 Node* array_size = Parameter(Descriptor::kArraySizeSmiParameter); 4296 Node* allocation_site = UndefinedConstant(); 4297 4298 GenerateConstructor(context, function, array_map, array_size, allocation_site, 4299 kind, DONT_TRACK_ALLOCATION_SITE); 4300 } 4301 4302 #define GENERATE_ARRAY_CTOR(name, kind_camel, kind_caps, mode_camel, \ 4303 mode_caps) \ 4304 TF_BUILTIN(Array##name##Constructor_##kind_camel##_##mode_camel, \ 4305 ArrayBuiltinsAssembler) { \ 4306 GenerateArray##name##Constructor(kind_caps, mode_caps); \ 4307 } 4308 4309 // The ArrayNoArgumentConstructor builtin family. 4310 GENERATE_ARRAY_CTOR(NoArgument, PackedSmi, PACKED_SMI_ELEMENTS, DontOverride, 4311 DONT_OVERRIDE); 4312 GENERATE_ARRAY_CTOR(NoArgument, HoleySmi, HOLEY_SMI_ELEMENTS, DontOverride, 4313 DONT_OVERRIDE); 4314 GENERATE_ARRAY_CTOR(NoArgument, PackedSmi, PACKED_SMI_ELEMENTS, 4315 DisableAllocationSites, DISABLE_ALLOCATION_SITES); 4316 GENERATE_ARRAY_CTOR(NoArgument, HoleySmi, HOLEY_SMI_ELEMENTS, 4317 DisableAllocationSites, DISABLE_ALLOCATION_SITES); 4318 GENERATE_ARRAY_CTOR(NoArgument, Packed, PACKED_ELEMENTS, DisableAllocationSites, 4319 DISABLE_ALLOCATION_SITES); 4320 GENERATE_ARRAY_CTOR(NoArgument, Holey, HOLEY_ELEMENTS, DisableAllocationSites, 4321 DISABLE_ALLOCATION_SITES); 4322 GENERATE_ARRAY_CTOR(NoArgument, PackedDouble, PACKED_DOUBLE_ELEMENTS, 4323 DisableAllocationSites, DISABLE_ALLOCATION_SITES); 4324 GENERATE_ARRAY_CTOR(NoArgument, HoleyDouble, HOLEY_DOUBLE_ELEMENTS, 4325 DisableAllocationSites, DISABLE_ALLOCATION_SITES); 4326 4327 // The ArraySingleArgumentConstructor builtin family. 4328 GENERATE_ARRAY_CTOR(SingleArgument, PackedSmi, PACKED_SMI_ELEMENTS, 4329 DontOverride, DONT_OVERRIDE); 4330 GENERATE_ARRAY_CTOR(SingleArgument, HoleySmi, HOLEY_SMI_ELEMENTS, DontOverride, 4331 DONT_OVERRIDE); 4332 GENERATE_ARRAY_CTOR(SingleArgument, PackedSmi, PACKED_SMI_ELEMENTS, 4333 DisableAllocationSites, DISABLE_ALLOCATION_SITES); 4334 GENERATE_ARRAY_CTOR(SingleArgument, HoleySmi, HOLEY_SMI_ELEMENTS, 4335 DisableAllocationSites, DISABLE_ALLOCATION_SITES); 4336 GENERATE_ARRAY_CTOR(SingleArgument, Packed, PACKED_ELEMENTS, 4337 DisableAllocationSites, DISABLE_ALLOCATION_SITES); 4338 GENERATE_ARRAY_CTOR(SingleArgument, Holey, HOLEY_ELEMENTS, 4339 DisableAllocationSites, DISABLE_ALLOCATION_SITES); 4340 GENERATE_ARRAY_CTOR(SingleArgument, PackedDouble, PACKED_DOUBLE_ELEMENTS, 4341 DisableAllocationSites, DISABLE_ALLOCATION_SITES); 4342 GENERATE_ARRAY_CTOR(SingleArgument, HoleyDouble, HOLEY_DOUBLE_ELEMENTS, 4343 DisableAllocationSites, DISABLE_ALLOCATION_SITES); 4344 4345 #undef GENERATE_ARRAY_CTOR 4346 4347 #define GENERATE_INTERNAL_ARRAY_CTOR(name, kind_camel, kind_caps) \ 4348 TF_BUILTIN(InternalArray##name##Constructor_##kind_camel, \ 4349 ArrayBuiltinsAssembler) { \ 4350 GenerateInternalArray##name##Constructor(kind_caps); \ 4351 } 4352 4353 GENERATE_INTERNAL_ARRAY_CTOR(NoArgument, Packed, PACKED_ELEMENTS); 4354 GENERATE_INTERNAL_ARRAY_CTOR(NoArgument, Holey, HOLEY_ELEMENTS); 4355 GENERATE_INTERNAL_ARRAY_CTOR(SingleArgument, Packed, PACKED_ELEMENTS); 4356 GENERATE_INTERNAL_ARRAY_CTOR(SingleArgument, Holey, HOLEY_ELEMENTS); 4357 4358 #undef GENERATE_INTERNAL_ARRAY_CTOR 4359 4360 } // namespace internal 4361 } // namespace v8 4362