Skip to content

Commit

Permalink
Execution stack & promises (#2107)
Browse files Browse the repository at this point in the history
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
```
  • Loading branch information
Razican committed Jun 15, 2022
1 parent 4f8398e commit 0454dde
Show file tree
Hide file tree
Showing 19 changed files with 1,670 additions and 26 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 38 additions & 8 deletions boa_engine/src/builtins/iterable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,19 +202,26 @@ pub struct IteratorResult {
}

impl IteratorResult {
/// Get `done` property of iterator result object.
/// `IteratorComplete ( iterResult )`
///
/// The abstract operation `IteratorComplete` takes argument `iterResult` (an `Object`) and
/// returns either a normal completion containing a `Boolean` or a throw completion.
///
/// More information:
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-iteratorclose
/// [spec]: https://tc39.es/ecma262/#sec-iteratorcomplete
#[inline]
pub fn complete(&self, context: &mut Context) -> JsResult<bool> {
// 1. Return ToBoolean(? Get(iterResult, "done")).
Ok(self.object.get("done", context)?.to_boolean())
}

/// Get `value` property of iterator result object.
/// `IteratorValue ( iterResult )`
///
/// The abstract operation `IteratorValue` takes argument `iterResult` (an `Object`) and
/// returns either a normal completion containing an ECMAScript language value or a throw
/// completion.
///
/// More information:
/// - [ECMA reference][spec]
Expand All @@ -226,13 +233,16 @@ impl IteratorResult {
self.object.get("value", context)
}
}

/// Iterator Record
///
/// An Iterator Record is a Record value used to encapsulate an
/// `Iterator` or `AsyncIterator` along with the next method.
/// `Iterator` or `AsyncIterator` along with the `next` method.
///
/// More information:
/// - [ECMA reference][spec]
///
/// [spec]:https://tc39.es/ecma262/#table-iterator-record-fields
/// [spec]: https://tc39.es/ecma262/#sec-iterator-records
#[derive(Debug)]
pub struct IteratorRecord {
/// `[[Iterator]]`
Expand Down Expand Up @@ -265,7 +275,11 @@ impl IteratorRecord {
&self.next_function
}

/// Get the next value in the iterator
/// `IteratorNext ( iteratorRecord [ , value ] )`
///
/// The abstract operation `IteratorNext` takes argument `iteratorRecord` (an `Iterator`
/// Record) and optional argument `value` (an ECMAScript language value) and returns either a
/// normal completion containing an `Object` or a throw completion.
///
/// More information:
/// - [ECMA reference][spec]
Expand Down Expand Up @@ -298,7 +312,18 @@ impl IteratorRecord {
}
}

#[inline]
/// `IteratorStep ( iteratorRecord )`
///
/// The abstract operation `IteratorStep` takes argument `iteratorRecord` (an `Iterator`
/// Record) and returns either a normal completion containing either an `Object` or `false`, or
/// a throw completion. It requests the next value from `iteratorRecord.[[Iterator]]` by
/// calling `iteratorRecord.[[NextMethod]]` and returns either `false` indicating that the
/// iterator has reached its end or the `IteratorResult` object if a next value is available.
///
/// More information:
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-iteratorstep
pub(crate) fn step(&self, context: &mut Context) -> JsResult<Option<IteratorResult>> {
let _timer = Profiler::global().start_event("IteratorRecord::step", "iterator");

Expand All @@ -317,7 +342,12 @@ impl IteratorRecord {
Ok(Some(result))
}

/// Cleanup the iterator
/// `IteratorClose ( iteratorRecord, completion )`
///
/// The abstract operation `IteratorClose` takes arguments `iteratorRecord` (an
/// [Iterator Record][Self]) and `completion` (a Completion Record) and returns a Completion
/// Record. It is used to notify an iterator that it should perform any actions it would
/// normally perform when it has reached its completed state.
///
/// More information:
/// - [ECMA reference][spec]
Expand Down
5 changes: 4 additions & 1 deletion boa_engine/src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod math;
pub mod nan;
pub mod number;
pub mod object;
pub mod promise;
pub mod proxy;
pub mod reflect;
pub mod regexp;
Expand Down Expand Up @@ -57,6 +58,7 @@ pub(crate) use self::{
number::Number,
object::for_in_iterator::ForInIterator,
object::Object as BuiltInObjectObject,
promise::Promise,
proxy::Proxy,
reflect::Reflect,
regexp::RegExp,
Expand Down Expand Up @@ -182,7 +184,8 @@ pub fn init(context: &mut Context) {
AggregateError,
Reflect,
Generator,
GeneratorFunction
GeneratorFunction,
Promise
};

#[cfg(feature = "intl")]
Expand Down

0 comments on commit 0454dde

Please sign in to comment.