Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Side by Side Diff: src/builtins/builtins-promise.cc

Issue 2541283002: [promises] Port ResolvePromise to TF (Closed)
Patch Set: Fix test Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/builtins/builtins.h ('k') | src/contexts.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 #include "src/code-factory.h" 7 #include "src/code-factory.h"
8 #include "src/code-stub-assembler.h" 8 #include "src/code-stub-assembler.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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 55
80 return *isolate->factory()->NewJSArrayWithElements(result, FAST_ELEMENTS, 2, 56 return *isolate->factory()->NewJSArrayWithElements(result, FAST_ELEMENTS, 2,
81 NOT_TENURED); 57 NOT_TENURED);
82 } 58 }
83 59
84 void PromiseInit(CodeStubAssembler* a, compiler::Node* promise, 60 void PromiseInit(CodeStubAssembler* a, compiler::Node* promise,
85 compiler::Node* status, compiler::Node* result) { 61 compiler::Node* status, compiler::Node* result) {
86 CSA_ASSERT(a, a->TaggedIsSmi(status)); 62 CSA_ASSERT(a, a->TaggedIsSmi(status));
87 a->StoreObjectField(promise, JSPromise::kStatusOffset, status); 63 a->StoreObjectField(promise, JSPromise::kStatusOffset, status);
88 a->StoreObjectField(promise, JSPromise::kResultOffset, result); 64 a->StoreObjectField(promise, JSPromise::kResultOffset, result);
65 a->StoreObjectField(promise, JSPromise::kFlagsOffset, a->SmiConstant(0));
89 } 66 }
90 67
91 void Builtins::Generate_PromiseConstructor( 68 void Builtins::Generate_PromiseConstructor(
92 compiler::CodeAssemblerState* state) { 69 compiler::CodeAssemblerState* state) {
93 CodeStubAssembler a(state); 70 CodeStubAssembler a(state);
94 typedef CodeStubAssembler::Variable Variable; 71 typedef CodeStubAssembler::Variable Variable;
95 typedef CodeStubAssembler::Label Label; 72 typedef CodeStubAssembler::Label Label;
96 typedef compiler::Node Node; 73 typedef compiler::Node Node;
97 74
98 Node* const executor = a.Parameter(1); 75 Node* const executor = a.Parameter(1);
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
299 a.GotoIf(a.TaggedIsSmi(maybe_promise), &if_notpromise); 276 a.GotoIf(a.TaggedIsSmi(maybe_promise), &if_notpromise);
300 277
301 Node* const result = a.SelectBooleanConstant( 278 Node* const result = a.SelectBooleanConstant(
302 a.HasInstanceType(maybe_promise, JS_PROMISE_TYPE)); 279 a.HasInstanceType(maybe_promise, JS_PROMISE_TYPE));
303 a.Return(result); 280 a.Return(result);
304 281
305 a.Bind(&if_notpromise); 282 a.Bind(&if_notpromise);
306 a.Return(a.FalseConstant()); 283 a.Return(a.FalseConstant());
307 } 284 }
308 285
309 namespace { 286 namespace {
Benedikt Meurer 2016/12/08 05:22:30 Nit: empty line after namespace {
gsathya 2016/12/08 05:28:40 Done.
287 compiler::Node* PromiseHasHandler(CodeStubAssembler* a,
288 compiler::Node* promise) {
289 typedef compiler::Node Node;
290
291 Node* const flags = a->LoadObjectField(promise, JSPromise::kFlagsOffset);
292 return a->IsSetWord(a->SmiUntag(flags), 1 << JSPromise::kHasHandlerBit);
293 }
294
295 void PromiseSetHandler(CodeStubAssembler* a, compiler::Node* promise) {
Benedikt Meurer 2016/12/08 05:22:30 Nit: PromiseSetHasHandler
gsathya 2016/12/08 05:28:40 Done.
296 typedef compiler::Node Node;
297
298 Node* const flags = a->LoadObjectField(promise, JSPromise::kFlagsOffset);
299 Node* const new_flags = a->WordOr(
Benedikt Meurer 2016/12/08 05:22:30 You can perform this operation on the Smi directly
gsathya 2016/12/08 05:28:40 Done.
300 a->SmiUntag(flags), a->IntPtrConstant(1 << JSPromise::kHasHandlerBit));
301 a->StoreObjectField(promise, JSPromise::kFlagsOffset, a->SmiTag(new_flags));
302 }
303
310 compiler::Node* SpeciesConstructor(CodeStubAssembler* a, Isolate* isolate, 304 compiler::Node* SpeciesConstructor(CodeStubAssembler* a, Isolate* isolate,
311 compiler::Node* context, 305 compiler::Node* context,
312 compiler::Node* object, 306 compiler::Node* object,
313 compiler::Node* default_constructor) { 307 compiler::Node* default_constructor) {
314 typedef compiler::Node Node; 308 typedef compiler::Node Node;
315 typedef CodeStubAssembler::Label Label; 309 typedef CodeStubAssembler::Label Label;
316 typedef CodeStubAssembler::Variable Variable; 310 typedef CodeStubAssembler::Variable Variable;
317 311
318 Variable var_result(a, MachineRepresentation::kTagged); 312 Variable var_result(a, MachineRepresentation::kTagged);
319 var_result.Bind(default_constructor); 313 var_result.Bind(default_constructor);
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
397 391
398 compiler::Node* InternalPerformPromiseThen(CodeStubAssembler* a, 392 compiler::Node* InternalPerformPromiseThen(CodeStubAssembler* a,
399 compiler::Node* context, 393 compiler::Node* context,
400 compiler::Node* promise, 394 compiler::Node* promise,
401 compiler::Node* on_resolve, 395 compiler::Node* on_resolve,
402 compiler::Node* on_reject, 396 compiler::Node* on_reject,
403 compiler::Node* deferred) { 397 compiler::Node* deferred) {
404 typedef CodeStubAssembler::Variable Variable; 398 typedef CodeStubAssembler::Variable Variable;
405 typedef CodeStubAssembler::Label Label; 399 typedef CodeStubAssembler::Label Label;
406 typedef compiler::Node Node; 400 typedef compiler::Node Node;
407
408 Isolate* isolate = a->isolate(); 401 Isolate* isolate = a->isolate();
409 Node* const native_context = a->LoadNativeContext(context); 402 Node* const native_context = a->LoadNativeContext(context);
410 403
411 Variable var_on_resolve(a, MachineRepresentation::kTagged), 404 Variable var_on_resolve(a, MachineRepresentation::kTagged),
412 var_on_reject(a, MachineRepresentation::kTagged); 405 var_on_reject(a, MachineRepresentation::kTagged);
413 406
414 var_on_resolve.Bind(on_resolve); 407 var_on_resolve.Bind(on_resolve);
415 var_on_reject.Bind(on_reject); 408 var_on_reject.Bind(on_reject);
416 409
417 Label out(a), if_onresolvenotcallable(a), onrejectcheck(a), 410 Label out(a), if_onresolvenotcallable(a), onrejectcheck(a),
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
529 &reject); 522 &reject);
530 523
531 // TODO(gsathya): Move this to TF. 524 // TODO(gsathya): Move this to TF.
532 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result, 525 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result,
533 var_on_resolve.value(), deferred, 526 var_on_resolve.value(), deferred,
534 a->SmiConstant(kPromiseFulfilled)); 527 a->SmiConstant(kPromiseFulfilled));
535 a->Goto(&out); 528 a->Goto(&out);
536 529
537 a->Bind(&reject); 530 a->Bind(&reject);
538 { 531 {
539 Callable getproperty_callable = CodeFactory::GetProperty(isolate); 532 Node* const has_handler = PromiseHasHandler(a, promise);
540 Node* const key =
541 a->HeapConstant(isolate->factory()->promise_has_handler_symbol());
542 Node* const has_handler =
543 a->CallStub(getproperty_callable, context, promise, key);
544
545 Label enqueue(a); 533 Label enqueue(a);
546 534
547 // TODO(gsathya): Fold these runtime calls and move to TF. 535 // TODO(gsathya): Fold these runtime calls and move to TF.
548 a->GotoIf(a->WordEqual(has_handler, a->TrueConstant()), &enqueue); 536 a->GotoIf(has_handler, &enqueue);
549 a->CallRuntime(Runtime::kPromiseRevokeReject, context, promise); 537 a->CallRuntime(Runtime::kPromiseRevokeReject, context, promise);
550 a->Goto(&enqueue); 538 a->Goto(&enqueue);
551 539
552 a->Bind(&enqueue); 540 a->Bind(&enqueue);
553 { 541 {
554 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result, 542 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result,
555 var_on_reject.value(), deferred, 543 var_on_reject.value(), deferred,
556 a->SmiConstant(kPromiseRejected)); 544 a->SmiConstant(kPromiseRejected));
557 545
558 a->Goto(&out); 546 a->Goto(&out);
559 } 547 }
560 } 548 }
561 } 549 }
562 } 550 }
563 551
564 a->Bind(&out); 552 a->Bind(&out);
565 // TODO(gsathya): Protect with debug check. 553 PromiseSetHandler(a, promise);
566 a->CallRuntime(
567 Runtime::kSetProperty, context, promise,
568 a->HeapConstant(isolate->factory()->promise_has_handler_symbol()),
569 a->TrueConstant(), a->SmiConstant(STRICT));
570 554
571 // TODO(gsathya): This call will be removed once we don't have to 555 // TODO(gsathya): This call will be removed once we don't have to
572 // deal with deferred objects. 556 // deal with deferred objects.
573 Callable getproperty_callable = CodeFactory::GetProperty(isolate); 557 Callable getproperty_callable = CodeFactory::GetProperty(isolate);
574 Node* const key = 558 Node* const key =
575 a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise")); 559 a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise"));
576 Node* const result = 560 Node* const result =
577 a->CallStub(getproperty_callable, context, deferred, key); 561 a->CallStub(getproperty_callable, context, deferred, key);
578 562
579 return result; 563 return result;
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
660 } 644 }
661 645
662 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, 646 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
663 // resultCapability). 647 // resultCapability).
664 a.Bind(&perform_promise_then); 648 a.Bind(&perform_promise_then);
665 Node* const result = InternalPerformPromiseThen( 649 Node* const result = InternalPerformPromiseThen(
666 &a, context, promise, on_resolve, on_reject, var_deferred.value()); 650 &a, context, promise, on_resolve, on_reject, var_deferred.value());
667 a.Return(result); 651 a.Return(result);
668 } 652 }
669 653
654 namespace {
Benedikt Meurer 2016/12/08 05:22:30 Nit: whitespace after { and before }
gsathya 2016/12/08 05:28:40 Done.
655 // Promise fast path implementations rely on unmodified JSPromise instances.
656 // We use a fairly coarse granularity for this and simply check whether both
657 // the promise itself is unmodified (i.e. its map has not changed) and its
658 // prototype is unmodified.
659 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp
660 void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context,
661 compiler::Node* promise,
662 CodeStubAssembler::Label* if_isunmodified,
663 CodeStubAssembler::Label* if_ismodified) {
664 typedef compiler::Node Node;
665
666 // TODO(gsathya): Assert if promise is receiver
667 Node* const map = a->LoadMap(promise);
668 Node* const native_context = a->LoadNativeContext(context);
669 Node* const promise_fun =
670 a->LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
671 Node* const initial_map =
672 a->LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
673 Node* const has_initialmap = a->WordEqual(map, initial_map);
674
675 a->GotoUnless(has_initialmap, if_ismodified);
676
677 Node* const initial_proto_initial_map = a->LoadContextElement(
678 native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX);
679 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map));
680 Node* const proto_has_initialmap =
681 a->WordEqual(proto_map, initial_proto_initial_map);
682
683 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
684 }
685 } // namespace
686
687 void InternalResolvePromise(CodeStubAssembler* a, compiler::Node* context,
Benedikt Meurer 2016/12/08 05:22:30 Nit: move into the namespace above.
gsathya 2016/12/08 05:28:40 Done.
688 compiler::Node* promise, compiler::Node* result,
689 CodeStubAssembler::Label* out) {
690 typedef CodeStubAssembler::Variable Variable;
691 typedef CodeStubAssembler::Label Label;
692 typedef compiler::Node Node;
693
694 Isolate* isolate = a->isolate();
695
696 Variable var_reason(a, MachineRepresentation::kTagged),
697 var_then(a, MachineRepresentation::kTagged);
698
699 Label do_enqueue(a), fulfill(a), if_cycle(a, Label::kDeferred),
700 if_rejectpromise(a, Label::kDeferred);
701
702 // 6. If SameValue(resolution, promise) is true, then
703 a->GotoIf(a->SameValue(promise, result, context), &if_cycle);
704
705 // 7. If Type(resolution) is not Object, then
706 a->GotoIf(a->TaggedIsSmi(result), &fulfill);
707 a->GotoUnless(a->IsJSReceiver(result), &fulfill);
708
709 Label if_nativepromise(a), if_notnativepromise(a, Label::kDeferred);
710 BranchIfFastPath(a, context, result, &if_nativepromise, &if_notnativepromise);
711
712 // Resolution is a native promise and if it's already resolved or
713 // rejected, shortcircuit the resolution procedure by directly
714 // reusing the value from the promise.
715 a->Bind(&if_nativepromise);
716 {
717 Node* const thenable_status =
718 a->LoadObjectField(result, JSPromise::kStatusOffset);
719 Node* const thenable_value =
720 a->LoadObjectField(result, JSPromise::kResultOffset);
721
722 Label if_isnotpending(a);
723 a->GotoUnless(a->SmiEqual(a->SmiConstant(kPromisePending), thenable_status),
724 &if_isnotpending);
725
726 // TODO(gsathya): Use a marker here instead of the actual then
727 // callback, and check for the marker in PromiseResolveThenableJob
728 // and perform PromiseThen.
729 Node* const native_context = a->LoadNativeContext(context);
730 Node* const then =
731 a->LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
732 var_then.Bind(then);
733 a->Goto(&do_enqueue);
734
735 a->Bind(&if_isnotpending);
736 {
737 Label if_fulfilled(a), if_rejected(a);
738 a->Branch(a->SmiEqual(a->SmiConstant(kPromiseFulfilled), thenable_status),
739 &if_fulfilled, &if_rejected);
740
741 a->Bind(&if_fulfilled);
742 {
743 a->CallRuntime(Runtime::kPromiseFulfill, context, promise,
744 a->SmiConstant(kPromiseFulfilled), thenable_value);
745 PromiseSetHandler(a, promise);
746 a->Goto(out);
747 }
748
749 a->Bind(&if_rejected);
750 {
751 Label reject(a);
752 Node* const has_handler = PromiseHasHandler(a, result);
753
754 // Promise has already been rejected, but had no handler.
755 // Revoke previously triggered reject event.
756 a->GotoIf(has_handler, &reject);
757 a->CallRuntime(Runtime::kPromiseRevokeReject, context, result);
758 a->Goto(&reject);
759
760 a->Bind(&reject);
761 // Don't cause a debug event as this case is forwarding a rejection
762 a->CallRuntime(Runtime::kPromiseReject, context, promise,
763 thenable_value, a->FalseConstant());
764 PromiseSetHandler(a, result);
765 a->Goto(out);
766 }
767 }
768 }
769
770 a->Bind(&if_notnativepromise);
771 {
772 // 8. Let then be Get(resolution, "then").
773 Node* const then_str = a->HeapConstant(isolate->factory()->then_string());
774 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
775 Node* const then =
776 a->CallStub(getproperty_callable, context, result, then_str);
777
778 // 9. If then is an abrupt completion, then
779 a->GotoIfException(then, &if_rejectpromise, &var_reason);
780
781 // 11. If IsCallable(thenAction) is false, then
782 a->GotoIf(a->TaggedIsSmi(then), &fulfill);
783 Node* const then_map = a->LoadMap(then);
784 a->GotoUnless(a->IsCallableMap(then_map), &fulfill);
785 var_then.Bind(then);
786 a->Goto(&do_enqueue);
787 }
788
789 a->Bind(&do_enqueue);
790 {
791 Label enqueue(a);
792 a->GotoUnless(a->IsDebugActive(), &enqueue);
793 a->GotoIf(a->TaggedIsSmi(result), &enqueue);
794 a->GotoUnless(a->HasInstanceType(result, JS_PROMISE_TYPE), &enqueue);
795 // Mark the dependency of the new promise on the resolution
796 Node* const key =
797 a->HeapConstant(isolate->factory()->promise_handled_by_symbol());
798 a->CallRuntime(Runtime::kSetProperty, context, result, key, promise,
799 a->SmiConstant(STRICT));
800 a->Goto(&enqueue);
801
802 // 12. Perform EnqueueJob("PromiseJobs",
803 // PromiseResolveThenableJob, « promise, resolution, thenAction
804 // »).
805 a->Bind(&enqueue);
806 a->CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise,
807 result, var_then.value());
808 a->Goto(out);
809 }
810 // 7.b Return FulfillPromise(promise, resolution).
811 a->Bind(&fulfill);
812 {
813 a->CallRuntime(Runtime::kPromiseFulfill, context, promise,
814 a->SmiConstant(kPromiseFulfilled), result);
815 a->Goto(out);
816 }
817
818 a->Bind(&if_cycle);
819 {
820 // 6.a Let selfResolutionError be a newly created TypeError object.
821 Node* const message_id = a->SmiConstant(MessageTemplate::kPromiseCyclic);
822 Node* const error =
823 a->CallRuntime(Runtime::kNewTypeError, context, message_id, result);
824 var_reason.Bind(error);
825
826 // 6.b Return RejectPromise(promise, selfResolutionError).
827 a->Goto(&if_rejectpromise);
828 }
829
830 // 9.a Return RejectPromise(promise, then.[[Value]]).
831 a->Bind(&if_rejectpromise);
832 {
833 a->CallRuntime(Runtime::kPromiseReject, context, promise,
834 var_reason.value(), a->TrueConstant());
835 a->Goto(out);
836 }
837 }
838
839 // ES#sec-promise-resolve-functions
840 // Promise Resolve Functions
841 void Builtins::Generate_PromiseResolveClosure(
842 compiler::CodeAssemblerState* state) {
843 CodeStubAssembler a(state);
844 typedef compiler::Node Node;
845 typedef CodeStubAssembler::Label Label;
846
847 Node* const value = a.Parameter(1);
848 Node* const context = a.Parameter(4);
849
850 Label out(&a);
851
852 // 3. Let alreadyResolved be F.[[AlreadyResolved]].
853 Node* const has_already_visited_slot =
854 a.IntPtrConstant(PromiseUtils::kAlreadyVisitedSlot);
855
856 Node* const has_already_visited =
857 a.LoadFixedArrayElement(context, has_already_visited_slot);
858
859 // 4. If alreadyResolved.[[Value]] is true, return undefined.
860 a.GotoIf(a.SmiEqual(has_already_visited, a.SmiConstant(1)), &out);
861
862 // 5.Set alreadyResolved.[[Value]] to true.
863 a.StoreFixedArrayElement(context, has_already_visited_slot, a.SmiConstant(1));
864
865 // 2. Let promise be F.[[Promise]].
866 Node* const promise = a.LoadFixedArrayElement(
867 context, a.IntPtrConstant(PromiseUtils::kPromiseSlot));
868
869 InternalResolvePromise(&a, context, promise, value, &out);
870
871 a.Bind(&out);
872 a.Return(a.UndefinedConstant());
873 }
874
875 void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) {
876 CodeStubAssembler a(state);
877 typedef compiler::Node Node;
878 typedef CodeStubAssembler::Label Label;
879
880 Node* const promise = a.Parameter(1);
881 Node* const result = a.Parameter(2);
882 Node* const context = a.Parameter(5);
883
884 Label out(&a);
885 InternalResolvePromise(&a, context, promise, result, &out);
886
887 a.Bind(&out);
888 a.Return(a.UndefinedConstant());
889 }
890
670 } // namespace internal 891 } // namespace internal
671 } // namespace v8 892 } // namespace v8
OLDNEW
« no previous file with comments | « src/builtins/builtins.h ('k') | src/contexts.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698