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
|
|
|
use crate::{prelude::JsObject, 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
|
|
|
|
|
|
|
/// `JobCallback` records
|
|
|
|
///
|
|
|
|
/// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-jobcallback-records
|
|
|
|
#[derive(Debug, Clone, Trace, Finalize)]
|
|
|
|
pub struct JobCallback {
|
|
|
|
callback: JsObject,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl JobCallback {
|
|
|
|
/// `HostMakeJobCallback ( callback )`
|
|
|
|
///
|
|
|
|
/// The host-defined abstract operation `HostMakeJobCallback` takes argument `callback` (a
|
|
|
|
/// function object) and returns a `JobCallback` Record.
|
|
|
|
///
|
|
|
|
/// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-hostmakejobcallback
|
|
|
|
pub fn make_job_callback(callback: JsObject) -> Self {
|
|
|
|
// 1. Return the JobCallback Record { [[Callback]]: callback, [[HostDefined]]: empty }.
|
|
|
|
Self { callback }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `HostCallJobCallback ( jobCallback, V, argumentsList )`
|
|
|
|
///
|
|
|
|
/// The host-defined abstract operation `HostCallJobCallback` takes arguments `jobCallback` (a
|
|
|
|
/// `JobCallback` Record), `V` (an ECMAScript language value), and `argumentsList` (a `List` of
|
|
|
|
/// ECMAScript language values) and returns either a normal completion containing an ECMAScript
|
|
|
|
/// language value or a throw completion.
|
|
|
|
///
|
|
|
|
/// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-hostcalljobcallback
|
|
|
|
pub fn call_job_callback(
|
|
|
|
&self,
|
|
|
|
v: &JsValue,
|
|
|
|
arguments_list: &[JsValue],
|
|
|
|
context: &mut Context,
|
|
|
|
) -> JsResult<JsValue> {
|
|
|
|
// It must perform and return the result of Call(jobCallback.[[Callback]], V, argumentsList).
|
|
|
|
|
|
|
|
// 1. Assert: IsCallable(jobCallback.[[Callback]]) is true.
|
|
|
|
assert!(
|
|
|
|
self.callback.is_callable(),
|
|
|
|
"the callback of the job callback was not callable"
|
|
|
|
);
|
|
|
|
|
|
|
|
// 2. Return ? Call(jobCallback.[[Callback]], V, argumentsList).
|
|
|
|
self.callback.__call__(v, arguments_list, context)
|
|
|
|
}
|
|
|
|
}
|