 Chromium Code Reviews
 Chromium Code Reviews Issue 2541283002:
  [promises] Port ResolvePromise to TF  (Closed)
    
  
    Issue 2541283002:
  [promises] Port ResolvePromise to TF  (Closed) 
  | 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 |