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-promise.h" | 5 #include "src/builtins/builtins-promise.h" |
6 #include "src/builtins/builtins-constructor.h" | 6 #include "src/builtins/builtins-constructor.h" |
7 #include "src/builtins/builtins-utils.h" | 7 #include "src/builtins/builtins-utils.h" |
8 #include "src/builtins/builtins.h" | 8 #include "src/builtins/builtins.h" |
9 #include "src/code-factory.h" | 9 #include "src/code-factory.h" |
10 #include "src/code-stub-assembler.h" | 10 #include "src/code-stub-assembler.h" |
(...skipping 13 matching lines...) Expand all Loading... |
24 | 24 |
25 Label out(this); | 25 Label out(this); |
26 GotoUnless(IsPromiseHookEnabled(), &out); | 26 GotoUnless(IsPromiseHookEnabled(), &out); |
27 CallRuntime(Runtime::kPromiseHookInit, context, instance, parent); | 27 CallRuntime(Runtime::kPromiseHookInit, context, instance, parent); |
28 Goto(&out); | 28 Goto(&out); |
29 | 29 |
30 Bind(&out); | 30 Bind(&out); |
31 return instance; | 31 return instance; |
32 } | 32 } |
33 | 33 |
34 Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext( | 34 std::pair<Node*, Node*> |
| 35 PromiseBuiltinsAssembler::CreatePromiseResolvingFunctions( |
35 Node* promise, Node* debug_event, Node* native_context) { | 36 Node* promise, Node* debug_event, Node* native_context) { |
36 Node* const context = | 37 Node* const promise_context = CreatePromiseResolvingFunctionsContext( |
37 Allocate(FixedArray::SizeFor(PromiseUtils::kPromiseContextLength)); | 38 promise, debug_event, native_context); |
| 39 Node* const map = LoadContextElement( |
| 40 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
| 41 Node* const resolve_info = |
| 42 LoadContextElement(native_context, Context::PROMISE_RESOLVE_SHARED_FUN); |
| 43 Node* const resolve = |
| 44 AllocateFunctionWithMapAndContext(map, resolve_info, promise_context); |
| 45 Node* const reject_info = |
| 46 LoadContextElement(native_context, Context::PROMISE_REJECT_SHARED_FUN); |
| 47 Node* const reject = |
| 48 AllocateFunctionWithMapAndContext(map, reject_info, promise_context); |
| 49 return std::make_pair(resolve, reject); |
| 50 } |
| 51 |
| 52 Node* PromiseBuiltinsAssembler::NewPromiseCapability(Node* context, |
| 53 Node* constructor, |
| 54 Node* debug_event) { |
| 55 if (debug_event == nullptr) { |
| 56 debug_event = TrueConstant(); |
| 57 } |
| 58 |
| 59 Node* native_context = LoadNativeContext(context); |
| 60 |
| 61 Node* map = LoadRoot(Heap::kJSPromiseCapabilityMapRootIndex); |
| 62 Node* capability = AllocateJSObjectFromMap(map); |
| 63 |
| 64 StoreObjectFieldNoWriteBarrier( |
| 65 capability, JSPromiseCapability::kPromiseOffset, UndefinedConstant()); |
| 66 StoreObjectFieldNoWriteBarrier( |
| 67 capability, JSPromiseCapability::kResolveOffset, UndefinedConstant()); |
| 68 StoreObjectFieldNoWriteBarrier(capability, JSPromiseCapability::kRejectOffset, |
| 69 UndefinedConstant()); |
| 70 |
| 71 Variable var_result(this, MachineRepresentation::kTagged); |
| 72 var_result.Bind(capability); |
| 73 |
| 74 Label if_builtin_promise(this), if_custom_promise(this, Label::kDeferred), |
| 75 out(this); |
| 76 Branch(WordEqual(constructor, |
| 77 LoadContextElement(native_context, |
| 78 Context::PROMISE_FUNCTION_INDEX)), |
| 79 &if_builtin_promise, &if_custom_promise); |
| 80 |
| 81 Bind(&if_builtin_promise); |
| 82 { |
| 83 Node* promise = AllocateJSPromise(context); |
| 84 PromiseInit(promise); |
| 85 StoreObjectFieldNoWriteBarrier( |
| 86 capability, JSPromiseCapability::kPromiseOffset, promise); |
| 87 |
| 88 Node* resolve = nullptr; |
| 89 Node* reject = nullptr; |
| 90 |
| 91 std::tie(resolve, reject) = |
| 92 CreatePromiseResolvingFunctions(promise, debug_event, native_context); |
| 93 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, resolve); |
| 94 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, reject); |
| 95 |
| 96 GotoUnless(IsPromiseHookEnabled(), &out); |
| 97 CallRuntime(Runtime::kPromiseHookInit, context, promise, |
| 98 UndefinedConstant()); |
| 99 Goto(&out); |
| 100 } |
| 101 |
| 102 Bind(&if_custom_promise); |
| 103 { |
| 104 Label if_notcallable(this, Label::kDeferred); |
| 105 Node* executor_context = |
| 106 CreatePromiseGetCapabilitiesExecutorContext(capability, native_context); |
| 107 Node* executor_info = LoadContextElement( |
| 108 native_context, Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN); |
| 109 Node* function_map = LoadContextElement( |
| 110 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
| 111 Node* executor = AllocateFunctionWithMapAndContext( |
| 112 function_map, executor_info, executor_context); |
| 113 |
| 114 Node* promise = ConstructJS(CodeFactory::Construct(isolate()), context, |
| 115 constructor, executor); |
| 116 |
| 117 Node* resolve = |
| 118 LoadObjectField(capability, JSPromiseCapability::kResolveOffset); |
| 119 GotoIf(TaggedIsSmi(resolve), &if_notcallable); |
| 120 GotoUnless(IsCallableMap(LoadMap(resolve)), &if_notcallable); |
| 121 |
| 122 Node* reject = |
| 123 LoadObjectField(capability, JSPromiseCapability::kRejectOffset); |
| 124 GotoIf(TaggedIsSmi(reject), &if_notcallable); |
| 125 GotoUnless(IsCallableMap(LoadMap(reject)), &if_notcallable); |
| 126 |
| 127 StoreObjectField(capability, JSPromiseCapability::kPromiseOffset, promise); |
| 128 |
| 129 Goto(&out); |
| 130 |
| 131 Bind(&if_notcallable); |
| 132 Node* message = SmiConstant(MessageTemplate::kPromiseNonCallable); |
| 133 StoreObjectField(capability, JSPromiseCapability::kPromiseOffset, |
| 134 UndefinedConstant()); |
| 135 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, |
| 136 UndefinedConstant()); |
| 137 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, |
| 138 UndefinedConstant()); |
| 139 CallRuntime(Runtime::kThrowTypeError, context, message); |
| 140 var_result.Bind(UndefinedConstant()); |
| 141 Goto(&out); |
| 142 } |
| 143 |
| 144 Bind(&out); |
| 145 return var_result.value(); |
| 146 } |
| 147 |
| 148 Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context, |
| 149 int slots) { |
| 150 DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS); |
| 151 |
| 152 Node* const context = Allocate(FixedArray::SizeFor(slots)); |
38 StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex); | 153 StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex); |
39 StoreObjectFieldNoWriteBarrier( | 154 StoreObjectFieldNoWriteBarrier(context, FixedArray::kLengthOffset, |
40 context, FixedArray::kLengthOffset, | 155 SmiConstant(slots)); |
41 SmiConstant(PromiseUtils::kPromiseContextLength)); | |
42 | 156 |
43 Node* const empty_fn = | 157 Node* const empty_fn = |
44 LoadContextElement(native_context, Context::CLOSURE_INDEX); | 158 LoadContextElement(native_context, Context::CLOSURE_INDEX); |
45 StoreContextElementNoWriteBarrier(context, Context::CLOSURE_INDEX, empty_fn); | 159 StoreContextElementNoWriteBarrier(context, Context::CLOSURE_INDEX, empty_fn); |
46 StoreContextElementNoWriteBarrier(context, Context::PREVIOUS_INDEX, | 160 StoreContextElementNoWriteBarrier(context, Context::PREVIOUS_INDEX, |
47 UndefinedConstant()); | 161 UndefinedConstant()); |
48 StoreContextElementNoWriteBarrier(context, Context::EXTENSION_INDEX, | 162 StoreContextElementNoWriteBarrier(context, Context::EXTENSION_INDEX, |
49 TheHoleConstant()); | 163 TheHoleConstant()); |
50 StoreContextElementNoWriteBarrier(context, Context::NATIVE_CONTEXT_INDEX, | 164 StoreContextElementNoWriteBarrier(context, Context::NATIVE_CONTEXT_INDEX, |
51 native_context); | 165 native_context); |
| 166 return context; |
| 167 } |
| 168 |
| 169 Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext( |
| 170 Node* promise, Node* debug_event, Node* native_context) { |
| 171 Node* const context = |
| 172 CreatePromiseContext(native_context, PromiseUtils::kPromiseContextLength); |
52 StoreContextElementNoWriteBarrier(context, PromiseUtils::kAlreadyVisitedSlot, | 173 StoreContextElementNoWriteBarrier(context, PromiseUtils::kAlreadyVisitedSlot, |
53 SmiConstant(0)); | 174 SmiConstant(0)); |
54 StoreContextElementNoWriteBarrier(context, PromiseUtils::kPromiseSlot, | 175 StoreContextElementNoWriteBarrier(context, PromiseUtils::kPromiseSlot, |
55 promise); | 176 promise); |
56 StoreContextElementNoWriteBarrier(context, PromiseUtils::kDebugEventSlot, | 177 StoreContextElementNoWriteBarrier(context, PromiseUtils::kDebugEventSlot, |
57 debug_event); | 178 debug_event); |
58 return context; | 179 return context; |
59 } | 180 } |
60 | 181 |
61 std::pair<Node*, Node*> | 182 Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext( |
62 PromiseBuiltinsAssembler::CreatePromiseResolvingFunctions( | 183 Node* promise_capability, Node* native_context) { |
63 Node* promise, Node* debug_event, Node* native_context) { | 184 int kContextLength = GetPromiseCapabilityExecutor::kContextLength; |
64 Node* const promise_context = CreatePromiseResolvingFunctionsContext( | 185 Node* context = CreatePromiseContext(native_context, kContextLength); |
65 promise, debug_event, native_context); | 186 StoreContextElementNoWriteBarrier( |
66 Node* const map = LoadContextElement( | 187 context, GetPromiseCapabilityExecutor::kCapabilitySlot, |
67 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | 188 promise_capability); |
68 Node* const resolve_info = | 189 return context; |
69 LoadContextElement(native_context, Context::PROMISE_RESOLVE_SHARED_FUN); | |
70 Node* const resolve = | |
71 AllocateFunctionWithMapAndContext(map, resolve_info, promise_context); | |
72 Node* const reject_info = | |
73 LoadContextElement(native_context, Context::PROMISE_REJECT_SHARED_FUN); | |
74 Node* const reject = | |
75 AllocateFunctionWithMapAndContext(map, reject_info, promise_context); | |
76 | |
77 return std::make_pair(resolve, reject); | |
78 } | 190 } |
79 | 191 |
80 Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( | 192 Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( |
81 Node* context, Node* value, MessageTemplate::Template msg_template) { | 193 Node* context, Node* value, MessageTemplate::Template msg_template) { |
82 Label out(this), throw_exception(this, Label::kDeferred); | 194 Label out(this), throw_exception(this, Label::kDeferred); |
83 Variable var_value_map(this, MachineRepresentation::kTagged); | 195 Variable var_value_map(this, MachineRepresentation::kTagged); |
84 | 196 |
85 GotoIf(TaggedIsSmi(value), &throw_exception); | 197 GotoIf(TaggedIsSmi(value), &throw_exception); |
86 | 198 |
87 // Load the instance type of the {value}. | 199 // Load the instance type of the {value}. |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
227 { | 339 { |
228 Node* const deferred_promise = AllocateAndInitPromise(context, promise); | 340 Node* const deferred_promise = AllocateAndInitPromise(context, promise); |
229 var_deferred_promise.Bind(deferred_promise); | 341 var_deferred_promise.Bind(deferred_promise); |
230 var_deferred_on_resolve.Bind(UndefinedConstant()); | 342 var_deferred_on_resolve.Bind(UndefinedConstant()); |
231 var_deferred_on_reject.Bind(UndefinedConstant()); | 343 var_deferred_on_reject.Bind(UndefinedConstant()); |
232 Goto(&perform_promise_then); | 344 Goto(&perform_promise_then); |
233 } | 345 } |
234 | 346 |
235 Bind(&promise_capability); | 347 Bind(&promise_capability); |
236 { | 348 { |
237 // TODO(gsathya): Move this to TF. | 349 Node* const capability = NewPromiseCapability(context, constructor); |
238 Node* const new_promise_capability = LoadContextElement( | 350 var_deferred_promise.Bind( |
239 native_context, Context::NEW_PROMISE_CAPABILITY_INDEX); | 351 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset)); |
240 Node* const deferred = | 352 var_deferred_on_resolve.Bind( |
241 CallJS(call_callable, context, new_promise_capability, | 353 LoadObjectField(capability, JSPromiseCapability::kResolveOffset)); |
242 UndefinedConstant(), constructor); | 354 var_deferred_on_reject.Bind( |
243 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 355 LoadObjectField(capability, JSPromiseCapability::kRejectOffset)); |
244 Node* key = HeapConstant(isolate->factory()->promise_string()); | |
245 Node* const deferred_promise = | |
246 CallStub(getproperty_callable, context, deferred, key); | |
247 var_deferred_promise.Bind(deferred_promise); | |
248 | |
249 key = HeapConstant(isolate->factory()->resolve_string()); | |
250 Node* const deferred_on_resolve = | |
251 CallStub(getproperty_callable, context, deferred, key); | |
252 var_deferred_on_resolve.Bind(deferred_on_resolve); | |
253 | |
254 key = HeapConstant(isolate->factory()->reject_string()); | |
255 Node* const deferred_on_reject = | |
256 CallStub(getproperty_callable, context, deferred, key); | |
257 var_deferred_on_reject.Bind(deferred_on_reject); | |
258 | |
259 Goto(&perform_promise_then); | 356 Goto(&perform_promise_then); |
260 } | 357 } |
261 | 358 |
262 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, | 359 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, |
263 // resultCapability). | 360 // resultCapability). |
264 Bind(&perform_promise_then); | 361 Bind(&perform_promise_then); |
265 Node* const result = InternalPerformPromiseThen( | 362 Node* const result = InternalPerformPromiseThen( |
266 context, promise, on_resolve, on_reject, var_deferred_promise.value(), | 363 context, promise, on_resolve, on_reject, var_deferred_promise.value(), |
267 var_deferred_on_resolve.value(), var_deferred_on_reject.value()); | 364 var_deferred_on_resolve.value(), var_deferred_on_reject.value()); |
268 return result; | 365 return result; |
(...skipping 395 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
664 handle(PromiseUtils::GetDebugEvent(context), isolate); | 761 handle(PromiseUtils::GetDebugEvent(context), isolate); |
665 MaybeHandle<Object> maybe_result; | 762 MaybeHandle<Object> maybe_result; |
666 Handle<Object> argv[] = {promise, value, debug_event}; | 763 Handle<Object> argv[] = {promise, value, debug_event}; |
667 RETURN_FAILURE_ON_EXCEPTION( | 764 RETURN_FAILURE_ON_EXCEPTION( |
668 isolate, Execution::Call(isolate, isolate->promise_internal_reject(), | 765 isolate, Execution::Call(isolate, isolate->promise_internal_reject(), |
669 isolate->factory()->undefined_value(), | 766 isolate->factory()->undefined_value(), |
670 arraysize(argv), argv)); | 767 arraysize(argv), argv)); |
671 return isolate->heap()->undefined_value(); | 768 return isolate->heap()->undefined_value(); |
672 } | 769 } |
673 | 770 |
674 // ES#sec-createresolvingfunctions | |
675 // CreateResolvingFunctions ( promise ) | |
676 TF_BUILTIN(CreateResolvingFunctions, PromiseBuiltinsAssembler) { | |
677 Node* const promise = Parameter(1); | |
678 Node* const debug_event = Parameter(2); | |
679 Node* const context = Parameter(5); | |
680 Node* const native_context = LoadNativeContext(context); | |
681 | |
682 Node* resolve = nullptr; | |
683 Node* reject = nullptr; | |
684 | |
685 std::tie(resolve, reject) = | |
686 CreatePromiseResolvingFunctions(promise, debug_event, native_context); | |
687 | |
688 Node* const kSize = IntPtrConstant(2); | |
689 const ElementsKind kind = FAST_ELEMENTS; | |
690 const WriteBarrierMode barrier_mode = SKIP_WRITE_BARRIER; | |
691 const ParameterMode parameter_mode = INTPTR_PARAMETERS; | |
692 Node* const arr = AllocateFixedArray(kind, kSize, parameter_mode); | |
693 StoreFixedArrayElement(arr, 0, resolve, barrier_mode); | |
694 StoreFixedArrayElement(arr, 1, reject, barrier_mode); | |
695 | |
696 Node* const array_map = LoadJSArrayElementsMap(kind, native_context); | |
697 Node* const length = SmiTag(kSize); | |
698 Node* const result = AllocateUninitializedJSArrayWithoutElements( | |
699 kind, array_map, length, nullptr); | |
700 | |
701 StoreObjectField(result, JSObject::kElementsOffset, arr); | |
702 Return(result); | |
703 } | |
704 | |
705 TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { | 771 TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { |
706 Node* const executor = Parameter(1); | 772 Node* const executor = Parameter(1); |
707 Node* const new_target = Parameter(2); | 773 Node* const new_target = Parameter(2); |
708 Node* const context = Parameter(4); | 774 Node* const context = Parameter(4); |
709 Isolate* isolate = this->isolate(); | 775 Isolate* isolate = this->isolate(); |
710 | 776 |
711 Label if_targetisundefined(this, Label::kDeferred); | 777 Label if_targetisundefined(this, Label::kDeferred); |
712 | 778 |
713 GotoIf(IsUndefined(new_target), &if_targetisundefined); | 779 GotoIf(IsUndefined(new_target), &if_targetisundefined); |
714 | 780 |
(...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1062 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 1128 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
1063 Node* const then = | 1129 Node* const then = |
1064 CallStub(getproperty_callable, context, promise, then_str); | 1130 CallStub(getproperty_callable, context, promise, then_str); |
1065 Callable call_callable = CodeFactory::Call(isolate); | 1131 Callable call_callable = CodeFactory::Call(isolate); |
1066 Node* const result = | 1132 Node* const result = |
1067 CallJS(call_callable, context, then, promise, on_resolve, on_reject); | 1133 CallJS(call_callable, context, then, promise, on_resolve, on_reject); |
1068 Return(result); | 1134 Return(result); |
1069 } | 1135 } |
1070 } | 1136 } |
1071 | 1137 |
| 1138 TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { |
| 1139 Node* const resolve = Parameter(1); |
| 1140 Node* const reject = Parameter(2); |
| 1141 Node* const context = Parameter(5); |
| 1142 |
| 1143 Node* const capability = LoadContextElement( |
| 1144 context, GetPromiseCapabilityExecutor::kCapabilitySlot); |
| 1145 |
| 1146 Label if_alreadyinvoked(this, Label::kDeferred); |
| 1147 GotoIf(WordNotEqual( |
| 1148 LoadObjectField(capability, JSPromiseCapability::kResolveOffset), |
| 1149 UndefinedConstant()), |
| 1150 &if_alreadyinvoked); |
| 1151 GotoIf(WordNotEqual( |
| 1152 LoadObjectField(capability, JSPromiseCapability::kRejectOffset), |
| 1153 UndefinedConstant()), |
| 1154 &if_alreadyinvoked); |
| 1155 |
| 1156 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, resolve); |
| 1157 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, reject); |
| 1158 |
| 1159 Return(UndefinedConstant()); |
| 1160 |
| 1161 Bind(&if_alreadyinvoked); |
| 1162 Node* message = SmiConstant(MessageTemplate::kPromiseExecutorAlreadyInvoked); |
| 1163 Return(CallRuntime(Runtime::kThrowTypeError, context, message)); |
| 1164 } |
| 1165 |
| 1166 TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) { |
| 1167 Node* constructor = Parameter(1); |
| 1168 Node* debug_event = Parameter(2); |
| 1169 Node* context = Parameter(5); |
| 1170 |
| 1171 CSA_ASSERT_JS_ARGC_EQ(this, 2); |
| 1172 |
| 1173 Return(NewPromiseCapability(context, constructor, debug_event)); |
| 1174 } |
| 1175 |
1072 } // namespace internal | 1176 } // namespace internal |
1073 } // namespace v8 | 1177 } // namespace v8 |
OLD | NEW |