diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 2b5d834a90d..1d8c33695ca 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -596,9 +596,9 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { let index = self.next_opcode_location(); self.emit( Opcode::JumpTable, - &[Operand::U32(count), Operand::U32(Self::DUMMY_ADDRESS)], + &[Operand::U32(Self::DUMMY_ADDRESS), Operand::U32(count)], ); - let default = Label { index: index + 4 }; + let default = Label { index }; let mut labels = Vec::with_capacity(count as usize); for i in 0..count { labels.push(Label { diff --git a/boa_engine/src/vm/call_frame/mod.rs b/boa_engine/src/vm/call_frame/mod.rs index 9597e3a28ac..123b966cfa7 100644 --- a/boa_engine/src/vm/call_frame/mod.rs +++ b/boa_engine/src/vm/call_frame/mod.rs @@ -87,9 +87,10 @@ impl CallFrame { } /// Indicates how a generator function that has been called/resumed should return. -#[derive(Copy, Clone, Debug, PartialEq, Default)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] #[repr(u8)] -pub(crate) enum GeneratorResumeKind { +#[allow(missing_docs)] +pub enum GeneratorResumeKind { #[default] Normal = 0, Throw, diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index 86134adf504..f4bee9bafe9 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -301,387 +301,335 @@ impl CodeBlock { /// Returns an empty `String` if no operands are present. #[cfg(any(feature = "trace", feature = "flowgraph"))] pub(crate) fn instruction_operands(&self, pc: &mut usize, interner: &Interner) -> String { - let opcode: Opcode = self.bytecode[*pc].into(); - *pc += size_of::(); - match opcode { - Opcode::SetFunctionName => { - let operand = self.read::(*pc); - *pc += size_of::(); - match operand { - 0 => "prefix: none", - 1 => "prefix: get", - 2 => "prefix: set", - _ => unreachable!(), - } - .to_owned() - } - Opcode::RotateLeft | Opcode::RotateRight => { - let result = self.read::(*pc).to_string(); - *pc += size_of::(); - result + use super::Instruction; + + let instruction = Instruction::from_bytecode(&self.bytecode, pc); + match instruction { + Instruction::SetFunctionName { prefix } => match prefix { + 0 => "prefix: none", + 1 => "prefix: get", + 2 => "prefix: set", + _ => unreachable!(), } - Opcode::Generator => { - let result = self.read::(*pc); - *pc += size_of::(); - format!("async: {}", result != 0) + .to_owned(), + Instruction::RotateLeft { n } | Instruction::RotateRight { n } => n.to_string(), + Instruction::Generator { r#async } => { + format!("async: {async}") } - Opcode::PushInt8 => { - let result = self.read::(*pc).to_string(); - *pc += size_of::(); - result + Instruction::PushInt8 { value } => value.to_string(), + Instruction::PushInt16 { value } => value.to_string(), + Instruction::PushInt32 { value } => value.to_string(), + Instruction::PushFloat { value } => ryu_js::Buffer::new().format(value).to_string(), + Instruction::PushDouble { value } => ryu_js::Buffer::new().format(value).to_string(), + Instruction::PushLiteral { index: value } + | Instruction::ThrowNewTypeError { message: value } + | Instruction::Jump { address: value } + | Instruction::JumpIfTrue { address: value } + | Instruction::JumpIfFalse { address: value } + | Instruction::JumpIfNotUndefined { address: value } + | Instruction::JumpIfNullOrUndefined { address: value } + | Instruction::Case { address: value } + | Instruction::Default { address: value } + | Instruction::LogicalAnd { exit: value } + | Instruction::LogicalOr { exit: value } + | Instruction::Coalesce { exit: value } + | Instruction::CallEval { + argument_count: value, } - Opcode::PushInt16 => { - let result = self.read::(*pc).to_string(); - *pc += size_of::(); - result + | Instruction::Call { + argument_count: value, } - Opcode::PushInt32 => { - let result = self.read::(*pc).to_string(); - *pc += size_of::(); - result + | Instruction::New { + argument_count: value, } - Opcode::PushFloat => { - let operand = self.read::(*pc); - *pc += size_of::(); - ryu_js::Buffer::new().format(operand).to_string() + | Instruction::SuperCall { + argument_count: value, } - Opcode::PushDouble => { - let operand = self.read::(*pc); - *pc += size_of::(); - ryu_js::Buffer::new().format(operand).to_string() + | Instruction::ConcatToString { value_count: value } => value.to_string(), + Instruction::PushDeclarativeEnvironment { + compile_environments_index, } - Opcode::PushLiteral - | Opcode::ThrowNewTypeError - | Opcode::Jump - | Opcode::JumpIfTrue - | Opcode::JumpIfFalse - | Opcode::JumpIfNotUndefined - | Opcode::JumpIfNullOrUndefined - | Opcode::Case - | Opcode::Default - | Opcode::LogicalAnd - | Opcode::LogicalOr - | Opcode::Coalesce - | Opcode::CallEval - | Opcode::Call - | Opcode::New - | Opcode::SuperCall - | Opcode::ConcatToString => { - let result = self.read::(*pc).to_string(); - *pc += size_of::(); - result + | Instruction::PushFunctionEnvironment { + compile_environments_index, + } => compile_environments_index.to_string(), + Instruction::CopyDataProperties { + excluded_key_count: value1, + excluded_key_count_computed: value2, } - Opcode::PushDeclarativeEnvironment | Opcode::PushFunctionEnvironment => { - let operand = self.read::(*pc); - *pc += size_of::(); - format!("{operand}") + | Instruction::GeneratorDelegateNext { + return_method_undefined: value1, + throw_method_undefined: value2, } - Opcode::CopyDataProperties - | Opcode::GeneratorDelegateNext - | Opcode::GeneratorDelegateResume => { - let operand1 = self.read::(*pc); - *pc += size_of::(); - let operand2 = self.read::(*pc); - *pc += size_of::(); - format!("{operand1}, {operand2}") + | Instruction::GeneratorDelegateResume { + exit: value1, + r#return: value2, + } => { + format!("{value1}, {value2}") } - Opcode::TemplateLookup | Opcode::TemplateCreate => { - let operand1 = self.read::(*pc); - *pc += size_of::(); - let operand2 = self.read::(*pc); - *pc += size_of::(); - format!("{operand1}, {operand2}") + Instruction::TemplateLookup { exit: value, site } + | Instruction::TemplateCreate { count: value, site } => { + format!("{value}, {site}") } - Opcode::GetArrowFunction - | Opcode::GetAsyncArrowFunction - | Opcode::GetFunction - | Opcode::GetFunctionAsync => { - let operand = self.read::(*pc); - *pc += size_of::() + size_of::(); + Instruction::GetArrowFunction { index, method } + | Instruction::GetAsyncArrowFunction { index, method } + | Instruction::GetFunction { index, method } + | Instruction::GetFunctionAsync { index, method } => { format!( - "{operand:04}: '{}' (length: {})", - self.functions[operand as usize] + "{index:04}: '{}' (length: {}), method: {method}", + self.functions[index as usize] .name() .to_std_string_escaped(), - self.functions[operand as usize].length + self.functions[index as usize].length ) } - Opcode::GetGenerator | Opcode::GetGeneratorAsync => { - let operand = self.read::(*pc); - *pc += size_of::(); + Instruction::GetGenerator { index } | Instruction::GetGeneratorAsync { index } => { format!( - "{operand:04}: '{}' (length: {})", - self.functions[operand as usize] + "{index:04}: '{}' (length: {})", + self.functions[index as usize] .name() .to_std_string_escaped(), - self.functions[operand as usize].length + self.functions[index as usize].length ) } - Opcode::DefVar - | Opcode::DefInitVar - | Opcode::PutLexicalValue - | Opcode::GetName - | Opcode::GetLocator - | Opcode::GetNameAndLocator - | Opcode::GetNameOrUndefined - | Opcode::SetName - | Opcode::DeleteName => { - let operand = self.read::(*pc); - *pc += size_of::(); + Instruction::DefVar { index } + | Instruction::DefInitVar { index } + | Instruction::PutLexicalValue { index } + | Instruction::GetName { index } + | Instruction::GetLocator { index } + | Instruction::GetNameAndLocator { index } + | Instruction::GetNameOrUndefined { index } + | Instruction::SetName { index } + | Instruction::DeleteName { index } => { format!( - "{:04}: '{}'", - operand, - interner.resolve_expect(self.bindings[operand as usize].name().sym()), + "{index:04}: '{}'", + interner.resolve_expect(self.bindings[index as usize].name().sym()), ) } - Opcode::GetPropertyByName - | Opcode::GetMethod - | Opcode::SetPropertyByName - | Opcode::DefineOwnPropertyByName - | Opcode::DefineClassStaticMethodByName - | Opcode::DefineClassMethodByName - | Opcode::SetPropertyGetterByName - | Opcode::DefineClassStaticGetterByName - | Opcode::DefineClassGetterByName - | Opcode::SetPropertySetterByName - | Opcode::DefineClassStaticSetterByName - | Opcode::DefineClassSetterByName - | Opcode::DeletePropertyByName - | Opcode::SetPrivateField - | Opcode::DefinePrivateField - | Opcode::SetPrivateMethod - | Opcode::SetPrivateSetter - | Opcode::SetPrivateGetter - | Opcode::GetPrivateField - | Opcode::PushClassFieldPrivate - | Opcode::PushClassPrivateGetter - | Opcode::PushClassPrivateSetter - | Opcode::PushClassPrivateMethod - | Opcode::InPrivate - | Opcode::ThrowMutateImmutable => { - let operand = self.read::(*pc); - *pc += size_of::(); + Instruction::GetPropertyByName { index } + | Instruction::GetMethod { index } + | Instruction::SetPropertyByName { index } + | Instruction::DefineOwnPropertyByName { index } + | Instruction::DefineClassStaticMethodByName { index } + | Instruction::DefineClassMethodByName { index } + | Instruction::SetPropertyGetterByName { index } + | Instruction::DefineClassStaticGetterByName { index } + | Instruction::DefineClassGetterByName { index } + | Instruction::SetPropertySetterByName { index } + | Instruction::DefineClassStaticSetterByName { index } + | Instruction::DefineClassSetterByName { index } + | Instruction::DeletePropertyByName { index } + | Instruction::SetPrivateField { index } + | Instruction::DefinePrivateField { index } + | Instruction::SetPrivateMethod { index } + | Instruction::SetPrivateSetter { index } + | Instruction::SetPrivateGetter { index } + | Instruction::GetPrivateField { index } + | Instruction::PushClassFieldPrivate { index } + | Instruction::PushClassPrivateGetter { index } + | Instruction::PushClassPrivateSetter { index } + | Instruction::PushClassPrivateMethod { index } + | Instruction::InPrivate { index } + | Instruction::ThrowMutateImmutable { index } => { format!( - "{operand:04}: '{}'", - self.names[operand as usize].to_std_string_escaped(), + "{index:04}: '{}'", + self.names[index as usize].to_std_string_escaped(), ) } - Opcode::PushPrivateEnvironment => { - let count = self.read::(*pc); - *pc += size_of::() * (count as usize + 1); - String::new() + Instruction::PushPrivateEnvironment { name_indices } => { + format!("{name_indices:?}") } - Opcode::JumpTable => { - let count = self.read::(*pc); - *pc += size_of::(); - let default = self.read::(*pc); - *pc += size_of::(); - - let mut operands = format!("#{count}: Default: {default:4}"); - for i in 1..=count { - let address = self.read::(*pc); - *pc += size_of::(); - + Instruction::JumpTable { default, addresses } => { + let mut operands = format!("#{}: Default: {default:4}", addresses.len()); + for (i, address) in addresses.iter().enumerate() { operands += &format!(", {i}: {address}"); } operands } - Opcode::JumpIfNotResumeKind => { - let exit = self.read::(*pc); - *pc += size_of::(); - - let resume_kind = self.read::(*pc); - *pc += size_of::(); - - format!( - "ResumeKind: {:?}, exit: {exit}", - JsValue::new(resume_kind).to_generator_resume_kind() - ) + Instruction::JumpIfNotResumeKind { exit, resume_kind } => { + format!("ResumeKind: {resume_kind:?}, exit: {exit}") } - Opcode::CreateIteratorResult => { - let done = self.read::(*pc) != 0; - *pc += size_of::(); + Instruction::CreateIteratorResult { done } => { format!("done: {done}") } - Opcode::Pop - | Opcode::Dup - | Opcode::Swap - | Opcode::PushZero - | Opcode::PushOne - | Opcode::PushNaN - | Opcode::PushPositiveInfinity - | Opcode::PushNegativeInfinity - | Opcode::PushNull - | Opcode::PushTrue - | Opcode::PushFalse - | Opcode::PushUndefined - | Opcode::PushEmptyObject - | Opcode::PushClassPrototype - | Opcode::SetClassPrototype - | Opcode::SetHomeObject - | Opcode::SetHomeObjectClass - | Opcode::Add - | Opcode::Sub - | Opcode::Div - | Opcode::Mul - | Opcode::Mod - | Opcode::Pow - | Opcode::ShiftRight - | Opcode::ShiftLeft - | Opcode::UnsignedShiftRight - | Opcode::BitOr - | Opcode::BitAnd - | Opcode::BitXor - | Opcode::BitNot - | Opcode::In - | Opcode::Eq - | Opcode::StrictEq - | Opcode::NotEq - | Opcode::StrictNotEq - | Opcode::GreaterThan - | Opcode::GreaterThanOrEq - | Opcode::LessThan - | Opcode::LessThanOrEq - | Opcode::InstanceOf - | Opcode::TypeOf - | Opcode::Void - | Opcode::LogicalNot - | Opcode::Pos - | Opcode::Neg - | Opcode::Inc - | Opcode::IncPost - | Opcode::Dec - | Opcode::DecPost - | Opcode::GetPropertyByValue - | Opcode::GetPropertyByValuePush - | Opcode::SetPropertyByValue - | Opcode::DefineOwnPropertyByValue - | Opcode::DefineClassStaticMethodByValue - | Opcode::DefineClassMethodByValue - | Opcode::SetPropertyGetterByValue - | Opcode::DefineClassStaticGetterByValue - | Opcode::DefineClassGetterByValue - | Opcode::SetPropertySetterByValue - | Opcode::DefineClassStaticSetterByValue - | Opcode::DefineClassSetterByValue - | Opcode::DeletePropertyByValue - | Opcode::DeleteSuperThrow - | Opcode::ToPropertyKey - | Opcode::ToBoolean - | Opcode::Throw - | Opcode::ReThrow - | Opcode::Exception - | Opcode::MaybeException - | Opcode::This - | Opcode::Super - | Opcode::Return - | Opcode::AsyncGeneratorClose - | Opcode::CreatePromiseCapability - | Opcode::CompletePromiseCapability - | Opcode::PopEnvironment - | Opcode::IncrementLoopIteration - | Opcode::CreateForInIterator - | Opcode::GetIterator - | Opcode::GetAsyncIterator - | Opcode::IteratorNext - | Opcode::IteratorNextWithoutPop - | Opcode::IteratorFinishAsyncNext - | Opcode::IteratorValue - | Opcode::IteratorValueWithoutPop - | Opcode::IteratorResult - | Opcode::IteratorDone - | Opcode::IteratorToArray - | Opcode::IteratorPop - | Opcode::IteratorReturn - | Opcode::IteratorStackEmpty - | Opcode::RequireObjectCoercible - | Opcode::ValueNotNullOrUndefined - | Opcode::RestParameterInit - | Opcode::RestParameterPop - | Opcode::PushValueToArray - | Opcode::PushElisionToArray - | Opcode::PushIteratorToArray - | Opcode::PushNewArray - | Opcode::GeneratorYield - | Opcode::AsyncGeneratorYield - | Opcode::GeneratorNext - | Opcode::PushClassField - | Opcode::SuperCallDerived - | Opcode::Await - | Opcode::NewTarget - | Opcode::ImportMeta - | Opcode::SuperCallPrepare - | Opcode::CallEvalSpread - | Opcode::CallSpread - | Opcode::NewSpread - | Opcode::SuperCallSpread - | Opcode::SetPrototype - | Opcode::PushObjectEnvironment - | Opcode::IsObject - | Opcode::SetNameByLocator - | Opcode::PopPrivateEnvironment - | Opcode::ImportCall - | Opcode::GetReturnValue - | Opcode::SetReturnValue - | Opcode::Nop => String::new(), - Opcode::Reserved1 - | Opcode::Reserved2 - | Opcode::Reserved3 - | Opcode::Reserved4 - | Opcode::Reserved5 - | Opcode::Reserved6 - | Opcode::Reserved7 - | Opcode::Reserved8 - | Opcode::Reserved9 - | Opcode::Reserved10 - | Opcode::Reserved11 - | Opcode::Reserved12 - | Opcode::Reserved13 - | Opcode::Reserved14 - | Opcode::Reserved15 - | Opcode::Reserved16 - | Opcode::Reserved17 - | Opcode::Reserved18 - | Opcode::Reserved19 - | Opcode::Reserved20 - | Opcode::Reserved21 - | Opcode::Reserved22 - | Opcode::Reserved23 - | Opcode::Reserved24 - | Opcode::Reserved25 - | Opcode::Reserved26 - | Opcode::Reserved27 - | Opcode::Reserved28 - | Opcode::Reserved29 - | Opcode::Reserved30 - | Opcode::Reserved31 - | Opcode::Reserved32 - | Opcode::Reserved33 - | Opcode::Reserved34 - | Opcode::Reserved35 - | Opcode::Reserved36 - | Opcode::Reserved37 - | Opcode::Reserved38 - | Opcode::Reserved39 - | Opcode::Reserved40 - | Opcode::Reserved41 - | Opcode::Reserved42 - | Opcode::Reserved43 - | Opcode::Reserved44 - | Opcode::Reserved45 - | Opcode::Reserved46 - | Opcode::Reserved47 - | Opcode::Reserved48 - | Opcode::Reserved49 - | Opcode::Reserved50 - | Opcode::Reserved51 - | Opcode::Reserved52 - | Opcode::Reserved53 - | Opcode::Reserved54 - | Opcode::Reserved55 - | Opcode::Reserved56 - | Opcode::Reserved57 - | Opcode::Reserved58 => unreachable!("Reserved opcodes are unrechable"), + Instruction::Pop + | Instruction::Dup + | Instruction::Swap + | Instruction::PushZero + | Instruction::PushOne + | Instruction::PushNaN + | Instruction::PushPositiveInfinity + | Instruction::PushNegativeInfinity + | Instruction::PushNull + | Instruction::PushTrue + | Instruction::PushFalse + | Instruction::PushUndefined + | Instruction::PushEmptyObject + | Instruction::PushClassPrototype + | Instruction::SetClassPrototype + | Instruction::SetHomeObject + | Instruction::SetHomeObjectClass + | Instruction::Add + | Instruction::Sub + | Instruction::Div + | Instruction::Mul + | Instruction::Mod + | Instruction::Pow + | Instruction::ShiftRight + | Instruction::ShiftLeft + | Instruction::UnsignedShiftRight + | Instruction::BitOr + | Instruction::BitAnd + | Instruction::BitXor + | Instruction::BitNot + | Instruction::In + | Instruction::Eq + | Instruction::StrictEq + | Instruction::NotEq + | Instruction::StrictNotEq + | Instruction::GreaterThan + | Instruction::GreaterThanOrEq + | Instruction::LessThan + | Instruction::LessThanOrEq + | Instruction::InstanceOf + | Instruction::TypeOf + | Instruction::Void + | Instruction::LogicalNot + | Instruction::Pos + | Instruction::Neg + | Instruction::Inc + | Instruction::IncPost + | Instruction::Dec + | Instruction::DecPost + | Instruction::GetPropertyByValue + | Instruction::GetPropertyByValuePush + | Instruction::SetPropertyByValue + | Instruction::DefineOwnPropertyByValue + | Instruction::DefineClassStaticMethodByValue + | Instruction::DefineClassMethodByValue + | Instruction::SetPropertyGetterByValue + | Instruction::DefineClassStaticGetterByValue + | Instruction::DefineClassGetterByValue + | Instruction::SetPropertySetterByValue + | Instruction::DefineClassStaticSetterByValue + | Instruction::DefineClassSetterByValue + | Instruction::DeletePropertyByValue + | Instruction::DeleteSuperThrow + | Instruction::ToPropertyKey + | Instruction::ToBoolean + | Instruction::Throw + | Instruction::ReThrow + | Instruction::Exception + | Instruction::MaybeException + | Instruction::This + | Instruction::Super + | Instruction::Return + | Instruction::AsyncGeneratorClose + | Instruction::CreatePromiseCapability + | Instruction::CompletePromiseCapability + | Instruction::PopEnvironment + | Instruction::IncrementLoopIteration + | Instruction::CreateForInIterator + | Instruction::GetIterator + | Instruction::GetAsyncIterator + | Instruction::IteratorNext + | Instruction::IteratorNextWithoutPop + | Instruction::IteratorFinishAsyncNext + | Instruction::IteratorValue + | Instruction::IteratorValueWithoutPop + | Instruction::IteratorResult + | Instruction::IteratorDone + | Instruction::IteratorToArray + | Instruction::IteratorPop + | Instruction::IteratorReturn + | Instruction::IteratorStackEmpty + | Instruction::RequireObjectCoercible + | Instruction::ValueNotNullOrUndefined + | Instruction::RestParameterInit + | Instruction::RestParameterPop + | Instruction::PushValueToArray + | Instruction::PushElisionToArray + | Instruction::PushIteratorToArray + | Instruction::PushNewArray + | Instruction::GeneratorYield + | Instruction::AsyncGeneratorYield + | Instruction::GeneratorNext + | Instruction::PushClassField + | Instruction::SuperCallDerived + | Instruction::Await + | Instruction::NewTarget + | Instruction::ImportMeta + | Instruction::SuperCallPrepare + | Instruction::CallEvalSpread + | Instruction::CallSpread + | Instruction::NewSpread + | Instruction::SuperCallSpread + | Instruction::SetPrototype + | Instruction::PushObjectEnvironment + | Instruction::IsObject + | Instruction::SetNameByLocator + | Instruction::PopPrivateEnvironment + | Instruction::ImportCall + | Instruction::GetReturnValue + | Instruction::SetReturnValue + | Instruction::Nop => String::new(), + Instruction::Reserved1 + | Instruction::Reserved2 + | Instruction::Reserved3 + | Instruction::Reserved4 + | Instruction::Reserved5 + | Instruction::Reserved6 + | Instruction::Reserved7 + | Instruction::Reserved8 + | Instruction::Reserved9 + | Instruction::Reserved10 + | Instruction::Reserved11 + | Instruction::Reserved12 + | Instruction::Reserved13 + | Instruction::Reserved14 + | Instruction::Reserved15 + | Instruction::Reserved16 + | Instruction::Reserved17 + | Instruction::Reserved18 + | Instruction::Reserved19 + | Instruction::Reserved20 + | Instruction::Reserved21 + | Instruction::Reserved22 + | Instruction::Reserved23 + | Instruction::Reserved24 + | Instruction::Reserved25 + | Instruction::Reserved26 + | Instruction::Reserved27 + | Instruction::Reserved28 + | Instruction::Reserved29 + | Instruction::Reserved30 + | Instruction::Reserved31 + | Instruction::Reserved32 + | Instruction::Reserved33 + | Instruction::Reserved34 + | Instruction::Reserved35 + | Instruction::Reserved36 + | Instruction::Reserved37 + | Instruction::Reserved38 + | Instruction::Reserved39 + | Instruction::Reserved40 + | Instruction::Reserved41 + | Instruction::Reserved42 + | Instruction::Reserved43 + | Instruction::Reserved44 + | Instruction::Reserved45 + | Instruction::Reserved46 + | Instruction::Reserved47 + | Instruction::Reserved48 + | Instruction::Reserved49 + | Instruction::Reserved50 + | Instruction::Reserved51 + | Instruction::Reserved52 + | Instruction::Reserved53 + | Instruction::Reserved54 + | Instruction::Reserved55 + | Instruction::Reserved56 + | Instruction::Reserved57 + | Instruction::Reserved58 => unreachable!("Reserved opcodes are unrechable"), } } } diff --git a/boa_engine/src/vm/flowgraph/mod.rs b/boa_engine/src/vm/flowgraph/mod.rs index 8c6b57bfc6c..a6b05f08803 100644 --- a/boa_engine/src/vm/flowgraph/mod.rs +++ b/boa_engine/src/vm/flowgraph/mod.rs @@ -1,9 +1,8 @@ //! This module is responsible for generating the vm instruction flowgraph. -use crate::vm::{CodeBlock, Opcode}; +use crate::vm::CodeBlock; use boa_interner::Interner; use boa_macros::utf16; -use std::mem::size_of; mod color; mod edge; @@ -15,6 +14,8 @@ pub use edge::*; pub use graph::*; pub use node::*; +use super::Instruction; + impl CodeBlock { /// Output the [`CodeBlock`] VM instructions into a [`Graph`]. #[allow(clippy::match_same_arms)] @@ -30,104 +31,78 @@ impl CodeBlock { let mut pc = 0; while pc < self.bytecode.len() { - let opcode: Opcode = self.bytecode[pc].into(); - let opcode_str = opcode.as_str(); let previous_pc = pc; + let instruction = Instruction::from_bytecode(&self.bytecode, &mut pc); + let opcode = instruction.opcode(); + let opcode_str = opcode.as_str(); - let mut tmp = pc; + let mut tmp = previous_pc; let label = format!( "{opcode_str} {}", self.instruction_operands(&mut tmp, interner) ); - pc += size_of::(); - match opcode { - Opcode::SetFunctionName => { - let operand = self.read::(pc); - pc += size_of::(); - let label = format!( - "{opcode_str} {}", - match operand { - 0 => "prefix: none", - 1 => "prefix: get", - 2 => "prefix: set", - _ => unreachable!(), - } - ); + match instruction { + Instruction::SetFunctionName { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::RotateLeft | Opcode::RotateRight | Opcode::CreateIteratorResult => { - pc += size_of::(); + Instruction::RotateLeft { .. } + | Instruction::RotateRight { .. } + | Instruction::CreateIteratorResult { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::Generator => { - pc += size_of::(); - + Instruction::Generator { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::PushInt8 => { - pc += size_of::(); - + Instruction::PushInt8 { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::PushInt16 => { - pc += size_of::(); + Instruction::PushInt16 { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::PushInt32 => { - pc += size_of::(); + Instruction::PushInt32 { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::PushFloat => { - pc += size_of::(); - + Instruction::PushFloat { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::PushDouble => { - pc += size_of::(); - + Instruction::PushDouble { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::PushLiteral => { - let operand = self.read::(pc); - pc += size_of::(); - let operand_str = self.literals[operand as usize].display().to_string(); + Instruction::PushLiteral { index } => { + let operand_str = self.literals[index as usize].display().to_string(); let operand_str = operand_str.escape_debug(); let label = format!("{opcode_str} {operand_str}"); graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::Jump => { - let operand = self.read::(pc); - pc += size_of::(); + Instruction::Jump { address } => { graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None); graph.add_edge( previous_pc, - operand as usize, + address as usize, None, Color::None, EdgeStyle::Line, ); } - Opcode::JumpIfFalse - | Opcode::JumpIfTrue - | Opcode::JumpIfNotUndefined - | Opcode::JumpIfNullOrUndefined => { - let operand = self.read::(pc); - pc += size_of::(); + Instruction::JumpIfFalse { address } + | Instruction::JumpIfTrue { address } + | Instruction::JumpIfNotUndefined { address } + | Instruction::JumpIfNullOrUndefined { address } => { graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None); graph.add_edge( previous_pc, - operand as usize, + address as usize, Some("YES".into()), Color::Green, EdgeStyle::Line, @@ -140,19 +115,13 @@ impl CodeBlock { EdgeStyle::Line, ); } - Opcode::TemplateLookup | Opcode::TemplateCreate => { - let start_address = self.read::(pc); - pc += size_of::(); - let end_address = self.read::(pc); - pc += size_of::(); - - let label = format!("{opcode_str} {start_address}, {end_address}"); + Instruction::TemplateLookup { .. } | Instruction::TemplateCreate { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::Red); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::LogicalAnd | Opcode::LogicalOr | Opcode::Coalesce => { - let exit = self.read::(pc); - pc += size_of::(); + Instruction::LogicalAnd { exit } + | Instruction::LogicalOr { exit } + | Instruction::Coalesce { exit } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); graph.add_edge( @@ -163,9 +132,7 @@ impl CodeBlock { EdgeStyle::Line, ); } - Opcode::Case => { - let address = self.read::(pc) as usize; - pc += size_of::(); + Instruction::Case { address } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge( previous_pc, @@ -176,23 +143,26 @@ impl CodeBlock { ); graph.add_edge( previous_pc, - address, + address as usize, Some("YES".into()), Color::Green, EdgeStyle::Line, ); } - Opcode::Default => { - let address = self.read::(pc) as usize; - pc += size_of::(); + Instruction::Default { address } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); - graph.add_edge(previous_pc, address, None, Color::None, EdgeStyle::Line); + graph.add_edge( + previous_pc, + address as usize, + None, + Color::None, + EdgeStyle::Line, + ); } - Opcode::GeneratorDelegateNext => { - let throw_method_undefined = self.read::(pc) as usize; - pc += size_of::(); - let return_method_undefined = self.read::(pc) as usize; - pc += size_of::(); + Instruction::GeneratorDelegateNext { + return_method_undefined, + throw_method_undefined, + } => { graph.add_node( previous_pc, NodeShape::Diamond, @@ -202,24 +172,20 @@ impl CodeBlock { graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); graph.add_edge( previous_pc, - throw_method_undefined, + throw_method_undefined as usize, Some("`throw` undefined".into()), Color::Red, EdgeStyle::Line, ); graph.add_edge( previous_pc, - return_method_undefined, + return_method_undefined as usize, Some("`return` undefined".into()), Color::Blue, EdgeStyle::Line, ); } - Opcode::GeneratorDelegateResume => { - let return_gen = self.read::(pc) as usize; - pc += size_of::(); - let exit = self.read::(pc) as usize; - pc += size_of::(); + Instruction::GeneratorDelegateResume { r#return, exit } => { graph.add_node( previous_pc, NodeShape::Diamond, @@ -229,35 +195,28 @@ impl CodeBlock { graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); graph.add_edge( previous_pc, - return_gen, + r#return as usize, Some("return".into()), Color::Yellow, EdgeStyle::Line, ); graph.add_edge( previous_pc, - exit, + exit as usize, Some("done".into()), Color::Blue, EdgeStyle::Line, ); } - Opcode::CallEval - | Opcode::Call - | Opcode::New - | Opcode::SuperCall - | Opcode::ConcatToString => { - pc += size_of::(); + Instruction::CallEval { .. } + | Instruction::Call { .. } + | Instruction::New { .. } + | Instruction::SuperCall { .. } + | Instruction::ConcatToString { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::JumpIfNotResumeKind => { - let exit = self.read::(pc); - pc += size_of::(); - - let _resume_kind = self.read::(pc); - pc += size_of::(); - + Instruction::JumpIfNotResumeKind { exit, .. } => { graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None); graph.add_edge( previous_pc, @@ -268,21 +227,14 @@ impl CodeBlock { ); graph.add_edge(previous_pc, pc, None, Color::Green, EdgeStyle::Line); } - Opcode::CopyDataProperties => { - let operand1 = self.read::(pc); - pc += size_of::(); - let operand2 = self.read::(pc); - pc += size_of::(); - - let label = format!("{opcode_str} {operand1}, {operand2}"); + Instruction::CopyDataProperties { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::PushDeclarativeEnvironment | Opcode::PushFunctionEnvironment => { + Instruction::PushDeclarativeEnvironment { .. } + | Instruction::PushFunctionEnvironment { .. } => { let random = rand::random(); - pc += size_of::(); - graph.add_node( previous_pc, NodeShape::None, @@ -291,93 +243,62 @@ impl CodeBlock { ); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::PopEnvironment => { + Instruction::PopEnvironment => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::GetArrowFunction - | Opcode::GetAsyncArrowFunction - | Opcode::GetFunction - | Opcode::GetFunctionAsync => { - let operand = self.read::(pc); - let fn_name = self.functions[operand as usize] - .name() - .to_std_string_escaped(); - pc += size_of::() + size_of::(); - let label = format!( - "{opcode_str} '{fn_name}' (length: {})", - self.functions[operand as usize].length - ); + Instruction::GetArrowFunction { .. } + | Instruction::GetAsyncArrowFunction { .. } + | Instruction::GetFunction { .. } + | Instruction::GetFunctionAsync { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::GetGenerator | Opcode::GetGeneratorAsync => { - let operand = self.read::(pc); - let fn_name = self.functions[operand as usize] - .name() - .to_std_string_escaped(); - let label = format!( - "{opcode_str} '{fn_name}' (length: {})", - self.functions[operand as usize].length - ); + Instruction::GetGenerator { .. } | Instruction::GetGeneratorAsync { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::DefVar - | Opcode::DefInitVar - | Opcode::PutLexicalValue - | Opcode::GetName - | Opcode::GetLocator - | Opcode::GetNameAndLocator - | Opcode::GetNameOrUndefined - | Opcode::SetName - | Opcode::DeleteName => { - let operand = self.read::(pc); - pc += size_of::(); - let label = format!( - "{opcode_str} '{}'", - interner.resolve_expect(self.bindings[operand as usize].name().sym()), - ); + Instruction::DefVar { .. } + | Instruction::DefInitVar { .. } + | Instruction::PutLexicalValue { .. } + | Instruction::GetName { .. } + | Instruction::GetLocator { .. } + | Instruction::GetNameAndLocator { .. } + | Instruction::GetNameOrUndefined { .. } + | Instruction::SetName { .. } + | Instruction::DeleteName { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::GetPropertyByName - | Opcode::GetMethod - | Opcode::SetPropertyByName - | Opcode::DefineOwnPropertyByName - | Opcode::DefineClassStaticMethodByName - | Opcode::DefineClassMethodByName - | Opcode::SetPropertyGetterByName - | Opcode::DefineClassStaticGetterByName - | Opcode::DefineClassGetterByName - | Opcode::SetPropertySetterByName - | Opcode::DefineClassStaticSetterByName - | Opcode::DefineClassSetterByName - | Opcode::SetPrivateField - | Opcode::DefinePrivateField - | Opcode::SetPrivateMethod - | Opcode::SetPrivateSetter - | Opcode::SetPrivateGetter - | Opcode::GetPrivateField - | Opcode::DeletePropertyByName - | Opcode::PushClassFieldPrivate - | Opcode::PushClassPrivateGetter - | Opcode::PushClassPrivateSetter - | Opcode::PushClassPrivateMethod - | Opcode::InPrivate - | Opcode::ThrowMutateImmutable => { - let operand = self.read::(pc); - pc += size_of::(); - let label = format!( - "{opcode_str} '{}'", - self.names[operand as usize].to_std_string_escaped(), - ); + Instruction::GetPropertyByName { .. } + | Instruction::GetMethod { .. } + | Instruction::SetPropertyByName { .. } + | Instruction::DefineOwnPropertyByName { .. } + | Instruction::DefineClassStaticMethodByName { .. } + | Instruction::DefineClassMethodByName { .. } + | Instruction::SetPropertyGetterByName { .. } + | Instruction::DefineClassStaticGetterByName { .. } + | Instruction::DefineClassGetterByName { .. } + | Instruction::SetPropertySetterByName { .. } + | Instruction::DefineClassStaticSetterByName { .. } + | Instruction::DefineClassSetterByName { .. } + | Instruction::SetPrivateField { .. } + | Instruction::DefinePrivateField { .. } + | Instruction::SetPrivateMethod { .. } + | Instruction::SetPrivateSetter { .. } + | Instruction::SetPrivateGetter { .. } + | Instruction::GetPrivateField { .. } + | Instruction::DeletePropertyByName { .. } + | Instruction::PushClassFieldPrivate { .. } + | Instruction::PushClassPrivateGetter { .. } + | Instruction::PushClassPrivateSetter { .. } + | Instruction::PushClassPrivateMethod { .. } + | Instruction::InPrivate { .. } + | Instruction::ThrowMutateImmutable { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::ThrowNewTypeError => { - pc += size_of::(); - + Instruction::ThrowNewTypeError { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); if let Some((i, handler)) = self.find_handler(previous_pc as u32) { graph.add_edge( @@ -389,7 +310,7 @@ impl CodeBlock { ); } } - Opcode::Throw | Opcode::ReThrow => { + Instruction::Throw | Instruction::ReThrow => { if let Some((i, handler)) = self.find_handler(previous_pc as u32) { graph.add_node(previous_pc, NodeShape::Record, label.into(), Color::None); graph.add_edge( @@ -403,18 +324,11 @@ impl CodeBlock { graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None); } } - Opcode::PushPrivateEnvironment => { - let count = self.read::(pc); - pc += size_of::() * (count as usize + 1); + Instruction::PushPrivateEnvironment { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::JumpTable => { - let count = self.read::(pc); - pc += size_of::(); - let default = self.read::(pc); - pc += size_of::(); - + Instruction::JumpTable { default, addresses } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge( previous_pc, @@ -424,200 +338,197 @@ impl CodeBlock { EdgeStyle::Line, ); - for i in 0..count { - let address = self.read::(pc); - pc += size_of::(); - + for (i, address) in addresses.iter().enumerate() { graph.add_edge( previous_pc, - address as usize, + *address as usize, Some(format!("Index: {i}").into()), Color::None, EdgeStyle::Line, ); } } - Opcode::Pop - | Opcode::Dup - | Opcode::Swap - | Opcode::PushZero - | Opcode::PushOne - | Opcode::PushNaN - | Opcode::PushPositiveInfinity - | Opcode::PushNegativeInfinity - | Opcode::PushNull - | Opcode::PushTrue - | Opcode::PushFalse - | Opcode::PushUndefined - | Opcode::PushEmptyObject - | Opcode::PushClassPrototype - | Opcode::SetClassPrototype - | Opcode::SetHomeObject - | Opcode::SetHomeObjectClass - | Opcode::Add - | Opcode::Sub - | Opcode::Div - | Opcode::Mul - | Opcode::Mod - | Opcode::Pow - | Opcode::ShiftRight - | Opcode::ShiftLeft - | Opcode::UnsignedShiftRight - | Opcode::BitOr - | Opcode::BitAnd - | Opcode::BitXor - | Opcode::BitNot - | Opcode::In - | Opcode::Eq - | Opcode::StrictEq - | Opcode::NotEq - | Opcode::StrictNotEq - | Opcode::GreaterThan - | Opcode::GreaterThanOrEq - | Opcode::LessThan - | Opcode::LessThanOrEq - | Opcode::InstanceOf - | Opcode::TypeOf - | Opcode::Void - | Opcode::LogicalNot - | Opcode::Pos - | Opcode::Neg - | Opcode::Inc - | Opcode::IncPost - | Opcode::Dec - | Opcode::DecPost - | Opcode::GetPropertyByValue - | Opcode::GetPropertyByValuePush - | Opcode::SetPropertyByValue - | Opcode::DefineOwnPropertyByValue - | Opcode::DefineClassStaticMethodByValue - | Opcode::DefineClassMethodByValue - | Opcode::SetPropertyGetterByValue - | Opcode::DefineClassStaticGetterByValue - | Opcode::DefineClassGetterByValue - | Opcode::SetPropertySetterByValue - | Opcode::DefineClassStaticSetterByValue - | Opcode::DefineClassSetterByValue - | Opcode::DeletePropertyByValue - | Opcode::DeleteSuperThrow - | Opcode::ToPropertyKey - | Opcode::ToBoolean - | Opcode::This - | Opcode::Super - | Opcode::IncrementLoopIteration - | Opcode::CreateForInIterator - | Opcode::GetIterator - | Opcode::GetAsyncIterator - | Opcode::IteratorNext - | Opcode::IteratorNextWithoutPop - | Opcode::IteratorFinishAsyncNext - | Opcode::IteratorValue - | Opcode::IteratorValueWithoutPop - | Opcode::IteratorResult - | Opcode::IteratorDone - | Opcode::IteratorToArray - | Opcode::IteratorPop - | Opcode::IteratorReturn - | Opcode::IteratorStackEmpty - | Opcode::RequireObjectCoercible - | Opcode::ValueNotNullOrUndefined - | Opcode::RestParameterInit - | Opcode::RestParameterPop - | Opcode::PushValueToArray - | Opcode::PushElisionToArray - | Opcode::PushIteratorToArray - | Opcode::PushNewArray - | Opcode::GeneratorYield - | Opcode::AsyncGeneratorYield - | Opcode::AsyncGeneratorClose - | Opcode::CreatePromiseCapability - | Opcode::CompletePromiseCapability - | Opcode::GeneratorNext - | Opcode::PushClassField - | Opcode::SuperCallDerived - | Opcode::Await - | Opcode::NewTarget - | Opcode::ImportMeta - | Opcode::CallEvalSpread - | Opcode::CallSpread - | Opcode::NewSpread - | Opcode::SuperCallSpread - | Opcode::SuperCallPrepare - | Opcode::SetPrototype - | Opcode::IsObject - | Opcode::SetNameByLocator - | Opcode::PushObjectEnvironment - | Opcode::PopPrivateEnvironment - | Opcode::ImportCall - | Opcode::GetReturnValue - | Opcode::SetReturnValue - | Opcode::Exception - | Opcode::MaybeException - | Opcode::Nop => { + Instruction::Pop + | Instruction::Dup + | Instruction::Swap + | Instruction::PushZero + | Instruction::PushOne + | Instruction::PushNaN + | Instruction::PushPositiveInfinity + | Instruction::PushNegativeInfinity + | Instruction::PushNull + | Instruction::PushTrue + | Instruction::PushFalse + | Instruction::PushUndefined + | Instruction::PushEmptyObject + | Instruction::PushClassPrototype + | Instruction::SetClassPrototype + | Instruction::SetHomeObject + | Instruction::SetHomeObjectClass + | Instruction::Add + | Instruction::Sub + | Instruction::Div + | Instruction::Mul + | Instruction::Mod + | Instruction::Pow + | Instruction::ShiftRight + | Instruction::ShiftLeft + | Instruction::UnsignedShiftRight + | Instruction::BitOr + | Instruction::BitAnd + | Instruction::BitXor + | Instruction::BitNot + | Instruction::In + | Instruction::Eq + | Instruction::StrictEq + | Instruction::NotEq + | Instruction::StrictNotEq + | Instruction::GreaterThan + | Instruction::GreaterThanOrEq + | Instruction::LessThan + | Instruction::LessThanOrEq + | Instruction::InstanceOf + | Instruction::TypeOf + | Instruction::Void + | Instruction::LogicalNot + | Instruction::Pos + | Instruction::Neg + | Instruction::Inc + | Instruction::IncPost + | Instruction::Dec + | Instruction::DecPost + | Instruction::GetPropertyByValue + | Instruction::GetPropertyByValuePush + | Instruction::SetPropertyByValue + | Instruction::DefineOwnPropertyByValue + | Instruction::DefineClassStaticMethodByValue + | Instruction::DefineClassMethodByValue + | Instruction::SetPropertyGetterByValue + | Instruction::DefineClassStaticGetterByValue + | Instruction::DefineClassGetterByValue + | Instruction::SetPropertySetterByValue + | Instruction::DefineClassStaticSetterByValue + | Instruction::DefineClassSetterByValue + | Instruction::DeletePropertyByValue + | Instruction::DeleteSuperThrow + | Instruction::ToPropertyKey + | Instruction::ToBoolean + | Instruction::This + | Instruction::Super + | Instruction::IncrementLoopIteration + | Instruction::CreateForInIterator + | Instruction::GetIterator + | Instruction::GetAsyncIterator + | Instruction::IteratorNext + | Instruction::IteratorNextWithoutPop + | Instruction::IteratorFinishAsyncNext + | Instruction::IteratorValue + | Instruction::IteratorValueWithoutPop + | Instruction::IteratorResult + | Instruction::IteratorDone + | Instruction::IteratorToArray + | Instruction::IteratorPop + | Instruction::IteratorReturn + | Instruction::IteratorStackEmpty + | Instruction::RequireObjectCoercible + | Instruction::ValueNotNullOrUndefined + | Instruction::RestParameterInit + | Instruction::RestParameterPop + | Instruction::PushValueToArray + | Instruction::PushElisionToArray + | Instruction::PushIteratorToArray + | Instruction::PushNewArray + | Instruction::GeneratorYield + | Instruction::AsyncGeneratorYield + | Instruction::AsyncGeneratorClose + | Instruction::CreatePromiseCapability + | Instruction::CompletePromiseCapability + | Instruction::GeneratorNext + | Instruction::PushClassField + | Instruction::SuperCallDerived + | Instruction::Await + | Instruction::NewTarget + | Instruction::ImportMeta + | Instruction::CallEvalSpread + | Instruction::CallSpread + | Instruction::NewSpread + | Instruction::SuperCallSpread + | Instruction::SuperCallPrepare + | Instruction::SetPrototype + | Instruction::IsObject + | Instruction::SetNameByLocator + | Instruction::PushObjectEnvironment + | Instruction::PopPrivateEnvironment + | Instruction::ImportCall + | Instruction::GetReturnValue + | Instruction::SetReturnValue + | Instruction::Exception + | Instruction::MaybeException + | Instruction::Nop => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::Return => { + Instruction::Return => { graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::Red); } - Opcode::Reserved1 - | Opcode::Reserved2 - | Opcode::Reserved3 - | Opcode::Reserved4 - | Opcode::Reserved5 - | Opcode::Reserved6 - | Opcode::Reserved7 - | Opcode::Reserved8 - | Opcode::Reserved9 - | Opcode::Reserved10 - | Opcode::Reserved11 - | Opcode::Reserved12 - | Opcode::Reserved13 - | Opcode::Reserved14 - | Opcode::Reserved15 - | Opcode::Reserved16 - | Opcode::Reserved17 - | Opcode::Reserved18 - | Opcode::Reserved19 - | Opcode::Reserved20 - | Opcode::Reserved21 - | Opcode::Reserved22 - | Opcode::Reserved23 - | Opcode::Reserved24 - | Opcode::Reserved25 - | Opcode::Reserved26 - | Opcode::Reserved27 - | Opcode::Reserved28 - | Opcode::Reserved29 - | Opcode::Reserved30 - | Opcode::Reserved31 - | Opcode::Reserved32 - | Opcode::Reserved33 - | Opcode::Reserved34 - | Opcode::Reserved35 - | Opcode::Reserved36 - | Opcode::Reserved37 - | Opcode::Reserved38 - | Opcode::Reserved39 - | Opcode::Reserved40 - | Opcode::Reserved41 - | Opcode::Reserved42 - | Opcode::Reserved43 - | Opcode::Reserved44 - | Opcode::Reserved45 - | Opcode::Reserved46 - | Opcode::Reserved47 - | Opcode::Reserved48 - | Opcode::Reserved49 - | Opcode::Reserved50 - | Opcode::Reserved51 - | Opcode::Reserved52 - | Opcode::Reserved53 - | Opcode::Reserved54 - | Opcode::Reserved55 - | Opcode::Reserved56 - | Opcode::Reserved57 - | Opcode::Reserved58 => unreachable!("Reserved opcodes are unrechable"), + Instruction::Reserved1 + | Instruction::Reserved2 + | Instruction::Reserved3 + | Instruction::Reserved4 + | Instruction::Reserved5 + | Instruction::Reserved6 + | Instruction::Reserved7 + | Instruction::Reserved8 + | Instruction::Reserved9 + | Instruction::Reserved10 + | Instruction::Reserved11 + | Instruction::Reserved12 + | Instruction::Reserved13 + | Instruction::Reserved14 + | Instruction::Reserved15 + | Instruction::Reserved16 + | Instruction::Reserved17 + | Instruction::Reserved18 + | Instruction::Reserved19 + | Instruction::Reserved20 + | Instruction::Reserved21 + | Instruction::Reserved22 + | Instruction::Reserved23 + | Instruction::Reserved24 + | Instruction::Reserved25 + | Instruction::Reserved26 + | Instruction::Reserved27 + | Instruction::Reserved28 + | Instruction::Reserved29 + | Instruction::Reserved30 + | Instruction::Reserved31 + | Instruction::Reserved32 + | Instruction::Reserved33 + | Instruction::Reserved34 + | Instruction::Reserved35 + | Instruction::Reserved36 + | Instruction::Reserved37 + | Instruction::Reserved38 + | Instruction::Reserved39 + | Instruction::Reserved40 + | Instruction::Reserved41 + | Instruction::Reserved42 + | Instruction::Reserved43 + | Instruction::Reserved44 + | Instruction::Reserved45 + | Instruction::Reserved46 + | Instruction::Reserved47 + | Instruction::Reserved48 + | Instruction::Reserved49 + | Instruction::Reserved50 + | Instruction::Reserved51 + | Instruction::Reserved52 + | Instruction::Reserved53 + | Instruction::Reserved54 + | Instruction::Reserved55 + | Instruction::Reserved56 + | Instruction::Reserved57 + | Instruction::Reserved58 => unreachable!("Reserved opcodes are unrechable"), } } diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 9e3c4d7de97..28589995e66 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -33,10 +33,13 @@ mod runtime_limits; pub mod flowgraph; pub use runtime_limits::RuntimeLimits; -pub use {call_frame::CallFrame, code_block::CodeBlock, opcode::Opcode}; +pub use { + call_frame::{CallFrame, GeneratorResumeKind}, + code_block::CodeBlock, + opcode::{Instruction, InstructionIterator, Opcode}, +}; pub(crate) use { - call_frame::GeneratorResumeKind, code_block::{ create_function_object, create_function_object_fast, create_generator_function_object, CodeBlockFlags, Handler, diff --git a/boa_engine/src/vm/opcode/control_flow/jump.rs b/boa_engine/src/vm/opcode/control_flow/jump.rs index 776c20ea705..b38fea3bce6 100644 --- a/boa_engine/src/vm/opcode/control_flow/jump.rs +++ b/boa_engine/src/vm/opcode/control_flow/jump.rs @@ -118,8 +118,8 @@ impl Operation for JumpTable { const INSTRUCTION: &'static str = "INST - JumpTable"; fn execute(context: &mut Context<'_>) -> JsResult { - let count = context.vm.read::(); let default = context.vm.read::(); + let count = context.vm.read::(); let value = context.vm.pop(); if let JsValue::Integer(value) = &value { diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index aa6fbee8e6e..8dbdb1172ed 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -1,5 +1,7 @@ +use std::iter::FusedIterator; + /// The opcodes of the vm. -use crate::{vm::CompletionType, Context, JsResult}; +use crate::{vm::CompletionType, Context, JsResult, JsValue}; // Operation modules mod r#await; @@ -86,26 +88,224 @@ pub(crate) use unary_ops::*; #[doc(inline)] pub(crate) use value::*; -macro_rules! generate_impl { +use super::{code_block::Readable, GeneratorResumeKind}; +use thin_vec::ThinVec; + +/// Read type T from code. +/// +/// # Safety +/// +/// Does not check if read happens out-of-bounds. +pub(crate) unsafe fn read_unchecked(bytes: &[u8], offset: usize) -> T +where + T: Readable, +{ + // Safety: + // The function caller must ensure that the read is in bounds. + // + // This has to be an unaligned read because we can't guarantee that + // the types are aligned. + unsafe { bytes.as_ptr().add(offset).cast::().read_unaligned() } +} + +/// Read type T from code. +#[track_caller] +pub(crate) fn read(bytes: &[u8], offset: usize) -> T +where + T: Readable, +{ + assert!(offset + std::mem::size_of::() - 1 < bytes.len()); + + // Safety: We checked that it is not an out-of-bounds read, + // so this is safe. + unsafe { read_unchecked(bytes, offset) } +} + +trait BytecodeConversion: Sized { + fn to_bytecode(&self, bytes: &mut Vec); + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self; +} + +impl BytecodeConversion for GeneratorResumeKind { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.push(*self as u8); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + JsValue::from(value).to_generator_resume_kind() + } +} + +impl BytecodeConversion for bool { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.push(u8::from(*self)); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value != 0 + } +} + +impl BytecodeConversion for i8 { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.push(*self as u8); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value + } +} + +impl BytecodeConversion for u8 { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.push(*self); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value + } +} + +impl BytecodeConversion for i16 { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.to_ne_bytes()); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value + } +} + +impl BytecodeConversion for u16 { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.to_ne_bytes()); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value + } +} + +impl BytecodeConversion for i32 { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.to_ne_bytes()); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value + } +} + +impl BytecodeConversion for u32 { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.to_ne_bytes()); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value + } +} + +impl BytecodeConversion for i64 { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.to_ne_bytes()); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value + } +} + +impl BytecodeConversion for u64 { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.to_ne_bytes()); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value + } +} + +impl BytecodeConversion for f32 { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.to_ne_bytes()); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value + } +} + +impl BytecodeConversion for f64 { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.to_ne_bytes()); + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let value = read::(bytes, *pc); + *pc += std::mem::size_of::(); + value + } +} + +impl BytecodeConversion for ThinVec { + fn to_bytecode(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&(self.len() as u32).to_ne_bytes()); + for item in self { + bytes.extend_from_slice(&item.to_ne_bytes()); + } + } + fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let count = read::(bytes, *pc); + *pc += std::mem::size_of::(); + let mut result = Self::with_capacity(count as usize); + for _ in 0..count { + let item = read::(bytes, *pc); + *pc += std::mem::size_of::(); + result.push(item); + } + result + } +} + +/// Generate [`Opcode`]s and [`Instruction`]s enums. +macro_rules! generate_opcodes { ( name $name:ident ) => { $name }; ( name $name:ident => $mapping:ident ) => { $mapping }; + // If if-block is empty use else-block, use if-block otherwise. + { if { $($if:tt)+ } else { $($else:tt)* } } => { $($if)+ }; + { if { } else { $($else:tt)* } } => { $($else)* }; + ( - $(#[$outer:meta])* - pub enum $Type:ident { - $( - $(#[$inner:ident $($args:tt)*])* - $Variant:ident $(=> $mapping:ident)? $(= $index:expr)* - ),* - $(,)? - } + $( + $(#[$inner:ident $($args:tt)*])* + $Variant:ident $({ + $( + $(#[$fieldinner:ident $($fieldargs:tt)*])* + $FieldName:ident : $FieldType:ty + ),* + })? $(=> $mapping:ident)? + ),* + $(,)? ) => { /// The opcodes of the vm. - $(#[$outer])* - pub enum $Type { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u8)] + pub enum Opcode { $( $(#[$inner $($args)*])* - $Variant $(= $index)* + $Variant ),* } @@ -122,11 +322,11 @@ macro_rules! generate_impl { } } - impl $Type { + impl Opcode { const MAX: usize = 2usize.pow(8); const NAMES: [&'static str; Self::MAX] = [ - $( $mapping)?)>::NAME),* + $( $mapping)?)>::NAME),* ]; /// Name of this opcode. @@ -136,7 +336,7 @@ macro_rules! generate_impl { } const INSTRUCTIONS: [&'static str; Self::MAX] = [ - $( $mapping)?)>::INSTRUCTION),* + $( $mapping)?)>::INSTRUCTION),* ]; /// Name of the profiler event for this opcode. @@ -146,13 +346,90 @@ macro_rules! generate_impl { } const EXECUTE_FNS: [fn(&mut Context<'_>) -> JsResult; Self::MAX] = [ - $( $mapping)?)>::execute),* + $( $mapping)?)>::execute),* ]; pub(super) fn execute(self, context: &mut Context<'_>) -> JsResult { Self::EXECUTE_FNS[self as usize](context) } } + + /// This represents a VM instruction, it contains both opcode and operands. + #[derive(Debug, Clone, PartialEq)] + #[repr(u8)] + pub enum Instruction { + $( + $(#[$inner $($args)*])* + $Variant $({ + $( + $(#[$fieldinner $($fieldargs)*])* + #[allow(missing_docs)] + $FieldName : $FieldType + ),* + })? + ),* + } + + impl Instruction { + /// Convert [`Instruction`] to compact bytecode. + #[inline] + pub fn to_bytecode(&self, bytes: &mut Vec) { + match self { + $( + Self::$Variant $({ + $( $FieldName ),* + })? => { + bytes.push(Opcode::$Variant as u8); + $({ + $( BytecodeConversion::to_bytecode($FieldName, bytes); )* + })? + } + ),* + } + } + + /// Convert compact bytecode to [`Instruction`]. + /// + /// # Panics + /// + /// If the provided bytecode is not valid. + #[inline] + #[must_use] + pub fn from_bytecode(bytes: &[u8], pc: &mut usize) -> Self { + let opcode = bytes[*pc].into(); + *pc += 1; + match opcode { + $( + Opcode::$Variant => { + generate_opcodes!( + if { + $({ + Self::$Variant { + $( + $FieldName: BytecodeConversion::from_bytecode(bytes, pc) + ),* + } + })? + } else { + Self::$Variant + } + ) + } + ),* + } + } + + /// Get the [`Opcode`] of the [`Instruction`]. + #[inline] + #[must_use] + pub const fn opcode(&self) -> Opcode { + match self { + $( + Self::$Variant $({ $( $FieldName: _ ),* })? => Opcode::$Variant + ),* + } + } + } }; } @@ -169,1633 +446,1629 @@ pub(crate) trait Operation { fn execute(context: &mut Context<'_>) -> JsResult; } -generate_impl! { - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - #[repr(u8)] - pub enum Opcode { - /// Pop the top value from the stack. - /// - /// Operands: - /// - /// Stack: value **=>** - Pop = 0, - - /// Push a copy of the top value on the stack. - /// - /// Operands: - /// - /// Stack: value **=>** value, value - Dup, - - /// Swap the top two values on the stack. - /// - /// Operands: - /// - /// Stack: second, first **=>** first, second - Swap, - - /// Rotates the top `n` values of the stack to the left by `1`. - /// - /// Equivalent to calling [`slice::rotate_left`] with argument `1` on the top `n` values of the - /// stack. - /// - /// Operands: n: `u8` - /// - /// Stack: v\[n\], v\[n-1\], ... , v\[1\], v\[0\] **=>** v\[n-1\], ... , v\[1\], v\[0\], v\[n\] - RotateLeft, - - /// Rotates the top `n` values of the stack to the right by `1`. - /// - /// Equivalent to calling [`slice::rotate_right`] with argument `1` on the top `n` values of the - /// stack. - /// - /// Operands: n: `u8` - /// - /// Stack: v\[n\], v\[n-1\], ... , v\[1\], v\[0\] **=>** v\[0\], v\[n\], v\[n-1\], ... , v\[1\] - RotateRight, - - /// Push integer `0` on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `0` - PushZero, - - /// Push integer `1` on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `1` - PushOne, - - /// Push `i8` value on the stack. - /// - /// Operands: value: `i8` - /// - /// Stack: **=>** value - PushInt8, - - /// Push i16 value on the stack. - /// - /// Operands: value: `i16` - /// - /// Stack: **=>** value - PushInt16, - - /// Push i32 value on the stack. - /// - /// Operands: value: `i32` - /// - /// Stack: **=>** value - PushInt32, - - /// Push `f32` value on the stack. - /// - /// Operands: value: `f32` - /// - /// Stack: **=>** value - PushFloat, - - /// Push `f64` value on the stack. - /// - /// Operands: value: `f64` - /// - /// Stack: **=>** value - PushDouble, - - /// Push `NaN` integer on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `NaN` - PushNaN, - - /// Push `Infinity` value on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `Infinity` - PushPositiveInfinity, - - /// Push `-Infinity` value on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `-Infinity` - PushNegativeInfinity, - - /// Push `null` value on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `null` - PushNull, - - /// Push `true` value on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `true` - PushTrue, - - /// Push `false` value on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `false` - PushFalse, - - /// Push `undefined` value on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `undefined` - PushUndefined, - - /// Push literal value on the stack. - /// - /// Like strings and bigints. The index operand is used to index into the `literals` - /// array to get the value. - /// - /// Operands: index: `u32` - /// - /// Stack: **=>** (`literals[index]`) - PushLiteral, - - /// Push empty object `{}` value on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `{}` - PushEmptyObject, - - /// Get the prototype of a superclass and push it on the stack. - /// - /// Operands: - /// - /// Stack: class, superclass **=>** class, superclass.prototype - PushClassPrototype, - - /// Set the prototype of a class object. - /// - /// Operands: - /// - /// Stack: class, prototype **=>** class.prototype - SetClassPrototype, - - /// Set home object internal slot of an object literal method. - /// - /// Operands: - /// - /// Stack: home, function **=>** home, function - SetHomeObject, - - /// Set home object internal slot of a class method. - /// - /// Operands: - /// - /// Stack: home, function **=>** home, function - SetHomeObjectClass, - - /// Set the prototype of an object if the value is an object or null. - /// - /// Operands: - /// - /// Stack: object, value **=>** - SetPrototype, - - /// Push an empty array value on the stack. - /// - /// Operands: - /// - /// Stack: **=>** `[]` - PushNewArray, - - /// Push a value to an array. - /// - /// Operands: - /// - /// Stack: array, value **=>** array - PushValueToArray, - - /// Push an empty element/hole to an array. - /// - /// Operands: - /// - /// Stack: array **=>** array - PushElisionToArray, - - /// Push all iterator values to an array. - /// - /// Operands: - /// - /// Stack: array, iterator, next_method **=>** array - PushIteratorToArray, - - /// Binary `+` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs + rhs) - Add, - - /// Binary `-` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs - rhs) - Sub, - - /// Binary `/` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs / rhs) - Div, - - /// Binary `*` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs * rhs) - Mul, - - /// Binary `%` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs % rhs) - Mod, - - /// Binary `**` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs ** rhs) - Pow, - - /// Binary `>>` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs >> rhs) - ShiftRight, - - /// Binary `<<` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** `(lhs << rhs)` - ShiftLeft, - - /// Binary `>>>` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs >>> rhs) - UnsignedShiftRight, - - /// Binary bitwise `|` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs | rhs) - BitOr, - - /// Binary bitwise `&` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs & rhs) - BitAnd, - - /// Binary bitwise `^` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs ^ rhs) - BitXor, - - /// Unary bitwise `~` operator. - /// - /// Operands: - /// - /// Stack: value **=>** ~value - BitNot, - - /// Binary `in` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs `in` rhs) - In, - - /// Binary `in` operator for private names. - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: rhs **=>** (private_name `in` rhs) - InPrivate, - - /// Binary `==` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs `==` rhs) - Eq, - - /// Binary `===` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs `===` rhs) - StrictEq, - - /// Binary `!=` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs `!=` rhs) - NotEq, - - /// Binary `!==` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs `!==` rhs) - StrictNotEq, - - /// Binary `>` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs > rhs) - GreaterThan, - - /// Binary `>=` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs >= rhs) - GreaterThanOrEq, - - /// Binary `<` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** `(lhs < rhs)` - LessThan, - - /// Binary `<=` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** `(lhs <= rhs)` - LessThanOrEq, - - /// Binary `instanceof` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs instanceof rhs) - InstanceOf, - - /// Binary logical `&&` operator. - /// - /// This is a short-circuit operator, if the `lhs` value is `false`, then it jumps to `exit` address. - /// - /// Operands: exit: `u32` - /// - /// Stack: lhs, rhs **=>** (lhs && rhs) - LogicalAnd, - - /// Binary logical `||` operator. - /// - /// This is a short-circuit operator, if the `lhs` value is `true`, then it jumps to `exit` address. - /// - /// Operands: exit: `u32` - /// - /// Stack: lhs, rhs **=>** (lhs || rhs) - LogicalOr, - - /// Binary `??` operator. - /// - /// This is a short-circuit operator, if the `lhs` value is **not** `null` or `undefined`, - /// then it jumps to `exit` address. - /// - /// Operands: exit: `u32` - /// - /// Stack: lhs, rhs **=>** (lhs ?? rhs) - Coalesce, - - /// Unary `typeof` operator. - /// - /// Operands: - /// - /// Stack: value **=>** (`typeof` value) - TypeOf, - - /// Unary `void` operator. - /// - /// Operands: - /// - /// Stack: value **=>** `undefined` - Void, - - /// Unary logical `!` operator. - /// - /// Operands: - /// - /// Stack: value **=>** (!value) - LogicalNot, - - /// Unary `+` operator. - /// - /// Operands: - /// - /// Stack: value **=>** (+value) - Pos, - - /// Unary `-` operator. - /// - /// Operands: - /// - /// Stack: value **=>** (-value) - Neg, - - /// Unary `++` operator. - /// - /// Operands: - /// - /// Stack: value **=>** (value + 1) - Inc, - - /// Unary postfix `++` operator. - /// - /// Operands: - /// - /// Stack: value **=>** (ToNumeric(value)), (value + 1) - IncPost, - - /// Unary `--` operator. - /// - /// Operands: - /// - /// Stack: value **=>** (value - 1) - Dec, - - /// Unary postfix `--` operator. - /// - /// Operands: - /// - /// Stack: value **=>** (ToNumeric(value)), (value - 1) - DecPost, - - /// Declare `var` type variable. - /// - /// Operands: name_index: `u32` - /// - /// Stack: **=>** - DefVar, - - /// Declare and initialize `var` type variable. - /// - /// Operands: name_index: `u32` - /// - /// Stack: value **=>** - DefInitVar, - - /// Initialize a lexical binding. - /// - /// Operands: name_index: `u32` - /// - /// Stack: value **=>** - PutLexicalValue, - - /// Throws an error because the binding access is illegal. - /// - /// Operands: binding_index: `u32` - /// - /// Stack: **=>** - ThrowMutateImmutable, - - /// Find a binding on the environment chain and push its value. - /// - /// Operands: name_index: `u32` - /// - /// Stack: **=>** value - GetName, - - /// Find a binding on the environment and set the `current_binding` of the current frame. - /// - /// Operands: name_index: `u32` - /// - /// Stack: **=>** - GetLocator, - - /// Find a binding on the environment chain and push its value to the stack and its - /// `BindingLocator` to the `bindings_stack`. - /// - /// Operands: name_index: `u32` - /// - /// Stack: **=>** value - GetNameAndLocator, - - /// Find a binding on the environment chain and push its value. If the binding does not exist push undefined. - /// - /// Operands: name_index: `u32` - /// - /// Stack: **=>** value - GetNameOrUndefined, - - /// Find a binding on the environment chain and assign its value. - /// - /// Operands: name_index: `u32` - /// - /// Stack: value **=>** - SetName, - - /// Assigns a value to the binding pointed by the top of the `bindings_stack`. - /// - /// Stack: value **=>** - SetNameByLocator, - - /// Deletes a property of the global object. - /// - /// Operands: name_index: `u32` - /// - /// Stack: **=>** deleted - DeleteName, - - /// Get a property by name from an object an push it on the stack. - /// - /// Like `object.name` - /// - /// Operands: name_index: `u32` - /// - /// Stack: object, receiver **=>** value - GetPropertyByName, - - /// Get a property method or undefined if the property is null or undefined. - /// - /// Throws if the method is not a callable object. - /// - /// Operands: name_index: `u32` - /// Stack: object **=>** object, method - GetMethod, - - /// Get a property by value from an object an push it on the stack. - /// - /// Like `object[key]` - /// - /// Operands: - /// - /// Stack: object, receiver, key **=>** value - GetPropertyByValue, - - /// Get a property by value from an object an push the key and value on the stack. - /// - /// Like `object[key]` - /// - /// Operands: - /// - /// Stack: object, receiver, key **=>** key, value - GetPropertyByValuePush, - - /// Sets a property by name of an object. - /// - /// Like `object.name = value` - /// - /// Operands: name_index: `u32` - /// - /// Stack: object, receiver, value **=>** value - SetPropertyByName, - - /// Sets the name of a function object. - /// - /// This operation is corresponds to the `SetFunctionName` abstract operation in the [spec]. - /// - /// The prefix operand is mapped as follows: - /// * 0 -> no prefix - /// * 1 -> "get " - /// * 2 -> "set " - /// - /// Operands: prefix: `u8` - /// - /// Stack: name, function **=>** function - /// - /// [spec]: https://tc39.es/ecma262/#sec-setfunctionname - SetFunctionName, - - /// Defines a own property of an object by name. - /// - /// Operands: name_index: `u32` - /// - /// Stack: object, value **=>** - DefineOwnPropertyByName, - - /// Defines a static class method by name. - /// - /// Operands: name_index: `u32` - /// - /// Stack: class, function **=>** - DefineClassStaticMethodByName, - - /// Defines a class method by name. - /// - /// Operands: name_index: `u32` - /// - /// Stack: class_proto, function **=>** - DefineClassMethodByName, - - /// Sets a property by value of an object. - /// - /// Like `object[key] = value` - /// - /// Operands: - /// - /// Stack: object, receiver, key, value **=>** value - SetPropertyByValue, - - /// Defines a own property of an object by value. - /// - /// Operands: - /// - /// Stack: object, key, value **=>** - DefineOwnPropertyByValue, - - /// Defines a static class method by value. - /// - /// Operands: - /// - /// Stack: class, key, function **=>** - DefineClassStaticMethodByValue, - - /// Defines a class method by value. - /// - /// Operands: - /// - /// Stack: class_proto, key, function **=>** - DefineClassMethodByValue, - - /// Sets a getter property by name of an object. - /// - /// Like `get name() value` - /// - /// Operands: name_index: `u32` - /// - /// Stack: object, value **=>** - SetPropertyGetterByName, - - /// Defines a static getter class method by name. - /// - /// Like `static get name() value` - /// - /// Operands: name_index: `u32` - /// - /// Stack: class, function **=>** - DefineClassStaticGetterByName, - - /// Defines a getter class method by name. - /// - /// Like `get name() value` - /// - /// Operands: name_index: `u32` - /// - /// Stack: class_proto, function **=>** class - DefineClassGetterByName, - - /// Sets a getter property by value of an object. - /// - /// Like `get [key]() value` - /// - /// Operands: - /// - /// Stack: object, key, value **=>** - SetPropertyGetterByValue, - - /// Defines a static getter class method by value. - /// - /// Like `static get [key]() value` - /// - /// Operands: - /// - /// Stack: class, key, function **=>** - DefineClassStaticGetterByValue, - - /// Defines a getter class method by value. - /// - /// Like `get [key]() value` - /// - /// Operands: - /// - /// Stack: class_proto, key, function **=>** - DefineClassGetterByValue, - - /// Sets a setter property by name of an object. - /// - /// Like `set name() value` - /// - /// Operands: name_index: `u32` - /// - /// Stack: object, value **=>** - SetPropertySetterByName, - - /// Defines a static setter class method by name. - /// - /// Like `static set name() value` - /// - /// Operands: name_index: `u32` - /// - /// Stack: class, function **=>** - DefineClassStaticSetterByName, - - /// Defines a setter class method by name. - /// - /// Like `set name() value` - /// - /// Operands: name_index: `u32` - /// - /// Stack: class_proto, function **=>** - DefineClassSetterByName, - - /// Sets a setter property by value of an object. - /// - /// Like `set [key]() value` - /// - /// Operands: - /// - /// Stack: object, key, value **=>** - SetPropertySetterByValue, - - /// Defines a static setter class method by value. - /// - /// Like `static set [key]() value` - /// - /// Operands: - /// - /// Stack: class, key, function **=>** - DefineClassStaticSetterByValue, - - /// Defines a setter class method by value. - /// - /// Like `set [key]() value` - /// - /// Operands: - /// - /// Stack: class_proto, key, function **=>** - DefineClassSetterByValue, - - /// Set the value of a private property of an object by it's name. - /// - /// Like `obj.#name = value` - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: object, value **=>** value - SetPrivateField, - - /// Define a private property of a class constructor by it's name. - /// - /// Like `#name = value` - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: object, value **=>** - DefinePrivateField, - - /// Set a private method of a class constructor by it's name. - /// - /// Like `#name() {}` - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: object, value **=>** - SetPrivateMethod, - - /// Set a private setter property of a class constructor by it's name. - /// - /// Like `set #name() {}` - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: object, value **=>** - SetPrivateSetter, - - /// Set a private getter property of a class constructor by it's name. - /// - /// Like `get #name() {}` - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: object, value **=>** - SetPrivateGetter, - - /// Get a private property by name from an object an push it on the stack. - /// - /// Like `object.#name` - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: object **=>** value - GetPrivateField, - - /// Push a field to a class. - /// - /// Operands: - /// - /// Stack: class, field_name, field_function **=>** - PushClassField, - - /// Push a private field to the class. - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: class, field_function **=>** - PushClassFieldPrivate, - - /// Push a private getter to the class. - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: class, getter **=>** - PushClassPrivateGetter, - - /// Push a private setter to the class. - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: class, setter **=>** - PushClassPrivateSetter, - - /// Push a private method to the class. - /// - /// Operands: private_name_index: `u32` - /// - /// Stack: class, method **=>** - PushClassPrivateMethod, - - /// Deletes a property by name of an object. - /// - /// Like `delete object.key` - /// - /// Operands: name_index: `u32` - /// - /// Stack: object **=>** - DeletePropertyByName, - - /// Deletes a property by value of an object. - /// - /// Like `delete object[key]` - /// - /// Operands: - /// - /// Stack: object, key **=>** - DeletePropertyByValue, - - /// Throws an error when trying to delete a property of `super` - /// - /// Operands: - /// - /// Stack: **=>** - DeleteSuperThrow, - - /// Copy all properties of one object to another object. - /// - /// Operands: excluded_key_count: `u32`, excluded_key_count_computed: `u32` - /// - /// Stack: excluded_key_computed_0 ... excluded_key_computed_n, source, value, excluded_key_0 ... excluded_key_n **=>** value - CopyDataProperties, - - /// Call ToPropertyKey on the value on the stack. - /// - /// Operands: - /// - /// Stack: value **=>** key - ToPropertyKey, - - /// Unconditional jump to address. - /// - /// Operands: address: `u32` - /// - /// Stack: **=>** - Jump, - - /// Conditional jump to address. - /// - /// If the value popped is [`truthy`][truthy] then jump to `address`. - /// - /// Operands: address: `u32` - /// - /// Stack: cond **=>** - /// - /// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy - JumpIfTrue, - - /// Conditional jump to address. - /// - /// If the value popped is [`falsy`][falsy] then jump to `address`. - /// - /// Operands: address: `u32` - /// - /// Stack: cond **=>** - /// - /// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy - JumpIfFalse, - - /// Conditional jump to address. - /// - /// If the value popped is not undefined jump to `address`. - /// - /// Operands: address: `u32` - /// - /// Stack: value **=>** value - JumpIfNotUndefined, - - /// Conditional jump to address. - /// - /// If the value popped is undefined jump to `address`. - /// - /// Operands: address: `u32` - /// - /// Stack: value **=>** value - JumpIfNullOrUndefined, - - /// 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: count: `u32`, default: `u32`, address: `u32` * count - /// - /// Stack: value: [`i32`] **=>** - JumpTable, - - /// Throw exception. - /// - /// This sets pending exception and searches for an exception handler. - /// - /// Operands: - /// - /// Stack: value **=>** - Throw, - - /// 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. - /// - /// Operands: - /// - /// Stack: **=>** - ReThrow, - - /// Get the thrown pending exception (if it's set) and push on the stack. - /// - /// If there is no pending exception, which can happend if we are handling `return()` call on generator, - /// then we rethrow the empty exception. See [`Opcode::ReThrow`]. - /// - /// Operands: - /// - /// Stack: **=>** exception - Exception, - - /// Get the thrown pending exception if it's set and push `true`, otherwise push only `false`. - /// - /// Operands: - /// - /// Stack: **=>** (`true`, exception) or `false` - MaybeException, - - /// Throw a new `TypeError` exception - /// - /// Operands: message: u32 - /// - /// Stack: **=>** - ThrowNewTypeError, - - /// Pops value converts it to boolean and pushes it back. - /// - /// Operands: - /// - /// Stack: value **=>** (`ToBoolean(value)`) - ToBoolean, - - /// Pushes `this` value - /// - /// Operands: - /// - /// Stack: **=>** this - This, - - /// Pushes the current `super` value to the stack. - /// - /// Operands: - /// - /// Stack: **=>** super - Super, - - /// Get the super constructor and the new target of the current environment. - /// - /// Operands: - /// - /// Stack: **=>** super_constructor, new_target - SuperCallPrepare, - - /// Execute the `super()` method. - /// - /// Operands: argument_count: `u32` - /// - /// Stack: super_constructor, new_target, argument_1, ... argument_n **=>** - SuperCall, - - /// Execute the `super()` method where the arguments contain spreads. - /// - /// Operands: - /// - /// Stack: super_constructor, new_target, arguments_array **=>** - SuperCallSpread, - - /// Execute the `super()` method when no constructor of the class is defined. - /// - /// Operands: - /// - /// Stack: argument_n, ... argument_1 **=>** - SuperCallDerived, - - /// Dynamically import a module. - /// - /// Operands: - /// - /// Stack: specifier **=>** promise - ImportCall, - - /// Pop the two values of the stack, strict equal compares the two values, - /// if true jumps to address, otherwise push the second pop'ed value. - /// - /// Operands: address: `u32` - /// - /// Stack: value, cond **=>** cond (if `cond !== value`). - Case, - - /// Pops the top of stack and jump to address. - /// - /// Operands: address: `u32` - /// - /// Stack: `value` **=>** - Default, - - /// Get arrow function from the pre-compiled inner functions. - /// - /// Operands: address: `u32`, method: `u8` - /// - /// Stack: **=>** func - GetArrowFunction, - - /// Get async arrow function from the pre-compiled inner functions. - /// - /// Operands: address: `u32`, method: `u8` - /// - /// Stack: **=>** func - GetAsyncArrowFunction, - - /// Get function from the pre-compiled inner functions. - /// - /// Operands: address: `u32`, is_method: `u8` - /// - /// Stack: **=>** func - GetFunction, - - /// Get async function from the pre-compiled inner functions. - /// - /// Operands: address: `u32`, method: `u8` - /// - /// Stack: **=>** func - GetFunctionAsync, - - /// Get generator function from the pre-compiled inner functions. - /// - /// Operands: address: `u32`, - /// - /// Stack: **=>** func - GetGenerator, - - /// Get async generator function from the pre-compiled inner functions. - /// - /// Operands: address: `u32`, - /// - /// Stack: **=>** func - GetGeneratorAsync, - - /// Call a function named "eval". - /// - /// Operands: argument_count: `u32` - /// - /// Stack: this, func, argument_1, ... argument_n **=>** result - CallEval, - - /// Call a function named "eval" where the arguments contain spreads. - /// - /// Operands: - /// - /// Stack: this, func, arguments_array **=>** result - CallEvalSpread, - - /// Call a function. - /// - /// Operands: argument_count: `u32` - /// - /// Stack: this, func, argument_1, ... argument_n **=>** result - Call, - - /// Call a function where the arguments contain spreads. - /// - /// Operands: - /// - /// Stack: this, func, arguments_array **=>** result - CallSpread, - - /// Call construct on a function. - /// - /// Operands: argument_count: `u32` - /// - /// Stack: func, argument_1, ... argument_n **=>** result - New, - - /// Call construct on a function where the arguments contain spreads. - /// - /// Operands: - /// - /// Stack: arguments_array, func **=>** result - NewSpread, - - /// Return from a function. - /// - /// Operands: - /// - /// Stack: **=>** - Return, - - /// Close an async generator function. - /// - /// Operands: - /// - /// Stack: **=>** - AsyncGeneratorClose, - - /// Creates the generator object and yields. - /// - /// Operands: async: `u8` - /// - /// Stack: **=>** resume_kind - Generator, - - /// Get return value of a function. - /// - /// Operands: - /// - /// Stack: **=>** value - GetReturnValue, - - /// Set return value of a function. - /// - /// Operands: - /// - /// Stack: value **=>** - SetReturnValue, - - /// Push a declarative environment. - /// - /// Operands: compile_environments_index: `u32` - /// - /// Stack: **=>** - PushDeclarativeEnvironment, - - /// Push an object environment. - /// - /// Operands: - /// - /// Stack: object **=>** - PushObjectEnvironment, - - /// Push a function environment. - /// - /// Operands: compile_environments_index: `u32` - /// - /// Stack: **=>** - PushFunctionEnvironment, - - /// Pop the current environment. - /// - /// Operands: - /// - /// Stack: **=>** - PopEnvironment, - - /// Increment loop itearation count. - /// - /// Used for limiting the loop iteration. - /// - /// Operands: - /// - /// Stack: **=>** - IncrementLoopIteration, - - /// Creates the ForInIterator of an object. - /// - /// Stack: object **=>** - /// - /// Iterator Stack: `iterator` - CreateForInIterator, - - /// Gets the iterator of an object. - /// - /// Operands: - /// - /// Stack: object **=>** - /// - /// Iterator Stack: `iterator` - GetIterator, - - /// Gets the async iterator of an object. - /// - /// Operands: - /// - /// Stack: object **=>** - /// - /// Iterator Stack: `iterator` - GetAsyncIterator, - - /// Calls the `next` method of `iterator`, updating its record with the next value. - /// - /// Operands: - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorNext, - - /// Calls the `next` method of `iterator`, updating its record with the next value. - /// - /// Operands: - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorNextWithoutPop, - - /// Returns `true` if the current iterator is done, or `false` otherwise - /// - /// Stack: **=>** done - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorDone, - - /// Finishes the call to `Opcode::IteratorNext` within a `for await` loop by setting the current - /// result of the current iterator. - /// - /// Operands: - /// - /// Stack: `next_result`, `resume_kind` **=>** `resume_kind` - /// - /// Iterator Stack: iterator **=>** iterator - IteratorFinishAsyncNext, - - /// Gets the `value` property of the current iterator record. - /// - /// Stack: **=>** `value` - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorValue, - - /// Gets the `value` property of the current iterator record. - /// - /// Stack: **=>** `value` - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorValueWithoutPop, - - /// Gets the last iteration result of the current iterator record. - /// - /// Stack: **=>** `result` - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorResult, - - /// Consume the iterator and construct and array with all the values. - /// - /// Operands: - /// - /// Stack: **=>** array - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorToArray, - - /// Pop an iterator from the call frame close iterator stack. - /// - /// Iterator Stack: - /// - `iterator` **=>** - IteratorPop, - - /// Pushes `true` to the stack if the iterator stack is empty. - /// - /// Stack: - /// - **=>** `is_empty` - /// - /// Iterator Stack: - /// - **=>** - IteratorStackEmpty, - - /// Creates a new iterator result object. - /// - /// Operands: - /// - done: bool (codified as u8 with `0` -> `false` and `!0` -> `true`) - /// - /// Stack: - /// - value **=>** - /// - CreateIteratorResult, - - /// Calls `return` on the current iterator and returns the result. - /// - /// Stack: **=>** return_val (if return is a method), is_return_method - /// - /// Iterator Stack: `iterator` **=>** - IteratorReturn, - - /// Concat multiple stack objects into a string. - /// - /// Operands: value_count: `u32` - /// - /// Stack: `value_1`,...`value_n` **=>** `string` - ConcatToString, - - /// Call RequireObjectCoercible on the stack value. - /// - /// Operands: - /// - /// Stack: value **=>** value - RequireObjectCoercible, - - /// Require the stack value to be neither null nor undefined. - /// - /// Operands: - /// - /// Stack: value **=>** value - ValueNotNullOrUndefined, - - /// Initialize the rest parameter value of a function from the remaining arguments. - /// - /// Operands: - /// - /// Stack: `argument_1` .. `argument_n` **=>** `array` - RestParameterInit, - - /// Pop the remaining arguments of a function. - /// - /// Operands: - /// - /// Stack: `argument_1` .. `argument_n` **=>** - RestParameterPop, - - /// Yields from the current generator execution. - /// - /// Operands: - /// - /// Stack: **=>** resume_kind, received - GeneratorYield, - - /// Resumes the current generator function. - /// - /// If the `resume_kind` is `Throw`, then the value is poped and thrown, otherwise if `Return` - /// we pop the value, set it as the return value and throw and empty exception. See [`Opcode::ReThrow`]. - /// - /// Operands: - /// - /// Stack: `resume_kind`, value **=>** value - GeneratorNext, - - /// Yields from the current async generator execution. - /// - /// Operands: - /// - /// Stack: value **=>** received - AsyncGeneratorYield, - - /// Create a promise capacity for an async function, if not already set. - /// - /// Operands: - /// - /// Stack: **=>** - CreatePromiseCapability, - - /// Resolves or rejects the promise capability of an async function. - /// - /// If the pending exception is set, reject and rethrow the exception, otherwise resolve. - /// - /// Operands: - /// - /// Stack: **=>** - CompletePromiseCapability, - - /// Jumps to the specified address if the resume kind is not equal. - /// - /// Operands: `exit`: `u32`, `resume_kind`: `u8`. - /// - /// Stack: `resume_kind` **=>** `resume_kind` - JumpIfNotResumeKind, - - /// Delegates the current async generator function to another iterator. - /// - /// Operands: throw_method_undefined: `u32`, return_method_undefined: `u32` - /// - /// Stack: received **=>** result - GeneratorDelegateNext, - - /// Resume the async generator with yield delegate logic after it awaits a value. - /// - /// Operands: return: `u32`, exit: `u32` - /// - /// Stack: is_return, received **=>** value - GeneratorDelegateResume, - - /// Stops the current async function and schedules it to resume later. - /// - /// Operands: - /// - /// Stack: promise **=>** received - Await, - - /// Push the current new target to the stack. - /// - /// Operands: - /// - /// Stack: **=>** `new.target` - NewTarget, - - /// Push the current `import.meta` to the stack. - /// - /// Operands: - /// - /// Stack: **=>** `import.meta` - ImportMeta, - - /// Pushes `true` to the stack if the top stack value is an object, or `false` otherwise. - /// - /// Operands: - /// - /// Stack: value **=>** is_object - IsObject, - - /// Lookup if a tagged template object is cached and skip the creation if it is. - /// - /// Operands: jump: `u32`, site: `u64` - /// - /// Stack: **=>** template (if cached) - TemplateLookup, - - /// Create a new tagged template object and cache it. - /// - /// Operands: count: `u32`, site: `u64` - /// - /// Stack: count * (cooked_value, raw_value) **=>** template - TemplateCreate, - - /// Push a private environment. - /// - /// Operands: count: `u32`, count * private_name_index: `u32` - /// - /// Stack: class **=>** class - PushPrivateEnvironment, - - /// Pop a private environment. - /// - /// Operands: - /// - /// Stack: **=>** - PopPrivateEnvironment, - - /// No-operation instruction, does nothing. - /// - /// Operands: - /// - /// Stack: **=>** - Nop, - - /// 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, - } +generate_opcodes! { + /// Pop the top value from the stack. + /// + /// Operands: + /// + /// Stack: value **=>** + Pop, + + /// Push a copy of the top value on the stack. + /// + /// Operands: + /// + /// Stack: value **=>** value, value + Dup, + + /// Swap the top two values on the stack. + /// + /// Operands: + /// + /// Stack: second, first **=>** first, second + Swap, + + /// Rotates the top `n` values of the stack to the left by `1`. + /// + /// Equivalent to calling [`slice::rotate_left`] with argument `1` on the top `n` values of the + /// stack. + /// + /// Operands: n: `u8` + /// + /// Stack: v\[n\], v\[n-1\], ... , v\[1\], v\[0\] **=>** v\[n-1\], ... , v\[1\], v\[0\], v\[n\] + RotateLeft { n: u8 }, + + /// Rotates the top `n` values of the stack to the right by `1`. + /// + /// Equivalent to calling [`slice::rotate_right`] with argument `1` on the top `n` values of the + /// stack. + /// + /// Operands: n: `u8` + /// + /// Stack: v\[n\], v\[n-1\], ... , v\[1\], v\[0\] **=>** v\[0\], v\[n\], v\[n-1\], ... , v\[1\] + RotateRight { n: u8 }, + + /// Push integer `0` on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `0` + PushZero, + + /// Push integer `1` on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `1` + PushOne, + + /// Push `i8` value on the stack. + /// + /// Operands: value: `i8` + /// + /// Stack: **=>** value + PushInt8 { value: i8 }, + + /// Push i16 value on the stack. + /// + /// Operands: value: `i16` + /// + /// Stack: **=>** value + PushInt16 { value: i16 }, + + /// Push i32 value on the stack. + /// + /// Operands: value: `i32` + /// + /// Stack: **=>** value + PushInt32 { value: i32 }, + + /// Push `f32` value on the stack. + /// + /// Operands: value: `f32` + /// + /// Stack: **=>** value + PushFloat { value: f32 }, + + /// Push `f64` value on the stack. + /// + /// Operands: value: `f64` + /// + /// Stack: **=>** value + PushDouble { value: f64 }, + + /// Push `NaN` integer on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `NaN` + PushNaN, + + /// Push `Infinity` value on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `Infinity` + PushPositiveInfinity, + + /// Push `-Infinity` value on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `-Infinity` + PushNegativeInfinity, + + /// Push `null` value on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `null` + PushNull, + + /// Push `true` value on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `true` + PushTrue, + + /// Push `false` value on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `false` + PushFalse, + + /// Push `undefined` value on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `undefined` + PushUndefined, + + /// Push literal value on the stack. + /// + /// Like strings and bigints. The index operand is used to index into the `literals` + /// array to get the value. + /// + /// Operands: index: `u32` + /// + /// Stack: **=>** (`literals[index]`) + PushLiteral { index: u32 }, + + /// Push empty object `{}` value on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `{}` + PushEmptyObject, + + /// Get the prototype of a superclass and push it on the stack. + /// + /// Operands: + /// + /// Stack: class, superclass **=>** class, superclass.prototype + PushClassPrototype, + + /// Set the prototype of a class object. + /// + /// Operands: + /// + /// Stack: class, prototype **=>** class.prototype + SetClassPrototype, + + /// Set home object internal slot of an object literal method. + /// + /// Operands: + /// + /// Stack: home, function **=>** home, function + SetHomeObject, + + /// Set home object internal slot of a class method. + /// + /// Operands: + /// + /// Stack: home, function **=>** home, function + SetHomeObjectClass, + + /// Set the prototype of an object if the value is an object or null. + /// + /// Operands: + /// + /// Stack: object, value **=>** + SetPrototype, + + /// Push an empty array value on the stack. + /// + /// Operands: + /// + /// Stack: **=>** `[]` + PushNewArray, + + /// Push a value to an array. + /// + /// Operands: + /// + /// Stack: array, value **=>** array + PushValueToArray, + + /// Push an empty element/hole to an array. + /// + /// Operands: + /// + /// Stack: array **=>** array + PushElisionToArray, + + /// Push all iterator values to an array. + /// + /// Operands: + /// + /// Stack: array, iterator, next_method **=>** array + PushIteratorToArray, + + /// Binary `+` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs + rhs) + Add, + + /// Binary `-` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs - rhs) + Sub, + + /// Binary `/` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs / rhs) + Div, + + /// Binary `*` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs * rhs) + Mul, + + /// Binary `%` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs % rhs) + Mod, + + /// Binary `**` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs ** rhs) + Pow, + + /// Binary `>>` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs >> rhs) + ShiftRight, + + /// Binary `<<` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** `(lhs << rhs)` + ShiftLeft, + + /// Binary `>>>` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs >>> rhs) + UnsignedShiftRight, + + /// Binary bitwise `|` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs | rhs) + BitOr, + + /// Binary bitwise `&` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs & rhs) + BitAnd, + + /// Binary bitwise `^` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs ^ rhs) + BitXor, + + /// Unary bitwise `~` operator. + /// + /// Operands: + /// + /// Stack: value **=>** ~value + BitNot, + + /// Binary `in` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs `in` rhs) + In, + + /// Binary `in` operator for private names. + /// + /// Operands: index: `u32` + /// + /// Stack: rhs **=>** (private_name `in` rhs) + InPrivate { index: u32 }, + + /// Binary `==` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs `==` rhs) + Eq, + + /// Binary `===` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs `===` rhs) + StrictEq, + + /// Binary `!=` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs `!=` rhs) + NotEq, + + /// Binary `!==` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs `!==` rhs) + StrictNotEq, + + /// Binary `>` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs > rhs) + GreaterThan, + + /// Binary `>=` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs >= rhs) + GreaterThanOrEq, + + /// Binary `<` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** `(lhs < rhs)` + LessThan, + + /// Binary `<=` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** `(lhs <= rhs)` + LessThanOrEq, + + /// Binary `instanceof` operator. + /// + /// Operands: + /// + /// Stack: lhs, rhs **=>** (lhs instanceof rhs) + InstanceOf, + + /// Binary logical `&&` operator. + /// + /// This is a short-circuit operator, if the `lhs` value is `false`, then it jumps to `exit` address. + /// + /// Operands: exit: `u32` + /// + /// Stack: lhs, rhs **=>** (lhs && rhs) + LogicalAnd { exit: u32 }, + + /// Binary logical `||` operator. + /// + /// This is a short-circuit operator, if the `lhs` value is `true`, then it jumps to `exit` address. + /// + /// Operands: exit: `u32` + /// + /// Stack: lhs, rhs **=>** (lhs || rhs) + LogicalOr { exit: u32 }, + + /// Binary `??` operator. + /// + /// This is a short-circuit operator, if the `lhs` value is **not** `null` or `undefined`, + /// then it jumps to `exit` address. + /// + /// Operands: exit: `u32` + /// + /// Stack: lhs, rhs **=>** (lhs ?? rhs) + Coalesce { exit: u32 }, + + /// Unary `typeof` operator. + /// + /// Operands: + /// + /// Stack: value **=>** (`typeof` value) + TypeOf, + + /// Unary `void` operator. + /// + /// Operands: + /// + /// Stack: value **=>** `undefined` + Void, + + /// Unary logical `!` operator. + /// + /// Operands: + /// + /// Stack: value **=>** (!value) + LogicalNot, + + /// Unary `+` operator. + /// + /// Operands: + /// + /// Stack: value **=>** (+value) + Pos, + + /// Unary `-` operator. + /// + /// Operands: + /// + /// Stack: value **=>** (-value) + Neg, + + /// Unary `++` operator. + /// + /// Operands: + /// + /// Stack: value **=>** (value + 1) + Inc, + + /// Unary postfix `++` operator. + /// + /// Operands: + /// + /// Stack: value **=>** (ToNumeric(value)), (value + 1) + IncPost, + + /// Unary `--` operator. + /// + /// Operands: + /// + /// Stack: value **=>** (value - 1) + Dec, + + /// Unary postfix `--` operator. + /// + /// Operands: + /// + /// Stack: value **=>** (ToNumeric(value)), (value - 1) + DecPost, + + /// Declare `var` type variable. + /// + /// Operands: index: `u32` + /// + /// Stack: **=>** + DefVar { index: u32 }, + + /// Declare and initialize `var` type variable. + /// + /// Operands: index: `u32` + /// + /// Stack: value **=>** + DefInitVar { index: u32 }, + + /// Initialize a lexical binding. + /// + /// Operands: index: `u32` + /// + /// Stack: value **=>** + PutLexicalValue { index: u32 }, + + /// Throws an error because the binding access is illegal. + /// + /// Operands: index: `u32` + /// + /// Stack: **=>** + ThrowMutateImmutable { index: u32 }, + + /// Find a binding on the environment chain and push its value. + /// + /// Operands: index: `u32` + /// + /// Stack: **=>** value + GetName { index: u32 }, + + /// Find a binding on the environment and set the `current_binding` of the current frame. + /// + /// Operands: index: `u32` + /// + /// Stack: **=>** + GetLocator { index: u32 }, + + /// Find a binding on the environment chain and push its value to the stack and its + /// `BindingLocator` to the `bindings_stack`. + /// + /// Operands: index: `u32` + /// + /// Stack: **=>** value + GetNameAndLocator { index: u32 }, + + /// Find a binding on the environment chain and push its value. If the binding does not exist push undefined. + /// + /// Operands: index: `u32` + /// + /// Stack: **=>** value + GetNameOrUndefined { index: u32 }, + + /// Find a binding on the environment chain and assign its value. + /// + /// Operands: index: `u32` + /// + /// Stack: value **=>** + SetName { index: u32 }, + + /// Assigns a value to the binding pointed by the top of the `bindings_stack`. + /// + /// Stack: value **=>** + SetNameByLocator, + + /// Deletes a property of the global object. + /// + /// Operands: index: `u32` + /// + /// Stack: **=>** deleted + DeleteName { index: u32 }, + + /// Get a property by name from an object an push it on the stack. + /// + /// Like `object.name` + /// + /// Operands: index: `u32` + /// + /// Stack: object, receiver **=>** value + GetPropertyByName { index: u32 }, + + /// Get a property method or undefined if the property is null or undefined. + /// + /// Throws if the method is not a callable object. + /// + /// Operands: index: `u32` + /// Stack: object **=>** object, method + GetMethod { index: u32 }, + + /// Get a property by value from an object an push it on the stack. + /// + /// Like `object[key]` + /// + /// Operands: + /// + /// Stack: object, receiver, key **=>** value + GetPropertyByValue, + + /// Get a property by value from an object an push the key and value on the stack. + /// + /// Like `object[key]` + /// + /// Operands: + /// + /// Stack: object, receiver, key **=>** key, value + GetPropertyByValuePush, + + /// Sets a property by name of an object. + /// + /// Like `object.name = value` + /// + /// Operands: index: `u32` + /// + /// Stack: object, receiver, value **=>** value + SetPropertyByName { index: u32 }, + + /// Sets the name of a function object. + /// + /// This operation is corresponds to the `SetFunctionName` abstract operation in the [spec]. + /// + /// The prefix operand is mapped as follows: + /// * 0 -> no prefix + /// * 1 -> "get " + /// * 2 -> "set " + /// + /// Operands: prefix: `u8` + /// + /// Stack: name, function **=>** function + /// + /// [spec]: https://tc39.es/ecma262/#sec-setfunctionname + SetFunctionName { prefix: u8 }, + + /// Defines a own property of an object by name. + /// + /// Operands: index: `u32` + /// + /// Stack: object, value **=>** + DefineOwnPropertyByName { index: u32 }, + + /// Defines a static class method by name. + /// + /// Operands: index: `u32` + /// + /// Stack: class, function **=>** + DefineClassStaticMethodByName { index: u32 }, + + /// Defines a class method by name. + /// + /// Operands: index: `u32` + /// + /// Stack: class_proto, function **=>** + DefineClassMethodByName { index: u32 }, + + /// Sets a property by value of an object. + /// + /// Like `object[key] = value` + /// + /// Operands: + /// + /// Stack: object, receiver, key, value **=>** value + SetPropertyByValue, + + /// Defines a own property of an object by value. + /// + /// Operands: + /// + /// Stack: object, key, value **=>** + DefineOwnPropertyByValue, + + /// Defines a static class method by value. + /// + /// Operands: + /// + /// Stack: class, key, function **=>** + DefineClassStaticMethodByValue, + + /// Defines a class method by value. + /// + /// Operands: + /// + /// Stack: class_proto, key, function **=>** + DefineClassMethodByValue, + + /// Sets a getter property by name of an object. + /// + /// Like `get name() value` + /// + /// Operands: index: `u32` + /// + /// Stack: object, value **=>** + SetPropertyGetterByName { index: u32 }, + + /// Defines a static getter class method by name. + /// + /// Like `static get name() value` + /// + /// Operands: index: `u32` + /// + /// Stack: class, binding_function **=>** + DefineClassStaticGetterByName { index: u32 }, + + /// Defines a getter class method by name. + /// + /// Like `get name() value` + /// + /// Operands: index: `u32` + /// + /// Stack: class_proto, function **=>** class + DefineClassGetterByName { index: u32 }, + + /// Sets a getter property by value of an object. + /// + /// Like `get [key]() value` + /// + /// Operands: + /// + /// Stack: object, key, value **=>** + SetPropertyGetterByValue, + + /// Defines a static getter class method by value. + /// + /// Like `static get [key]() value` + /// + /// Operands: + /// + /// Stack: class, key, function **=>** + DefineClassStaticGetterByValue, + + /// Defines a getter class method by value. + /// + /// Like `get [key]() value` + /// + /// Operands: + /// + /// Stack: class_proto, key, function **=>** + DefineClassGetterByValue, + + /// Sets a setter property by name of an object. + /// + /// Like `set name() value` + /// + /// Operands: index: `u32` + /// + /// Stack: object, value **=>** + SetPropertySetterByName { index: u32 }, + + /// Defines a static setter class method by name. + /// + /// Like `static set name() value` + /// + /// Operands: index: `u32` + /// + /// Stack: class, function **=>** + DefineClassStaticSetterByName { index: u32 }, + + /// Defines a setter class method by name. + /// + /// Like `set name() value` + /// + /// Operands: index: `u32` + /// + /// Stack: class_proto, function **=>** + DefineClassSetterByName { index: u32 }, + + /// Sets a setter property by value of an object. + /// + /// Like `set [key]() value` + /// + /// Operands: + /// + /// Stack: object, key, value **=>** + SetPropertySetterByValue, + + /// Defines a static setter class method by value. + /// + /// Like `static set [key]() value` + /// + /// Operands: + /// + /// Stack: class, key, function **=>** + DefineClassStaticSetterByValue, + + /// Defines a setter class method by value. + /// + /// Like `set [key]() value` + /// + /// Operands: + /// + /// Stack: class_proto, key, function **=>** + DefineClassSetterByValue, + + /// Set the value of a private property of an object by it's name. + /// + /// Like `obj.#name = value` + /// + /// Operands: index: `u32` + /// + /// Stack: object, value **=>** value + SetPrivateField { index: u32 }, + + /// Define a private property of a class constructor by it's name. + /// + /// Like `#name = value` + /// + /// Operands: index: `u32` + /// + /// Stack: object, value **=>** + DefinePrivateField { index: u32 }, + + /// Set a private method of a class constructor by it's name. + /// + /// Like `#name() {}` + /// + /// Operands: index: `u32` + /// + /// Stack: object, value **=>** + SetPrivateMethod { index: u32 }, + + /// Set a private setter property of a class constructor by it's name. + /// + /// Like `set #name() {}` + /// + /// Operands: index: `u32` + /// + /// Stack: object, value **=>** + SetPrivateSetter { index: u32 }, + + /// Set a private getter property of a class constructor by it's name. + /// + /// Like `get #name() {}` + /// + /// Operands: index: `u32` + /// + /// Stack: object, value **=>** + SetPrivateGetter { index: u32 }, + + /// Get a private property by name from an object an push it on the stack. + /// + /// Like `object.#name` + /// + /// Operands: index: `u32` + /// + /// Stack: object **=>** value + GetPrivateField { index: u32 }, + + /// Push a field to a class. + /// + /// Operands: + /// + /// Stack: class, field_name, field_function **=>** + PushClassField, + + /// Push a private field to the class. + /// + /// Operands: index: `u32` + /// + /// Stack: class, field_function **=>** + PushClassFieldPrivate { index: u32 }, + + /// Push a private getter to the class. + /// + /// Operands: index: `u32` + /// + /// Stack: class, getter **=>** + PushClassPrivateGetter { index: u32 }, + + /// Push a private setter to the class. + /// + /// Operands: index: `u32` + /// + /// Stack: class, setter **=>** + PushClassPrivateSetter { index: u32 }, + + /// Push a private method to the class. + /// + /// Operands: index: `u32` + /// + /// Stack: class, method **=>** + PushClassPrivateMethod { index: u32 }, + + /// Deletes a property by name of an object. + /// + /// Like `delete object.key` + /// + /// Operands: index: `u32` + /// + /// Stack: object **=>** + DeletePropertyByName { index: u32 }, + + /// Deletes a property by value of an object. + /// + /// Like `delete object[key]` + /// + /// Operands: + /// + /// Stack: object, key **=>** + DeletePropertyByValue, + + /// Throws an error when trying to delete a property of `super` + /// + /// Operands: + /// + /// Stack: **=>** + DeleteSuperThrow, + + /// Copy all properties of one object to another object. + /// + /// Operands: excluded_key_count: `u32`, excluded_key_count_computed: `u32` + /// + /// Stack: excluded_key_computed_0 ... excluded_key_computed_n, source, value, excluded_key_0 ... excluded_key_n **=>** value + CopyDataProperties { excluded_key_count: u32, excluded_key_count_computed: u32 }, + + /// Call ToPropertyKey on the value on the stack. + /// + /// Operands: + /// + /// Stack: value **=>** key + ToPropertyKey, + + /// Unconditional jump to address. + /// + /// Operands: address: `u32` + /// + /// Stack: **=>** + Jump { address: u32 }, + + /// Conditional jump to address. + /// + /// If the value popped is [`truthy`][truthy] then jump to `address`. + /// + /// Operands: address: `u32` + /// + /// Stack: cond **=>** + /// + /// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy + JumpIfTrue { address: u32 }, + + /// Conditional jump to address. + /// + /// If the value popped is [`falsy`][falsy] then jump to `address`. + /// + /// Operands: address: `u32` + /// + /// Stack: cond **=>** + /// + /// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy + JumpIfFalse { address: u32 }, + + /// Conditional jump to address. + /// + /// If the value popped is not undefined jump to `address`. + /// + /// Operands: address: `u32` + /// + /// Stack: value **=>** value + JumpIfNotUndefined { address: u32 }, + + /// Conditional jump to address. + /// + /// If the value popped is undefined jump to `address`. + /// + /// Operands: address: `u32` + /// + /// Stack: value **=>** value + JumpIfNullOrUndefined { address: u32 }, + + /// 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: default: `u32`, count: `u32`, address: `u32` * count + /// + /// Stack: value: [`i32`] **=>** + JumpTable { default: u32, addresses: ThinVec }, + + /// Throw exception. + /// + /// This sets pending exception and searches for an exception handler. + /// + /// Operands: + /// + /// Stack: value **=>** + Throw, + + /// 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. + /// + /// Operands: + /// + /// Stack: **=>** + ReThrow, + + /// Get the thrown pending exception (if it's set) and push on the stack. + /// + /// If there is no pending exception, which can happend if we are handling `return()` call on generator, + /// then we rethrow the empty exception. See [`Opcode::ReThrow`]. + /// + /// Operands: + /// + /// Stack: **=>** exception + Exception, + + /// Get the thrown pending exception if it's set and push `true`, otherwise push only `false`. + /// + /// Operands: + /// + /// Stack: **=>** (`true`, exception) or `false` + MaybeException, + + /// Throw a new `TypeError` exception + /// + /// Operands: message: u32 + /// + /// Stack: **=>** + ThrowNewTypeError { message: u32 }, + + /// Pops value converts it to boolean and pushes it back. + /// + /// Operands: + /// + /// Stack: value **=>** (`ToBoolean(value)`) + ToBoolean, + + /// Pushes `this` value + /// + /// Operands: + /// + /// Stack: **=>** this + This, + + /// Pushes the current `super` value to the stack. + /// + /// Operands: + /// + /// Stack: **=>** super + Super, + + /// Get the super constructor and the new target of the current environment. + /// + /// Operands: + /// + /// Stack: **=>** super_constructor, new_target + SuperCallPrepare, + + /// Execute the `super()` method. + /// + /// Operands: argument_count: `u32` + /// + /// Stack: super_constructor, new_target, argument_1, ... argument_n **=>** + SuperCall { argument_count: u32 }, + + /// Execute the `super()` method where the arguments contain spreads. + /// + /// Operands: + /// + /// Stack: super_constructor, new_target, arguments_array **=>** + SuperCallSpread, + + /// Execute the `super()` method when no constructor of the class is defined. + /// + /// Operands: + /// + /// Stack: argument_n, ... argument_1 **=>** + SuperCallDerived, + + /// Dynamically import a module. + /// + /// Operands: + /// + /// Stack: specifier **=>** promise + ImportCall, + + /// Pop the two values of the stack, strict equal compares the two values, + /// if true jumps to address, otherwise push the second pop'ed value. + /// + /// Operands: address: `u32` + /// + /// Stack: value, cond **=>** cond (if `cond !== value`). + Case { address: u32 }, + + /// Pops the top of stack and jump to address. + /// + /// Operands: address: `u32` + /// + /// Stack: `value` **=>** + Default { address: u32 }, + + /// Get arrow function from the pre-compiled inner functions. + /// + /// Operands: address: `u32`, method: `u8` + /// + /// Stack: **=>** func + GetArrowFunction { index: u32, method: bool }, + + /// Get async arrow function from the pre-compiled inner functions. + /// + /// Operands: index: `u32`, method: `u8` + /// + /// Stack: **=>** func + GetAsyncArrowFunction { index: u32, method: bool }, + + /// Get function from the pre-compiled inner functions. + /// + /// Operands: index: `u32`, is_method: `u8` + /// + /// Stack: **=>** func + GetFunction { index: u32, method: bool }, + + /// Get async function from the pre-compiled inner functions. + /// + /// Operands: index: `u32`, method: `u8` + /// + /// Stack: **=>** func + GetFunctionAsync { index: u32, method: bool }, + + /// Get generator function from the pre-compiled inner functions. + /// + /// Operands: index: `u32`, + /// + /// Stack: **=>** func + GetGenerator { index: u32 }, + + /// Get async generator function from the pre-compiled inner functions. + /// + /// Operands: index: `u32`, + /// + /// Stack: **=>** func + GetGeneratorAsync { index: u32 }, + + /// Call a function named "eval". + /// + /// Operands: argument_count: `u32` + /// + /// Stack: this, func, argument_1, ... argument_n **=>** result + CallEval { argument_count: u32 }, + + /// Call a function named "eval" where the arguments contain spreads. + /// + /// Operands: + /// + /// Stack: this, func, arguments_array **=>** result + CallEvalSpread, + + /// Call a function. + /// + /// Operands: argument_count: `u32` + /// + /// Stack: this, func, argument_1, ... argument_n **=>** result + Call { argument_count: u32 }, + + /// Call a function where the arguments contain spreads. + /// + /// Operands: + /// + /// Stack: this, func, arguments_array **=>** result + CallSpread, + + /// Call construct on a function. + /// + /// Operands: argument_count: `u32` + /// + /// Stack: func, argument_1, ... argument_n **=>** result + New { argument_count: u32 }, + + /// Call construct on a function where the arguments contain spreads. + /// + /// Operands: + /// + /// Stack: arguments_array, func **=>** result + NewSpread, + + /// Return from a function. + /// + /// Operands: + /// + /// Stack: **=>** + Return, + + /// Close an async generator function. + /// + /// Operands: + /// + /// Stack: **=>** + AsyncGeneratorClose, + + /// Creates the generator object and yields. + /// + /// Operands: async: `u8` + /// + /// Stack: **=>** resume_kind + Generator { r#async: bool }, + + /// Get return value of a function. + /// + /// Operands: + /// + /// Stack: **=>** value + GetReturnValue, + + /// Set return value of a function. + /// + /// Operands: + /// + /// Stack: value **=>** + SetReturnValue, + + /// Push a declarative environment. + /// + /// Operands: compile_environments_index: `u32` + /// + /// Stack: **=>** + PushDeclarativeEnvironment { compile_environments_index: u32 }, + + /// Push an object environment. + /// + /// Operands: + /// + /// Stack: object **=>** + PushObjectEnvironment, + + /// Push a function environment. + /// + /// Operands: compile_environments_index: `u32` + /// + /// Stack: **=>** + PushFunctionEnvironment { compile_environments_index: u32 }, + + /// Pop the current environment. + /// + /// Operands: + /// + /// Stack: **=>** + PopEnvironment, + + /// Increment loop itearation count. + /// + /// Used for limiting the loop iteration. + /// + /// Operands: + /// + /// Stack: **=>** + IncrementLoopIteration, + + /// Creates the ForInIterator of an object. + /// + /// Stack: object **=>** + /// + /// Iterator Stack: `iterator` + CreateForInIterator, + + /// Gets the iterator of an object. + /// + /// Operands: + /// + /// Stack: object **=>** + /// + /// Iterator Stack: `iterator` + GetIterator, + + /// Gets the async iterator of an object. + /// + /// Operands: + /// + /// Stack: object **=>** + /// + /// Iterator Stack: `iterator` + GetAsyncIterator, + + /// Calls the `next` method of `iterator`, updating its record with the next value. + /// + /// Operands: + /// + /// Iterator Stack: `iterator` **=>** `iterator` + IteratorNext, + + /// Calls the `next` method of `iterator`, updating its record with the next value. + /// + /// Operands: + /// + /// Iterator Stack: `iterator` **=>** `iterator` + IteratorNextWithoutPop, + + /// Returns `true` if the current iterator is done, or `false` otherwise + /// + /// Stack: **=>** done + /// + /// Iterator Stack: `iterator` **=>** `iterator` + IteratorDone, + + /// Finishes the call to `Opcode::IteratorNext` within a `for await` loop by setting the current + /// result of the current iterator. + /// + /// Operands: + /// + /// Stack: `next_result`, `resume_kind` **=>** `resume_kind` + /// + /// Iterator Stack: iterator **=>** iterator + IteratorFinishAsyncNext, + + /// Gets the `value` property of the current iterator record. + /// + /// Stack: **=>** `value` + /// + /// Iterator Stack: `iterator` **=>** `iterator` + IteratorValue, + + /// Gets the `value` property of the current iterator record. + /// + /// Stack: **=>** `value` + /// + /// Iterator Stack: `iterator` **=>** `iterator` + IteratorValueWithoutPop, + + /// Gets the last iteration result of the current iterator record. + /// + /// Stack: **=>** `result` + /// + /// Iterator Stack: `iterator` **=>** `iterator` + IteratorResult, + + /// Consume the iterator and construct and array with all the values. + /// + /// Operands: + /// + /// Stack: **=>** array + /// + /// Iterator Stack: `iterator` **=>** `iterator` + IteratorToArray, + + /// Pop an iterator from the call frame close iterator stack. + /// + /// Iterator Stack: + /// - `iterator` **=>** + IteratorPop, + + /// Pushes `true` to the stack if the iterator stack is empty. + /// + /// Stack: + /// - **=>** `is_empty` + /// + /// Iterator Stack: + /// - **=>** + IteratorStackEmpty, + + /// Creates a new iterator result object. + /// + /// Operands: + /// - done: bool (codified as u8 with `0` -> `false` and `!0` -> `true`) + /// + /// Stack: + /// - value **=>** + /// + CreateIteratorResult { done: bool }, + + /// Calls `return` on the current iterator and returns the result. + /// + /// Stack: **=>** return_val (if return is a method), is_return_method + /// + /// Iterator Stack: `iterator` **=>** + IteratorReturn, + + /// Concat multiple stack objects into a string. + /// + /// Operands: value_count: `u32` + /// + /// Stack: `value_1`,...`value_n` **=>** `string` + ConcatToString { value_count: u32 }, + + /// Call RequireObjectCoercible on the stack value. + /// + /// Operands: + /// + /// Stack: value **=>** value + RequireObjectCoercible, + + /// Require the stack value to be neither null nor undefined. + /// + /// Operands: + /// + /// Stack: value **=>** value + ValueNotNullOrUndefined, + + /// Initialize the rest parameter value of a function from the remaining arguments. + /// + /// Operands: + /// + /// Stack: `argument_1` .. `argument_n` **=>** `array` + RestParameterInit, + + /// Pop the remaining arguments of a function. + /// + /// Operands: + /// + /// Stack: `argument_1` .. `argument_n` **=>** + RestParameterPop, + + /// Yields from the current generator execution. + /// + /// Operands: + /// + /// Stack: **=>** resume_kind, received + GeneratorYield, + + /// Resumes the current generator function. + /// + /// If the `resume_kind` is `Throw`, then the value is poped and thrown, otherwise if `Return` + /// we pop the value, set it as the return value and throw and empty exception. See [`Opcode::ReThrow`]. + /// + /// Operands: + /// + /// Stack: `resume_kind`, value **=>** value + GeneratorNext, + + /// Yields from the current async generator execution. + /// + /// Operands: + /// + /// Stack: value **=>** received + AsyncGeneratorYield, + + /// Create a promise capacity for an async function, if not already set. + /// + /// Operands: + /// + /// Stack: **=>** + CreatePromiseCapability, + + /// Resolves or rejects the promise capability of an async function. + /// + /// If the pending exception is set, reject and rethrow the exception, otherwise resolve. + /// + /// Operands: + /// + /// Stack: **=>** + CompletePromiseCapability, + + /// Jumps to the specified address if the resume kind is not equal. + /// + /// Operands: `exit`: `u32`, `resume_kind`: `u8`. + /// + /// Stack: `resume_kind` **=>** `resume_kind` + JumpIfNotResumeKind { exit: u32, resume_kind: GeneratorResumeKind }, + + /// Delegates the current async generator function to another iterator. + /// + /// Operands: throw_method_undefined: `u32`, return_method_undefined: `u32` + /// + /// Stack: received **=>** result + GeneratorDelegateNext { throw_method_undefined: u32, return_method_undefined: u32 }, + + /// Resume the async generator with yield delegate logic after it awaits a value. + /// + /// Operands: return: `u32`, exit: `u32` + /// + /// Stack: is_return, received **=>** value + GeneratorDelegateResume { r#return: u32, exit: u32 }, + + /// Stops the current async function and schedules it to resume later. + /// + /// Operands: + /// + /// Stack: promise **=>** received + Await, + + /// Push the current new target to the stack. + /// + /// Operands: + /// + /// Stack: **=>** `new.target` + NewTarget, + + /// Push the current `import.meta` to the stack. + /// + /// Operands: + /// + /// Stack: **=>** `import.meta` + ImportMeta, + + /// Pushes `true` to the stack if the top stack value is an object, or `false` otherwise. + /// + /// Operands: + /// + /// Stack: value **=>** is_object + IsObject, + + /// Lookup if a tagged template object is cached and skip the creation if it is. + /// + /// Operands: exit: `u32`, site: `u64` + /// + /// Stack: **=>** template (if cached) + TemplateLookup { exit: u32, site: u64 }, + + /// Create a new tagged template object and cache it. + /// + /// Operands: count: `u32`, site: `u64` + /// + /// Stack: count * (cooked_value, raw_value) **=>** template + TemplateCreate { count: u32, site: u64 }, + + /// Push a private environment. + /// + /// Operands: count: `u32`, count * name_index: `u32` + /// + /// Stack: class **=>** class + PushPrivateEnvironment { name_indices: ThinVec }, + + /// Pop a private environment. + /// + /// Operands: + /// + /// Stack: **=>** + PopPrivateEnvironment, + + /// No-operation instruction, does nothing. + /// + /// Operands: + /// + /// Stack: **=>** + Nop, + + /// 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, } /// Specific opcodes for bindings. @@ -1809,3 +2082,34 @@ pub(crate) enum BindingOpcode { InitConst, SetName, } + +/// Iterator over the instructions in the compact bytecode. +#[derive(Debug, Clone)] +pub struct InstructionIterator<'bytecode> { + bytes: &'bytecode [u8], + pc: usize, +} + +impl<'bytecode> InstructionIterator<'bytecode> { + /// Create a new [`InstructionIterator`] from bytecode array. + #[inline] + #[must_use] + pub const fn new(bytes: &'bytecode [u8]) -> Self { + Self { bytes, pc: 0 } + } +} + +impl Iterator for InstructionIterator<'_> { + type Item = Instruction; + + #[inline] + fn next(&mut self) -> Option { + if self.pc >= self.bytes.len() { + return None; + } + + Some(Instruction::from_bytecode(self.bytes, &mut self.pc)) + } +} + +impl FusedIterator for InstructionIterator<'_> {}