FerroBabe 0.1.0

A lightweight Rust library for disassembling Java class files without a JVM.
Documentation
use rust_asm::insn::{Insn, LdcValue, MemberRef};

use super::ConstantPoolIndex;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ByteOffset(u16);

impl ByteOffset {
    pub(crate) const fn new(offset: u16) -> Self {
        Self(offset)
    }

    #[must_use]
    pub const fn get(self) -> u16 {
        self.0
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Instruction<'a> {
    offset: ByteOffset,
    instruction: &'a Insn,
}

impl<'a> Instruction<'a> {
    pub(crate) const fn new(offset: u16, instruction: &'a Insn) -> Self {
        Self {
            offset: ByteOffset::new(offset),
            instruction,
        }
    }

    #[must_use]
    pub const fn offset(&self) -> ByteOffset {
        self.offset
    }

    #[must_use]
    pub fn opcode(&self) -> u8 {
        opcode_of(self.instruction)
    }

    #[must_use]
    pub fn operand(&self) -> InstructionOperand<'a> {
        let offset = i32::from(self.offset.get());

        match self.instruction {
            Insn::Simple(_) => InstructionOperand::None,
            Insn::Int(node) => InstructionOperand::Immediate(node.operand),
            Insn::Var(node) => InstructionOperand::Local(node.var_index),
            Insn::Type(node) => {
                InstructionOperand::ConstantPool(ConstantPoolIndex::new(node.type_index))
            }
            Insn::Field(node) => InstructionOperand::Member(MemberReference::from(&node.field_ref)),
            Insn::Method(node) => {
                InstructionOperand::Member(MemberReference::from(&node.method_ref))
            }
            Insn::InvokeInterface(node) => InstructionOperand::InvokeInterface {
                method: ConstantPoolIndex::new(node.method_index),
                count: node.count,
            },
            Insn::InvokeDynamic(node) => InstructionOperand::InvokeDynamic {
                call_site: ConstantPoolIndex::new(node.method_index),
            },
            Insn::Jump(node) => InstructionOperand::Branch {
                relative: node.offset,
                target: offset + node.offset,
            },
            Insn::Ldc(node) => InstructionOperand::Ldc(LdcValueRef::from(&node.value)),
            Insn::Iinc(node) => InstructionOperand::Increment {
                local: node.var_index,
                amount: node.increment,
            },
            Insn::TableSwitch(node) => InstructionOperand::TableSwitch {
                default: SwitchTarget::new(node.default_offset, offset + node.default_offset),
                low: node.low,
                high: node.high,
                targets: &node.offsets,
                base_offset: self.offset,
            },
            Insn::LookupSwitch(node) => InstructionOperand::LookupSwitch {
                default: SwitchTarget::new(node.default_offset, offset + node.default_offset),
                pairs: &node.pairs,
                base_offset: self.offset,
            },
            Insn::MultiANewArray(node) => InstructionOperand::MultiArray {
                class: ConstantPoolIndex::new(node.type_index),
                dimensions: node.dimensions,
            },
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum InstructionOperand<'a> {
    None,
    Immediate(i32),
    Local(u16),
    ConstantPool(ConstantPoolIndex),
    Member(MemberReference<'a>),
    InvokeInterface {
        method: ConstantPoolIndex,
        count: u8,
    },
    InvokeDynamic {
        call_site: ConstantPoolIndex,
    },
    Branch {
        relative: i32,
        target: i32,
    },
    Ldc(LdcValueRef<'a>),
    Increment {
        local: u16,
        amount: i16,
    },
    TableSwitch {
        default: SwitchTarget,
        low: i32,
        high: i32,
        targets: &'a [i32],
        base_offset: ByteOffset,
    },
    LookupSwitch {
        default: SwitchTarget,
        pairs: &'a [(i32, i32)],
        base_offset: ByteOffset,
    },
    MultiArray {
        class: ConstantPoolIndex,
        dimensions: u8,
    },
}

#[derive(Debug, Clone, Copy)]
pub enum MemberReference<'a> {
    ConstantPool(ConstantPoolIndex),
    Symbolic {
        owner: &'a str,
        name: &'a str,
        descriptor: &'a str,
    },
}

impl<'a> From<&'a MemberRef> for MemberReference<'a> {
    fn from(value: &'a MemberRef) -> Self {
        match value {
            MemberRef::Index(index) => Self::ConstantPool(ConstantPoolIndex::new(*index)),
            MemberRef::Symbolic {
                owner,
                name,
                descriptor,
            } => Self::Symbolic {
                owner,
                name,
                descriptor,
            },
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum LdcValueRef<'a> {
    ConstantPool(ConstantPoolIndex),
    String(&'a str),
    TypeDescriptor,
    Integer(i32),
    Float(f32),
    Long(i64),
    Double(f64),
}

impl<'a> From<&'a LdcValue> for LdcValueRef<'a> {
    fn from(value: &'a LdcValue) -> Self {
        match value {
            LdcValue::Index(index) => Self::ConstantPool(ConstantPoolIndex::new(*index)),
            LdcValue::String(value) => Self::String(value),
            LdcValue::Type(_) => Self::TypeDescriptor,
            LdcValue::Int(value) => Self::Integer(*value),
            LdcValue::Float(value) => Self::Float(*value),
            LdcValue::Long(value) => Self::Long(*value),
            LdcValue::Double(value) => Self::Double(*value),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SwitchTarget {
    relative: i32,
    target: i32,
}

impl SwitchTarget {
    const fn new(relative: i32, target: i32) -> Self {
        Self { relative, target }
    }

    #[must_use]
    pub const fn relative(self) -> i32 {
        self.relative
    }

    #[must_use]
    pub const fn target(self) -> i32 {
        self.target
    }
}

fn opcode_of(instruction: &Insn) -> u8 {
    match instruction {
        Insn::Simple(node) => node.opcode,
        Insn::Int(node) => node.insn.opcode,
        Insn::Var(node) => node.insn.opcode,
        Insn::Type(node) => node.insn.opcode,
        Insn::Field(node) => node.insn.opcode,
        Insn::Method(node) => node.insn.opcode,
        Insn::InvokeInterface(node) => node.insn.opcode,
        Insn::InvokeDynamic(node) => node.insn.opcode,
        Insn::Jump(node) => node.insn.opcode,
        Insn::Ldc(node) => node.insn.opcode,
        Insn::Iinc(node) => node.insn.opcode,
        Insn::TableSwitch(node) => node.insn.opcode,
        Insn::LookupSwitch(node) => node.insn.opcode,
        Insn::MultiANewArray(node) => node.insn.opcode,
    }
}