diff --git a/Cargo.lock b/Cargo.lock index 5897615ab05..a03f6b7ba16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,6 +432,7 @@ dependencies = [ "rand", "regress", "rustc-hash 2.1.1", + "rustversion", "ryu-js", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index c3543f4d29e..6d42c02e576 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ members = [ exclude = [ "tests/fuzz", # Does weird things on Windows tests - "tests/src", # Just a hack to have fuzz inside tests - "tests/wpt", # Should not run WPT by default. + "tests/src", # Just a hack to have fuzz inside tests + "tests/wpt", # Should not run WPT by default. ] [workspace.package] @@ -75,7 +75,9 @@ phf = { version = "0.13.1", default-features = false } pollster = "0.4.0" regex = "1.12.3" regress = { version = "0.11.0", features = ["utf16"] } -reqwest = { version = "0.13.2", default-features = false, features = ["rustls-no-provider"] } +reqwest = { version = "0.13.2", default-features = false, features = [ + "rustls-no-provider", +] } rustls = { version = "0.23.37", default-features = false, features = ["ring"] } rustc-hash = { version = "2.1.1", default-features = false } serde_json = "1.0.149" @@ -129,7 +131,9 @@ cfg-if = "1.0.4" either = "1.15.0" sys-locale = "0.3.2" timezone_provider = { version = "0.2.0" } -temporal_rs = { version = "0.2.0", default-features = false, features = ["float64_representable_durations"] } +temporal_rs = { version = "0.2.0", default-features = false, features = [ + "float64_representable_durations", +] } web-time = "1.1.0" criterion = "0.8.1" float-cmp = "0.10.0" @@ -146,6 +150,7 @@ temp-env = "0.3.6" strum = { version = "0.28", features = ["derive"] } husky-rs = "0.3.2" async-channel = "2.5.0" +rustversion = "1" # ICU4X diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index 480a7a7adeb..881d7b5ec19 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -71,7 +71,12 @@ trace = ["js"] annex-b = ["boa_ast/annex-b", "boa_parser/annex-b"] # Enable Boa's Temporal proposal implementation -temporal = ["dep:icu_calendar", "dep:temporal_rs", "dep:timezone_provider", "timezone_provider/experimental_tzif"] +temporal = [ + "dep:icu_calendar", + "dep:temporal_rs", + "dep:timezone_provider", + "timezone_provider/experimental_tzif", +] #Enable access to host system timezone system-time-zone = ["dep:iana-time-zone"] @@ -91,6 +96,10 @@ xsum = ["dep:xsum"] # Native Backtraces native-backtrace = [] +# Enable support for tail call optimization. Requires nightly +# See https://github.com/rust-lang/rust/issues/112788 and https://github.com/rust-lang/rust/issues/151401 +tailcall = [] + [dependencies] tag_ptr.workspace = true boa_interner.workspace = true @@ -123,7 +132,9 @@ thiserror.workspace = true dashmap.workspace = true num_enum.workspace = true thin-vec.workspace = true -itertools = { workspace = true, default-features = false, features = ["use_alloc"] } +itertools = { workspace = true, default-features = false, features = [ + "use_alloc", +] } icu_normalizer = { workspace = true, features = [ "compiled_data", "utf16_iter", @@ -180,9 +191,7 @@ icu_decimal = { workspace = true, default-features = false, features = [ writeable = { workspace = true, optional = true } yoke = { workspace = true, optional = true } zerofrom = { workspace = true, optional = true } -fixed_decimal = { workspace = true, features = [ - "ryu", -], optional = true } +fixed_decimal = { workspace = true, features = ["ryu"], optional = true } tinystr = { workspace = true, optional = true } # temporal deps @@ -203,6 +212,9 @@ textwrap.workspace = true test-case.workspace = true husky-rs.workspace = true +[build-dependencies] +rustversion.workspace = true + [target.x86_64-unknown-linux-gnu.dev-dependencies] jemallocator.workspace = true diff --git a/core/engine/build.rs b/core/engine/build.rs new file mode 100644 index 00000000000..9d4a79f798f --- /dev/null +++ b/core/engine/build.rs @@ -0,0 +1,10 @@ +//! Build script for the `boa_engine` crate to detect if the toolchain is nightly. +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + // Tell rustc that `cfg(boa_nightly)` is intentional + println!("cargo:rustc-check-cfg=cfg(boa_nightly)"); + + if rustversion::cfg!(since(2026 - 01 - 26)) { + println!("cargo:rustc-cfg=boa_nightly"); + } +} diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index db4607d1abd..5ebe1869579 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -2742,6 +2742,8 @@ impl<'ctx> ByteCompiler<'ctx> { debug_id: CodeBlock::get_next_codeblock_id(), #[cfg(feature = "trace")] traced: Cell::new(false), + #[cfg(all(feature = "trace", all(feature = "tailcall", boa_nightly)))] + last_trace_time: Cell::new(None), } } diff --git a/core/engine/src/lib.rs b/core/engine/src/lib.rs index 37558607d06..88ef592520c 100644 --- a/core/engine/src/lib.rs +++ b/core/engine/src/lib.rs @@ -50,6 +50,9 @@ html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg", html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg" )] +#![cfg_attr(all(feature = "tailcall", boa_nightly), allow(incomplete_features))] +#![cfg_attr(all(feature = "tailcall", boa_nightly), feature(explicit_tail_calls))] +#![cfg_attr(all(feature = "tailcall", boa_nightly), feature(rust_preserve_none_cc))] #![cfg_attr(test, allow(clippy::needless_raw_string_hashes))] // Makes strings a bit more copy-pastable #![cfg_attr(not(test), forbid(clippy::unwrap_used))] #![allow( diff --git a/core/engine/src/script.rs b/core/engine/src/script.rs index ceba9c24a9a..bfeba76eb15 100644 --- a/core/engine/src/script.rs +++ b/core/engine/src/script.rs @@ -188,6 +188,8 @@ impl Script { /// This uses an implementation defined amount of "clock cycles" that need to pass before /// execution is suspended. See [`Script::evaluate_async_with_budget`] if you want to also /// customize this parameter. + /// + /// Note: worse performance due to loop-based dispatch #[allow(clippy::future_not_send)] pub async fn evaluate_async(&self, context: &mut Context) -> JsResult { self.evaluate_async_with_budget(context, 256).await @@ -200,6 +202,8 @@ impl Script { /// CPU clock cycles a VM instruction will take, but all instructions have a "cost" associated /// with them that depends on their individual complexity. We'd recommend benchmarking with /// different budget sizes in order to find the ideal yielding time for your application. + /// + /// Note: worse performance due to loop-based dispatch #[allow(clippy::future_not_send)] pub async fn evaluate_async_with_budget( &self, diff --git a/core/engine/src/vm/code_block.rs b/core/engine/src/vm/code_block.rs index d9eec2fc949..745394c8f2b 100644 --- a/core/engine/src/vm/code_block.rs +++ b/core/engine/src/vm/code_block.rs @@ -14,6 +14,8 @@ use bitflags::bitflags; use boa_ast::scope::{BindingLocator, Scope}; use boa_gc::{Finalize, Gc, Trace, empty_trace}; use itertools::Itertools; +#[cfg(all(feature = "trace", all(feature = "tailcall", boa_nightly)))] +use std::time::Instant; use std::{cell::Cell, fmt::Display, fmt::Write as _}; use thin_vec::ThinVec; @@ -172,6 +174,10 @@ pub struct CodeBlock { #[cfg(feature = "trace")] #[unsafe_ignore_trace] pub(crate) traced: Cell, + + #[cfg(all(feature = "trace", all(feature = "tailcall", boa_nightly)))] + #[unsafe_ignore_trace] + pub(crate) last_trace_time: Cell>, } /// ---- `CodeBlock` public API ---- @@ -204,6 +210,8 @@ impl CodeBlock { debug_id: CodeBlock::get_next_codeblock_id(), #[cfg(feature = "trace")] traced: Cell::new(false), + #[cfg(all(feature = "trace", all(feature = "tailcall", boa_nightly)))] + last_trace_time: Cell::new(None), } } diff --git a/core/engine/src/vm/mod.rs b/core/engine/src/vm/mod.rs index f01c9bb47dd..b9f31900db9 100644 --- a/core/engine/src/vm/mod.rs +++ b/core/engine/src/vm/mod.rs @@ -12,8 +12,14 @@ use crate::{ object::JsFunction, realm::Realm, script::Script, - vm::opcode::{OPCODE_HANDLERS, OPCODE_HANDLERS_BUDGET}, }; + +#[cfg(not(all(feature = "tailcall", boa_nightly)))] +use crate::vm::opcode::{OPCODE_HANDLERS, OPCODE_HANDLERS_BUDGET}; + +#[cfg(all(feature = "tailcall", boa_nightly))] +use crate::vm::opcode::OPCODE_HANDLERS_BUDGET; + use boa_gc::{Finalize, Gc, Trace, custom_trace}; use shadow_stack::ShadowStack; use std::{future::Future, ops::ControlFlow, path::Path, pin::Pin, task}; @@ -634,9 +640,16 @@ impl Context { "{msg:-^width$}", width = Self::COLUMN_WIDTH * Self::NUMBER_OF_COLUMNS - 10 ); + + let time_header = if cfg!(all(feature = "tailcall", boa_nightly)) { + "ΔTime(prev)" + } else { + "Time" + }; + println!( "{: CompletionRecord { + #[cfg(not(all(feature = "tailcall", boa_nightly)))] while let Some(byte) = self .vm .frame() @@ -923,8 +939,11 @@ impl Context { ControlFlow::Break(value) => return value, } } + #[cfg(not(all(feature = "tailcall", boa_nightly)))] + return CompletionRecord::Throw(JsError::from_native(JsNativeError::error())); - CompletionRecord::Throw(JsError::from_native(JsNativeError::error())) + #[cfg(all(feature = "tailcall", boa_nightly))] + return self.dispatch_next(self.vm.frame().pc as usize); } /// Checks if we haven't exceeded the defined runtime limits. diff --git a/core/engine/src/vm/opcode/mod.rs b/core/engine/src/vm/opcode/mod.rs index e304514a8fc..9f37caf6667 100644 --- a/core/engine/src/vm/opcode/mod.rs +++ b/core/engine/src/vm/opcode/mod.rs @@ -8,6 +8,9 @@ use args::{Argument, read}; use std::ops::ControlFlow; use thin_vec::ThinVec; +#[cfg(all(feature = "tailcall", boa_nightly))] +use tailcall::generate_opcode_tailcall_handlers; + mod args; // Operation modules @@ -34,6 +37,8 @@ mod push; mod rest_parameter; mod set; mod switch; +#[cfg(all(feature = "tailcall", boa_nightly))] +mod tailcall; mod templates; mod to; mod unary_ops; @@ -394,16 +399,6 @@ macro_rules! generate_opcodes { )* } - type OpcodeHandler = fn(&mut Context, usize) -> ControlFlow; - - pub(crate) const OPCODE_HANDLERS: [OpcodeHandler; 256] = { - [ - $( - paste::paste! { [] }, - )* - ] - }; - type OpcodeHandlerBudget = fn(&mut Context, usize, &mut u32) -> ControlFlow; pub(crate) const OPCODE_HANDLERS_BUDGET: [OpcodeHandlerBudget; 256] = { @@ -414,20 +409,6 @@ macro_rules! generate_opcodes { ] }; - $( - paste::paste! { - #[inline(always)] - #[allow(unused_parens)] - fn [](context: &mut Context, pc: usize) -> ControlFlow { - let bytes = &context.vm.frame().code_block.bytecode.bytes; - let (args, next_pc) = <($($($FieldType),*)?)>::decode(bytes, pc + 1); - context.vm.frame_mut().pc = next_pc as u32; - let result = $Variant::operation(args, context); - IntoCompletionRecord::into_completion_record(result, context) - } - } - )* - $( paste::paste! { #[inline(always)] @@ -497,6 +478,46 @@ macro_rules! generate_opcodes { } } +#[cfg(not(all(feature = "tailcall", boa_nightly)))] +macro_rules! generate_opcode_handlers { + ( + $( + $(#[$comment:ident $($args:tt)*])* + $Variant:ident $({ + $( + $(#[$fieldinner:ident $($fieldargs:tt)*])* + $FieldName:ident : $FieldType:ty + ),* + $(,)? + })? $(=> $mapping:ident)? + ),* + $(,)? + ) => { + type OpcodeHandler = fn(&mut Context, usize) -> ControlFlow; + pub(crate) const OPCODE_HANDLERS: [OpcodeHandler; 256] = { + [ + $( + paste::paste! { [] }, + )* + ] + }; + + $( + paste::paste! { + #[inline(always)] + #[allow(unused_parens)] + fn [](context: &mut Context, pc: usize) -> ControlFlow { + let bytes = &context.vm.frame().code_block.bytecode.bytes; + let (args, next_pc) = <($($($FieldType),*)?)>::decode(bytes, pc + 1); + context.vm.frame_mut().pc = next_pc as u32; + let result = $Variant::operation(args, context); + IntoCompletionRecord::into_completion_record(result, context) + } + } + )* + } +} + /// Iterator over the instructions in the compact bytecode. // #[derive(Debug, Clone)] pub(crate) struct InstructionIterator<'bytecode> { @@ -541,1722 +562,1735 @@ impl Iterator for InstructionIterator<'_> { } } -generate_opcodes! { - /// Pop the top value from the stack. - /// - /// - Stack: value **=>** - Pop, - - /// Store integer `0` in dst. - /// - /// - Registers: - /// - Output: dst - StoreZero { dst: RegisterOperand }, - - /// Store integer `1` in dst. - /// - /// - Registers: - /// - Output: dst - StoreOne { dst: RegisterOperand }, - - /// Store `i8` value in dst. - /// - /// - Operands: - /// - value: `i8` - /// - Registers: - /// - Output: dst - StoreInt8 { dst: RegisterOperand, value: i8 }, - - /// Store `i16` value in dst. - /// - /// - Operands: - /// - value: `i16` - /// - Registers: - /// - Output: dst - StoreInt16 { dst: RegisterOperand, value: i16 }, - - /// Store `i32` value in dst. - /// - /// - Operands: - /// - value: `i32` - /// - Registers: - /// - Output: dst - StoreInt32 { dst: RegisterOperand, value: i32 }, - - /// Store `f32` value in dst. - /// - /// - Operands: - /// - value: `f32` - /// - Registers: - /// - Output: dst - StoreFloat { dst: RegisterOperand, value: f32 }, - - /// Store `f64` value in dst. - /// - /// - Operands: - /// - value: `f64` - /// - Registers: - /// - Output: dst - StoreDouble { dst: RegisterOperand, value: f64 }, - - /// Store `NaN` in dst. - /// - /// - Registers: - /// - Output: dst - StoreNan { dst: RegisterOperand }, - - /// Store `Infinity` in dst. - /// - /// - Registers: - /// - Output: dst - StorePositiveInfinity { dst: RegisterOperand }, - - /// Store `-Infinity` in dst. - /// - /// - Registers: - /// - Output: dst - StoreNegativeInfinity { dst: RegisterOperand }, - - /// Store `null` in dst. - /// - /// - Registers: - /// - Output: dst - StoreNull { dst: RegisterOperand }, - - /// Store `true` in dst. - /// - /// - Registers: - /// - Output: dst - StoreTrue { dst: RegisterOperand }, - - /// Store `false` in dst. - /// - /// - Registers: - /// - Output: dst - StoreFalse { dst: RegisterOperand }, - - /// Store `undefined` in dst. - /// - /// - Registers: - /// - Output: dst - StoreUndefined { dst: RegisterOperand }, - - /// Store literal value in dst. - /// - /// Like strings and bigints. The index operand is used to index into the `literals` - /// array to get the value. - /// - /// - Operands: - /// - index: `IndexOperand` - /// - Registers: - /// - Output: dst - StoreLiteral { dst: RegisterOperand, index: IndexOperand }, - - /// Store regexp value in dst. - /// - /// - Operands: - /// - pattern_index: `IndexOperand` - /// - flags: `IndexOperand` - /// - Registers: - /// - Output: dst - StoreRegexp { dst: RegisterOperand, pattern_index: IndexOperand, flags_index: IndexOperand }, - - /// Store empty object `{}` in dst. - /// - /// - Registers: - /// - Output: dst - StoreEmptyObject { dst: RegisterOperand }, - - /// Get the prototype of a superclass and store it in dst. - /// - /// Additionally this sets the `[[prototype]]` of the class and the `DERIVED` flag. - /// - /// - Registers: - /// - Input: class, superclass - /// - Output: dst - StoreClassPrototype { - dst: RegisterOperand, - class: RegisterOperand, - superclass: RegisterOperand - }, - - /// Set the prototype of a class object. - /// - /// - Registers: - /// - Input: class, prototype - /// - Output: dst - SetClassPrototype { - dst: RegisterOperand, - prototype: RegisterOperand, - class: RegisterOperand - }, - - /// Set home object internal slot of an object literal method. - /// - /// - Registers: - /// - Input: function, home - SetHomeObject { - function: RegisterOperand, - home: RegisterOperand - }, - - /// Get home object internal slot of an object literal method. - /// - /// - Registers (inout): - /// - function: - /// - in: `JsObject`. - /// - out: `JsObject` or `null` if the home object is not set. - GetHomeObject { - function: RegisterOperand, - }, - - /// Set the prototype of an object if the value is an object or null. - /// - /// - Registers (in): - /// - object: `JsObject`. - /// - prototype: `JsObject` or `null` - SetPrototype { - object: RegisterOperand, - prototype: RegisterOperand - }, - - /// Get the prototype of an object. - /// - /// - Registers (inout): - /// - object: - /// - in: `JsObject`. - /// - out: `JsObject` or `null`. - GetPrototype { - object: RegisterOperand, - }, - - /// Store an empty array in dst. - /// - /// - Registers: - /// - Output: dst - StoreNewArray { dst: RegisterOperand }, - - /// Push a value to an array. - /// - /// - Registers: - /// - Input: array, value - PushValueToArray { value: RegisterOperand, array: RegisterOperand }, - - /// Push an empty element/hole to an array. - /// - /// - Registers: - /// - Input: array - PushElisionToArray { array: RegisterOperand }, - - /// Push all iterator values to an array. - /// - /// - Registers: - /// - Input: array - PushIteratorToArray { array: RegisterOperand }, - - /// Binary `+` operator. - /// - /// - Registers - /// - Input: lhs, rhs - /// - Output: dst - Add { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `-` operator. - /// - /// - Registers - /// - Input: lhs, rhs - /// - Output: dst - Sub { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `/` operator. - /// - /// - Registers - /// - Input: lhs, rhs - /// - Output: dst - Div { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `*` operator. - /// - /// - Registers - /// - Input: lhs, rhs - /// - Output: dst - Mul { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `%` operator. - /// - /// - Registers - /// - Input: lhs, rhs - /// - Output: dst - Mod { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `**` operator. - /// - /// - Registers - /// - Input: lhs, rhs - /// - Output: dst - Pow { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `>>` operator. - /// - /// - Registers - /// - Input: lhs, rhs - /// - Output: dst - ShiftRight { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `<<` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - ShiftLeft { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `>>>` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - UnsignedShiftRight { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary bitwise `|` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - BitOr { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary bitwise `&` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - BitAnd { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary bitwise `^` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - BitXor { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Unary bitwise `~` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - BitNot { value: RegisterOperand }, - - /// Binary `in` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - In { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `in` operator for private names. - /// - /// - Operands: index: `u32` - /// - Registers: - /// - Input: rhs - /// - Output: dst - InPrivate { dst: RegisterOperand, index: IndexOperand, rhs: RegisterOperand }, - - /// Binary `==` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - Eq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `===` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - StrictEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `!=` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - NotEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `!==` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - StrictNotEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `>` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - GreaterThan { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `>=` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - GreaterThanOrEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `<` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - LessThan { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `<=` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - LessThanOrEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary `instanceof` operator. - /// - /// - Registers: - /// - Input: lhs, rhs - /// - Output: dst - InstanceOf { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Binary logical `&&` operator. - /// - /// This is a short-circuit operator, if the `lhs` value is `false`, then it jumps to `exit` address. - /// - /// - Operands: - /// - address: `u32` - /// - Registers: - /// - Input: value - /// - Output: value - LogicalAnd { address: Address, value: RegisterOperand }, - - /// Binary logical `||` operator. - /// - /// This is a short-circuit operator, if the `lhs` value is `true`, then it jumps to `exit` address. - /// - /// - Operands: - /// - address: `Address` - /// - Registers: - /// - Input: value - /// - Output: value - LogicalOr { address: Address, value: RegisterOperand }, +macro_rules! for_each_opcode { + ($m:ident) => { + $m! { + /// Pop the top value from the stack. + /// + /// - Stack: value **=>** + Pop, + + /// Store integer `0` in dst. + /// + /// - Registers: + /// - Output: dst + StoreZero { dst: RegisterOperand }, + + /// Store integer `1` in dst. + /// + /// - Registers: + /// - Output: dst + StoreOne { dst: RegisterOperand }, + + /// Store `i8` value in dst. + /// + /// - Operands: + /// - value: `i8` + /// - Registers: + /// - Output: dst + StoreInt8 { dst: RegisterOperand, value: i8 }, + + /// Store `i16` value in dst. + /// + /// - Operands: + /// - value: `i16` + /// - Registers: + /// - Output: dst + StoreInt16 { dst: RegisterOperand, value: i16 }, + + /// Store `i32` value in dst. + /// + /// - Operands: + /// - value: `i32` + /// - Registers: + /// - Output: dst + StoreInt32 { dst: RegisterOperand, value: i32 }, + + /// Store `f32` value in dst. + /// + /// - Operands: + /// - value: `f32` + /// - Registers: + /// - Output: dst + StoreFloat { dst: RegisterOperand, value: f32 }, + + /// Store `f64` value in dst. + /// + /// - Operands: + /// - value: `f64` + /// - Registers: + /// - Output: dst + StoreDouble { dst: RegisterOperand, value: f64 }, + + /// Store `NaN` in dst. + /// + /// - Registers: + /// - Output: dst + StoreNan { dst: RegisterOperand }, + + /// Store `Infinity` in dst. + /// + /// - Registers: + /// - Output: dst + StorePositiveInfinity { dst: RegisterOperand }, + + /// Store `-Infinity` in dst. + /// + /// - Registers: + /// - Output: dst + StoreNegativeInfinity { dst: RegisterOperand }, + + /// Store `null` in dst. + /// + /// - Registers: + /// - Output: dst + StoreNull { dst: RegisterOperand }, + + /// Store `true` in dst. + /// + /// - Registers: + /// - Output: dst + StoreTrue { dst: RegisterOperand }, + + /// Store `false` in dst. + /// + /// - Registers: + /// - Output: dst + StoreFalse { dst: RegisterOperand }, + + /// Store `undefined` in dst. + /// + /// - Registers: + /// - Output: dst + StoreUndefined { dst: RegisterOperand }, + + /// Store literal value in dst. + /// + /// Like strings and bigints. The index operand is used to index into the `literals` + /// array to get the value. + /// + /// - Operands: + /// - index: `IndexOperand` + /// - Registers: + /// - Output: dst + StoreLiteral { dst: RegisterOperand, index: IndexOperand }, + + /// Store regexp value in dst. + /// + /// - Operands: + /// - pattern_index: `IndexOperand` + /// - flags: `IndexOperand` + /// - Registers: + /// - Output: dst + StoreRegexp { dst: RegisterOperand, pattern_index: IndexOperand, flags_index: IndexOperand }, + + /// Store empty object `{}` in dst. + /// + /// - Registers: + /// - Output: dst + StoreEmptyObject { dst: RegisterOperand }, + + /// Get the prototype of a superclass and store it in dst. + /// + /// Additionally this sets the `[[prototype]]` of the class and the `DERIVED` flag. + /// + /// - Registers: + /// - Input: class, superclass + /// - Output: dst + StoreClassPrototype { + dst: RegisterOperand, + class: RegisterOperand, + superclass: RegisterOperand + }, + + /// Set the prototype of a class object. + /// + /// - Registers: + /// - Input: class, prototype + /// - Output: dst + SetClassPrototype { + dst: RegisterOperand, + prototype: RegisterOperand, + class: RegisterOperand + }, + + /// Set home object internal slot of an object literal method. + /// + /// - Registers: + /// - Input: function, home + SetHomeObject { + function: RegisterOperand, + home: RegisterOperand + }, + + /// Get home object internal slot of an object literal method. + /// + /// - Registers (inout): + /// - function: + /// - in: `JsObject`. + /// - out: `JsObject` or `null` if the home object is not set. + GetHomeObject { + function: RegisterOperand, + }, + + /// Set the prototype of an object if the value is an object or null. + /// + /// - Registers (in): + /// - object: `JsObject`. + /// - prototype: `JsObject` or `null` + SetPrototype { + object: RegisterOperand, + prototype: RegisterOperand + }, + + /// Get the prototype of an object. + /// + /// - Registers (inout): + /// - object: + /// - in: `JsObject`. + /// - out: `JsObject` or `null`. + GetPrototype { + object: RegisterOperand, + }, + + /// Store an empty array in dst. + /// + /// - Registers: + /// - Output: dst + StoreNewArray { dst: RegisterOperand }, + + /// Push a value to an array. + /// + /// - Registers: + /// - Input: array, value + PushValueToArray { value: RegisterOperand, array: RegisterOperand }, + + /// Push an empty element/hole to an array. + /// + /// - Registers: + /// - Input: array + PushElisionToArray { array: RegisterOperand }, + + /// Push all iterator values to an array. + /// + /// - Registers: + /// - Input: array + PushIteratorToArray { array: RegisterOperand }, + + /// Binary `+` operator. + /// + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Add { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `-` operator. + /// + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Sub { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `/` operator. + /// + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Div { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `*` operator. + /// + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Mul { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `%` operator. + /// + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Mod { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `**` operator. + /// + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Pow { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `>>` operator. + /// + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + ShiftRight { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `<<` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + ShiftLeft { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `>>>` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + UnsignedShiftRight { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary bitwise `|` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + BitOr { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary bitwise `&` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + BitAnd { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary bitwise `^` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + BitXor { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Unary bitwise `~` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + BitNot { value: RegisterOperand }, + + /// Binary `in` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + In { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `in` operator for private names. + /// + /// - Operands: index: `u32` + /// - Registers: + /// - Input: rhs + /// - Output: dst + InPrivate { dst: RegisterOperand, index: IndexOperand, rhs: RegisterOperand }, + + /// Binary `==` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + Eq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `===` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + StrictEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `!=` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + NotEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `!==` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + StrictNotEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `>` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + GreaterThan { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `>=` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + GreaterThanOrEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `<` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + LessThan { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `<=` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + LessThanOrEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary `instanceof` operator. + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + InstanceOf { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Binary logical `&&` operator. + /// + /// This is a short-circuit operator, if the `lhs` value is `false`, then it jumps to `exit` address. + /// + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Input: value + /// - Output: value + LogicalAnd { address: Address, value: RegisterOperand }, + + /// Binary logical `||` operator. + /// + /// This is a short-circuit operator, if the `lhs` value is `true`, then it jumps to `exit` address. + /// + /// - Operands: + /// - address: `Address` + /// - Registers: + /// - Input: value + /// - Output: value + LogicalOr { address: Address, value: RegisterOperand }, + + /// Binary `??` operator. + /// + /// This is a short-circuit operator, if the `lhs` value is **not** `null` or `undefined`, + /// then it jumps to `exit` address. + /// + /// - Operands: + /// - address: `Address` + /// - Registers: + /// - Input: value + /// - Output: value + Coalesce { address: Address, value: RegisterOperand }, + + /// Unary `typeof` operator. + /// + /// - Registers: + /// - Input: value + /// - Output: value + TypeOf { value: RegisterOperand }, + + /// Unary logical `!` operator. + /// + /// - Registers: + /// - Input: value + /// - Output: value + LogicalNot { value: RegisterOperand }, + + /// Unary `+` operator. + /// + /// - Registers: + /// - Input: value + /// - Output: value + Pos { value: RegisterOperand }, + + /// Unary `-` operator. + /// + /// - Registers: + /// - Input: value + /// - Output: value + Neg { value: RegisterOperand }, + + /// Unary `++` operator. + /// + /// - Registers: + /// - Input: src + /// - Output: dst + Inc { dst: RegisterOperand, src: RegisterOperand }, + + /// Unary `--` operator. + /// + /// - Registers: + /// - Input: src + /// - Output: dst + Dec { dst: RegisterOperand, src: RegisterOperand }, + + /// Declare `var` type variable. + /// + /// - Operands: + /// - binding_index: `IndexOperand` + DefVar { binding_index: IndexOperand }, + + /// Declare and initialize `var` type variable. + /// + /// - Operands: + /// - binding_index: `IndexOperand` + /// - Registers: + /// - Input: src + DefInitVar { src: RegisterOperand, binding_index: IndexOperand }, + + /// Initialize a lexical binding. + /// + /// - Operands: + /// - binding_index: `IndexOperand` + /// - Registers: + /// - Input: src + PutLexicalValue { src: RegisterOperand, binding_index: IndexOperand }, + + /// Throws an error because the binding access is illegal. + /// + /// - Operands: + /// -index: `IndexOperand` + ThrowMutateImmutable { index: IndexOperand }, + + /// Get i-th argument of the current frame. + /// + /// Returns `undefined` if `arguments.len()` < `index`. + /// + /// - Operands: + /// - index: `IndexOperand` + /// - Registers: + /// - Output: dst + GetArgument { index: IndexOperand, dst: RegisterOperand }, + + /// Find a binding on the environment chain and store its value in dst. + /// + /// - Operands: + /// - binding_index: `IndexOperand` + /// - Registers: + /// - Output: dst + GetName { dst: RegisterOperand, binding_index: IndexOperand }, + + /// Find a binding in the global object and store its value in dst. + /// + /// - Operands: + /// - binding_index: `IndexOperand` + /// - ic_index: `IndexOperand` + /// - Registers: + /// - Output: dst + GetNameGlobal { dst: RegisterOperand, binding_index: IndexOperand, ic_index: IndexOperand }, + + /// Find a binding on the environment and set the `current_binding` of the current frame. + /// + /// - Operands: + /// - binding_index: `IndexOperand` + GetLocator { binding_index: IndexOperand }, + + /// Find a binding on the environment chain and store its value in dst, and push its + /// `BindingLocator` to the `bindings_stack`. + /// + /// - Operands: + /// - binding_index: `IndexOperand` + /// - Registers: + /// - Output: dst + GetNameAndLocator { dst: RegisterOperand, binding_index: IndexOperand }, + + /// Find a binding on the environment chain and store its value in dst. If the binding does not exist, store undefined. + /// + /// - Operands: + /// - binding_index: `IndexOperand` + /// - Registers: + /// - Output: dst + GetNameOrUndefined { dst: RegisterOperand, binding_index: IndexOperand }, + + /// Find a binding on the environment chain and assign its value. + /// + /// - Operands: + /// - binding_index: `IndexOperand` + /// - Registers: + /// - Input: src + SetName { src: RegisterOperand, binding_index: IndexOperand }, + + /// Assigns a value to the binding pointed by the top of the `bindings_stack`. + /// + /// - Registers: + /// - Input: src + SetNameByLocator { src: RegisterOperand }, + + /// Deletes a property of the global object. + /// + /// - Operands: + /// - binding_index: `IndexOperand` + /// - Registers: + /// - Output: dst + DeleteName { dst: RegisterOperand, binding_index: IndexOperand }, + + /// Gets a method from an object, or `undefined` if the method does not exist. + /// + /// Operands: + /// - name_index: constant `JsString`. + /// + /// Registers (inout) + /// - object: `JsObject` as input, `JsObject` or `undefined` as output. + GetMethod { object: RegisterOperand, name_index: IndexOperand }, + + /// Get the length property by name from an object. + /// + /// Like `object.name` + /// + /// - Operands: + /// - ic_index: `IndexOperand` + /// - Registers: + /// - Input: value + /// - Output: dst + GetLengthProperty { + dst: RegisterOperand, + value: RegisterOperand, + ic_index: IndexOperand + }, + + /// Get a property by name from an object. + /// + /// Like `object.name` + /// + /// - Operands: + /// - ic_index: `IndexOperand` + /// - Registers: + /// - Input: value + /// - Output: dst + GetPropertyByName { + dst: RegisterOperand, + value: RegisterOperand, + ic_index: IndexOperand + }, + + /// Get a property by name from an object. + /// + /// Like `object.name` + /// + /// - Operands: + /// - ic_index: `IndexOperand` + /// - Registers: + /// - Input: receiver, value + /// - Output: dst + GetPropertyByNameWithThis { + dst: RegisterOperand, + receiver: RegisterOperand, + value: RegisterOperand, + ic_index: IndexOperand + }, + + /// Get a property by value from an object. + /// + /// Like `object[key]` + /// + /// - Registers: + /// - Input: object, receiver, key + /// - Output: dst + GetPropertyByValue { + dst: RegisterOperand, + key: RegisterOperand, + receiver: RegisterOperand, + object: RegisterOperand + }, + + /// Get a property by value from an object. + /// + /// Like `object[key]` + /// + /// - Registers: + /// - Input: object, receiver + /// - Output: dst, key + GetPropertyByValuePush { + dst: RegisterOperand, + key: RegisterOperand, + receiver: RegisterOperand, + object: RegisterOperand + }, + + /// Sets a property by name of an object. + /// + /// Like `object.name = value` + /// + /// - Operands: + /// - ic_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + SetPropertyByName { + value: RegisterOperand, + object: RegisterOperand, + ic_index: IndexOperand + }, + + /// Sets a property by name of an object. + /// + /// Like `object.name = value` + /// + /// - Operands: + /// - ic_index: `IndexOperand` + /// - Registers: + /// - Input: object,receiver, value + SetPropertyByNameWithThis { + value: RegisterOperand, + receiver: RegisterOperand, + object: RegisterOperand, + ic_index: IndexOperand + }, + + /// Sets the name of a function object. + /// + /// This operation is corresponds to the `SetFunctionName` abstract operation in the [spec]. + /// + /// [spec]: https://tc39.es/ecma262/#sec-setfunctionname + /// + /// - Operands: + /// - prefix + /// - 0: no prefix + /// - 1: "get " + /// - 2: "set " + /// - Registers: + /// - Input: function, name + SetFunctionName { function: RegisterOperand, name: RegisterOperand, prefix: IndexOperand }, + + /// Defines a own property of an object by name. + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + DefineOwnPropertyByName { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Defines a static class method by name. + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + DefineClassStaticMethodByName { + value: RegisterOperand, + object: RegisterOperand, + name_index: IndexOperand + }, + + /// Defines a class method by name. + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + DefineClassMethodByName { + value: RegisterOperand, + object: RegisterOperand, + name_index: IndexOperand + }, + + /// Sets a property by value of an object. + /// + /// Like `object[key] = value` + /// + /// - Registers: + /// - Input: value, key, receiver, object + SetPropertyByValue { + value: RegisterOperand, + key: RegisterOperand, + receiver: RegisterOperand, + object: RegisterOperand + }, + + /// Defines a own property of an object by value. + /// + /// - Registers: + /// - Input: object, key, value + DefineOwnPropertyByValue { + value: RegisterOperand, + key: RegisterOperand, + object: RegisterOperand + }, + + /// Defines a static class method by value. + /// + /// - Registers: + /// - Input: object, key, value + DefineClassStaticMethodByValue { + value: RegisterOperand, + key: RegisterOperand, + object: RegisterOperand + }, + + /// Defines a class method by value. + /// + /// - Registers: + /// - Input: object, key, value + DefineClassMethodByValue { + value: RegisterOperand, + key: RegisterOperand, + object: RegisterOperand + }, + + /// Sets a getter property by name of an object. + /// + /// Like `get name() value` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + SetPropertyGetterByName { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Defines a static getter class method by name. + /// + /// Like `static get name() value` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + DefineClassStaticGetterByName { + value: RegisterOperand, + object: RegisterOperand, + name_index: IndexOperand + }, + + /// Defines a getter class method by name. + /// + /// Like `get name() value` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + DefineClassGetterByName { + value: RegisterOperand, + object: RegisterOperand, + name_index: IndexOperand + }, + + /// Sets a getter property by value of an object. + /// + /// Like `get [key]() value` + /// + /// - Registers: + /// - Input: object, key, value + SetPropertyGetterByValue { + value: RegisterOperand, + key: RegisterOperand, + object: RegisterOperand + }, + + /// Defines a static getter class method by value. + /// + /// Like `static get [key]() value` + /// + /// - Registers: + /// - Input: object, key, value + DefineClassStaticGetterByValue { + value: RegisterOperand, + key: RegisterOperand, + object: RegisterOperand + }, + + /// Defines a getter class method by value. + /// + /// Like `get [key]() value` + /// + /// - Registers: + /// - Input: object, key, value + DefineClassGetterByValue { + value: RegisterOperand, + key: RegisterOperand, + object: RegisterOperand + }, + + /// Sets a setter property by name of an object. + /// + /// Like `set name() value` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + SetPropertySetterByName { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Defines a static setter class method by name. + /// + /// Like `static set name() value` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + DefineClassStaticSetterByName { + value: RegisterOperand, + object: RegisterOperand, + name_index: IndexOperand + }, + + /// Defines a setter class method by name. + /// + /// Like `set name() value` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + DefineClassSetterByName { + value: RegisterOperand, + object: RegisterOperand, + name_index: IndexOperand + }, + + /// Sets a setter property by value of an object. + /// + /// Like `set [key]() value` + /// + /// - Registers: + /// - Input: object, key, value + SetPropertySetterByValue { + value: RegisterOperand, + key: RegisterOperand, + object: RegisterOperand + }, + + /// Defines a static setter class method by value. + /// + /// Like `static set [key]() value` + /// + /// - Registers: + /// - Input: object, key, value + DefineClassStaticSetterByValue { + value: RegisterOperand, + key: RegisterOperand, + object: RegisterOperand + }, + + /// Defines a setter class method by value. + /// + /// Like `set [key]() value` + /// + /// - Registers: + /// - Input: object, key, value + DefineClassSetterByValue { + value: RegisterOperand, + key: RegisterOperand, + object: RegisterOperand + }, + + /// Set the value of a private property of an object by it's name. + /// + /// Like `obj.#name = value` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + SetPrivateField { value: RegisterOperand, object: RegisterOperand, name_index: IndexOperand }, + + /// Define a private property of a class constructor by it's name. + /// + /// Like `#name = value` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + DefinePrivateField { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Set a private method of a class constructor by it's name. + /// + /// Like `#name() {}` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + SetPrivateMethod { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Set a private setter property of a class constructor by it's name. + /// + /// Like `set #name() {}` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + SetPrivateSetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Set a private getter property of a class constructor by it's name. + /// + /// Like `get #name() {}` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + SetPrivateGetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Get a private property by name from an object and store it in dst. + /// + /// Like `object.#name` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object + /// - Output: dst + GetPrivateField { dst: RegisterOperand, object: RegisterOperand, name_index: IndexOperand }, + + /// Push a field to a class. + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - is_anonymous_function: `bool` + /// - Registers: + /// - Input: object, value + PushClassField { object: RegisterOperand, name: RegisterOperand, value: RegisterOperand, is_anonymous_function: IndexOperand }, + + /// Push a private field to the class. + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + PushClassFieldPrivate { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Push a private getter to the class. + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + PushClassPrivateGetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Push a private setter to the class. + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, value + PushClassPrivateSetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Push a private method to the class. + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object, proto, value + PushClassPrivateMethod { object: RegisterOperand, proto: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, + + /// Deletes a property by name of an object. + /// + /// Like `delete object.key` + /// + /// - Operands: + /// - name_index: `IndexOperand` + /// - Registers: + /// - Input: object + DeletePropertyByName { object: RegisterOperand, name_index: IndexOperand }, + + /// Deletes a property by value of an object. + /// + /// Like `delete object[key]` + /// + /// - Registers: + /// - Input: object, key + DeletePropertyByValue { object: RegisterOperand, key: RegisterOperand }, + + /// Throws an error when trying to delete a property of `super` + DeleteSuperThrow, + + /// Copy all properties of one object to another object. + /// + /// - Registers: + /// - Input: object, source, excluded_keys + CopyDataProperties { object: RegisterOperand, source: RegisterOperand, excluded_keys: ThinVec }, + + /// Call ToPropertyKey on the value on the stack. + /// + /// - Registers: + /// - Input: src + /// - Output: dst + ToPropertyKey { src: RegisterOperand, dst: RegisterOperand }, + + /// Unconditional jump to address. + /// + /// - Operands: + /// - address: `u32` + Jump { address: Address }, + + /// Conditional jump to address. + /// + /// If the value popped is [`truthy`][truthy] then jump to `address`. + /// + /// - Operands: + /// - address: `Address` + /// - Registers (in): + /// - `value`: `JsValue` + /// + /// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy + JumpIfTrue { address: Address, value: RegisterOperand }, + + /// Conditional jump to address. + /// + /// If the value popped is [`falsy`][falsy] then jump to `address`. + /// + /// - Operands: + /// - address: `Address` + /// - Registers (in): + /// - `value`: `JsValue` + /// + /// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy + JumpIfFalse { address: Address, value: RegisterOperand }, + + /// Conditional jump to address. + /// + /// If the value popped is not undefined jump to `address`. + /// + /// - Operands: + /// - address: `Address`. + /// - Registers (in): + /// - value: `JsValue` + JumpIfNotUndefined { address: Address, value: RegisterOperand }, + + /// Conditional jump to address. + /// + /// If the value popped is undefined jump to `address`. + /// + /// - Operands: + /// - address: `Address`. + /// - Registers (in): + /// - value: `JsValue`. + JumpIfNullOrUndefined { address: Address, value: RegisterOperand }, + + /// Fused `<` comparison + conditional jump. + /// + /// Jumps to `address` if `!(lhs < rhs)`. + /// + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Input: lhs, rhs + JumpIfNotLessThan { address: Address, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Fused `<=` comparison + conditional jump. + /// + /// Jumps to `address` if `!(lhs <= rhs)`. + /// + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Input: lhs, rhs + JumpIfNotLessThanOrEqual { address: Address, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Fused `>` comparison + conditional jump. + /// + /// Jumps to `address` if `!(lhs > rhs)`. + /// + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Input: lhs, rhs + JumpIfNotGreaterThan { address: Address, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Fused `>=` comparison + conditional jump. + /// + /// Jumps to `address` if `!(lhs >= rhs)`. + /// + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Input: lhs, rhs + JumpIfNotGreaterThanOrEqual { address: Address, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Conditional jump to address. + /// + /// Jump to `address` if two values are not equal. + /// + /// - Operands: + /// - address: `Address` + /// - Registers (in): + /// - lhs: `JsValue`. + /// - rhs: `JsValue`. + JumpIfNotEqual { address: Address, lhs: RegisterOperand, rhs: RegisterOperand }, + + /// Jump table that jumps depending on top value of the stack. + /// + /// This is used to handle special cases when we call `continue`, `break` or `return` in a try block, + /// that has finally block. + /// + /// Operands: index: Register, count: `u32`, address: `Address` * count + JumpTable { index: u32, addresses: ThinVec
}, + + /// Throw exception. + /// + /// This sets pending exception and searches for an exception handler. + /// + /// - Registers: + /// - Input: src + Throw { src: RegisterOperand }, + + /// Rethrow thrown exception. + /// + /// This is also used to handle generator `return()` call, we throw an empty exception, by setting pending exception to [`None`], + /// propagating it and calling finally code until there is no exception handler left, in that case we consume the empty exception and return + /// from the generator. + ReThrow, + + /// Get the thrown pending exception (if it's set) and store it in dst. + /// + /// If there is no pending exception, which can happen if we are handling `return()` call on generator, + /// then we rethrow the empty exception. See [`Opcode::ReThrow`]. + /// + /// - Registers: + /// - Output: dst + Exception { dst: RegisterOperand }, + + /// Get the thrown pending exception if it's set and store `true` in `has_exception`, otherwise store `false`. + /// + /// - Registers: + /// - Output: exception, has_exception + MaybeException { has_exception: RegisterOperand, exception: RegisterOperand }, + + /// Throw a new `TypeError` exception + /// + /// - Operands: + /// - message: `IndexOperand` + ThrowNewTypeError { message: IndexOperand }, + + /// Throw a new `ReferenceError` exception + /// + /// - Operands: + /// - message: `IndexOperand` + ThrowNewReferenceError { message: IndexOperand }, + + /// Gets the function object of the current function environment + /// + /// - Registers (out): + /// - function_object: `JsObject`. + GetFunctionObject { function_object: RegisterOperand }, + + /// Pushes `this` value + /// + /// - Registers: + /// - Output: dst + This { dst: RegisterOperand }, + + /// Pushes `this` value that is related to the object environment of the given binding + /// + /// - Operands: + /// - index: `IndexOperand` + /// - Registers: + /// - Output: dst + ThisForObjectEnvironmentName { dst: RegisterOperand, index: IndexOperand }, + + /// Execute the `super()` method. + /// + /// - Operands: + /// - argument_count: `IndexOperand` + /// - Stack: super_constructor, argument_1, ... argument_n **=>** + SuperCall { argument_count: IndexOperand }, + + /// Execute the `super()` method where the arguments contain spreads. + /// + /// Operands: + /// + /// Stack: super_constructor, arguments_array **=>** + SuperCallSpread, + + /// Execute the `super()` method when no constructor of the class is defined. + /// + /// Operands: + /// + /// Stack: argument_n, ... argument_1 **=>** + SuperCallDerived, + + /// Binds `this` value and initializes the instance elements. + /// + /// Performs steps 7-12 of [`SuperCall: super Arguments`][spec] + /// + /// - Registers: + /// - Input: value + /// - Output: value + /// + /// [spec]: https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation + BindThisValue { value: RegisterOperand }, - /// Binary `??` operator. - /// - /// This is a short-circuit operator, if the `lhs` value is **not** `null` or `undefined`, - /// then it jumps to `exit` address. + /// Dynamically import a module. /// /// - Operands: - /// - address: `Address` - /// - Registers: - /// - Input: value - /// - Output: value - Coalesce { address: Address, value: RegisterOperand }, - - /// Unary `typeof` operator. - /// - /// - Registers: - /// - Input: value - /// - Output: value - TypeOf { value: RegisterOperand }, - - /// Unary logical `!` operator. - /// - /// - Registers: - /// - Input: value - /// - Output: value - LogicalNot { value: RegisterOperand }, - - /// Unary `+` operator. - /// - /// - Registers: - /// - Input: value - /// - Output: value - Pos { value: RegisterOperand }, - - /// Unary `-` operator. - /// - /// - Registers: - /// - Input: value - /// - Output: value - Neg { value: RegisterOperand }, - - /// Unary `++` operator. - /// - /// - Registers: - /// - Input: src - /// - Output: dst - Inc { dst: RegisterOperand, src: RegisterOperand }, - - /// Unary `--` operator. - /// + /// - phase: `IndexOperand` (0 = evaluation, 1 = defer, 2 = source) /// - Registers: - /// - Input: src - /// - Output: dst - Dec { dst: RegisterOperand, src: RegisterOperand }, + /// - Input: specifier, options + /// - Output: specifier + ImportCall { specifier: RegisterOperand, options: RegisterOperand, phase: IndexOperand }, - /// Declare `var` type variable. - /// - /// - Operands: - /// - binding_index: `IndexOperand` - DefVar { binding_index: IndexOperand }, + /// Strict equal compare two register values, + /// if true jumps to address. + /// - Operands: + /// - address: `Address` + /// - Registers: + /// - Input: value, condition + Case { address: Address, value: RegisterOperand, condition: RegisterOperand }, + + /// Get function from the pre-compiled inner functions. + /// + /// - Operands: + /// - index: `IndexOperand` + /// - Registers: + /// - Output: dst + GetFunction { dst: RegisterOperand, index: IndexOperand }, + + /// Call a function named "eval". + /// + /// - Operands: + /// - argument_count: `IndexOperand` + /// - scope_index: `IndexOperand` + /// - Stack: this, func, argument_1, ... argument_n **=>** result + CallEval { argument_count: IndexOperand, scope_index: IndexOperand }, + + /// Call a function named "eval" where the arguments contain spreads. + /// + /// - Operands: + /// - scope_index: `IndexOperand` + /// - Stack: Stack: this, func, arguments_array **=>** result + CallEvalSpread { scope_index: IndexOperand }, + + /// Call a function. + /// + /// - Operands: + /// - argument_count: `IndexOperand` + /// - Stack: this, func, argument_1, ... argument_n **=>** result + Call { argument_count: IndexOperand }, + + /// Call a function where the arguments contain spreads. + /// + /// Operands: + /// + /// Stack: this, func, arguments_array **=>** result + CallSpread, + + /// Call construct on a function. + /// + /// - Operands: + /// - argument_count: `IndexOperand` + /// - Stack: this, func, argument_1, ... argument_n **=>** result + New { argument_count: IndexOperand }, + + /// Call construct on a function where the arguments contain spreads. + /// + /// Operands: + /// + /// Stack: arguments_array, func **=>** result + NewSpread, + + /// Check return from a function. + CheckReturn, + + /// Return from a function. + Return, + + /// Close an async generator function. + AsyncGeneratorClose, + + /// Creates the Generator object and yields. + /// + /// - Stack: **=>** resume_kind + Generator, + + /// Creates the AsyncGenerator object and yields. + /// + /// - Stack: **=>** resume_kind + AsyncGenerator, + + /// Set return value of a function. + /// + /// - Registers: + /// - Input: src + SetAccumulator { src: RegisterOperand }, + + // Set return value of a function. + /// + /// - Registers: + /// - Output: dst + SetRegisterFromAccumulator { dst: RegisterOperand }, + + /// Move value of operand `src` to register `dst`. + /// + /// - Registers: + /// - Input: src + /// - Output: dst + Move { dst: RegisterOperand, src: RegisterOperand }, + + /// Pop value from the stack and push to register `dst` + /// + /// - Registers: + /// - Output: dst + PopIntoRegister { dst: RegisterOperand }, + + /// Copy value at register `src` and push it on the stack. + /// + /// - Registers: + /// - Input: src + PushFromRegister { src: RegisterOperand }, + + /// Push a declarative environment. + /// + /// - Operands: + /// - scope_index: `IndexOperand` + PushScope { scope_index: IndexOperand }, + + /// Push an object environment. + /// + /// - Registers: + /// - Input: src + PushObjectEnvironment { src: RegisterOperand }, + + /// Pop the current environment. + PopEnvironment, + + /// Increment loop iteration count. + /// + /// Used for limiting the loop iteration. + IncrementLoopIteration, + + /// Creates the ForInIterator of an object. + /// + /// - Registers: + /// - Input: src + CreateForInIterator { src: RegisterOperand }, + + /// Gets the iterator of an object. + /// + /// - Registers: + /// - Input: src + /// - Iterator Stack: **=>** `iterator` + GetIterator { src: RegisterOperand }, + + /// Gets the async iterator of an object. + /// + /// - Registers: + /// - Input: src + /// - Iterator Stack: **=>** `iterator` + GetAsyncIterator { src: RegisterOperand }, + + /// Pop an iterator from the iterators stack + /// - Registers (out) + /// - iterator: `JsObject`. + /// - next: `JsValue`. + IteratorPop { iterator: RegisterOperand, next: RegisterOperand }, + + /// Pushes an iterator on the iterators stack + /// - Registers (in) + /// - iterator: `JsObject`. + /// - next: `JsValue`. + IteratorPush { iterator: RegisterOperand, next: RegisterOperand }, + + /// Calls the `next` method of `iterator`, updating its record with the next value. + /// + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorNext, + + /// Updates the result of the currently active iterator. + /// - Registers (inout) + /// - result: `JsValue` (in), `bool` (out) with the `done` value of the iterator. + IteratorUpdateResult { result: RegisterOperand }, + + /// Returns `true` if the current iterator is done, or `false` otherwise + /// + /// - Registers: + /// - Output: dst + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorDone { dst: RegisterOperand }, + + /// Finishes the call to `Opcode::IteratorNext` within a `for await` loop by setting the current + /// result of the current iterator. + /// + /// - Registers: + /// - Input: resume_kind, value + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorFinishAsyncNext { resume_kind: RegisterOperand, value: RegisterOperand }, + + /// Gets the `value` property of the current iterator record. + /// + /// - Registers: + /// - Output: dst + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorValue { dst: RegisterOperand }, + + /// Gets the last iteration result of the current iterator record. + /// + /// - Registers: + /// - Output: dst + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorResult { dst: RegisterOperand }, + + /// Consume the iterator and construct and array with all the values. + /// + /// - Registers: + /// - Output: dst + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorToArray { dst: RegisterOperand }, + + /// Store `true` in dst if the iterator stack is empty. + /// + /// - Registers: + /// - Output: dst + /// - Iterator Stack: **=>** + IteratorStackEmpty { dst: RegisterOperand }, + + /// Creates a new iterator result object. + /// + /// - Operands: + /// - done: `bool` (codified as u8 with `0` -> `false` and `!0` -> `true`) + /// - Registers: + /// - Input: value + /// - Output: value + CreateIteratorResult { value: RegisterOperand, done: IndexOperand }, + + /// Calls `return` on the current iterator and returns the result. + /// + /// - Registers: + /// - Output: value, called + /// - Iterator Stack: `iterator` **=>** + IteratorReturn { value: RegisterOperand, called: RegisterOperand }, + + /// Concat multiple stack objects into a string. + /// + /// - Registers: + /// - Input: values + /// - Output: dst + ConcatToString { dst: RegisterOperand, values: ThinVec }, + + /// Require the stack value to be neither null nor undefined. + /// + /// - Registers: + /// - Input: src + ValueNotNullOrUndefined { src: RegisterOperand }, + + /// Initialize the rest parameter value of a function from the remaining arguments. + /// + /// - Stack: `argument_1` .. `argument_n` **=>** + /// - Registers: + /// - Output: dst + RestParameterInit { dst: RegisterOperand }, + + /// Yields from the current generator execution. + /// + /// - Registers: + /// - Input: src + /// - Output: resume_kind, received + GeneratorYield { src: RegisterOperand }, + + /// Yields from the current async generator execution. + /// + /// - Registers: + /// - Input: src + /// - Output: resume_kind, received + AsyncGeneratorYield { src: RegisterOperand }, + + /// Create a promise capacity for an async function, if not already set. + CreatePromiseCapability, + + /// Stops the current async function and schedules it to resume later. + /// + /// - Registers: + /// - Input: src + /// - Output: resume_kind, received + Await { src: RegisterOperand }, + + /// Store the current new target in dst. + /// + /// - Registers: + /// - Output: dst + NewTarget { dst: RegisterOperand }, + + /// Store the current `import.meta` in dst. + /// + /// - Registers: + /// - Output: dst + ImportMeta { dst: RegisterOperand }, + + /// Store `true` in the register if the value is an object, or `false` otherwise. + /// + /// - Registers: + /// - Input: value + /// - Output: value + IsObject { value: RegisterOperand }, + + /// Lookup if a tagged template object is cached and skip the creation if it is. + /// + /// - Operands: + /// - address: `u32` + /// - site: `u64` + /// - Registers: + /// - Output: dst + TemplateLookup { address: Address, site: u64, dst: RegisterOperand }, + + /// Create a new tagged template object and cache it. + /// + /// - Operands: + /// - site: `u64` + /// - Registers: + /// - Inputs: values + /// - Output: dst + TemplateCreate { site: u64, dst: RegisterOperand, values: ThinVec }, + + /// Push a private environment. + /// + /// Operands: count: `u32`, count * name_index: `u32` + /// + /// - Registers: + /// - Input: class, name_indices + PushPrivateEnvironment { class: RegisterOperand, name_indices: ThinVec }, + + /// Pop a private environment. + PopPrivateEnvironment, + + /// Creates a mapped `arguments` object. + /// + /// Performs [`10.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env )`] + /// + /// [spec]: https://tc39.es/ecma262/#sec-createunmappedargumentsobject + /// + /// - Registers: + /// - Output: dst + CreateMappedArgumentsObject { dst: RegisterOperand }, + + /// Creates an unmapped `arguments` object. + /// + /// Performs: [`10.4.4.6 CreateUnmappedArgumentsObject ( argumentsList )`] + /// + /// [spec]: https://tc39.es/ecma262/#sec-createmappedargumentsobject + /// + /// - Registers: + /// - Output: dst + CreateUnmappedArgumentsObject { dst: RegisterOperand }, + + /// Reserved [`Opcode`]. + Reserved1 => Reserved, + /// Reserved [`Opcode`]. + Reserved2 => Reserved, + /// Reserved [`Opcode`]. + Reserved3 => Reserved, + /// Reserved [`Opcode`]. + Reserved4 => Reserved, + /// Reserved [`Opcode`]. + Reserved5 => Reserved, + /// Reserved [`Opcode`]. + Reserved6 => Reserved, + /// Reserved [`Opcode`]. + Reserved7 => Reserved, + /// Reserved [`Opcode`]. + Reserved8 => Reserved, + /// Reserved [`Opcode`]. + Reserved9 => Reserved, + /// Reserved [`Opcode`]. + Reserved10 => Reserved, + /// Reserved [`Opcode`]. + Reserved11 => Reserved, + /// Reserved [`Opcode`]. + Reserved12 => Reserved, + /// Reserved [`Opcode`]. + Reserved13 => Reserved, + /// Reserved [`Opcode`]. + Reserved14 => Reserved, + /// Reserved [`Opcode`]. + Reserved15 => Reserved, + /// Reserved [`Opcode`]. + Reserved16 => Reserved, + /// Reserved [`Opcode`]. + Reserved17 => Reserved, + /// Reserved [`Opcode`]. + Reserved18 => Reserved, + /// Reserved [`Opcode`]. + Reserved19 => Reserved, + /// Reserved [`Opcode`]. + Reserved20 => Reserved, + /// Reserved [`Opcode`]. + Reserved21 => Reserved, + /// Reserved [`Opcode`]. + Reserved22 => Reserved, + /// Reserved [`Opcode`]. + Reserved23 => Reserved, + /// Reserved [`Opcode`]. + Reserved24 => Reserved, + /// Reserved [`Opcode`]. + Reserved25 => Reserved, + /// Reserved [`Opcode`]. + Reserved26 => Reserved, + /// Reserved [`Opcode`]. + Reserved27 => Reserved, + /// Reserved [`Opcode`]. + Reserved28 => Reserved, + /// Reserved [`Opcode`]. + Reserved29 => Reserved, + /// Reserved [`Opcode`]. + Reserved30 => Reserved, + /// Reserved [`Opcode`]. + Reserved31 => Reserved, + /// Reserved [`Opcode`]. + Reserved32 => Reserved, + /// Reserved [`Opcode`]. + Reserved33 => Reserved, + /// Reserved [`Opcode`]. + Reserved34 => Reserved, + /// Reserved [`Opcode`]. + Reserved35 => Reserved, + /// Reserved [`Opcode`]. + Reserved36 => Reserved, + /// Reserved [`Opcode`]. + Reserved37 => Reserved, + /// Reserved [`Opcode`]. + Reserved38 => Reserved, + /// Reserved [`Opcode`]. + Reserved39 => Reserved, + /// Reserved [`Opcode`]. + Reserved40 => Reserved, + /// Reserved [`Opcode`]. + Reserved41 => Reserved, + /// Reserved [`Opcode`]. + Reserved42 => Reserved, + /// Reserved [`Opcode`]. + Reserved43 => Reserved, + /// Reserved [`Opcode`]. + Reserved44 => Reserved, + /// Reserved [`Opcode`]. + Reserved45 => Reserved, + /// Reserved [`Opcode`]. + Reserved46 => Reserved, + /// Reserved [`Opcode`]. + Reserved47 => Reserved, + /// Reserved [`Opcode`]. + Reserved48 => Reserved, + /// Reserved [`Opcode`]. + Reserved49 => Reserved, + /// Reserved [`Opcode`]. + Reserved50 => Reserved, + /// Reserved [`Opcode`]. + Reserved51 => Reserved, + /// Reserved [`Opcode`]. + Reserved52 => Reserved, + /// Reserved [`Opcode`]. + Reserved53 => Reserved, + /// Reserved [`Opcode`]. + Reserved54 => Reserved, + /// Reserved [`Opcode`]. + Reserved55 => Reserved, + /// Reserved [`Opcode`]. + Reserved56 => Reserved, + /// Reserved [`Opcode`]. + Reserved57 => Reserved, + /// Reserved [`Opcode`]. + Reserved58 => Reserved, + /// Reserved [`Opcode`]. + Reserved59 => Reserved, + /// Reserved [`Opcode`]. + Reserved60 => Reserved, + } + }; +} - /// Declare and initialize `var` type variable. - /// - /// - Operands: - /// - binding_index: `IndexOperand` - /// - Registers: - /// - Input: src - DefInitVar { src: RegisterOperand, binding_index: IndexOperand }, +// Budget handlers are generated here +for_each_opcode!(generate_opcodes); - /// Initialize a lexical binding. - /// - /// - Operands: - /// - binding_index: `IndexOperand` - /// - Registers: - /// - Input: src - PutLexicalValue { src: RegisterOperand, binding_index: IndexOperand }, +#[cfg(not(all(feature = "tailcall", boa_nightly)))] +for_each_opcode!(generate_opcode_handlers); - /// Throws an error because the binding access is illegal. - /// - /// - Operands: - /// -index: `IndexOperand` - ThrowMutateImmutable { index: IndexOperand }, - - /// Get i-th argument of the current frame. - /// - /// Returns `undefined` if `arguments.len()` < `index`. - /// - /// - Operands: - /// - index: `IndexOperand` - /// - Registers: - /// - Output: dst - GetArgument { index: IndexOperand, dst: RegisterOperand }, - - /// Find a binding on the environment chain and store its value in dst. - /// - /// - Operands: - /// - binding_index: `IndexOperand` - /// - Registers: - /// - Output: dst - GetName { dst: RegisterOperand, binding_index: IndexOperand }, - - /// Find a binding in the global object and store its value in dst. - /// - /// - Operands: - /// - binding_index: `IndexOperand` - /// - ic_index: `IndexOperand` - /// - Registers: - /// - Output: dst - GetNameGlobal { dst: RegisterOperand, binding_index: IndexOperand, ic_index: IndexOperand }, - - /// Find a binding on the environment and set the `current_binding` of the current frame. - /// - /// - Operands: - /// - binding_index: `IndexOperand` - GetLocator { binding_index: IndexOperand }, - - /// Find a binding on the environment chain and store its value in dst, and push its - /// `BindingLocator` to the `bindings_stack`. - /// - /// - Operands: - /// - binding_index: `IndexOperand` - /// - Registers: - /// - Output: dst - GetNameAndLocator { dst: RegisterOperand, binding_index: IndexOperand }, - - /// Find a binding on the environment chain and store its value in dst. If the binding does not exist, store undefined. - /// - /// - Operands: - /// - binding_index: `IndexOperand` - /// - Registers: - /// - Output: dst - GetNameOrUndefined { dst: RegisterOperand, binding_index: IndexOperand }, - - /// Find a binding on the environment chain and assign its value. - /// - /// - Operands: - /// - binding_index: `IndexOperand` - /// - Registers: - /// - Input: src - SetName { src: RegisterOperand, binding_index: IndexOperand }, - - /// Assigns a value to the binding pointed by the top of the `bindings_stack`. - /// - /// - Registers: - /// - Input: src - SetNameByLocator { src: RegisterOperand }, - - /// Deletes a property of the global object. - /// - /// - Operands: - /// - binding_index: `IndexOperand` - /// - Registers: - /// - Output: dst - DeleteName { dst: RegisterOperand, binding_index: IndexOperand }, - - /// Gets a method from an object, or `undefined` if the method does not exist. - /// - /// Operands: - /// - name_index: constant `JsString`. - /// - /// Registers (inout) - /// - object: `JsObject` as input, `JsObject` or `undefined` as output. - GetMethod { object: RegisterOperand, name_index: IndexOperand }, - - /// Get the length property by name from an object. - /// - /// Like `object.name` - /// - /// - Operands: - /// - ic_index: `IndexOperand` - /// - Registers: - /// - Input: value - /// - Output: dst - GetLengthProperty { - dst: RegisterOperand, - value: RegisterOperand, - ic_index: IndexOperand - }, - - /// Get a property by name from an object. - /// - /// Like `object.name` - /// - /// - Operands: - /// - ic_index: `IndexOperand` - /// - Registers: - /// - Input: value - /// - Output: dst - GetPropertyByName { - dst: RegisterOperand, - value: RegisterOperand, - ic_index: IndexOperand - }, - - /// Get a property by name from an object. - /// - /// Like `object.name` - /// - /// - Operands: - /// - ic_index: `IndexOperand` - /// - Registers: - /// - Input: receiver, value - /// - Output: dst - GetPropertyByNameWithThis { - dst: RegisterOperand, - receiver: RegisterOperand, - value: RegisterOperand, - ic_index: IndexOperand - }, - - /// Get a property by value from an object. - /// - /// Like `object[key]` - /// - /// - Registers: - /// - Input: object, receiver, key - /// - Output: dst - GetPropertyByValue { - dst: RegisterOperand, - key: RegisterOperand, - receiver: RegisterOperand, - object: RegisterOperand - }, - - /// Get a property by value from an object. - /// - /// Like `object[key]` - /// - /// - Registers: - /// - Input: object, receiver - /// - Output: dst, key - GetPropertyByValuePush { - dst: RegisterOperand, - key: RegisterOperand, - receiver: RegisterOperand, - object: RegisterOperand - }, - - /// Sets a property by name of an object. - /// - /// Like `object.name = value` - /// - /// - Operands: - /// - ic_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - SetPropertyByName { - value: RegisterOperand, - object: RegisterOperand, - ic_index: IndexOperand - }, - - /// Sets a property by name of an object. - /// - /// Like `object.name = value` - /// - /// - Operands: - /// - ic_index: `IndexOperand` - /// - Registers: - /// - Input: object,receiver, value - SetPropertyByNameWithThis { - value: RegisterOperand, - receiver: RegisterOperand, - object: RegisterOperand, - ic_index: IndexOperand - }, - - /// Sets the name of a function object. - /// - /// This operation is corresponds to the `SetFunctionName` abstract operation in the [spec]. - /// - /// [spec]: https://tc39.es/ecma262/#sec-setfunctionname - /// - /// - Operands: - /// - prefix - /// - 0: no prefix - /// - 1: "get " - /// - 2: "set " - /// - Registers: - /// - Input: function, name - SetFunctionName { function: RegisterOperand, name: RegisterOperand, prefix: IndexOperand }, - - /// Defines a own property of an object by name. - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - DefineOwnPropertyByName { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Defines a static class method by name. - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - DefineClassStaticMethodByName { - value: RegisterOperand, - object: RegisterOperand, - name_index: IndexOperand - }, - - /// Defines a class method by name. - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - DefineClassMethodByName { - value: RegisterOperand, - object: RegisterOperand, - name_index: IndexOperand - }, - - /// Sets a property by value of an object. - /// - /// Like `object[key] = value` - /// - /// - Registers: - /// - Input: value, key, receiver, object - SetPropertyByValue { - value: RegisterOperand, - key: RegisterOperand, - receiver: RegisterOperand, - object: RegisterOperand - }, - - /// Defines a own property of an object by value. - /// - /// - Registers: - /// - Input: object, key, value - DefineOwnPropertyByValue { - value: RegisterOperand, - key: RegisterOperand, - object: RegisterOperand - }, - - /// Defines a static class method by value. - /// - /// - Registers: - /// - Input: object, key, value - DefineClassStaticMethodByValue { - value: RegisterOperand, - key: RegisterOperand, - object: RegisterOperand - }, - - /// Defines a class method by value. - /// - /// - Registers: - /// - Input: object, key, value - DefineClassMethodByValue { - value: RegisterOperand, - key: RegisterOperand, - object: RegisterOperand - }, - - /// Sets a getter property by name of an object. - /// - /// Like `get name() value` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - SetPropertyGetterByName { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Defines a static getter class method by name. - /// - /// Like `static get name() value` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - DefineClassStaticGetterByName { - value: RegisterOperand, - object: RegisterOperand, - name_index: IndexOperand - }, - - /// Defines a getter class method by name. - /// - /// Like `get name() value` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - DefineClassGetterByName { - value: RegisterOperand, - object: RegisterOperand, - name_index: IndexOperand - }, - - /// Sets a getter property by value of an object. - /// - /// Like `get [key]() value` - /// - /// - Registers: - /// - Input: object, key, value - SetPropertyGetterByValue { - value: RegisterOperand, - key: RegisterOperand, - object: RegisterOperand - }, - - /// Defines a static getter class method by value. - /// - /// Like `static get [key]() value` - /// - /// - Registers: - /// - Input: object, key, value - DefineClassStaticGetterByValue { - value: RegisterOperand, - key: RegisterOperand, - object: RegisterOperand - }, - - /// Defines a getter class method by value. - /// - /// Like `get [key]() value` - /// - /// - Registers: - /// - Input: object, key, value - DefineClassGetterByValue { - value: RegisterOperand, - key: RegisterOperand, - object: RegisterOperand - }, - - /// Sets a setter property by name of an object. - /// - /// Like `set name() value` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - SetPropertySetterByName { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Defines a static setter class method by name. - /// - /// Like `static set name() value` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - DefineClassStaticSetterByName { - value: RegisterOperand, - object: RegisterOperand, - name_index: IndexOperand - }, - - /// Defines a setter class method by name. - /// - /// Like `set name() value` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - DefineClassSetterByName { - value: RegisterOperand, - object: RegisterOperand, - name_index: IndexOperand - }, - - /// Sets a setter property by value of an object. - /// - /// Like `set [key]() value` - /// - /// - Registers: - /// - Input: object, key, value - SetPropertySetterByValue { - value: RegisterOperand, - key: RegisterOperand, - object: RegisterOperand - }, - - /// Defines a static setter class method by value. - /// - /// Like `static set [key]() value` - /// - /// - Registers: - /// - Input: object, key, value - DefineClassStaticSetterByValue { - value: RegisterOperand, - key: RegisterOperand, - object: RegisterOperand - }, - - /// Defines a setter class method by value. - /// - /// Like `set [key]() value` - /// - /// - Registers: - /// - Input: object, key, value - DefineClassSetterByValue { - value: RegisterOperand, - key: RegisterOperand, - object: RegisterOperand - }, - - /// Set the value of a private property of an object by it's name. - /// - /// Like `obj.#name = value` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - SetPrivateField { value: RegisterOperand, object: RegisterOperand, name_index: IndexOperand }, - - /// Define a private property of a class constructor by it's name. - /// - /// Like `#name = value` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - DefinePrivateField { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Set a private method of a class constructor by it's name. - /// - /// Like `#name() {}` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - SetPrivateMethod { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Set a private setter property of a class constructor by it's name. - /// - /// Like `set #name() {}` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - SetPrivateSetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Set a private getter property of a class constructor by it's name. - /// - /// Like `get #name() {}` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - SetPrivateGetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Get a private property by name from an object and store it in dst. - /// - /// Like `object.#name` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object - /// - Output: dst - GetPrivateField { dst: RegisterOperand, object: RegisterOperand, name_index: IndexOperand }, - - /// Push a field to a class. - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - is_anonymous_function: `bool` - /// - Registers: - /// - Input: object, value - PushClassField { object: RegisterOperand, name: RegisterOperand, value: RegisterOperand, is_anonymous_function: IndexOperand }, - - /// Push a private field to the class. - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - PushClassFieldPrivate { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Push a private getter to the class. - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - PushClassPrivateGetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Push a private setter to the class. - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, value - PushClassPrivateSetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Push a private method to the class. - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object, proto, value - PushClassPrivateMethod { object: RegisterOperand, proto: RegisterOperand, value: RegisterOperand, name_index: IndexOperand }, - - /// Deletes a property by name of an object. - /// - /// Like `delete object.key` - /// - /// - Operands: - /// - name_index: `IndexOperand` - /// - Registers: - /// - Input: object - DeletePropertyByName { object: RegisterOperand, name_index: IndexOperand }, - - /// Deletes a property by value of an object. - /// - /// Like `delete object[key]` - /// - /// - Registers: - /// - Input: object, key - DeletePropertyByValue { object: RegisterOperand, key: RegisterOperand }, - - /// Throws an error when trying to delete a property of `super` - DeleteSuperThrow, - - /// Copy all properties of one object to another object. - /// - /// - Registers: - /// - Input: object, source, excluded_keys - CopyDataProperties { object: RegisterOperand, source: RegisterOperand, excluded_keys: ThinVec }, - - /// Call ToPropertyKey on the value on the stack. - /// - /// - Registers: - /// - Input: src - /// - Output: dst - ToPropertyKey { src: RegisterOperand, dst: RegisterOperand }, - - /// Unconditional jump to address. - /// - /// - Operands: - /// - address: `u32` - Jump { address: Address }, - - /// Conditional jump to address. - /// - /// If the value popped is [`truthy`][truthy] then jump to `address`. - /// - /// - Operands: - /// - address: `Address` - /// - Registers (in): - /// - `value`: `JsValue` - /// - /// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy - JumpIfTrue { address: Address, value: RegisterOperand }, - - /// Conditional jump to address. - /// - /// If the value popped is [`falsy`][falsy] then jump to `address`. - /// - /// - Operands: - /// - address: `Address` - /// - Registers (in): - /// - `value`: `JsValue` - /// - /// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy - JumpIfFalse { address: Address, value: RegisterOperand }, - - /// Conditional jump to address. - /// - /// If the value popped is not undefined jump to `address`. - /// - /// - Operands: - /// - address: `Address`. - /// - Registers (in): - /// - value: `JsValue` - JumpIfNotUndefined { address: Address, value: RegisterOperand }, - - /// Conditional jump to address. - /// - /// If the value popped is undefined jump to `address`. - /// - /// - Operands: - /// - address: `Address`. - /// - Registers (in): - /// - value: `JsValue`. - JumpIfNullOrUndefined { address: Address, value: RegisterOperand }, - - /// Fused `<` comparison + conditional jump. - /// - /// Jumps to `address` if `!(lhs < rhs)`. - /// - /// - Operands: - /// - address: `u32` - /// - Registers: - /// - Input: lhs, rhs - JumpIfNotLessThan { address: Address, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Fused `<=` comparison + conditional jump. - /// - /// Jumps to `address` if `!(lhs <= rhs)`. - /// - /// - Operands: - /// - address: `u32` - /// - Registers: - /// - Input: lhs, rhs - JumpIfNotLessThanOrEqual { address: Address, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Fused `>` comparison + conditional jump. - /// - /// Jumps to `address` if `!(lhs > rhs)`. - /// - /// - Operands: - /// - address: `u32` - /// - Registers: - /// - Input: lhs, rhs - JumpIfNotGreaterThan { address: Address, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Fused `>=` comparison + conditional jump. - /// - /// Jumps to `address` if `!(lhs >= rhs)`. - /// - /// - Operands: - /// - address: `u32` - /// - Registers: - /// - Input: lhs, rhs - JumpIfNotGreaterThanOrEqual { address: Address, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Conditional jump to address. - /// - /// Jump to `address` if two values are not equal. - /// - /// - Operands: - /// - address: `Address` - /// - Registers (in): - /// - lhs: `JsValue`. - /// - rhs: `JsValue`. - JumpIfNotEqual { address: Address, lhs: RegisterOperand, rhs: RegisterOperand }, - - /// Jump table that jumps depending on top value of the stack. - /// - /// This is used to handle special cases when we call `continue`, `break` or `return` in a try block, - /// that has finally block. - /// - /// Operands: index: Register, count: `u32`, address: `Address` * count - JumpTable { index: u32, addresses: ThinVec
}, - - /// Throw exception. - /// - /// This sets pending exception and searches for an exception handler. - /// - /// - Registers: - /// - Input: src - Throw { src: RegisterOperand }, - - /// Rethrow thrown exception. - /// - /// This is also used to handle generator `return()` call, we throw an empty exception, by setting pending exception to [`None`], - /// propagating it and calling finally code until there is no exception handler left, in that case we consume the empty exception and return - /// from the generator. - ReThrow, - - /// Get the thrown pending exception (if it's set) and store it in dst. - /// - /// If there is no pending exception, which can happen if we are handling `return()` call on generator, - /// then we rethrow the empty exception. See [`Opcode::ReThrow`]. - /// - /// - Registers: - /// - Output: dst - Exception { dst: RegisterOperand }, - - /// Get the thrown pending exception if it's set and store `true` in `has_exception`, otherwise store `false`. - /// - /// - Registers: - /// - Output: exception, has_exception - MaybeException { has_exception: RegisterOperand, exception: RegisterOperand }, - - /// Throw a new `TypeError` exception - /// - /// - Operands: - /// - message: `IndexOperand` - ThrowNewTypeError { message: IndexOperand }, - - /// Throw a new `ReferenceError` exception - /// - /// - Operands: - /// - message: `IndexOperand` - ThrowNewReferenceError { message: IndexOperand }, - - /// Gets the function object of the current function environment - /// - /// - Registers (out): - /// - function_object: `JsObject`. - GetFunctionObject { function_object: RegisterOperand }, - - /// Pushes `this` value - /// - /// - Registers: - /// - Output: dst - This { dst: RegisterOperand }, - - /// Pushes `this` value that is related to the object environment of the given binding - /// - /// - Operands: - /// - index: `IndexOperand` - /// - Registers: - /// - Output: dst - ThisForObjectEnvironmentName { dst: RegisterOperand, index: IndexOperand }, - - /// Execute the `super()` method. - /// - /// - Operands: - /// - argument_count: `IndexOperand` - /// - Stack: super_constructor, argument_1, ... argument_n **=>** - SuperCall { argument_count: IndexOperand }, - - /// Execute the `super()` method where the arguments contain spreads. - /// - /// Operands: - /// - /// Stack: super_constructor, arguments_array **=>** - SuperCallSpread, - - /// Execute the `super()` method when no constructor of the class is defined. - /// - /// Operands: - /// - /// Stack: argument_n, ... argument_1 **=>** - SuperCallDerived, - - /// Binds `this` value and initializes the instance elements. - /// - /// Performs steps 7-12 of [`SuperCall: super Arguments`][spec] - /// - /// - Registers: - /// - Input: value - /// - Output: value - /// - /// [spec]: https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation - BindThisValue { value: RegisterOperand }, - - /// Dynamically import a module. - /// - /// - Operands: - /// - phase: `IndexOperand` (0 = evaluation, 1 = defer, 2 = source) - /// - Registers: - /// - Input: specifier, options - /// - Output: specifier - ImportCall { specifier: RegisterOperand, options: RegisterOperand, phase: IndexOperand }, - - /// Strict equal compare two register values, - /// if true jumps to address. - /// - Operands: - /// - address: `Address` - /// - Registers: - /// - Input: value, condition - Case { address: Address, value: RegisterOperand, condition: RegisterOperand }, - - /// Get function from the pre-compiled inner functions. - /// - /// - Operands: - /// - index: `IndexOperand` - /// - Registers: - /// - Output: dst - GetFunction { dst: RegisterOperand, index: IndexOperand }, - - /// Call a function named "eval". - /// - /// - Operands: - /// - argument_count: `IndexOperand` - /// - scope_index: `IndexOperand` - /// - Stack: this, func, argument_1, ... argument_n **=>** result - CallEval { argument_count: IndexOperand, scope_index: IndexOperand }, - - /// Call a function named "eval" where the arguments contain spreads. - /// - /// - Operands: - /// - scope_index: `IndexOperand` - /// - Stack: Stack: this, func, arguments_array **=>** result - CallEvalSpread { scope_index: IndexOperand }, - - /// Call a function. - /// - /// - Operands: - /// - argument_count: `IndexOperand` - /// - Stack: this, func, argument_1, ... argument_n **=>** result - Call { argument_count: IndexOperand }, - - /// Call a function where the arguments contain spreads. - /// - /// Operands: - /// - /// Stack: this, func, arguments_array **=>** result - CallSpread, - - /// Call construct on a function. - /// - /// - Operands: - /// - argument_count: `IndexOperand` - /// - Stack: this, func, argument_1, ... argument_n **=>** result - New { argument_count: IndexOperand }, - - /// Call construct on a function where the arguments contain spreads. - /// - /// Operands: - /// - /// Stack: arguments_array, func **=>** result - NewSpread, - - /// Check return from a function. - CheckReturn, - - /// Return from a function. - Return, - - /// Close an async generator function. - AsyncGeneratorClose, - - /// Creates the Generator object and yields. - /// - /// - Stack: **=>** resume_kind - Generator, - - /// Creates the AsyncGenerator object and yields. - /// - /// - Stack: **=>** resume_kind - AsyncGenerator, - - /// Set return value of a function. - /// - /// - Registers: - /// - Input: src - SetAccumulator { src: RegisterOperand }, - - // Set return value of a function. - /// - /// - Registers: - /// - Output: dst - SetRegisterFromAccumulator { dst: RegisterOperand }, - - /// Move value of operand `src` to register `dst`. - /// - /// - Registers: - /// - Input: src - /// - Output: dst - Move { dst: RegisterOperand, src: RegisterOperand }, - - /// Pop value from the stack and push to register `dst` - /// - /// - Registers: - /// - Output: dst - PopIntoRegister { dst: RegisterOperand }, - - /// Copy value at register `src` and push it on the stack. - /// - /// - Registers: - /// - Input: src - PushFromRegister { src: RegisterOperand }, - - /// Push a declarative environment. - /// - /// - Operands: - /// - scope_index: `IndexOperand` - PushScope { scope_index: IndexOperand }, - - /// Push an object environment. - /// - /// - Registers: - /// - Input: src - PushObjectEnvironment { src: RegisterOperand }, - - /// Pop the current environment. - PopEnvironment, - - /// Increment loop iteration count. - /// - /// Used for limiting the loop iteration. - IncrementLoopIteration, - - /// Creates the ForInIterator of an object. - /// - /// - Registers: - /// - Input: src - CreateForInIterator { src: RegisterOperand }, - - /// Gets the iterator of an object. - /// - /// - Registers: - /// - Input: src - /// - Iterator Stack: **=>** `iterator` - GetIterator { src: RegisterOperand }, - - /// Gets the async iterator of an object. - /// - /// - Registers: - /// - Input: src - /// - Iterator Stack: **=>** `iterator` - GetAsyncIterator { src: RegisterOperand }, - - /// Pop an iterator from the iterators stack - /// - Registers (out) - /// - iterator: `JsObject`. - /// - next: `JsValue`. - IteratorPop { iterator: RegisterOperand, next: RegisterOperand }, - - /// Pushes an iterator on the iterators stack - /// - Registers (in) - /// - iterator: `JsObject`. - /// - next: `JsValue`. - IteratorPush { iterator: RegisterOperand, next: RegisterOperand }, - - /// Calls the `next` method of `iterator`, updating its record with the next value. - /// - /// - Iterator Stack: `iterator` **=>** `iterator` - IteratorNext, - - /// Updates the result of the currently active iterator. - /// - Registers (inout) - /// - result: `JsValue` (in), `bool` (out) with the `done` value of the iterator. - IteratorUpdateResult { result: RegisterOperand }, - - /// Returns `true` if the current iterator is done, or `false` otherwise - /// - /// - Registers: - /// - Output: dst - /// - Iterator Stack: `iterator` **=>** `iterator` - IteratorDone { dst: RegisterOperand }, - - /// Finishes the call to `Opcode::IteratorNext` within a `for await` loop by setting the current - /// result of the current iterator. - /// - /// - Registers: - /// - Input: resume_kind, value - /// - Iterator Stack: `iterator` **=>** `iterator` - IteratorFinishAsyncNext { resume_kind: RegisterOperand, value: RegisterOperand }, - - /// Gets the `value` property of the current iterator record. - /// - /// - Registers: - /// - Output: dst - /// - Iterator Stack: `iterator` **=>** `iterator` - IteratorValue { dst: RegisterOperand }, - - /// Gets the last iteration result of the current iterator record. - /// - /// - Registers: - /// - Output: dst - /// - Iterator Stack: `iterator` **=>** `iterator` - IteratorResult { dst: RegisterOperand }, - - /// Consume the iterator and construct and array with all the values. - /// - /// - Registers: - /// - Output: dst - /// - Iterator Stack: `iterator` **=>** `iterator` - IteratorToArray { dst: RegisterOperand }, - - /// Store `true` in dst if the iterator stack is empty. - /// - /// - Registers: - /// - Output: dst - /// - Iterator Stack: **=>** - IteratorStackEmpty { dst: RegisterOperand }, - - /// Creates a new iterator result object. - /// - /// - Operands: - /// - done: `bool` (codified as u8 with `0` -> `false` and `!0` -> `true`) - /// - Registers: - /// - Input: value - /// - Output: value - CreateIteratorResult { value: RegisterOperand, done: IndexOperand }, - - /// Calls `return` on the current iterator and returns the result. - /// - /// - Registers: - /// - Output: value, called - /// - Iterator Stack: `iterator` **=>** - IteratorReturn { value: RegisterOperand, called: RegisterOperand }, - - /// Concat multiple stack objects into a string. - /// - /// - Registers: - /// - Input: values - /// - Output: dst - ConcatToString { dst: RegisterOperand, values: ThinVec }, - - /// Require the stack value to be neither null nor undefined. - /// - /// - Registers: - /// - Input: src - ValueNotNullOrUndefined { src: RegisterOperand }, - - /// Initialize the rest parameter value of a function from the remaining arguments. - /// - /// - Stack: `argument_1` .. `argument_n` **=>** - /// - Registers: - /// - Output: dst - RestParameterInit { dst: RegisterOperand }, - - /// Yields from the current generator execution. - /// - /// - Registers: - /// - Input: src - /// - Output: resume_kind, received - GeneratorYield { src: RegisterOperand }, - - /// Yields from the current async generator execution. - /// - /// - Registers: - /// - Input: src - /// - Output: resume_kind, received - AsyncGeneratorYield { src: RegisterOperand }, - - /// Create a promise capacity for an async function, if not already set. - CreatePromiseCapability, - - /// Stops the current async function and schedules it to resume later. - /// - /// - Registers: - /// - Input: src - /// - Output: resume_kind, received - Await { src: RegisterOperand }, - - /// Store the current new target in dst. - /// - /// - Registers: - /// - Output: dst - NewTarget { dst: RegisterOperand }, - - /// Store the current `import.meta` in dst. - /// - /// - Registers: - /// - Output: dst - ImportMeta { dst: RegisterOperand }, - - /// Store `true` in the register if the value is an object, or `false` otherwise. - /// - /// - Registers: - /// - Input: value - /// - Output: value - IsObject { value: RegisterOperand }, - - /// Lookup if a tagged template object is cached and skip the creation if it is. - /// - /// - Operands: - /// - address: `u32` - /// - site: `u64` - /// - Registers: - /// - Output: dst - TemplateLookup { address: Address, site: u64, dst: RegisterOperand }, - - /// Create a new tagged template object and cache it. - /// - /// - Operands: - /// - site: `u64` - /// - Registers: - /// - Inputs: values - /// - Output: dst - TemplateCreate { site: u64, dst: RegisterOperand, values: ThinVec }, - - /// Push a private environment. - /// - /// Operands: count: `u32`, count * name_index: `u32` - /// - /// - Registers: - /// - Input: class, name_indices - PushPrivateEnvironment { class: RegisterOperand, name_indices: ThinVec }, - - /// Pop a private environment. - PopPrivateEnvironment, - - /// Creates a mapped `arguments` object. - /// - /// Performs [`10.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env )`] - /// - /// [spec]: https://tc39.es/ecma262/#sec-createunmappedargumentsobject - /// - /// - Registers: - /// - Output: dst - CreateMappedArgumentsObject { dst: RegisterOperand }, - - /// Creates an unmapped `arguments` object. - /// - /// Performs: [`10.4.4.6 CreateUnmappedArgumentsObject ( argumentsList )`] - /// - /// [spec]: https://tc39.es/ecma262/#sec-createmappedargumentsobject - /// - /// - Registers: - /// - Output: dst - CreateUnmappedArgumentsObject { dst: RegisterOperand }, - - /// Reserved [`Opcode`]. - Reserved1 => Reserved, - /// Reserved [`Opcode`]. - Reserved2 => Reserved, - /// Reserved [`Opcode`]. - Reserved3 => Reserved, - /// Reserved [`Opcode`]. - Reserved4 => Reserved, - /// Reserved [`Opcode`]. - Reserved5 => Reserved, - /// Reserved [`Opcode`]. - Reserved6 => Reserved, - /// Reserved [`Opcode`]. - Reserved7 => Reserved, - /// Reserved [`Opcode`]. - Reserved8 => Reserved, - /// Reserved [`Opcode`]. - Reserved9 => Reserved, - /// Reserved [`Opcode`]. - Reserved10 => Reserved, - /// Reserved [`Opcode`]. - Reserved11 => Reserved, - /// Reserved [`Opcode`]. - Reserved12 => Reserved, - /// Reserved [`Opcode`]. - Reserved13 => Reserved, - /// Reserved [`Opcode`]. - Reserved14 => Reserved, - /// Reserved [`Opcode`]. - Reserved15 => Reserved, - /// Reserved [`Opcode`]. - Reserved16 => Reserved, - /// Reserved [`Opcode`]. - Reserved17 => Reserved, - /// Reserved [`Opcode`]. - Reserved18 => Reserved, - /// Reserved [`Opcode`]. - Reserved19 => Reserved, - /// Reserved [`Opcode`]. - Reserved20 => Reserved, - /// Reserved [`Opcode`]. - Reserved21 => Reserved, - /// Reserved [`Opcode`]. - Reserved22 => Reserved, - /// Reserved [`Opcode`]. - Reserved23 => Reserved, - /// Reserved [`Opcode`]. - Reserved24 => Reserved, - /// Reserved [`Opcode`]. - Reserved25 => Reserved, - /// Reserved [`Opcode`]. - Reserved26 => Reserved, - /// Reserved [`Opcode`]. - Reserved27 => Reserved, - /// Reserved [`Opcode`]. - Reserved28 => Reserved, - /// Reserved [`Opcode`]. - Reserved29 => Reserved, - /// Reserved [`Opcode`]. - Reserved30 => Reserved, - /// Reserved [`Opcode`]. - Reserved31 => Reserved, - /// Reserved [`Opcode`]. - Reserved32 => Reserved, - /// Reserved [`Opcode`]. - Reserved33 => Reserved, - /// Reserved [`Opcode`]. - Reserved34 => Reserved, - /// Reserved [`Opcode`]. - Reserved35 => Reserved, - /// Reserved [`Opcode`]. - Reserved36 => Reserved, - /// Reserved [`Opcode`]. - Reserved37 => Reserved, - /// Reserved [`Opcode`]. - Reserved38 => Reserved, - /// Reserved [`Opcode`]. - Reserved39 => Reserved, - /// Reserved [`Opcode`]. - Reserved40 => Reserved, - /// Reserved [`Opcode`]. - Reserved41 => Reserved, - /// Reserved [`Opcode`]. - Reserved42 => Reserved, - /// Reserved [`Opcode`]. - Reserved43 => Reserved, - /// Reserved [`Opcode`]. - Reserved44 => Reserved, - /// Reserved [`Opcode`]. - Reserved45 => Reserved, - /// Reserved [`Opcode`]. - Reserved46 => Reserved, - /// Reserved [`Opcode`]. - Reserved47 => Reserved, - /// Reserved [`Opcode`]. - Reserved48 => Reserved, - /// Reserved [`Opcode`]. - Reserved49 => Reserved, - /// Reserved [`Opcode`]. - Reserved50 => Reserved, - /// Reserved [`Opcode`]. - Reserved51 => Reserved, - /// Reserved [`Opcode`]. - Reserved52 => Reserved, - /// Reserved [`Opcode`]. - Reserved53 => Reserved, - /// Reserved [`Opcode`]. - Reserved54 => Reserved, - /// Reserved [`Opcode`]. - Reserved55 => Reserved, - /// Reserved [`Opcode`]. - Reserved56 => Reserved, - /// Reserved [`Opcode`]. - Reserved57 => Reserved, - /// Reserved [`Opcode`]. - Reserved58 => Reserved, - /// Reserved [`Opcode`]. - Reserved59 => Reserved, - /// Reserved [`Opcode`]. - Reserved60 => Reserved, -} +#[cfg(all(feature = "tailcall", boa_nightly))] +for_each_opcode!(generate_opcode_tailcall_handlers); diff --git a/core/engine/src/vm/opcode/tailcall.rs b/core/engine/src/vm/opcode/tailcall.rs new file mode 100644 index 00000000000..c7bb54b42f0 --- /dev/null +++ b/core/engine/src/vm/opcode/tailcall.rs @@ -0,0 +1,107 @@ +use crate::{ + Context, JsError, JsNativeError, + vm::{Opcode, completion_record::CompletionRecord}, +}; + +use super::OPCODE_HANDLERS_TAILCALL; + +impl Context { + pub(crate) extern "rust-preserve-none" fn dispatch_next( + &mut self, + pc: usize, + ) -> CompletionRecord { + match self.vm.frame().code_block.bytecode.bytes.get(pc) { + Some(&byte) => { + #[cfg(all(feature = "trace", not(all(feature = "tailcall", boa_nightly))))] + unreachable!(); + let opcode = Opcode::decode(byte); + + #[cfg(feature = "trace")] + if self.vm.trace || self.vm.frame().code_block.traceable() { + use crate::sys::time::Instant; + + if self.vm.current_frame != Some(self.vm.frame()) { + println!(); + self.trace_call_frame(); + self.vm.current_frame = Some(self.vm.frame()); + } + + let frame = self.vm.frame(); + let (instruction, _) = frame.code_block.bytecode.next_instruction(pc); + + let operands = frame.code_block().instruction_operands(&instruction); + + // measure time since last dispatch + let now = Instant::now(); + let duration = now - frame.code_block().last_trace_time.get().unwrap_or(now); + frame.code_block().last_trace_time.set(Some(now)); + + let stack = self.vm.stack.display_trace(frame, self.vm.frames.len() - 1); + + println!( + "{: CompletionRecord::Throw(JsError::from_native(JsNativeError::error())), // program ended without a return + } + } +} + +macro_rules! generate_opcode_tailcall_handlers { + ( + $( + $(#[$comment:ident $($args:tt)*])* + $Variant:ident $({ + $( + $(#[$fieldinner:ident $($fieldargs:tt)*])* + $FieldName:ident : $FieldType:ty + ),* + $(,)? + })? $(=> $mapping:ident)? + ),* + $(,)? + ) => { + type OpcodeHandlerTailCall = extern "rust-preserve-none" fn(&mut Context, usize) -> CompletionRecord; + + const OPCODE_HANDLERS_TAILCALL: [OpcodeHandlerTailCall; 256] = { + [ + $( + paste::paste! { [] }, + )* + ] + }; + + $( + paste::paste! { + #[allow(unused_parens)] + extern "rust-preserve-none" fn []( + context: &mut Context, + pc: usize, + ) -> CompletionRecord { + let bytes = &context.vm.frame().code_block.bytecode.bytes; + let (args, next_pc) = <($($($FieldType),*)?)>::decode(bytes, pc + 1); + context.vm.frame_mut().pc = next_pc as u32; + let result = $Variant::operation(args, context); + + let cr = IntoCompletionRecord::into_completion_record(result, context); + + // This match MUST be the last expression — both arms in tail position + match cr { + ControlFlow::Continue(()) => become context.dispatch_next(context.vm.frame().pc as usize), + ControlFlow::Break(value) => value, + } + } + } + )* + } +} + +pub(crate) use generate_opcode_tailcall_handlers;