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. 16
      boa_engine/src/builtins/array/mod.rs
  4. 2
      boa_engine/src/builtins/array_buffer/mod.rs
  5. 149
      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 = [
"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",

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" }
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"

16
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(|| {

2
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(|| {

149
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<IteratorResult> {
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]

19
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());

34
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<JsValue> {
// 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.

8
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

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

@ -2968,7 +2968,7 @@ impl TypedArray {
context: &mut Context,
) -> JsResult<JsObject> {
// 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

45
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);
}
}

26
boa_engine/src/context/mod.rs

@ -177,17 +177,34 @@ impl Context {
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]
pub(crate) fn call(
&mut self,
f: &JsValue,
this: &JsValue,
args: &[JsValue],
v: &JsValue,
arguments_list: &[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()
.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();

11
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<JsValue> {
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)
}

4
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<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__(
&self,
args: &[JsValue],
new_target: &JsValue,
new_target: &JsObject,
context: &mut Context,
) -> JsResult<JsValue> {
let _timer = Profiler::global().start_event("Object::__construct__", "object");
@ -324,7 +324,7 @@ pub(crate) struct InternalObjectMethods {
pub(crate) __call__:
Option<fn(&JsObject, &JsValue, &[JsValue], &mut Context) -> JsResult<JsValue>>,
pub(crate) __construct__:
Option<fn(&JsObject, &[JsValue], &JsValue, &mut Context) -> JsResult<JsValue>>,
Option<fn(&JsObject, &[JsValue], &JsObject, &mut Context) -> JsResult<JsValue>>,
}
/// 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:
/// - [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<JsValue> {
// 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,
)?;

13
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.
// <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]
#[inline]
pub fn construct(
&self,
args: &[JsValue],
new_target: &JsValue,
new_target: Option<&JsObject>,
context: &mut Context,
) -> JsResult<JsValue> {
// 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<K>(&self, key: K, context: &mut Context) -> JsResult<Self>
where

3
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 {

5
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();

147
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::<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 = 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::<u32>();
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);

34
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",

2
test262

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