From d173e085298444b1fd7a46d2cb7a4682e25fc411 Mon Sep 17 00:00:00 2001 From: Iban Eguia Date: Sun, 19 Jun 2022 15:44:55 +0000 Subject: [PATCH] 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> --- Cargo.lock | 44 +++--- boa_cli/Cargo.toml | 2 +- boa_engine/src/builtins/array/mod.rs | 16 +- boa_engine/src/builtins/array_buffer/mod.rs | 2 +- boa_engine/src/builtins/iterable/mod.rs | 149 ++++++++++++------ boa_engine/src/builtins/promise/mod.rs | 19 ++- boa_engine/src/builtins/reflect/mod.rs | 34 ++-- boa_engine/src/builtins/regexp/mod.rs | 8 +- boa_engine/src/builtins/typed_array/mod.rs | 2 +- boa_engine/src/bytecompiler.rs | 45 +++--- boa_engine/src/context/mod.rs | 26 ++- .../object/internal_methods/bound_function.rs | 11 +- .../src/object/internal_methods/function.rs | 4 +- boa_engine/src/object/internal_methods/mod.rs | 4 +- .../src/object/internal_methods/proxy.rs | 12 +- boa_engine/src/object/operations.rs | 13 +- boa_engine/src/vm/call_frame.rs | 3 + boa_engine/src/vm/code_block.rs | 5 +- boa_engine/src/vm/mod.rs | 147 ++++++++++------- boa_engine/src/vm/opcode.rs | 34 ++-- test262 | 2 +- 21 files changed, 348 insertions(+), 234 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51ff585b12..27e92881e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,7 +75,7 @@ version = "0.15.0" dependencies = [ "boa_engine", "boa_interner", - "clap 3.1.18", + "clap 3.2.5", "colored", "jemallocator", "phf", @@ -270,16 +270,16 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.18" +version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" +checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", "indexmap", - "lazy_static", + "once_cell", "strsim 0.10.0", "termcolor", "textwrap 0.15.0", @@ -287,9 +287,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.18" +version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" +checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613" dependencies = [ "os_str_bytes", ] @@ -367,9 +367,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" dependencies = [ "cfg-if", "crossbeam-utils", @@ -388,26 +388,26 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "lazy_static", "memoffset", + "once_cell", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +checksum = "8ff1f980957787286a554052d03c7aee98d99cc32e09f6d45f0a814133c87978" dependencies = [ "cfg-if", - "lazy_static", + "once_cell", ] [[package]] @@ -859,9 +859,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" dependencies = [ "wasm-bindgen", ] @@ -1718,9 +1718,9 @@ checksum = "1218098468b8085b19a2824104c70d976491d247ce194bbd9dc77181150cdfd6" [[package]] name = "unicode-ident" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" [[package]] name = "unicode-normalization" @@ -1846,9 +1846,9 @@ checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index f3a7066d6b..4264af7d67 100644 --- a/boa_cli/Cargo.toml +++ b/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" } rustyline = "9.1.2" rustyline-derive = "0.6.0" -clap = { version = "3.1.18", features = ["derive"] } +clap = { version = "3.2.5", features = ["derive"] } serde_json = "1.0.81" colored = "2.0.0" regex = "1.5.6" diff --git a/boa_engine/src/builtins/array/mod.rs b/boa_engine/src/builtins/array/mod.rs index 7404a876cf..02b2ff784e 100644 --- a/boa_engine/src/builtins/array/mod.rs +++ b/boa_engine/src/builtins/array/mod.rs @@ -366,12 +366,10 @@ impl Array { // 7. If IsConstructor(C) is false, throw a TypeError exception. if let Some(c) = c.as_constructor() { // 8. Return ? Construct(C, « 𝔽(length) »). - Ok( - c.construct(&[JsValue::new(length)], &c.clone().into(), context)? - .as_object() - .expect("constructing an object should always return an object") - .clone(), - ) + Ok(c.construct(&[JsValue::new(length)], Some(c), context)? + .as_object() + .expect("constructing an object should always return an object") + .clone()) } else { context.throw_type_error("Symbol.species must be a constructor") } @@ -421,7 +419,7 @@ impl Array { // i. Let A be ? ArrayCreate(0en). let a = match this.as_constructor() { Some(constructor) => constructor - .construct(&[], this, context)? + .construct(&[], None, context)? .as_object() .cloned() .ok_or_else(|| { @@ -500,7 +498,7 @@ impl Array { // a. Let A be ? ArrayCreate(len). let a = match this.as_constructor() { Some(constructor) => constructor - .construct(&[len.into()], this, context)? + .construct(&[len.into()], None, context)? .as_object() .cloned() .ok_or_else(|| { @@ -582,7 +580,7 @@ impl Array { // a. Let A be ? ArrayCreate(len). let a = match this.as_constructor() { Some(constructor) => constructor - .construct(&[len.into()], this, context)? + .construct(&[len.into()], None, context)? .as_object() .cloned() .ok_or_else(|| { diff --git a/boa_engine/src/builtins/array_buffer/mod.rs b/boa_engine/src/builtins/array_buffer/mod.rs index e36ddf7a7f..647e4bdf12 100644 --- a/boa_engine/src/builtins/array_buffer/mod.rs +++ b/boa_engine/src/builtins/array_buffer/mod.rs @@ -241,7 +241,7 @@ impl ArrayBuffer { let ctor = obj.species_constructor(StandardConstructors::array_buffer, context)?; // 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]]). let new_obj = new.as_object().cloned().ok_or_else(|| { diff --git a/boa_engine/src/builtins/iterable/mod.rs b/boa_engine/src/builtins/iterable/mod.rs index ee55c6b762..ee92160023 100644 --- a/boa_engine/src/builtins/iterable/mod.rs +++ b/boa_engine/src/builtins/iterable/mod.rs @@ -162,16 +162,20 @@ impl JsValue { let iterator = context.call(&method, self, &[])?; // 4. If Type(iterator) is not Object, throw a TypeError exception. - if !iterator.is_object() { - return context.throw_type_error("the iterator is not an object"); - } + let iterator_obj = iterator + .as_object() + .ok_or_else(|| context.construct_type_error("the iterator is not an object"))?; // 5. Let nextMethod be ? GetV(iterator, "next"). let next_method = iterator.get_v("next", context)?; // 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }. // 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 } +/// The result of the iteration process. #[derive(Debug)] pub struct IteratorResult { object: JsObject, @@ -247,32 +252,53 @@ impl IteratorResult { pub struct IteratorRecord { /// `[[Iterator]]` /// - /// An object that conforms to the Iterator or AsyncIterator interface. - iterator_object: JsValue, + /// An object that conforms to the `Iterator` or `AsyncIterator` interface. + iterator: JsObject, /// `[[NextMethod]]` /// - /// The next method of the `[[Iterator]]` object. - next_function: JsValue, + /// The `next` method of the `[[Iterator]]` object. + next_method: JsValue, + + /// `[[Done]]` + /// + /// Whether the iterator has been closed. + done: bool, } impl IteratorRecord { + /// Creates a new `IteratorRecord` with the given iterator object, next method and `done` flag. #[inline] - pub fn new(iterator_object: JsValue, next_function: JsValue) -> Self { + pub fn new(iterator: JsObject, next_method: JsValue, done: bool) -> Self { Self { - iterator_object, - next_function, + iterator, + 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 iterator_object(&self) -> &JsValue { - &self.iterator_object + pub(crate) fn next_method(&self) -> &JsValue { + &self.next_method } + /// Get the `[[Done]]` field of the `IteratorRecord`. #[inline] - pub(crate) fn next_function(&self) -> &JsValue { - &self.next_function + pub(crate) fn done(&self) -> bool { + self.done + } + + /// Sets the `[[Done]]` field of the `IteratorRecord`. + #[inline] + pub(crate) fn set_done(&mut self, done: bool) { + self.done = done; } /// `IteratorNext ( iteratorRecord [ , value ] )` @@ -293,14 +319,22 @@ impl IteratorRecord { ) -> JsResult { let _timer = Profiler::global().start_event("IteratorRecord::next", "iterator"); - // 1. If value is not present, then - // a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). - // 2. Else, - // a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « value »). + // Note: We check if iteratorRecord.[[NextMethod]] is callable here. + // 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 { - context.call(&self.next_function, &self.iterator_object, &[value])? + // 2. Else, + // a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « value »). + next_method.call(&self.iterator.clone().into(), &[value], context)? } 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. @@ -345,9 +379,9 @@ impl IteratorRecord { /// `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. + /// [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] @@ -362,42 +396,49 @@ impl IteratorRecord { let _timer = Profiler::global().start_event("IteratorRecord::close", "iterator"); // 1. Assert: Type(iteratorRecord.[[Iterator]]) is Object. + // 2. Let iterator be iteratorRecord.[[Iterator]]. - // 3. Let innerResult be GetMethod(iterator, "return"). - let inner_result = self.iterator_object.get_method("return", context); + let iterator = &self.iterator; + + // 3. Let innerResult be Completion(GetMethod(iterator, "return")). + let inner_result = iterator.get_method("return", context); // 4. If innerResult.[[Type]] is normal, then - if let Ok(inner_value) = inner_result { - // a. Let return be innerResult.[[Value]]. - match inner_value { - // 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. - // 8. Return Completion(completion). - return Ok(completion); + let inner_result = match inner_result { + Ok(inner_result) => { + // a. Let return be innerResult.[[Value]]. + let r#return = inner_result; + + if let Some(r#return) = r#return { + // c. Set innerResult to Completion(Call(return, iterator)). + 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?; - // 6. If innerResult.[[Type]] is throw, return Completion(innerResult). - inner_result?; + // 6. If innerResult.[[Type]] is throw, return ? innerResult. + let inner_result = inner_result?; - // 7. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception. - // 8. Return Completion(completion). - Ok(completion) + if inner_result.is_object() { + // 8. Return ? 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). items.get_iterator(context, Some(IteratorHint::Sync), None)? }; + // 3. Let values be a new empty List. let mut values = Vec::new(); @@ -442,7 +484,10 @@ pub(crate) fn iterable_to_list( 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: /// - [ECMA reference][spec] diff --git a/boa_engine/src/builtins/promise/mod.rs b/boa_engine/src/builtins/promise/mod.rs index cca904f395..8f7c8c9933 100644 --- a/boa_engine/src/builtins/promise/mod.rs +++ b/boa_engine/src/builtins/promise/mod.rs @@ -164,7 +164,7 @@ impl PromiseCapability { .into(); // 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 = &mut promise_capability.try_borrow_mut().expect("msg"); @@ -661,10 +661,11 @@ impl Promise { // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). 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)). - let result = Self::perform_promise_race( - &iterator_record, + let mut result = Self::perform_promise_race( + &mut iterator_record, c, &promise_capability, &promise_resolve, @@ -674,7 +675,9 @@ impl Promise { // 8. If result is an abrupt completion, then if result.is_err() { // 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). if_abrupt_reject_promise!(result, promise_capability, context); @@ -698,7 +701,7 @@ impl Promise { /// /// [spec]: https://tc39.es/ecma262/#sec-performpromiserace fn perform_promise_race( - iterator_record: &IteratorRecord, + iterator_record: &mut IteratorRecord, constructor: &JsValue, result_capability: &PromiseCapability, promise_resolve: &JsValue, @@ -711,7 +714,7 @@ impl Promise { // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true. if next.is_err() { - // TODO: set the [[Done]] field in the IteratorRecord (currently doesn't exist) + iterator_record.set_done(true); } // c. ReturnIfAbrupt(next). @@ -723,7 +726,7 @@ impl Promise { // f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true. 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). @@ -744,7 +747,7 @@ impl Promise { } else { // d. If next is false, then // 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]]. return Ok(result_capability.promise.clone()); diff --git a/boa_engine/src/builtins/reflect/mod.rs b/boa_engine/src/builtins/reflect/mod.rs index 0adad88ba6..9835c35834 100644 --- a/boa_engine/src/builtins/reflect/mod.rs +++ b/boa_engine/src/builtins/reflect/mod.rs @@ -13,7 +13,7 @@ use super::{Array, JsArgs}; use crate::{ builtins::{self, BuiltIn}, - object::{JsObject, ObjectInitializer}, + object::ObjectInitializer, property::Attribute, symbol::WellKnownSymbols, Context, JsResult, JsValue, @@ -102,27 +102,31 @@ impl Reflect { args: &[JsValue], context: &mut Context, ) -> JsResult { + // 1. If IsConstructor(target) is false, throw a TypeError exception. let target = args - .get(0) - .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be a function"))?; - let args_list = args.get_or_undefined(1); - - if !target.is_constructor() { - return context.throw_type_error("target must be a constructor"); - } + .get_or_undefined(0) + .as_constructor() + .ok_or_else(|| context.construct_type_error("target must be a constructor"))?; let new_target = if let Some(new_target) = args.get(2) { - if new_target.as_object().map(JsObject::is_constructor) != Some(true) { - return context.throw_type_error("newTarget must be constructor"); + // 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception. + 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 { - target.clone().into() + // 2. If newTarget is not present, set newTarget to target. + target }; - let args = args_list.create_list_from_array_like(&[], context)?; - target.construct(&args, &new_target, context) + // 4. Let args be ? CreateListFromArrayLike(argumentsList). + 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. diff --git a/boa_engine/src/builtins/regexp/mod.rs b/boa_engine/src/builtins/regexp/mod.rs index dd92445704..49f419b17a 100644 --- a/boa_engine/src/builtins/regexp/mod.rs +++ b/boa_engine/src/builtins/regexp/mod.rs @@ -1183,11 +1183,7 @@ impl RegExp { let flags = regexp.get("flags", context)?.to_string(context)?; // 6. Let matcher be ? Construct(C, « R, flags »). - let matcher = c.construct( - &[this.clone(), flags.clone().into()], - &c.clone().into(), - context, - )?; + let matcher = c.construct(&[this.clone(), flags.clone().into()], Some(&c), context)?; let matcher = matcher .as_object() .expect("construct must always return an Object"); @@ -1580,7 +1576,7 @@ impl RegExp { // 10. Let splitter be ? Construct(C, « rx, newFlags »). let splitter = constructor.construct( &[this.clone(), new_flags.into()], - &constructor.clone().into(), + Some(&constructor), context, )?; let splitter = splitter diff --git a/boa_engine/src/builtins/typed_array/mod.rs b/boa_engine/src/builtins/typed_array/mod.rs index 1b30d9a958..2fcf0ba16c 100644 --- a/boa_engine/src/builtins/typed_array/mod.rs +++ b/boa_engine/src/builtins/typed_array/mod.rs @@ -2968,7 +2968,7 @@ impl TypedArray { context: &mut Context, ) -> JsResult { // 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). let obj = new_typed_array diff --git a/boa_engine/src/bytecompiler.rs b/boa_engine/src/bytecompiler.rs index 367f59ae02..b43e888c0c 100644 --- a/boa_engine/src/bytecompiler.rs +++ b/boa_engine/src/bytecompiler.rs @@ -1433,7 +1433,6 @@ impl<'b> ByteCompiler<'b> { self.patch_jump(exit); self.pop_loop_control_info(); self.emit_opcode(Opcode::LoopEnd); - self.emit_opcode(Opcode::PushFalse); self.emit_opcode(Opcode::IteratorClose); self.patch_jump(early_exit); @@ -1517,7 +1516,6 @@ impl<'b> ByteCompiler<'b> { self.patch_jump(exit); self.pop_loop_control_info(); self.emit_opcode(Opcode::LoopEnd); - self.emit_opcode(Opcode::PushFalse); self.emit_opcode(Opcode::IteratorClose); } Node::WhileLoop(while_) => { @@ -1569,8 +1567,12 @@ impl<'b> ByteCompiler<'b> { } else { 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::FinallySetJump, &[start_address]); } else { @@ -1614,6 +1616,7 @@ impl<'b> ByteCompiler<'b> { for _ in 0..emit_for_of_in_exit { self.emit_opcode(Opcode::Pop); self.emit_opcode(Opcode::Pop); + self.emit_opcode(Opcode::Pop); } for _ in 0..num_loops { self.emit_opcode(Opcode::LoopEnd); @@ -1645,8 +1648,12 @@ impl<'b> ByteCompiler<'b> { } else { 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); } else { self.emit_opcode(Opcode::TryEnd); @@ -2276,26 +2283,18 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::ValueNotNullOrUndefined); self.emit_opcode(Opcode::InitIterator); - for (i, binding) in pattern.bindings().iter().enumerate() { + for binding in pattern.bindings().iter() { use BindingPatternTypeArray::{ BindingPattern, BindingPatternRest, Elision, Empty, GetConstField, GetConstFieldRest, GetField, GetFieldRest, SingleName, SingleNameRest, }; - let next = if i == pattern.bindings().len() - 1 { - Opcode::IteratorNextFull - } else { - Opcode::IteratorNext - }; - match binding { // ArrayBindingPattern : [ ] - Empty => { - self.emit_opcode(Opcode::PushFalse); - } + Empty => {} // ArrayBindingPattern : [ Elision ] Elision => { - self.emit_opcode(next); + self.emit_opcode(Opcode::IteratorNext); self.emit_opcode(Opcode::Pop); } // SingleNameBinding : BindingIdentifier Initializer[opt] @@ -2303,7 +2302,7 @@ impl<'b> ByteCompiler<'b> { ident, default_init, } => { - self.emit_opcode(next); + self.emit_opcode(Opcode::IteratorNext); if let Some(init) = default_init { let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); @@ -2313,11 +2312,11 @@ impl<'b> ByteCompiler<'b> { self.emit_binding(def, *ident); } GetField { get_field } => { - self.emit_opcode(next); + self.emit_opcode(Opcode::IteratorNext); self.access_set(Access::ByValue { node: get_field }, None, false)?; } GetConstField { get_const_field } => { - self.emit_opcode(next); + self.emit_opcode(Opcode::IteratorNext); self.access_set( Access::ByName { node: get_const_field, @@ -2328,19 +2327,17 @@ impl<'b> ByteCompiler<'b> { } // BindingElement : BindingPattern Initializer[opt] BindingPattern { pattern } => { - self.emit_opcode(next); + self.emit_opcode(Opcode::IteratorNext); self.compile_declaration_pattern(pattern, def)?; } // BindingRestElement : ... BindingIdentifier SingleNameRest { ident } => { self.emit_opcode(Opcode::IteratorToArray); self.emit_binding(def, *ident); - self.emit_opcode(Opcode::PushTrue); } GetFieldRest { get_field } => { self.emit_opcode(Opcode::IteratorToArray); self.access_set(Access::ByValue { node: get_field }, None, false)?; - self.emit_opcode(Opcode::PushTrue); } GetConstFieldRest { get_const_field } => { self.emit_opcode(Opcode::IteratorToArray); @@ -2351,21 +2348,15 @@ impl<'b> ByteCompiler<'b> { None, false, )?; - self.emit_opcode(Opcode::PushTrue); } // BindingRestElement : ... BindingPattern BindingPatternRest { pattern } => { self.emit_opcode(Opcode::IteratorToArray); 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); } } diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 122b0460f6..3aa71d5a17 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -177,17 +177,34 @@ impl Context { parser.parse_all(self) } - /// + /// `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] pub(crate) fn call( &mut self, f: &JsValue, - this: &JsValue, - args: &[JsValue], + v: &JsValue, + arguments_list: &[JsValue], ) -> JsResult { + // 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() .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. @@ -707,6 +724,7 @@ impl Context { param_count: 0, arg_count: 0, generator_resume_kind: GeneratorResumeKind::Normal, + thrown: false, }); self.realm.set_global_binding_number(); diff --git a/boa_engine/src/object/internal_methods/bound_function.rs b/boa_engine/src/object/internal_methods/bound_function.rs index d48d4ea3b9..1b504d44b3 100644 --- a/boa_engine/src/object/internal_methods/bound_function.rs +++ b/boa_engine/src/object/internal_methods/bound_function.rs @@ -68,7 +68,7 @@ fn bound_function_exotic_call( fn bound_function_exotic_construct( obj: &JsObject, arguments_list: &[JsValue], - new_target: &JsValue, + new_target: &JsObject, context: &mut Context, ) -> JsResult { let object = obj.borrow(); @@ -89,11 +89,12 @@ fn bound_function_exotic_construct( args.extend_from_slice(arguments_list); // 5. If SameValue(F, newTarget) is true, set newTarget to target. - let new_target = match new_target { - JsValue::Object(new_target) if JsObject::equals(obj, new_target) => target.clone().into(), - _ => new_target.clone(), + let new_target = if JsObject::equals(obj, new_target) { + target + } else { + new_target }; // 6. Return ? Construct(target, args, newTarget). - target.construct(&args, &new_target, context) + target.construct(&args, Some(new_target), context) } diff --git a/boa_engine/src/object/internal_methods/function.rs b/boa_engine/src/object/internal_methods/function.rs index 797868855f..c80ace69ee 100644 --- a/boa_engine/src/object/internal_methods/function.rs +++ b/boa_engine/src/object/internal_methods/function.rs @@ -53,8 +53,8 @@ fn function_call( fn function_construct( obj: &JsObject, args: &[JsValue], - new_target: &JsValue, + new_target: &JsObject, context: &mut Context, ) -> JsResult { - obj.construct_internal(args, new_target, context) + obj.construct_internal(args, &new_target.clone().into(), context) } diff --git a/boa_engine/src/object/internal_methods/mod.rs b/boa_engine/src/object/internal_methods/mod.rs index d628f6d116..4e660fbf3b 100644 --- a/boa_engine/src/object/internal_methods/mod.rs +++ b/boa_engine/src/object/internal_methods/mod.rs @@ -259,7 +259,7 @@ impl JsObject { pub(crate) fn __construct__( &self, args: &[JsValue], - new_target: &JsValue, + new_target: &JsObject, context: &mut Context, ) -> JsResult { let _timer = Profiler::global().start_event("Object::__construct__", "object"); @@ -324,7 +324,7 @@ pub(crate) struct InternalObjectMethods { pub(crate) __call__: Option JsResult>, pub(crate) __construct__: - Option JsResult>, + Option JsResult>, } /// Abstract operation `OrdinaryGetPrototypeOf`. diff --git a/boa_engine/src/object/internal_methods/proxy.rs b/boa_engine/src/object/internal_methods/proxy.rs index 7ecb7e0d8a..3359153b20 100644 --- a/boa_engine/src/object/internal_methods/proxy.rs +++ b/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: /// - [ECMAScript reference][spec] @@ -944,7 +944,7 @@ fn proxy_exotic_call( fn proxy_exotic_construct( obj: &JsObject, args: &[JsValue], - new_target: &JsValue, + new_target: &JsObject, context: &mut Context, ) -> JsResult { // 1. Let handler be O.[[ProxyHandler]]. @@ -966,7 +966,7 @@ fn proxy_exotic_construct( // 7. If trap is undefined, then } else { // 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). @@ -975,7 +975,11 @@ fn proxy_exotic_construct( // 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »). let new_obj = trap.call( &handler.into(), - &[target.clone().into(), arg_array.into(), new_target.clone()], + &[ + target.clone().into(), + arg_array.into(), + new_target.clone().into(), + ], context, )?; diff --git a/boa_engine/src/object/operations.rs b/boa_engine/src/object/operations.rs index 0b148ac012..5a8ef6accb 100644 --- a/boa_engine/src/object/operations.rs +++ b/boa_engine/src/object/operations.rs @@ -309,21 +309,28 @@ impl JsObject { self.__call__(this, args, context) } + /// `Construct ( F [ , argumentsList [ , newTarget ] ] )` + /// /// Construct an instance of this object with the specified arguments. /// /// # Panics /// /// Panics if the object is currently mutably borrowed. - // + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-construct #[track_caller] #[inline] pub fn construct( &self, args: &[JsValue], - new_target: &JsValue, + new_target: Option<&JsObject>, context: &mut Context, ) -> JsResult { // 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. // 3. Return ? F.[[Construct]](argumentsList, newTarget). self.__construct__(args, new_target, context) @@ -660,7 +667,7 @@ impl JsValue { /// More information: /// - [ECMAScript reference][spec] /// - /// [spec]: https://tc39.es/ecma262/#sec-getmethod + /// [spec]: https://tc39.es/ecma262/#sec-getv #[inline] pub(crate) fn get_v(&self, key: K, context: &mut Context) -> JsResult where diff --git a/boa_engine/src/vm/call_frame.rs b/boa_engine/src/vm/call_frame.rs index 34e29a348e..89d02ab39b 100644 --- a/boa_engine/src/vm/call_frame.rs +++ b/boa_engine/src/vm/call_frame.rs @@ -32,6 +32,9 @@ pub struct CallFrame { pub(crate) arg_count: usize, #[unsafe_ignore_trace] pub(crate) generator_resume_kind: GeneratorResumeKind, + + // Indicate that the last try block has thrown an exception. + pub(crate) thrown: bool, } impl CallFrame { diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index ebbf03a151..99d896be55 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -266,6 +266,7 @@ impl CodeBlock { ) } Opcode::Pop + | Opcode::PopIfThrown | Opcode::Dup | Opcode::Swap | Opcode::PushZero @@ -336,7 +337,6 @@ impl CodeBlock { | Opcode::LoopEnd | Opcode::InitIterator | Opcode::IteratorNext - | Opcode::IteratorNextFull | Opcode::IteratorClose | Opcode::IteratorToArray | Opcode::RequireObjectCoercible @@ -717,6 +717,7 @@ impl JsObject { param_count, arg_count, generator_resume_kind: GeneratorResumeKind::Normal, + thrown: false, }); let result = context.run(); @@ -820,6 +821,7 @@ impl JsObject { param_count, arg_count, generator_resume_kind: GeneratorResumeKind::Normal, + thrown: false, }; let mut stack = args; @@ -1016,6 +1018,7 @@ impl JsObject { param_count, arg_count, generator_resume_kind: GeneratorResumeKind::Normal, + thrown: false, }); let result = context.run(); diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 176e4f1388..7ceee54749 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -146,6 +146,13 @@ impl Context { Opcode::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 => { let value = self.vm.pop(); self.vm.push(value.clone()); @@ -233,11 +240,17 @@ impl Context { self.vm.push(array); } 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 = iterator.as_object().expect("iterator was not an object"); 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)? { Array::push(&array, &[next.value(self)?], self)?; } @@ -1144,6 +1157,7 @@ impl Context { num_env: 0, num_loop_stack_entries: 0, }); + self.vm.frame_mut().thrown = false; } Opcode::CatchEnd2 => { let frame = self.vm.frame_mut(); @@ -1386,7 +1400,7 @@ impl Context { let result = func .as_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); } @@ -1413,7 +1427,7 @@ impl Context { let result = func .as_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); } @@ -1527,7 +1541,7 @@ impl Context { let object = object.to_object(self)?; let iterator = ForInIterator::create_for_in_iterator(JsValue::new(object), self); - let next_function = iterator + let next_method = iterator .get_property("next") .as_ref() .map(PropertyDescriptor::expect_value) @@ -1535,39 +1549,32 @@ impl Context { .ok_or_else(|| self.construct_type_error("Could not find property `next`"))?; self.vm.push(iterator); - self.vm.push(next_function); + self.vm.push(next_method); + self.vm.push(false); } Opcode::InitIterator => { let object = self.vm.pop(); let iterator = object.get_iterator(self, None, None)?; - self.vm.push(iterator.iterator_object()); - self.vm.push(iterator.next_function()); + self.vm.push(iterator.iterator().clone()); + self.vm.push(iterator.next_method().clone()); + self.vm.push(iterator.done()); } Opcode::IteratorNext => { - let next_function = self.vm.pop(); - let iterator = self.vm.pop(); - - let iterator_record = IteratorRecord::new(iterator.clone(), next_function.clone()); - let next = iterator_record.step(self)?; - - 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 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 = 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)?; - self.vm.push(iterator); - self.vm.push(next_function); + self.vm.push(iterator.clone()); + self.vm.push(next_method); if let Some(next) = next { let value = next.value(self)?; self.vm.push(false); @@ -1578,19 +1585,31 @@ impl Context { } } Opcode::IteratorClose => { - let done = 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(); - if !done.as_boolean().expect("not a boolean") { - let iterator_record = IteratorRecord::new(iterator, next_function); + let iterator = iterator.as_object().expect("iterator was not an object"); + if !done { + let iterator_record = IteratorRecord::new(iterator.clone(), next_method, done); iterator_record.close(Ok(JsValue::Null), self)?; } } 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 = 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(); while let Some(result) = iterator_record.step(self)? { @@ -1599,20 +1618,29 @@ impl Context { let array = Array::create_array_from_list(values, self); - self.vm.push(iterator); - self.vm.push(next_function); + self.vm.push(iterator.clone()); + self.vm.push(next_method); + self.vm.push(true); self.vm.push(array); } Opcode::ForInLoopNext => { let address = self.vm.read::(); - 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 = 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)? { - self.vm.push(iterator); - self.vm.push(next_function); + self.vm.push(iterator.clone()); + self.vm.push(next_method); + self.vm.push(done); let value = next.value(self)?; self.vm.push(value); } else { @@ -1620,8 +1648,9 @@ impl Context { self.vm.frame_mut().loop_env_stack_dec(); self.vm.frame_mut().try_env_stack_dec(); self.realm.environments.pop(); - self.vm.push(iterator); - self.vm.push(next_function); + self.vm.push(iterator.clone()); + self.vm.push(next_method); + self.vm.push(done); } } Opcode::ConcatToString => { @@ -1717,12 +1746,19 @@ impl Context { Opcode::GeneratorNextDelegate => { let done_address = self.vm.read::(); 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 = iterator.as_object().expect("iterator was not an object"); match self.vm.frame().generator_resume_kind { 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(|| { self.construct_type_error("generator next method returned non-object") })?; @@ -1734,15 +1770,16 @@ impl Context { return Ok(ShouldExit::False); } let value = result_object.get("value", self)?; - self.vm.push(iterator); - self.vm.push(next_function); + self.vm.push(iterator.clone()); + self.vm.push(next_method.clone()); + self.vm.push(done); self.vm.push(value); return Ok(ShouldExit::Yield); } GeneratorResumeKind::Throw => { let throw = iterator.get_method("throw", self)?; 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(|| { self.construct_type_error( "generator throw method returned non-object", @@ -1756,14 +1793,15 @@ impl Context { return Ok(ShouldExit::False); } let value = result_object.get("value", self)?; - self.vm.push(iterator); - self.vm.push(next_function); + self.vm.push(iterator.clone()); + self.vm.push(next_method.clone()); + self.vm.push(done); self.vm.push(value); return Ok(ShouldExit::Yield); } self.vm.frame_mut().pc = done_address as usize; let iterator_record = - IteratorRecord::new(iterator.clone(), next_function.clone()); + IteratorRecord::new(iterator.clone(), next_method, done); iterator_record.close(Ok(JsValue::Undefined), self)?; let error = self.construct_type_error("iterator does not have a throw method"); @@ -1772,7 +1810,8 @@ impl Context { GeneratorResumeKind::Return => { let r#return = iterator.get_method("return", self)?; 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(|| { self.construct_type_error( "generator return method returned non-object", @@ -1786,8 +1825,9 @@ impl Context { return Ok(ShouldExit::True); } let value = result_object.get("value", self)?; - self.vm.push(iterator); - self.vm.push(next_function); + self.vm.push(iterator.clone()); + self.vm.push(next_method.clone()); + self.vm.push(done); self.vm.push(value); return Ok(ShouldExit::Yield); } @@ -1928,6 +1968,7 @@ impl Context { self.vm.frame_mut().pc = address as usize; self.vm.frame_mut().catch.pop(); self.vm.frame_mut().finally_return = FinallyReturn::Err; + self.vm.frame_mut().thrown = true; self.vm.push(e); } else { self.vm.stack.truncate(start_stack_size); diff --git a/boa_engine/src/vm/opcode.rs b/boa_engine/src/vm/opcode.rs index 08e6c48893..917386b390 100644 --- a/boa_engine/src/vm/opcode.rs +++ b/boa_engine/src/vm/opcode.rs @@ -9,6 +9,13 @@ pub enum Opcode { /// Stack: value **=>** 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. /// /// Operands: @@ -163,7 +170,7 @@ pub enum Opcode { /// /// Operands: /// - /// Stack: array, iterator, next_function **=>** array + /// Stack: array, iterator, next_method, done **=>** array PushIteratorToArray, /// Binary `+` operator. @@ -920,42 +927,35 @@ pub enum Opcode { /// /// Operands: address: `u32` /// - /// Stack: object **=>** iterator, next_function + /// Stack: object **=>** iterator, next_method, done ForInLoopInitIterator, /// Initialize an iterator. /// /// Operands: /// - /// Stack: object **=>** iterator, next_function + /// Stack: object **=>** iterator, next_method, done InitIterator, /// Advance the iterator by one and put the value on the stack. /// /// Operands: /// - /// Stack: iterator, next_function **=>** iterator, next_function, next_value + /// Stack: iterator, next_method, done **=>** iterator, next_method, done, next_value 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. /// /// Operands: /// - /// Stack: iterator, next_function, done **=>** + /// Stack: iterator, next_method, done **=>** IteratorClose, /// Consume the iterator and construct and array with all the values. /// /// Operands: /// - /// Stack: iterator, next_function **=>** iterator, next_function, array + /// Stack: iterator, next_method, done **=>** iterator, next_method, done, array IteratorToArray, /// 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` /// - /// Stack: iterator, next_function **=>** iterator, next_function, next_result + /// Stack: iterator, next_method, done **=>** iterator, next_method, done, next_result ForInLoopNext, /// Concat multiple stack objects into a string. @@ -1034,7 +1034,7 @@ pub enum Opcode { /// /// Operands: done_address: `u32` /// - /// Stack: iterator, next_function, received **=>** iterator, next_function + /// Stack: iterator, next_method, done, received **=>** iterator, next_method, done GeneratorNextDelegate, /// No-operation instruction, does nothing. @@ -1060,6 +1060,7 @@ impl Opcode { pub fn as_str(self) -> &'static str { match self { Self::Pop => "Pop", + Self::PopIfThrown => "PopIfThrown", Self::Dup => "Dup", Self::Swap => "Swap", Self::PushZero => "PushZero", @@ -1185,7 +1186,6 @@ impl Opcode { Self::ForInLoopInitIterator => "ForInLoopInitIterator", Self::InitIterator => "InitIterator", Self::IteratorNext => "IteratorNext", - Self::IteratorNextFull => "IteratorNextFull", Self::IteratorClose => "IteratorClose", Self::IteratorToArray => "IteratorToArray", Self::ForInLoopNext => "ForInLoopNext", @@ -1207,6 +1207,7 @@ impl Opcode { pub fn as_instruction_str(self) -> &'static str { match self { Self::Pop => "INST - Pop", + Self::PopIfThrown => "INST - PopIfThrown", Self::Dup => "INST - Dup", Self::Swap => "INST - Swap", Self::PushZero => "INST - PushZero", @@ -1319,7 +1320,6 @@ impl Opcode { Self::ForInLoopInitIterator => "INST - ForInLoopInitIterator", Self::InitIterator => "INST - InitIterator", Self::IteratorNext => "INST - IteratorNext", - Self::IteratorNextFull => "INST - IteratorNextFull", Self::IteratorClose => "INST - IteratorClose", Self::IteratorToArray => "INST - IteratorToArray", Self::ForInLoopNext => "INST - ForInLoopNext", diff --git a/test262 b/test262 index 79e3bc5176..74de3d1d32 160000 --- a/test262 +++ b/test262 @@ -1 +1 @@ -Subproject commit 79e3bc5176b6f29a5aed3b7164a9c623a3a9a63b +Subproject commit 74de3d1d327b3238d6193242c3cc157746f2faad