Rust编写的JavaScript引擎,该项目是一个试验性质的项目。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

273 lines
10 KiB

//! 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};
use crate::{
object::{JsFunction, NativeObject},
realm::Realm,
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.
/// 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.
///
/// `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
/// 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
/// 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
/// 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>,
}
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,
}
}
/// Creates a new `NativeJob` from a closure and an execution realm.
pub fn with_realm<F>(f: F, realm: Realm) -> Self
where
F: FnOnce(&mut Context<'_>) -> JsResult<JsValue> + 'static,
{
Self {
f: Box::new(f),
realm: Some(realm),
}
}
/// 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(self, context: &mut Context<'_>) -> JsResult<JsValue> {
if let Some(realm) = self.realm {
let old_realm = context.enter_realm(realm);
let result = (self.f)(context);
context.enter_realm(old_realm);
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
#[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,
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 {
/// Creates a new `JobCallback`.
pub fn new<T: Any + Trace>(callback: JsFunction, host_defined: T) -> Self {
Self {
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
}
/// 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. Note that host-defined [Jobs] need to satisfy
/// a set of requirements for them to be spec-compliant.
///
/// [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.
///
/// 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<'_>);
}
/// 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`]:
///
/// ```
/// 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
#[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<'_>) {}
}
/// 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.
///
/// To disable running promise jobs on the engine, see [`IdleJobQueue`].
#[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<'_>) {
// If realm is not null ...
// TODO
// Let scriptOrModule be ...
// TODO
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
}