1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/JobCallback.h>
#include <LibJS/Runtime/Promise.h>
#include <LibJS/Runtime/PromiseJobs.h>
#include <LibJS/Runtime/PromiseReaction.h>
namespace JS {
PromiseReactionJob* PromiseReactionJob::create(GlobalObject& global_object, PromiseReaction& reaction, Value argument)
{
return global_object.heap().allocate<PromiseReactionJob>(global_object, reaction, argument, *global_object.function_prototype());
}
PromiseReactionJob::PromiseReactionJob(PromiseReaction& reaction, Value argument, Object& prototype)
: NativeFunction(prototype)
, m_reaction(reaction)
, m_argument(argument)
{
}
// 27.2.2.1 NewPromiseReactionJob ( reaction, argument ), https://tc39.es/ecma262/#sec-newpromisereactionjob
ThrowCompletionOr<Value> PromiseReactionJob::call()
{
auto& global_object = this->global_object();
// a. Let promiseCapability be reaction.[[Capability]].
auto& promise_capability = m_reaction.capability();
// b. Let type be reaction.[[Type]].
auto type = m_reaction.type();
// c. Let handler be reaction.[[Handler]].
auto handler = m_reaction.handler();
Completion handler_result;
// d. If handler is empty, then
if (!handler.has_value()) {
dbgln_if(PROMISE_DEBUG, "[PromiseReactionJob @ {}]: Handler is empty", this);
// i. If type is Fulfill, let handlerResult be NormalCompletion(argument).
if (type == PromiseReaction::Type::Fulfill) {
dbgln_if(PROMISE_DEBUG, "[PromiseReactionJob @ {}]: Reaction type is Type::Fulfill, setting handler result to {}", this, m_argument);
handler_result = normal_completion(m_argument);
}
// ii. Else,
else {
// 1. Assert: type is Reject.
VERIFY(type == PromiseReaction::Type::Reject);
// 2. Let handlerResult be ThrowCompletion(argument).
dbgln_if(PROMISE_DEBUG, "[PromiseReactionJob @ {}]: Reaction type is Type::Reject, throwing exception with argument {}", this, m_argument);
handler_result = throw_completion(m_argument);
}
}
// e. Else, let handlerResult be HostCallJobCallback(handler, undefined, « argument »).
else {
dbgln_if(PROMISE_DEBUG, "[PromiseReactionJob @ {}]: Calling handler callback {} @ {} with argument {}", this, handler.value().callback->class_name(), handler.value().callback, m_argument);
handler_result = call_job_callback(global_object, handler.value(), js_undefined(), m_argument);
}
// f. If promiseCapability is undefined, then
if (!promise_capability.has_value()) {
// i. Assert: handlerResult is not an abrupt completion.
VERIFY(!handler_result.is_abrupt());
// ii. Return NormalCompletion(empty).
dbgln_if(PROMISE_DEBUG, "[PromiseReactionJob @ {}]: Reaction has no PromiseCapability, returning empty value", this);
// TODO: This can't return an empty value at the moment, because the implicit conversion to Completion would fail.
// Change it back when this is using completions (`return normal_completion({})`)
return js_undefined();
}
// g. Assert: promiseCapability is a PromiseCapability Record.
// h. If handlerResult is an abrupt completion, then
if (handler_result.is_abrupt()) {
// i. Let status be Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »).
auto* reject_function = promise_capability.value().reject;
dbgln_if(PROMISE_DEBUG, "[PromiseReactionJob @ {}]: Calling PromiseCapability's reject function @ {}", this, reject_function);
return JS::call(global_object, *reject_function, js_undefined(), *handler_result.value());
}
// i. Else,
else {
// i. Let status be Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »).
auto* resolve_function = promise_capability.value().resolve;
dbgln_if(PROMISE_DEBUG, "[PromiseReactionJob @ {}]: Calling PromiseCapability's resolve function @ {}", this, resolve_function);
return JS::call(global_object, *resolve_function, js_undefined(), *handler_result.value());
}
// j. Return Completion(status).
}
void PromiseReactionJob::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(&m_reaction);
visitor.visit(m_argument);
}
PromiseResolveThenableJob* PromiseResolveThenableJob::create(GlobalObject& global_object, Promise& promise_to_resolve, Value thenable, JobCallback then)
{
// FIXME: A bunch of stuff regarding realms, see step 2-5 in the spec linked below
return global_object.heap().allocate<PromiseResolveThenableJob>(global_object, promise_to_resolve, thenable, then, *global_object.function_prototype());
}
PromiseResolveThenableJob::PromiseResolveThenableJob(Promise& promise_to_resolve, Value thenable, JobCallback then, Object& prototype)
: NativeFunction(prototype)
, m_promise_to_resolve(promise_to_resolve)
, m_thenable(thenable)
, m_then(then)
{
}
// 27.2.2.2 NewPromiseResolveThenableJob ( promiseToResolve, thenable, then ), https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob
ThrowCompletionOr<Value> PromiseResolveThenableJob::call()
{
auto& global_object = this->global_object();
// a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
auto [resolve_function, reject_function] = m_promise_to_resolve.create_resolving_functions();
// b. Let thenCallResult be HostCallJobCallback(then, thenable, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »).
dbgln_if(PROMISE_DEBUG, "[PromiseResolveThenableJob @ {}]: Calling then job callback for thenable {}", this, &m_thenable);
auto then_call_result = call_job_callback(global_object, m_then, m_thenable, &resolve_function, &reject_function);
// c. If thenCallResult is an abrupt completion, then
if (then_call_result.is_error()) {
// i. Let status be Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »).
dbgln_if(PROMISE_DEBUG, "[PromiseResolveThenableJob @ {}]: then_call_result is an abrupt completion, calling reject function with value {}", this, *then_call_result.throw_completion().value());
auto status = JS::call(global_object, &reject_function, js_undefined(), *then_call_result.throw_completion().value());
// ii. Return Completion(status).
return status;
}
// d. Return Completion(thenCallResult).
dbgln_if(PROMISE_DEBUG, "[PromiseResolveThenableJob @ {}]: Returning then call result {}", this, then_call_result.value());
return then_call_result;
}
void PromiseResolveThenableJob::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(&m_promise_to_resolve);
visitor.visit(m_thenable);
visitor.visit(m_then.callback);
}
}
|