mirror of
https://github.com/duhanbalci/dexpr.git
synced 2026-08-30 07:51:41 +00:00
244 lines
8.5 KiB
Rust
244 lines
8.5 KiB
Rust
use crate::bytecode::BytecodeReader;
|
|
use crate::opcodes::OpCodeByte;
|
|
|
|
/// A utility function to disassemble bytecode for debugging
|
|
pub fn disassemble_bytecode(bytecode: &[u8]) -> Vec<String> {
|
|
let mut result = Vec::new();
|
|
let mut reader = BytecodeReader::new(bytecode);
|
|
|
|
while reader.remaining() > 0 {
|
|
let start_position = reader.position();
|
|
let opcode_byte = match reader.read_byte() {
|
|
Ok(b) => b,
|
|
Err(_) => break,
|
|
};
|
|
|
|
let opcode = match OpCodeByte::from_byte(opcode_byte) {
|
|
Some(op) => op,
|
|
None => {
|
|
result.push(format!(
|
|
"{:04x}: Unknown opcode: 0x{:02x}",
|
|
start_position, opcode_byte
|
|
));
|
|
continue;
|
|
}
|
|
};
|
|
|
|
let instruction = match opcode {
|
|
OpCodeByte::LoadConst => {
|
|
let reg = reader.read_byte();
|
|
let value = reader.read_value();
|
|
match (reg, value) {
|
|
(Ok(r), Ok(v)) => format!("{:04x}: LoadConst r{}, {}", start_position, r, v),
|
|
_ => format!("{:04x}: LoadConst (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::Move => {
|
|
let dest = reader.read_byte();
|
|
let src = reader.read_byte();
|
|
match (dest, src) {
|
|
(Ok(d), Ok(s)) => format!("{:04x}: Move r{} = r{}", start_position, d, s),
|
|
_ => format!("{:04x}: Move (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::LoadLocal => {
|
|
let reg = reader.read_byte();
|
|
let offset = reader.read_byte();
|
|
match (reg, offset) {
|
|
(Ok(r), Ok(o)) => format!("{:04x}: LoadLocal r{}, offset={}", start_position, r, o),
|
|
_ => format!("{:04x}: LoadLocal (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::StoreLocal => {
|
|
let offset = reader.read_byte();
|
|
let reg = reader.read_byte();
|
|
match (offset, reg) {
|
|
(Ok(o), Ok(r)) => format!("{:04x}: StoreLocal offset={}, r{}", start_position, o, r),
|
|
_ => format!("{:04x}: StoreLocal (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::LoadGlobal => {
|
|
let reg = reader.read_byte();
|
|
let name = reader.read_string();
|
|
match (reg, name) {
|
|
(Ok(r), Ok(n)) => format!("{:04x}: LoadGlobal r{}, \"{}\"", start_position, r, n),
|
|
_ => format!("{:04x}: LoadGlobal (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::StoreGlobal => {
|
|
let name = reader.read_string();
|
|
let reg = reader.read_byte();
|
|
match (name, reg) {
|
|
(Ok(n), Ok(r)) => format!("{:04x}: StoreGlobal \"{}\", r{}", start_position, n, r),
|
|
_ => format!("{:04x}: StoreGlobal (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::Add
|
|
| OpCodeByte::Sub
|
|
| OpCodeByte::Mul
|
|
| OpCodeByte::Div
|
|
| OpCodeByte::Mod
|
|
| OpCodeByte::Pow
|
|
| OpCodeByte::Lt
|
|
| OpCodeByte::Lte
|
|
| OpCodeByte::Gt
|
|
| OpCodeByte::Gte
|
|
| OpCodeByte::Eq
|
|
| OpCodeByte::Neq
|
|
| OpCodeByte::And
|
|
| OpCodeByte::Or
|
|
| OpCodeByte::Contains
|
|
| OpCodeByte::Concat => {
|
|
let res = reader.read_byte();
|
|
let left = reader.read_byte();
|
|
let right = reader.read_byte();
|
|
match (res, left, right) {
|
|
(Ok(r), Ok(l), Ok(rg)) => {
|
|
format!("{:04x}: {:?} r{}, r{}, r{}", start_position, opcode, r, l, rg)
|
|
}
|
|
_ => format!("{:04x}: {:?} (truncated)", start_position, opcode),
|
|
}
|
|
}
|
|
OpCodeByte::Neg | OpCodeByte::Not => {
|
|
let res = reader.read_byte();
|
|
let operand = reader.read_byte();
|
|
match (res, operand) {
|
|
(Ok(r), Ok(o)) => format!("{:04x}: {:?} r{}, r{}", start_position, opcode, r, o),
|
|
_ => format!("{:04x}: {:?} (truncated)", start_position, opcode),
|
|
}
|
|
}
|
|
OpCodeByte::Jump => match reader.read_u32() {
|
|
Ok(addr) => format!("{:04x}: Jump -> 0x{:04x}", start_position, addr),
|
|
Err(_) => format!("{:04x}: Jump (truncated)", start_position),
|
|
},
|
|
OpCodeByte::JumpIfFalse => {
|
|
let reg = reader.read_byte();
|
|
let addr = reader.read_u32();
|
|
match (reg, addr) {
|
|
(Ok(r), Ok(a)) => format!("{:04x}: JumpIfFalse r{} -> 0x{:04x}", start_position, r, a),
|
|
_ => format!("{:04x}: JumpIfFalse (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::JumpIfTrue => {
|
|
let reg = reader.read_byte();
|
|
let addr = reader.read_u32();
|
|
match (reg, addr) {
|
|
(Ok(r), Ok(a)) => format!("{:04x}: JumpIfTrue r{} -> 0x{:04x}", start_position, r, a),
|
|
_ => format!("{:04x}: JumpIfTrue (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::MethodCall => {
|
|
let res = reader.read_byte();
|
|
let obj = reader.read_byte();
|
|
let method = reader.read_string();
|
|
let arg_count = reader.read_byte();
|
|
match (res, obj, method, arg_count) {
|
|
(Ok(r), Ok(o), Ok(m), Ok(count)) => {
|
|
let mut arg_regs = Vec::new();
|
|
let mut truncated = false;
|
|
for _ in 0..count {
|
|
match reader.read_byte() {
|
|
Ok(reg) => arg_regs.push(format!("r{}", reg)),
|
|
Err(_) => {
|
|
truncated = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if truncated {
|
|
format!(
|
|
"{:04x}: MethodCall r{} = r{}.{}(truncated args)",
|
|
start_position, r, o, m
|
|
)
|
|
} else {
|
|
format!(
|
|
"{:04x}: MethodCall r{} = r{}.{}({})",
|
|
start_position,
|
|
r,
|
|
o,
|
|
m,
|
|
arg_regs.join(", ")
|
|
)
|
|
}
|
|
}
|
|
_ => format!("{:04x}: MethodCall (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::Log => match reader.read_byte() {
|
|
Ok(reg) => format!("{:04x}: Log r{}", start_position, reg),
|
|
Err(_) => format!("{:04x}: Log (truncated)", start_position),
|
|
},
|
|
OpCodeByte::CallDefault => {
|
|
let res = reader.read_byte();
|
|
let fn_id = reader.read_byte();
|
|
let arg_count = reader.read_byte();
|
|
match (res, fn_id, arg_count) {
|
|
(Ok(r), Ok(id), Ok(count)) => {
|
|
let fn_name = crate::opcodes::default_fn::name(id).unwrap_or("?");
|
|
let mut arg_regs = Vec::new();
|
|
for _ in 0..count {
|
|
if let Ok(reg) = reader.read_byte() {
|
|
arg_regs.push(format!("r{}", reg));
|
|
}
|
|
}
|
|
format!(
|
|
"{:04x}: CallDefault r{} = {}({})",
|
|
start_position, r, fn_name, arg_regs.join(", ")
|
|
)
|
|
}
|
|
_ => format!("{:04x}: CallDefault (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::CallExternal => {
|
|
let res = reader.read_byte();
|
|
let name = reader.read_string();
|
|
let arg_count = reader.read_byte();
|
|
match (res, name, arg_count) {
|
|
(Ok(r), Ok(n), Ok(count)) => {
|
|
let mut arg_regs = Vec::new();
|
|
for _ in 0..count {
|
|
if let Ok(reg) = reader.read_byte() {
|
|
arg_regs.push(format!("r{}", reg));
|
|
}
|
|
}
|
|
format!(
|
|
"{:04x}: CallExternal r{} = {}({})",
|
|
start_position, r, n, arg_regs.join(", ")
|
|
)
|
|
}
|
|
_ => format!("{:04x}: CallExternal (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::GetProperty => {
|
|
let dest = reader.read_byte();
|
|
let obj = reader.read_byte();
|
|
let prop = reader.read_string();
|
|
match (dest, obj, prop) {
|
|
(Ok(d), Ok(o), Ok(p)) => format!("{:04x}: GetProperty r{} = r{}.{}", start_position, d, o, p),
|
|
_ => format!("{:04x}: GetProperty (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::SetProperty => {
|
|
let obj = reader.read_byte();
|
|
let prop = reader.read_string();
|
|
let val = reader.read_byte();
|
|
match (obj, prop, val) {
|
|
(Ok(o), Ok(p), Ok(v)) => format!("{:04x}: SetProperty r{}.{} = r{}", start_position, o, p, v),
|
|
_ => format!("{:04x}: SetProperty (truncated)", start_position),
|
|
}
|
|
}
|
|
OpCodeByte::SetResult => match reader.read_byte() {
|
|
Ok(reg) => format!("{:04x}: SetResult r{}", start_position, reg),
|
|
Err(_) => format!("{:04x}: SetResult (truncated)", start_position),
|
|
},
|
|
OpCodeByte::ClearResult => format!("{:04x}: ClearResult", start_position),
|
|
OpCodeByte::End => format!("{:04x}: End", start_position),
|
|
};
|
|
|
|
result.push(instruction);
|
|
}
|
|
|
|
result
|
|
}
|
|
|