Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
//! Boa's API to create and customize `ECMAScript` jobs and job queues.
|
|
|
|
//!
|
|
|
|
//! [`NativeJob`] is an ECMAScript [Job], or a closure that runs an `ECMAScript` computation when
|
|
|
|
//! there's no other computation running.
|
|
|
|
//!
|
|
|
|
//! [`JobCallback`] is an ECMAScript [`JobCallback`] record, containing an `ECMAScript` function
|
|
|
|
//! that is executed when a promise is either fulfilled or rejected.
|
|
|
|
//!
|
|
|
|
//! [`JobQueue`] is a trait encompassing the required functionality for a job queue; this allows
|
|
|
|
//! implementing custom event loops, custom handling of Jobs or other fun things.
|
|
|
|
//! This trait is also accompanied by two implementors of the trait:
|
|
|
|
//! - [`IdleJobQueue`], which is a queue that does nothing, and the default queue if no queue is
|
|
|
|
//! provided. Useful for hosts that want to disable promises.
|
|
|
|
//! - [`SimpleJobQueue`], which is a simple FIFO queue that runs all jobs to completion, bailing
|
|
|
|
//! on the first error encountered.
|
|
|
|
//!
|
|
|
|
//! [Job]: https://tc39.es/ecma262/#sec-jobs
|
|
|
|
//! [JobCallback]: https://tc39.es/ecma262/#sec-jobcallback-records
|
|
|
|
|
|
|
|
use std::{any::Any, cell::RefCell, collections::VecDeque, fmt::Debug, future::Future, pin::Pin};
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
|
|
|
|
use crate::{
|
|
|
|
object::{JsFunction, NativeObject},
|
|
|
|
realm::Realm,
|
|
|
|
vm::ActiveRunnable,
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
Context, JsResult, JsValue,
|
|
|
|
};
|
|
|
|
use boa_gc::{Finalize, Trace};
|
Execution stack & promises (#2107)
This PR overrides #1923. It also removes the `queues` dependency added there, and rebases it to the latest `main` branch state.
It adds the following:
- A job queue (in `Context`)
- The constructor [`Promise`](https://tc39.es/ecma262/#sec-promise-executor)
- [`Promise.race`](https://tc39.es/ecma262/#sec-promise.race)
- [`Promise.reject`](https://tc39.es/ecma262/#sec-promise.reject)
- [`Promise.resolve`](https://tc39.es/ecma262/#sec-promise.resolve)
- [`get Promise [ @@species ]`](https://tc39.es/ecma262/#sec-get-promise-@@species)
- [`Promise.prototype [ @@toStringTag ]`](https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag)
- [`Promise.prototype.then`](https://tc39.es/ecma262/#sec-promise.prototype.then)
- [`Promise.prototype.finally`](https://tc39.es/ecma262/#sec-promise.prototype.finally)
- [`Promise.prototype.catch`](https://tc39.es/ecma262/#sec-promise.prototype.catch)
- The additional needed infrastructure
- [`PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )`](https://tc39.es/ecma262/#sec-performpromisethen)
- [`TriggerPromiseReactions ( reactions, argument )`](https://tc39.es/ecma262/#sec-triggerpromisereactions)
- [`PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve )`](https://tc39.es/ecma262/#sec-performpromiserace)
- [`RejectPromise ( promise, reason )`](https://tc39.es/ecma262/#sec-rejectpromise)
- [`FulfillPromise ( promise, value )`](https://tc39.es/ecma262/#sec-fulfillpromise)
- [`IfAbruptRejectPromise ( value, capability )`](https://tc39.es/ecma262/#sec-ifabruptrejectpromise)
- [`CreateResolvingFunctions ( promise )`](https://tc39.es/ecma262/#sec-createresolvingfunctions)
- [`NewPromiseCapability ( C )`](https://tc39.es/ecma262/#sec-newpromisecapability)
- [`NewPromiseReactionJob ( reaction, argument )`](https://tc39.es/ecma262/#sec-newpromisereactionjob)
- [`NewPromiseResolveThenableJob ( promiseToResolve, thenable, then )`](https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob)
- [`PromiseResolve ( C, x )`](https://tc39.es/ecma262/#sec-promise-resolve)
- A test case showcasing the run-to-completion semantics.
An example program that shows the control flow with this addition is:
```javascript
new Promise((res, rej) => {
console.log("A");
res(undefined);
}).then((_) => console.log("B"));
console.log("C");
```
Which would output:
```
A
C
B
```
2 years ago
|
|
|
|
|
|
|
/// The [`Future`] job passed to the [`JobQueue::enqueue_future_job`] operation.
|
|
|
|
pub type FutureJob = Pin<Box<dyn Future<Output = NativeJob> + 'static>>;
|
|
|
|
|
|
|
|
/// An ECMAScript [Job] closure.
|
|
|
|
///
|
|
|
|
/// The specification allows scheduling any [`NativeJob`] closure by the host into the job queue.
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
/// However, host-defined jobs must abide to a set of requirements.
|
|
|
|
///
|
|
|
|
/// ### Requirements
|
|
|
|
///
|
|
|
|
/// - At some future point in time, when there is no running execution context and the execution
|
|
|
|
/// context stack is empty, the implementation must:
|
|
|
|
/// - Perform any host-defined preparation steps.
|
|
|
|
/// - Invoke the Job Abstract Closure.
|
|
|
|
/// - Perform any host-defined cleanup steps, after which the execution context stack must be empty.
|
|
|
|
/// - Only one Job may be actively undergoing evaluation at any point in time.
|
|
|
|
/// - Once evaluation of a Job starts, it must run to completion before evaluation of any other Job starts.
|
|
|
|
/// - The Abstract Closure must return a normal completion, implementing its own handling of errors.
|
|
|
|
///
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
/// `NativeJob`s API differs slightly on the last requirement, since it allows closures returning
|
|
|
|
/// [`JsResult`], but it's okay because `NativeJob`s are handled by the host anyways; a host could
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
/// pass a closure returning `Err` and handle the error on [`JobQueue::run_jobs`], making the closure
|
|
|
|
/// effectively run as if it never returned `Err`.
|
|
|
|
///
|
|
|
|
/// ## [`Trace`]?
|
|
|
|
///
|
|
|
|
/// `NativeJob` doesn't implement `Trace` because it doesn't need to; all jobs can only be run once
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
/// and putting a [`JobQueue`] on a garbage collected object should definitely be discouraged.
|
|
|
|
///
|
|
|
|
/// On the other hand, it seems like this type breaks all the safety checks of the
|
|
|
|
/// [`NativeFunction`] API, since you can capture any `Trace` variable into the closure... but it
|
|
|
|
/// doesn't!
|
|
|
|
/// The garbage collector doesn't need to trace the captured variables because the closures
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
/// are always stored on the [`JobQueue`], which is always rooted, which means the captured variables
|
|
|
|
/// are also rooted, allowing us to capture any variable in the closure for free!
|
|
|
|
///
|
|
|
|
/// [Job]: https://tc39.es/ecma262/#sec-jobs
|
|
|
|
/// [`NativeFunction`]: crate::native_function::NativeFunction
|
|
|
|
pub struct NativeJob {
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
f: Box<dyn FnOnce(&mut Context<'_>) -> JsResult<JsValue>>,
|
|
|
|
realm: Option<Realm>,
|
|
|
|
active_runnable: Option<ActiveRunnable>,
|
|
|
|
}
|
|
|
|
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
impl Debug for NativeJob {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("NativeJob").field("f", &"Closure").finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl NativeJob {
|
|
|
|
/// Creates a new `NativeJob` from a closure.
|
|
|
|
pub fn new<F>(f: F) -> Self
|
|
|
|
where
|
|
|
|
F: FnOnce(&mut Context<'_>) -> JsResult<JsValue> + 'static,
|
|
|
|
{
|
|
|
|
Self {
|
|
|
|
f: Box::new(f),
|
|
|
|
realm: None,
|
|
|
|
active_runnable: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a new `NativeJob` from a closure and an execution realm.
|
|
|
|
pub fn with_realm<F>(f: F, realm: Realm, context: &mut Context<'_>) -> Self
|
|
|
|
where
|
|
|
|
F: FnOnce(&mut Context<'_>) -> JsResult<JsValue> + 'static,
|
|
|
|
{
|
|
|
|
Self {
|
|
|
|
f: Box::new(f),
|
|
|
|
realm: Some(realm),
|
|
|
|
active_runnable: context.vm.active_runnable.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets a reference to the execution realm of the job.
|
|
|
|
pub const fn realm(&self) -> Option<&Realm> {
|
|
|
|
self.realm.as_ref()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Calls the native job with the specified [`Context`].
|
|
|
|
///
|
|
|
|
/// # Note
|
|
|
|
///
|
|
|
|
/// If the native job has an execution realm defined, this sets the running execution
|
|
|
|
/// context to the realm's before calling the inner closure, and resets it after execution.
|
|
|
|
pub fn call(mut self, context: &mut Context<'_>) -> JsResult<JsValue> {
|
|
|
|
// If realm is not null, each time job is invoked the implementation must perform
|
|
|
|
// implementation-defined steps such that execution is prepared to evaluate ECMAScript
|
|
|
|
// code at the time of job's invocation.
|
|
|
|
if let Some(realm) = self.realm {
|
|
|
|
let old_realm = context.enter_realm(realm);
|
|
|
|
|
|
|
|
// Let scriptOrModule be GetActiveScriptOrModule() at the time HostEnqueuePromiseJob is
|
|
|
|
// invoked. If realm is not null, each time job is invoked the implementation must
|
|
|
|
// perform implementation-defined steps such that scriptOrModule is the active script or
|
|
|
|
// module at the time of job's invocation.
|
|
|
|
std::mem::swap(&mut context.vm.active_runnable, &mut self.active_runnable);
|
|
|
|
|
|
|
|
let result = (self.f)(context);
|
|
|
|
|
|
|
|
context.enter_realm(old_realm);
|
|
|
|
std::mem::swap(&mut context.vm.active_runnable, &mut self.active_runnable);
|
|
|
|
|
|
|
|
result
|
|
|
|
} else {
|
|
|
|
(self.f)(context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// [`JobCallback`][spec] records.
|
Execution stack & promises (#2107)
This PR overrides #1923. It also removes the `queues` dependency added there, and rebases it to the latest `main` branch state.
It adds the following:
- A job queue (in `Context`)
- The constructor [`Promise`](https://tc39.es/ecma262/#sec-promise-executor)
- [`Promise.race`](https://tc39.es/ecma262/#sec-promise.race)
- [`Promise.reject`](https://tc39.es/ecma262/#sec-promise.reject)
- [`Promise.resolve`](https://tc39.es/ecma262/#sec-promise.resolve)
- [`get Promise [ @@species ]`](https://tc39.es/ecma262/#sec-get-promise-@@species)
- [`Promise.prototype [ @@toStringTag ]`](https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag)
- [`Promise.prototype.then`](https://tc39.es/ecma262/#sec-promise.prototype.then)
- [`Promise.prototype.finally`](https://tc39.es/ecma262/#sec-promise.prototype.finally)
- [`Promise.prototype.catch`](https://tc39.es/ecma262/#sec-promise.prototype.catch)
- The additional needed infrastructure
- [`PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )`](https://tc39.es/ecma262/#sec-performpromisethen)
- [`TriggerPromiseReactions ( reactions, argument )`](https://tc39.es/ecma262/#sec-triggerpromisereactions)
- [`PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve )`](https://tc39.es/ecma262/#sec-performpromiserace)
- [`RejectPromise ( promise, reason )`](https://tc39.es/ecma262/#sec-rejectpromise)
- [`FulfillPromise ( promise, value )`](https://tc39.es/ecma262/#sec-fulfillpromise)
- [`IfAbruptRejectPromise ( value, capability )`](https://tc39.es/ecma262/#sec-ifabruptrejectpromise)
- [`CreateResolvingFunctions ( promise )`](https://tc39.es/ecma262/#sec-createresolvingfunctions)
- [`NewPromiseCapability ( C )`](https://tc39.es/ecma262/#sec-newpromisecapability)
- [`NewPromiseReactionJob ( reaction, argument )`](https://tc39.es/ecma262/#sec-newpromisereactionjob)
- [`NewPromiseResolveThenableJob ( promiseToResolve, thenable, then )`](https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob)
- [`PromiseResolve ( C, x )`](https://tc39.es/ecma262/#sec-promise-resolve)
- A test case showcasing the run-to-completion semantics.
An example program that shows the control flow with this addition is:
```javascript
new Promise((res, rej) => {
console.log("A");
res(undefined);
}).then((_) => console.log("B"));
console.log("C");
```
Which would output:
```
A
C
B
```
2 years ago
|
|
|
///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-jobcallback-records
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
#[derive(Trace, Finalize)]
|
Execution stack & promises (#2107)
This PR overrides #1923. It also removes the `queues` dependency added there, and rebases it to the latest `main` branch state.
It adds the following:
- A job queue (in `Context`)
- The constructor [`Promise`](https://tc39.es/ecma262/#sec-promise-executor)
- [`Promise.race`](https://tc39.es/ecma262/#sec-promise.race)
- [`Promise.reject`](https://tc39.es/ecma262/#sec-promise.reject)
- [`Promise.resolve`](https://tc39.es/ecma262/#sec-promise.resolve)
- [`get Promise [ @@species ]`](https://tc39.es/ecma262/#sec-get-promise-@@species)
- [`Promise.prototype [ @@toStringTag ]`](https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag)
- [`Promise.prototype.then`](https://tc39.es/ecma262/#sec-promise.prototype.then)
- [`Promise.prototype.finally`](https://tc39.es/ecma262/#sec-promise.prototype.finally)
- [`Promise.prototype.catch`](https://tc39.es/ecma262/#sec-promise.prototype.catch)
- The additional needed infrastructure
- [`PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )`](https://tc39.es/ecma262/#sec-performpromisethen)
- [`TriggerPromiseReactions ( reactions, argument )`](https://tc39.es/ecma262/#sec-triggerpromisereactions)
- [`PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve )`](https://tc39.es/ecma262/#sec-performpromiserace)
- [`RejectPromise ( promise, reason )`](https://tc39.es/ecma262/#sec-rejectpromise)
- [`FulfillPromise ( promise, value )`](https://tc39.es/ecma262/#sec-fulfillpromise)
- [`IfAbruptRejectPromise ( value, capability )`](https://tc39.es/ecma262/#sec-ifabruptrejectpromise)
- [`CreateResolvingFunctions ( promise )`](https://tc39.es/ecma262/#sec-createresolvingfunctions)
- [`NewPromiseCapability ( C )`](https://tc39.es/ecma262/#sec-newpromisecapability)
- [`NewPromiseReactionJob ( reaction, argument )`](https://tc39.es/ecma262/#sec-newpromisereactionjob)
- [`NewPromiseResolveThenableJob ( promiseToResolve, thenable, then )`](https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob)
- [`PromiseResolve ( C, x )`](https://tc39.es/ecma262/#sec-promise-resolve)
- A test case showcasing the run-to-completion semantics.
An example program that shows the control flow with this addition is:
```javascript
new Promise((res, rej) => {
console.log("A");
res(undefined);
}).then((_) => console.log("B"));
console.log("C");
```
Which would output:
```
A
C
B
```
2 years ago
|
|
|
pub struct JobCallback {
|
|
|
|
callback: JsFunction,
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
host_defined: Box<dyn NativeObject>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for JobCallback {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("JobCallback")
|
|
|
|
.field("callback", &self.callback)
|
|
|
|
.field("host_defined", &"dyn NativeObject")
|
|
|
|
.finish()
|
|
|
|
}
|
Execution stack & promises (#2107)
This PR overrides #1923. It also removes the `queues` dependency added there, and rebases it to the latest `main` branch state.
It adds the following:
- A job queue (in `Context`)
- The constructor [`Promise`](https://tc39.es/ecma262/#sec-promise-executor)
- [`Promise.race`](https://tc39.es/ecma262/#sec-promise.race)
- [`Promise.reject`](https://tc39.es/ecma262/#sec-promise.reject)
- [`Promise.resolve`](https://tc39.es/ecma262/#sec-promise.resolve)
- [`get Promise [ @@species ]`](https://tc39.es/ecma262/#sec-get-promise-@@species)
- [`Promise.prototype [ @@toStringTag ]`](https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag)
- [`Promise.prototype.then`](https://tc39.es/ecma262/#sec-promise.prototype.then)
- [`Promise.prototype.finally`](https://tc39.es/ecma262/#sec-promise.prototype.finally)
- [`Promise.prototype.catch`](https://tc39.es/ecma262/#sec-promise.prototype.catch)
- The additional needed infrastructure
- [`PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )`](https://tc39.es/ecma262/#sec-performpromisethen)
- [`TriggerPromiseReactions ( reactions, argument )`](https://tc39.es/ecma262/#sec-triggerpromisereactions)
- [`PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve )`](https://tc39.es/ecma262/#sec-performpromiserace)
- [`RejectPromise ( promise, reason )`](https://tc39.es/ecma262/#sec-rejectpromise)
- [`FulfillPromise ( promise, value )`](https://tc39.es/ecma262/#sec-fulfillpromise)
- [`IfAbruptRejectPromise ( value, capability )`](https://tc39.es/ecma262/#sec-ifabruptrejectpromise)
- [`CreateResolvingFunctions ( promise )`](https://tc39.es/ecma262/#sec-createresolvingfunctions)
- [`NewPromiseCapability ( C )`](https://tc39.es/ecma262/#sec-newpromisecapability)
- [`NewPromiseReactionJob ( reaction, argument )`](https://tc39.es/ecma262/#sec-newpromisereactionjob)
- [`NewPromiseResolveThenableJob ( promiseToResolve, thenable, then )`](https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob)
- [`PromiseResolve ( C, x )`](https://tc39.es/ecma262/#sec-promise-resolve)
- A test case showcasing the run-to-completion semantics.
An example program that shows the control flow with this addition is:
```javascript
new Promise((res, rej) => {
console.log("A");
res(undefined);
}).then((_) => console.log("B"));
console.log("C");
```
Which would output:
```
A
C
B
```
2 years ago
|
|
|
}
|
|
|
|
|
|
|
|
impl JobCallback {
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
/// Creates a new `JobCallback`.
|
|
|
|
pub fn new<T: Any + Trace>(callback: JsFunction, host_defined: T) -> Self {
|
|
|
|
Self {
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
callback,
|
|
|
|
host_defined: Box::new(host_defined),
|
|
|
|
}
|
Execution stack & promises (#2107)
This PR overrides #1923. It also removes the `queues` dependency added there, and rebases it to the latest `main` branch state.
It adds the following:
- A job queue (in `Context`)
- The constructor [`Promise`](https://tc39.es/ecma262/#sec-promise-executor)
- [`Promise.race`](https://tc39.es/ecma262/#sec-promise.race)
- [`Promise.reject`](https://tc39.es/ecma262/#sec-promise.reject)
- [`Promise.resolve`](https://tc39.es/ecma262/#sec-promise.resolve)
- [`get Promise [ @@species ]`](https://tc39.es/ecma262/#sec-get-promise-@@species)
- [`Promise.prototype [ @@toStringTag ]`](https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag)
- [`Promise.prototype.then`](https://tc39.es/ecma262/#sec-promise.prototype.then)
- [`Promise.prototype.finally`](https://tc39.es/ecma262/#sec-promise.prototype.finally)
- [`Promise.prototype.catch`](https://tc39.es/ecma262/#sec-promise.prototype.catch)
- The additional needed infrastructure
- [`PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )`](https://tc39.es/ecma262/#sec-performpromisethen)
- [`TriggerPromiseReactions ( reactions, argument )`](https://tc39.es/ecma262/#sec-triggerpromisereactions)
- [`PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve )`](https://tc39.es/ecma262/#sec-performpromiserace)
- [`RejectPromise ( promise, reason )`](https://tc39.es/ecma262/#sec-rejectpromise)
- [`FulfillPromise ( promise, value )`](https://tc39.es/ecma262/#sec-fulfillpromise)
- [`IfAbruptRejectPromise ( value, capability )`](https://tc39.es/ecma262/#sec-ifabruptrejectpromise)
- [`CreateResolvingFunctions ( promise )`](https://tc39.es/ecma262/#sec-createresolvingfunctions)
- [`NewPromiseCapability ( C )`](https://tc39.es/ecma262/#sec-newpromisecapability)
- [`NewPromiseReactionJob ( reaction, argument )`](https://tc39.es/ecma262/#sec-newpromisereactionjob)
- [`NewPromiseResolveThenableJob ( promiseToResolve, thenable, then )`](https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob)
- [`PromiseResolve ( C, x )`](https://tc39.es/ecma262/#sec-promise-resolve)
- A test case showcasing the run-to-completion semantics.
An example program that shows the control flow with this addition is:
```javascript
new Promise((res, rej) => {
console.log("A");
res(undefined);
}).then((_) => console.log("B"));
console.log("C");
```
Which would output:
```
A
C
B
```
2 years ago
|
|
|
}
|
|
|
|
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
/// Gets the inner callback of the job.
|
|
|
|
pub const fn callback(&self) -> &JsFunction {
|
|
|
|
&self.callback
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets a reference to the host defined additional field as an `Any` trait object.
|
|
|
|
pub fn host_defined(&self) -> &dyn Any {
|
|
|
|
self.host_defined.as_any()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets a mutable reference to the host defined additional field as an `Any` trait object.
|
|
|
|
pub fn host_defined_mut(&mut self) -> &mut dyn Any {
|
|
|
|
self.host_defined.as_mut_any()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A queue of `ECMAscript` [Jobs].
|
|
|
|
///
|
|
|
|
/// This is the main API that allows creating custom event loops with custom job queues.
|
|
|
|
///
|
|
|
|
/// [Jobs]: https://tc39.es/ecma262/#sec-jobs
|
|
|
|
pub trait JobQueue {
|
|
|
|
/// [`HostEnqueuePromiseJob ( job, realm )`][spec].
|
Execution stack & promises (#2107)
This PR overrides #1923. It also removes the `queues` dependency added there, and rebases it to the latest `main` branch state.
It adds the following:
- A job queue (in `Context`)
- The constructor [`Promise`](https://tc39.es/ecma262/#sec-promise-executor)
- [`Promise.race`](https://tc39.es/ecma262/#sec-promise.race)
- [`Promise.reject`](https://tc39.es/ecma262/#sec-promise.reject)
- [`Promise.resolve`](https://tc39.es/ecma262/#sec-promise.resolve)
- [`get Promise [ @@species ]`](https://tc39.es/ecma262/#sec-get-promise-@@species)
- [`Promise.prototype [ @@toStringTag ]`](https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag)
- [`Promise.prototype.then`](https://tc39.es/ecma262/#sec-promise.prototype.then)
- [`Promise.prototype.finally`](https://tc39.es/ecma262/#sec-promise.prototype.finally)
- [`Promise.prototype.catch`](https://tc39.es/ecma262/#sec-promise.prototype.catch)
- The additional needed infrastructure
- [`PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )`](https://tc39.es/ecma262/#sec-performpromisethen)
- [`TriggerPromiseReactions ( reactions, argument )`](https://tc39.es/ecma262/#sec-triggerpromisereactions)
- [`PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve )`](https://tc39.es/ecma262/#sec-performpromiserace)
- [`RejectPromise ( promise, reason )`](https://tc39.es/ecma262/#sec-rejectpromise)
- [`FulfillPromise ( promise, value )`](https://tc39.es/ecma262/#sec-fulfillpromise)
- [`IfAbruptRejectPromise ( value, capability )`](https://tc39.es/ecma262/#sec-ifabruptrejectpromise)
- [`CreateResolvingFunctions ( promise )`](https://tc39.es/ecma262/#sec-createresolvingfunctions)
- [`NewPromiseCapability ( C )`](https://tc39.es/ecma262/#sec-newpromisecapability)
- [`NewPromiseReactionJob ( reaction, argument )`](https://tc39.es/ecma262/#sec-newpromisereactionjob)
- [`NewPromiseResolveThenableJob ( promiseToResolve, thenable, then )`](https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob)
- [`PromiseResolve ( C, x )`](https://tc39.es/ecma262/#sec-promise-resolve)
- A test case showcasing the run-to-completion semantics.
An example program that shows the control flow with this addition is:
```javascript
new Promise((res, rej) => {
console.log("A");
res(undefined);
}).then((_) => console.log("B"));
console.log("C");
```
Which would output:
```
A
C
B
```
2 years ago
|
|
|
///
|
|
|
|
/// Enqueues a [`NativeJob`] on the job queue.
|
|
|
|
///
|
|
|
|
/// # Requirements
|
|
|
|
///
|
|
|
|
/// Per the [spec]:
|
|
|
|
/// > An implementation of `HostEnqueuePromiseJob` must conform to the requirements in [9.5][Jobs] as well as the
|
|
|
|
/// following:
|
|
|
|
/// > - If `realm` is not null, each time `job` is invoked the implementation must perform implementation-defined steps
|
|
|
|
/// such that execution is prepared to evaluate ECMAScript code at the time of job's invocation.
|
|
|
|
/// > - Let `scriptOrModule` be `GetActiveScriptOrModule()` at the time `HostEnqueuePromiseJob` is invoked. If realm
|
|
|
|
/// is not null, each time job is invoked the implementation must perform implementation-defined steps such that
|
|
|
|
/// `scriptOrModule` is the active script or module at the time of job's invocation.
|
|
|
|
/// > - Jobs must run in the same order as the `HostEnqueuePromiseJob` invocations that scheduled them.
|
|
|
|
///
|
|
|
|
/// Of all the requirements, Boa guarantees the first two by its internal implementation of `NativeJob`, meaning
|
|
|
|
/// the implementer must only guarantee that jobs are run in the same order as they're enqueued.
|
|
|
|
///
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-hostenqueuepromisejob
|
|
|
|
/// [Jobs]: https://tc39.es/ecma262/#sec-jobs
|
|
|
|
fn enqueue_promise_job(&self, job: NativeJob, context: &mut Context<'_>);
|
|
|
|
|
|
|
|
/// Runs all jobs in the queue.
|
|
|
|
///
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
/// Running a job could enqueue more jobs in the queue. The implementor of the trait
|
|
|
|
/// determines if the method should loop until there are no more queued jobs or if
|
|
|
|
/// it should only run one iteration of the queue.
|
|
|
|
fn run_jobs(&self, context: &mut Context<'_>);
|
|
|
|
|
|
|
|
/// Enqueues a new [`Future`] job on the job queue.
|
|
|
|
///
|
|
|
|
/// On completion, `future` returns a new [`NativeJob`] that needs to be enqueued into the
|
|
|
|
/// job queue to update the state of the inner `Promise`, which is what ECMAScript sees. Failing
|
|
|
|
/// to do this will leave the inner `Promise` in the `pending` state, which won't call any `then`
|
|
|
|
/// or `catch` handlers, even if `future` was already completed.
|
|
|
|
fn enqueue_future_job(&self, future: FutureJob, context: &mut Context<'_>);
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
}
|
|
|
|
|
|
|
|
/// A job queue that does nothing.
|
|
|
|
///
|
|
|
|
/// This queue is mostly useful if you want to disable the promise capabilities of the engine. This
|
|
|
|
/// can be done by passing a reference to it to the [`ContextBuilder`]:
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use boa_engine::{context::ContextBuilder, job::{JobQueue, IdleJobQueue}};
|
|
|
|
///
|
|
|
|
/// let queue: &dyn JobQueue = &IdleJobQueue;
|
|
|
|
/// let context = ContextBuilder::new().job_queue(queue).build();
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// [`ContextBuilder`]: crate::context::ContextBuilder
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub struct IdleJobQueue;
|
|
|
|
|
|
|
|
impl JobQueue for IdleJobQueue {
|
|
|
|
fn enqueue_promise_job(&self, _: NativeJob, _: &mut Context<'_>) {}
|
|
|
|
|
|
|
|
fn run_jobs(&self, _: &mut Context<'_>) {}
|
|
|
|
|
|
|
|
fn enqueue_future_job(&self, _: FutureJob, _: &mut Context<'_>) {}
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
}
|
|
|
|
|
|
|
|
/// A simple FIFO job queue that bails on the first error.
|
|
|
|
///
|
|
|
|
/// This is the default job queue for the [`Context`], but it is mostly pretty limited for
|
|
|
|
/// custom event queues.
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
///
|
|
|
|
/// To disable running promise jobs on the engine, see [`IdleJobQueue`].
|
Implement host hooks and job queues APIs (#2529)
Follows from #2528, and should complement #2411 to implement the module import hooks.
~~Similarly to the Intl/ICU4X PR (#2478), this has a lot of trivial changes caused by the new lifetimes. I thought about passing the queue and the hooks by value, but it was very painful having to wrap everything with `Rc` in order to be accessible by the host.
In contrast, `&dyn` can be easily provided by the host and has the advantage of not requiring additional allocations, with the downside of adding two more lifetimes to our `Context`, but I think it's worth.~~ I was able to unify all lifetimes into the shortest one of the three, making our API just like before!
Changes:
- Added a new `HostHooks` trait and a `&dyn HostHooks` field to `Context`. This allows hosts to implement the trait for their custom type, then pass it to the context.
- Added a new `JobQueue` trait and a `&dyn JobQueue` field to our `Context`, allowing custom event loops and other fun things.
- Added two simple implementations of `JobQueue`: `IdleJobQueue` which does nothing and `SimpleJobQueue` which runs all jobs until all successfully complete or until any of them throws an error.
- Modified `boa_cli` to run all jobs until the queue is empty, even if a job returns `Err`. This also prints all errors to the user.
2 years ago
|
|
|
#[derive(Default)]
|
|
|
|
pub struct SimpleJobQueue(RefCell<VecDeque<NativeJob>>);
|
|
|
|
|
|
|
|
impl Debug for SimpleJobQueue {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_tuple("SimpleQueue").field(&"..").finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SimpleJobQueue {
|
|
|
|
/// Creates an empty `SimpleJobQueue`.
|
|
|
|
#[must_use]
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl JobQueue for SimpleJobQueue {
|
|
|
|
fn enqueue_promise_job(&self, job: NativeJob, _: &mut Context<'_>) {
|
|
|
|
self.0.borrow_mut().push_back(job);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_jobs(&self, context: &mut Context<'_>) {
|
|
|
|
// Yeah, I have no idea why Rust extends the lifetime of a `RefCell` that should be immediately
|
|
|
|
// dropped after calling `pop_front`.
|
|
|
|
let mut next_job = self.0.borrow_mut().pop_front();
|
|
|
|
while let Some(job) = next_job {
|
|
|
|
if job.call(context).is_err() {
|
|
|
|
self.0.borrow_mut().clear();
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
next_job = self.0.borrow_mut().pop_front();
|
|
|
|
}
|
Execution stack & promises (#2107)
This PR overrides #1923. It also removes the `queues` dependency added there, and rebases it to the latest `main` branch state.
It adds the following:
- A job queue (in `Context`)
- The constructor [`Promise`](https://tc39.es/ecma262/#sec-promise-executor)
- [`Promise.race`](https://tc39.es/ecma262/#sec-promise.race)
- [`Promise.reject`](https://tc39.es/ecma262/#sec-promise.reject)
- [`Promise.resolve`](https://tc39.es/ecma262/#sec-promise.resolve)
- [`get Promise [ @@species ]`](https://tc39.es/ecma262/#sec-get-promise-@@species)
- [`Promise.prototype [ @@toStringTag ]`](https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag)
- [`Promise.prototype.then`](https://tc39.es/ecma262/#sec-promise.prototype.then)
- [`Promise.prototype.finally`](https://tc39.es/ecma262/#sec-promise.prototype.finally)
- [`Promise.prototype.catch`](https://tc39.es/ecma262/#sec-promise.prototype.catch)
- The additional needed infrastructure
- [`PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )`](https://tc39.es/ecma262/#sec-performpromisethen)
- [`TriggerPromiseReactions ( reactions, argument )`](https://tc39.es/ecma262/#sec-triggerpromisereactions)
- [`PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve )`](https://tc39.es/ecma262/#sec-performpromiserace)
- [`RejectPromise ( promise, reason )`](https://tc39.es/ecma262/#sec-rejectpromise)
- [`FulfillPromise ( promise, value )`](https://tc39.es/ecma262/#sec-fulfillpromise)
- [`IfAbruptRejectPromise ( value, capability )`](https://tc39.es/ecma262/#sec-ifabruptrejectpromise)
- [`CreateResolvingFunctions ( promise )`](https://tc39.es/ecma262/#sec-createresolvingfunctions)
- [`NewPromiseCapability ( C )`](https://tc39.es/ecma262/#sec-newpromisecapability)
- [`NewPromiseReactionJob ( reaction, argument )`](https://tc39.es/ecma262/#sec-newpromisereactionjob)
- [`NewPromiseResolveThenableJob ( promiseToResolve, thenable, then )`](https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob)
- [`PromiseResolve ( C, x )`](https://tc39.es/ecma262/#sec-promise-resolve)
- A test case showcasing the run-to-completion semantics.
An example program that shows the control flow with this addition is:
```javascript
new Promise((res, rej) => {
console.log("A");
res(undefined);
}).then((_) => console.log("B"));
console.log("C");
```
Which would output:
```
A
C
B
```
2 years ago
|
|
|
}
|
|
|
|
|
|
|
|
fn enqueue_future_job(&self, future: FutureJob, context: &mut Context<'_>) {
|
|
|
|
let job = pollster::block_on(future);
|
|
|
|
self.enqueue_promise_job(job, context);
|
|
|
|
}
|
Execution stack & promises (#2107)
This PR overrides #1923. It also removes the `queues` dependency added there, and rebases it to the latest `main` branch state.
It adds the following:
- A job queue (in `Context`)
- The constructor [`Promise`](https://tc39.es/ecma262/#sec-promise-executor)
- [`Promise.race`](https://tc39.es/ecma262/#sec-promise.race)
- [`Promise.reject`](https://tc39.es/ecma262/#sec-promise.reject)
- [`Promise.resolve`](https://tc39.es/ecma262/#sec-promise.resolve)
- [`get Promise [ @@species ]`](https://tc39.es/ecma262/#sec-get-promise-@@species)
- [`Promise.prototype [ @@toStringTag ]`](https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag)
- [`Promise.prototype.then`](https://tc39.es/ecma262/#sec-promise.prototype.then)
- [`Promise.prototype.finally`](https://tc39.es/ecma262/#sec-promise.prototype.finally)
- [`Promise.prototype.catch`](https://tc39.es/ecma262/#sec-promise.prototype.catch)
- The additional needed infrastructure
- [`PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )`](https://tc39.es/ecma262/#sec-performpromisethen)
- [`TriggerPromiseReactions ( reactions, argument )`](https://tc39.es/ecma262/#sec-triggerpromisereactions)
- [`PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve )`](https://tc39.es/ecma262/#sec-performpromiserace)
- [`RejectPromise ( promise, reason )`](https://tc39.es/ecma262/#sec-rejectpromise)
- [`FulfillPromise ( promise, value )`](https://tc39.es/ecma262/#sec-fulfillpromise)
- [`IfAbruptRejectPromise ( value, capability )`](https://tc39.es/ecma262/#sec-ifabruptrejectpromise)
- [`CreateResolvingFunctions ( promise )`](https://tc39.es/ecma262/#sec-createresolvingfunctions)
- [`NewPromiseCapability ( C )`](https://tc39.es/ecma262/#sec-newpromisecapability)
- [`NewPromiseReactionJob ( reaction, argument )`](https://tc39.es/ecma262/#sec-newpromisereactionjob)
- [`NewPromiseResolveThenableJob ( promiseToResolve, thenable, then )`](https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob)
- [`PromiseResolve ( C, x )`](https://tc39.es/ecma262/#sec-promise-resolve)
- A test case showcasing the run-to-completion semantics.
An example program that shows the control flow with this addition is:
```javascript
new Promise((res, rej) => {
console.log("A");
res(undefined);
}).then((_) => console.log("B"));
console.log("C");
```
Which would output:
```
A
C
B
```
2 years ago
|
|
|
}
|