OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "src/builtins/builtins-promise.h" | |
6 #include "src/builtins/builtins-constructor.h" | |
7 #include "src/builtins/builtins-utils.h" | |
8 #include "src/builtins/builtins.h" | |
9 #include "src/code-factory.h" | |
10 #include "src/code-stub-assembler.h" | |
11 #include "src/objects-inl.h" | |
12 | |
13 namespace v8 { | |
14 namespace internal { | |
15 | |
16 Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) { | |
17 Node* const native_context = LoadNativeContext(context); | |
18 Node* const promise_fun = | |
19 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
20 Node* const initial_map = | |
21 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
22 Node* const instance = AllocateJSObjectFromMap(initial_map); | |
23 return instance; | |
24 } | |
25 | |
26 void PromiseBuiltinsAssembler::PromiseInit(Node* promise) { | |
27 StoreObjectField(promise, JSPromise::kStatusOffset, | |
28 SmiConstant(v8::Promise::kPending)); | |
29 StoreObjectField(promise, JSPromise::kFlagsOffset, SmiConstant(0)); | |
30 } | |
31 | |
32 Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context) { | |
33 return AllocateAndInitJSPromise(context, UndefinedConstant()); | |
34 } | |
35 | |
36 Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context, | |
37 Node* parent) { | |
38 Node* const instance = AllocateJSPromise(context); | |
39 PromiseInit(instance); | |
40 | |
41 Label out(this); | |
42 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); | |
43 CallRuntime(Runtime::kPromiseHookInit, context, instance, parent); | |
44 Goto(&out); | |
45 | |
46 Bind(&out); | |
47 return instance; | |
48 } | |
49 | |
50 Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise(Node* context, | |
51 Node* status, | |
52 Node* result) { | |
53 CSA_ASSERT(this, TaggedIsSmi(status)); | |
54 | |
55 Node* const instance = AllocateJSPromise(context); | |
56 | |
57 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kStatusOffset, status); | |
58 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kResultOffset, result); | |
59 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset, | |
60 SmiConstant(0)); | |
61 | |
62 Label out(this); | |
63 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); | |
64 CallRuntime(Runtime::kPromiseHookInit, context, instance, | |
65 UndefinedConstant()); | |
66 Goto(&out); | |
67 | |
68 Bind(&out); | |
69 return instance; | |
70 } | |
71 | |
72 std::pair<Node*, Node*> | |
73 PromiseBuiltinsAssembler::CreatePromiseResolvingFunctions( | |
74 Node* promise, Node* debug_event, Node* native_context) { | |
75 Node* const promise_context = CreatePromiseResolvingFunctionsContext( | |
76 promise, debug_event, native_context); | |
77 Node* const map = LoadContextElement( | |
78 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
79 Node* const resolve_info = | |
80 LoadContextElement(native_context, Context::PROMISE_RESOLVE_SHARED_FUN); | |
81 Node* const resolve = | |
82 AllocateFunctionWithMapAndContext(map, resolve_info, promise_context); | |
83 Node* const reject_info = | |
84 LoadContextElement(native_context, Context::PROMISE_REJECT_SHARED_FUN); | |
85 Node* const reject = | |
86 AllocateFunctionWithMapAndContext(map, reject_info, promise_context); | |
87 return std::make_pair(resolve, reject); | |
88 } | |
89 | |
90 Node* PromiseBuiltinsAssembler::NewPromiseCapability(Node* context, | |
91 Node* constructor, | |
92 Node* debug_event) { | |
93 if (debug_event == nullptr) { | |
94 debug_event = TrueConstant(); | |
95 } | |
96 | |
97 Node* native_context = LoadNativeContext(context); | |
98 | |
99 Node* map = LoadRoot(Heap::kJSPromiseCapabilityMapRootIndex); | |
100 Node* capability = AllocateJSObjectFromMap(map); | |
101 | |
102 StoreObjectFieldNoWriteBarrier( | |
103 capability, JSPromiseCapability::kPromiseOffset, UndefinedConstant()); | |
104 StoreObjectFieldNoWriteBarrier( | |
105 capability, JSPromiseCapability::kResolveOffset, UndefinedConstant()); | |
106 StoreObjectFieldNoWriteBarrier(capability, JSPromiseCapability::kRejectOffset, | |
107 UndefinedConstant()); | |
108 | |
109 Variable var_result(this, MachineRepresentation::kTagged); | |
110 var_result.Bind(capability); | |
111 | |
112 Label if_builtin_promise(this), if_custom_promise(this, Label::kDeferred), | |
113 out(this); | |
114 Branch(WordEqual(constructor, | |
115 LoadContextElement(native_context, | |
116 Context::PROMISE_FUNCTION_INDEX)), | |
117 &if_builtin_promise, &if_custom_promise); | |
118 | |
119 Bind(&if_builtin_promise); | |
120 { | |
121 Node* promise = AllocateJSPromise(context); | |
122 PromiseInit(promise); | |
123 StoreObjectFieldNoWriteBarrier( | |
124 capability, JSPromiseCapability::kPromiseOffset, promise); | |
125 | |
126 Node* resolve = nullptr; | |
127 Node* reject = nullptr; | |
128 | |
129 std::tie(resolve, reject) = | |
130 CreatePromiseResolvingFunctions(promise, debug_event, native_context); | |
131 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, resolve); | |
132 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, reject); | |
133 | |
134 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); | |
135 CallRuntime(Runtime::kPromiseHookInit, context, promise, | |
136 UndefinedConstant()); | |
137 Goto(&out); | |
138 } | |
139 | |
140 Bind(&if_custom_promise); | |
141 { | |
142 Label if_notcallable(this, Label::kDeferred); | |
143 Node* executor_context = | |
144 CreatePromiseGetCapabilitiesExecutorContext(capability, native_context); | |
145 Node* executor_info = LoadContextElement( | |
146 native_context, Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN); | |
147 Node* function_map = LoadContextElement( | |
148 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
149 Node* executor = AllocateFunctionWithMapAndContext( | |
150 function_map, executor_info, executor_context); | |
151 | |
152 Node* promise = ConstructJS(CodeFactory::Construct(isolate()), context, | |
153 constructor, executor); | |
154 | |
155 Node* resolve = | |
156 LoadObjectField(capability, JSPromiseCapability::kResolveOffset); | |
157 GotoIf(TaggedIsSmi(resolve), &if_notcallable); | |
158 GotoIfNot(IsCallableMap(LoadMap(resolve)), &if_notcallable); | |
159 | |
160 Node* reject = | |
161 LoadObjectField(capability, JSPromiseCapability::kRejectOffset); | |
162 GotoIf(TaggedIsSmi(reject), &if_notcallable); | |
163 GotoIfNot(IsCallableMap(LoadMap(reject)), &if_notcallable); | |
164 | |
165 StoreObjectField(capability, JSPromiseCapability::kPromiseOffset, promise); | |
166 | |
167 Goto(&out); | |
168 | |
169 Bind(&if_notcallable); | |
170 Node* message = SmiConstant(MessageTemplate::kPromiseNonCallable); | |
171 StoreObjectField(capability, JSPromiseCapability::kPromiseOffset, | |
172 UndefinedConstant()); | |
173 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, | |
174 UndefinedConstant()); | |
175 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, | |
176 UndefinedConstant()); | |
177 CallRuntime(Runtime::kThrowTypeError, context, message); | |
178 Unreachable(); | |
179 } | |
180 | |
181 Bind(&out); | |
182 return var_result.value(); | |
183 } | |
184 | |
185 Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context, | |
186 int slots) { | |
187 DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS); | |
188 | |
189 Node* const context = Allocate(FixedArray::SizeFor(slots)); | |
190 StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex); | |
191 StoreObjectFieldNoWriteBarrier(context, FixedArray::kLengthOffset, | |
192 SmiConstant(slots)); | |
193 | |
194 Node* const empty_fn = | |
195 LoadContextElement(native_context, Context::CLOSURE_INDEX); | |
196 StoreContextElementNoWriteBarrier(context, Context::CLOSURE_INDEX, empty_fn); | |
197 StoreContextElementNoWriteBarrier(context, Context::PREVIOUS_INDEX, | |
198 UndefinedConstant()); | |
199 StoreContextElementNoWriteBarrier(context, Context::EXTENSION_INDEX, | |
200 TheHoleConstant()); | |
201 StoreContextElementNoWriteBarrier(context, Context::NATIVE_CONTEXT_INDEX, | |
202 native_context); | |
203 return context; | |
204 } | |
205 | |
206 Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext( | |
207 Node* promise, Node* debug_event, Node* native_context) { | |
208 Node* const context = | |
209 CreatePromiseContext(native_context, kPromiseContextLength); | |
210 StoreContextElementNoWriteBarrier(context, kAlreadyVisitedSlot, | |
211 SmiConstant(0)); | |
212 StoreContextElementNoWriteBarrier(context, kPromiseSlot, promise); | |
213 StoreContextElementNoWriteBarrier(context, kDebugEventSlot, debug_event); | |
214 return context; | |
215 } | |
216 | |
217 Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext( | |
218 Node* promise_capability, Node* native_context) { | |
219 int kContextLength = kCapabilitiesContextLength; | |
220 Node* context = CreatePromiseContext(native_context, kContextLength); | |
221 StoreContextElementNoWriteBarrier(context, kCapabilitySlot, | |
222 promise_capability); | |
223 return context; | |
224 } | |
225 | |
226 Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( | |
227 Node* context, Node* value, MessageTemplate::Template msg_template, | |
228 const char* method_name) { | |
229 Label out(this), throw_exception(this, Label::kDeferred); | |
230 Variable var_value_map(this, MachineRepresentation::kTagged); | |
231 | |
232 GotoIf(TaggedIsSmi(value), &throw_exception); | |
233 | |
234 // Load the instance type of the {value}. | |
235 var_value_map.Bind(LoadMap(value)); | |
236 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); | |
237 | |
238 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); | |
239 | |
240 // The {value} is not a compatible receiver for this method. | |
241 Bind(&throw_exception); | |
242 { | |
243 Node* const method = | |
244 method_name == nullptr | |
245 ? UndefinedConstant() | |
246 : HeapConstant( | |
247 isolate()->factory()->NewStringFromAsciiChecked(method_name)); | |
248 Node* const message_id = SmiConstant(msg_template); | |
249 CallRuntime(Runtime::kThrowTypeError, context, message_id, method); | |
250 Unreachable(); | |
251 } | |
252 | |
253 Bind(&out); | |
254 return var_value_map.value(); | |
255 } | |
256 | |
257 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) { | |
258 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); | |
259 return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit); | |
260 } | |
261 | |
262 void PromiseBuiltinsAssembler::PromiseSetHasHandler(Node* promise) { | |
263 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); | |
264 Node* const new_flags = | |
265 SmiOr(flags, SmiConstant(1 << JSPromise::kHasHandlerBit)); | |
266 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags); | |
267 } | |
268 | |
269 void PromiseBuiltinsAssembler::PromiseSetHandledHint(Node* promise) { | |
270 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); | |
271 Node* const new_flags = | |
272 SmiOr(flags, SmiConstant(1 << JSPromise::kHandledHintBit)); | |
273 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags); | |
274 } | |
275 | |
276 Node* PromiseBuiltinsAssembler::SpeciesConstructor(Node* context, Node* object, | |
277 Node* default_constructor) { | |
278 Isolate* isolate = this->isolate(); | |
279 Variable var_result(this, MachineRepresentation::kTagged); | |
280 var_result.Bind(default_constructor); | |
281 | |
282 // 2. Let C be ? Get(O, "constructor"). | |
283 Node* const constructor = | |
284 GetProperty(context, object, isolate->factory()->constructor_string()); | |
285 | |
286 // 3. If C is undefined, return defaultConstructor. | |
287 Label out(this); | |
288 GotoIf(IsUndefined(constructor), &out); | |
289 | |
290 // 4. If Type(C) is not Object, throw a TypeError exception. | |
291 ThrowIfNotJSReceiver(context, constructor, | |
292 MessageTemplate::kConstructorNotReceiver); | |
293 | |
294 // 5. Let S be ? Get(C, @@species). | |
295 Node* const species = | |
296 GetProperty(context, constructor, isolate->factory()->species_symbol()); | |
297 | |
298 // 6. If S is either undefined or null, return defaultConstructor. | |
299 GotoIf(IsUndefined(species), &out); | |
300 GotoIf(WordEqual(species, NullConstant()), &out); | |
301 | |
302 // 7. If IsConstructor(S) is true, return S. | |
303 Label throw_error(this); | |
304 Node* species_bitfield = LoadMapBitField(LoadMap(species)); | |
305 GotoIfNot(Word32Equal(Word32And(species_bitfield, | |
306 Int32Constant((1 << Map::kIsConstructor))), | |
307 Int32Constant(1 << Map::kIsConstructor)), | |
308 &throw_error); | |
309 var_result.Bind(species); | |
310 Goto(&out); | |
311 | |
312 // 8. Throw a TypeError exception. | |
313 Bind(&throw_error); | |
314 { | |
315 Node* const message_id = | |
316 SmiConstant(MessageTemplate::kSpeciesNotConstructor); | |
317 CallRuntime(Runtime::kThrowTypeError, context, message_id); | |
318 Unreachable(); | |
319 } | |
320 | |
321 Bind(&out); | |
322 return var_result.value(); | |
323 } | |
324 | |
325 void PromiseBuiltinsAssembler::AppendPromiseCallback(int offset, Node* promise, | |
326 Node* value) { | |
327 Node* elements = LoadObjectField(promise, offset); | |
328 Node* length = LoadFixedArrayBaseLength(elements); | |
329 CodeStubAssembler::ParameterMode mode = OptimalParameterMode(); | |
330 length = TaggedToParameter(length, mode); | |
331 | |
332 Node* delta = IntPtrOrSmiConstant(1, mode); | |
333 Node* new_capacity = IntPtrOrSmiAdd(length, delta, mode); | |
334 | |
335 const ElementsKind kind = FAST_ELEMENTS; | |
336 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; | |
337 const CodeStubAssembler::AllocationFlags flags = | |
338 CodeStubAssembler::kAllowLargeObjectAllocation; | |
339 int additional_offset = 0; | |
340 | |
341 Node* new_elements = AllocateFixedArray(kind, new_capacity, mode, flags); | |
342 | |
343 CopyFixedArrayElements(kind, elements, new_elements, length, barrier_mode, | |
344 mode); | |
345 StoreFixedArrayElement(new_elements, length, value, barrier_mode, | |
346 additional_offset, mode); | |
347 | |
348 StoreObjectField(promise, offset, new_elements); | |
349 } | |
350 | |
351 Node* PromiseBuiltinsAssembler::InternalPromiseThen(Node* context, | |
352 Node* promise, | |
353 Node* on_resolve, | |
354 Node* on_reject) { | |
355 Isolate* isolate = this->isolate(); | |
356 | |
357 // 2. If IsPromise(promise) is false, throw a TypeError exception. | |
358 ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, | |
359 "Promise.prototype.then"); | |
360 | |
361 Node* const native_context = LoadNativeContext(context); | |
362 Node* const promise_fun = | |
363 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
364 | |
365 // 3. Let C be ? SpeciesConstructor(promise, %Promise%). | |
366 Node* constructor = SpeciesConstructor(context, promise, promise_fun); | |
367 | |
368 // 4. Let resultCapability be ? NewPromiseCapability(C). | |
369 Callable call_callable = CodeFactory::Call(isolate); | |
370 Label fast_promise_capability(this), promise_capability(this), | |
371 perform_promise_then(this); | |
372 Variable var_deferred_promise(this, MachineRepresentation::kTagged), | |
373 var_deferred_on_resolve(this, MachineRepresentation::kTagged), | |
374 var_deferred_on_reject(this, MachineRepresentation::kTagged); | |
375 | |
376 Branch(WordEqual(promise_fun, constructor), &fast_promise_capability, | |
377 &promise_capability); | |
378 | |
379 Bind(&fast_promise_capability); | |
380 { | |
381 Node* const deferred_promise = AllocateAndInitJSPromise(context, promise); | |
382 var_deferred_promise.Bind(deferred_promise); | |
383 var_deferred_on_resolve.Bind(UndefinedConstant()); | |
384 var_deferred_on_reject.Bind(UndefinedConstant()); | |
385 Goto(&perform_promise_then); | |
386 } | |
387 | |
388 Bind(&promise_capability); | |
389 { | |
390 Node* const capability = NewPromiseCapability(context, constructor); | |
391 var_deferred_promise.Bind( | |
392 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset)); | |
393 var_deferred_on_resolve.Bind( | |
394 LoadObjectField(capability, JSPromiseCapability::kResolveOffset)); | |
395 var_deferred_on_reject.Bind( | |
396 LoadObjectField(capability, JSPromiseCapability::kRejectOffset)); | |
397 Goto(&perform_promise_then); | |
398 } | |
399 | |
400 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, | |
401 // resultCapability). | |
402 Bind(&perform_promise_then); | |
403 Node* const result = InternalPerformPromiseThen( | |
404 context, promise, on_resolve, on_reject, var_deferred_promise.value(), | |
405 var_deferred_on_resolve.value(), var_deferred_on_reject.value()); | |
406 return result; | |
407 } | |
408 | |
409 Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen( | |
410 Node* context, Node* promise, Node* on_resolve, Node* on_reject, | |
411 Node* deferred_promise, Node* deferred_on_resolve, | |
412 Node* deferred_on_reject) { | |
413 | |
414 Variable var_on_resolve(this, MachineRepresentation::kTagged), | |
415 var_on_reject(this, MachineRepresentation::kTagged); | |
416 | |
417 var_on_resolve.Bind(on_resolve); | |
418 var_on_reject.Bind(on_reject); | |
419 | |
420 Label out(this), if_onresolvenotcallable(this), onrejectcheck(this), | |
421 append_callbacks(this); | |
422 GotoIf(TaggedIsSmi(on_resolve), &if_onresolvenotcallable); | |
423 | |
424 Isolate* isolate = this->isolate(); | |
425 Node* const on_resolve_map = LoadMap(on_resolve); | |
426 Branch(IsCallableMap(on_resolve_map), &onrejectcheck, | |
427 &if_onresolvenotcallable); | |
428 | |
429 Bind(&if_onresolvenotcallable); | |
430 { | |
431 Node* const default_resolve_handler_symbol = HeapConstant( | |
432 isolate->factory()->promise_default_resolve_handler_symbol()); | |
433 var_on_resolve.Bind(default_resolve_handler_symbol); | |
434 Goto(&onrejectcheck); | |
435 } | |
436 | |
437 Bind(&onrejectcheck); | |
438 { | |
439 Label if_onrejectnotcallable(this); | |
440 GotoIf(TaggedIsSmi(on_reject), &if_onrejectnotcallable); | |
441 | |
442 Node* const on_reject_map = LoadMap(on_reject); | |
443 Branch(IsCallableMap(on_reject_map), &append_callbacks, | |
444 &if_onrejectnotcallable); | |
445 | |
446 Bind(&if_onrejectnotcallable); | |
447 { | |
448 Node* const default_reject_handler_symbol = HeapConstant( | |
449 isolate->factory()->promise_default_reject_handler_symbol()); | |
450 var_on_reject.Bind(default_reject_handler_symbol); | |
451 Goto(&append_callbacks); | |
452 } | |
453 } | |
454 | |
455 Bind(&append_callbacks); | |
456 { | |
457 Label fulfilled_check(this); | |
458 Node* const status = LoadObjectField(promise, JSPromise::kStatusOffset); | |
459 GotoIfNot(SmiEqual(status, SmiConstant(v8::Promise::kPending)), | |
460 &fulfilled_check); | |
461 | |
462 Node* const existing_deferred_promise = | |
463 LoadObjectField(promise, JSPromise::kDeferredPromiseOffset); | |
464 | |
465 Label if_noexistingcallbacks(this), if_existingcallbacks(this); | |
466 Branch(IsUndefined(existing_deferred_promise), &if_noexistingcallbacks, | |
467 &if_existingcallbacks); | |
468 | |
469 Bind(&if_noexistingcallbacks); | |
470 { | |
471 // Store callbacks directly in the slots. | |
472 StoreObjectField(promise, JSPromise::kDeferredPromiseOffset, | |
473 deferred_promise); | |
474 StoreObjectField(promise, JSPromise::kDeferredOnResolveOffset, | |
475 deferred_on_resolve); | |
476 StoreObjectField(promise, JSPromise::kDeferredOnRejectOffset, | |
477 deferred_on_reject); | |
478 StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, | |
479 var_on_resolve.value()); | |
480 StoreObjectField(promise, JSPromise::kRejectReactionsOffset, | |
481 var_on_reject.value()); | |
482 Goto(&out); | |
483 } | |
484 | |
485 Bind(&if_existingcallbacks); | |
486 { | |
487 Label if_singlecallback(this), if_multiplecallbacks(this); | |
488 BranchIfJSObject(existing_deferred_promise, &if_singlecallback, | |
489 &if_multiplecallbacks); | |
490 | |
491 Bind(&if_singlecallback); | |
492 { | |
493 // Create new FixedArrays to store callbacks, and migrate | |
494 // existing callbacks. | |
495 Node* const deferred_promise_arr = | |
496 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
497 StoreFixedArrayElement(deferred_promise_arr, 0, | |
498 existing_deferred_promise); | |
499 StoreFixedArrayElement(deferred_promise_arr, 1, deferred_promise); | |
500 | |
501 Node* const deferred_on_resolve_arr = | |
502 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
503 StoreFixedArrayElement( | |
504 deferred_on_resolve_arr, 0, | |
505 LoadObjectField(promise, JSPromise::kDeferredOnResolveOffset)); | |
506 StoreFixedArrayElement(deferred_on_resolve_arr, 1, deferred_on_resolve); | |
507 | |
508 Node* const deferred_on_reject_arr = | |
509 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
510 StoreFixedArrayElement( | |
511 deferred_on_reject_arr, 0, | |
512 LoadObjectField(promise, JSPromise::kDeferredOnRejectOffset)); | |
513 StoreFixedArrayElement(deferred_on_reject_arr, 1, deferred_on_reject); | |
514 | |
515 Node* const fulfill_reactions = | |
516 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
517 StoreFixedArrayElement( | |
518 fulfill_reactions, 0, | |
519 LoadObjectField(promise, JSPromise::kFulfillReactionsOffset)); | |
520 StoreFixedArrayElement(fulfill_reactions, 1, var_on_resolve.value()); | |
521 | |
522 Node* const reject_reactions = | |
523 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
524 StoreFixedArrayElement( | |
525 reject_reactions, 0, | |
526 LoadObjectField(promise, JSPromise::kRejectReactionsOffset)); | |
527 StoreFixedArrayElement(reject_reactions, 1, var_on_reject.value()); | |
528 | |
529 // Store new FixedArrays in promise. | |
530 StoreObjectField(promise, JSPromise::kDeferredPromiseOffset, | |
531 deferred_promise_arr); | |
532 StoreObjectField(promise, JSPromise::kDeferredOnResolveOffset, | |
533 deferred_on_resolve_arr); | |
534 StoreObjectField(promise, JSPromise::kDeferredOnRejectOffset, | |
535 deferred_on_reject_arr); | |
536 StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, | |
537 fulfill_reactions); | |
538 StoreObjectField(promise, JSPromise::kRejectReactionsOffset, | |
539 reject_reactions); | |
540 Goto(&out); | |
541 } | |
542 | |
543 Bind(&if_multiplecallbacks); | |
544 { | |
545 AppendPromiseCallback(JSPromise::kDeferredPromiseOffset, promise, | |
546 deferred_promise); | |
547 AppendPromiseCallback(JSPromise::kDeferredOnResolveOffset, promise, | |
548 deferred_on_resolve); | |
549 AppendPromiseCallback(JSPromise::kDeferredOnRejectOffset, promise, | |
550 deferred_on_reject); | |
551 AppendPromiseCallback(JSPromise::kFulfillReactionsOffset, promise, | |
552 var_on_resolve.value()); | |
553 AppendPromiseCallback(JSPromise::kRejectReactionsOffset, promise, | |
554 var_on_reject.value()); | |
555 Goto(&out); | |
556 } | |
557 } | |
558 | |
559 Bind(&fulfilled_check); | |
560 { | |
561 Label reject(this); | |
562 Node* const result = LoadObjectField(promise, JSPromise::kResultOffset); | |
563 GotoIfNot(WordEqual(status, SmiConstant(v8::Promise::kFulfilled)), | |
564 &reject); | |
565 | |
566 Node* info = AllocatePromiseReactionJobInfo( | |
567 result, var_on_resolve.value(), deferred_promise, deferred_on_resolve, | |
568 deferred_on_reject, context); | |
569 // TODO(gsathya): Move this to TF | |
570 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); | |
571 Goto(&out); | |
572 | |
573 Bind(&reject); | |
574 { | |
575 Node* const has_handler = PromiseHasHandler(promise); | |
576 Label enqueue(this); | |
577 | |
578 // TODO(gsathya): Fold these runtime calls and move to TF. | |
579 GotoIf(has_handler, &enqueue); | |
580 CallRuntime(Runtime::kPromiseRevokeReject, context, promise); | |
581 Goto(&enqueue); | |
582 | |
583 Bind(&enqueue); | |
584 { | |
585 Node* info = AllocatePromiseReactionJobInfo( | |
586 result, var_on_reject.value(), deferred_promise, | |
587 deferred_on_resolve, deferred_on_reject, context); | |
588 // TODO(gsathya): Move this to TF | |
589 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); | |
590 Goto(&out); | |
591 } | |
592 } | |
593 } | |
594 } | |
595 | |
596 Bind(&out); | |
597 PromiseSetHasHandler(promise); | |
598 return deferred_promise; | |
599 } | |
600 | |
601 // Promise fast path implementations rely on unmodified JSPromise instances. | |
602 // We use a fairly coarse granularity for this and simply check whether both | |
603 // the promise itself is unmodified (i.e. its map has not changed) and its | |
604 // prototype is unmodified. | |
605 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp | |
606 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise, | |
607 Label* if_isunmodified, | |
608 Label* if_ismodified) { | |
609 Node* const native_context = LoadNativeContext(context); | |
610 Node* const promise_fun = | |
611 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
612 BranchIfFastPath(native_context, promise_fun, promise, if_isunmodified, | |
613 if_ismodified); | |
614 } | |
615 | |
616 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* native_context, | |
617 Node* promise_fun, | |
618 Node* promise, | |
619 Label* if_isunmodified, | |
620 Label* if_ismodified) { | |
621 CSA_ASSERT(this, IsNativeContext(native_context)); | |
622 CSA_ASSERT(this, | |
623 WordEqual(promise_fun, | |
624 LoadContextElement(native_context, | |
625 Context::PROMISE_FUNCTION_INDEX))); | |
626 | |
627 Node* const map = LoadMap(promise); | |
628 Node* const initial_map = | |
629 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
630 Node* const has_initialmap = WordEqual(map, initial_map); | |
631 | |
632 GotoIfNot(has_initialmap, if_ismodified); | |
633 | |
634 Node* const initial_proto_initial_map = | |
635 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); | |
636 Node* const proto_map = LoadMap(LoadMapPrototype(map)); | |
637 Node* const proto_has_initialmap = | |
638 WordEqual(proto_map, initial_proto_initial_map); | |
639 | |
640 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | |
641 } | |
642 | |
643 Node* PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobInfo( | |
644 Node* thenable, Node* then, Node* resolve, Node* reject, Node* context) { | |
645 Node* const info = Allocate(PromiseResolveThenableJobInfo::kSize); | |
646 StoreMapNoWriteBarrier(info, | |
647 Heap::kPromiseResolveThenableJobInfoMapRootIndex); | |
648 StoreObjectFieldNoWriteBarrier( | |
649 info, PromiseResolveThenableJobInfo::kThenableOffset, thenable); | |
650 StoreObjectFieldNoWriteBarrier( | |
651 info, PromiseResolveThenableJobInfo::kThenOffset, then); | |
652 StoreObjectFieldNoWriteBarrier( | |
653 info, PromiseResolveThenableJobInfo::kResolveOffset, resolve); | |
654 StoreObjectFieldNoWriteBarrier( | |
655 info, PromiseResolveThenableJobInfo::kRejectOffset, reject); | |
656 StoreObjectFieldNoWriteBarrier( | |
657 info, PromiseResolveThenableJobInfo::kContextOffset, context); | |
658 return info; | |
659 } | |
660 | |
661 void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context, | |
662 Node* promise, | |
663 Node* result) { | |
664 Isolate* isolate = this->isolate(); | |
665 | |
666 Variable var_reason(this, MachineRepresentation::kTagged), | |
667 var_then(this, MachineRepresentation::kTagged); | |
668 | |
669 Label do_enqueue(this), fulfill(this), if_cycle(this, Label::kDeferred), | |
670 if_rejectpromise(this, Label::kDeferred), out(this); | |
671 | |
672 Label cycle_check(this); | |
673 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &cycle_check); | |
674 CallRuntime(Runtime::kPromiseHookResolve, context, promise); | |
675 Goto(&cycle_check); | |
676 | |
677 Bind(&cycle_check); | |
678 // 6. If SameValue(resolution, promise) is true, then | |
679 GotoIf(SameValue(promise, result), &if_cycle); | |
680 | |
681 // 7. If Type(resolution) is not Object, then | |
682 GotoIf(TaggedIsSmi(result), &fulfill); | |
683 GotoIfNot(IsJSReceiver(result), &fulfill); | |
684 | |
685 Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred); | |
686 Node* const native_context = LoadNativeContext(context); | |
687 Node* const promise_fun = | |
688 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
689 BranchIfFastPath(native_context, promise_fun, result, &if_nativepromise, | |
690 &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 Bind(&if_nativepromise); | |
696 { | |
697 Node* const thenable_status = | |
698 LoadObjectField(result, JSPromise::kStatusOffset); | |
699 Node* const thenable_value = | |
700 LoadObjectField(result, JSPromise::kResultOffset); | |
701 | |
702 Label if_isnotpending(this); | |
703 GotoIfNot(SmiEqual(SmiConstant(v8::Promise::kPending), 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 then = | |
710 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); | |
711 var_then.Bind(then); | |
712 Goto(&do_enqueue); | |
713 | |
714 Bind(&if_isnotpending); | |
715 { | |
716 Label if_fulfilled(this), if_rejected(this); | |
717 Branch(SmiEqual(SmiConstant(v8::Promise::kFulfilled), thenable_status), | |
718 &if_fulfilled, &if_rejected); | |
719 | |
720 Bind(&if_fulfilled); | |
721 { | |
722 PromiseFulfill(context, promise, thenable_value, | |
723 v8::Promise::kFulfilled); | |
724 PromiseSetHasHandler(promise); | |
725 Goto(&out); | |
726 } | |
727 | |
728 Bind(&if_rejected); | |
729 { | |
730 Label reject(this); | |
731 Node* const has_handler = PromiseHasHandler(result); | |
732 | |
733 // Promise has already been rejected, but had no handler. | |
734 // Revoke previously triggered reject event. | |
735 GotoIf(has_handler, &reject); | |
736 CallRuntime(Runtime::kPromiseRevokeReject, context, result); | |
737 Goto(&reject); | |
738 | |
739 Bind(&reject); | |
740 // Don't cause a debug event as this case is forwarding a rejection. | |
741 InternalPromiseReject(context, promise, thenable_value, false); | |
742 PromiseSetHasHandler(result); | |
743 Goto(&out); | |
744 } | |
745 } | |
746 } | |
747 | |
748 Bind(&if_notnativepromise); | |
749 { | |
750 // 8. Let then be Get(resolution, "then"). | |
751 Node* const then = | |
752 GetProperty(context, result, isolate->factory()->then_string()); | |
753 | |
754 // 9. If then is an abrupt completion, then | |
755 GotoIfException(then, &if_rejectpromise, &var_reason); | |
756 | |
757 // 11. If IsCallable(thenAction) is false, then | |
758 GotoIf(TaggedIsSmi(then), &fulfill); | |
759 Node* const then_map = LoadMap(then); | |
760 GotoIfNot(IsCallableMap(then_map), &fulfill); | |
761 var_then.Bind(then); | |
762 Goto(&do_enqueue); | |
763 } | |
764 | |
765 Bind(&do_enqueue); | |
766 { | |
767 // TODO(gsathya): Add fast path for native promises with unmodified | |
768 // PromiseThen (which don't need these resolving functions, but | |
769 // instead can just call resolve/reject directly). | |
770 Node* resolve = nullptr; | |
771 Node* reject = nullptr; | |
772 std::tie(resolve, reject) = CreatePromiseResolvingFunctions( | |
773 promise, FalseConstant(), native_context); | |
774 | |
775 Node* const info = AllocatePromiseResolveThenableJobInfo( | |
776 result, var_then.value(), resolve, reject, context); | |
777 | |
778 Label enqueue(this); | |
779 GotoIfNot(IsDebugActive(), &enqueue); | |
780 | |
781 GotoIf(TaggedIsSmi(result), &enqueue); | |
782 GotoIfNot(HasInstanceType(result, JS_PROMISE_TYPE), &enqueue); | |
783 | |
784 // Mark the dependency of the new promise on the resolution | |
785 Node* const key = | |
786 HeapConstant(isolate->factory()->promise_handled_by_symbol()); | |
787 CallRuntime(Runtime::kSetProperty, context, result, key, promise, | |
788 SmiConstant(STRICT)); | |
789 Goto(&enqueue); | |
790 | |
791 // 12. Perform EnqueueJob("PromiseJobs", | |
792 // PromiseResolveThenableJob, « promise, resolution, thenAction»). | |
793 Bind(&enqueue); | |
794 // TODO(gsathya): Move this to TF | |
795 CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, info); | |
796 Goto(&out); | |
797 } | |
798 | |
799 // 7.b Return FulfillPromise(promise, resolution). | |
800 Bind(&fulfill); | |
801 { | |
802 PromiseFulfill(context, promise, result, v8::Promise::kFulfilled); | |
803 Goto(&out); | |
804 } | |
805 | |
806 Bind(&if_cycle); | |
807 { | |
808 // 6.a Let selfResolutionError be a newly created TypeError object. | |
809 Node* const message_id = SmiConstant(MessageTemplate::kPromiseCyclic); | |
810 Node* const error = | |
811 CallRuntime(Runtime::kNewTypeError, context, message_id, result); | |
812 var_reason.Bind(error); | |
813 | |
814 // 6.b Return RejectPromise(promise, selfResolutionError). | |
815 Goto(&if_rejectpromise); | |
816 } | |
817 | |
818 // 9.a Return RejectPromise(promise, then.[[Value]]). | |
819 Bind(&if_rejectpromise); | |
820 { | |
821 // Don't cause a debug event as this case is forwarding a rejection. | |
822 InternalPromiseReject(context, promise, var_reason.value(), false); | |
823 Goto(&out); | |
824 } | |
825 | |
826 Bind(&out); | |
827 } | |
828 | |
829 void PromiseBuiltinsAssembler::PromiseFulfill( | |
830 Node* context, Node* promise, Node* result, | |
831 v8::Promise::PromiseState status) { | |
832 Label do_promisereset(this), debug_async_event_enqueue_recurring(this); | |
833 | |
834 Node* const status_smi = SmiConstant(static_cast<int>(status)); | |
835 Node* const deferred_promise = | |
836 LoadObjectField(promise, JSPromise::kDeferredPromiseOffset); | |
837 | |
838 GotoIf(IsUndefined(deferred_promise), &debug_async_event_enqueue_recurring); | |
839 | |
840 Node* const tasks = | |
841 status == v8::Promise::kFulfilled | |
842 ? LoadObjectField(promise, JSPromise::kFulfillReactionsOffset) | |
843 : LoadObjectField(promise, JSPromise::kRejectReactionsOffset); | |
844 | |
845 Node* const deferred_on_resolve = | |
846 LoadObjectField(promise, JSPromise::kDeferredOnResolveOffset); | |
847 Node* const deferred_on_reject = | |
848 LoadObjectField(promise, JSPromise::kDeferredOnRejectOffset); | |
849 | |
850 Node* const info = AllocatePromiseReactionJobInfo( | |
851 result, tasks, deferred_promise, deferred_on_resolve, deferred_on_reject, | |
852 context); | |
853 | |
854 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); | |
855 Goto(&debug_async_event_enqueue_recurring); | |
856 | |
857 Bind(&debug_async_event_enqueue_recurring); | |
858 { | |
859 GotoIfNot(IsDebugActive(), &do_promisereset); | |
860 CallRuntime(Runtime::kDebugAsyncEventEnqueueRecurring, context, promise, | |
861 status_smi); | |
862 Goto(&do_promisereset); | |
863 } | |
864 | |
865 Bind(&do_promisereset); | |
866 { | |
867 StoreObjectField(promise, JSPromise::kStatusOffset, status_smi); | |
868 StoreObjectField(promise, JSPromise::kResultOffset, result); | |
869 StoreObjectFieldRoot(promise, JSPromise::kDeferredPromiseOffset, | |
870 Heap::kUndefinedValueRootIndex); | |
871 StoreObjectFieldRoot(promise, JSPromise::kDeferredOnResolveOffset, | |
872 Heap::kUndefinedValueRootIndex); | |
873 StoreObjectFieldRoot(promise, JSPromise::kDeferredOnRejectOffset, | |
874 Heap::kUndefinedValueRootIndex); | |
875 StoreObjectFieldRoot(promise, JSPromise::kFulfillReactionsOffset, | |
876 Heap::kUndefinedValueRootIndex); | |
877 StoreObjectFieldRoot(promise, JSPromise::kRejectReactionsOffset, | |
878 Heap::kUndefinedValueRootIndex); | |
879 } | |
880 } | |
881 | |
882 void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed( | |
883 Node* context, Node* native_context, Node* promise_constructor, | |
884 Node* executor, Label* if_noaccess) { | |
885 Variable var_executor(this, MachineRepresentation::kTagged); | |
886 var_executor.Bind(executor); | |
887 Label has_access(this), call_runtime(this, Label::kDeferred); | |
888 | |
889 // If executor is a bound function, load the bound function until we've | |
890 // reached an actual function. | |
891 Label found_function(this), loop_over_bound_function(this, &var_executor); | |
892 Goto(&loop_over_bound_function); | |
893 Bind(&loop_over_bound_function); | |
894 { | |
895 Node* executor_type = LoadInstanceType(var_executor.value()); | |
896 GotoIf(InstanceTypeEqual(executor_type, JS_FUNCTION_TYPE), &found_function); | |
897 GotoIfNot(InstanceTypeEqual(executor_type, JS_BOUND_FUNCTION_TYPE), | |
898 &call_runtime); | |
899 var_executor.Bind(LoadObjectField( | |
900 var_executor.value(), JSBoundFunction::kBoundTargetFunctionOffset)); | |
901 Goto(&loop_over_bound_function); | |
902 } | |
903 | |
904 // Load the context from the function and compare it to the Promise | |
905 // constructor's context. If they match, everything is fine, otherwise, bail | |
906 // out to the runtime. | |
907 Bind(&found_function); | |
908 { | |
909 Node* function_context = | |
910 LoadObjectField(var_executor.value(), JSFunction::kContextOffset); | |
911 Node* native_function_context = LoadNativeContext(function_context); | |
912 Branch(WordEqual(native_context, native_function_context), &has_access, | |
913 &call_runtime); | |
914 } | |
915 | |
916 Bind(&call_runtime); | |
917 { | |
918 Branch(WordEqual(CallRuntime(Runtime::kAllowDynamicFunction, context, | |
919 promise_constructor), | |
920 BooleanConstant(true)), | |
921 &has_access, if_noaccess); | |
922 } | |
923 | |
924 Bind(&has_access); | |
925 } | |
926 | |
927 void PromiseBuiltinsAssembler::InternalPromiseReject(Node* context, | |
928 Node* promise, Node* value, | |
929 Node* debug_event) { | |
930 Label out(this); | |
931 GotoIfNot(IsDebugActive(), &out); | |
932 GotoIfNot(WordEqual(TrueConstant(), debug_event), &out); | |
933 CallRuntime(Runtime::kDebugPromiseReject, context, promise, value); | |
934 Goto(&out); | |
935 | |
936 Bind(&out); | |
937 InternalPromiseReject(context, promise, value, false); | |
938 } | |
939 | |
940 // This duplicates a lot of logic from PromiseRejectEvent in | |
941 // runtime-promise.cc | |
942 void PromiseBuiltinsAssembler::InternalPromiseReject(Node* context, | |
943 Node* promise, Node* value, | |
944 bool debug_event) { | |
945 Label fulfill(this), report_unhandledpromise(this), run_promise_hook(this); | |
946 | |
947 if (debug_event) { | |
948 GotoIfNot(IsDebugActive(), &run_promise_hook); | |
949 CallRuntime(Runtime::kDebugPromiseReject, context, promise, value); | |
950 Goto(&run_promise_hook); | |
951 } else { | |
952 Goto(&run_promise_hook); | |
953 } | |
954 | |
955 Bind(&run_promise_hook); | |
956 { | |
957 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &report_unhandledpromise); | |
958 CallRuntime(Runtime::kPromiseHookResolve, context, promise); | |
959 Goto(&report_unhandledpromise); | |
960 } | |
961 | |
962 Bind(&report_unhandledpromise); | |
963 { | |
964 GotoIf(PromiseHasHandler(promise), &fulfill); | |
965 CallRuntime(Runtime::kReportPromiseReject, context, promise, value); | |
966 Goto(&fulfill); | |
967 } | |
968 | |
969 Bind(&fulfill); | |
970 PromiseFulfill(context, promise, value, v8::Promise::kRejected); | |
971 } | |
972 | |
973 // ES#sec-promise-reject-functions | |
974 // Promise Reject Functions | |
975 TF_BUILTIN(PromiseRejectClosure, PromiseBuiltinsAssembler) { | |
976 Node* const value = Parameter(1); | |
977 Node* const context = Parameter(4); | |
978 | |
979 Label out(this); | |
980 | |
981 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. | |
982 int has_already_visited_slot = kAlreadyVisitedSlot; | |
983 | |
984 Node* const has_already_visited = | |
985 LoadContextElement(context, has_already_visited_slot); | |
986 | |
987 // 4. If alreadyResolved.[[Value]] is true, return undefined. | |
988 GotoIf(SmiEqual(has_already_visited, SmiConstant(1)), &out); | |
989 | |
990 // 5.Set alreadyResolved.[[Value]] to true. | |
991 StoreContextElementNoWriteBarrier(context, has_already_visited_slot, | |
992 SmiConstant(1)); | |
993 | |
994 // 2. Let promise be F.[[Promise]]. | |
995 Node* const promise = | |
996 LoadContextElement(context, IntPtrConstant(kPromiseSlot)); | |
997 Node* const debug_event = | |
998 LoadContextElement(context, IntPtrConstant(kDebugEventSlot)); | |
999 | |
1000 InternalPromiseReject(context, promise, value, debug_event); | |
1001 Return(UndefinedConstant()); | |
1002 | |
1003 Bind(&out); | |
1004 Return(UndefinedConstant()); | |
1005 } | |
1006 | |
1007 TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { | |
1008 Node* const executor = Parameter(1); | |
1009 Node* const new_target = Parameter(2); | |
1010 Node* const context = Parameter(4); | |
1011 Isolate* isolate = this->isolate(); | |
1012 | |
1013 Label if_targetisundefined(this, Label::kDeferred); | |
1014 | |
1015 GotoIf(IsUndefined(new_target), &if_targetisundefined); | |
1016 | |
1017 Label if_notcallable(this, Label::kDeferred); | |
1018 | |
1019 GotoIf(TaggedIsSmi(executor), &if_notcallable); | |
1020 | |
1021 Node* const executor_map = LoadMap(executor); | |
1022 GotoIfNot(IsCallableMap(executor_map), &if_notcallable); | |
1023 | |
1024 Node* const native_context = LoadNativeContext(context); | |
1025 Node* const promise_fun = | |
1026 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
1027 Node* const is_debug_active = IsDebugActive(); | |
1028 Label if_targetisnotmodified(this), | |
1029 if_targetismodified(this, Label::kDeferred), run_executor(this), | |
1030 debug_push(this), if_noaccess(this, Label::kDeferred); | |
1031 | |
1032 BranchIfAccessCheckFailed(context, native_context, promise_fun, executor, | |
1033 &if_noaccess); | |
1034 | |
1035 Branch(WordEqual(promise_fun, new_target), &if_targetisnotmodified, | |
1036 &if_targetismodified); | |
1037 | |
1038 Variable var_result(this, MachineRepresentation::kTagged), | |
1039 var_reject_call(this, MachineRepresentation::kTagged), | |
1040 var_reason(this, MachineRepresentation::kTagged); | |
1041 | |
1042 Bind(&if_targetisnotmodified); | |
1043 { | |
1044 Node* const instance = AllocateAndInitJSPromise(context); | |
1045 var_result.Bind(instance); | |
1046 Goto(&debug_push); | |
1047 } | |
1048 | |
1049 Bind(&if_targetismodified); | |
1050 { | |
1051 ConstructorBuiltinsAssembler constructor_assembler(this->state()); | |
1052 Node* const instance = constructor_assembler.EmitFastNewObject( | |
1053 context, promise_fun, new_target); | |
1054 PromiseInit(instance); | |
1055 var_result.Bind(instance); | |
1056 | |
1057 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &debug_push); | |
1058 CallRuntime(Runtime::kPromiseHookInit, context, instance, | |
1059 UndefinedConstant()); | |
1060 Goto(&debug_push); | |
1061 } | |
1062 | |
1063 Bind(&debug_push); | |
1064 { | |
1065 GotoIfNot(is_debug_active, &run_executor); | |
1066 CallRuntime(Runtime::kDebugPushPromise, context, var_result.value()); | |
1067 Goto(&run_executor); | |
1068 } | |
1069 | |
1070 Bind(&run_executor); | |
1071 { | |
1072 Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred); | |
1073 | |
1074 Node *resolve, *reject; | |
1075 std::tie(resolve, reject) = CreatePromiseResolvingFunctions( | |
1076 var_result.value(), TrueConstant(), native_context); | |
1077 Callable call_callable = CodeFactory::Call(isolate); | |
1078 | |
1079 Node* const maybe_exception = CallJS(call_callable, context, executor, | |
1080 UndefinedConstant(), resolve, reject); | |
1081 | |
1082 GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); | |
1083 Branch(is_debug_active, &debug_pop, &out); | |
1084 | |
1085 Bind(&if_rejectpromise); | |
1086 { | |
1087 Callable call_callable = CodeFactory::Call(isolate); | |
1088 CallJS(call_callable, context, reject, UndefinedConstant(), | |
1089 var_reason.value()); | |
1090 Branch(is_debug_active, &debug_pop, &out); | |
1091 } | |
1092 | |
1093 Bind(&debug_pop); | |
1094 { | |
1095 CallRuntime(Runtime::kDebugPopPromise, context); | |
1096 Goto(&out); | |
1097 } | |
1098 Bind(&out); | |
1099 Return(var_result.value()); | |
1100 } | |
1101 | |
1102 // 1. If NewTarget is undefined, throw a TypeError exception. | |
1103 Bind(&if_targetisundefined); | |
1104 { | |
1105 Node* const message_id = SmiConstant(MessageTemplate::kNotAPromise); | |
1106 CallRuntime(Runtime::kThrowTypeError, context, message_id, new_target); | |
1107 Unreachable(); | |
1108 } | |
1109 | |
1110 // 2. If IsCallable(executor) is false, throw a TypeError exception. | |
1111 Bind(&if_notcallable); | |
1112 { | |
1113 Node* const message_id = | |
1114 SmiConstant(MessageTemplate::kResolverNotAFunction); | |
1115 CallRuntime(Runtime::kThrowTypeError, context, message_id, executor); | |
1116 Unreachable(); | |
1117 } | |
1118 | |
1119 // Silently fail if the stack looks fishy. | |
1120 Bind(&if_noaccess); | |
1121 { | |
1122 Node* const counter_id = | |
1123 SmiConstant(v8::Isolate::kPromiseConstructorReturnedUndefined); | |
1124 CallRuntime(Runtime::kIncrementUseCounter, context, counter_id); | |
1125 Return(UndefinedConstant()); | |
1126 } | |
1127 } | |
1128 | |
1129 TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) { | |
1130 Node* const parent = Parameter(1); | |
1131 Node* const context = Parameter(4); | |
1132 Return(AllocateAndInitJSPromise(context, parent)); | |
1133 } | |
1134 | |
1135 TF_BUILTIN(IsPromise, PromiseBuiltinsAssembler) { | |
1136 Node* const maybe_promise = Parameter(1); | |
1137 Label if_notpromise(this, Label::kDeferred); | |
1138 | |
1139 GotoIf(TaggedIsSmi(maybe_promise), &if_notpromise); | |
1140 | |
1141 Node* const result = | |
1142 SelectBooleanConstant(HasInstanceType(maybe_promise, JS_PROMISE_TYPE)); | |
1143 Return(result); | |
1144 | |
1145 Bind(&if_notpromise); | |
1146 Return(FalseConstant()); | |
1147 } | |
1148 | |
1149 // ES#sec-promise.prototype.then | |
1150 // Promise.prototype.catch ( onFulfilled, onRejected ) | |
1151 TF_BUILTIN(PromiseThen, PromiseBuiltinsAssembler) { | |
1152 // 1. Let promise be the this value. | |
1153 Node* const promise = Parameter(0); | |
1154 Node* const on_resolve = Parameter(1); | |
1155 Node* const on_reject = Parameter(2); | |
1156 Node* const context = Parameter(5); | |
1157 | |
1158 Node* const result = | |
1159 InternalPromiseThen(context, promise, on_resolve, on_reject); | |
1160 Return(result); | |
1161 } | |
1162 | |
1163 // ES#sec-promise-resolve-functions | |
1164 // Promise Resolve Functions | |
1165 TF_BUILTIN(PromiseResolveClosure, PromiseBuiltinsAssembler) { | |
1166 Node* const value = Parameter(1); | |
1167 Node* const context = Parameter(4); | |
1168 | |
1169 Label out(this); | |
1170 | |
1171 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. | |
1172 int has_already_visited_slot = kAlreadyVisitedSlot; | |
1173 | |
1174 Node* const has_already_visited = | |
1175 LoadContextElement(context, has_already_visited_slot); | |
1176 | |
1177 // 4. If alreadyResolved.[[Value]] is true, return undefined. | |
1178 GotoIf(SmiEqual(has_already_visited, SmiConstant(1)), &out); | |
1179 | |
1180 // 5.Set alreadyResolved.[[Value]] to true. | |
1181 StoreContextElementNoWriteBarrier(context, has_already_visited_slot, | |
1182 SmiConstant(1)); | |
1183 | |
1184 // 2. Let promise be F.[[Promise]]. | |
1185 Node* const promise = | |
1186 LoadContextElement(context, IntPtrConstant(kPromiseSlot)); | |
1187 | |
1188 InternalResolvePromise(context, promise, value); | |
1189 Return(UndefinedConstant()); | |
1190 | |
1191 Bind(&out); | |
1192 Return(UndefinedConstant()); | |
1193 } | |
1194 | |
1195 TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) { | |
1196 Node* const promise = Parameter(1); | |
1197 Node* const result = Parameter(2); | |
1198 Node* const context = Parameter(5); | |
1199 | |
1200 InternalResolvePromise(context, promise, result); | |
1201 Return(UndefinedConstant()); | |
1202 } | |
1203 | |
1204 TF_BUILTIN(PromiseHandleReject, PromiseBuiltinsAssembler) { | |
1205 typedef PromiseHandleRejectDescriptor Descriptor; | |
1206 | |
1207 Node* const promise = Parameter(Descriptor::kPromise); | |
1208 Node* const on_reject = Parameter(Descriptor::kOnReject); | |
1209 Node* const exception = Parameter(Descriptor::kException); | |
1210 Node* const context = Parameter(Descriptor::kContext); | |
1211 | |
1212 Callable call_callable = CodeFactory::Call(isolate()); | |
1213 Variable var_unused(this, MachineRepresentation::kTagged); | |
1214 | |
1215 Label if_internalhandler(this), if_customhandler(this, Label::kDeferred); | |
1216 Branch(IsUndefined(on_reject), &if_internalhandler, &if_customhandler); | |
1217 | |
1218 Bind(&if_internalhandler); | |
1219 { | |
1220 InternalPromiseReject(context, promise, exception, false); | |
1221 Return(UndefinedConstant()); | |
1222 } | |
1223 | |
1224 Bind(&if_customhandler); | |
1225 { | |
1226 CallJS(call_callable, context, on_reject, UndefinedConstant(), exception); | |
1227 Return(UndefinedConstant()); | |
1228 } | |
1229 } | |
1230 | |
1231 TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { | |
1232 Node* const value = Parameter(1); | |
1233 Node* const handler = Parameter(2); | |
1234 Node* const deferred_promise = Parameter(3); | |
1235 Node* const deferred_on_resolve = Parameter(4); | |
1236 Node* const deferred_on_reject = Parameter(5); | |
1237 Node* const context = Parameter(8); | |
1238 Isolate* isolate = this->isolate(); | |
1239 | |
1240 Variable var_reason(this, MachineRepresentation::kTagged); | |
1241 | |
1242 Node* const is_debug_active = IsDebugActive(); | |
1243 Label run_handler(this), if_rejectpromise(this), promisehook_before(this), | |
1244 promisehook_after(this), debug_pop(this); | |
1245 | |
1246 GotoIfNot(is_debug_active, &promisehook_before); | |
1247 CallRuntime(Runtime::kDebugPushPromise, context, deferred_promise); | |
1248 Goto(&promisehook_before); | |
1249 | |
1250 Bind(&promisehook_before); | |
1251 { | |
1252 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &run_handler); | |
1253 CallRuntime(Runtime::kPromiseHookBefore, context, deferred_promise); | |
1254 Goto(&run_handler); | |
1255 } | |
1256 | |
1257 Bind(&run_handler); | |
1258 { | |
1259 Label if_defaulthandler(this), if_callablehandler(this), | |
1260 if_internalhandler(this), if_customhandler(this, Label::kDeferred); | |
1261 Variable var_result(this, MachineRepresentation::kTagged); | |
1262 | |
1263 Branch(IsSymbol(handler), &if_defaulthandler, &if_callablehandler); | |
1264 | |
1265 Bind(&if_defaulthandler); | |
1266 { | |
1267 Label if_resolve(this), if_reject(this); | |
1268 Node* const default_resolve_handler_symbol = HeapConstant( | |
1269 isolate->factory()->promise_default_resolve_handler_symbol()); | |
1270 Branch(WordEqual(default_resolve_handler_symbol, handler), &if_resolve, | |
1271 &if_reject); | |
1272 | |
1273 Bind(&if_resolve); | |
1274 { | |
1275 var_result.Bind(value); | |
1276 Branch(IsUndefined(deferred_on_resolve), &if_internalhandler, | |
1277 &if_customhandler); | |
1278 } | |
1279 | |
1280 Bind(&if_reject); | |
1281 { | |
1282 var_reason.Bind(value); | |
1283 Goto(&if_rejectpromise); | |
1284 } | |
1285 } | |
1286 | |
1287 Bind(&if_callablehandler); | |
1288 { | |
1289 Callable call_callable = CodeFactory::Call(isolate); | |
1290 Node* const result = | |
1291 CallJS(call_callable, context, handler, UndefinedConstant(), value); | |
1292 var_result.Bind(result); | |
1293 GotoIfException(result, &if_rejectpromise, &var_reason); | |
1294 Branch(IsUndefined(deferred_on_resolve), &if_internalhandler, | |
1295 &if_customhandler); | |
1296 } | |
1297 | |
1298 Bind(&if_internalhandler); | |
1299 InternalResolvePromise(context, deferred_promise, var_result.value()); | |
1300 Goto(&promisehook_after); | |
1301 | |
1302 Bind(&if_customhandler); | |
1303 { | |
1304 Callable call_callable = CodeFactory::Call(isolate); | |
1305 Node* const maybe_exception = | |
1306 CallJS(call_callable, context, deferred_on_resolve, | |
1307 UndefinedConstant(), var_result.value()); | |
1308 GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); | |
1309 Goto(&promisehook_after); | |
1310 } | |
1311 } | |
1312 | |
1313 Bind(&if_rejectpromise); | |
1314 { | |
1315 Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate); | |
1316 CallStub(promise_handle_reject, context, deferred_promise, | |
1317 deferred_on_reject, var_reason.value()); | |
1318 Goto(&promisehook_after); | |
1319 } | |
1320 | |
1321 Bind(&promisehook_after); | |
1322 { | |
1323 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &debug_pop); | |
1324 CallRuntime(Runtime::kPromiseHookAfter, context, deferred_promise); | |
1325 Goto(&debug_pop); | |
1326 } | |
1327 | |
1328 Bind(&debug_pop); | |
1329 { | |
1330 Label out(this); | |
1331 | |
1332 GotoIfNot(is_debug_active, &out); | |
1333 CallRuntime(Runtime::kDebugPopPromise, context); | |
1334 Goto(&out); | |
1335 | |
1336 Bind(&out); | |
1337 Return(UndefinedConstant()); | |
1338 } | |
1339 } | |
1340 | |
1341 // ES#sec-promise.prototype.catch | |
1342 // Promise.prototype.catch ( onRejected ) | |
1343 TF_BUILTIN(PromiseCatch, PromiseBuiltinsAssembler) { | |
1344 // 1. Let promise be the this value. | |
1345 Node* const promise = Parameter(0); | |
1346 Node* const on_resolve = UndefinedConstant(); | |
1347 Node* const on_reject = Parameter(1); | |
1348 Node* const context = Parameter(4); | |
1349 | |
1350 Label if_internalthen(this), if_customthen(this, Label::kDeferred); | |
1351 GotoIf(TaggedIsSmi(promise), &if_customthen); | |
1352 BranchIfFastPath(context, promise, &if_internalthen, &if_customthen); | |
1353 | |
1354 Bind(&if_internalthen); | |
1355 { | |
1356 Node* const result = | |
1357 InternalPromiseThen(context, promise, on_resolve, on_reject); | |
1358 Return(result); | |
1359 } | |
1360 | |
1361 Bind(&if_customthen); | |
1362 { | |
1363 Node* const then = | |
1364 GetProperty(context, promise, isolate()->factory()->then_string()); | |
1365 Callable call_callable = CodeFactory::Call(isolate()); | |
1366 Node* const result = | |
1367 CallJS(call_callable, context, then, promise, on_resolve, on_reject); | |
1368 Return(result); | |
1369 } | |
1370 } | |
1371 | |
1372 TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) { | |
1373 // 1. Let C be the this value. | |
1374 Node* receiver = Parameter(0); | |
1375 Node* value = Parameter(1); | |
1376 Node* context = Parameter(4); | |
1377 Isolate* isolate = this->isolate(); | |
1378 | |
1379 // 2. If Type(C) is not Object, throw a TypeError exception. | |
1380 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, | |
1381 "PromiseResolve"); | |
1382 | |
1383 Node* const native_context = LoadNativeContext(context); | |
1384 Node* const promise_fun = | |
1385 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
1386 | |
1387 Label if_valueisnativepromise(this), if_valueisnotnativepromise(this), | |
1388 if_valueisnotpromise(this); | |
1389 | |
1390 // 3.If IsPromise(x) is true, then | |
1391 GotoIf(TaggedIsSmi(value), &if_valueisnotpromise); | |
1392 | |
1393 // This shortcircuits the constructor lookups. | |
1394 GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &if_valueisnotpromise); | |
1395 | |
1396 // This adds a fast path as non-subclassed native promises don't have | |
1397 // an observable constructor lookup. | |
1398 BranchIfFastPath(native_context, promise_fun, value, &if_valueisnativepromise, | |
1399 &if_valueisnotnativepromise); | |
1400 | |
1401 Bind(&if_valueisnativepromise); | |
1402 { | |
1403 GotoIfNot(WordEqual(promise_fun, receiver), &if_valueisnotnativepromise); | |
1404 Return(value); | |
1405 } | |
1406 | |
1407 // At this point, value or/and receiver are not native promises, but | |
1408 // they could be of the same subclass. | |
1409 Bind(&if_valueisnotnativepromise); | |
1410 { | |
1411 // 3.a Let xConstructor be ? Get(x, "constructor"). | |
1412 // The constructor lookup is observable. | |
1413 Node* const constructor = | |
1414 GetProperty(context, value, isolate->factory()->constructor_string()); | |
1415 | |
1416 // 3.b If SameValue(xConstructor, C) is true, return x. | |
1417 GotoIfNot(SameValue(constructor, receiver), &if_valueisnotpromise); | |
1418 | |
1419 Return(value); | |
1420 } | |
1421 | |
1422 Bind(&if_valueisnotpromise); | |
1423 { | |
1424 Label if_nativepromise(this), if_notnativepromise(this); | |
1425 Branch(WordEqual(promise_fun, receiver), &if_nativepromise, | |
1426 &if_notnativepromise); | |
1427 | |
1428 // This adds a fast path for native promises that don't need to | |
1429 // create NewPromiseCapability. | |
1430 Bind(&if_nativepromise); | |
1431 { | |
1432 Node* const result = AllocateAndInitJSPromise(context); | |
1433 InternalResolvePromise(context, result, value); | |
1434 Return(result); | |
1435 } | |
1436 | |
1437 Bind(&if_notnativepromise); | |
1438 { | |
1439 // 4. Let promiseCapability be ? NewPromiseCapability(C). | |
1440 Node* const capability = NewPromiseCapability(context, receiver); | |
1441 | |
1442 // 5. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). | |
1443 Callable call_callable = CodeFactory::Call(isolate); | |
1444 Node* const resolve = | |
1445 LoadObjectField(capability, JSPromiseCapability::kResolveOffset); | |
1446 CallJS(call_callable, context, resolve, UndefinedConstant(), value); | |
1447 | |
1448 // 6. Return promiseCapability.[[Promise]]. | |
1449 Node* const result = | |
1450 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset); | |
1451 Return(result); | |
1452 } | |
1453 } | |
1454 } | |
1455 | |
1456 TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { | |
1457 Node* const resolve = Parameter(1); | |
1458 Node* const reject = Parameter(2); | |
1459 Node* const context = Parameter(5); | |
1460 | |
1461 Node* const capability = LoadContextElement(context, kCapabilitySlot); | |
1462 | |
1463 Label if_alreadyinvoked(this, Label::kDeferred); | |
1464 GotoIf(WordNotEqual( | |
1465 LoadObjectField(capability, JSPromiseCapability::kResolveOffset), | |
1466 UndefinedConstant()), | |
1467 &if_alreadyinvoked); | |
1468 GotoIf(WordNotEqual( | |
1469 LoadObjectField(capability, JSPromiseCapability::kRejectOffset), | |
1470 UndefinedConstant()), | |
1471 &if_alreadyinvoked); | |
1472 | |
1473 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, resolve); | |
1474 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, reject); | |
1475 | |
1476 Return(UndefinedConstant()); | |
1477 | |
1478 Bind(&if_alreadyinvoked); | |
1479 Node* message = SmiConstant(MessageTemplate::kPromiseExecutorAlreadyInvoked); | |
1480 CallRuntime(Runtime::kThrowTypeError, context, message); | |
1481 Unreachable(); | |
1482 } | |
1483 | |
1484 TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) { | |
1485 Node* constructor = Parameter(1); | |
1486 Node* debug_event = Parameter(2); | |
1487 Node* context = Parameter(5); | |
1488 | |
1489 CSA_ASSERT_JS_ARGC_EQ(this, 2); | |
1490 | |
1491 Return(NewPromiseCapability(context, constructor, debug_event)); | |
1492 } | |
1493 | |
1494 TF_BUILTIN(PromiseReject, PromiseBuiltinsAssembler) { | |
1495 // 1. Let C be the this value. | |
1496 Node* const receiver = Parameter(0); | |
1497 Node* const reason = Parameter(1); | |
1498 Node* const context = Parameter(4); | |
1499 | |
1500 // 2. If Type(C) is not Object, throw a TypeError exception. | |
1501 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, | |
1502 "PromiseReject"); | |
1503 | |
1504 Label if_nativepromise(this), if_custompromise(this, Label::kDeferred); | |
1505 Node* const native_context = LoadNativeContext(context); | |
1506 Node* const promise_fun = | |
1507 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
1508 Branch(WordEqual(promise_fun, receiver), &if_nativepromise, | |
1509 &if_custompromise); | |
1510 | |
1511 Bind(&if_nativepromise); | |
1512 { | |
1513 Node* const promise = AllocateAndSetJSPromise( | |
1514 context, SmiConstant(v8::Promise::kRejected), reason); | |
1515 CallRuntime(Runtime::kPromiseRejectEventFromStack, context, promise, | |
1516 reason); | |
1517 Return(promise); | |
1518 } | |
1519 | |
1520 Bind(&if_custompromise); | |
1521 { | |
1522 // 3. Let promiseCapability be ? NewPromiseCapability(C). | |
1523 Node* const capability = NewPromiseCapability(context, receiver); | |
1524 | |
1525 // 4. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »). | |
1526 Node* const reject = | |
1527 LoadObjectField(capability, JSPromiseCapability::kRejectOffset); | |
1528 Callable call_callable = CodeFactory::Call(isolate()); | |
1529 CallJS(call_callable, context, reject, UndefinedConstant(), reason); | |
1530 | |
1531 // 5. Return promiseCapability.[[Promise]]. | |
1532 Node* const promise = | |
1533 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset); | |
1534 Return(promise); | |
1535 } | |
1536 } | |
1537 | |
1538 TF_BUILTIN(InternalPromiseReject, PromiseBuiltinsAssembler) { | |
1539 Node* const promise = Parameter(1); | |
1540 Node* const reason = Parameter(2); | |
1541 Node* const debug_event = Parameter(3); | |
1542 Node* const context = Parameter(6); | |
1543 | |
1544 InternalPromiseReject(context, promise, reason, debug_event); | |
1545 Return(UndefinedConstant()); | |
1546 } | |
1547 | |
1548 Node* PromiseBuiltinsAssembler::CreatePromiseFinallyContext( | |
1549 Node* on_finally, Node* native_context) { | |
1550 Node* const context = | |
1551 CreatePromiseContext(native_context, kOnFinallyContextLength); | |
1552 StoreContextElementNoWriteBarrier(context, kOnFinallySlot, on_finally); | |
1553 return context; | |
1554 } | |
1555 | |
1556 std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions( | |
1557 Node* on_finally, Node* native_context) { | |
1558 Node* const promise_context = | |
1559 CreatePromiseFinallyContext(on_finally, native_context); | |
1560 Node* const map = LoadContextElement( | |
1561 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
1562 Node* const then_finally_info = LoadContextElement( | |
1563 native_context, Context::PROMISE_THEN_FINALLY_SHARED_FUN); | |
1564 Node* const then_finally = AllocateFunctionWithMapAndContext( | |
1565 map, then_finally_info, promise_context); | |
1566 Node* const catch_finally_info = LoadContextElement( | |
1567 native_context, Context::PROMISE_CATCH_FINALLY_SHARED_FUN); | |
1568 Node* const catch_finally = AllocateFunctionWithMapAndContext( | |
1569 map, catch_finally_info, promise_context); | |
1570 return std::make_pair(then_finally, catch_finally); | |
1571 } | |
1572 | |
1573 TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) { | |
1574 Node* const context = Parameter(3); | |
1575 | |
1576 Node* const value = LoadContextElement(context, kOnFinallySlot); | |
1577 Return(value); | |
1578 } | |
1579 | |
1580 Node* PromiseBuiltinsAssembler::CreateValueThunkFunctionContext( | |
1581 Node* value, Node* native_context) { | |
1582 Node* const context = | |
1583 CreatePromiseContext(native_context, kOnFinallyContextLength); | |
1584 StoreContextElementNoWriteBarrier(context, kOnFinallySlot, value); | |
1585 return context; | |
1586 } | |
1587 | |
1588 Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value, | |
1589 Node* native_context) { | |
1590 Node* const value_thunk_context = | |
1591 CreateValueThunkFunctionContext(value, native_context); | |
1592 Node* const map = LoadContextElement( | |
1593 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
1594 Node* const value_thunk_info = LoadContextElement( | |
1595 native_context, Context::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN); | |
1596 Node* const value_thunk = AllocateFunctionWithMapAndContext( | |
1597 map, value_thunk_info, value_thunk_context); | |
1598 return value_thunk; | |
1599 } | |
1600 | |
1601 TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) { | |
1602 CSA_ASSERT_JS_ARGC_EQ(this, 1); | |
1603 | |
1604 Node* const value = Parameter(1); | |
1605 Node* const context = Parameter(4); | |
1606 | |
1607 Node* const on_finally = LoadContextElement(context, kOnFinallySlot); | |
1608 | |
1609 // 2.a Let result be ? Call(onFinally, undefined). | |
1610 Callable call_callable = CodeFactory::Call(isolate()); | |
1611 Node* result = | |
1612 CallJS(call_callable, context, on_finally, UndefinedConstant()); | |
1613 | |
1614 // 2.b Let promise be ! PromiseResolve( %Promise%, result). | |
1615 Node* const promise = AllocateAndInitJSPromise(context); | |
1616 InternalResolvePromise(context, promise, result); | |
1617 | |
1618 // 2.c Let valueThunk be equivalent to a function that returns value. | |
1619 Node* native_context = LoadNativeContext(context); | |
1620 Node* const value_thunk = CreateValueThunkFunction(value, native_context); | |
1621 | |
1622 // 2.d Let promiseCapability be ! NewPromiseCapability( %Promise%). | |
1623 Node* const promise_capability = AllocateAndInitJSPromise(context, promise); | |
1624 | |
1625 // 2.e Return PerformPromiseThen(promise, valueThunk, undefined, | |
1626 // promiseCapability). | |
1627 InternalPerformPromiseThen(context, promise, value_thunk, UndefinedConstant(), | |
1628 promise_capability, UndefinedConstant(), | |
1629 UndefinedConstant()); | |
1630 Return(promise_capability); | |
1631 } | |
1632 | |
1633 TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) { | |
1634 Node* const context = Parameter(3); | |
1635 | |
1636 Node* const reason = LoadContextElement(context, kOnFinallySlot); | |
1637 CallRuntime(Runtime::kThrow, context, reason); | |
1638 Unreachable(); | |
1639 } | |
1640 | |
1641 Node* PromiseBuiltinsAssembler::CreateThrowerFunctionContext( | |
1642 Node* reason, Node* native_context) { | |
1643 Node* const context = | |
1644 CreatePromiseContext(native_context, kOnFinallyContextLength); | |
1645 StoreContextElementNoWriteBarrier(context, kOnFinallySlot, reason); | |
1646 return context; | |
1647 } | |
1648 | |
1649 Node* PromiseBuiltinsAssembler::CreateThrowerFunction(Node* reason, | |
1650 Node* native_context) { | |
1651 Node* const thrower_context = | |
1652 CreateThrowerFunctionContext(reason, native_context); | |
1653 Node* const map = LoadContextElement( | |
1654 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
1655 Node* const thrower_info = LoadContextElement( | |
1656 native_context, Context::PROMISE_THROWER_FINALLY_SHARED_FUN); | |
1657 Node* const thrower = | |
1658 AllocateFunctionWithMapAndContext(map, thrower_info, thrower_context); | |
1659 return thrower; | |
1660 } | |
1661 | |
1662 TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) { | |
1663 CSA_ASSERT_JS_ARGC_EQ(this, 1); | |
1664 | |
1665 Node* const reason = Parameter(1); | |
1666 Node* const context = Parameter(4); | |
1667 | |
1668 Node* const on_finally = LoadContextElement(context, kOnFinallySlot); | |
1669 | |
1670 // 2.a Let result be ? Call(onFinally, undefined). | |
1671 Callable call_callable = CodeFactory::Call(isolate()); | |
1672 Node* result = | |
1673 CallJS(call_callable, context, on_finally, UndefinedConstant()); | |
1674 | |
1675 // 2.b Let promise be ! PromiseResolve( %Promise%, result). | |
1676 Node* const promise = AllocateAndInitJSPromise(context); | |
1677 InternalResolvePromise(context, promise, result); | |
1678 | |
1679 // 2.c Let thrower be equivalent to a function that throws reason. | |
1680 Node* native_context = LoadNativeContext(context); | |
1681 Node* const thrower = CreateThrowerFunction(reason, native_context); | |
1682 | |
1683 // 2.d Let promiseCapability be ! NewPromiseCapability( %Promise%). | |
1684 Node* const promise_capability = AllocateAndInitJSPromise(context, promise); | |
1685 | |
1686 // 2.e Return PerformPromiseThen(promise, thrower, undefined, | |
1687 // promiseCapability). | |
1688 InternalPerformPromiseThen(context, promise, thrower, UndefinedConstant(), | |
1689 promise_capability, UndefinedConstant(), | |
1690 UndefinedConstant()); | |
1691 Return(promise_capability); | |
1692 } | |
1693 | |
1694 TF_BUILTIN(PromiseFinally, PromiseBuiltinsAssembler) { | |
1695 CSA_ASSERT_JS_ARGC_EQ(this, 1); | |
1696 | |
1697 // 1. Let promise be the this value. | |
1698 Node* const promise = Parameter(0); | |
1699 Node* const on_finally = Parameter(1); | |
1700 Node* const context = Parameter(4); | |
1701 | |
1702 // 2. If IsPromise(promise) is false, throw a TypeError exception. | |
1703 ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, | |
1704 "Promise.prototype.finally"); | |
1705 | |
1706 Variable var_then_finally(this, MachineRepresentation::kTagged), | |
1707 var_catch_finally(this, MachineRepresentation::kTagged); | |
1708 | |
1709 Label if_notcallable(this, Label::kDeferred), perform_finally(this); | |
1710 | |
1711 // 3. Let thenFinally be ! CreateThenFinally(onFinally). | |
1712 // 4. Let catchFinally be ! CreateCatchFinally(onFinally). | |
1713 GotoIf(TaggedIsSmi(on_finally), &if_notcallable); | |
1714 Node* const on_finally_map = LoadMap(on_finally); | |
1715 GotoIfNot(IsCallableMap(on_finally_map), &if_notcallable); | |
1716 | |
1717 Node* const native_context = LoadNativeContext(context); | |
1718 Node* then_finally = nullptr; | |
1719 Node* catch_finally = nullptr; | |
1720 std::tie(then_finally, catch_finally) = | |
1721 CreatePromiseFinallyFunctions(on_finally, native_context); | |
1722 var_then_finally.Bind(then_finally); | |
1723 var_catch_finally.Bind(catch_finally); | |
1724 Goto(&perform_finally); | |
1725 | |
1726 Bind(&if_notcallable); | |
1727 { | |
1728 var_then_finally.Bind(on_finally); | |
1729 var_catch_finally.Bind(on_finally); | |
1730 Goto(&perform_finally); | |
1731 } | |
1732 | |
1733 // 5. Return PerformPromiseThen(promise, valueThunk, undefined, | |
1734 // promiseCapability). | |
1735 Bind(&perform_finally); | |
1736 Label if_nativepromise(this), if_custompromise(this, Label::kDeferred); | |
1737 BranchIfFastPath(context, promise, &if_nativepromise, &if_custompromise); | |
1738 | |
1739 Bind(&if_nativepromise); | |
1740 { | |
1741 Node* deferred_promise = AllocateAndInitJSPromise(context, promise); | |
1742 InternalPerformPromiseThen(context, promise, var_then_finally.value(), | |
1743 var_catch_finally.value(), deferred_promise, | |
1744 UndefinedConstant(), UndefinedConstant()); | |
1745 Return(deferred_promise); | |
1746 } | |
1747 | |
1748 Bind(&if_custompromise); | |
1749 { | |
1750 Node* const then = | |
1751 GetProperty(context, promise, isolate()->factory()->then_string()); | |
1752 Callable call_callable = CodeFactory::Call(isolate()); | |
1753 // 5. Return ? Invoke(promise, "then", « thenFinally, catchFinally »). | |
1754 Node* const result = | |
1755 CallJS(call_callable, context, then, promise, var_then_finally.value(), | |
1756 var_catch_finally.value()); | |
1757 Return(result); | |
1758 } | |
1759 } | |
1760 | |
1761 } // namespace internal | |
1762 } // namespace v8 | |
OLD | NEW |