Files
nix-js/fix-vm/src/bytecode_reader.rs
T
2026-05-04 21:50:27 +08:00

143 lines
4.0 KiB
Rust

use fix_codegen::OperandType;
use fix_common::StringId;
use num_enum::TryFromPrimitive;
use string_interner::Symbol as _;
use crate::{OperandData, VmRuntimeCtx};
pub(crate) struct BytecodeReader<'a> {
bytecode: &'a [u8],
pc: usize,
inst_start_pc: usize,
}
impl<'a> BytecodeReader<'a> {
#[cfg_attr(feature = "tailcall", allow(dead_code))]
pub(crate) fn new(bytecode: &'a [u8], pc: usize) -> Self {
Self {
bytecode,
pc,
inst_start_pc: pc,
}
}
#[inline(always)]
#[cfg_attr(not(feature = "tailcall"), allow(dead_code))]
pub(crate) fn from_after_op(bytecode: &'a [u8], inst_start_pc: usize) -> Self {
Self {
bytecode,
pc: inst_start_pc + 1,
inst_start_pc,
}
}
#[inline(always)]
#[cfg_attr(debug_assertions, track_caller)]
fn read_array<const N: usize>(&mut self) -> [u8; N] {
let ret = self.bytecode[self.pc..self.pc + N]
.try_into()
.expect("read_array failed");
self.pc += N;
ret
}
#[inline(always)]
#[cfg_attr(feature = "tailcall", allow(dead_code))]
pub(crate) fn read_op(&mut self) -> fix_codegen::Op {
use fix_codegen::Op;
self.inst_start_pc = self.pc;
let byte = self.bytecode[self.pc];
if !likely_stable::likely((0..Op::Illegal as u8).contains(&byte)) {
panic!("unknown opcode: {byte:#04x}")
}
self.pc += 1;
unsafe { std::mem::transmute::<u8, Op>(byte) }
}
#[inline(always)]
pub(crate) fn read_u8(&mut self) -> u8 {
let val = self.bytecode[self.pc];
self.pc += 1;
val
}
#[inline(always)]
pub(crate) fn read_u16(&mut self) -> u16 {
u16::from_le_bytes(self.read_array())
}
#[inline(always)]
pub(crate) fn read_u32(&mut self) -> u32 {
u32::from_le_bytes(self.read_array())
}
#[inline(always)]
pub(crate) fn read_i32(&mut self) -> i32 {
i32::from_le_bytes(self.read_array())
}
#[inline(always)]
pub(crate) fn read_i64(&mut self) -> i64 {
i64::from_le_bytes(self.read_array())
}
#[inline(always)]
pub(crate) fn read_f64(&mut self) -> f64 {
f64::from_le_bytes(self.read_array())
}
#[inline(always)]
pub(crate) fn read_string_id(&mut self) -> StringId {
let raw = self.read_u32();
#[allow(clippy::unwrap_used)]
StringId(string_interner::symbol::SymbolU32::try_from_usize(raw as usize).unwrap())
}
#[inline(always)]
pub(crate) fn read_operand_data<C: VmRuntimeCtx>(&mut self, ctx: &C) -> OperandData {
let tag = self.read_u8();
let Ok(ty) = OperandType::try_from_primitive(tag)
.map_err(|err| panic!("unknown operand tag: {:#04x}", err.number));
match ty {
OperandType::Const => {
let id = self.read_u32();
OperandData::Const(ctx.get_const(id))
}
OperandType::BigInt => {
let val = self.read_i64();
OperandData::BigInt(val)
}
OperandType::Local => {
let layer = self.read_u8();
let idx = self.read_u32();
OperandData::Local { layer, idx }
}
OperandType::BuiltinConst => {
let id = self.read_string_id();
OperandData::BuiltinConst(id)
}
OperandType::Builtins => OperandData::Builtins,
OperandType::ReplBinding => {
let id = self.read_string_id();
OperandData::ReplBinding(id)
}
OperandType::ScopedImportBinding => {
let id = self.read_string_id();
OperandData::ScopedImportBinding(id)
}
}
}
pub(crate) fn pc(&self) -> usize {
self.pc
}
pub(crate) fn set_pc(&mut self, pc: usize) {
self.pc = pc;
}
pub(crate) fn inst_start_pc(&self) -> usize {
self.inst_start_pc
}
}