Browse Source

Started adding the [[Done]] field to iterators (#2125)

This Pull Request adds the `[[Done]]` field to iterator records.

Co-authored-by: raskad <32105367+raskad@users.noreply.github.com>
pull/2130/head
Iban Eguia 2 years ago
parent
commit
d173e08529
  1. 44
      Cargo.lock
  2. 2
      boa_cli/Cargo.toml
  3. 12
      boa_engine/src/builtins/array/mod.rs
  4. 2
      boa_engine/src/builtins/array_buffer/mod.rs
  5. 139
      boa_engine/src/builtins/iterable/mod.rs
  6. 19
      boa_engine/src/builtins/promise/mod.rs
  7. 34
      boa_engine/src/builtins/reflect/mod.rs
  8. 8
      boa_engine/src/builtins/regexp/mod.rs
  9. 2
      boa_engine/src/builtins/typed_array/mod.rs
  10. 45
      boa_engine/src/bytecompiler.rs
  11. 26
      boa_engine/src/context/mod.rs
  12. 11
      boa_engine/src/object/internal_methods/bound_function.rs
  13. 4
      boa_engine/src/object/internal_methods/function.rs
  14. 4
      boa_engine/src/object/internal_methods/mod.rs
  15. 12
      boa_engine/src/object/internal_methods/proxy.rs
  16. 13
      boa_engine/src/object/operations.rs
  17. 3
      boa_engine/src/vm/call_frame.rs
  18. 5
      boa_engine/src/vm/code_block.rs
  19. 147
      boa_engine/src/vm/mod.rs
  20. 34
      boa_engine/src/vm/opcode.rs
  21. 2
      test262

44
Cargo.lock generated

@ -75,7 +75,7 @@ version = "0.15.0"
dependencies = [ dependencies = [
"boa_engine", "boa_engine",
"boa_interner", "boa_interner",
"clap 3.1.18", "clap 3.2.5",
"colored", "colored",
"jemallocator", "jemallocator",
"phf", "phf",
@ -270,16 +270,16 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.1.18" version = "3.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7"
dependencies = [ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
"clap_derive", "clap_derive",
"clap_lex", "clap_lex",
"indexmap", "indexmap",
"lazy_static", "once_cell",
"strsim 0.10.0", "strsim 0.10.0",
"termcolor", "termcolor",
"textwrap 0.15.0", "textwrap 0.15.0",
@ -287,9 +287,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "3.1.18" version = "3.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9"
dependencies = [ dependencies = [
"heck 0.4.0", "heck 0.4.0",
"proc-macro-error", "proc-macro-error",
@ -300,9 +300,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.2.0" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
dependencies = [ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
@ -367,9 +367,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.4" version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
@ -388,26 +388,26 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.9.8" version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
"lazy_static",
"memoffset", "memoffset",
"once_cell",
"scopeguard", "scopeguard",
] ]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.8" version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" checksum = "8ff1f980957787286a554052d03c7aee98d99cc32e09f6d45f0a814133c87978"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"lazy_static", "once_cell",
] ]
[[package]] [[package]]
@ -859,9 +859,9 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.57" version = "0.3.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
@ -1718,9 +1718,9 @@ checksum = "1218098468b8085b19a2824104c70d976491d247ce194bbd9dc77181150cdfd6"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
@ -1846,9 +1846,9 @@ checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.57" version = "0.3.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",

2
boa_cli/Cargo.toml

@ -16,7 +16,7 @@ boa_engine = { path = "../boa_engine", features = ["deser", "console"], version
boa_interner = { path = "../boa_interner", version = "0.15.0" } boa_interner = { path = "../boa_interner", version = "0.15.0" }
rustyline = "9.1.2" rustyline = "9.1.2"
rustyline-derive = "0.6.0" rustyline-derive = "0.6.0"
clap = { version = "3.1.18", features = ["derive"] } clap = { version = "3.2.5", features = ["derive"] }
serde_json = "1.0.81" serde_json = "1.0.81"
colored = "2.0.0" colored = "2.0.0"
regex = "1.5.6" regex = "1.5.6"

12
boa_engine/src/builtins/array/mod.rs

@ -366,12 +366,10 @@ impl Array {
// 7. If IsConstructor(C) is false, throw a TypeError exception. // 7. If IsConstructor(C) is false, throw a TypeError exception.
if let Some(c) = c.as_constructor() { if let Some(c) = c.as_constructor() {
// 8. Return ? Construct(C, « 𝔽(length) »). // 8. Return ? Construct(C, « 𝔽(length) »).
Ok( Ok(c.construct(&[JsValue::new(length)], Some(c), context)?
c.construct(&[JsValue::new(length)], &c.clone().into(), context)?
.as_object() .as_object()
.expect("constructing an object should always return an object") .expect("constructing an object should always return an object")
.clone(), .clone())
)
} else { } else {
context.throw_type_error("Symbol.species must be a constructor") context.throw_type_error("Symbol.species must be a constructor")
} }
@ -421,7 +419,7 @@ impl Array {
// i. Let A be ? ArrayCreate(0en). // i. Let A be ? ArrayCreate(0en).
let a = match this.as_constructor() { let a = match this.as_constructor() {
Some(constructor) => constructor Some(constructor) => constructor
.construct(&[], this, context)? .construct(&[], None, context)?
.as_object() .as_object()
.cloned() .cloned()
.ok_or_else(|| { .ok_or_else(|| {
@ -500,7 +498,7 @@ impl Array {
// a. Let A be ? ArrayCreate(len). // a. Let A be ? ArrayCreate(len).
let a = match this.as_constructor() { let a = match this.as_constructor() {
Some(constructor) => constructor Some(constructor) => constructor
.construct(&[len.into()], this, context)? .construct(&[len.into()], None, context)?
.as_object() .as_object()
.cloned() .cloned()
.ok_or_else(|| { .ok_or_else(|| {
@ -582,7 +580,7 @@ impl Array {
// a. Let A be ? ArrayCreate(len). // a. Let A be ? ArrayCreate(len).
let a = match this.as_constructor() { let a = match this.as_constructor() {
Some(constructor) => constructor Some(constructor) => constructor
.construct(&[len.into()], this, context)? .construct(&[len.into()], None, context)?
.as_object() .as_object()
.cloned() .cloned()
.ok_or_else(|| { .ok_or_else(|| {

2
boa_engine/src/builtins/array_buffer/mod.rs

@ -241,7 +241,7 @@ impl ArrayBuffer {
let ctor = obj.species_constructor(StandardConstructors::array_buffer, context)?; let ctor = obj.species_constructor(StandardConstructors::array_buffer, context)?;
// 16. Let new be ? Construct(ctor, « 𝔽(newLen) »). // 16. Let new be ? Construct(ctor, « 𝔽(newLen) »).
let new = ctor.construct(&[new_len.into()], &ctor.clone().into(), context)?; let new = ctor.construct(&[new_len.into()], Some(&ctor), context)?;
// 17. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]). // 17. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).
let new_obj = new.as_object().cloned().ok_or_else(|| { let new_obj = new.as_object().cloned().ok_or_else(|| {

139
boa_engine/src/builtins/iterable/mod.rs

@ -162,16 +162,20 @@ impl JsValue {
let iterator = context.call(&method, self, &[])?; let iterator = context.call(&method, self, &[])?;
// 4. If Type(iterator) is not Object, throw a TypeError exception. // 4. If Type(iterator) is not Object, throw a TypeError exception.
if !iterator.is_object() { let iterator_obj = iterator
return context.throw_type_error("the iterator is not an object"); .as_object()
} .ok_or_else(|| context.construct_type_error("the iterator is not an object"))?;
// 5. Let nextMethod be ? GetV(iterator, "next"). // 5. Let nextMethod be ? GetV(iterator, "next").
let next_method = iterator.get_v("next", context)?; let next_method = iterator.get_v("next", context)?;
// 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }. // 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
// 7. Return iteratorRecord. // 7. Return iteratorRecord.
Ok(IteratorRecord::new(iterator, next_method)) Ok(IteratorRecord::new(
iterator_obj.clone(),
next_method,
false,
))
} }
} }
@ -196,6 +200,7 @@ fn create_iterator_prototype(context: &mut Context) -> JsObject {
iterator_prototype iterator_prototype
} }
/// The result of the iteration process.
#[derive(Debug)] #[derive(Debug)]
pub struct IteratorResult { pub struct IteratorResult {
object: JsObject, object: JsObject,
@ -247,32 +252,53 @@ impl IteratorResult {
pub struct IteratorRecord { pub struct IteratorRecord {
/// `[[Iterator]]` /// `[[Iterator]]`
/// ///
/// An object that conforms to the Iterator or AsyncIterator interface. /// An object that conforms to the `Iterator` or `AsyncIterator` interface.
iterator_object: JsValue, iterator: JsObject,
/// `[[NextMethod]]` /// `[[NextMethod]]`
/// ///
/// The next method of the `[[Iterator]]` object. /// The `next` method of the `[[Iterator]]` object.
next_function: JsValue, next_method: JsValue,
/// `[[Done]]`
///
/// Whether the iterator has been closed.
done: bool,
} }
impl IteratorRecord { impl IteratorRecord {
/// Creates a new `IteratorRecord` with the given iterator object, next method and `done` flag.
#[inline] #[inline]
pub fn new(iterator_object: JsValue, next_function: JsValue) -> Self { pub fn new(iterator: JsObject, next_method: JsValue, done: bool) -> Self {
Self { Self {
iterator_object, iterator,
next_function, next_method,
done,
}
} }
/// Get the `[[Iterator]]` field of the `IteratorRecord`.
#[inline]
pub(crate) fn iterator(&self) -> &JsObject {
&self.iterator
}
/// Get the `[[NextMethod]]` field of the `IteratorRecord`.
#[inline]
pub(crate) fn next_method(&self) -> &JsValue {
&self.next_method
} }
/// Get the `[[Done]]` field of the `IteratorRecord`.
#[inline] #[inline]
pub(crate) fn iterator_object(&self) -> &JsValue { pub(crate) fn done(&self) -> bool {
&self.iterator_object self.done
} }
/// Sets the `[[Done]]` field of the `IteratorRecord`.
#[inline] #[inline]
pub(crate) fn next_function(&self) -> &JsValue { pub(crate) fn set_done(&mut self, done: bool) {
&self.next_function self.done = done;
} }
/// `IteratorNext ( iteratorRecord [ , value ] )` /// `IteratorNext ( iteratorRecord [ , value ] )`
@ -293,14 +319,22 @@ impl IteratorRecord {
) -> JsResult<IteratorResult> { ) -> JsResult<IteratorResult> {
let _timer = Profiler::global().start_event("IteratorRecord::next", "iterator"); let _timer = Profiler::global().start_event("IteratorRecord::next", "iterator");
// 1. If value is not present, then // Note: We check if iteratorRecord.[[NextMethod]] is callable here.
// a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). // This check would happen in `Call` according to the spec, but we do not implement call for `JsValue`.
let next_method = if let Some(next_method) = self.next_method.as_callable() {
next_method
} else {
return context.throw_type_error("iterable next method not a function");
};
let result = if let Some(value) = value {
// 2. Else, // 2. Else,
// a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « value »). // a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « value »).
let result = if let Some(value) = value { next_method.call(&self.iterator.clone().into(), &[value], context)?
context.call(&self.next_function, &self.iterator_object, &[value])?
} else { } else {
context.call(&self.next_function, &self.iterator_object, &[])? // 1. If value is not present, then
// a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]).
next_method.call(&self.iterator.clone().into(), &[], context)?
}; };
// 3. If Type(result) is not Object, throw a TypeError exception. // 3. If Type(result) is not Object, throw a TypeError exception.
@ -345,9 +379,9 @@ impl IteratorRecord {
/// `IteratorClose ( iteratorRecord, completion )` /// `IteratorClose ( iteratorRecord, completion )`
/// ///
/// The abstract operation `IteratorClose` takes arguments `iteratorRecord` (an /// The abstract operation `IteratorClose` takes arguments `iteratorRecord` (an
/// [Iterator Record][Self]) and `completion` (a Completion Record) and returns a Completion /// [Iterator Record][Self]) and `completion` (a `Completion` Record) and returns a
/// Record. It is used to notify an iterator that it should perform any actions it would /// `Completion` Record. It is used to notify an iterator that it should perform any actions it
/// normally perform when it has reached its completed state. /// would normally perform when it has reached its completed state.
/// ///
/// More information: /// More information:
/// - [ECMA reference][spec] /// - [ECMA reference][spec]
@ -362,42 +396,49 @@ impl IteratorRecord {
let _timer = Profiler::global().start_event("IteratorRecord::close", "iterator"); let _timer = Profiler::global().start_event("IteratorRecord::close", "iterator");
// 1. Assert: Type(iteratorRecord.[[Iterator]]) is Object. // 1. Assert: Type(iteratorRecord.[[Iterator]]) is Object.
// 2. Let iterator be iteratorRecord.[[Iterator]]. // 2. Let iterator be iteratorRecord.[[Iterator]].
// 3. Let innerResult be GetMethod(iterator, "return"). let iterator = &self.iterator;
let inner_result = self.iterator_object.get_method("return", context);
// 3. Let innerResult be Completion(GetMethod(iterator, "return")).
let inner_result = iterator.get_method("return", context);
// 4. If innerResult.[[Type]] is normal, then // 4. If innerResult.[[Type]] is normal, then
if let Ok(inner_value) = inner_result { let inner_result = match inner_result {
Ok(inner_result) => {
// a. Let return be innerResult.[[Value]]. // a. Let return be innerResult.[[Value]].
match inner_value { let r#return = inner_result;
// b. If return is undefined, return Completion(completion).
None => return completion,
// c. Set innerResult to Call(return, iterator).
Some(value) => {
let inner_result = value.call(&self.iterator_object, &[], context);
// 5. If completion.[[Type]] is throw, return Completion(completion).
let completion = completion?;
// 6. If innerResult.[[Type]] is throw, return Completion(innerResult).
inner_result?;
// 7. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception. if let Some(r#return) = r#return {
// 8. Return Completion(completion). // c. Set innerResult to Completion(Call(return, iterator)).
return Ok(completion); r#return.call(&iterator.clone().into(), &[], context)
} else {
// b. If return is undefined, return ? completion.
return completion;
} }
} }
Err(inner_result) => {
// 5. If completion.[[Type]] is throw, return ? completion.
completion?;
// 6. If innerResult.[[Type]] is throw, return ? innerResult.
return Err(inner_result);
} }
};
// 5. If completion.[[Type]] is throw, return Completion(completion). // 5. If completion.[[Type]] is throw, return ? completion.
let completion = completion?; let completion = completion?;
// 6. If innerResult.[[Type]] is throw, return Completion(innerResult). // 6. If innerResult.[[Type]] is throw, return ? innerResult.
inner_result?; let inner_result = inner_result?;
// 7. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception. if inner_result.is_object() {
// 8. Return Completion(completion). // 8. Return ? completion.
Ok(completion) Ok(completion)
} else {
// 7. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception.
context.throw_type_error("inner result was not an object")
}
} }
} }
@ -424,6 +465,7 @@ pub(crate) fn iterable_to_list(
// a. Let iteratorRecord be ? GetIterator(items, sync). // a. Let iteratorRecord be ? GetIterator(items, sync).
items.get_iterator(context, Some(IteratorHint::Sync), None)? items.get_iterator(context, Some(IteratorHint::Sync), None)?
}; };
// 3. Let values be a new empty List. // 3. Let values be a new empty List.
let mut values = Vec::new(); let mut values = Vec::new();
@ -442,7 +484,10 @@ pub(crate) fn iterable_to_list(
Ok(values) Ok(values)
} }
/// A shorthand for a sequence of algorithm steps that use an Iterator Record /// `IfAbruptCloseIterator ( value, iteratorRecord )`
///
/// `IfAbruptCloseIterator` is a shorthand for a sequence of algorithm steps that use an `Iterator`
/// Record.
/// ///
/// More information: /// More information:
/// - [ECMA reference][spec] /// - [ECMA reference][spec]

19
boa_engine/src/builtins/promise/mod.rs

@ -164,7 +164,7 @@ impl PromiseCapability {
.into(); .into();
// 6. Let promise be ? Construct(C, « executor »). // 6. Let promise be ? Construct(C, « executor »).
let promise = c.construct(&[executor], &c.clone().into(), context)?; let promise = c.construct(&[executor], Some(&c), context)?;
let promise_capability: &mut Self = let promise_capability: &mut Self =
&mut promise_capability.try_borrow_mut().expect("msg"); &mut promise_capability.try_borrow_mut().expect("msg");
@ -661,10 +661,11 @@ impl Promise {
// 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
if_abrupt_reject_promise!(iterator_record, promise_capability, context); if_abrupt_reject_promise!(iterator_record, promise_capability, context);
let mut iterator_record = iterator_record;
// 7. Let result be Completion(PerformPromiseRace(iteratorRecord, C, promiseCapability, promiseResolve)). // 7. Let result be Completion(PerformPromiseRace(iteratorRecord, C, promiseCapability, promiseResolve)).
let result = Self::perform_promise_race( let mut result = Self::perform_promise_race(
&iterator_record, &mut iterator_record,
c, c,
&promise_capability, &promise_capability,
&promise_resolve, &promise_resolve,
@ -674,7 +675,9 @@ impl Promise {
// 8. If result is an abrupt completion, then // 8. If result is an abrupt completion, then
if result.is_err() { if result.is_err() {
// a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)).
// TODO: set the [[Done]] field in the IteratorRecord (currently doesn't exist) if !iterator_record.done() {
result = iterator_record.close(result, context);
}
// b. IfAbruptRejectPromise(result, promiseCapability). // b. IfAbruptRejectPromise(result, promiseCapability).
if_abrupt_reject_promise!(result, promise_capability, context); if_abrupt_reject_promise!(result, promise_capability, context);
@ -698,7 +701,7 @@ impl Promise {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-performpromiserace /// [spec]: https://tc39.es/ecma262/#sec-performpromiserace
fn perform_promise_race( fn perform_promise_race(
iterator_record: &IteratorRecord, iterator_record: &mut IteratorRecord,
constructor: &JsValue, constructor: &JsValue,
result_capability: &PromiseCapability, result_capability: &PromiseCapability,
promise_resolve: &JsValue, promise_resolve: &JsValue,
@ -711,7 +714,7 @@ impl Promise {
// b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true. // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
if next.is_err() { if next.is_err() {
// TODO: set the [[Done]] field in the IteratorRecord (currently doesn't exist) iterator_record.set_done(true);
} }
// c. ReturnIfAbrupt(next). // c. ReturnIfAbrupt(next).
@ -723,7 +726,7 @@ impl Promise {
// f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true. // f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
if next_value.is_err() { if next_value.is_err() {
// TODO: set the [[Done]] field in the IteratorRecord (currently doesn't exist) iterator_record.set_done(true);
} }
// g. ReturnIfAbrupt(nextValue). // g. ReturnIfAbrupt(nextValue).
@ -744,7 +747,7 @@ impl Promise {
} else { } else {
// d. If next is false, then // d. If next is false, then
// i. Set iteratorRecord.[[Done]] to true. // i. Set iteratorRecord.[[Done]] to true.
// TODO: set the [[Done]] field in the IteratorRecord (currently doesn't exist) iterator_record.set_done(true);
// ii. Return resultCapability.[[Promise]]. // ii. Return resultCapability.[[Promise]].
return Ok(result_capability.promise.clone()); return Ok(result_capability.promise.clone());

34
boa_engine/src/builtins/reflect/mod.rs

@ -13,7 +13,7 @@
use super::{Array, JsArgs}; use super::{Array, JsArgs};
use crate::{ use crate::{
builtins::{self, BuiltIn}, builtins::{self, BuiltIn},
object::{JsObject, ObjectInitializer}, object::ObjectInitializer,
property::Attribute, property::Attribute,
symbol::WellKnownSymbols, symbol::WellKnownSymbols,
Context, JsResult, JsValue, Context, JsResult, JsValue,
@ -102,27 +102,31 @@ impl Reflect {
args: &[JsValue], args: &[JsValue],
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. If IsConstructor(target) is false, throw a TypeError exception.
let target = args let target = args
.get(0) .get_or_undefined(0)
.and_then(JsValue::as_object) .as_constructor()
.ok_or_else(|| context.construct_type_error("target must be a function"))?; .ok_or_else(|| context.construct_type_error("target must be a constructor"))?;
let args_list = args.get_or_undefined(1);
if !target.is_constructor() {
return context.throw_type_error("target must be a constructor");
}
let new_target = if let Some(new_target) = args.get(2) { let new_target = if let Some(new_target) = args.get(2) {
if new_target.as_object().map(JsObject::is_constructor) != Some(true) { // 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception.
return context.throw_type_error("newTarget must be constructor"); if let Some(new_target) = new_target.as_constructor() {
new_target
} else {
return context.throw_type_error("newTarget must be a constructor");
} }
new_target.clone()
} else { } else {
target.clone().into() // 2. If newTarget is not present, set newTarget to target.
target
}; };
let args = args_list.create_list_from_array_like(&[], context)?; // 4. Let args be ? CreateListFromArrayLike(argumentsList).
target.construct(&args, &new_target, context) let args = args
.get_or_undefined(1)
.create_list_from_array_like(&[], context)?;
// 5. Return ? Construct(target, args, newTarget).
target.construct(&args, Some(new_target), context)
} }
/// Defines a property on an object. /// Defines a property on an object.

8
boa_engine/src/builtins/regexp/mod.rs

@ -1183,11 +1183,7 @@ impl RegExp {
let flags = regexp.get("flags", context)?.to_string(context)?; let flags = regexp.get("flags", context)?.to_string(context)?;
// 6. Let matcher be ? Construct(C, « R, flags »). // 6. Let matcher be ? Construct(C, « R, flags »).
let matcher = c.construct( let matcher = c.construct(&[this.clone(), flags.clone().into()], Some(&c), context)?;
&[this.clone(), flags.clone().into()],
&c.clone().into(),
context,
)?;
let matcher = matcher let matcher = matcher
.as_object() .as_object()
.expect("construct must always return an Object"); .expect("construct must always return an Object");
@ -1580,7 +1576,7 @@ impl RegExp {
// 10. Let splitter be ? Construct(C, « rx, newFlags »). // 10. Let splitter be ? Construct(C, « rx, newFlags »).
let splitter = constructor.construct( let splitter = constructor.construct(
&[this.clone(), new_flags.into()], &[this.clone(), new_flags.into()],
&constructor.clone().into(), Some(&constructor),
context, context,
)?; )?;
let splitter = splitter let splitter = splitter

2
boa_engine/src/builtins/typed_array/mod.rs

@ -2968,7 +2968,7 @@ impl TypedArray {
context: &mut Context, context: &mut Context,
) -> JsResult<JsObject> { ) -> JsResult<JsObject> {
// 1. Let newTypedArray be ? Construct(constructor, argumentList). // 1. Let newTypedArray be ? Construct(constructor, argumentList).
let new_typed_array = constructor.construct(args, &constructor.clone().into(), context)?; let new_typed_array = constructor.construct(args, Some(constructor), context)?;
// 2. Perform ? ValidateTypedArray(newTypedArray). // 2. Perform ? ValidateTypedArray(newTypedArray).
let obj = new_typed_array let obj = new_typed_array

45
boa_engine/src/bytecompiler.rs

@ -1433,7 +1433,6 @@ impl<'b> ByteCompiler<'b> {
self.patch_jump(exit); self.patch_jump(exit);
self.pop_loop_control_info(); self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd); self.emit_opcode(Opcode::LoopEnd);
self.emit_opcode(Opcode::PushFalse);
self.emit_opcode(Opcode::IteratorClose); self.emit_opcode(Opcode::IteratorClose);
self.patch_jump(early_exit); self.patch_jump(early_exit);
@ -1517,7 +1516,6 @@ impl<'b> ByteCompiler<'b> {
self.patch_jump(exit); self.patch_jump(exit);
self.pop_loop_control_info(); self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd); self.emit_opcode(Opcode::LoopEnd);
self.emit_opcode(Opcode::PushFalse);
self.emit_opcode(Opcode::IteratorClose); self.emit_opcode(Opcode::IteratorClose);
} }
Node::WhileLoop(while_) => { Node::WhileLoop(while_) => {
@ -1569,8 +1567,12 @@ impl<'b> ByteCompiler<'b> {
} else { } else {
false false
}; };
let in_catch_no_finally = !info.has_finally && info.in_catch;
if in_finally || (!info.has_finally && info.in_catch) { if in_finally {
self.emit_opcode(Opcode::PopIfThrown);
}
if in_finally || in_catch_no_finally {
self.emit_opcode(Opcode::CatchEnd2); self.emit_opcode(Opcode::CatchEnd2);
self.emit(Opcode::FinallySetJump, &[start_address]); self.emit(Opcode::FinallySetJump, &[start_address]);
} else { } else {
@ -1614,6 +1616,7 @@ impl<'b> ByteCompiler<'b> {
for _ in 0..emit_for_of_in_exit { for _ in 0..emit_for_of_in_exit {
self.emit_opcode(Opcode::Pop); self.emit_opcode(Opcode::Pop);
self.emit_opcode(Opcode::Pop); self.emit_opcode(Opcode::Pop);
self.emit_opcode(Opcode::Pop);
} }
for _ in 0..num_loops { for _ in 0..num_loops {
self.emit_opcode(Opcode::LoopEnd); self.emit_opcode(Opcode::LoopEnd);
@ -1645,8 +1648,12 @@ impl<'b> ByteCompiler<'b> {
} else { } else {
false false
}; };
let in_catch_no_finally = !info.has_finally && info.in_catch;
if in_finally || (!info.has_finally && info.in_catch) { if in_finally {
self.emit_opcode(Opcode::PopIfThrown);
}
if in_finally || in_catch_no_finally {
self.emit_opcode(Opcode::CatchEnd2); self.emit_opcode(Opcode::CatchEnd2);
} else { } else {
self.emit_opcode(Opcode::TryEnd); self.emit_opcode(Opcode::TryEnd);
@ -2276,26 +2283,18 @@ impl<'b> ByteCompiler<'b> {
self.emit_opcode(Opcode::ValueNotNullOrUndefined); self.emit_opcode(Opcode::ValueNotNullOrUndefined);
self.emit_opcode(Opcode::InitIterator); self.emit_opcode(Opcode::InitIterator);
for (i, binding) in pattern.bindings().iter().enumerate() { for binding in pattern.bindings().iter() {
use BindingPatternTypeArray::{ use BindingPatternTypeArray::{
BindingPattern, BindingPatternRest, Elision, Empty, GetConstField, BindingPattern, BindingPatternRest, Elision, Empty, GetConstField,
GetConstFieldRest, GetField, GetFieldRest, SingleName, SingleNameRest, GetConstFieldRest, GetField, GetFieldRest, SingleName, SingleNameRest,
}; };
let next = if i == pattern.bindings().len() - 1 {
Opcode::IteratorNextFull
} else {
Opcode::IteratorNext
};
match binding { match binding {
// ArrayBindingPattern : [ ] // ArrayBindingPattern : [ ]
Empty => { Empty => {}
self.emit_opcode(Opcode::PushFalse);
}
// ArrayBindingPattern : [ Elision ] // ArrayBindingPattern : [ Elision ]
Elision => { Elision => {
self.emit_opcode(next); self.emit_opcode(Opcode::IteratorNext);
self.emit_opcode(Opcode::Pop); self.emit_opcode(Opcode::Pop);
} }
// SingleNameBinding : BindingIdentifier Initializer[opt] // SingleNameBinding : BindingIdentifier Initializer[opt]
@ -2303,7 +2302,7 @@ impl<'b> ByteCompiler<'b> {
ident, ident,
default_init, default_init,
} => { } => {
self.emit_opcode(next); self.emit_opcode(Opcode::IteratorNext);
if let Some(init) = default_init { if let Some(init) = default_init {
let skip = let skip =
self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined);
@ -2313,11 +2312,11 @@ impl<'b> ByteCompiler<'b> {
self.emit_binding(def, *ident); self.emit_binding(def, *ident);
} }
GetField { get_field } => { GetField { get_field } => {
self.emit_opcode(next); self.emit_opcode(Opcode::IteratorNext);
self.access_set(Access::ByValue { node: get_field }, None, false)?; self.access_set(Access::ByValue { node: get_field }, None, false)?;
} }
GetConstField { get_const_field } => { GetConstField { get_const_field } => {
self.emit_opcode(next); self.emit_opcode(Opcode::IteratorNext);
self.access_set( self.access_set(
Access::ByName { Access::ByName {
node: get_const_field, node: get_const_field,
@ -2328,19 +2327,17 @@ impl<'b> ByteCompiler<'b> {
} }
// BindingElement : BindingPattern Initializer[opt] // BindingElement : BindingPattern Initializer[opt]
BindingPattern { pattern } => { BindingPattern { pattern } => {
self.emit_opcode(next); self.emit_opcode(Opcode::IteratorNext);
self.compile_declaration_pattern(pattern, def)?; self.compile_declaration_pattern(pattern, def)?;
} }
// BindingRestElement : ... BindingIdentifier // BindingRestElement : ... BindingIdentifier
SingleNameRest { ident } => { SingleNameRest { ident } => {
self.emit_opcode(Opcode::IteratorToArray); self.emit_opcode(Opcode::IteratorToArray);
self.emit_binding(def, *ident); self.emit_binding(def, *ident);
self.emit_opcode(Opcode::PushTrue);
} }
GetFieldRest { get_field } => { GetFieldRest { get_field } => {
self.emit_opcode(Opcode::IteratorToArray); self.emit_opcode(Opcode::IteratorToArray);
self.access_set(Access::ByValue { node: get_field }, None, false)?; self.access_set(Access::ByValue { node: get_field }, None, false)?;
self.emit_opcode(Opcode::PushTrue);
} }
GetConstFieldRest { get_const_field } => { GetConstFieldRest { get_const_field } => {
self.emit_opcode(Opcode::IteratorToArray); self.emit_opcode(Opcode::IteratorToArray);
@ -2351,19 +2348,13 @@ impl<'b> ByteCompiler<'b> {
None, None,
false, false,
)?; )?;
self.emit_opcode(Opcode::PushTrue);
} }
// BindingRestElement : ... BindingPattern // BindingRestElement : ... BindingPattern
BindingPatternRest { pattern } => { BindingPatternRest { pattern } => {
self.emit_opcode(Opcode::IteratorToArray); self.emit_opcode(Opcode::IteratorToArray);
self.compile_declaration_pattern(pattern, def)?; self.compile_declaration_pattern(pattern, def)?;
self.emit_opcode(Opcode::PushTrue);
}
} }
} }
if pattern.bindings().is_empty() {
self.emit_opcode(Opcode::PushFalse);
} }
self.emit_opcode(Opcode::IteratorClose); self.emit_opcode(Opcode::IteratorClose);

26
boa_engine/src/context/mod.rs

@ -177,17 +177,34 @@ impl Context {
parser.parse_all(self) parser.parse_all(self)
} }
/// <https://tc39.es/ecma262/#sec-call> /// `Call ( F, V [ , argumentsList ] )`
///
/// The abstract operation `Call` takes arguments `F` (an ECMAScript language value) and `V`
/// (an ECMAScript language value) and optional argument `argumentsList` (a `List` of
/// ECMAScript language values) and returns either a normal completion containing an ECMAScript
/// language value or a throw completion. It is used to call the `[[Call]]` internal method of
/// a function object. `F` is the function object, `V` is an ECMAScript language value that is
/// the `this` value of the `[[Call]]`, and `argumentsList` is the value passed to the
/// corresponding argument of the internal method. If `argumentsList` is not present, a new
/// empty `List` is used as its value.
///
/// More information:
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-call
#[inline] #[inline]
pub(crate) fn call( pub(crate) fn call(
&mut self, &mut self,
f: &JsValue, f: &JsValue,
this: &JsValue, v: &JsValue,
args: &[JsValue], arguments_list: &[JsValue],
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. If argumentsList is not present, set argumentsList to a new empty List.
// 2. If IsCallable(F) is false, throw a TypeError exception.
// 3. Return ? F.[[Call]](V, argumentsList).
f.as_callable() f.as_callable()
.ok_or_else(|| self.construct_type_error("Value is not callable")) .ok_or_else(|| self.construct_type_error("Value is not callable"))
.and_then(|obj| obj.call(this, args, self)) .and_then(|f| f.call(v, arguments_list, self))
} }
/// Return the global object. /// Return the global object.
@ -707,6 +724,7 @@ impl Context {
param_count: 0, param_count: 0,
arg_count: 0, arg_count: 0,
generator_resume_kind: GeneratorResumeKind::Normal, generator_resume_kind: GeneratorResumeKind::Normal,
thrown: false,
}); });
self.realm.set_global_binding_number(); self.realm.set_global_binding_number();

11
boa_engine/src/object/internal_methods/bound_function.rs

@ -68,7 +68,7 @@ fn bound_function_exotic_call(
fn bound_function_exotic_construct( fn bound_function_exotic_construct(
obj: &JsObject, obj: &JsObject,
arguments_list: &[JsValue], arguments_list: &[JsValue],
new_target: &JsValue, new_target: &JsObject,
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
let object = obj.borrow(); let object = obj.borrow();
@ -89,11 +89,12 @@ fn bound_function_exotic_construct(
args.extend_from_slice(arguments_list); args.extend_from_slice(arguments_list);
// 5. If SameValue(F, newTarget) is true, set newTarget to target. // 5. If SameValue(F, newTarget) is true, set newTarget to target.
let new_target = match new_target { let new_target = if JsObject::equals(obj, new_target) {
JsValue::Object(new_target) if JsObject::equals(obj, new_target) => target.clone().into(), target
_ => new_target.clone(), } else {
new_target
}; };
// 6. Return ? Construct(target, args, newTarget). // 6. Return ? Construct(target, args, newTarget).
target.construct(&args, &new_target, context) target.construct(&args, Some(new_target), context)
} }

4
boa_engine/src/object/internal_methods/function.rs

@ -53,8 +53,8 @@ fn function_call(
fn function_construct( fn function_construct(
obj: &JsObject, obj: &JsObject,
args: &[JsValue], args: &[JsValue],
new_target: &JsValue, new_target: &JsObject,
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
obj.construct_internal(args, new_target, context) obj.construct_internal(args, &new_target.clone().into(), context)
} }

4
boa_engine/src/object/internal_methods/mod.rs

@ -259,7 +259,7 @@ impl JsObject {
pub(crate) fn __construct__( pub(crate) fn __construct__(
&self, &self,
args: &[JsValue], args: &[JsValue],
new_target: &JsValue, new_target: &JsObject,
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
let _timer = Profiler::global().start_event("Object::__construct__", "object"); let _timer = Profiler::global().start_event("Object::__construct__", "object");
@ -324,7 +324,7 @@ pub(crate) struct InternalObjectMethods {
pub(crate) __call__: pub(crate) __call__:
Option<fn(&JsObject, &JsValue, &[JsValue], &mut Context) -> JsResult<JsValue>>, Option<fn(&JsObject, &JsValue, &[JsValue], &mut Context) -> JsResult<JsValue>>,
pub(crate) __construct__: pub(crate) __construct__:
Option<fn(&JsObject, &[JsValue], &JsValue, &mut Context) -> JsResult<JsValue>>, Option<fn(&JsObject, &[JsValue], &JsObject, &mut Context) -> JsResult<JsValue>>,
} }
/// Abstract operation `OrdinaryGetPrototypeOf`. /// Abstract operation `OrdinaryGetPrototypeOf`.

12
boa_engine/src/object/internal_methods/proxy.rs

@ -935,7 +935,7 @@ fn proxy_exotic_call(
) )
} }
/// `10.5.13 [[Construct]] ( argumentsList, newTarget )` /// `[[Construct]] ( argumentsList, newTarget )`
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
@ -944,7 +944,7 @@ fn proxy_exotic_call(
fn proxy_exotic_construct( fn proxy_exotic_construct(
obj: &JsObject, obj: &JsObject,
args: &[JsValue], args: &[JsValue],
new_target: &JsValue, new_target: &JsObject,
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. Let handler be O.[[ProxyHandler]]. // 1. Let handler be O.[[ProxyHandler]].
@ -966,7 +966,7 @@ fn proxy_exotic_construct(
// 7. If trap is undefined, then // 7. If trap is undefined, then
} else { } else {
// a. Return ? Construct(target, argumentsList, newTarget). // a. Return ? Construct(target, argumentsList, newTarget).
return target.construct(args, new_target, context); return target.construct(args, Some(new_target), context);
}; };
// 8. Let argArray be ! CreateArrayFromList(argumentsList). // 8. Let argArray be ! CreateArrayFromList(argumentsList).
@ -975,7 +975,11 @@ fn proxy_exotic_construct(
// 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »). // 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).
let new_obj = trap.call( let new_obj = trap.call(
&handler.into(), &handler.into(),
&[target.clone().into(), arg_array.into(), new_target.clone()], &[
target.clone().into(),
arg_array.into(),
new_target.clone().into(),
],
context, context,
)?; )?;

13
boa_engine/src/object/operations.rs

@ -309,21 +309,28 @@ impl JsObject {
self.__call__(this, args, context) self.__call__(this, args, context)
} }
/// `Construct ( F [ , argumentsList [ , newTarget ] ] )`
///
/// Construct an instance of this object with the specified arguments. /// Construct an instance of this object with the specified arguments.
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if the object is currently mutably borrowed. /// Panics if the object is currently mutably borrowed.
// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget> ///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-construct
#[track_caller] #[track_caller]
#[inline] #[inline]
pub fn construct( pub fn construct(
&self, &self,
args: &[JsValue], args: &[JsValue],
new_target: &JsValue, new_target: Option<&JsObject>,
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. If newTarget is not present, set newTarget to F. // 1. If newTarget is not present, set newTarget to F.
let new_target = new_target.unwrap_or(self);
// 2. If argumentsList is not present, set argumentsList to a new empty List. // 2. If argumentsList is not present, set argumentsList to a new empty List.
// 3. Return ? F.[[Construct]](argumentsList, newTarget). // 3. Return ? F.[[Construct]](argumentsList, newTarget).
self.__construct__(args, new_target, context) self.__construct__(args, new_target, context)
@ -660,7 +667,7 @@ impl JsValue {
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-getmethod /// [spec]: https://tc39.es/ecma262/#sec-getv
#[inline] #[inline]
pub(crate) fn get_v<K>(&self, key: K, context: &mut Context) -> JsResult<Self> pub(crate) fn get_v<K>(&self, key: K, context: &mut Context) -> JsResult<Self>
where where

3
boa_engine/src/vm/call_frame.rs

@ -32,6 +32,9 @@ pub struct CallFrame {
pub(crate) arg_count: usize, pub(crate) arg_count: usize,
#[unsafe_ignore_trace] #[unsafe_ignore_trace]
pub(crate) generator_resume_kind: GeneratorResumeKind, pub(crate) generator_resume_kind: GeneratorResumeKind,
// Indicate that the last try block has thrown an exception.
pub(crate) thrown: bool,
} }
impl CallFrame { impl CallFrame {

5
boa_engine/src/vm/code_block.rs

@ -266,6 +266,7 @@ impl CodeBlock {
) )
} }
Opcode::Pop Opcode::Pop
| Opcode::PopIfThrown
| Opcode::Dup | Opcode::Dup
| Opcode::Swap | Opcode::Swap
| Opcode::PushZero | Opcode::PushZero
@ -336,7 +337,6 @@ impl CodeBlock {
| Opcode::LoopEnd | Opcode::LoopEnd
| Opcode::InitIterator | Opcode::InitIterator
| Opcode::IteratorNext | Opcode::IteratorNext
| Opcode::IteratorNextFull
| Opcode::IteratorClose | Opcode::IteratorClose
| Opcode::IteratorToArray | Opcode::IteratorToArray
| Opcode::RequireObjectCoercible | Opcode::RequireObjectCoercible
@ -717,6 +717,7 @@ impl JsObject {
param_count, param_count,
arg_count, arg_count,
generator_resume_kind: GeneratorResumeKind::Normal, generator_resume_kind: GeneratorResumeKind::Normal,
thrown: false,
}); });
let result = context.run(); let result = context.run();
@ -820,6 +821,7 @@ impl JsObject {
param_count, param_count,
arg_count, arg_count,
generator_resume_kind: GeneratorResumeKind::Normal, generator_resume_kind: GeneratorResumeKind::Normal,
thrown: false,
}; };
let mut stack = args; let mut stack = args;
@ -1016,6 +1018,7 @@ impl JsObject {
param_count, param_count,
arg_count, arg_count,
generator_resume_kind: GeneratorResumeKind::Normal, generator_resume_kind: GeneratorResumeKind::Normal,
thrown: false,
}); });
let result = context.run(); let result = context.run();

147
boa_engine/src/vm/mod.rs

@ -146,6 +146,13 @@ impl Context {
Opcode::Pop => { Opcode::Pop => {
let _val = self.vm.pop(); let _val = self.vm.pop();
} }
Opcode::PopIfThrown => {
let frame = self.vm.frame_mut();
if frame.thrown {
frame.thrown = false;
self.vm.pop();
}
}
Opcode::Dup => { Opcode::Dup => {
let value = self.vm.pop(); let value = self.vm.pop();
self.vm.push(value.clone()); self.vm.push(value.clone());
@ -233,11 +240,17 @@ impl Context {
self.vm.push(array); self.vm.push(array);
} }
Opcode::PushIteratorToArray => { Opcode::PushIteratorToArray => {
let next_function = self.vm.pop(); let done = self
.vm
.pop()
.as_boolean()
.expect("iterator [[Done]] was not a boolean");
let next_method = self.vm.pop();
let iterator = self.vm.pop(); let iterator = self.vm.pop();
let iterator = iterator.as_object().expect("iterator was not an object");
let array = self.vm.pop(); let array = self.vm.pop();
let iterator = IteratorRecord::new(iterator, next_function); let iterator = IteratorRecord::new(iterator.clone(), next_method, done);
while let Some(next) = iterator.step(self)? { while let Some(next) = iterator.step(self)? {
Array::push(&array, &[next.value(self)?], self)?; Array::push(&array, &[next.value(self)?], self)?;
} }
@ -1144,6 +1157,7 @@ impl Context {
num_env: 0, num_env: 0,
num_loop_stack_entries: 0, num_loop_stack_entries: 0,
}); });
self.vm.frame_mut().thrown = false;
} }
Opcode::CatchEnd2 => { Opcode::CatchEnd2 => {
let frame = self.vm.frame_mut(); let frame = self.vm.frame_mut();
@ -1386,7 +1400,7 @@ impl Context {
let result = func let result = func
.as_constructor() .as_constructor()
.ok_or_else(|| self.construct_type_error("not a constructor")) .ok_or_else(|| self.construct_type_error("not a constructor"))
.and_then(|cons| cons.__construct__(&arguments, &cons.clone().into(), self))?; .and_then(|cons| cons.__construct__(&arguments, cons, self))?;
self.vm.push(result); self.vm.push(result);
} }
@ -1413,7 +1427,7 @@ impl Context {
let result = func let result = func
.as_constructor() .as_constructor()
.ok_or_else(|| self.construct_type_error("not a constructor")) .ok_or_else(|| self.construct_type_error("not a constructor"))
.and_then(|cons| cons.__construct__(&arguments, &cons.clone().into(), self))?; .and_then(|cons| cons.__construct__(&arguments, cons, self))?;
self.vm.push(result); self.vm.push(result);
} }
@ -1527,7 +1541,7 @@ impl Context {
let object = object.to_object(self)?; let object = object.to_object(self)?;
let iterator = ForInIterator::create_for_in_iterator(JsValue::new(object), self); let iterator = ForInIterator::create_for_in_iterator(JsValue::new(object), self);
let next_function = iterator let next_method = iterator
.get_property("next") .get_property("next")
.as_ref() .as_ref()
.map(PropertyDescriptor::expect_value) .map(PropertyDescriptor::expect_value)
@ -1535,39 +1549,32 @@ impl Context {
.ok_or_else(|| self.construct_type_error("Could not find property `next`"))?; .ok_or_else(|| self.construct_type_error("Could not find property `next`"))?;
self.vm.push(iterator); self.vm.push(iterator);
self.vm.push(next_function); self.vm.push(next_method);
self.vm.push(false);
} }
Opcode::InitIterator => { Opcode::InitIterator => {
let object = self.vm.pop(); let object = self.vm.pop();
let iterator = object.get_iterator(self, None, None)?; let iterator = object.get_iterator(self, None, None)?;
self.vm.push(iterator.iterator_object()); self.vm.push(iterator.iterator().clone());
self.vm.push(iterator.next_function()); self.vm.push(iterator.next_method().clone());
self.vm.push(iterator.done());
} }
Opcode::IteratorNext => { Opcode::IteratorNext => {
let next_function = self.vm.pop(); let done = self
let iterator = self.vm.pop(); .vm
.pop()
let iterator_record = IteratorRecord::new(iterator.clone(), next_function.clone()); .as_boolean()
let next = iterator_record.step(self)?; .expect("iterator [[Done]] was not a boolean");
let next_method = self.vm.pop();
self.vm.push(iterator);
self.vm.push(next_function);
if let Some(next) = next {
let value = next.value(self)?;
self.vm.push(value);
} else {
self.vm.push(JsValue::undefined());
}
}
Opcode::IteratorNextFull => {
let next_function = self.vm.pop();
let iterator = self.vm.pop(); let iterator = self.vm.pop();
let iterator = iterator.as_object().expect("iterator was not an object");
let iterator_record = IteratorRecord::new(iterator.clone(), next_function.clone()); let iterator_record =
IteratorRecord::new(iterator.clone(), next_method.clone(), done);
let next = iterator_record.step(self)?; let next = iterator_record.step(self)?;
self.vm.push(iterator); self.vm.push(iterator.clone());
self.vm.push(next_function); self.vm.push(next_method);
if let Some(next) = next { if let Some(next) = next {
let value = next.value(self)?; let value = next.value(self)?;
self.vm.push(false); self.vm.push(false);
@ -1578,19 +1585,31 @@ impl Context {
} }
} }
Opcode::IteratorClose => { Opcode::IteratorClose => {
let done = self.vm.pop(); let done = self
let next_function = self.vm.pop(); .vm
.pop()
.as_boolean()
.expect("iterator [[Done]] was not a boolean");
let next_method = self.vm.pop();
let iterator = self.vm.pop(); let iterator = self.vm.pop();
if !done.as_boolean().expect("not a boolean") { let iterator = iterator.as_object().expect("iterator was not an object");
let iterator_record = IteratorRecord::new(iterator, next_function); if !done {
let iterator_record = IteratorRecord::new(iterator.clone(), next_method, done);
iterator_record.close(Ok(JsValue::Null), self)?; iterator_record.close(Ok(JsValue::Null), self)?;
} }
} }
Opcode::IteratorToArray => { Opcode::IteratorToArray => {
let next_function = self.vm.pop(); let done = self
.vm
.pop()
.as_boolean()
.expect("iterator [[Done]] was not a boolean");
let next_method = self.vm.pop();
let iterator = self.vm.pop(); let iterator = self.vm.pop();
let iterator = iterator.as_object().expect("iterator was not an object");
let iterator_record = IteratorRecord::new(iterator.clone(), next_function.clone()); let iterator_record =
IteratorRecord::new(iterator.clone(), next_method.clone(), done);
let mut values = Vec::new(); let mut values = Vec::new();
while let Some(result) = iterator_record.step(self)? { while let Some(result) = iterator_record.step(self)? {
@ -1599,20 +1618,29 @@ impl Context {
let array = Array::create_array_from_list(values, self); let array = Array::create_array_from_list(values, self);
self.vm.push(iterator); self.vm.push(iterator.clone());
self.vm.push(next_function); self.vm.push(next_method);
self.vm.push(true);
self.vm.push(array); self.vm.push(array);
} }
Opcode::ForInLoopNext => { Opcode::ForInLoopNext => {
let address = self.vm.read::<u32>(); let address = self.vm.read::<u32>();
let next_function = self.vm.pop(); let done = self
.vm
.pop()
.as_boolean()
.expect("iterator [[Done]] was not a boolean");
let next_method = self.vm.pop();
let iterator = self.vm.pop(); let iterator = self.vm.pop();
let iterator = iterator.as_object().expect("iterator was not an object");
let iterator_record = IteratorRecord::new(iterator.clone(), next_function.clone()); let iterator_record =
IteratorRecord::new(iterator.clone(), next_method.clone(), done);
if let Some(next) = iterator_record.step(self)? { if let Some(next) = iterator_record.step(self)? {
self.vm.push(iterator); self.vm.push(iterator.clone());
self.vm.push(next_function); self.vm.push(next_method);
self.vm.push(done);
let value = next.value(self)?; let value = next.value(self)?;
self.vm.push(value); self.vm.push(value);
} else { } else {
@ -1620,8 +1648,9 @@ impl Context {
self.vm.frame_mut().loop_env_stack_dec(); self.vm.frame_mut().loop_env_stack_dec();
self.vm.frame_mut().try_env_stack_dec(); self.vm.frame_mut().try_env_stack_dec();
self.realm.environments.pop(); self.realm.environments.pop();
self.vm.push(iterator); self.vm.push(iterator.clone());
self.vm.push(next_function); self.vm.push(next_method);
self.vm.push(done);
} }
} }
Opcode::ConcatToString => { Opcode::ConcatToString => {
@ -1717,12 +1746,19 @@ impl Context {
Opcode::GeneratorNextDelegate => { Opcode::GeneratorNextDelegate => {
let done_address = self.vm.read::<u32>(); let done_address = self.vm.read::<u32>();
let received = self.vm.pop(); let received = self.vm.pop();
let next_function = self.vm.pop(); let done = self
.vm
.pop()
.as_boolean()
.expect("iterator [[Done]] was not a boolean");
let next_method = self.vm.pop();
let iterator = self.vm.pop(); let iterator = self.vm.pop();
let iterator = iterator.as_object().expect("iterator was not an object");
match self.vm.frame().generator_resume_kind { match self.vm.frame().generator_resume_kind {
GeneratorResumeKind::Normal => { GeneratorResumeKind::Normal => {
let result = self.call(&next_function, &iterator, &[received])?; let result =
self.call(&next_method, &iterator.clone().into(), &[received])?;
let result_object = result.as_object().ok_or_else(|| { let result_object = result.as_object().ok_or_else(|| {
self.construct_type_error("generator next method returned non-object") self.construct_type_error("generator next method returned non-object")
})?; })?;
@ -1734,15 +1770,16 @@ impl Context {
return Ok(ShouldExit::False); return Ok(ShouldExit::False);
} }
let value = result_object.get("value", self)?; let value = result_object.get("value", self)?;
self.vm.push(iterator); self.vm.push(iterator.clone());
self.vm.push(next_function); self.vm.push(next_method.clone());
self.vm.push(done);
self.vm.push(value); self.vm.push(value);
return Ok(ShouldExit::Yield); return Ok(ShouldExit::Yield);
} }
GeneratorResumeKind::Throw => { GeneratorResumeKind::Throw => {
let throw = iterator.get_method("throw", self)?; let throw = iterator.get_method("throw", self)?;
if let Some(throw) = throw { if let Some(throw) = throw {
let result = throw.call(&iterator, &[received], self)?; let result = throw.call(&iterator.clone().into(), &[received], self)?;
let result_object = result.as_object().ok_or_else(|| { let result_object = result.as_object().ok_or_else(|| {
self.construct_type_error( self.construct_type_error(
"generator throw method returned non-object", "generator throw method returned non-object",
@ -1756,14 +1793,15 @@ impl Context {
return Ok(ShouldExit::False); return Ok(ShouldExit::False);
} }
let value = result_object.get("value", self)?; let value = result_object.get("value", self)?;
self.vm.push(iterator); self.vm.push(iterator.clone());
self.vm.push(next_function); self.vm.push(next_method.clone());
self.vm.push(done);
self.vm.push(value); self.vm.push(value);
return Ok(ShouldExit::Yield); return Ok(ShouldExit::Yield);
} }
self.vm.frame_mut().pc = done_address as usize; self.vm.frame_mut().pc = done_address as usize;
let iterator_record = let iterator_record =
IteratorRecord::new(iterator.clone(), next_function.clone()); IteratorRecord::new(iterator.clone(), next_method, done);
iterator_record.close(Ok(JsValue::Undefined), self)?; iterator_record.close(Ok(JsValue::Undefined), self)?;
let error = let error =
self.construct_type_error("iterator does not have a throw method"); self.construct_type_error("iterator does not have a throw method");
@ -1772,7 +1810,8 @@ impl Context {
GeneratorResumeKind::Return => { GeneratorResumeKind::Return => {
let r#return = iterator.get_method("return", self)?; let r#return = iterator.get_method("return", self)?;
if let Some(r#return) = r#return { if let Some(r#return) = r#return {
let result = r#return.call(&iterator, &[received], self)?; let result =
r#return.call(&iterator.clone().into(), &[received], self)?;
let result_object = result.as_object().ok_or_else(|| { let result_object = result.as_object().ok_or_else(|| {
self.construct_type_error( self.construct_type_error(
"generator return method returned non-object", "generator return method returned non-object",
@ -1786,8 +1825,9 @@ impl Context {
return Ok(ShouldExit::True); return Ok(ShouldExit::True);
} }
let value = result_object.get("value", self)?; let value = result_object.get("value", self)?;
self.vm.push(iterator); self.vm.push(iterator.clone());
self.vm.push(next_function); self.vm.push(next_method.clone());
self.vm.push(done);
self.vm.push(value); self.vm.push(value);
return Ok(ShouldExit::Yield); return Ok(ShouldExit::Yield);
} }
@ -1928,6 +1968,7 @@ impl Context {
self.vm.frame_mut().pc = address as usize; self.vm.frame_mut().pc = address as usize;
self.vm.frame_mut().catch.pop(); self.vm.frame_mut().catch.pop();
self.vm.frame_mut().finally_return = FinallyReturn::Err; self.vm.frame_mut().finally_return = FinallyReturn::Err;
self.vm.frame_mut().thrown = true;
self.vm.push(e); self.vm.push(e);
} else { } else {
self.vm.stack.truncate(start_stack_size); self.vm.stack.truncate(start_stack_size);

34
boa_engine/src/vm/opcode.rs

@ -9,6 +9,13 @@ pub enum Opcode {
/// Stack: value **=>** /// Stack: value **=>**
Pop, Pop,
/// Pop the top value from the stack if the last try block has thrown a value.
///
/// Operands:
///
/// Stack: value **=>**
PopIfThrown,
/// Push a copy of the top value on the stack. /// Push a copy of the top value on the stack.
/// ///
/// Operands: /// Operands:
@ -163,7 +170,7 @@ pub enum Opcode {
/// ///
/// Operands: /// Operands:
/// ///
/// Stack: array, iterator, next_function **=>** array /// Stack: array, iterator, next_method, done **=>** array
PushIteratorToArray, PushIteratorToArray,
/// Binary `+` operator. /// Binary `+` operator.
@ -920,42 +927,35 @@ pub enum Opcode {
/// ///
/// Operands: address: `u32` /// Operands: address: `u32`
/// ///
/// Stack: object **=>** iterator, next_function /// Stack: object **=>** iterator, next_method, done
ForInLoopInitIterator, ForInLoopInitIterator,
/// Initialize an iterator. /// Initialize an iterator.
/// ///
/// Operands: /// Operands:
/// ///
/// Stack: object **=>** iterator, next_function /// Stack: object **=>** iterator, next_method, done
InitIterator, InitIterator,
/// Advance the iterator by one and put the value on the stack. /// Advance the iterator by one and put the value on the stack.
/// ///
/// Operands: /// Operands:
/// ///
/// Stack: iterator, next_function **=>** iterator, next_function, next_value /// Stack: iterator, next_method, done **=>** iterator, next_method, done, next_value
IteratorNext, IteratorNext,
/// Advance the iterator by one and put done and value on the stack.
///
/// Operands:
///
/// Stack: iterator, next_function **=>** iterator, next_function, next_done, next_value
IteratorNextFull,
/// Close an iterator. /// Close an iterator.
/// ///
/// Operands: /// Operands:
/// ///
/// Stack: iterator, next_function, done **=>** /// Stack: iterator, next_method, done **=>**
IteratorClose, IteratorClose,
/// Consume the iterator and construct and array with all the values. /// Consume the iterator and construct and array with all the values.
/// ///
/// Operands: /// Operands:
/// ///
/// Stack: iterator, next_function **=>** iterator, next_function, array /// Stack: iterator, next_method, done **=>** iterator, next_method, done, array
IteratorToArray, IteratorToArray,
/// Move to the next value in a for..in loop or jump to exit of the loop if done. /// Move to the next value in a for..in loop or jump to exit of the loop if done.
@ -964,7 +964,7 @@ pub enum Opcode {
/// ///
/// Operands: address: `u32` /// Operands: address: `u32`
/// ///
/// Stack: iterator, next_function **=>** iterator, next_function, next_result /// Stack: iterator, next_method, done **=>** iterator, next_method, done, next_result
ForInLoopNext, ForInLoopNext,
/// Concat multiple stack objects into a string. /// Concat multiple stack objects into a string.
@ -1034,7 +1034,7 @@ pub enum Opcode {
/// ///
/// Operands: done_address: `u32` /// Operands: done_address: `u32`
/// ///
/// Stack: iterator, next_function, received **=>** iterator, next_function /// Stack: iterator, next_method, done, received **=>** iterator, next_method, done
GeneratorNextDelegate, GeneratorNextDelegate,
/// No-operation instruction, does nothing. /// No-operation instruction, does nothing.
@ -1060,6 +1060,7 @@ impl Opcode {
pub fn as_str(self) -> &'static str { pub fn as_str(self) -> &'static str {
match self { match self {
Self::Pop => "Pop", Self::Pop => "Pop",
Self::PopIfThrown => "PopIfThrown",
Self::Dup => "Dup", Self::Dup => "Dup",
Self::Swap => "Swap", Self::Swap => "Swap",
Self::PushZero => "PushZero", Self::PushZero => "PushZero",
@ -1185,7 +1186,6 @@ impl Opcode {
Self::ForInLoopInitIterator => "ForInLoopInitIterator", Self::ForInLoopInitIterator => "ForInLoopInitIterator",
Self::InitIterator => "InitIterator", Self::InitIterator => "InitIterator",
Self::IteratorNext => "IteratorNext", Self::IteratorNext => "IteratorNext",
Self::IteratorNextFull => "IteratorNextFull",
Self::IteratorClose => "IteratorClose", Self::IteratorClose => "IteratorClose",
Self::IteratorToArray => "IteratorToArray", Self::IteratorToArray => "IteratorToArray",
Self::ForInLoopNext => "ForInLoopNext", Self::ForInLoopNext => "ForInLoopNext",
@ -1207,6 +1207,7 @@ impl Opcode {
pub fn as_instruction_str(self) -> &'static str { pub fn as_instruction_str(self) -> &'static str {
match self { match self {
Self::Pop => "INST - Pop", Self::Pop => "INST - Pop",
Self::PopIfThrown => "INST - PopIfThrown",
Self::Dup => "INST - Dup", Self::Dup => "INST - Dup",
Self::Swap => "INST - Swap", Self::Swap => "INST - Swap",
Self::PushZero => "INST - PushZero", Self::PushZero => "INST - PushZero",
@ -1319,7 +1320,6 @@ impl Opcode {
Self::ForInLoopInitIterator => "INST - ForInLoopInitIterator", Self::ForInLoopInitIterator => "INST - ForInLoopInitIterator",
Self::InitIterator => "INST - InitIterator", Self::InitIterator => "INST - InitIterator",
Self::IteratorNext => "INST - IteratorNext", Self::IteratorNext => "INST - IteratorNext",
Self::IteratorNextFull => "INST - IteratorNextFull",
Self::IteratorClose => "INST - IteratorClose", Self::IteratorClose => "INST - IteratorClose",
Self::IteratorToArray => "INST - IteratorToArray", Self::IteratorToArray => "INST - IteratorToArray",
Self::ForInLoopNext => "INST - ForInLoopNext", Self::ForInLoopNext => "INST - ForInLoopNext",

2
test262

@ -1 +1 @@
Subproject commit 79e3bc5176b6f29a5aed3b7164a9c623a3a9a63b Subproject commit 74de3d1d327b3238d6193242c3cc157746f2faad
Loading…
Cancel
Save