| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/builtins/builtins.h" | 5 #include "src/builtins/builtins.h" |
| 6 #include "src/builtins/builtins-utils.h" | 6 #include "src/builtins/builtins-utils.h" |
| 7 | 7 |
| 8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
| 9 #include "src/code-stub-assembler.h" | 9 #include "src/code-stub-assembler.h" |
| 10 #include "src/contexts.h" | 10 #include "src/contexts.h" |
| (...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 181 | 181 |
| 182 if (JSArray::HasReadOnlyLength(array)) { | 182 if (JSArray::HasReadOnlyLength(array)) { |
| 183 return CallJsIntrinsic(isolate, isolate->array_push(), args); | 183 return CallJsIntrinsic(isolate, isolate->array_push(), args); |
| 184 } | 184 } |
| 185 | 185 |
| 186 ElementsAccessor* accessor = array->GetElementsAccessor(); | 186 ElementsAccessor* accessor = array->GetElementsAccessor(); |
| 187 int new_length = accessor->Push(array, &args, to_add); | 187 int new_length = accessor->Push(array, &args, to_add); |
| 188 return Smi::FromInt(new_length); | 188 return Smi::FromInt(new_length); |
| 189 } | 189 } |
| 190 | 190 |
| 191 TF_BUILTIN(FastArrayPush, CodeStubAssembler) { | |
| 192 Variable arg_index(this, MachineType::PointerRepresentation()); | |
| 193 Label default_label(this, &arg_index); | |
| 194 Label smi_transition(this); | |
| 195 Label object_push_pre(this); | |
| 196 Label object_push(this, &arg_index); | |
| 197 Label double_push(this, &arg_index); | |
| 198 Label double_transition(this); | |
| 199 Label runtime(this, Label::kDeferred); | |
| 200 | |
| 201 Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); | |
| 202 Node* context = Parameter(BuiltinDescriptor::kContext); | |
| 203 Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); | |
| 204 | |
| 205 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc)); | |
| 206 Node* receiver = args.GetReceiver(); | |
| 207 Node* kind = nullptr; | |
| 208 | |
| 209 Label fast(this); | |
| 210 BranchIfFastJSArray(receiver, context, FastJSArrayAccessMode::ANY_ACCESS, | |
| 211 &fast, &runtime); | |
| 212 | |
| 213 Bind(&fast); | |
| 214 { | |
| 215 // Disallow pushing onto prototypes. It might be the JSArray prototype. | |
| 216 // Disallow pushing onto non-extensible objects. | |
| 217 Comment("Disallow pushing onto prototypes"); | |
| 218 Node* map = LoadMap(receiver); | |
| 219 Node* bit_field2 = LoadMapBitField2(map); | |
| 220 int mask = static_cast<int>(Map::IsPrototypeMapBits::kMask) | | |
| 221 (1 << Map::kIsExtensible); | |
| 222 Node* test = Word32And(bit_field2, Int32Constant(mask)); | |
| 223 GotoIf(Word32NotEqual(test, Int32Constant(1 << Map::kIsExtensible)), | |
| 224 &runtime); | |
| 225 | |
| 226 // Disallow pushing onto arrays in dictionary named property mode. We need | |
| 227 // to figure out whether the length property is still writable. | |
| 228 Comment("Disallow pushing onto arrays in dictionary named property mode"); | |
| 229 GotoIf(IsDictionaryMap(map), &runtime); | |
| 230 | |
| 231 // Check whether the length property is writable. The length property is the | |
| 232 // only default named property on arrays. It's nonconfigurable, hence is | |
| 233 // guaranteed to stay the first property. | |
| 234 Node* descriptors = LoadMapDescriptors(map); | |
| 235 Node* details = | |
| 236 LoadFixedArrayElement(descriptors, DescriptorArray::ToDetailsIndex(0)); | |
| 237 GotoIf(IsSetSmi(details, PropertyDetails::kAttributesReadOnlyMask), | |
| 238 &runtime); | |
| 239 | |
| 240 arg_index.Bind(IntPtrConstant(0)); | |
| 241 kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); | |
| 242 | |
| 243 GotoIf(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_SMI_ELEMENTS)), | |
| 244 &object_push_pre); | |
| 245 | |
| 246 Node* new_length = BuildAppendJSArray(FAST_SMI_ELEMENTS, context, receiver, | |
| 247 args, arg_index, &smi_transition); | |
| 248 args.PopAndReturn(new_length); | |
| 249 } | |
| 250 | |
| 251 // If the argument is not a smi, then use a heavyweight SetProperty to | |
| 252 // transition the array for only the single next element. If the argument is | |
| 253 // a smi, the failure is due to some other reason and we should fall back on | |
| 254 // the most generic implementation for the rest of the array. | |
| 255 Bind(&smi_transition); | |
| 256 { | |
| 257 Node* arg = args.AtIndex(arg_index.value()); | |
| 258 GotoIf(TaggedIsSmi(arg), &default_label); | |
| 259 Node* length = LoadJSArrayLength(receiver); | |
| 260 // TODO(danno): Use the KeyedStoreGeneric stub here when possible, | |
| 261 // calling into the runtime to do the elements transition is overkill. | |
| 262 CallRuntime(Runtime::kSetProperty, context, receiver, length, arg, | |
| 263 SmiConstant(STRICT)); | |
| 264 Increment(arg_index); | |
| 265 // The runtime SetProperty call could have converted the array to dictionary | |
| 266 // mode, which must be detected to abort the fast-path. | |
| 267 Node* map = LoadMap(receiver); | |
| 268 Node* bit_field2 = LoadMapBitField2(map); | |
| 269 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); | |
| 270 GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)), | |
| 271 &default_label); | |
| 272 | |
| 273 GotoIfNotNumber(arg, &object_push); | |
| 274 Goto(&double_push); | |
| 275 } | |
| 276 | |
| 277 Bind(&object_push_pre); | |
| 278 { | |
| 279 Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)), | |
| 280 &double_push, &object_push); | |
| 281 } | |
| 282 | |
| 283 Bind(&object_push); | |
| 284 { | |
| 285 Node* new_length = BuildAppendJSArray(FAST_ELEMENTS, context, receiver, | |
| 286 args, arg_index, &default_label); | |
| 287 args.PopAndReturn(new_length); | |
| 288 } | |
| 289 | |
| 290 Bind(&double_push); | |
| 291 { | |
| 292 Node* new_length = | |
| 293 BuildAppendJSArray(FAST_DOUBLE_ELEMENTS, context, receiver, args, | |
| 294 arg_index, &double_transition); | |
| 295 args.PopAndReturn(new_length); | |
| 296 } | |
| 297 | |
| 298 // If the argument is not a double, then use a heavyweight SetProperty to | |
| 299 // transition the array for only the single next element. If the argument is | |
| 300 // a double, the failure is due to some other reason and we should fall back | |
| 301 // on the most generic implementation for the rest of the array. | |
| 302 Bind(&double_transition); | |
| 303 { | |
| 304 Node* arg = args.AtIndex(arg_index.value()); | |
| 305 GotoIfNumber(arg, &default_label); | |
| 306 Node* length = LoadJSArrayLength(receiver); | |
| 307 // TODO(danno): Use the KeyedStoreGeneric stub here when possible, | |
| 308 // calling into the runtime to do the elements transition is overkill. | |
| 309 CallRuntime(Runtime::kSetProperty, context, receiver, length, arg, | |
| 310 SmiConstant(STRICT)); | |
| 311 Increment(arg_index); | |
| 312 // The runtime SetProperty call could have converted the array to dictionary | |
| 313 // mode, which must be detected to abort the fast-path. | |
| 314 Node* map = LoadMap(receiver); | |
| 315 Node* bit_field2 = LoadMapBitField2(map); | |
| 316 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); | |
| 317 GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)), | |
| 318 &default_label); | |
| 319 Goto(&object_push); | |
| 320 } | |
| 321 | |
| 322 // Fallback that stores un-processed arguments using the full, heavyweight | |
| 323 // SetProperty machinery. | |
| 324 Bind(&default_label); | |
| 325 { | |
| 326 args.ForEach( | |
| 327 [this, receiver, context](Node* arg) { | |
| 328 Node* length = LoadJSArrayLength(receiver); | |
| 329 CallRuntime(Runtime::kSetProperty, context, receiver, length, arg, | |
| 330 SmiConstant(STRICT)); | |
| 331 }, | |
| 332 arg_index.value()); | |
| 333 args.PopAndReturn(LoadJSArrayLength(receiver)); | |
| 334 } | |
| 335 | |
| 336 Bind(&runtime); | |
| 337 { | |
| 338 Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset, | |
| 339 MachineType::TaggedPointer()); | |
| 340 TailCallStub(CodeFactory::ArrayPush(isolate()), context, target, new_target, | |
| 341 argc); | |
| 342 } | |
| 343 } | |
| 344 | |
| 345 BUILTIN(ArrayPop) { | 191 BUILTIN(ArrayPop) { |
| 346 HandleScope scope(isolate); | 192 HandleScope scope(isolate); |
| 347 Handle<Object> receiver = args.receiver(); | 193 Handle<Object> receiver = args.receiver(); |
| 348 if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0)) { | 194 if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0)) { |
| 349 return CallJsIntrinsic(isolate, isolate->array_pop(), args); | 195 return CallJsIntrinsic(isolate, isolate->array_pop(), args); |
| 350 } | 196 } |
| 351 | 197 |
| 352 Handle<JSArray> array = Handle<JSArray>::cast(receiver); | 198 Handle<JSArray> array = Handle<JSArray>::cast(receiver); |
| 353 | 199 |
| 354 uint32_t len = static_cast<uint32_t>(Smi::cast(array->length())->value()); | 200 uint32_t len = static_cast<uint32_t>(Smi::cast(array->length())->value()); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 408 | 254 |
| 409 if (JSArray::HasReadOnlyLength(array)) { | 255 if (JSArray::HasReadOnlyLength(array)) { |
| 410 return CallJsIntrinsic(isolate, isolate->array_unshift(), args); | 256 return CallJsIntrinsic(isolate, isolate->array_unshift(), args); |
| 411 } | 257 } |
| 412 | 258 |
| 413 ElementsAccessor* accessor = array->GetElementsAccessor(); | 259 ElementsAccessor* accessor = array->GetElementsAccessor(); |
| 414 int new_length = accessor->Unshift(array, &args, to_add); | 260 int new_length = accessor->Unshift(array, &args, to_add); |
| 415 return Smi::FromInt(new_length); | 261 return Smi::FromInt(new_length); |
| 416 } | 262 } |
| 417 | 263 |
| 418 class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { | |
| 419 public: | |
| 420 explicit ArrayBuiltinCodeStubAssembler(compiler::CodeAssemblerState* state) | |
| 421 : CodeStubAssembler(state) {} | |
| 422 | |
| 423 typedef std::function<Node*(Node* o, Node* len)> BuiltinResultGenerator; | |
| 424 typedef std::function<void(Node* a, Node* pK, Node* value)> | |
| 425 CallResultProcessor; | |
| 426 | |
| 427 void GenerateArrayIteratingBuiltinBody( | |
| 428 const char* name, Node* receiver, Node* callbackfn, Node* this_arg, | |
| 429 Node* context, const BuiltinResultGenerator& generator, | |
| 430 const CallResultProcessor& processor) { | |
| 431 Variable k(this, MachineRepresentation::kTagged, SmiConstant(0)); | |
| 432 Label non_array(this), slow(this, &k), array_changes(this, &k); | |
| 433 | |
| 434 // TODO(danno): Seriously? Do we really need to throw the exact error | |
| 435 // message on null and undefined so that the webkit tests pass? | |
| 436 Label throw_null_undefined_exception(this, Label::kDeferred); | |
| 437 GotoIf(WordEqual(receiver, NullConstant()), | |
| 438 &throw_null_undefined_exception); | |
| 439 GotoIf(WordEqual(receiver, UndefinedConstant()), | |
| 440 &throw_null_undefined_exception); | |
| 441 | |
| 442 // By the book: taken directly from the ECMAScript 2015 specification | |
| 443 | |
| 444 // 1. Let O be ToObject(this value). | |
| 445 // 2. ReturnIfAbrupt(O) | |
| 446 Node* o = CallStub(CodeFactory::ToObject(isolate()), context, receiver); | |
| 447 | |
| 448 // 3. Let len be ToLength(Get(O, "length")). | |
| 449 // 4. ReturnIfAbrupt(len). | |
| 450 Variable merged_length(this, MachineRepresentation::kTagged); | |
| 451 Label has_length(this, &merged_length), not_js_array(this); | |
| 452 GotoIf(DoesntHaveInstanceType(o, JS_ARRAY_TYPE), ¬_js_array); | |
| 453 merged_length.Bind(LoadJSArrayLength(o)); | |
| 454 Goto(&has_length); | |
| 455 Bind(¬_js_array); | |
| 456 Node* len_property = | |
| 457 GetProperty(context, o, isolate()->factory()->length_string()); | |
| 458 merged_length.Bind( | |
| 459 CallStub(CodeFactory::ToLength(isolate()), context, len_property)); | |
| 460 Goto(&has_length); | |
| 461 Bind(&has_length); | |
| 462 Node* len = merged_length.value(); | |
| 463 | |
| 464 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. | |
| 465 Label type_exception(this, Label::kDeferred); | |
| 466 Label done(this); | |
| 467 GotoIf(TaggedIsSmi(callbackfn), &type_exception); | |
| 468 Branch(IsCallableMap(LoadMap(callbackfn)), &done, &type_exception); | |
| 469 | |
| 470 Bind(&throw_null_undefined_exception); | |
| 471 { | |
| 472 CallRuntime( | |
| 473 Runtime::kThrowTypeError, context, | |
| 474 SmiConstant(MessageTemplate::kCalledOnNullOrUndefined), | |
| 475 HeapConstant(isolate()->factory()->NewStringFromAsciiChecked(name))); | |
| 476 Unreachable(); | |
| 477 } | |
| 478 | |
| 479 Bind(&type_exception); | |
| 480 { | |
| 481 CallRuntime(Runtime::kThrowTypeError, context, | |
| 482 SmiConstant(MessageTemplate::kCalledNonCallable), callbackfn); | |
| 483 Unreachable(); | |
| 484 } | |
| 485 | |
| 486 Bind(&done); | |
| 487 | |
| 488 Node* a = generator(o, len); | |
| 489 | |
| 490 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. | |
| 491 // [Already done by the arguments adapter] | |
| 492 | |
| 493 HandleFastElements(context, this_arg, o, len, callbackfn, processor, a, k, | |
| 494 &slow); | |
| 495 | |
| 496 // 7. Let k be 0. | |
| 497 // Already done above in initialization of the Variable k | |
| 498 | |
| 499 Bind(&slow); | |
| 500 { | |
| 501 // 8. Repeat, while k < len | |
| 502 Label loop(this, &k); | |
| 503 Label after_loop(this); | |
| 504 Goto(&loop); | |
| 505 Bind(&loop); | |
| 506 { | |
| 507 GotoUnlessNumberLessThan(k.value(), len, &after_loop); | |
| 508 | |
| 509 Label done_element(this); | |
| 510 // a. Let Pk be ToString(k). | |
| 511 Node* p_k = ToString(context, k.value()); | |
| 512 | |
| 513 // b. Let kPresent be HasProperty(O, Pk). | |
| 514 // c. ReturnIfAbrupt(kPresent). | |
| 515 Node* k_present = | |
| 516 CallStub(CodeFactory::HasProperty(isolate()), context, p_k, o); | |
| 517 | |
| 518 // d. If kPresent is true, then | |
| 519 GotoIf(WordNotEqual(k_present, TrueConstant()), &done_element); | |
| 520 | |
| 521 // i. Let kValue be Get(O, Pk). | |
| 522 // ii. ReturnIfAbrupt(kValue). | |
| 523 Node* k_value = GetProperty(context, o, k.value()); | |
| 524 | |
| 525 // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»). | |
| 526 // iv. ReturnIfAbrupt(funcResult). | |
| 527 Node* result = CallJS(CodeFactory::Call(isolate()), context, callbackfn, | |
| 528 this_arg, k_value, k.value(), o); | |
| 529 | |
| 530 processor(a, p_k, result); | |
| 531 Goto(&done_element); | |
| 532 Bind(&done_element); | |
| 533 | |
| 534 // e. Increase k by 1. | |
| 535 k.Bind(NumberInc(k.value())); | |
| 536 Goto(&loop); | |
| 537 } | |
| 538 Bind(&after_loop); | |
| 539 Return(a); | |
| 540 } | |
| 541 } | |
| 542 | |
| 543 private: | |
| 544 Node* VisitAllFastElementsOneKind(Node* context, ElementsKind kind, | |
| 545 Node* this_arg, Node* o, Node* len, | |
| 546 Node* callbackfn, | |
| 547 const CallResultProcessor& processor, | |
| 548 Node* a, Label* array_changed, | |
| 549 ParameterMode mode) { | |
| 550 Comment("begin VisitAllFastElementsOneKind"); | |
| 551 Variable original_map(this, MachineRepresentation::kTagged); | |
| 552 original_map.Bind(LoadMap(o)); | |
| 553 VariableList list({&original_map}, zone()); | |
| 554 Node* last_index = nullptr; | |
| 555 BuildFastLoop( | |
| 556 list, IntPtrOrSmiConstant(0, mode), TaggedToParameter(len, mode), | |
| 557 [=, &original_map, &last_index](Node* index) { | |
| 558 last_index = index; | |
| 559 Label one_element_done(this), hole_element(this); | |
| 560 | |
| 561 // Check if o's map has changed during the callback. If so, we have to | |
| 562 // fall back to the slower spec implementation for the rest of the | |
| 563 // iteration. | |
| 564 Node* o_map = LoadMap(o); | |
| 565 GotoIf(WordNotEqual(o_map, original_map.value()), array_changed); | |
| 566 | |
| 567 // Check if o's length has changed during the callback and if the | |
| 568 // index is now out of range of the new length. | |
| 569 Node* tagged_index = ParameterToTagged(index, mode); | |
| 570 GotoIf(SmiGreaterThanOrEqual(tagged_index, LoadJSArrayLength(o)), | |
| 571 array_changed); | |
| 572 | |
| 573 // Re-load the elements array. If may have been resized. | |
| 574 Node* elements = LoadElements(o); | |
| 575 | |
| 576 // Fast case: load the element directly from the elements FixedArray | |
| 577 // and call the callback if the element is not the hole. | |
| 578 DCHECK(kind == FAST_ELEMENTS || kind == FAST_DOUBLE_ELEMENTS); | |
| 579 int base_size = kind == FAST_ELEMENTS | |
| 580 ? FixedArray::kHeaderSize | |
| 581 : (FixedArray::kHeaderSize - kHeapObjectTag); | |
| 582 Node* offset = ElementOffsetFromIndex(index, kind, mode, base_size); | |
| 583 Node* value = nullptr; | |
| 584 if (kind == FAST_ELEMENTS) { | |
| 585 value = LoadObjectField(elements, offset); | |
| 586 GotoIf(WordEqual(value, TheHoleConstant()), &hole_element); | |
| 587 } else { | |
| 588 Node* double_value = | |
| 589 LoadDoubleWithHoleCheck(elements, offset, &hole_element); | |
| 590 value = AllocateHeapNumberWithValue(double_value); | |
| 591 } | |
| 592 Node* result = CallJS(CodeFactory::Call(isolate()), context, | |
| 593 callbackfn, this_arg, value, tagged_index, o); | |
| 594 processor(a, tagged_index, result); | |
| 595 Goto(&one_element_done); | |
| 596 | |
| 597 Bind(&hole_element); | |
| 598 // Check if o's prototype change unexpectedly has elements after the | |
| 599 // callback in the case of a hole. | |
| 600 BranchIfPrototypesHaveNoElements(o_map, &one_element_done, | |
| 601 array_changed); | |
| 602 | |
| 603 Bind(&one_element_done); | |
| 604 }, | |
| 605 1, mode, IndexAdvanceMode::kPost); | |
| 606 Comment("end VisitAllFastElementsOneKind"); | |
| 607 return last_index; | |
| 608 } | |
| 609 | |
| 610 void HandleFastElements(Node* context, Node* this_arg, Node* o, Node* len, | |
| 611 Node* callbackfn, CallResultProcessor processor, | |
| 612 Node* a, Variable& k, Label* slow) { | |
| 613 Label switch_on_elements_kind(this), fast_elements(this), | |
| 614 maybe_double_elements(this), fast_double_elements(this); | |
| 615 | |
| 616 Comment("begin HandleFastElements"); | |
| 617 // Non-smi lengths must use the slow path. | |
| 618 GotoIf(TaggedIsNotSmi(len), slow); | |
| 619 | |
| 620 BranchIfFastJSArray(o, context, | |
| 621 CodeStubAssembler::FastJSArrayAccessMode::INBOUNDS_READ, | |
| 622 &switch_on_elements_kind, slow); | |
| 623 | |
| 624 Bind(&switch_on_elements_kind); | |
| 625 // Select by ElementsKind | |
| 626 Node* o_map = LoadMap(o); | |
| 627 Node* bit_field2 = LoadMapBitField2(o_map); | |
| 628 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); | |
| 629 Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)), | |
| 630 &maybe_double_elements, &fast_elements); | |
| 631 | |
| 632 ParameterMode mode = OptimalParameterMode(); | |
| 633 Bind(&fast_elements); | |
| 634 { | |
| 635 Label array_changed(this, Label::kDeferred); | |
| 636 Node* last_index = VisitAllFastElementsOneKind( | |
| 637 context, FAST_ELEMENTS, this_arg, o, len, callbackfn, processor, a, | |
| 638 &array_changed, mode); | |
| 639 | |
| 640 // No exception, return success | |
| 641 Return(a); | |
| 642 | |
| 643 Bind(&array_changed); | |
| 644 k.Bind(ParameterToTagged(last_index, mode)); | |
| 645 Goto(slow); | |
| 646 } | |
| 647 | |
| 648 Bind(&maybe_double_elements); | |
| 649 Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_DOUBLE_ELEMENTS)), | |
| 650 slow, &fast_double_elements); | |
| 651 | |
| 652 Bind(&fast_double_elements); | |
| 653 { | |
| 654 Label array_changed(this, Label::kDeferred); | |
| 655 Node* last_index = VisitAllFastElementsOneKind( | |
| 656 context, FAST_DOUBLE_ELEMENTS, this_arg, o, len, callbackfn, | |
| 657 processor, a, &array_changed, mode); | |
| 658 | |
| 659 // No exception, return success | |
| 660 Return(a); | |
| 661 | |
| 662 Bind(&array_changed); | |
| 663 k.Bind(ParameterToTagged(last_index, mode)); | |
| 664 Goto(slow); | |
| 665 } | |
| 666 } | |
| 667 }; | |
| 668 | |
| 669 TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) { | |
| 670 Node* receiver = Parameter(ForEachDescriptor::kReceiver); | |
| 671 Node* callbackfn = Parameter(ForEachDescriptor::kCallback); | |
| 672 Node* this_arg = Parameter(ForEachDescriptor::kThisArg); | |
| 673 Node* context = Parameter(ForEachDescriptor::kContext); | |
| 674 | |
| 675 GenerateArrayIteratingBuiltinBody( | |
| 676 "Array.prototype.forEach", receiver, callbackfn, this_arg, context, | |
| 677 [=](Node*, Node*) { return UndefinedConstant(); }, | |
| 678 [](Node* a, Node* p_k, Node* value) {}); | |
| 679 } | |
| 680 | |
| 681 TF_BUILTIN(ArrayEvery, ArrayBuiltinCodeStubAssembler) { | |
| 682 Node* receiver = Parameter(ForEachDescriptor::kReceiver); | |
| 683 Node* callbackfn = Parameter(ForEachDescriptor::kCallback); | |
| 684 Node* this_arg = Parameter(ForEachDescriptor::kThisArg); | |
| 685 Node* context = Parameter(ForEachDescriptor::kContext); | |
| 686 | |
| 687 GenerateArrayIteratingBuiltinBody( | |
| 688 "Array.prototype.every", receiver, callbackfn, this_arg, context, | |
| 689 [=](Node*, Node*) { return TrueConstant(); }, | |
| 690 [=](Node* a, Node* p_k, Node* value) { | |
| 691 Label true_continue(this), return_false(this); | |
| 692 BranchIfToBooleanIsTrue(value, &true_continue, &return_false); | |
| 693 Bind(&return_false); | |
| 694 Return(FalseConstant()); | |
| 695 Bind(&true_continue); | |
| 696 }); | |
| 697 } | |
| 698 | |
| 699 TF_BUILTIN(ArraySome, ArrayBuiltinCodeStubAssembler) { | |
| 700 Node* receiver = Parameter(ForEachDescriptor::kReceiver); | |
| 701 Node* callbackfn = Parameter(ForEachDescriptor::kCallback); | |
| 702 Node* this_arg = Parameter(ForEachDescriptor::kThisArg); | |
| 703 Node* context = Parameter(ForEachDescriptor::kContext); | |
| 704 | |
| 705 GenerateArrayIteratingBuiltinBody( | |
| 706 "Array.prototype.some", receiver, callbackfn, this_arg, context, | |
| 707 [=](Node*, Node*) { return FalseConstant(); }, | |
| 708 [=](Node* a, Node* p_k, Node* value) { | |
| 709 Label false_continue(this), return_true(this); | |
| 710 BranchIfToBooleanIsTrue(value, &return_true, &false_continue); | |
| 711 Bind(&return_true); | |
| 712 Return(TrueConstant()); | |
| 713 Bind(&false_continue); | |
| 714 }); | |
| 715 } | |
| 716 | |
| 717 BUILTIN(ArraySlice) { | 264 BUILTIN(ArraySlice) { |
| 718 HandleScope scope(isolate); | 265 HandleScope scope(isolate); |
| 719 Handle<Object> receiver = args.receiver(); | 266 Handle<Object> receiver = args.receiver(); |
| 720 int len = -1; | 267 int len = -1; |
| 721 int relative_start = 0; | 268 int relative_start = 0; |
| 722 int relative_end = 0; | 269 int relative_end = 0; |
| 723 | 270 |
| 724 if (receiver->IsJSArray()) { | 271 if (receiver->IsJSArray()) { |
| 725 DisallowHeapAllocation no_gc; | 272 DisallowHeapAllocation no_gc; |
| 726 JSArray* array = JSArray::cast(*receiver); | 273 JSArray* array = JSArray::cast(*receiver); |
| (...skipping 958 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1685 isolate, species, Object::ArraySpeciesConstructor(isolate, receiver)); | 1232 isolate, species, Object::ArraySpeciesConstructor(isolate, receiver)); |
| 1686 if (*species == *isolate->array_function()) { | 1233 if (*species == *isolate->array_function()) { |
| 1687 if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) { | 1234 if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) { |
| 1688 return *result_array; | 1235 return *result_array; |
| 1689 } | 1236 } |
| 1690 if (isolate->has_pending_exception()) return isolate->heap()->exception(); | 1237 if (isolate->has_pending_exception()) return isolate->heap()->exception(); |
| 1691 } | 1238 } |
| 1692 return Slow_ArrayConcat(&args, species, isolate); | 1239 return Slow_ArrayConcat(&args, species, isolate); |
| 1693 } | 1240 } |
| 1694 | 1241 |
| 1695 TF_BUILTIN(ArrayIsArray, CodeStubAssembler) { | |
| 1696 Node* object = Parameter(1); | |
| 1697 Node* context = Parameter(4); | |
| 1698 | |
| 1699 Label call_runtime(this), return_true(this), return_false(this); | |
| 1700 | |
| 1701 GotoIf(TaggedIsSmi(object), &return_false); | |
| 1702 Node* instance_type = LoadInstanceType(object); | |
| 1703 | |
| 1704 GotoIf(Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE)), | |
| 1705 &return_true); | |
| 1706 | |
| 1707 // TODO(verwaest): Handle proxies in-place. | |
| 1708 Branch(Word32Equal(instance_type, Int32Constant(JS_PROXY_TYPE)), | |
| 1709 &call_runtime, &return_false); | |
| 1710 | |
| 1711 Bind(&return_true); | |
| 1712 Return(BooleanConstant(true)); | |
| 1713 | |
| 1714 Bind(&return_false); | |
| 1715 Return(BooleanConstant(false)); | |
| 1716 | |
| 1717 Bind(&call_runtime); | |
| 1718 Return(CallRuntime(Runtime::kArrayIsArray, context, object)); | |
| 1719 } | |
| 1720 | |
| 1721 TF_BUILTIN(ArrayIncludes, CodeStubAssembler) { | |
| 1722 Node* const array = Parameter(0); | |
| 1723 Node* const search_element = Parameter(1); | |
| 1724 Node* const start_from = Parameter(2); | |
| 1725 Node* const context = Parameter(3 + 2); | |
| 1726 | |
| 1727 Variable index_var(this, MachineType::PointerRepresentation()); | |
| 1728 | |
| 1729 Label init_k(this), return_true(this), return_false(this), call_runtime(this); | |
| 1730 Label init_len(this), select_loop(this); | |
| 1731 | |
| 1732 index_var.Bind(IntPtrConstant(0)); | |
| 1733 | |
| 1734 // Take slow path if not a JSArray, if retrieving elements requires | |
| 1735 // traversing prototype, or if access checks are required. | |
| 1736 BranchIfFastJSArray(array, context, FastJSArrayAccessMode::INBOUNDS_READ, | |
| 1737 &init_len, &call_runtime); | |
| 1738 | |
| 1739 Bind(&init_len); | |
| 1740 // JSArray length is always an Smi for fast arrays. | |
| 1741 CSA_ASSERT(this, TaggedIsSmi(LoadObjectField(array, JSArray::kLengthOffset))); | |
| 1742 Node* const len = LoadAndUntagObjectField(array, JSArray::kLengthOffset); | |
| 1743 | |
| 1744 GotoIf(IsUndefined(start_from), &select_loop); | |
| 1745 | |
| 1746 // Bailout to slow path if startIndex is not an Smi. | |
| 1747 Branch(TaggedIsSmi(start_from), &init_k, &call_runtime); | |
| 1748 | |
| 1749 Bind(&init_k); | |
| 1750 CSA_ASSERT(this, TaggedIsSmi(start_from)); | |
| 1751 Node* const untagged_start_from = SmiToWord(start_from); | |
| 1752 index_var.Bind(Select( | |
| 1753 IntPtrGreaterThanOrEqual(untagged_start_from, IntPtrConstant(0)), | |
| 1754 [=]() { return untagged_start_from; }, | |
| 1755 [=]() { | |
| 1756 Node* const index = IntPtrAdd(len, untagged_start_from); | |
| 1757 return SelectConstant(IntPtrLessThan(index, IntPtrConstant(0)), | |
| 1758 IntPtrConstant(0), index, | |
| 1759 MachineType::PointerRepresentation()); | |
| 1760 }, | |
| 1761 MachineType::PointerRepresentation())); | |
| 1762 | |
| 1763 Goto(&select_loop); | |
| 1764 Bind(&select_loop); | |
| 1765 static int32_t kElementsKind[] = { | |
| 1766 FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS, | |
| 1767 FAST_HOLEY_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS, | |
| 1768 }; | |
| 1769 | |
| 1770 Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this); | |
| 1771 Label* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects, | |
| 1772 &if_smiorobjects, &if_smiorobjects, | |
| 1773 &if_packed_doubles, &if_holey_doubles}; | |
| 1774 | |
| 1775 Node* map = LoadMap(array); | |
| 1776 Node* elements_kind = LoadMapElementsKind(map); | |
| 1777 Node* elements = LoadElements(array); | |
| 1778 Switch(elements_kind, &return_false, kElementsKind, element_kind_handlers, | |
| 1779 arraysize(kElementsKind)); | |
| 1780 | |
| 1781 Bind(&if_smiorobjects); | |
| 1782 { | |
| 1783 Variable search_num(this, MachineRepresentation::kFloat64); | |
| 1784 Label ident_loop(this, &index_var), heap_num_loop(this, &search_num), | |
| 1785 string_loop(this, &index_var), undef_loop(this, &index_var), | |
| 1786 not_smi(this), not_heap_num(this); | |
| 1787 | |
| 1788 GotoIfNot(TaggedIsSmi(search_element), ¬_smi); | |
| 1789 search_num.Bind(SmiToFloat64(search_element)); | |
| 1790 Goto(&heap_num_loop); | |
| 1791 | |
| 1792 Bind(¬_smi); | |
| 1793 GotoIf(WordEqual(search_element, UndefinedConstant()), &undef_loop); | |
| 1794 Node* map = LoadMap(search_element); | |
| 1795 GotoIfNot(IsHeapNumberMap(map), ¬_heap_num); | |
| 1796 search_num.Bind(LoadHeapNumberValue(search_element)); | |
| 1797 Goto(&heap_num_loop); | |
| 1798 | |
| 1799 Bind(¬_heap_num); | |
| 1800 Node* search_type = LoadMapInstanceType(map); | |
| 1801 GotoIf(IsStringInstanceType(search_type), &string_loop); | |
| 1802 Goto(&ident_loop); | |
| 1803 | |
| 1804 Bind(&ident_loop); | |
| 1805 { | |
| 1806 GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); | |
| 1807 Node* element_k = LoadFixedArrayElement(elements, index_var.value()); | |
| 1808 GotoIf(WordEqual(element_k, search_element), &return_true); | |
| 1809 | |
| 1810 index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); | |
| 1811 Goto(&ident_loop); | |
| 1812 } | |
| 1813 | |
| 1814 Bind(&undef_loop); | |
| 1815 { | |
| 1816 GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); | |
| 1817 Node* element_k = LoadFixedArrayElement(elements, index_var.value()); | |
| 1818 GotoIf(WordEqual(element_k, UndefinedConstant()), &return_true); | |
| 1819 GotoIf(WordEqual(element_k, TheHoleConstant()), &return_true); | |
| 1820 | |
| 1821 index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); | |
| 1822 Goto(&undef_loop); | |
| 1823 } | |
| 1824 | |
| 1825 Bind(&heap_num_loop); | |
| 1826 { | |
| 1827 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var); | |
| 1828 BranchIfFloat64IsNaN(search_num.value(), &nan_loop, ¬_nan_loop); | |
| 1829 | |
| 1830 Bind(¬_nan_loop); | |
| 1831 { | |
| 1832 Label continue_loop(this), not_smi(this); | |
| 1833 GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); | |
| 1834 Node* element_k = LoadFixedArrayElement(elements, index_var.value()); | |
| 1835 GotoIfNot(TaggedIsSmi(element_k), ¬_smi); | |
| 1836 Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)), | |
| 1837 &return_true, &continue_loop); | |
| 1838 | |
| 1839 Bind(¬_smi); | |
| 1840 GotoIfNot(IsHeapNumber(element_k), &continue_loop); | |
| 1841 Branch(Float64Equal(search_num.value(), LoadHeapNumberValue(element_k)), | |
| 1842 &return_true, &continue_loop); | |
| 1843 | |
| 1844 Bind(&continue_loop); | |
| 1845 index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); | |
| 1846 Goto(¬_nan_loop); | |
| 1847 } | |
| 1848 | |
| 1849 Bind(&nan_loop); | |
| 1850 { | |
| 1851 Label continue_loop(this); | |
| 1852 GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); | |
| 1853 Node* element_k = LoadFixedArrayElement(elements, index_var.value()); | |
| 1854 GotoIf(TaggedIsSmi(element_k), &continue_loop); | |
| 1855 GotoIfNot(IsHeapNumber(element_k), &continue_loop); | |
| 1856 BranchIfFloat64IsNaN(LoadHeapNumberValue(element_k), &return_true, | |
| 1857 &continue_loop); | |
| 1858 | |
| 1859 Bind(&continue_loop); | |
| 1860 index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); | |
| 1861 Goto(&nan_loop); | |
| 1862 } | |
| 1863 } | |
| 1864 | |
| 1865 Bind(&string_loop); | |
| 1866 { | |
| 1867 Label continue_loop(this); | |
| 1868 GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); | |
| 1869 Node* element_k = LoadFixedArrayElement(elements, index_var.value()); | |
| 1870 GotoIf(TaggedIsSmi(element_k), &continue_loop); | |
| 1871 GotoIfNot(IsStringInstanceType(LoadInstanceType(element_k)), | |
| 1872 &continue_loop); | |
| 1873 | |
| 1874 // TODO(bmeurer): Consider inlining the StringEqual logic here. | |
| 1875 Node* result = CallStub(CodeFactory::StringEqual(isolate()), context, | |
| 1876 search_element, element_k); | |
| 1877 Branch(WordEqual(BooleanConstant(true), result), &return_true, | |
| 1878 &continue_loop); | |
| 1879 | |
| 1880 Bind(&continue_loop); | |
| 1881 index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); | |
| 1882 Goto(&string_loop); | |
| 1883 } | |
| 1884 } | |
| 1885 | |
| 1886 Bind(&if_packed_doubles); | |
| 1887 { | |
| 1888 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var), | |
| 1889 hole_loop(this, &index_var), search_notnan(this); | |
| 1890 Variable search_num(this, MachineRepresentation::kFloat64); | |
| 1891 | |
| 1892 GotoIfNot(TaggedIsSmi(search_element), &search_notnan); | |
| 1893 search_num.Bind(SmiToFloat64(search_element)); | |
| 1894 Goto(¬_nan_loop); | |
| 1895 | |
| 1896 Bind(&search_notnan); | |
| 1897 GotoIfNot(IsHeapNumber(search_element), &return_false); | |
| 1898 | |
| 1899 search_num.Bind(LoadHeapNumberValue(search_element)); | |
| 1900 | |
| 1901 BranchIfFloat64IsNaN(search_num.value(), &nan_loop, ¬_nan_loop); | |
| 1902 | |
| 1903 // Search for HeapNumber | |
| 1904 Bind(¬_nan_loop); | |
| 1905 { | |
| 1906 Label continue_loop(this); | |
| 1907 GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); | |
| 1908 Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), | |
| 1909 MachineType::Float64()); | |
| 1910 Branch(Float64Equal(element_k, search_num.value()), &return_true, | |
| 1911 &continue_loop); | |
| 1912 Bind(&continue_loop); | |
| 1913 index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); | |
| 1914 Goto(¬_nan_loop); | |
| 1915 } | |
| 1916 | |
| 1917 // Search for NaN | |
| 1918 Bind(&nan_loop); | |
| 1919 { | |
| 1920 Label continue_loop(this); | |
| 1921 GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); | |
| 1922 Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), | |
| 1923 MachineType::Float64()); | |
| 1924 BranchIfFloat64IsNaN(element_k, &return_true, &continue_loop); | |
| 1925 Bind(&continue_loop); | |
| 1926 index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); | |
| 1927 Goto(&nan_loop); | |
| 1928 } | |
| 1929 } | |
| 1930 | |
| 1931 Bind(&if_holey_doubles); | |
| 1932 { | |
| 1933 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var), | |
| 1934 hole_loop(this, &index_var), search_notnan(this); | |
| 1935 Variable search_num(this, MachineRepresentation::kFloat64); | |
| 1936 | |
| 1937 GotoIfNot(TaggedIsSmi(search_element), &search_notnan); | |
| 1938 search_num.Bind(SmiToFloat64(search_element)); | |
| 1939 Goto(¬_nan_loop); | |
| 1940 | |
| 1941 Bind(&search_notnan); | |
| 1942 GotoIf(WordEqual(search_element, UndefinedConstant()), &hole_loop); | |
| 1943 GotoIfNot(IsHeapNumber(search_element), &return_false); | |
| 1944 | |
| 1945 search_num.Bind(LoadHeapNumberValue(search_element)); | |
| 1946 | |
| 1947 BranchIfFloat64IsNaN(search_num.value(), &nan_loop, ¬_nan_loop); | |
| 1948 | |
| 1949 // Search for HeapNumber | |
| 1950 Bind(¬_nan_loop); | |
| 1951 { | |
| 1952 Label continue_loop(this); | |
| 1953 GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); | |
| 1954 | |
| 1955 // Load double value or continue if it contains a double hole. | |
| 1956 Node* element_k = LoadFixedDoubleArrayElement( | |
| 1957 elements, index_var.value(), MachineType::Float64(), 0, | |
| 1958 INTPTR_PARAMETERS, &continue_loop); | |
| 1959 | |
| 1960 Branch(Float64Equal(element_k, search_num.value()), &return_true, | |
| 1961 &continue_loop); | |
| 1962 Bind(&continue_loop); | |
| 1963 index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); | |
| 1964 Goto(¬_nan_loop); | |
| 1965 } | |
| 1966 | |
| 1967 // Search for NaN | |
| 1968 Bind(&nan_loop); | |
| 1969 { | |
| 1970 Label continue_loop(this); | |
| 1971 GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); | |
| 1972 | |
| 1973 // Load double value or continue if it contains a double hole. | |
| 1974 Node* element_k = LoadFixedDoubleArrayElement( | |
| 1975 elements, index_var.value(), MachineType::Float64(), 0, | |
| 1976 INTPTR_PARAMETERS, &continue_loop); | |
| 1977 | |
| 1978 BranchIfFloat64IsNaN(element_k, &return_true, &continue_loop); | |
| 1979 Bind(&continue_loop); | |
| 1980 index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); | |
| 1981 Goto(&nan_loop); | |
| 1982 } | |
| 1983 | |
| 1984 // Search for the Hole | |
| 1985 Bind(&hole_loop); | |
| 1986 { | |
| 1987 GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); | |
| 1988 | |
| 1989 // Check if the element is a double hole, but don't load it. | |
| 1990 LoadFixedDoubleArrayElement(elements, index_var.value(), | |
| 1991 MachineType::None(), 0, INTPTR_PARAMETERS, | |
| 1992 &return_true); | |
| 1993 | |
| 1994 index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); | |
| 1995 Goto(&hole_loop); | |
| 1996 } | |
| 1997 } | |
| 1998 | |
| 1999 Bind(&return_true); | |
| 2000 Return(TrueConstant()); | |
| 2001 | |
| 2002 Bind(&return_false); | |
| 2003 Return(FalseConstant()); | |
| 2004 | |
| 2005 Bind(&call_runtime); | |
| 2006 Return(CallRuntime(Runtime::kArrayIncludes_Slow, context, array, | |
| 2007 search_element, start_from)); | |
| 2008 } | |
| 2009 | |
| 2010 TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) { | |
| 2011 Node* array = Parameter(0); | |
| 2012 Node* search_element = Parameter(1); | |
| 2013 Node* start_from = Parameter(2); | |
| 2014 Node* context = Parameter(3 + 2); | |
| 2015 | |
| 2016 Node* intptr_zero = IntPtrConstant(0); | |
| 2017 Node* intptr_one = IntPtrConstant(1); | |
| 2018 | |
| 2019 Variable len_var(this, MachineType::PointerRepresentation()), | |
| 2020 index_var(this, MachineType::PointerRepresentation()), | |
| 2021 start_from_var(this, MachineType::PointerRepresentation()); | |
| 2022 | |
| 2023 Label init_k(this), return_found(this), return_not_found(this), | |
| 2024 call_runtime(this); | |
| 2025 | |
| 2026 Label init_len(this); | |
| 2027 | |
| 2028 index_var.Bind(intptr_zero); | |
| 2029 len_var.Bind(intptr_zero); | |
| 2030 | |
| 2031 // Take slow path if not a JSArray, if retrieving elements requires | |
| 2032 // traversing prototype, or if access checks are required. | |
| 2033 BranchIfFastJSArray(array, context, FastJSArrayAccessMode::INBOUNDS_READ, | |
| 2034 &init_len, &call_runtime); | |
| 2035 | |
| 2036 Bind(&init_len); | |
| 2037 { | |
| 2038 // JSArray length is always an Smi for fast arrays. | |
| 2039 CSA_ASSERT(this, | |
| 2040 TaggedIsSmi(LoadObjectField(array, JSArray::kLengthOffset))); | |
| 2041 Node* len = LoadAndUntagObjectField(array, JSArray::kLengthOffset); | |
| 2042 | |
| 2043 len_var.Bind(len); | |
| 2044 Branch(WordEqual(len_var.value(), intptr_zero), &return_not_found, &init_k); | |
| 2045 } | |
| 2046 | |
| 2047 Bind(&init_k); | |
| 2048 { | |
| 2049 // For now only deal with undefined and Smis here; we must be really careful | |
| 2050 // with side-effects from the ToInteger conversion as the side-effects might | |
| 2051 // render our assumptions about the receiver being a fast JSArray and the | |
| 2052 // length invalid. | |
| 2053 Label done(this), init_k_smi(this), init_k_other(this), init_k_zero(this), | |
| 2054 init_k_n(this); | |
| 2055 Branch(TaggedIsSmi(start_from), &init_k_smi, &init_k_other); | |
| 2056 | |
| 2057 Bind(&init_k_smi); | |
| 2058 { | |
| 2059 // The fromIndex is a Smi. | |
| 2060 start_from_var.Bind(SmiUntag(start_from)); | |
| 2061 Goto(&init_k_n); | |
| 2062 } | |
| 2063 | |
| 2064 Bind(&init_k_other); | |
| 2065 { | |
| 2066 // The fromIndex must be undefined then, otherwise bailout and let the | |
| 2067 // runtime deal with the full ToInteger conversion. | |
| 2068 GotoIfNot(IsUndefined(start_from), &call_runtime); | |
| 2069 start_from_var.Bind(intptr_zero); | |
| 2070 Goto(&init_k_n); | |
| 2071 } | |
| 2072 | |
| 2073 Bind(&init_k_n); | |
| 2074 { | |
| 2075 Label if_positive(this), if_negative(this), done(this); | |
| 2076 Branch(IntPtrLessThan(start_from_var.value(), intptr_zero), &if_negative, | |
| 2077 &if_positive); | |
| 2078 | |
| 2079 Bind(&if_positive); | |
| 2080 { | |
| 2081 index_var.Bind(start_from_var.value()); | |
| 2082 Goto(&done); | |
| 2083 } | |
| 2084 | |
| 2085 Bind(&if_negative); | |
| 2086 { | |
| 2087 index_var.Bind(IntPtrAdd(len_var.value(), start_from_var.value())); | |
| 2088 Branch(IntPtrLessThan(index_var.value(), intptr_zero), &init_k_zero, | |
| 2089 &done); | |
| 2090 } | |
| 2091 | |
| 2092 Bind(&init_k_zero); | |
| 2093 { | |
| 2094 index_var.Bind(intptr_zero); | |
| 2095 Goto(&done); | |
| 2096 } | |
| 2097 | |
| 2098 Bind(&done); | |
| 2099 } | |
| 2100 } | |
| 2101 | |
| 2102 static int32_t kElementsKind[] = { | |
| 2103 FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS, | |
| 2104 FAST_HOLEY_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS, | |
| 2105 }; | |
| 2106 | |
| 2107 Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this); | |
| 2108 Label* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects, | |
| 2109 &if_smiorobjects, &if_smiorobjects, | |
| 2110 &if_packed_doubles, &if_holey_doubles}; | |
| 2111 | |
| 2112 Node* map = LoadMap(array); | |
| 2113 Node* elements_kind = LoadMapElementsKind(map); | |
| 2114 Node* elements = LoadElements(array); | |
| 2115 Switch(elements_kind, &return_not_found, kElementsKind, element_kind_handlers, | |
| 2116 arraysize(kElementsKind)); | |
| 2117 | |
| 2118 Bind(&if_smiorobjects); | |
| 2119 { | |
| 2120 Variable search_num(this, MachineRepresentation::kFloat64); | |
| 2121 Label ident_loop(this, &index_var), heap_num_loop(this, &search_num), | |
| 2122 string_loop(this, &index_var), not_smi(this), not_heap_num(this); | |
| 2123 | |
| 2124 GotoIfNot(TaggedIsSmi(search_element), ¬_smi); | |
| 2125 search_num.Bind(SmiToFloat64(search_element)); | |
| 2126 Goto(&heap_num_loop); | |
| 2127 | |
| 2128 Bind(¬_smi); | |
| 2129 Node* map = LoadMap(search_element); | |
| 2130 GotoIfNot(IsHeapNumberMap(map), ¬_heap_num); | |
| 2131 search_num.Bind(LoadHeapNumberValue(search_element)); | |
| 2132 Goto(&heap_num_loop); | |
| 2133 | |
| 2134 Bind(¬_heap_num); | |
| 2135 Node* search_type = LoadMapInstanceType(map); | |
| 2136 GotoIf(IsStringInstanceType(search_type), &string_loop); | |
| 2137 Goto(&ident_loop); | |
| 2138 | |
| 2139 Bind(&ident_loop); | |
| 2140 { | |
| 2141 GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()), | |
| 2142 &return_not_found); | |
| 2143 Node* element_k = LoadFixedArrayElement(elements, index_var.value()); | |
| 2144 GotoIf(WordEqual(element_k, search_element), &return_found); | |
| 2145 | |
| 2146 index_var.Bind(IntPtrAdd(index_var.value(), intptr_one)); | |
| 2147 Goto(&ident_loop); | |
| 2148 } | |
| 2149 | |
| 2150 Bind(&heap_num_loop); | |
| 2151 { | |
| 2152 Label not_nan_loop(this, &index_var); | |
| 2153 BranchIfFloat64IsNaN(search_num.value(), &return_not_found, | |
| 2154 ¬_nan_loop); | |
| 2155 | |
| 2156 Bind(¬_nan_loop); | |
| 2157 { | |
| 2158 Label continue_loop(this), not_smi(this); | |
| 2159 GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()), | |
| 2160 &return_not_found); | |
| 2161 Node* element_k = LoadFixedArrayElement(elements, index_var.value()); | |
| 2162 GotoIfNot(TaggedIsSmi(element_k), ¬_smi); | |
| 2163 Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)), | |
| 2164 &return_found, &continue_loop); | |
| 2165 | |
| 2166 Bind(¬_smi); | |
| 2167 GotoIfNot(IsHeapNumber(element_k), &continue_loop); | |
| 2168 Branch(Float64Equal(search_num.value(), LoadHeapNumberValue(element_k)), | |
| 2169 &return_found, &continue_loop); | |
| 2170 | |
| 2171 Bind(&continue_loop); | |
| 2172 index_var.Bind(IntPtrAdd(index_var.value(), intptr_one)); | |
| 2173 Goto(¬_nan_loop); | |
| 2174 } | |
| 2175 } | |
| 2176 | |
| 2177 Bind(&string_loop); | |
| 2178 { | |
| 2179 Label continue_loop(this); | |
| 2180 GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()), | |
| 2181 &return_not_found); | |
| 2182 Node* element_k = LoadFixedArrayElement(elements, index_var.value()); | |
| 2183 GotoIf(TaggedIsSmi(element_k), &continue_loop); | |
| 2184 GotoIfNot(IsString(element_k), &continue_loop); | |
| 2185 | |
| 2186 // TODO(bmeurer): Consider inlining the StringEqual logic here. | |
| 2187 Callable callable = CodeFactory::StringEqual(isolate()); | |
| 2188 Node* result = CallStub(callable, context, search_element, element_k); | |
| 2189 Branch(WordEqual(BooleanConstant(true), result), &return_found, | |
| 2190 &continue_loop); | |
| 2191 | |
| 2192 Bind(&continue_loop); | |
| 2193 index_var.Bind(IntPtrAdd(index_var.value(), intptr_one)); | |
| 2194 Goto(&string_loop); | |
| 2195 } | |
| 2196 } | |
| 2197 | |
| 2198 Bind(&if_packed_doubles); | |
| 2199 { | |
| 2200 Label not_nan_loop(this, &index_var), search_notnan(this); | |
| 2201 Variable search_num(this, MachineRepresentation::kFloat64); | |
| 2202 | |
| 2203 GotoIfNot(TaggedIsSmi(search_element), &search_notnan); | |
| 2204 search_num.Bind(SmiToFloat64(search_element)); | |
| 2205 Goto(¬_nan_loop); | |
| 2206 | |
| 2207 Bind(&search_notnan); | |
| 2208 GotoIfNot(IsHeapNumber(search_element), &return_not_found); | |
| 2209 | |
| 2210 search_num.Bind(LoadHeapNumberValue(search_element)); | |
| 2211 | |
| 2212 BranchIfFloat64IsNaN(search_num.value(), &return_not_found, ¬_nan_loop); | |
| 2213 | |
| 2214 // Search for HeapNumber | |
| 2215 Bind(¬_nan_loop); | |
| 2216 { | |
| 2217 GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()), | |
| 2218 &return_not_found); | |
| 2219 Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), | |
| 2220 MachineType::Float64()); | |
| 2221 GotoIf(Float64Equal(element_k, search_num.value()), &return_found); | |
| 2222 | |
| 2223 index_var.Bind(IntPtrAdd(index_var.value(), intptr_one)); | |
| 2224 Goto(¬_nan_loop); | |
| 2225 } | |
| 2226 } | |
| 2227 | |
| 2228 Bind(&if_holey_doubles); | |
| 2229 { | |
| 2230 Label not_nan_loop(this, &index_var), search_notnan(this); | |
| 2231 Variable search_num(this, MachineRepresentation::kFloat64); | |
| 2232 | |
| 2233 GotoIfNot(TaggedIsSmi(search_element), &search_notnan); | |
| 2234 search_num.Bind(SmiToFloat64(search_element)); | |
| 2235 Goto(¬_nan_loop); | |
| 2236 | |
| 2237 Bind(&search_notnan); | |
| 2238 GotoIfNot(IsHeapNumber(search_element), &return_not_found); | |
| 2239 | |
| 2240 search_num.Bind(LoadHeapNumberValue(search_element)); | |
| 2241 | |
| 2242 BranchIfFloat64IsNaN(search_num.value(), &return_not_found, ¬_nan_loop); | |
| 2243 | |
| 2244 // Search for HeapNumber | |
| 2245 Bind(¬_nan_loop); | |
| 2246 { | |
| 2247 Label continue_loop(this); | |
| 2248 GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()), | |
| 2249 &return_not_found); | |
| 2250 | |
| 2251 // Load double value or continue if it contains a double hole. | |
| 2252 Node* element_k = LoadFixedDoubleArrayElement( | |
| 2253 elements, index_var.value(), MachineType::Float64(), 0, | |
| 2254 INTPTR_PARAMETERS, &continue_loop); | |
| 2255 | |
| 2256 Branch(Float64Equal(element_k, search_num.value()), &return_found, | |
| 2257 &continue_loop); | |
| 2258 Bind(&continue_loop); | |
| 2259 index_var.Bind(IntPtrAdd(index_var.value(), intptr_one)); | |
| 2260 Goto(¬_nan_loop); | |
| 2261 } | |
| 2262 } | |
| 2263 | |
| 2264 Bind(&return_found); | |
| 2265 Return(SmiTag(index_var.value())); | |
| 2266 | |
| 2267 Bind(&return_not_found); | |
| 2268 Return(NumberConstant(-1)); | |
| 2269 | |
| 2270 Bind(&call_runtime); | |
| 2271 Return(CallRuntime(Runtime::kArrayIndexOf, context, array, search_element, | |
| 2272 start_from)); | |
| 2273 } | |
| 2274 | |
| 2275 class ArrayPrototypeIterationAssembler : public CodeStubAssembler { | |
| 2276 public: | |
| 2277 explicit ArrayPrototypeIterationAssembler(compiler::CodeAssemblerState* state) | |
| 2278 : CodeStubAssembler(state) {} | |
| 2279 | |
| 2280 protected: | |
| 2281 void Generate_ArrayPrototypeIterationMethod(IterationKind iteration_kind) { | |
| 2282 Node* receiver = Parameter(0); | |
| 2283 Node* context = Parameter(3); | |
| 2284 | |
| 2285 Variable var_array(this, MachineRepresentation::kTagged); | |
| 2286 Variable var_map(this, MachineRepresentation::kTagged); | |
| 2287 Variable var_type(this, MachineRepresentation::kWord32); | |
| 2288 | |
| 2289 Label if_isnotobject(this, Label::kDeferred); | |
| 2290 Label create_array_iterator(this); | |
| 2291 | |
| 2292 GotoIf(TaggedIsSmi(receiver), &if_isnotobject); | |
| 2293 var_array.Bind(receiver); | |
| 2294 var_map.Bind(LoadMap(receiver)); | |
| 2295 var_type.Bind(LoadMapInstanceType(var_map.value())); | |
| 2296 Branch(IsJSReceiverInstanceType(var_type.value()), &create_array_iterator, | |
| 2297 &if_isnotobject); | |
| 2298 | |
| 2299 Bind(&if_isnotobject); | |
| 2300 { | |
| 2301 Callable callable = CodeFactory::ToObject(isolate()); | |
| 2302 Node* result = CallStub(callable, context, receiver); | |
| 2303 var_array.Bind(result); | |
| 2304 var_map.Bind(LoadMap(result)); | |
| 2305 var_type.Bind(LoadMapInstanceType(var_map.value())); | |
| 2306 Goto(&create_array_iterator); | |
| 2307 } | |
| 2308 | |
| 2309 Bind(&create_array_iterator); | |
| 2310 Return(CreateArrayIterator(var_array.value(), var_map.value(), | |
| 2311 var_type.value(), context, iteration_kind)); | |
| 2312 } | |
| 2313 }; | |
| 2314 | |
| 2315 TF_BUILTIN(ArrayPrototypeValues, ArrayPrototypeIterationAssembler) { | |
| 2316 Generate_ArrayPrototypeIterationMethod(IterationKind::kValues); | |
| 2317 } | |
| 2318 | |
| 2319 TF_BUILTIN(ArrayPrototypeEntries, ArrayPrototypeIterationAssembler) { | |
| 2320 Generate_ArrayPrototypeIterationMethod(IterationKind::kEntries); | |
| 2321 } | |
| 2322 | |
| 2323 TF_BUILTIN(ArrayPrototypeKeys, ArrayPrototypeIterationAssembler) { | |
| 2324 Generate_ArrayPrototypeIterationMethod(IterationKind::kKeys); | |
| 2325 } | |
| 2326 | |
| 2327 TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) { | |
| 2328 Handle<String> operation = factory()->NewStringFromAsciiChecked( | |
| 2329 "Array Iterator.prototype.next", TENURED); | |
| 2330 | |
| 2331 Node* iterator = Parameter(0); | |
| 2332 Node* context = Parameter(3); | |
| 2333 | |
| 2334 Variable var_value(this, MachineRepresentation::kTagged); | |
| 2335 Variable var_done(this, MachineRepresentation::kTagged); | |
| 2336 | |
| 2337 // Required, or else `throw_bad_receiver` fails a DCHECK due to these | |
| 2338 // variables not being bound along all paths, despite not being used. | |
| 2339 var_done.Bind(TrueConstant()); | |
| 2340 var_value.Bind(UndefinedConstant()); | |
| 2341 | |
| 2342 Label throw_bad_receiver(this, Label::kDeferred); | |
| 2343 Label set_done(this); | |
| 2344 Label allocate_key_result(this); | |
| 2345 Label allocate_entry_if_needed(this); | |
| 2346 Label allocate_iterator_result(this); | |
| 2347 Label generic_values(this); | |
| 2348 | |
| 2349 // If O does not have all of the internal slots of an Array Iterator Instance | |
| 2350 // (22.1.5.3), throw a TypeError exception | |
| 2351 GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver); | |
| 2352 Node* instance_type = LoadInstanceType(iterator); | |
| 2353 GotoIf( | |
| 2354 Uint32LessThan( | |
| 2355 Int32Constant(LAST_ARRAY_ITERATOR_TYPE - FIRST_ARRAY_ITERATOR_TYPE), | |
| 2356 Int32Sub(instance_type, Int32Constant(FIRST_ARRAY_ITERATOR_TYPE))), | |
| 2357 &throw_bad_receiver); | |
| 2358 | |
| 2359 // Let a be O.[[IteratedObject]]. | |
| 2360 Node* array = | |
| 2361 LoadObjectField(iterator, JSArrayIterator::kIteratedObjectOffset); | |
| 2362 | |
| 2363 // Let index be O.[[ArrayIteratorNextIndex]]. | |
| 2364 Node* index = LoadObjectField(iterator, JSArrayIterator::kNextIndexOffset); | |
| 2365 Node* orig_map = | |
| 2366 LoadObjectField(iterator, JSArrayIterator::kIteratedObjectMapOffset); | |
| 2367 Node* array_map = LoadMap(array); | |
| 2368 | |
| 2369 Label if_isfastarray(this), if_isnotfastarray(this), | |
| 2370 if_isdetached(this, Label::kDeferred); | |
| 2371 | |
| 2372 Branch(WordEqual(orig_map, array_map), &if_isfastarray, &if_isnotfastarray); | |
| 2373 | |
| 2374 Bind(&if_isfastarray); | |
| 2375 { | |
| 2376 CSA_ASSERT(this, Word32Equal(LoadMapInstanceType(array_map), | |
| 2377 Int32Constant(JS_ARRAY_TYPE))); | |
| 2378 | |
| 2379 Node* length = LoadObjectField(array, JSArray::kLengthOffset); | |
| 2380 | |
| 2381 CSA_ASSERT(this, TaggedIsSmi(length)); | |
| 2382 CSA_ASSERT(this, TaggedIsSmi(index)); | |
| 2383 | |
| 2384 GotoIfNot(SmiBelow(index, length), &set_done); | |
| 2385 | |
| 2386 Node* one = SmiConstant(Smi::FromInt(1)); | |
| 2387 StoreObjectFieldNoWriteBarrier(iterator, JSArrayIterator::kNextIndexOffset, | |
| 2388 SmiAdd(index, one)); | |
| 2389 | |
| 2390 var_done.Bind(FalseConstant()); | |
| 2391 Node* elements = LoadElements(array); | |
| 2392 | |
| 2393 static int32_t kInstanceType[] = { | |
| 2394 JS_FAST_ARRAY_KEY_ITERATOR_TYPE, | |
| 2395 JS_FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2396 JS_FAST_HOLEY_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2397 JS_FAST_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2398 JS_FAST_HOLEY_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2399 JS_FAST_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2400 JS_FAST_HOLEY_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2401 JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2402 JS_FAST_HOLEY_SMI_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2403 JS_FAST_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2404 JS_FAST_HOLEY_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2405 JS_FAST_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2406 JS_FAST_HOLEY_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2407 }; | |
| 2408 | |
| 2409 Label packed_object_values(this), holey_object_values(this), | |
| 2410 packed_double_values(this), holey_double_values(this); | |
| 2411 Label* kInstanceTypeHandlers[] = { | |
| 2412 &allocate_key_result, &packed_object_values, &holey_object_values, | |
| 2413 &packed_object_values, &holey_object_values, &packed_double_values, | |
| 2414 &holey_double_values, &packed_object_values, &holey_object_values, | |
| 2415 &packed_object_values, &holey_object_values, &packed_double_values, | |
| 2416 &holey_double_values}; | |
| 2417 | |
| 2418 Switch(instance_type, &throw_bad_receiver, kInstanceType, | |
| 2419 kInstanceTypeHandlers, arraysize(kInstanceType)); | |
| 2420 | |
| 2421 Bind(&packed_object_values); | |
| 2422 { | |
| 2423 var_value.Bind(LoadFixedArrayElement(elements, index, 0, SMI_PARAMETERS)); | |
| 2424 Goto(&allocate_entry_if_needed); | |
| 2425 } | |
| 2426 | |
| 2427 Bind(&packed_double_values); | |
| 2428 { | |
| 2429 Node* value = LoadFixedDoubleArrayElement( | |
| 2430 elements, index, MachineType::Float64(), 0, SMI_PARAMETERS); | |
| 2431 var_value.Bind(AllocateHeapNumberWithValue(value)); | |
| 2432 Goto(&allocate_entry_if_needed); | |
| 2433 } | |
| 2434 | |
| 2435 Bind(&holey_object_values); | |
| 2436 { | |
| 2437 // Check the array_protector cell, and take the slow path if it's invalid. | |
| 2438 Node* invalid = SmiConstant(Smi::FromInt(Isolate::kProtectorInvalid)); | |
| 2439 Node* cell = LoadRoot(Heap::kArrayProtectorRootIndex); | |
| 2440 Node* cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |
| 2441 GotoIf(WordEqual(cell_value, invalid), &generic_values); | |
| 2442 | |
| 2443 var_value.Bind(UndefinedConstant()); | |
| 2444 Node* value = LoadFixedArrayElement(elements, index, 0, SMI_PARAMETERS); | |
| 2445 GotoIf(WordEqual(value, TheHoleConstant()), &allocate_entry_if_needed); | |
| 2446 var_value.Bind(value); | |
| 2447 Goto(&allocate_entry_if_needed); | |
| 2448 } | |
| 2449 | |
| 2450 Bind(&holey_double_values); | |
| 2451 { | |
| 2452 // Check the array_protector cell, and take the slow path if it's invalid. | |
| 2453 Node* invalid = SmiConstant(Smi::FromInt(Isolate::kProtectorInvalid)); | |
| 2454 Node* cell = LoadRoot(Heap::kArrayProtectorRootIndex); | |
| 2455 Node* cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |
| 2456 GotoIf(WordEqual(cell_value, invalid), &generic_values); | |
| 2457 | |
| 2458 var_value.Bind(UndefinedConstant()); | |
| 2459 Node* value = LoadFixedDoubleArrayElement( | |
| 2460 elements, index, MachineType::Float64(), 0, SMI_PARAMETERS, | |
| 2461 &allocate_entry_if_needed); | |
| 2462 var_value.Bind(AllocateHeapNumberWithValue(value)); | |
| 2463 Goto(&allocate_entry_if_needed); | |
| 2464 } | |
| 2465 } | |
| 2466 | |
| 2467 Bind(&if_isnotfastarray); | |
| 2468 { | |
| 2469 Label if_istypedarray(this), if_isgeneric(this); | |
| 2470 | |
| 2471 // If a is undefined, return CreateIterResultObject(undefined, true) | |
| 2472 GotoIf(WordEqual(array, UndefinedConstant()), &allocate_iterator_result); | |
| 2473 | |
| 2474 Node* array_type = LoadInstanceType(array); | |
| 2475 Branch(Word32Equal(array_type, Int32Constant(JS_TYPED_ARRAY_TYPE)), | |
| 2476 &if_istypedarray, &if_isgeneric); | |
| 2477 | |
| 2478 Bind(&if_isgeneric); | |
| 2479 { | |
| 2480 Label if_wasfastarray(this); | |
| 2481 | |
| 2482 Node* length = nullptr; | |
| 2483 { | |
| 2484 Variable var_length(this, MachineRepresentation::kTagged); | |
| 2485 Label if_isarray(this), if_isnotarray(this), done(this); | |
| 2486 Branch(Word32Equal(array_type, Int32Constant(JS_ARRAY_TYPE)), | |
| 2487 &if_isarray, &if_isnotarray); | |
| 2488 | |
| 2489 Bind(&if_isarray); | |
| 2490 { | |
| 2491 var_length.Bind(LoadObjectField(array, JSArray::kLengthOffset)); | |
| 2492 | |
| 2493 // Invalidate protector cell if needed | |
| 2494 Branch(WordNotEqual(orig_map, UndefinedConstant()), &if_wasfastarray, | |
| 2495 &done); | |
| 2496 | |
| 2497 Bind(&if_wasfastarray); | |
| 2498 { | |
| 2499 Label if_invalid(this, Label::kDeferred); | |
| 2500 // A fast array iterator transitioned to a slow iterator during | |
| 2501 // iteration. Invalidate fast_array_iteration_prtoector cell to | |
| 2502 // prevent potential deopt loops. | |
| 2503 StoreObjectFieldNoWriteBarrier( | |
| 2504 iterator, JSArrayIterator::kIteratedObjectMapOffset, | |
| 2505 UndefinedConstant()); | |
| 2506 GotoIf(Uint32LessThanOrEqual( | |
| 2507 instance_type, | |
| 2508 Int32Constant(JS_GENERIC_ARRAY_KEY_ITERATOR_TYPE)), | |
| 2509 &done); | |
| 2510 | |
| 2511 Node* invalid = | |
| 2512 SmiConstant(Smi::FromInt(Isolate::kProtectorInvalid)); | |
| 2513 Node* cell = LoadRoot(Heap::kFastArrayIterationProtectorRootIndex); | |
| 2514 StoreObjectFieldNoWriteBarrier(cell, Cell::kValueOffset, invalid); | |
| 2515 Goto(&done); | |
| 2516 } | |
| 2517 } | |
| 2518 | |
| 2519 Bind(&if_isnotarray); | |
| 2520 { | |
| 2521 Node* length = | |
| 2522 GetProperty(context, array, factory()->length_string()); | |
| 2523 Callable to_length = CodeFactory::ToLength(isolate()); | |
| 2524 var_length.Bind(CallStub(to_length, context, length)); | |
| 2525 Goto(&done); | |
| 2526 } | |
| 2527 | |
| 2528 Bind(&done); | |
| 2529 length = var_length.value(); | |
| 2530 } | |
| 2531 | |
| 2532 GotoUnlessNumberLessThan(index, length, &set_done); | |
| 2533 | |
| 2534 StoreObjectField(iterator, JSArrayIterator::kNextIndexOffset, | |
| 2535 NumberInc(index)); | |
| 2536 var_done.Bind(FalseConstant()); | |
| 2537 | |
| 2538 Branch( | |
| 2539 Uint32LessThanOrEqual( | |
| 2540 instance_type, Int32Constant(JS_GENERIC_ARRAY_KEY_ITERATOR_TYPE)), | |
| 2541 &allocate_key_result, &generic_values); | |
| 2542 | |
| 2543 Bind(&generic_values); | |
| 2544 { | |
| 2545 var_value.Bind(GetProperty(context, array, index)); | |
| 2546 Goto(&allocate_entry_if_needed); | |
| 2547 } | |
| 2548 } | |
| 2549 | |
| 2550 Bind(&if_istypedarray); | |
| 2551 { | |
| 2552 Node* buffer = LoadObjectField(array, JSTypedArray::kBufferOffset); | |
| 2553 GotoIf(IsDetachedBuffer(buffer), &if_isdetached); | |
| 2554 | |
| 2555 Node* length = LoadObjectField(array, JSTypedArray::kLengthOffset); | |
| 2556 | |
| 2557 CSA_ASSERT(this, TaggedIsSmi(length)); | |
| 2558 CSA_ASSERT(this, TaggedIsSmi(index)); | |
| 2559 | |
| 2560 GotoIfNot(SmiBelow(index, length), &set_done); | |
| 2561 | |
| 2562 Node* one = SmiConstant(1); | |
| 2563 StoreObjectFieldNoWriteBarrier( | |
| 2564 iterator, JSArrayIterator::kNextIndexOffset, SmiAdd(index, one)); | |
| 2565 var_done.Bind(FalseConstant()); | |
| 2566 | |
| 2567 Node* elements = LoadElements(array); | |
| 2568 Node* base_ptr = | |
| 2569 LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset); | |
| 2570 Node* external_ptr = | |
| 2571 LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset, | |
| 2572 MachineType::Pointer()); | |
| 2573 Node* data_ptr = IntPtrAdd(BitcastTaggedToWord(base_ptr), external_ptr); | |
| 2574 | |
| 2575 static int32_t kInstanceType[] = { | |
| 2576 JS_TYPED_ARRAY_KEY_ITERATOR_TYPE, | |
| 2577 JS_UINT8_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2578 JS_UINT8_CLAMPED_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2579 JS_INT8_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2580 JS_UINT16_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2581 JS_INT16_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2582 JS_UINT32_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2583 JS_INT32_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2584 JS_FLOAT32_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2585 JS_FLOAT64_ARRAY_KEY_VALUE_ITERATOR_TYPE, | |
| 2586 JS_UINT8_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2587 JS_UINT8_CLAMPED_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2588 JS_INT8_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2589 JS_UINT16_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2590 JS_INT16_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2591 JS_UINT32_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2592 JS_INT32_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2593 JS_FLOAT32_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2594 JS_FLOAT64_ARRAY_VALUE_ITERATOR_TYPE, | |
| 2595 }; | |
| 2596 | |
| 2597 Label uint8_values(this), int8_values(this), uint16_values(this), | |
| 2598 int16_values(this), uint32_values(this), int32_values(this), | |
| 2599 float32_values(this), float64_values(this); | |
| 2600 Label* kInstanceTypeHandlers[] = { | |
| 2601 &allocate_key_result, &uint8_values, &uint8_values, | |
| 2602 &int8_values, &uint16_values, &int16_values, | |
| 2603 &uint32_values, &int32_values, &float32_values, | |
| 2604 &float64_values, &uint8_values, &uint8_values, | |
| 2605 &int8_values, &uint16_values, &int16_values, | |
| 2606 &uint32_values, &int32_values, &float32_values, | |
| 2607 &float64_values, | |
| 2608 }; | |
| 2609 | |
| 2610 var_done.Bind(FalseConstant()); | |
| 2611 Switch(instance_type, &throw_bad_receiver, kInstanceType, | |
| 2612 kInstanceTypeHandlers, arraysize(kInstanceType)); | |
| 2613 | |
| 2614 Bind(&uint8_values); | |
| 2615 { | |
| 2616 Node* value_uint8 = LoadFixedTypedArrayElement( | |
| 2617 data_ptr, index, UINT8_ELEMENTS, SMI_PARAMETERS); | |
| 2618 var_value.Bind(SmiFromWord32(value_uint8)); | |
| 2619 Goto(&allocate_entry_if_needed); | |
| 2620 } | |
| 2621 Bind(&int8_values); | |
| 2622 { | |
| 2623 Node* value_int8 = LoadFixedTypedArrayElement( | |
| 2624 data_ptr, index, INT8_ELEMENTS, SMI_PARAMETERS); | |
| 2625 var_value.Bind(SmiFromWord32(value_int8)); | |
| 2626 Goto(&allocate_entry_if_needed); | |
| 2627 } | |
| 2628 Bind(&uint16_values); | |
| 2629 { | |
| 2630 Node* value_uint16 = LoadFixedTypedArrayElement( | |
| 2631 data_ptr, index, UINT16_ELEMENTS, SMI_PARAMETERS); | |
| 2632 var_value.Bind(SmiFromWord32(value_uint16)); | |
| 2633 Goto(&allocate_entry_if_needed); | |
| 2634 } | |
| 2635 Bind(&int16_values); | |
| 2636 { | |
| 2637 Node* value_int16 = LoadFixedTypedArrayElement( | |
| 2638 data_ptr, index, INT16_ELEMENTS, SMI_PARAMETERS); | |
| 2639 var_value.Bind(SmiFromWord32(value_int16)); | |
| 2640 Goto(&allocate_entry_if_needed); | |
| 2641 } | |
| 2642 Bind(&uint32_values); | |
| 2643 { | |
| 2644 Node* value_uint32 = LoadFixedTypedArrayElement( | |
| 2645 data_ptr, index, UINT32_ELEMENTS, SMI_PARAMETERS); | |
| 2646 var_value.Bind(ChangeUint32ToTagged(value_uint32)); | |
| 2647 Goto(&allocate_entry_if_needed); | |
| 2648 } | |
| 2649 Bind(&int32_values); | |
| 2650 { | |
| 2651 Node* value_int32 = LoadFixedTypedArrayElement( | |
| 2652 data_ptr, index, INT32_ELEMENTS, SMI_PARAMETERS); | |
| 2653 var_value.Bind(ChangeInt32ToTagged(value_int32)); | |
| 2654 Goto(&allocate_entry_if_needed); | |
| 2655 } | |
| 2656 Bind(&float32_values); | |
| 2657 { | |
| 2658 Node* value_float32 = LoadFixedTypedArrayElement( | |
| 2659 data_ptr, index, FLOAT32_ELEMENTS, SMI_PARAMETERS); | |
| 2660 var_value.Bind( | |
| 2661 AllocateHeapNumberWithValue(ChangeFloat32ToFloat64(value_float32))); | |
| 2662 Goto(&allocate_entry_if_needed); | |
| 2663 } | |
| 2664 Bind(&float64_values); | |
| 2665 { | |
| 2666 Node* value_float64 = LoadFixedTypedArrayElement( | |
| 2667 data_ptr, index, FLOAT64_ELEMENTS, SMI_PARAMETERS); | |
| 2668 var_value.Bind(AllocateHeapNumberWithValue(value_float64)); | |
| 2669 Goto(&allocate_entry_if_needed); | |
| 2670 } | |
| 2671 } | |
| 2672 } | |
| 2673 | |
| 2674 Bind(&set_done); | |
| 2675 { | |
| 2676 StoreObjectFieldNoWriteBarrier( | |
| 2677 iterator, JSArrayIterator::kIteratedObjectOffset, UndefinedConstant()); | |
| 2678 Goto(&allocate_iterator_result); | |
| 2679 } | |
| 2680 | |
| 2681 Bind(&allocate_key_result); | |
| 2682 { | |
| 2683 var_value.Bind(index); | |
| 2684 var_done.Bind(FalseConstant()); | |
| 2685 Goto(&allocate_iterator_result); | |
| 2686 } | |
| 2687 | |
| 2688 Bind(&allocate_entry_if_needed); | |
| 2689 { | |
| 2690 GotoIf(Int32GreaterThan(instance_type, | |
| 2691 Int32Constant(LAST_ARRAY_KEY_VALUE_ITERATOR_TYPE)), | |
| 2692 &allocate_iterator_result); | |
| 2693 | |
| 2694 Node* elements = AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
| 2695 StoreFixedArrayElement(elements, 0, index, SKIP_WRITE_BARRIER); | |
| 2696 StoreFixedArrayElement(elements, 1, var_value.value(), SKIP_WRITE_BARRIER); | |
| 2697 | |
| 2698 Node* entry = Allocate(JSArray::kSize); | |
| 2699 Node* map = LoadContextElement(LoadNativeContext(context), | |
| 2700 Context::JS_ARRAY_FAST_ELEMENTS_MAP_INDEX); | |
| 2701 | |
| 2702 StoreMapNoWriteBarrier(entry, map); | |
| 2703 StoreObjectFieldRoot(entry, JSArray::kPropertiesOffset, | |
| 2704 Heap::kEmptyFixedArrayRootIndex); | |
| 2705 StoreObjectFieldNoWriteBarrier(entry, JSArray::kElementsOffset, elements); | |
| 2706 StoreObjectFieldNoWriteBarrier(entry, JSArray::kLengthOffset, | |
| 2707 SmiConstant(Smi::FromInt(2))); | |
| 2708 | |
| 2709 var_value.Bind(entry); | |
| 2710 Goto(&allocate_iterator_result); | |
| 2711 } | |
| 2712 | |
| 2713 Bind(&allocate_iterator_result); | |
| 2714 { | |
| 2715 Node* result = Allocate(JSIteratorResult::kSize); | |
| 2716 Node* map = LoadContextElement(LoadNativeContext(context), | |
| 2717 Context::ITERATOR_RESULT_MAP_INDEX); | |
| 2718 StoreMapNoWriteBarrier(result, map); | |
| 2719 StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset, | |
| 2720 Heap::kEmptyFixedArrayRootIndex); | |
| 2721 StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset, | |
| 2722 Heap::kEmptyFixedArrayRootIndex); | |
| 2723 StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset, | |
| 2724 var_value.value()); | |
| 2725 StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset, | |
| 2726 var_done.value()); | |
| 2727 Return(result); | |
| 2728 } | |
| 2729 | |
| 2730 Bind(&throw_bad_receiver); | |
| 2731 { | |
| 2732 // The {receiver} is not a valid JSArrayIterator. | |
| 2733 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, | |
| 2734 HeapConstant(operation), iterator); | |
| 2735 Unreachable(); | |
| 2736 } | |
| 2737 | |
| 2738 Bind(&if_isdetached); | |
| 2739 { | |
| 2740 Node* message = SmiConstant(MessageTemplate::kDetachedOperation); | |
| 2741 CallRuntime(Runtime::kThrowTypeError, context, message, | |
| 2742 HeapConstant(operation)); | |
| 2743 Unreachable(); | |
| 2744 } | |
| 2745 } | |
| 2746 | |
| 2747 } // namespace internal | 1242 } // namespace internal |
| 2748 } // namespace v8 | 1243 } // namespace v8 |
| OLD | NEW |