refactor: reorganize crate hierarchy
This commit is contained in:
@@ -0,0 +1,466 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use colored::Colorize;
|
||||
use num_enum::TryFromPrimitive;
|
||||
|
||||
use crate::{InstructionPtr, Op, OperandType, PrimOpPhase};
|
||||
|
||||
pub trait DisassemblerContext {
|
||||
fn resolve_string(&self, id: u32) -> &str;
|
||||
fn get_code(&self) -> &[u8];
|
||||
}
|
||||
|
||||
pub struct Disassembler<'a, Ctx> {
|
||||
code: &'a [u8],
|
||||
ctx: &'a Ctx,
|
||||
pc: usize,
|
||||
}
|
||||
|
||||
impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
|
||||
pub fn new(ip: InstructionPtr, ctx: &'a Ctx) -> Self {
|
||||
Self {
|
||||
code: ctx.get_code(),
|
||||
ctx,
|
||||
pc: ip.0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_u8(&mut self) -> u8 {
|
||||
let b = self.code[self.pc];
|
||||
self.pc += 1;
|
||||
b
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_u16(&mut self) -> u16 {
|
||||
let bytes = self.code[self.pc..self.pc + 2]
|
||||
.try_into()
|
||||
.expect("no enough bytes");
|
||||
self.pc += 2;
|
||||
u16::from_le_bytes(bytes)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_u32(&mut self) -> u32 {
|
||||
let bytes = self.code[self.pc..self.pc + 4]
|
||||
.try_into()
|
||||
.expect("no enough bytes");
|
||||
self.pc += 4;
|
||||
u32::from_le_bytes(bytes)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_i32(&mut self) -> i32 {
|
||||
let bytes = self.code[self.pc..self.pc + 4]
|
||||
.try_into()
|
||||
.expect("no enough bytes");
|
||||
self.pc += 4;
|
||||
i32::from_le_bytes(bytes)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_i64(&mut self) -> i64 {
|
||||
let bytes = self.code[self.pc..self.pc + 8]
|
||||
.try_into()
|
||||
.expect("no enough bytes");
|
||||
self.pc += 8;
|
||||
i64::from_le_bytes(bytes)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_f64(&mut self) -> f64 {
|
||||
let bytes = self.code[self.pc..self.pc + 8]
|
||||
.try_into()
|
||||
.expect("no enough bytes");
|
||||
self.pc += 8;
|
||||
f64::from_le_bytes(bytes)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_operand_data(&mut self) {
|
||||
use OperandType::*;
|
||||
let tag = self.read_u8();
|
||||
let ty = OperandType::try_from_primitive(tag).expect("invalid operand type");
|
||||
match ty {
|
||||
Const => {
|
||||
self.read_u32();
|
||||
}
|
||||
BigInt => {
|
||||
self.read_i64();
|
||||
}
|
||||
Local => {
|
||||
self.read_u8();
|
||||
self.read_u32();
|
||||
}
|
||||
BuiltinConst => {
|
||||
self.read_u32();
|
||||
}
|
||||
Builtins => {}
|
||||
ReplBinding => {
|
||||
self.read_u32();
|
||||
}
|
||||
ScopedImportBinding => {
|
||||
self.read_u32();
|
||||
self.read_u32();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disassemble(&mut self) -> String {
|
||||
self.disassemble_impl(false)
|
||||
}
|
||||
|
||||
pub fn disassemble_colored(&mut self) -> String {
|
||||
self.disassemble_impl(true)
|
||||
}
|
||||
|
||||
fn disassemble_impl(&mut self, color: bool) -> String {
|
||||
let mut out = String::new();
|
||||
if color {
|
||||
let _ = writeln!(out, "{}", "=== Bytecode Disassembly ===".bold().white());
|
||||
let _ = writeln!(
|
||||
out,
|
||||
"{} {}",
|
||||
"Length:".white(),
|
||||
format!("{} bytes", self.code.len()).cyan()
|
||||
);
|
||||
} else {
|
||||
let _ = writeln!(out, "=== Bytecode Disassembly ===");
|
||||
let _ = writeln!(out, "Length: {} bytes", self.code.len());
|
||||
}
|
||||
|
||||
while self.pc < self.code.len() {
|
||||
let start_pos = self.pc;
|
||||
let op_byte = self.read_u8();
|
||||
let (mnemonic, args) = self.decode_instruction(op_byte, start_pos);
|
||||
|
||||
let bytes_slice = &self.code[start_pos + 1..self.pc];
|
||||
let mut chunks = bytes_slice.chunks(4);
|
||||
|
||||
let first_chunk = chunks.next().unwrap_or(&[]);
|
||||
let bytes_str = {
|
||||
let mut temp = format!("{:02x}", self.code[start_pos]);
|
||||
for b in first_chunk {
|
||||
let _ = write!(&mut temp, " {:02x}", b);
|
||||
}
|
||||
temp
|
||||
};
|
||||
|
||||
if color {
|
||||
let sep = if args.is_empty() { "" } else { " " };
|
||||
let _ = writeln!(
|
||||
out,
|
||||
"{} {:<14} | {}{}{}",
|
||||
format!("{:04x}", start_pos).dimmed(),
|
||||
bytes_str.green(),
|
||||
mnemonic.yellow().bold(),
|
||||
sep,
|
||||
args.cyan()
|
||||
);
|
||||
} else {
|
||||
let op_str = if args.is_empty() {
|
||||
mnemonic.to_string()
|
||||
} else {
|
||||
format!("{} {}", mnemonic, args)
|
||||
};
|
||||
let _ = writeln!(out, "{:04x} {:<14} | {}", start_pos, bytes_str, op_str);
|
||||
}
|
||||
|
||||
for chunk in chunks {
|
||||
let bytes_str = {
|
||||
let mut temp = String::from(" ");
|
||||
for b in chunk {
|
||||
let _ = write!(&mut temp, " {:02x}", b);
|
||||
}
|
||||
temp
|
||||
};
|
||||
|
||||
let extra_width = if start_pos > 0 {
|
||||
start_pos.ilog2() >> 4
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if color {
|
||||
let _ = write!(out, " ");
|
||||
for _ in 0..extra_width {
|
||||
let _ = write!(out, " ");
|
||||
}
|
||||
let _ = writeln!(out, " {:<14} |", bytes_str.green());
|
||||
} else {
|
||||
let _ = write!(out, " ");
|
||||
for _ in 0..extra_width {
|
||||
let _ = write!(out, " ");
|
||||
}
|
||||
let _ = writeln!(out, " {:<14} |", bytes_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn decode_instruction(&mut self, op_byte: u8, current_pc: usize) -> (&'static str, String) {
|
||||
let op = Op::try_from_primitive(op_byte).expect("invalid op code");
|
||||
|
||||
match op {
|
||||
Op::PushSmi => {
|
||||
let val = self.read_i32();
|
||||
("PushSmi", format!("{}", val))
|
||||
}
|
||||
Op::PushBigInt => {
|
||||
let val = self.read_i64();
|
||||
("PushBigInt", format!("{}", val))
|
||||
}
|
||||
Op::PushFloat => {
|
||||
let val = self.read_f64();
|
||||
("PushFloat", format!("{}", val))
|
||||
}
|
||||
Op::PushString => {
|
||||
let idx = self.read_u32();
|
||||
let s = self.ctx.resolve_string(idx);
|
||||
let len = s.len();
|
||||
let mut s_fmt = format!("{:?}", s);
|
||||
if s_fmt.len() > 60 {
|
||||
s_fmt.truncate(57);
|
||||
#[allow(clippy::unwrap_used)]
|
||||
write!(s_fmt, "...\" (total {len} bytes)").unwrap();
|
||||
}
|
||||
("PushString", format!("@{} {}", idx, s_fmt))
|
||||
}
|
||||
Op::PushNull => ("PushNull", String::new()),
|
||||
Op::PushTrue => ("PushTrue", String::new()),
|
||||
Op::PushFalse => ("PushFalse", String::new()),
|
||||
|
||||
Op::LoadLocal => {
|
||||
let idx = self.read_u32();
|
||||
("LoadLocal", format!("[{}]", idx))
|
||||
}
|
||||
Op::LoadOuter => {
|
||||
let depth = self.read_u8();
|
||||
let idx = self.read_u32();
|
||||
("LoadOuter", format!("depth={} [{}]", depth, idx))
|
||||
}
|
||||
Op::StoreLocal => {
|
||||
let idx = self.read_u32();
|
||||
("StoreLocal", format!("[{}]", idx))
|
||||
}
|
||||
Op::AllocLocals => {
|
||||
let count = self.read_u32();
|
||||
("AllocLocals", format!("count={}", count))
|
||||
}
|
||||
|
||||
Op::MakeThunk => {
|
||||
let offset = self.read_u32();
|
||||
("MakeThunk", format!("-> {:04x}", offset))
|
||||
}
|
||||
Op::MakeClosure => {
|
||||
let offset = self.read_u32();
|
||||
let slots = self.read_u32();
|
||||
("MakeClosure", format!("-> {:04x} slots={}", offset, slots))
|
||||
}
|
||||
Op::MakePatternClosure => {
|
||||
let offset = self.read_u32();
|
||||
let slots = self.read_u32();
|
||||
let req_count = self.read_u16();
|
||||
let opt_count = self.read_u16();
|
||||
let ellipsis = self.read_u8() != 0;
|
||||
|
||||
let mut arg_str = format!(
|
||||
"-> {:04x} slots={} req={} opt={} ...={})",
|
||||
offset, slots, req_count, opt_count, ellipsis
|
||||
);
|
||||
|
||||
arg_str.push_str(" Args=[");
|
||||
for _ in 0..req_count {
|
||||
let idx = self.read_u32();
|
||||
arg_str.push_str(&format!("Req({}) ", self.ctx.resolve_string(idx)));
|
||||
}
|
||||
for _ in 0..opt_count {
|
||||
let idx = self.read_u32();
|
||||
arg_str.push_str(&format!("Opt({}) ", self.ctx.resolve_string(idx)));
|
||||
}
|
||||
|
||||
let total_args = req_count + opt_count;
|
||||
for _ in 0..total_args {
|
||||
let _name_idx = self.read_u32();
|
||||
let _span_id = self.read_u32();
|
||||
}
|
||||
arg_str.push(']');
|
||||
|
||||
("MakePatternClosure", arg_str)
|
||||
}
|
||||
|
||||
Op::Call => {
|
||||
self.read_operand_data();
|
||||
("Call", "arg=?".into())
|
||||
}
|
||||
Op::DispatchPrimOp => {
|
||||
let phase = PrimOpPhase::try_from(self.read_u8()).expect("invalid primop phase");
|
||||
("DispatchPrimOp", format!("phase={phase:?}"))
|
||||
}
|
||||
|
||||
Op::MakeAttrs => {
|
||||
let static_count = self.read_u32();
|
||||
let dynamic_count = self.read_u32();
|
||||
let mut args = format!("static={} dynamic={}", static_count, dynamic_count);
|
||||
|
||||
for _ in 0..static_count {
|
||||
let key_id = self.read_u32();
|
||||
let _ = write!(args, " [{}={}", self.ctx.resolve_string(key_id), key_id);
|
||||
self.read_operand_data();
|
||||
let _span_id = self.read_u32();
|
||||
args.push(']');
|
||||
}
|
||||
|
||||
for _ in 0..dynamic_count {
|
||||
let _ = write!(args, " [dyn");
|
||||
self.read_operand_data();
|
||||
let _span_id = self.read_u32();
|
||||
args.push(']');
|
||||
}
|
||||
|
||||
("MakeAttrs", args)
|
||||
}
|
||||
Op::MakeEmptyAttrs => ("MakeEmptyAttrs", String::new()),
|
||||
|
||||
Op::SelectStatic => {
|
||||
let span_id = self.read_u32();
|
||||
let key_id = self.read_u32();
|
||||
(
|
||||
"SelectStatic",
|
||||
format!("key={} span={}", self.ctx.resolve_string(key_id), span_id),
|
||||
)
|
||||
}
|
||||
Op::SelectDynamic => {
|
||||
let span_id = self.read_u32();
|
||||
("SelectDynamic", format!("span={}", span_id))
|
||||
}
|
||||
Op::HasAttrPathStatic => {
|
||||
let span_id = self.read_u32();
|
||||
let key_id = self.read_u32();
|
||||
(
|
||||
"HasAttrPathStatic",
|
||||
format!("key={} span={}", self.ctx.resolve_string(key_id), span_id),
|
||||
)
|
||||
}
|
||||
Op::HasAttrPathDynamic => {
|
||||
let span_id = self.read_u32();
|
||||
("HasAttrPathDynamic", format!("span={}", span_id))
|
||||
}
|
||||
Op::HasAttrStatic => {
|
||||
let key_id = self.read_u32();
|
||||
(
|
||||
"HasAttrStatic",
|
||||
format!("key={}", self.ctx.resolve_string(key_id)),
|
||||
)
|
||||
}
|
||||
Op::HasAttrDynamic => ("HasAttrDynamic", String::new()),
|
||||
Op::HasAttrResolve => ("HasAttrResolve", String::new()),
|
||||
Op::JumpIfSelectSucceeded => {
|
||||
let offset = self.read_i32();
|
||||
let target = (current_pc as isize + 1 + 4 + offset as isize) as usize;
|
||||
(
|
||||
"JumpIfSelectSucceeded",
|
||||
format!("-> {:04x} offset={}", target, offset),
|
||||
)
|
||||
}
|
||||
Op::JumpIfSelectFailed => {
|
||||
let offset = self.read_i32();
|
||||
let target = (current_pc as isize + 1 + 4 + offset as isize) as usize;
|
||||
(
|
||||
"JumpIfSelectFailed",
|
||||
format!("-> {:04x} offset={}", target, offset),
|
||||
)
|
||||
}
|
||||
|
||||
Op::MakeList => {
|
||||
let count = self.read_u32();
|
||||
for _ in 0..count {
|
||||
self.read_operand_data();
|
||||
}
|
||||
("MakeList", format!("size={}", count))
|
||||
}
|
||||
Op::MakeEmptyList => ("MakeEmptyList", String::new()),
|
||||
|
||||
Op::OpAdd => ("OpAdd", String::new()),
|
||||
Op::OpSub => ("OpSub", String::new()),
|
||||
Op::OpMul => ("OpMul", String::new()),
|
||||
Op::OpDiv => ("OpDiv", String::new()),
|
||||
Op::OpEq => ("OpEq", String::new()),
|
||||
Op::OpNeq => ("OpNeq", String::new()),
|
||||
Op::OpLt => ("OpLt", String::new()),
|
||||
Op::OpGt => ("OpGt", String::new()),
|
||||
Op::OpLeq => ("OpLeq", String::new()),
|
||||
Op::OpGeq => ("OpGeq", String::new()),
|
||||
Op::OpConcat => ("OpConcat", String::new()),
|
||||
Op::OpUpdate => ("OpUpdate", String::new()),
|
||||
Op::OpNeg => ("OpNeg", String::new()),
|
||||
Op::OpNot => ("OpNot", String::new()),
|
||||
|
||||
Op::JumpIfFalse => {
|
||||
let offset = self.read_i32();
|
||||
let target = (current_pc as isize + 1 + 4 + offset as isize) as usize;
|
||||
(
|
||||
"JumpIfFalse",
|
||||
format!("-> {:04x} offset={}", target, offset),
|
||||
)
|
||||
}
|
||||
Op::JumpIfTrue => {
|
||||
let offset = self.read_i32();
|
||||
let target = (current_pc as isize + 1 + 4 + offset as isize) as usize;
|
||||
("JumpIfTrue", format!("-> {:04x} offset={}", target, offset))
|
||||
}
|
||||
Op::Jump => {
|
||||
let offset = self.read_i32();
|
||||
let target = (current_pc as isize + 1 + 4 + offset as isize) as usize;
|
||||
("Jump", format!("-> {:04x} offset={}", target, offset))
|
||||
}
|
||||
|
||||
Op::ConcatStrings => {
|
||||
let count = self.read_u16();
|
||||
let force = self.read_u8();
|
||||
("ConcatStrings", format!("count={} force={}", count, force))
|
||||
}
|
||||
Op::CoerceToString => ("CoerceToString", String::new()),
|
||||
Op::ResolvePath => {
|
||||
let dir_id = self.read_u32();
|
||||
let dir = self.ctx.resolve_string(dir_id);
|
||||
("ResolvePath", format!("dir={:?}", dir))
|
||||
}
|
||||
Op::Assert => {
|
||||
let raw_idx = self.read_u32();
|
||||
let span_id = self.read_u32();
|
||||
("Assert", format!("text_id={} span={}", raw_idx, span_id))
|
||||
}
|
||||
Op::LookupWith => {
|
||||
let idx = self.read_u32();
|
||||
let name = self.ctx.resolve_string(idx);
|
||||
let n = self.read_u8();
|
||||
for _ in 0..n {
|
||||
self.read_operand_data();
|
||||
}
|
||||
("LookupWith", format!("sym={:?} n={}", name, n))
|
||||
}
|
||||
|
||||
Op::LoadBuiltins => ("LoadBuiltins", String::new()),
|
||||
Op::LoadBuiltin => {
|
||||
let id = self.read_u8();
|
||||
("LoadBuiltin", format!("id={}", id))
|
||||
}
|
||||
Op::LoadReplBinding => {
|
||||
let idx = self.read_u32();
|
||||
let name = self.ctx.resolve_string(idx);
|
||||
("LoadReplBinding", format!("{:?}", name))
|
||||
}
|
||||
Op::LoadScopedBinding => {
|
||||
let slot = self.read_u32();
|
||||
let idx = self.read_u32();
|
||||
let name = self.ctx.resolve_string(idx);
|
||||
("LoadScopedBinding", format!("slot={} {:?}", slot, name))
|
||||
}
|
||||
Op::Return => ("Return", String::new()),
|
||||
Op::Illegal => ("Illegal", String::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use fix_lang::{BuiltinId, StringId};
|
||||
use num_enum::TryFromPrimitive;
|
||||
use string_interner::Symbol as _;
|
||||
|
||||
pub mod disassembler;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct InstructionPtr(pub usize);
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum Op {
|
||||
PushSmi,
|
||||
PushBigInt,
|
||||
PushFloat,
|
||||
PushString,
|
||||
PushNull,
|
||||
PushTrue,
|
||||
PushFalse,
|
||||
|
||||
LoadLocal,
|
||||
LoadOuter,
|
||||
StoreLocal,
|
||||
AllocLocals,
|
||||
|
||||
MakeThunk,
|
||||
MakeClosure,
|
||||
MakePatternClosure,
|
||||
|
||||
Call,
|
||||
DispatchPrimOp,
|
||||
|
||||
MakeAttrs,
|
||||
MakeEmptyAttrs,
|
||||
SelectStatic,
|
||||
SelectDynamic,
|
||||
HasAttrPathStatic,
|
||||
HasAttrPathDynamic,
|
||||
HasAttrStatic,
|
||||
HasAttrDynamic,
|
||||
HasAttrResolve,
|
||||
JumpIfSelectSucceeded,
|
||||
JumpIfSelectFailed,
|
||||
|
||||
MakeList,
|
||||
MakeEmptyList,
|
||||
|
||||
OpAdd,
|
||||
OpSub,
|
||||
OpMul,
|
||||
OpDiv,
|
||||
OpEq,
|
||||
OpNeq,
|
||||
OpLt,
|
||||
OpGt,
|
||||
OpLeq,
|
||||
OpGeq,
|
||||
OpConcat,
|
||||
OpUpdate,
|
||||
|
||||
OpNeg,
|
||||
OpNot,
|
||||
|
||||
JumpIfFalse,
|
||||
JumpIfTrue,
|
||||
Jump,
|
||||
|
||||
CoerceToString,
|
||||
|
||||
ConcatStrings,
|
||||
ResolvePath,
|
||||
|
||||
Assert,
|
||||
|
||||
LookupWith,
|
||||
|
||||
LoadBuiltins,
|
||||
LoadBuiltin,
|
||||
|
||||
LoadReplBinding,
|
||||
LoadScopedBinding,
|
||||
|
||||
Return,
|
||||
|
||||
Illegal,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
||||
pub enum OperandType {
|
||||
Const,
|
||||
BigInt,
|
||||
Local,
|
||||
BuiltinConst,
|
||||
Builtins,
|
||||
ReplBinding,
|
||||
ScopedImportBinding,
|
||||
}
|
||||
|
||||
pub enum Const {
|
||||
Smi(i32),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(StringId),
|
||||
Path(StringId),
|
||||
PrimOp {
|
||||
id: BuiltinId,
|
||||
arity: u8,
|
||||
dispatch_ip: u32,
|
||||
},
|
||||
Null,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
||||
pub enum AttrKeyType {
|
||||
Static,
|
||||
Dynamic,
|
||||
}
|
||||
|
||||
pub enum OperandData {
|
||||
Const(u32),
|
||||
BigInt(i64),
|
||||
Local { layer: u8, idx: u32 },
|
||||
BuiltinConst(StringId),
|
||||
Builtins,
|
||||
ReplBinding(StringId),
|
||||
ScopedImportBinding { slot_id: u32, name: StringId },
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub enum PrimOpPhase {
|
||||
Abort,
|
||||
Add,
|
||||
AddErrorContext,
|
||||
|
||||
All,
|
||||
AllCallPred,
|
||||
AllCheck,
|
||||
|
||||
Any,
|
||||
AnyCallPred,
|
||||
AnyCheck,
|
||||
|
||||
AppendContext,
|
||||
AttrNames,
|
||||
AttrValues,
|
||||
BaseNameOf,
|
||||
BitAnd,
|
||||
BitOr,
|
||||
BitXor,
|
||||
Break,
|
||||
CatAttrs,
|
||||
Ceil,
|
||||
CompareVersions,
|
||||
ConcatLists,
|
||||
ConcatMap,
|
||||
ConcatStringsSep,
|
||||
ConvertHash,
|
||||
|
||||
DeepSeq,
|
||||
DeepSeqPush,
|
||||
DeepSeqLoop,
|
||||
|
||||
Derivation,
|
||||
DerivationStrict,
|
||||
DirOf,
|
||||
Div,
|
||||
Elem,
|
||||
ElemAt,
|
||||
FetchGit,
|
||||
FetchMercurial,
|
||||
FetchTarball,
|
||||
FetchTree,
|
||||
FetchUrl,
|
||||
|
||||
FilterForceList,
|
||||
FilterCallPred,
|
||||
FilterCheck,
|
||||
|
||||
FilterSource,
|
||||
FindFile,
|
||||
Floor,
|
||||
FoldlStrict,
|
||||
FoldlStrictEmpty,
|
||||
FoldlStrictCall1,
|
||||
FoldlStrictCall2,
|
||||
FoldlStrictUpdate,
|
||||
FromJSON,
|
||||
FromTOML,
|
||||
FunctionArgs,
|
||||
GenList,
|
||||
GenericClosure,
|
||||
GetAttr,
|
||||
GetContext,
|
||||
GetEnv,
|
||||
GroupBy,
|
||||
HasAttr,
|
||||
HasContext,
|
||||
HashFile,
|
||||
HashString,
|
||||
Head,
|
||||
Import,
|
||||
IntersectAttrs,
|
||||
IsAttrs,
|
||||
IsBool,
|
||||
IsFloat,
|
||||
IsFunction,
|
||||
IsInt,
|
||||
IsList,
|
||||
IsNull,
|
||||
IsPath,
|
||||
IsString,
|
||||
Length,
|
||||
LessThan,
|
||||
ListToAttrs,
|
||||
Map,
|
||||
MapAttrs,
|
||||
Match,
|
||||
Mul,
|
||||
ParseDrvName,
|
||||
Partition,
|
||||
Path,
|
||||
PathExists,
|
||||
Placeholder,
|
||||
ReadDir,
|
||||
ReadFile,
|
||||
ReadFileType,
|
||||
RemoveAttrs,
|
||||
ReplaceStrings,
|
||||
ScopedImport,
|
||||
Seq,
|
||||
Sort,
|
||||
Split,
|
||||
SplitVersion,
|
||||
StorePath,
|
||||
StringLength,
|
||||
Sub,
|
||||
Substring,
|
||||
Tail,
|
||||
Throw,
|
||||
ToFile,
|
||||
ToJSON,
|
||||
ToPath,
|
||||
ToString,
|
||||
ToXML,
|
||||
Trace,
|
||||
TryEval,
|
||||
TypeOf,
|
||||
UnsafeDiscardStringContext,
|
||||
UnsafeGetAttrPos,
|
||||
Warn,
|
||||
ZipAttrsWith,
|
||||
|
||||
ForceResultShallow,
|
||||
ForceResultShallowPush,
|
||||
ForceResultShallowLoop,
|
||||
ForceResultDeepFinish,
|
||||
|
||||
EqStep,
|
||||
EqForce,
|
||||
|
||||
CallPattern,
|
||||
CallFunctor1,
|
||||
CallFunctor2,
|
||||
|
||||
ImportFinalize,
|
||||
ScopedImportFinalize,
|
||||
|
||||
AppendContextLoop,
|
||||
AppendContextEntryForced,
|
||||
AppendContextOutputsForced,
|
||||
AppendContextOutputElementLoop,
|
||||
AppendContextOutputElementForced,
|
||||
|
||||
UnsafeDiscardOutputDependency,
|
||||
|
||||
Illegal,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for PrimOpPhase {
|
||||
type Error = u8;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
if (0..Self::Illegal as u8).contains(&value) {
|
||||
Ok(unsafe { std::mem::transmute::<u8, Self>(value) })
|
||||
} else {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimOpPhase {
|
||||
pub fn entry_for_builtin(id: BuiltinId) -> Self {
|
||||
use BuiltinId::*;
|
||||
match id {
|
||||
Abort => Self::Abort,
|
||||
Add => Self::Add,
|
||||
AddErrorContext => Self::AddErrorContext,
|
||||
All => Self::All,
|
||||
Any => Self::Any,
|
||||
AppendContext => Self::AppendContext,
|
||||
AttrNames => Self::AttrNames,
|
||||
AttrValues => Self::AttrValues,
|
||||
BaseNameOf => Self::BaseNameOf,
|
||||
BitAnd => Self::BitAnd,
|
||||
BitOr => Self::BitOr,
|
||||
BitXor => Self::BitXor,
|
||||
Break => Self::Break,
|
||||
CatAttrs => Self::CatAttrs,
|
||||
Ceil => Self::Ceil,
|
||||
CompareVersions => Self::CompareVersions,
|
||||
ConcatLists => Self::ConcatLists,
|
||||
ConcatMap => Self::ConcatMap,
|
||||
ConcatStringsSep => Self::ConcatStringsSep,
|
||||
ConvertHash => Self::ConvertHash,
|
||||
DeepSeq => Self::DeepSeq,
|
||||
Derivation => Self::Derivation,
|
||||
DerivationStrict => Self::DerivationStrict,
|
||||
DirOf => Self::DirOf,
|
||||
Div => Self::Div,
|
||||
Elem => Self::Elem,
|
||||
ElemAt => Self::ElemAt,
|
||||
FetchGit => Self::FetchGit,
|
||||
FetchMercurial => Self::FetchMercurial,
|
||||
FetchTarball => Self::FetchTarball,
|
||||
FetchTree => Self::FetchTree,
|
||||
FetchUrl => Self::FetchUrl,
|
||||
Filter => Self::FilterForceList,
|
||||
FilterSource => Self::FilterSource,
|
||||
FindFile => Self::FindFile,
|
||||
Floor => Self::Floor,
|
||||
FoldlStrict => Self::FoldlStrict,
|
||||
FromJSON => Self::FromJSON,
|
||||
FromTOML => Self::FromTOML,
|
||||
FunctionArgs => Self::FunctionArgs,
|
||||
GenList => Self::GenList,
|
||||
GenericClosure => Self::GenericClosure,
|
||||
GetAttr => Self::GetAttr,
|
||||
GetContext => Self::GetContext,
|
||||
GetEnv => Self::GetEnv,
|
||||
GroupBy => Self::GroupBy,
|
||||
HasAttr => Self::HasAttr,
|
||||
HasContext => Self::HasContext,
|
||||
HashFile => Self::HashFile,
|
||||
HashString => Self::HashString,
|
||||
Head => Self::Head,
|
||||
Import => Self::Import,
|
||||
IntersectAttrs => Self::IntersectAttrs,
|
||||
IsAttrs => Self::IsAttrs,
|
||||
IsBool => Self::IsBool,
|
||||
IsFloat => Self::IsFloat,
|
||||
IsFunction => Self::IsFunction,
|
||||
IsInt => Self::IsInt,
|
||||
IsList => Self::IsList,
|
||||
IsNull => Self::IsNull,
|
||||
IsPath => Self::IsPath,
|
||||
IsString => Self::IsString,
|
||||
Length => Self::Length,
|
||||
LessThan => Self::LessThan,
|
||||
ListToAttrs => Self::ListToAttrs,
|
||||
Map => Self::Map,
|
||||
MapAttrs => Self::MapAttrs,
|
||||
Match => Self::Match,
|
||||
Mul => Self::Mul,
|
||||
ParseDrvName => Self::ParseDrvName,
|
||||
Partition => Self::Partition,
|
||||
Path => Self::Path,
|
||||
PathExists => Self::PathExists,
|
||||
Placeholder => Self::Placeholder,
|
||||
ReadDir => Self::ReadDir,
|
||||
ReadFile => Self::ReadFile,
|
||||
ReadFileType => Self::ReadFileType,
|
||||
RemoveAttrs => Self::RemoveAttrs,
|
||||
ReplaceStrings => Self::ReplaceStrings,
|
||||
ScopedImport => Self::ScopedImport,
|
||||
Seq => Self::Seq,
|
||||
Sort => Self::Sort,
|
||||
Split => Self::Split,
|
||||
SplitVersion => Self::SplitVersion,
|
||||
StorePath => Self::StorePath,
|
||||
StringLength => Self::StringLength,
|
||||
Sub => Self::Sub,
|
||||
Substring => Self::Substring,
|
||||
Tail => Self::Tail,
|
||||
Throw => Self::Throw,
|
||||
ToFile => Self::ToFile,
|
||||
ToJSON => Self::ToJSON,
|
||||
ToPath => Self::ToPath,
|
||||
ToString => Self::ToString,
|
||||
ToXML => Self::ToXML,
|
||||
Trace => Self::Trace,
|
||||
TryEval => Self::TryEval,
|
||||
TypeOf => Self::TypeOf,
|
||||
UnsafeDiscardStringContext => Self::UnsafeDiscardStringContext,
|
||||
UnsafeDiscardOutputDependency => Self::UnsafeDiscardOutputDependency,
|
||||
UnsafeGetAttrPos => Self::UnsafeGetAttrPos,
|
||||
Warn => Self::Warn,
|
||||
ZipAttrsWith => Self::ZipAttrsWith,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ip(self) -> u32 {
|
||||
self as u32 * 2
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BytecodeReader<'a> {
|
||||
bytecode: &'a [u8],
|
||||
pc: usize,
|
||||
inst_start_pc: usize,
|
||||
}
|
||||
|
||||
impl<'a> BytecodeReader<'a> {
|
||||
pub fn new(bytecode: &'a [u8], pc: usize) -> Self {
|
||||
Self {
|
||||
bytecode,
|
||||
pc,
|
||||
inst_start_pc: pc,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub 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)]
|
||||
pub fn read_op(&mut self) -> 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 fn read_u8(&mut self) -> u8 {
|
||||
let val = self.bytecode[self.pc];
|
||||
self.pc += 1;
|
||||
val
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_u16(&mut self) -> u16 {
|
||||
u16::from_le_bytes(self.read_array())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_u32(&mut self) -> u32 {
|
||||
u32::from_le_bytes(self.read_array())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_i32(&mut self) -> i32 {
|
||||
i32::from_le_bytes(self.read_array())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_i64(&mut self) -> i64 {
|
||||
i64::from_le_bytes(self.read_array())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_f64(&mut self) -> f64 {
|
||||
f64::from_le_bytes(self.read_array())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub 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 fn read_operand_data(&mut self) -> 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 => OperandData::Const(self.read_u32()),
|
||||
OperandType::BigInt => OperandData::BigInt(self.read_i64()),
|
||||
OperandType::Local => {
|
||||
let layer = self.read_u8();
|
||||
let idx = self.read_u32();
|
||||
OperandData::Local { layer, idx }
|
||||
}
|
||||
OperandType::BuiltinConst => OperandData::BuiltinConst(self.read_string_id()),
|
||||
OperandType::Builtins => OperandData::Builtins,
|
||||
OperandType::ReplBinding => OperandData::ReplBinding(self.read_string_id()),
|
||||
OperandType::ScopedImportBinding => {
|
||||
let slot_id = self.read_u32();
|
||||
let name = self.read_string_id();
|
||||
OperandData::ScopedImportBinding { slot_id, name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pc(&self) -> usize {
|
||||
self.pc
|
||||
}
|
||||
|
||||
pub fn set_pc(&mut self, pc: usize) {
|
||||
self.pc = pc;
|
||||
}
|
||||
|
||||
pub fn inst_start_pc(&self) -> usize {
|
||||
self.inst_start_pc
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user