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 408 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
419 | 419 |
420 if (JSArray::HasReadOnlyLength(array)) { | 420 if (JSArray::HasReadOnlyLength(array)) { |
421 return CallJsIntrinsic(isolate, isolate->array_unshift(), args); | 421 return CallJsIntrinsic(isolate, isolate->array_unshift(), args); |
422 } | 422 } |
423 | 423 |
424 ElementsAccessor* accessor = array->GetElementsAccessor(); | 424 ElementsAccessor* accessor = array->GetElementsAccessor(); |
425 int new_length = accessor->Unshift(array, &args, to_add); | 425 int new_length = accessor->Unshift(array, &args, to_add); |
426 return Smi::FromInt(new_length); | 426 return Smi::FromInt(new_length); |
427 } | 427 } |
428 | 428 |
429 class ForEachCodeStubAssembler : public CodeStubAssembler { | |
430 public: | |
431 explicit ForEachCodeStubAssembler(compiler::CodeAssemblerState* state) | |
432 : CodeStubAssembler(state) {} | |
433 | |
434 void VisitOneElement(Node* context, Node* this_arg, Node* o, Node* k, | |
435 Node* callbackfn) { | |
436 Comment("begin VisitOneElement"); | |
437 | |
438 // a. Let Pk be ToString(k). | |
439 Node* p_k = ToString(context, k); | |
440 | |
441 // b. Let kPresent be HasProperty(O, Pk). | |
442 // c. ReturnIfAbrupt(kPresent). | |
443 Node* k_present = | |
444 CallStub(CodeFactory::HasProperty(isolate()), context, p_k, o); | |
445 | |
446 // d. If kPresent is true, then | |
447 Label not_present(this); | |
448 GotoIf(WordNotEqual(k_present, TrueConstant()), ¬_present); | |
449 | |
450 // i. Let kValue be Get(O, Pk). | |
451 // ii. ReturnIfAbrupt(kValue). | |
452 Node* k_value = | |
453 CallStub(CodeFactory::GetProperty(isolate()), context, o, k); | |
454 | |
455 // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»). | |
456 // iv. ReturnIfAbrupt(funcResult). | |
457 CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg, k_value, | |
458 k, o); | |
459 | |
460 Goto(¬_present); | |
461 Bind(¬_present); | |
462 Comment("end VisitOneElement"); | |
463 } | |
464 | |
465 void VisitAllFastElements(Node* context, ElementsKind kind, Node* this_arg, | |
466 Node* o, Node* len, Node* callbackfn, | |
467 ParameterMode mode) { | |
468 Comment("begin VisitAllFastElements"); | |
469 Variable original_map(this, MachineRepresentation::kTagged); | |
470 original_map.Bind(LoadMap(o)); | |
471 VariableList list({&original_map}, zone()); | |
472 BuildFastLoop( | |
473 list, IntPtrOrSmiConstant(0, mode), TaggedToParameter(len, mode), | |
474 [context, kind, this, o, &original_map, callbackfn, this_arg, | |
475 mode](Node* index) { | |
476 Label one_element_done(this), array_changed(this, Label::kDeferred); | |
477 | |
478 // Check if o's map has changed during the callback. If so, we have to | |
479 // fall back to the slower spec implementation for the rest of the | |
480 // iteration. | |
481 GotoIf(WordNotEqual(LoadMap(o), original_map.value()), | |
482 &array_changed); | |
483 | |
484 // Check if o's length has changed during the callback and if the | |
485 // index is now out of range of the new length. | |
486 Node* tagged_index = ParameterToTagged(index, mode); | |
487 GotoIf(SmiGreaterThanOrEqual(tagged_index, LoadJSArrayLength(o)), | |
488 &array_changed); | |
489 | |
490 // Re-load the elements array. If may have been resized. | |
491 Node* elements = LoadElements(o); | |
492 | |
493 // Fast case: load the element directly from the elements FixedArray | |
494 // and call the callback if the element is not the hole. | |
495 DCHECK(kind == FAST_ELEMENTS || kind == FAST_DOUBLE_ELEMENTS); | |
496 int base_size = kind == FAST_ELEMENTS | |
497 ? FixedArray::kHeaderSize | |
498 : (FixedArray::kHeaderSize - kHeapObjectTag); | |
499 Node* offset = ElementOffsetFromIndex(index, kind, mode, base_size); | |
500 Node* value = nullptr; | |
501 if (kind == FAST_ELEMENTS) { | |
502 value = LoadObjectField(elements, offset); | |
503 GotoIf(WordEqual(value, TheHoleConstant()), &one_element_done); | |
504 } else { | |
505 Node* double_value = | |
506 LoadDoubleWithHoleCheck(elements, offset, &one_element_done); | |
507 value = AllocateHeapNumberWithValue(double_value); | |
508 } | |
Benedikt Meurer
2017/02/02 05:22:38
You need to handle holey arrays here. I.e. upon re
danno
2017/02/02 17:15:48
Done.
| |
509 CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg, | |
510 value, tagged_index, o); | |
511 Goto(&one_element_done); | |
512 | |
513 // O's changed during the forEach. Use the implementation precisely | |
514 // specified in the spec for the rest of the iteration, also making | |
515 // the failed original_map sticky in case of a subseuent change that | |
516 // goes back to the original map. | |
517 Bind(&array_changed); | |
518 VisitOneElement(context, this_arg, o, ParameterToTagged(index, mode), | |
519 callbackfn); | |
520 original_map.Bind(UndefinedConstant()); | |
521 Goto(&one_element_done); | |
522 | |
523 Bind(&one_element_done); | |
524 }, | |
525 1, mode, IndexAdvanceMode::kPost); | |
526 Comment("end VisitAllFastElements"); | |
527 } | |
528 }; | |
529 | |
530 TF_BUILTIN(ArrayForEach, ForEachCodeStubAssembler) { | |
531 Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); | |
532 Node* context = Parameter(BuiltinDescriptor::kContext); | |
533 Label non_array(this), examine_elements(this), fast_elements(this), | |
534 slow(this), maybe_double_elements(this), fast_double_elements(this); | |
535 | |
536 Node* argc_intptr = ChangeInt32ToIntPtr(argc); | |
537 CodeStubArguments args(this, argc_intptr); | |
538 Node* receiver = args.GetReceiver(); | |
539 | |
540 Variable callbackfn(this, MachineRepresentation::kTagged); | |
Benedikt Meurer
2017/02/02 05:22:38
Wouldn't it be easier to just use the arguments ad
danno
2017/02/02 17:15:48
Done.
| |
541 Label no_callbackfn(this); | |
542 callbackfn.Bind(UndefinedConstant()); | |
543 GotoIf(WordEqual(argc_intptr, IntPtrConstant(0)), &no_callbackfn); | |
544 callbackfn.Bind(args.AtIndex(0)); | |
545 Goto(&no_callbackfn); | |
546 Bind(&no_callbackfn); | |
547 | |
548 // TODO(danno): Seriously? Do we really need to throw the exact error message | |
Benedikt Meurer
2017/02/02 05:22:38
I think it's fine to just let ToObject fail and re
danno
2017/02/02 17:15:48
For consistency with the other Array builtins, I'm
| |
549 // on null and undefined so that the webkit tests pass? | |
550 Label throw_null_undefined_exception(this, Label::kDeferred); | |
551 GotoIf(WordEqual(receiver, NullConstant()), &throw_null_undefined_exception); | |
552 GotoIf(WordEqual(receiver, UndefinedConstant()), | |
553 &throw_null_undefined_exception); | |
554 | |
555 // By the book: taken directly from the ECMAScript 2015 specification | |
556 | |
557 // 1. Let O be ToObject(this value). | |
558 // 2. ReturnIfAbrupt(O) | |
559 Node* o = CallStub(CodeFactory::ToObject(isolate()), context, receiver); | |
560 | |
561 // 3. Let len be ToLength(Get(O, "length")). | |
562 // 4. ReturnIfAbrupt(len). | |
563 Variable merged_length(this, MachineRepresentation::kTagged); | |
564 Label has_length(this, &merged_length), not_js_array(this); | |
565 GotoIf(DoesntHaveInstanceType(o, JS_ARRAY_TYPE), ¬_js_array); | |
566 merged_length.Bind(LoadJSArrayLength(o)); | |
567 Goto(&has_length); | |
568 Bind(¬_js_array); | |
569 Node* len_property = | |
570 CallStub(CodeFactory::GetProperty(isolate()), context, o, | |
571 HeapConstant(isolate()->factory()->length_string())); | |
572 merged_length.Bind( | |
573 CallStub(CodeFactory::ToLength(isolate()), context, len_property)); | |
574 Goto(&has_length); | |
575 Bind(&has_length); | |
576 Node* len = merged_length.value(); | |
577 | |
578 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. | |
579 Label type_exception(this, Label::kDeferred); | |
580 GotoIf(TaggedIsSmi(callbackfn.value()), &type_exception); | |
581 GotoUnless(IsCallableMap(LoadMap(callbackfn.value())), &type_exception); | |
582 | |
583 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. | |
584 Variable this_arg(this, MachineRepresentation::kTagged); | |
585 this_arg.Bind(UndefinedConstant()); | |
586 Label this_arg_done(this); | |
587 GotoIf(WordEqual(argc_intptr, IntPtrConstant(1)), &this_arg_done); | |
588 this_arg.Bind(args.AtIndex(1)); | |
589 Goto(&this_arg_done); | |
590 Bind(&this_arg_done); | |
591 | |
592 // Non-smi lengths must use the slow path. | |
593 GotoIf(TaggedIsNotSmi(len), &slow); | |
594 | |
595 BranchIfFastJSArray(o, context, | |
596 CodeStubAssembler::FastJSArrayAccessMode::INBOUNDS_READ, | |
597 &examine_elements, &slow); | |
598 | |
599 Bind(&examine_elements); | |
600 | |
601 ParameterMode mode = OptimalParameterMode(); | |
602 | |
603 // Select by ElementsKind | |
604 Node* o_map = LoadMap(o); | |
605 Node* bit_field2 = LoadMapBitField2(o_map); | |
606 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); | |
607 Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)), | |
608 &maybe_double_elements, &fast_elements); | |
609 | |
610 Bind(&fast_elements); | |
611 { | |
612 VisitAllFastElements(context, FAST_ELEMENTS, this_arg.value(), o, len, | |
613 callbackfn.value(), mode); | |
614 | |
615 // No exception, return success | |
616 args.PopAndReturn(UndefinedConstant()); | |
617 } | |
618 | |
619 Bind(&maybe_double_elements); | |
620 Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_DOUBLE_ELEMENTS)), | |
621 &slow, &fast_double_elements); | |
622 | |
623 Bind(&fast_double_elements); | |
624 { | |
625 VisitAllFastElements(context, FAST_DOUBLE_ELEMENTS, this_arg.value(), o, | |
626 len, callbackfn.value(), mode); | |
627 | |
628 // No exception, return success | |
629 args.PopAndReturn(UndefinedConstant()); | |
630 } | |
631 | |
632 Bind(&slow); | |
633 { | |
634 // By the book: taken from the ECMAScript 2015 specification (cont.) | |
635 | |
636 // 7. Let k be 0. | |
637 Variable k(this, MachineRepresentation::kTagged); | |
638 k.Bind(SmiConstant(0)); | |
639 | |
640 // 8. Repeat, while k < len | |
641 Label loop(this, &k); | |
642 Label after_loop(this); | |
643 Goto(&loop); | |
644 Bind(&loop); | |
645 { | |
646 GotoUnlessNumberLessThan(k.value(), len, &after_loop); | |
647 | |
648 VisitOneElement(context, this_arg.value(), o, k.value(), | |
649 callbackfn.value()); | |
650 | |
651 // e. Increase k by 1. | |
652 k.Bind(NumberInc(k.value())); | |
653 Goto(&loop); | |
654 } | |
655 Bind(&after_loop); | |
656 args.PopAndReturn(UndefinedConstant()); | |
657 } | |
658 | |
659 Bind(&throw_null_undefined_exception); | |
660 { | |
661 CallRuntime(Runtime::kThrowTypeError, context, | |
662 SmiConstant(MessageTemplate::kCalledOnNullOrUndefined), | |
663 HeapConstant(isolate()->factory()->NewStringFromAsciiChecked( | |
664 "Array.prototype.forEach"))); | |
665 args.PopAndReturn(UndefinedConstant()); | |
666 } | |
667 | |
668 Bind(&type_exception); | |
669 { | |
670 CallRuntime(Runtime::kThrowTypeError, context, | |
671 SmiConstant(MessageTemplate::kCalledNonCallable), | |
672 callbackfn.value()); | |
673 args.PopAndReturn(UndefinedConstant()); | |
674 } | |
675 } | |
676 | |
429 BUILTIN(ArraySlice) { | 677 BUILTIN(ArraySlice) { |
430 HandleScope scope(isolate); | 678 HandleScope scope(isolate); |
431 Handle<Object> receiver = args.receiver(); | 679 Handle<Object> receiver = args.receiver(); |
432 int len = -1; | 680 int len = -1; |
433 int relative_start = 0; | 681 int relative_start = 0; |
434 int relative_end = 0; | 682 int relative_end = 0; |
435 | 683 |
436 if (receiver->IsJSArray()) { | 684 if (receiver->IsJSArray()) { |
437 DisallowHeapAllocation no_gc; | 685 DisallowHeapAllocation no_gc; |
438 JSArray* array = JSArray::cast(*receiver); | 686 JSArray* array = JSArray::cast(*receiver); |
(...skipping 2333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2772 Node* message = assembler.SmiConstant(MessageTemplate::kDetachedOperation); | 3020 Node* message = assembler.SmiConstant(MessageTemplate::kDetachedOperation); |
2773 Node* result = | 3021 Node* result = |
2774 assembler.CallRuntime(Runtime::kThrowTypeError, context, message, | 3022 assembler.CallRuntime(Runtime::kThrowTypeError, context, message, |
2775 assembler.HeapConstant(operation)); | 3023 assembler.HeapConstant(operation)); |
2776 assembler.Return(result); | 3024 assembler.Return(result); |
2777 } | 3025 } |
2778 } | 3026 } |
2779 | 3027 |
2780 } // namespace internal | 3028 } // namespace internal |
2781 } // namespace v8 | 3029 } // namespace v8 |
OLD | NEW |