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

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

Issue 2541283002: [promises] Port ResolvePromise to TF (Closed)
Patch Set: remove ResolvePromise 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
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::kHasHandlerOffset, 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 274 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 350
374 Node* elements = a->LoadObjectField(promise, offset); 351 Node* elements = a->LoadObjectField(promise, offset);
375 Node* length = a->LoadFixedArrayBaseLength(elements); 352 Node* length = a->LoadFixedArrayBaseLength(elements);
376 CodeStubAssembler::ParameterMode mode = a->OptimalParameterMode(); 353 CodeStubAssembler::ParameterMode mode = a->OptimalParameterMode();
377 length = a->UntagParameter(length, mode); 354 length = a->UntagParameter(length, mode);
378 355
379 Node* delta = a->IntPtrOrSmiConstant(1, mode); 356 Node* delta = a->IntPtrOrSmiConstant(1, mode);
380 Node* new_capacity = a->IntPtrAdd(length, delta); 357 Node* new_capacity = a->IntPtrAdd(length, delta);
381 ElementsKind kind = FAST_ELEMENTS; 358 ElementsKind kind = FAST_ELEMENTS;
382 359
383 Node* new_elements = a->AllocateFixedArray(kind, new_capacity, mode); 360 Node* new_elements = a->AllocateFixedArray(kind, new_capacity, mode);
jgruber 2016/12/07 12:59:38 Do you think we need to handle LO space allocation
gsathya 2016/12/08 05:18:14 This is handled in https://codereview.chromium.org
384 361
385 a->CopyFixedArrayElements(kind, elements, new_elements, length, 362 a->CopyFixedArrayElements(kind, elements, new_elements, length,
386 UPDATE_WRITE_BARRIER, mode); 363 UPDATE_WRITE_BARRIER, mode);
387 a->StoreFixedArrayElement(new_elements, length, value, UPDATE_WRITE_BARRIER, 364 a->StoreFixedArrayElement(new_elements, length, value, UPDATE_WRITE_BARRIER,
388 0, mode); 365 0, mode);
389 366
390 a->StoreObjectField(promise, offset, new_elements); 367 a->StoreObjectField(promise, offset, new_elements);
391 } 368 }
392 369
393 compiler::Node* InternalPerformPromiseThen(CodeStubAssembler* a, 370 compiler::Node* InternalPerformPromiseThen(CodeStubAssembler* a,
394 compiler::Node* context, 371 compiler::Node* context,
395 compiler::Node* promise, 372 compiler::Node* promise,
396 compiler::Node* on_resolve, 373 compiler::Node* on_resolve,
397 compiler::Node* on_reject, 374 compiler::Node* on_reject,
398 compiler::Node* deferred) { 375 compiler::Node* deferred) {
399 typedef CodeStubAssembler::Variable Variable; 376 typedef CodeStubAssembler::Variable Variable;
400 typedef CodeStubAssembler::Label Label; 377 typedef CodeStubAssembler::Label Label;
401 typedef compiler::Node Node; 378 typedef compiler::Node Node;
402
403 Isolate* isolate = a->isolate(); 379 Isolate* isolate = a->isolate();
404 Node* const native_context = a->LoadNativeContext(context); 380 Node* const native_context = a->LoadNativeContext(context);
405 381
406 Variable var_on_resolve(a, MachineRepresentation::kTagged), 382 Variable var_on_resolve(a, MachineRepresentation::kTagged),
407 var_on_reject(a, MachineRepresentation::kTagged); 383 var_on_reject(a, MachineRepresentation::kTagged);
408 384
409 var_on_resolve.Bind(on_resolve); 385 var_on_resolve.Bind(on_resolve);
410 var_on_reject.Bind(on_reject); 386 var_on_reject.Bind(on_reject);
411 387
412 Label out(a), if_onresolvenotcallable(a), onrejectcheck(a), 388 Label out(a), if_onresolvenotcallable(a), onrejectcheck(a),
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
525 &reject); 501 &reject);
526 502
527 // TODO(gsathya): Move this to TF. 503 // TODO(gsathya): Move this to TF.
528 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result, 504 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result,
529 var_on_resolve.value(), deferred, 505 var_on_resolve.value(), deferred,
530 a->SmiConstant(kPromiseFulfilled)); 506 a->SmiConstant(kPromiseFulfilled));
531 a->Goto(&out); 507 a->Goto(&out);
532 508
533 a->Bind(&reject); 509 a->Bind(&reject);
534 { 510 {
535 Callable getproperty_callable = CodeFactory::GetProperty(isolate);
536 Node* const key =
537 a->HeapConstant(isolate->factory()->promise_has_handler_symbol());
538 Node* const has_handler = 511 Node* const has_handler =
539 a->CallStub(getproperty_callable, context, promise, key); 512 a->LoadObjectField(promise, JSPromise::kHasHandlerOffset);
540
541 Label enqueue(a); 513 Label enqueue(a);
542 514
543 // TODO(gsathya): Fold these runtime calls and move to TF. 515 // TODO(gsathya): Fold these runtime calls and move to TF.
544 a->GotoIf(a->WordEqual(has_handler, a->TrueConstant()), &enqueue); 516 a->GotoIf(a->SmiEqual(has_handler, a->SmiConstant(1)), &enqueue);
545 a->CallRuntime(Runtime::kPromiseRevokeReject, context, promise); 517 a->CallRuntime(Runtime::kPromiseRevokeReject, context, promise);
546 a->Goto(&enqueue); 518 a->Goto(&enqueue);
547 519
548 a->Bind(&enqueue); 520 a->Bind(&enqueue);
549 { 521 {
550 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result, 522 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result,
551 var_on_reject.value(), deferred, 523 var_on_reject.value(), deferred,
552 a->SmiConstant(kPromiseRejected)); 524 a->SmiConstant(kPromiseRejected));
553 525
554 a->Goto(&out); 526 a->Goto(&out);
555 } 527 }
556 } 528 }
557 } 529 }
558 } 530 }
559 531
560 a->Bind(&out); 532 a->Bind(&out);
561 // TODO(gsathya): Protect with debug check. 533 a->StoreObjectField(promise, JSPromise::kHasHandlerOffset, a->SmiConstant(1));
562 a->CallRuntime(
563 Runtime::kSetProperty, context, promise,
564 a->HeapConstant(isolate->factory()->promise_has_handler_symbol()),
565 a->TrueConstant(), a->SmiConstant(STRICT));
566 534
567 // TODO(gsathya): This call will be removed once we don't have to 535 // TODO(gsathya): This call will be removed once we don't have to
568 // deal with deferred objects. 536 // deal with deferred objects.
569 Callable getproperty_callable = CodeFactory::GetProperty(isolate); 537 Callable getproperty_callable = CodeFactory::GetProperty(isolate);
570 Node* const key = 538 Node* const key =
571 a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise")); 539 a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise"));
572 Node* const result = 540 Node* const result =
573 a->CallStub(getproperty_callable, context, deferred, key); 541 a->CallStub(getproperty_callable, context, deferred, key);
574 542
575 return result; 543 return result;
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
656 } 624 }
657 625
658 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, 626 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
659 // resultCapability). 627 // resultCapability).
660 a.Bind(&perform_promise_then); 628 a.Bind(&perform_promise_then);
661 Node* const result = InternalPerformPromiseThen( 629 Node* const result = InternalPerformPromiseThen(
662 &a, context, promise, on_resolve, on_reject, var_deferred.value()); 630 &a, context, promise, on_resolve, on_reject, var_deferred.value());
663 a.Return(result); 631 a.Return(result);
664 } 632 }
665 633
634 namespace {
635 // Promise fast path implementations rely on unmodified JSPromise instances.
636 // We use a fairly coarse granularity for this and simply check whether both
637 // the promise itself is unmodified (i.e. its map has not changed) and its
638 // prototype is unmodified.
639 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp
640 void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context,
641 compiler::Node* promise,
642 CodeStubAssembler::Label* if_isunmodified,
643 CodeStubAssembler::Label* if_ismodified) {
644 typedef compiler::Node Node;
645
646 // TODO(gsathya): Assert if promise is receiver
647 Node* const map = a->LoadMap(promise);
648 Node* const native_context = a->LoadNativeContext(context);
649 Node* const promise_fun =
650 a->LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
651 Node* const initial_map =
652 a->LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
653 Node* const has_initialmap = a->WordEqual(map, initial_map);
654
655 a->GotoUnless(has_initialmap, if_ismodified);
656
657 Node* const initial_proto_initial_map = a->LoadContextElement(
658 native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX);
659 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map));
660 Node* const proto_has_initialmap =
661 a->WordEqual(proto_map, initial_proto_initial_map);
662
663 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
664 }
665 } // namespace
666
667 void InternalResolvePromise(CodeStubAssembler* a, compiler::Node* context,
668 compiler::Node* promise, compiler::Node* result,
669 CodeStubAssembler::Label* out) {
jgruber 2016/12/07 12:59:38 I don't think you gain much through the style IRP
gsathya 2016/12/08 05:18:14 I use this here in the follow on patch -- https://
670 typedef CodeStubAssembler::Variable Variable;
671 typedef CodeStubAssembler::Label Label;
672 typedef compiler::Node Node;
673
674 Isolate* isolate = a->isolate();
675
676 Variable var_reason(a, MachineRepresentation::kTagged),
677 var_then(a, MachineRepresentation::kTagged);
678
679 Label do_enqueue(a), fulfill(a), if_cycle(a, Label::kDeferred),
680 if_rejectpromise(a, Label::kDeferred);
681
682 // 6. If SameValue(resolution, promise) is true, then
683 a->GotoIf(a->SameValue(promise, result, context), &if_cycle);
684
685 // 7. If Type(resolution) is not Object, then
686 a->GotoIf(a->TaggedIsSmi(result), &fulfill);
687 a->GotoUnless(a->IsJSReceiver(result), &fulfill);
688
689 Label if_nativepromise(a), if_notnativepromise(a, Label::kDeferred);
690 BranchIfFastPath(a, context, result, &if_nativepromise, &if_notnativepromise);
691
692 // Resolution is a native promise and if it's already resolved or
693 // rejected, shortcircuit the resolution procedure by directly
694 // reusing the value from the promise.
695 a->Bind(&if_nativepromise);
696 {
697 Node* const thenable_status =
698 a->LoadObjectField(result, JSPromise::kStatusOffset);
699 Node* const thenable_value =
700 a->LoadObjectField(result, JSPromise::kResultOffset);
701
702 Label if_isnotpending(a);
703 a->GotoUnless(a->SmiEqual(a->SmiConstant(kPromisePending), thenable_status),
704 &if_isnotpending);
705
706 // TODO(gsathya): Use a marker here instead of the actual then
707 // callback, and check for the marker in PromiseResolveThenableJob
708 // and perform PromiseThen.
709 Node* const native_context = a->LoadNativeContext(context);
710 Node* const then =
711 a->LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
712 var_then.Bind(then);
713 a->Goto(&do_enqueue);
714
715 a->Bind(&if_isnotpending);
716 {
717 Label if_fulfilled(a), if_rejected(a);
718 a->Branch(a->SmiEqual(a->SmiConstant(kPromiseFulfilled), thenable_status),
719 &if_fulfilled, &if_rejected);
720
721 a->Bind(&if_fulfilled);
722 {
723 a->CallRuntime(Runtime::kPromiseFulfill, context, promise,
724 a->SmiConstant(kPromiseFulfilled), thenable_value);
725 a->StoreObjectField(promise, JSPromise::kHasHandlerOffset,
726 a->SmiConstant(1));
727 a->Goto(out);
728 }
729
730 a->Bind(&if_rejected);
731 {
732 Label reject(a);
733 Node* const has_handler =
734 a->LoadObjectField(promise, JSPromise::kHasHandlerOffset);
735
736 // Promise has already been rejected, but had no handler.
737 // Revoke previously triggered reject event.
738 a->GotoIf(a->SmiEqual(has_handler, a->SmiConstant(1)), &reject);
739 a->CallRuntime(Runtime::kPromiseRevokeReject, context, result);
740 a->Goto(&reject);
741
742 a->Bind(&reject);
743 // Don't cause a debug event as this case is forwarding a rejection
744 a->CallRuntime(Runtime::kPromiseReject, context, promise,
745 thenable_value, a->FalseConstant());
746 a->StoreObjectField(result, JSPromise::kHasHandlerOffset,
747 a->SmiConstant(1));
748 a->Goto(out);
749 }
750 }
751 }
752
753 a->Bind(&if_notnativepromise);
754 {
755 // 8. Let then be Get(resolution, "then").
756 Node* const then_str = a->HeapConstant(isolate->factory()->then_string());
757 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
758 Node* const then =
759 a->CallStub(getproperty_callable, context, result, then_str);
760
761 // 9. If then is an abrupt completion, then
762 a->GotoIfException(then, &if_rejectpromise, &var_reason);
763
764 // 11. If IsCallable(thenAction) is false, then
765 a->GotoIf(a->TaggedIsSmi(then), &fulfill);
766 Node* const then_map = a->LoadMap(then);
767 a->GotoUnless(a->IsCallableMap(then_map), &fulfill);
768 var_then.Bind(then);
769 a->Goto(&do_enqueue);
770 }
771
772 a->Bind(&do_enqueue);
773 {
774 Label enqueue(a);
775 a->GotoUnless(a->IsDebugActive(), &enqueue);
776 a->GotoIf(a->TaggedIsSmi(result), &enqueue);
777 Node* const is_promise =
778 a->SelectBooleanConstant(a->HasInstanceType(result, JS_PROMISE_TYPE));
779 a->GotoUnless(is_promise, &enqueue);
jgruber 2016/12/07 12:59:38 This will always evaluate to true (even if you pas
gsathya 2016/12/08 05:18:14 Done.
780 Node* const key =
781 a->HeapConstant(isolate->factory()->promise_handled_by_symbol());
782 a->CallRuntime(Runtime::kSetProperty, context, result, key, promise,
783 a->SmiConstant(STRICT));
784 a->Goto(&enqueue);
785
786 // 12. Perform EnqueueJob("PromiseJobs",
787 // PromiseResolveThenableJob, « promise, resolution, thenAction
788 // »).
789 a->Bind(&enqueue);
790 a->CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise,
791 result, var_then.value());
792 a->Goto(out);
793 }
794 // 7.b Return FulfillPromise(promise, resolution).
795 a->Bind(&fulfill);
796 {
797 a->CallRuntime(Runtime::kPromiseFulfill, context, promise,
798 a->SmiConstant(kPromiseFulfilled), result);
799 a->Goto(out);
800 }
801
802 a->Bind(&if_cycle);
803 {
804 // 6.a Let selfResolutionError be a newly created TypeError object.
805 Node* const message_id = a->SmiConstant(MessageTemplate::kPromiseCyclic);
806 Node* const error =
807 a->CallRuntime(Runtime::kNewTypeError, context, message_id, result);
808 var_reason.Bind(error);
809
810 // 6.b Return RejectPromise(promise, selfResolutionError).
811 a->Goto(&if_rejectpromise);
812 }
813
814 // 9.a Return RejectPromise(promise, then.[[Value]]).
815 a->Bind(&if_rejectpromise);
816 {
817 a->CallRuntime(Runtime::kPromiseReject, context, promise,
818 var_reason.value(), a->TrueConstant());
819 a->Goto(out);
820 }
821 }
822
823 // ES#sec-promise-resolve-functions
824 // Promise Resolve Functions
825 void Builtins::Generate_PromiseResolveClosure(
826 compiler::CodeAssemblerState* state) {
827 CodeStubAssembler a(state);
828 typedef compiler::Node Node;
829 typedef CodeStubAssembler::Label Label;
830
831 Node* const value = a.Parameter(1);
832 Node* const context = a.Parameter(4);
833
834 Label out(&a);
835
836 // 3. Let alreadyResolved be F.[[AlreadyResolved]].
837 Node* const has_already_visited_slot =
838 a.IntPtrConstant(PromiseUtils::kAlreadyVisitedSlot);
839
840 Node* const has_already_visited =
841 a.LoadFixedArrayElement(context, has_already_visited_slot);
842
843 // 4. If alreadyResolved.[[Value]] is true, return undefined.
844 a.GotoIf(a.SmiEqual(has_already_visited, a.SmiConstant(1)), &out);
845
846 // 5.Set alreadyResolved.[[Value]] to true.
847 a.StoreFixedArrayElement(context, has_already_visited_slot, a.SmiConstant(1));
848
849 // 2. Let promise be F.[[Promise]].
850 Node* const promise = a.LoadFixedArrayElement(
851 context, a.IntPtrConstant(PromiseUtils::kPromiseSlot));
852
853 InternalResolvePromise(&a, context, promise, value, &out);
854
855 a.Bind(&out);
856 a.Return(a.UndefinedConstant());
857 }
858
859 void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) {
860 CodeStubAssembler a(state);
861 typedef compiler::Node Node;
862 typedef CodeStubAssembler::Label Label;
863
864 Node* const promise = a.Parameter(1);
865 Node* const result = a.Parameter(2);
866 Node* const context = a.Parameter(5);
867
868 Label out(&a);
869 InternalResolvePromise(&a, context, promise, result, &out);
870
871 a.Bind(&out);
872 a.Return(a.UndefinedConstant());
873 }
874
666 } // namespace internal 875 } // namespace internal
667 } // namespace v8 876 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698