From 776a25bd08389bb9201067159729d4acad68bc34 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Sat, 5 Aug 2023 07:21:11 +0200 Subject: [PATCH] Add `Instruction` and `InstructionIterator` --- boa_engine/src/bytecompiler/mod.rs | 4 +- boa_engine/src/vm/call_frame/mod.rs | 5 +- boa_engine/src/vm/code_block.rs | 640 ++++++++--------- boa_engine/src/vm/flowgraph/mod.rs | 651 ++++++++---------- boa_engine/src/vm/mod.rs | 7 +- boa_engine/src/vm/opcode/control_flow/jump.rs | 2 +- boa_engine/src/vm/opcode/mod.rs | 550 +++++++++++---- 7 files changed, 1013 insertions(+), 846 deletions(-) diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 1973d3d76b8..d44ab75f084 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -595,9 +595,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 430840434e9..bd2ee9d7b04 100644 --- a/boa_engine/src/vm/call_frame/mod.rs +++ b/boa_engine/src/vm/call_frame/mod.rs @@ -93,9 +93,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 e060fcec06c..7b2fec9aad2 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 8ddef1b24de..13a8955c20b 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..2573db6f869 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; @@ -79,6 +81,7 @@ pub(crate) use swap::*; pub(crate) use switch::*; #[doc(inline)] pub(crate) use templates::*; +use thin_vec::ThinVec; #[doc(inline)] pub(crate) use to::*; #[doc(inline)] @@ -86,16 +89,209 @@ pub(crate) use unary_ops::*; #[doc(inline)] pub(crate) use value::*; +use super::{code_block::Readable, GeneratorResumeKind}; + +/// 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 + } +} + macro_rules! generate_impl { ( name $name:ident ) => { $name }; ( name $name:ident => $mapping:ident ) => { $mapping }; + { 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)* + $Variant:ident $({ + $( $(#[$fieldinner:ident $($fieldargs:tt)*])* $FieldName:ident : $FieldType:ty ),* + })? $(=> $mapping:ident)? $(= $index:expr)* ),* $(,)? } @@ -153,6 +349,83 @@ macro_rules! generate_impl { Self::EXECUTE_FNS[self as usize](context) } } + + /// This represents a VM instruction, it contains both opcode and operands. + #[derive(Debug, Clone, PartialEq)] + #[repr(u8)] + #[allow(missing_docs)] + pub enum Instruction { + $( + $(#[$inner $($args)*])* + $Variant $({ + $( + $(#[$fieldinner $($fieldargs)*])* + $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`]. + #[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_impl!( + 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 + ),* + } + } + } }; } @@ -202,7 +475,7 @@ generate_impl! { /// Operands: n: `u8` /// /// Stack: v\[n\], v\[n-1\], ... , v\[1\], v\[0\] **=>** v\[n-1\], ... , v\[1\], v\[0\], v\[n\] - RotateLeft, + RotateLeft { n: u8 }, /// Rotates the top `n` values of the stack to the right by `1`. /// @@ -212,7 +485,7 @@ generate_impl! { /// Operands: n: `u8` /// /// Stack: v\[n\], v\[n-1\], ... , v\[1\], v\[0\] **=>** v\[0\], v\[n\], v\[n-1\], ... , v\[1\] - RotateRight, + RotateRight { n: u8 }, /// Push integer `0` on the stack. /// @@ -233,35 +506,35 @@ generate_impl! { /// Operands: value: `i8` /// /// Stack: **=>** value - PushInt8, + PushInt8 { value: i8 }, /// Push i16 value on the stack. /// /// Operands: value: `i16` /// /// Stack: **=>** value - PushInt16, + PushInt16 { value: i16 }, /// Push i32 value on the stack. /// /// Operands: value: `i32` /// /// Stack: **=>** value - PushInt32, + PushInt32 { value: i32 }, /// Push `f32` value on the stack. /// /// Operands: value: `f32` /// /// Stack: **=>** value - PushFloat, + PushFloat { value: f32 }, /// Push `f64` value on the stack. /// /// Operands: value: `f64` /// /// Stack: **=>** value - PushDouble, + PushDouble { value: f64 }, /// Push `NaN` integer on the stack. /// @@ -320,7 +593,7 @@ generate_impl! { /// Operands: index: `u32` /// /// Stack: **=>** (`literals[index]`) - PushLiteral, + PushLiteral { index: u32 }, /// Push empty object `{}` value on the stack. /// @@ -492,10 +765,10 @@ generate_impl! { /// Binary `in` operator for private names. /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: rhs **=>** (private_name `in` rhs) - InPrivate, + InPrivate { index: u32 }, /// Binary `==` operator. /// @@ -567,7 +840,7 @@ generate_impl! { /// Operands: exit: `u32` /// /// Stack: lhs, rhs **=>** (lhs && rhs) - LogicalAnd, + LogicalAnd { exit: u32 }, /// Binary logical `||` operator. /// @@ -576,7 +849,7 @@ generate_impl! { /// Operands: exit: `u32` /// /// Stack: lhs, rhs **=>** (lhs || rhs) - LogicalOr, + LogicalOr { exit: u32 }, /// Binary `??` operator. /// @@ -586,7 +859,7 @@ generate_impl! { /// Operands: exit: `u32` /// /// Stack: lhs, rhs **=>** (lhs ?? rhs) - Coalesce, + Coalesce { exit: u32 }, /// Unary `typeof` operator. /// @@ -653,67 +926,67 @@ generate_impl! { /// Declare `var` type variable. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: **=>** - DefVar, + DefVar { index: u32 }, /// Declare and initialize `var` type variable. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: value **=>** - DefInitVar, + DefInitVar { index: u32 }, /// Initialize a lexical binding. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: value **=>** - PutLexicalValue, + PutLexicalValue { index: u32 }, /// Throws an error because the binding access is illegal. /// - /// Operands: binding_index: `u32` + /// Operands: index: `u32` /// /// Stack: **=>** - ThrowMutateImmutable, + ThrowMutateImmutable { index: u32 }, /// Find a binding on the environment chain and push its value. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: **=>** value - GetName, + GetName { index: u32 }, /// Find a binding on the environment and set the `current_binding` of the current frame. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: **=>** - GetLocator, + 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: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: **=>** value - GetNameAndLocator, + GetNameAndLocator { index: u32 }, /// Find a binding on the environment chain and push its value. If the binding does not exist push undefined. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: **=>** value - GetNameOrUndefined, + GetNameOrUndefined { index: u32 }, /// Find a binding on the environment chain and assign its value. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: value **=>** - SetName, + SetName { index: u32 }, /// Assigns a value to the binding pointed by the top of the `bindings_stack`. /// @@ -722,27 +995,27 @@ generate_impl! { /// Deletes a property of the global object. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: **=>** deleted - DeleteName, + DeleteName { index: u32 }, /// Get a property by name from an object an push it on the stack. /// /// Like `object.name` /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object, receiver **=>** value - GetPropertyByName, + 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: name_index: `u32` + /// Operands: index: `u32` /// Stack: object **=>** object, method - GetMethod, + GetMethod { index: u32 }, /// Get a property by value from an object an push it on the stack. /// @@ -766,10 +1039,10 @@ generate_impl! { /// /// Like `object.name = value` /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object, receiver, value **=>** value - SetPropertyByName, + SetPropertyByName { index: u32 }, /// Sets the name of a function object. /// @@ -780,33 +1053,33 @@ generate_impl! { /// * 1 -> "get " /// * 2 -> "set " /// - /// Operands: prefix: `u8` + /// Operands: prefix: `u8` /// /// Stack: name, function **=>** function /// /// [spec]: https://tc39.es/ecma262/#sec-setfunctionname - SetFunctionName, + SetFunctionName { prefix: u8 }, /// Defines a own property of an object by name. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object, value **=>** - DefineOwnPropertyByName, + DefineOwnPropertyByName { index: u32 }, /// Defines a static class method by name. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: class, function **=>** - DefineClassStaticMethodByName, + DefineClassStaticMethodByName { index: u32 }, /// Defines a class method by name. /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: class_proto, function **=>** - DefineClassMethodByName, + DefineClassMethodByName { index: u32 }, /// Sets a property by value of an object. /// @@ -842,28 +1115,28 @@ generate_impl! { /// /// Like `get name() value` /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object, value **=>** - SetPropertyGetterByName, + SetPropertyGetterByName { index: u32 }, /// Defines a static getter class method by name. /// /// Like `static get name() value` /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// - /// Stack: class, function **=>** - DefineClassStaticGetterByName, + /// Stack: class, binding_function **=>** + DefineClassStaticGetterByName { index: u32 }, /// Defines a getter class method by name. /// /// Like `get name() value` /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: class_proto, function **=>** class - DefineClassGetterByName, + DefineClassGetterByName { index: u32 }, /// Sets a getter property by value of an object. /// @@ -896,28 +1169,28 @@ generate_impl! { /// /// Like `set name() value` /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object, value **=>** - SetPropertySetterByName, + SetPropertySetterByName { index: u32 }, /// Defines a static setter class method by name. /// /// Like `static set name() value` /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: class, function **=>** - DefineClassStaticSetterByName, + DefineClassStaticSetterByName { index: u32 }, /// Defines a setter class method by name. /// /// Like `set name() value` /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: class_proto, function **=>** - DefineClassSetterByName, + DefineClassSetterByName { index: u32 }, /// Sets a setter property by value of an object. /// @@ -950,55 +1223,55 @@ generate_impl! { /// /// Like `obj.#name = value` /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object, value **=>** value - SetPrivateField, + SetPrivateField { index: u32 }, /// Define a private property of a class constructor by it's name. /// /// Like `#name = value` /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object, value **=>** - DefinePrivateField, + DefinePrivateField { index: u32 }, /// Set a private method of a class constructor by it's name. /// /// Like `#name() {}` /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object, value **=>** - SetPrivateMethod, + SetPrivateMethod { index: u32 }, /// Set a private setter property of a class constructor by it's name. /// /// Like `set #name() {}` /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object, value **=>** - SetPrivateSetter, + SetPrivateSetter { index: u32 }, /// Set a private getter property of a class constructor by it's name. /// /// Like `get #name() {}` /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object, value **=>** - SetPrivateGetter, + SetPrivateGetter { index: u32 }, /// Get a private property by name from an object an push it on the stack. /// /// Like `object.#name` /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object **=>** value - GetPrivateField, + GetPrivateField { index: u32 }, /// Push a field to a class. /// @@ -1009,40 +1282,40 @@ generate_impl! { /// Push a private field to the class. /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: class, field_function **=>** - PushClassFieldPrivate, + PushClassFieldPrivate { index: u32 }, /// Push a private getter to the class. /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: class, getter **=>** - PushClassPrivateGetter, + PushClassPrivateGetter { index: u32 }, /// Push a private setter to the class. /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: class, setter **=>** - PushClassPrivateSetter, + PushClassPrivateSetter { index: u32 }, /// Push a private method to the class. /// - /// Operands: private_name_index: `u32` + /// Operands: index: `u32` /// /// Stack: class, method **=>** - PushClassPrivateMethod, + PushClassPrivateMethod { index: u32 }, /// Deletes a property by name of an object. /// /// Like `delete object.key` /// - /// Operands: name_index: `u32` + /// Operands: index: `u32` /// /// Stack: object **=>** - DeletePropertyByName, + DeletePropertyByName { index: u32 }, /// Deletes a property by value of an object. /// @@ -1065,7 +1338,7 @@ generate_impl! { /// 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, + CopyDataProperties { excluded_key_count: u32, excluded_key_count_computed: u32 }, /// Call ToPropertyKey on the value on the stack. /// @@ -1079,7 +1352,7 @@ generate_impl! { /// Operands: address: `u32` /// /// Stack: **=>** - Jump, + Jump { address: u32 }, /// Conditional jump to address. /// @@ -1090,7 +1363,7 @@ generate_impl! { /// Stack: cond **=>** /// /// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy - JumpIfTrue, + JumpIfTrue { address: u32 }, /// Conditional jump to address. /// @@ -1101,7 +1374,7 @@ generate_impl! { /// Stack: cond **=>** /// /// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy - JumpIfFalse, + JumpIfFalse { address: u32 }, /// Conditional jump to address. /// @@ -1110,7 +1383,7 @@ generate_impl! { /// Operands: address: `u32` /// /// Stack: value **=>** value - JumpIfNotUndefined, + JumpIfNotUndefined { address: u32 }, /// Conditional jump to address. /// @@ -1119,17 +1392,17 @@ generate_impl! { /// Operands: address: `u32` /// /// Stack: value **=>** value - JumpIfNullOrUndefined, + 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: count: `u32`, default: `u32`, address: `u32` * count + /// Operands: default: `u32`, count: `u32`, address: `u32` * count /// /// Stack: value: [`i32`] **=>** - JumpTable, + JumpTable { default: u32, addresses: ThinVec }, /// Throw exception. /// @@ -1173,7 +1446,7 @@ generate_impl! { /// Operands: message: u32 /// /// Stack: **=>** - ThrowNewTypeError, + ThrowNewTypeError { message: u32 }, /// Pops value converts it to boolean and pushes it back. /// @@ -1208,7 +1481,7 @@ generate_impl! { /// Operands: argument_count: `u32` /// /// Stack: super_constructor, new_target, argument_1, ... argument_n **=>** - SuperCall, + SuperCall { argument_count: u32 }, /// Execute the `super()` method where the arguments contain spreads. /// @@ -1237,63 +1510,63 @@ generate_impl! { /// Operands: address: `u32` /// /// Stack: value, cond **=>** cond (if `cond !== value`). - Case, + Case { address: u32 }, /// Pops the top of stack and jump to address. /// /// Operands: address: `u32` /// /// Stack: `value` **=>** - Default, + Default { address: u32 }, /// Get arrow function from the pre-compiled inner functions. /// /// Operands: address: `u32`, method: `u8` /// /// Stack: **=>** func - GetArrowFunction, + GetArrowFunction { index: u32, method: bool }, /// Get async arrow function from the pre-compiled inner functions. /// - /// Operands: address: `u32`, method: `u8` + /// Operands: index: `u32`, method: `u8` /// /// Stack: **=>** func - GetAsyncArrowFunction, + GetAsyncArrowFunction { index: u32, method: bool }, /// Get function from the pre-compiled inner functions. /// - /// Operands: address: `u32`, is_method: `u8` + /// Operands: index: `u32`, is_method: `u8` /// /// Stack: **=>** func - GetFunction, + GetFunction { index: u32, method: bool }, /// Get async function from the pre-compiled inner functions. /// - /// Operands: address: `u32`, method: `u8` + /// Operands: index: `u32`, method: `u8` /// /// Stack: **=>** func - GetFunctionAsync, + GetFunctionAsync { index: u32, method: bool }, /// Get generator function from the pre-compiled inner functions. /// - /// Operands: address: `u32`, + /// Operands: index: `u32`, /// /// Stack: **=>** func - GetGenerator, + GetGenerator { index: u32 }, /// Get async generator function from the pre-compiled inner functions. /// - /// Operands: address: `u32`, + /// Operands: index: `u32`, /// /// Stack: **=>** func - GetGeneratorAsync, + GetGeneratorAsync { index: u32 }, /// Call a function named "eval". /// /// Operands: argument_count: `u32` /// /// Stack: this, func, argument_1, ... argument_n **=>** result - CallEval, + CallEval { argument_count: u32 }, /// Call a function named "eval" where the arguments contain spreads. /// @@ -1307,7 +1580,7 @@ generate_impl! { /// Operands: argument_count: `u32` /// /// Stack: this, func, argument_1, ... argument_n **=>** result - Call, + Call { argument_count: u32 }, /// Call a function where the arguments contain spreads. /// @@ -1321,7 +1594,7 @@ generate_impl! { /// Operands: argument_count: `u32` /// /// Stack: func, argument_1, ... argument_n **=>** result - New, + New { argument_count: u32 }, /// Call construct on a function where the arguments contain spreads. /// @@ -1349,7 +1622,7 @@ generate_impl! { /// Operands: async: `u8` /// /// Stack: **=>** resume_kind - Generator, + Generator { r#async: bool }, /// Get return value of a function. /// @@ -1370,7 +1643,7 @@ generate_impl! { /// Operands: compile_environments_index: `u32` /// /// Stack: **=>** - PushDeclarativeEnvironment, + PushDeclarativeEnvironment { compile_environments_index: u32 }, /// Push an object environment. /// @@ -1384,7 +1657,7 @@ generate_impl! { /// Operands: compile_environments_index: `u32` /// /// Stack: **=>** - PushFunctionEnvironment, + PushFunctionEnvironment { compile_environments_index: u32 }, /// Pop the current environment. /// @@ -1511,7 +1784,7 @@ generate_impl! { /// Stack: /// - value **=>** /// - CreateIteratorResult, + CreateIteratorResult { done: bool }, /// Calls `return` on the current iterator and returns the result. /// @@ -1525,7 +1798,7 @@ generate_impl! { /// Operands: value_count: `u32` /// /// Stack: `value_1`,...`value_n` **=>** `string` - ConcatToString, + ConcatToString { value_count: u32 }, /// Call RequireObjectCoercible on the stack value. /// @@ -1600,21 +1873,21 @@ generate_impl! { /// Operands: `exit`: `u32`, `resume_kind`: `u8`. /// /// Stack: `resume_kind` **=>** `resume_kind` - JumpIfNotResumeKind, + 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, + 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, + GeneratorDelegateResume { r#return: u32, exit: u32 }, /// Stops the current async function and schedules it to resume later. /// @@ -1646,24 +1919,24 @@ generate_impl! { /// Lookup if a tagged template object is cached and skip the creation if it is. /// - /// Operands: jump: `u32`, site: `u64` + /// Operands: exit: `u32`, site: `u64` /// /// Stack: **=>** template (if cached) - TemplateLookup, + 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, + TemplateCreate { count: u32, site: u64 }, /// Push a private environment. /// - /// Operands: count: `u32`, count * private_name_index: `u32` + /// Operands: count: `u32`, count * name_index: `u32` /// /// Stack: class **=>** class - PushPrivateEnvironment, + PushPrivateEnvironment { name_indices: ThinVec }, /// Pop a private environment. /// @@ -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<'_> {}