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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
use super::{Promise, PromiseCapability};
use crate::{
builtins::promise::{ReactionRecord, ReactionType},
job::JobCallback,
object::{FunctionBuilder, JsObject},
Context, JsValue,
};
use boa_gc::{Finalize, Trace};
#[derive(Debug, Clone, Copy)]
pub(crate) struct PromiseJob;
impl PromiseJob {
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-newpromisereactionjob
pub(crate) fn new_promise_reaction_job(
reaction: ReactionRecord,
argument: JsValue,
context: &mut Context,
) -> JobCallback {
#[derive(Debug, Trace, Finalize)]
struct ReactionJobCaptures {
reaction: ReactionRecord,
argument: JsValue,
}
// 1. Let job be a new Job Abstract Closure with no parameters that captures reaction and argument and performs the following steps when called:
let job = FunctionBuilder::closure_with_captures(
context,
|_this, _args, captures, context| {
let ReactionJobCaptures { reaction, argument } = captures;
let ReactionRecord {
// a. Let promiseCapability be reaction.[[Capability]].
promise_capability,
// b. Let type be reaction.[[Type]].
reaction_type,
// c. Let handler be reaction.[[Handler]].
handler,
} = reaction;
let handler_result = match handler {
// d. If handler is empty, then
None => match reaction_type {
// i. If type is Fulfill, let handlerResult be NormalCompletion(argument).
ReactionType::Fulfill => Ok(argument.clone()),
// ii. Else,
// 1. Assert: type is Reject.
ReactionType::Reject => {
// 2. Let handlerResult be ThrowCompletion(argument).
Err(argument.clone())
}
},
// e. Else, let handlerResult be Completion(HostCallJobCallback(handler, undefined, « argument »)).
Some(handler) => {
handler.call_job_callback(&JsValue::Undefined, &[argument.clone()], context)
}
};
match promise_capability {
None => {
// f. If promiseCapability is undefined, then
// i. Assert: handlerResult is not an abrupt completion.
assert!(
handler_result.is_ok(),
"Assertion: <handlerResult is not an abrupt completion> failed"
);
// ii. Return empty.
Ok(JsValue::Undefined)
}
Some(promise_capability_record) => {
// g. Assert: promiseCapability is a PromiseCapability Record.
let PromiseCapability {
promise: _,
resolve,
reject,
} = promise_capability_record;
match handler_result {
// h. If handlerResult is an abrupt completion, then
Err(value) => {
// i. Return ? Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »).
context.call(&reject.clone().into(), &JsValue::Undefined, &[value])
}
// i. Else,
Ok(value) => {
// i. Return ? Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »).
context.call(&resolve.clone().into(), &JsValue::Undefined, &[value])
}
}
}
}
},
ReactionJobCaptures { reaction, argument },
)
.build()
.into();
// 2. Let handlerRealm be null.
// 3. If reaction.[[Handler]] is not empty, then
// a. Let getHandlerRealmResult be Completion(GetFunctionRealm(reaction.[[Handler]].[[Callback]])).
// b. If getHandlerRealmResult is a normal completion, set handlerRealm to getHandlerRealmResult.[[Value]].
// c. Else, set handlerRealm to the current Realm Record.
// d. NOTE: handlerRealm is never null unless the handler is undefined. When the handler is a revoked Proxy and no ECMAScript code runs, handlerRealm is used to create error objects.
// 4. Return the Record { [[Job]]: job, [[Realm]]: handlerRealm }.
JobCallback::make_job_callback(job)
}
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob
pub(crate) fn new_promise_resolve_thenable_job(
promise_to_resolve: JsObject,
thenable: JsValue,
then: JobCallback,
context: &mut Context,
) -> JobCallback {
// 1. Let job be a new Job Abstract Closure with no parameters that captures promiseToResolve, thenable, and then and performs the following steps when called:
let job = FunctionBuilder::closure_with_captures(
context,
|_this: &JsValue, _args: &[JsValue], captures, context: &mut Context| {
let JobCapture {
promise_to_resolve,
thenable,
then,
} = captures;
// a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
let resolving_functions =
Promise::create_resolving_functions(promise_to_resolve, context);
// b. Let thenCallResult be Completion(HostCallJobCallback(then, thenable, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)).
let then_call_result = then.call_job_callback(
thenable,
&[
resolving_functions.resolve,
resolving_functions.reject.clone(),
],
context,
);
// c. If thenCallResult is an abrupt completion, then
if let Err(value) = then_call_result {
// i. Return ? Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »).
return context.call(
&resolving_functions.reject,
&JsValue::Undefined,
&[value],
);
}
// d. Return ? thenCallResult.
then_call_result
},
JobCapture::new(promise_to_resolve, thenable, then),
)
.build();
// 2. Let getThenRealmResult be Completion(GetFunctionRealm(then.[[Callback]])).
// 3. If getThenRealmResult is a normal completion, let thenRealm be getThenRealmResult.[[Value]].
// 4. Else, let thenRealm be the current Realm Record.
// 5. NOTE: thenRealm is never null. When then.[[Callback]] is a revoked Proxy and no code runs, thenRealm is used to create error objects.
// 6. Return the Record { [[Job]]: job, [[Realm]]: thenRealm }.
JobCallback::make_job_callback(job.into())
}
}
#[derive(Debug, Trace, Finalize)]
struct JobCapture {
promise_to_resolve: JsObject,
thenable: JsValue,
then: JobCallback,
}
impl JobCapture {
fn new(promise_to_resolve: JsObject, thenable: JsValue, then: JobCallback) -> Self {
Self {
promise_to_resolve,
thenable,
then,
}
}
}