mirror of https://github.com/boa-dev/boa.git
Browse Source
As part of the new modules PR, I was working on implementing the [host hooks](https://tc39.es/ecma262/#sec-host-hooks-summary) for the module import hooks and custom job queues. However, the promises module needed a bit of a refactor in order to couple with the new API. So, I thought it was a good idea to separate the promises refactor into its own PR, since the other PR is already big as it is. - Replaced some usages of `JobCallback` with a new `NativeJob` that isn't traced by the GC, since those closures are always rooted and executed by the `Context` globally. This will also allow hosts to pass their custom jobs to the job queue, and maybe could also accept futures in the Future (pun intended 😆). - Refactored several functions to account for the `HostPromiseRejectionTracker` hook which needs the promise `JsObject`. - Rewrote some patterns with newer Rust idioms.pull/2513/head
José Julián Espina
2 years ago
8 changed files with 401 additions and 402 deletions
@ -1,194 +0,0 @@
|
||||
use super::{Promise, PromiseCapability}; |
||||
use crate::{ |
||||
builtins::promise::{ReactionRecord, ReactionType}, |
||||
job::JobCallback, |
||||
native_function::NativeFunction, |
||||
object::{FunctionObjectBuilder, 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 = FunctionObjectBuilder::new( |
||||
context, |
||||
NativeFunction::from_copy_closure_with_captures( |
||||
|_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) |
||||
.map_err(|e| e.to_opaque(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]] »).
|
||||
reject.call(&JsValue::Undefined, &[value], context) |
||||
} |
||||
|
||||
// i. Else,
|
||||
Ok(value) => { |
||||
// i. Return ? Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »).
|
||||
resolve.call(&JsValue::Undefined, &[value], context) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
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 = FunctionObjectBuilder::new( |
||||
context, |
||||
NativeFunction::from_copy_closure_with_captures( |
||||
|_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.clone().into(), |
||||
resolving_functions.reject.clone().into(), |
||||
], |
||||
context, |
||||
); |
||||
|
||||
// c. If thenCallResult is an abrupt completion, then
|
||||
if let Err(value) = then_call_result { |
||||
let value = value.to_opaque(context); |
||||
// i. Return ? Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »).
|
||||
return resolving_functions.reject.call( |
||||
&JsValue::Undefined, |
||||
&[value], |
||||
context, |
||||
); |
||||
} |
||||
|
||||
// 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, |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue