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-utils.h" | 5 #include "src/builtins/builtins-utils.h" |
6 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" |
7 | 7 |
8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
9 #include "src/promise-utils.h" | 9 #include "src/promise-utils.h" |
10 | 10 |
11 namespace v8 { | 11 namespace v8 { |
12 namespace internal { | 12 namespace internal { |
13 | 13 |
14 // ES#sec-promise-resolve-functions | |
15 // Promise Resolve Functions | |
16 BUILTIN(PromiseResolveClosure) { | |
17 HandleScope scope(isolate); | |
18 | |
19 Handle<Context> context(isolate->context(), isolate); | |
20 | |
21 if (PromiseUtils::HasAlreadyVisited(context)) { | |
22 return isolate->heap()->undefined_value(); | |
23 } | |
24 | |
25 PromiseUtils::SetAlreadyVisited(context); | |
26 Handle<JSObject> promise = handle(PromiseUtils::GetPromise(context), isolate); | |
27 Handle<Object> value = args.atOrUndefined(isolate, 1); | |
28 | |
29 MaybeHandle<Object> maybe_result; | |
30 Handle<Object> argv[] = {promise, value}; | |
31 RETURN_FAILURE_ON_EXCEPTION( | |
32 isolate, Execution::Call(isolate, isolate->promise_resolve(), | |
33 isolate->factory()->undefined_value(), | |
34 arraysize(argv), argv)); | |
35 return isolate->heap()->undefined_value(); | |
36 } | |
37 | |
38 // ES#sec-promise-reject-functions | 14 // ES#sec-promise-reject-functions |
39 // Promise Reject Functions | 15 // Promise Reject Functions |
40 BUILTIN(PromiseRejectClosure) { | 16 BUILTIN(PromiseRejectClosure) { |
41 HandleScope scope(isolate); | 17 HandleScope scope(isolate); |
42 | 18 |
43 Handle<Context> context(isolate->context(), isolate); | 19 Handle<Context> context(isolate->context(), isolate); |
44 | 20 |
45 if (PromiseUtils::HasAlreadyVisited(context)) { | 21 if (PromiseUtils::HasAlreadyVisited(context)) { |
46 return isolate->heap()->undefined_value(); | 22 return isolate->heap()->undefined_value(); |
47 } | 23 } |
(...skipping 610 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
658 // deal with deferred objects. | 634 // deal with deferred objects. |
659 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 635 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
660 Node* const key = | 636 Node* const key = |
661 a.HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise")); | 637 a.HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise")); |
662 Node* const result = | 638 Node* const result = |
663 a.CallStub(getproperty_callable, context, var_deferred.value(), key); | 639 a.CallStub(getproperty_callable, context, var_deferred.value(), key); |
664 | 640 |
665 a.Return(result); | 641 a.Return(result); |
666 } | 642 } |
667 | 643 |
644 namespace { | |
645 // RegExp fast path implementations rely on unmodified JSPromise instances. | |
jgruber
2016/12/05 09:25:17
Promise
gsathya
2016/12/05 16:03:05
Done.
| |
646 // We use a fairly coarse granularity for this and simply check whether both | |
647 // the promise itself is unmodified (i.e. its map has not changed) and its | |
648 // prototype is unmodified. | |
649 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp | |
650 void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context, | |
651 compiler::Node* promise, | |
652 CodeStubAssembler::Label* if_isunmodified, | |
653 CodeStubAssembler::Label* if_ismodified) { | |
654 typedef compiler::Node Node; | |
655 | |
656 // TODO(gsathya): Assert if promise is receiver | |
657 Node* const map = a->LoadMap(promise); | |
658 Node* const native_context = a->LoadNativeContext(context); | |
659 Node* const promise_fun = | |
660 a->LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
661 Node* const initial_map = | |
662 a->LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
663 Node* const has_initialmap = a->WordEqual(map, initial_map); | |
664 | |
665 a->GotoUnless(has_initialmap, if_ismodified); | |
666 | |
667 Node* const initial_proto_initial_map = a->LoadContextElement( | |
668 native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); | |
669 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); | |
670 Node* const proto_has_initialmap = | |
671 a->WordEqual(proto_map, initial_proto_initial_map); | |
672 | |
673 // TODO(ishell): Update this check once map changes for constant field | |
jgruber
2016/12/05 09:25:17
We can remove this TODO since the mentioned change
gsathya
2016/12/05 16:03:05
Done.
| |
674 // tracking are landing. | |
675 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | |
676 } | |
677 } // namespace | |
678 | |
679 void InternalResolvePromise(CodeStubAssembler* a, compiler::Node* context, | |
680 compiler::Node* promise, compiler::Node* result) { | |
681 typedef CodeStubAssembler::Variable Variable; | |
jgruber
2016/12/05 09:25:17
As mentioned offline, you might consider switching
gsathya
2016/12/05 16:03:05
I'm going to wait until I finish the rest of the b
jgruber
2016/12/07 12:59:38
Acknowledged, sgtm.
| |
682 typedef CodeStubAssembler::Label Label; | |
683 typedef compiler::Node Node; | |
684 | |
685 Isolate* isolate = a->isolate(); | |
686 | |
687 Variable var_reason(a, MachineRepresentation::kTagged); | |
688 | |
689 Label out(a), fulfill(a), if_cycle(a, Label::kDeferred), | |
690 if_rejectpromise(a, Label::kDeferred); | |
691 | |
692 // 6. If SameValue(resolution, promise) is true, then | |
693 a->GotoIf(a->WordEqual(promise, result), &if_cycle); | |
jgruber
2016/12/05 09:25:17
SameValue is more involved than WordEqual. You can
gsathya
2016/12/05 16:03:05
Done.
| |
694 | |
695 // 7. If Type(resolution) is not Object, then | |
696 a->GotoIf(a->TaggedIsSmi(result), &fulfill); | |
697 Node* const result_instance_type = a->LoadMapInstanceType(a->LoadMap(result)); | |
698 a->GotoUnless(a->IsJSReceiverInstanceType(result_instance_type), &fulfill); | |
jgruber
2016/12/05 09:25:17
IsJSReceiver()
gsathya
2016/12/05 16:03:05
Done.
| |
699 | |
700 Label if_nativepromise(a), if_notnativepromise(a, Label::kDeferred); | |
701 BranchIfFastPath(a, context, result, &if_nativepromise, &if_notnativepromise); | |
702 | |
703 a->Bind(&if_nativepromise); | |
704 { | |
705 Node* const thenable_status = | |
706 a->LoadObjectField(result, JSPromise::kStatusOffset); | |
707 Node* const thenable_value = | |
708 a->LoadObjectField(result, JSPromise::kResultOffset); | |
709 | |
710 Label if_isnotpending(a); | |
711 a->GotoUnless(a->SmiEqual(a->SmiConstant(kPromisePending), thenable_status), | |
712 &if_isnotpending); | |
713 | |
714 Node* const native_context = a->LoadNativeContext(context); | |
715 Node* const then = | |
716 a->LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); | |
717 | |
718 a->CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise, | |
719 result, then); | |
720 a->Goto(&out); | |
721 | |
722 a->Bind(&if_isnotpending); | |
723 { | |
724 Label if_fulfilled(a), if_rejected(a); | |
725 a->Branch(a->SmiEqual(a->SmiConstant(kPromiseFulfilled), thenable_status), | |
726 &if_fulfilled, &if_rejected); | |
727 | |
728 a->Bind(&if_fulfilled); | |
729 { | |
730 a->CallRuntime(Runtime::kPromiseFulfill, context, promise, | |
731 a->SmiConstant(kPromiseFulfilled), thenable_value); | |
732 a->Goto(&out); | |
733 } | |
734 | |
735 a->Bind(&if_rejected); | |
736 { | |
737 a->CallRuntime(Runtime::kPromiseReject, context, promise, | |
738 thenable_value, a->FalseConstant()); | |
739 a->Goto(&out); | |
740 } | |
741 } | |
742 } | |
743 | |
744 a->Bind(&if_notnativepromise); | |
745 { | |
746 // 8. Let then be Get(resolution, "then"). | |
747 Node* const then_str = a->HeapConstant(isolate->factory()->then_string()); | |
748 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | |
749 Node* const then = | |
750 a->CallStub(getproperty_callable, context, result, then_str); | |
751 | |
752 // 9. If then is an abrupt completion, then | |
753 a->GotoIfException(then, &if_rejectpromise, &var_reason); | |
754 | |
755 // 11. If IsCallable(thenAction) is false, then | |
756 a->GotoIf(a->TaggedIsSmi(then), &fulfill); | |
757 Node* const then_map = a->LoadMap(then); | |
758 a->GotoUnless(a->IsCallableMap(then_map), &fulfill); | |
759 | |
760 // 12. Perform EnqueueJob("PromiseJobs", | |
761 // PromiseResolveThenableJob, « promise, resolution, thenAction | |
762 // »). | |
763 a->CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise, | |
764 result, then); | |
765 a->Goto(&out); | |
766 } | |
767 | |
768 // 7.b Return FulfillPromise(promise, resolution). | |
769 a->Bind(&fulfill); | |
770 { | |
771 a->CallRuntime(Runtime::kPromiseFulfill, context, promise, | |
772 a->SmiConstant(kPromiseFulfilled), result); | |
773 a->Goto(&out); | |
774 } | |
775 | |
776 a->Bind(&if_cycle); | |
777 { | |
778 // 6.a Let selfResolutionError be a newly created TypeError object. | |
779 Node* const message_id = a->SmiConstant(MessageTemplate::kPromiseCyclic); | |
780 Node* const error = | |
781 a->CallRuntime(Runtime::kNewTypeError, context, message_id, result); | |
782 var_reason.Bind(error); | |
783 | |
784 // 6.b Return RejectPromise(promise, selfResolutionError). | |
785 a->Goto(&if_rejectpromise); | |
786 } | |
787 | |
788 // 9.a Return RejectPromise(promise, then.[[Value]]). | |
789 a->Bind(&if_rejectpromise); | |
790 { | |
791 a->CallRuntime(Runtime::kPromiseReject, context, promise, | |
792 var_reason.value(), a->TrueConstant()); | |
793 a->Goto(&out); | |
794 } | |
795 | |
796 a->Bind(&out); | |
797 a->Return(a->UndefinedConstant()); | |
798 } | |
799 | |
800 // ES#sec-promise-resolve-functions | |
801 // Promise Resolve Functions | |
802 void Builtins::Generate_PromiseResolveClosure( | |
803 compiler::CodeAssemblerState* state) { | |
804 CodeStubAssembler a(state); | |
805 typedef compiler::Node Node; | |
806 typedef CodeStubAssembler::Label Label; | |
807 | |
808 Node* const value = a.Parameter(1); | |
809 Node* const context = a.Parameter(4); | |
810 Isolate* isolate = a.isolate(); | |
811 | |
812 Label out(&a); | |
813 | |
814 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. | |
815 Node* const has_already_visited_slot = | |
816 a.IntPtrConstant(PromiseUtils::kAlreadyVisitedSlot); | |
817 | |
818 Node* const has_already_visited = | |
819 a.LoadFixedArrayElement(context, has_already_visited_slot); | |
820 | |
821 // 4. If alreadyResolved.[[Value]] is true, return undefined. | |
822 a.GotoIf(a.SmiEqual(has_already_visited, a.SmiConstant(1)), &out); | |
jgruber
2016/12/05 09:25:17
Is line 825 below the only place where we write in
gsathya
2016/12/05 16:03:05
PromiseUtils::CreateResolvingFunctions and Promise
jgruber
2016/12/07 12:59:38
Ok. IMHO the fact that we use a Smi 0/1 encoding i
| |
823 | |
824 // 5.Set alreadyResolved.[[Value]] to true. | |
825 a.StoreFixedArrayElement(context, has_already_visited_slot, a.SmiConstant(1)); | |
826 | |
827 // 2. Let promise be F.[[Promise]]. | |
828 Node* const promise = a.LoadFixedArrayElement( | |
829 context, a.IntPtrConstant(PromiseUtils::kPromiseSlot)); | |
830 | |
831 InternalResolvePromise(&a, context, promise, value); | |
832 | |
833 a.Bind(&out); | |
834 a.Return(a.UndefinedConstant()); | |
835 } | |
836 | |
837 void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) { | |
838 CodeStubAssembler a(state); | |
839 typedef compiler::Node Node; | |
840 | |
841 Node* const promise = a.Parameter(1); | |
842 Node* const result = a.Parameter(2); | |
843 Node* const context = a.Parameter(5); | |
844 | |
845 InternalResolvePromise(&a, context, promise, result); | |
846 } | |
847 | |
668 } // namespace internal | 848 } // namespace internal |
669 } // namespace v8 | 849 } // namespace v8 |
OLD | NEW |