use std::iter::FusedIterator;
use crate::{vm::CompletionType, Context, JsResult, JsValue};
mod arguments;
mod r#await;
mod binary_ops;
mod call;
mod concat;
mod control_flow;
mod copy;
mod define;
mod delete;
mod dup;
mod environment;
mod generator;
mod get;
mod global;
mod iteration;
mod meta;
mod modifier;
mod new;
mod nop;
mod pop;
mod push;
mod require;
mod rest_parameter;
mod set;
mod swap;
mod switch;
mod templates;
mod to;
mod unary_ops;
mod value;
#[doc(inline)]
pub(crate) use arguments::*;
#[doc(inline)]
pub(crate) use binary_ops::*;
#[doc(inline)]
pub(crate) use call::*;
#[doc(inline)]
pub(crate) use concat::*;
#[doc(inline)]
pub(crate) use control_flow::*;
#[doc(inline)]
pub(crate) use copy::*;
#[doc(inline)]
pub(crate) use define::*;
#[doc(inline)]
pub(crate) use delete::*;
#[doc(inline)]
pub(crate) use dup::*;
#[doc(inline)]
pub(crate) use environment::*;
#[doc(inline)]
pub(crate) use generator::*;
#[doc(inline)]
pub(crate) use get::*;
#[doc(inline)]
pub(crate) use global::*;
#[doc(inline)]
pub(crate) use iteration::*;
#[doc(inline)]
pub(crate) use meta::*;
#[doc(inline)]
pub(crate) use modifier::*;
#[doc(inline)]
pub(crate) use new::*;
#[doc(inline)]
pub(crate) use nop::*;
#[doc(inline)]
pub(crate) use pop::*;
#[doc(inline)]
pub(crate) use push::*;
#[doc(inline)]
pub(crate) use r#await::*;
#[doc(inline)]
pub(crate) use require::*;
#[doc(inline)]
pub(crate) use rest_parameter::*;
#[doc(inline)]
pub(crate) use set::*;
#[doc(inline)]
pub(crate) use swap::*;
#[doc(inline)]
pub(crate) use switch::*;
#[doc(inline)]
pub(crate) use templates::*;
#[doc(inline)]
pub(crate) use to::*;
#[doc(inline)]
pub(crate) use unary_ops::*;
#[doc(inline)]
pub(crate) use value::*;
use super::{code_block::Readable, GeneratorResumeKind};
use thin_vec::ThinVec;
pub(crate) const unsafe fn read_unchecked<T>(bytes: &[u8], offset: usize) -> T
where
T: Readable,
{
unsafe { bytes.as_ptr().add(offset).cast::<T>().read_unaligned() }
}
#[track_caller]
pub(crate) fn read<T>(bytes: &[u8], offset: usize) -> T
where
T: Readable,
{
assert!(offset + std::mem::size_of::<T>() - 1 < bytes.len());
unsafe { read_unchecked(bytes, offset) }
}
#[derive(Default, Debug, Clone, Copy)]
pub(crate) enum VaryingOperandKind {
#[default]
U8,
U16,
U32,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct VaryingOperand {
kind: VaryingOperandKind,
value: u32,
}
impl PartialEq for VaryingOperand {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl VaryingOperand {
#[must_use]
pub(crate) fn u8(value: u8) -> Self {
Self {
kind: VaryingOperandKind::U8,
value: u32::from(value),
}
}
#[must_use]
pub(crate) fn u16(value: u16) -> Self {
Self {
kind: VaryingOperandKind::U16,
value: u32::from(value),
}
}
#[must_use]
pub(crate) const fn u32(value: u32) -> Self {
Self {
kind: VaryingOperandKind::U32,
value,
}
}
#[must_use]
pub(crate) const fn value(self) -> u32 {
self.value
}
#[must_use]
pub(crate) const fn kind(self) -> VaryingOperandKind {
self.kind
}
}
trait BytecodeConversion: Sized {
fn to_bytecode(&self, bytes: &mut Vec<u8>);
fn from_bytecode(bytes: &[u8], pc: &mut usize, varying_kind: VaryingOperandKind) -> Self;
}
impl BytecodeConversion for VaryingOperand {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
match self.kind() {
VaryingOperandKind::U8 => u8::to_bytecode(&(self.value() as u8), bytes),
VaryingOperandKind::U16 => u16::to_bytecode(&(self.value() as u16), bytes),
VaryingOperandKind::U32 => u32::to_bytecode(&self.value(), bytes),
}
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, varying_kind: VaryingOperandKind) -> Self {
match varying_kind {
VaryingOperandKind::U8 => Self::u8(u8::from_bytecode(bytes, pc, varying_kind)),
VaryingOperandKind::U16 => Self::u16(u16::from_bytecode(bytes, pc, varying_kind)),
VaryingOperandKind::U32 => Self::u32(u32::from_bytecode(bytes, pc, varying_kind)),
}
}
}
impl BytecodeConversion for GeneratorResumeKind {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.push(*self as u8);
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<u8>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
JsValue::from(value).to_generator_resume_kind()
}
}
impl BytecodeConversion for bool {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.push(u8::from(*self));
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<u8>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value != 0
}
}
impl BytecodeConversion for i8 {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.push(*self as u8);
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<Self>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value
}
}
impl BytecodeConversion for u8 {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.push(*self);
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<Self>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value
}
}
impl BytecodeConversion for i16 {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.extend_from_slice(&self.to_ne_bytes());
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<Self>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value
}
}
impl BytecodeConversion for u16 {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.extend_from_slice(&self.to_ne_bytes());
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<Self>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value
}
}
impl BytecodeConversion for i32 {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.extend_from_slice(&self.to_ne_bytes());
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<Self>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value
}
}
impl BytecodeConversion for u32 {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.extend_from_slice(&self.to_ne_bytes());
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<Self>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value
}
}
impl BytecodeConversion for i64 {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.extend_from_slice(&self.to_ne_bytes());
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<Self>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value
}
}
impl BytecodeConversion for u64 {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.extend_from_slice(&self.to_ne_bytes());
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<Self>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value
}
}
impl BytecodeConversion for f32 {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.extend_from_slice(&self.to_ne_bytes());
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<Self>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value
}
}
impl BytecodeConversion for f64 {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
bytes.extend_from_slice(&self.to_ne_bytes());
}
fn from_bytecode(bytes: &[u8], pc: &mut usize, _varying_kind: VaryingOperandKind) -> Self {
let value = read::<Self>(bytes, *pc);
*pc += std::mem::size_of::<Self>();
value
}
}
impl BytecodeConversion for ThinVec<u32> {
fn to_bytecode(&self, bytes: &mut Vec<u8>) {
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, _varying_kind: VaryingOperandKind) -> Self {
let count = read::<u32>(bytes, *pc);
*pc += std::mem::size_of::<u32>();
let mut result = Self::with_capacity(count as usize);
for _ in 0..count {
let item = read::<u32>(bytes, *pc);
*pc += std::mem::size_of::<u32>();
result.push(item);
}
result
}
}
macro_rules! generate_opcodes {
( name $name:ident ) => { $name };
( name $name:ident => $mapping:ident ) => { $mapping };
{ if { $($if:tt)+ } else { $($else:tt)* } } => { $($if)+ };
{ if { } else { $($else:tt)* } } => { $($else)* };
(
$(
$(#[$inner:ident $($args:tt)*])*
$Variant:ident $({
$(
$(#[$fieldinner:ident $($fieldargs:tt)*])*
$FieldName:ident : $FieldType:ty
),*
})? $(=> $mapping:ident)?
),*
$(,)?
) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub(crate) enum Opcode {
$(
$(#[$inner $($args)*])*
$Variant
),*
}
impl From<u8> for Opcode {
#[inline]
#[allow(non_upper_case_globals)]
fn from(value: u8) -> Self {
$(
const $Variant: u8 = Opcode::$Variant as u8;
)*
match value {
$($Variant => Self::$Variant),*
}
}
}
impl Opcode {
const MAX: usize = 2usize.pow(8);
#[allow(unused)]
const NAMES: [&'static str; Self::MAX * 3] = [
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::NAME),*,
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::NAME),*,
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::NAME),*
];
#[must_use]
#[allow(unused)]
pub(crate) const fn as_str(self) -> &'static str {
Self::NAMES[self as usize]
}
const INSTRUCTIONS: [&'static str; Self::MAX * 3] = [
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::INSTRUCTION),*,
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::INSTRUCTION),*,
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::INSTRUCTION),*
];
#[must_use]
pub(crate) const fn as_instruction_str(self) -> &'static str {
Self::INSTRUCTIONS[self as usize]
}
const SPEND_FNS: [fn(&mut Context, &mut u32) -> JsResult<CompletionType>; Self::MAX] = [
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::spend_budget_and_execute),*,
];
pub(super) fn spend_budget_and_execute(
self,
context: &mut Context,
budget: &mut u32
) -> JsResult<CompletionType> {
Self::SPEND_FNS[self as usize](context, budget)
}
const COSTS: [u8; Self::MAX] = [
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::COST),*,
];
pub(super) const fn cost(self) -> u8 {
Self::COSTS[self as usize]
}
const EXECUTE_FNS: [fn(&mut Context) -> JsResult<CompletionType>; Self::MAX * 3] = [
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::execute),*,
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::execute_with_u16_operands),*,
$(<generate_opcodes!(name $Variant $(=> $mapping)?)>::execute_with_u32_operands),*
];
pub(super) fn execute(self, context: &mut Context) -> JsResult<CompletionType> {
Self::EXECUTE_FNS[self as usize](context)
}
}
#[derive(Debug, Clone, PartialEq)]
#[repr(u8)]
pub(crate) enum Instruction {
$(
$(#[$inner $($args)*])*
$Variant $({
$(
$(#[$fieldinner $($fieldargs)*])*
$FieldName : $FieldType
),*
})?
),*
}
impl Instruction {
#[inline]
#[allow(dead_code)]
pub(crate) fn to_bytecode(&self, bytes: &mut Vec<u8>) {
match self {
$(
Self::$Variant $({
$( $FieldName ),*
})? => {
bytes.push(Opcode::$Variant as u8);
$({
$( BytecodeConversion::to_bytecode($FieldName, bytes); )*
})?
}
),*
}
}
#[inline]
#[must_use]
pub(crate) fn from_bytecode(bytes: &[u8], pc: &mut usize, varying_kind: VaryingOperandKind) -> Self {
let opcode = bytes[*pc].into();
*pc += 1;
match opcode {
$(
Opcode::$Variant => {
generate_opcodes!(
if {
$({
Self::$Variant {
$(
$FieldName: BytecodeConversion::from_bytecode(bytes, pc, varying_kind)
),*
}
})?
} else {
Self::$Variant
}
)
}
),*
}
}
#[inline]
#[must_use]
#[allow(unused)]
pub(crate) const fn opcode(&self) -> Opcode {
match self {
$(
Self::$Variant $({ $( $FieldName: _ ),* })? => Opcode::$Variant
),*
}
}
}
};
}
pub(crate) trait Operation {
const NAME: &'static str;
const INSTRUCTION: &'static str;
const COST: u8;
fn execute(context: &mut Context) -> JsResult<CompletionType>;
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
Reserved::execute_with_u16_operands(context)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
Reserved::execute_with_u32_operands(context)
}
fn spend_budget_and_execute(
context: &mut Context,
budget: &mut u32,
) -> JsResult<CompletionType> {
*budget = budget.saturating_sub(u32::from(Self::COST));
Self::execute(context)
}
}
generate_opcodes! {
Pop,
Dup,
Swap,
RotateLeft { n: u8 },
RotateRight { n: u8 },
PushZero,
PushOne,
PushInt8 { value: i8 },
PushInt16 { value: i16 },
PushInt32 { value: i32 },
PushFloat { value: f32 },
PushDouble { value: f64 },
PushNaN,
PushPositiveInfinity,
PushNegativeInfinity,
PushNull,
PushTrue,
PushFalse,
PushUndefined,
PushLiteral { index: VaryingOperand },
PushRegExp { pattern_index: VaryingOperand, flags_index: VaryingOperand },
PushEmptyObject,
PushClassPrototype,
SetClassPrototype,
SetHomeObject,
SetPrototype,
PushNewArray,
PushValueToArray,
PushElisionToArray,
PushIteratorToArray,
Add,
Sub,
Div,
Mul,
Mod,
Pow,
ShiftRight,
ShiftLeft,
UnsignedShiftRight,
BitOr,
BitAnd,
BitXor,
BitNot,
In,
InPrivate { index: VaryingOperand },
Eq,
StrictEq,
NotEq,
StrictNotEq,
GreaterThan,
GreaterThanOrEq,
LessThan,
LessThanOrEq,
InstanceOf,
LogicalAnd { exit: u32 },
LogicalOr { exit: u32 },
Coalesce { exit: u32 },
TypeOf,
Void,
LogicalNot,
Pos,
Neg,
Inc,
IncPost,
Dec,
DecPost,
DefVar { index: VaryingOperand },
DefInitVar { index: VaryingOperand },
PutLexicalValue { index: VaryingOperand },
ThrowMutateImmutable { index: VaryingOperand },
GetArgument { index: VaryingOperand },
GetName { index: VaryingOperand },
GetLocator { index: VaryingOperand },
GetNameAndLocator { index: VaryingOperand },
GetNameOrUndefined { index: VaryingOperand },
SetName { index: VaryingOperand },
SetNameByLocator,
DeleteName { index: VaryingOperand },
GetPropertyByName { index: VaryingOperand },
GetPropertyByValue,
GetPropertyByValuePush,
SetPropertyByName { index: VaryingOperand },
SetFunctionName { prefix: u8 },
DefineOwnPropertyByName { index: VaryingOperand },
DefineClassStaticMethodByName { index: VaryingOperand },
DefineClassMethodByName { index: VaryingOperand },
SetPropertyByValue,
DefineOwnPropertyByValue,
DefineClassStaticMethodByValue,
DefineClassMethodByValue,
SetPropertyGetterByName { index: VaryingOperand },
DefineClassStaticGetterByName { index: VaryingOperand },
DefineClassGetterByName { index: VaryingOperand },
SetPropertyGetterByValue,
DefineClassStaticGetterByValue,
DefineClassGetterByValue,
SetPropertySetterByName { index: VaryingOperand },
DefineClassStaticSetterByName { index: VaryingOperand },
DefineClassSetterByName { index: VaryingOperand },
SetPropertySetterByValue,
DefineClassStaticSetterByValue,
DefineClassSetterByValue,
SetPrivateField { index: VaryingOperand },
DefinePrivateField { index: VaryingOperand },
SetPrivateMethod { index: VaryingOperand },
SetPrivateSetter { index: VaryingOperand },
SetPrivateGetter { index: VaryingOperand },
GetPrivateField { index: VaryingOperand },
PushClassField,
PushClassFieldPrivate { index: VaryingOperand },
PushClassPrivateGetter { index: VaryingOperand },
PushClassPrivateSetter { index: VaryingOperand },
PushClassPrivateMethod { index: VaryingOperand },
DeletePropertyByName { index: VaryingOperand },
DeletePropertyByValue,
DeleteSuperThrow,
CopyDataProperties { excluded_key_count: VaryingOperand, excluded_key_count_computed: VaryingOperand },
ToPropertyKey,
Jump { address: u32 },
JumpIfTrue { address: u32 },
JumpIfFalse { address: u32 },
JumpIfNotUndefined { address: u32 },
JumpIfNullOrUndefined { address: u32 },
JumpTable { default: u32, addresses: ThinVec<u32> },
Throw,
ReThrow,
Exception,
MaybeException,
ThrowNewTypeError { message: VaryingOperand },
ThrowNewSyntaxError { message: VaryingOperand },
ToBoolean,
This,
ThisForObjectEnvironmentName { index: VaryingOperand },
Super,
SuperCallPrepare,
SuperCall { argument_count: VaryingOperand },
SuperCallSpread,
SuperCallDerived,
BindThisValue,
ImportCall,
Case { address: u32 },
Default { address: u32 },
GetFunction { index: VaryingOperand },
CallEval { argument_count: VaryingOperand },
CallEvalSpread,
Call { argument_count: VaryingOperand },
CallSpread,
New { argument_count: VaryingOperand },
NewSpread,
CheckReturn,
Return,
AsyncGeneratorClose,
Generator { r#async: bool },
GetReturnValue,
SetReturnValue,
PushDeclarativeEnvironment { compile_environments_index: VaryingOperand },
PushObjectEnvironment,
PopEnvironment,
IncrementLoopIteration,
CreateForInIterator,
GetIterator,
GetAsyncIterator,
IteratorNext,
IteratorNextWithoutPop,
IteratorDone,
IteratorFinishAsyncNext,
IteratorValue,
IteratorValueWithoutPop,
IteratorResult,
IteratorToArray,
IteratorStackEmpty,
CreateIteratorResult { done: bool },
IteratorReturn,
ConcatToString { value_count: VaryingOperand },
RequireObjectCoercible,
ValueNotNullOrUndefined,
RestParameterInit,
GeneratorYield,
GeneratorNext,
AsyncGeneratorYield,
CreatePromiseCapability,
CompletePromiseCapability,
JumpIfNotResumeKind { exit: u32, resume_kind: GeneratorResumeKind },
GeneratorDelegateNext { throw_method_undefined: u32, return_method_undefined: u32 },
GeneratorDelegateResume { r#return: u32, exit: u32 },
Await,
NewTarget,
ImportMeta,
IsObject,
TemplateLookup { exit: u32, site: u64 },
TemplateCreate { count: VaryingOperand, site: u64 },
PushPrivateEnvironment { name_indices: ThinVec<u32> },
PopPrivateEnvironment,
CreateMappedArgumentsObject,
CreateUnmappedArgumentsObject,
HasRestrictedGlobalProperty { index: VaryingOperand },
CanDeclareGlobalFunction { index: VaryingOperand },
CanDeclareGlobalVar { index: VaryingOperand },
CreateGlobalFunctionBinding { configurable: bool, index: VaryingOperand },
CreateGlobalVarBinding { configurable: bool, index: VaryingOperand },
Nop,
U16Operands,
U32Operands,
Reserved1 => Reserved,
Reserved2 => Reserved,
Reserved3 => Reserved,
Reserved4 => Reserved,
Reserved5 => Reserved,
Reserved6 => Reserved,
Reserved7 => Reserved,
Reserved8 => Reserved,
Reserved9 => Reserved,
Reserved10 => Reserved,
Reserved11 => Reserved,
Reserved12 => Reserved,
Reserved13 => Reserved,
Reserved14 => Reserved,
Reserved15 => Reserved,
Reserved16 => Reserved,
Reserved17 => Reserved,
Reserved18 => Reserved,
Reserved19 => Reserved,
Reserved20 => Reserved,
Reserved21 => Reserved,
Reserved22 => Reserved,
Reserved23 => Reserved,
Reserved24 => Reserved,
Reserved25 => Reserved,
Reserved26 => Reserved,
Reserved27 => Reserved,
Reserved28 => Reserved,
Reserved29 => Reserved,
Reserved30 => Reserved,
Reserved31 => Reserved,
Reserved32 => Reserved,
Reserved33 => Reserved,
Reserved34 => Reserved,
Reserved35 => Reserved,
Reserved36 => Reserved,
Reserved37 => Reserved,
Reserved38 => Reserved,
Reserved39 => Reserved,
Reserved40 => Reserved,
Reserved41 => Reserved,
Reserved42 => Reserved,
Reserved43 => Reserved,
Reserved44 => Reserved,
Reserved45 => Reserved,
Reserved46 => Reserved,
Reserved47 => Reserved,
Reserved48 => Reserved,
Reserved49 => Reserved,
Reserved50 => Reserved,
Reserved51 => Reserved,
Reserved52 => Reserved,
Reserved53 => Reserved,
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum BindingOpcode {
Var,
InitVar,
InitLexical,
SetName,
}
#[derive(Debug, Clone)]
pub(crate) struct InstructionIterator<'bytecode> {
bytes: &'bytecode [u8],
pc: usize,
}
#[allow(unused)]
impl<'bytecode> InstructionIterator<'bytecode> {
#[inline]
#[must_use]
pub(crate) const fn new(bytes: &'bytecode [u8]) -> Self {
Self { bytes, pc: 0 }
}
#[inline]
#[must_use]
pub(crate) const fn with_pc(bytes: &'bytecode [u8], pc: usize) -> Self {
Self { bytes, pc }
}
#[must_use]
pub(crate) const fn pc(&self) -> usize {
self.pc
}
}
impl Iterator for InstructionIterator<'_> {
type Item = (usize, VaryingOperandKind, Instruction);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let start_pc = self.pc;
if self.pc >= self.bytes.len() {
return None;
}
let instruction =
Instruction::from_bytecode(self.bytes, &mut self.pc, VaryingOperandKind::U8);
if instruction == Instruction::U16Operands {
return Some((
start_pc,
VaryingOperandKind::U16,
Instruction::from_bytecode(self.bytes, &mut self.pc, VaryingOperandKind::U16),
));
} else if instruction == Instruction::U32Operands {
return Some((
start_pc,
VaryingOperandKind::U32,
Instruction::from_bytecode(self.bytes, &mut self.pc, VaryingOperandKind::U32),
));
}
Some((start_pc, VaryingOperandKind::U8, instruction))
}
}
impl FusedIterator for InstructionIterator<'_> {}