lvm-class 0.7.2

JVM class file parser and writer for the Lagertha VM project
Documentation
use crate::ClassFormatErr;
use crate::attribute::AttributeKind;
use crate::constant_pool::ConstantPool;
use lvm_common::utils::cursor::ByteCursor;
use num_enum::TryFromPrimitive;

/// https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.7.3
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CodeAttributeInfo {
    LineNumberTable {
        attr_name_idx: u16,
        table: Vec<LineNumberEntry>,
    },
    LocalVariableTable {
        attr_name_idx: u16,
        table: Vec<LocalVariableEntry>,
    },
    StackMapTable {
        attr_name_idx: u16,
        table: Vec<StackMapFrame>,
    },
    LocalVariableTypeTable {
        attr_name_idx: u16,
        table: Vec<LocalVariableTypeEntry>,
    },
    RuntimeVisibleTypeAnnotations {
        attr_name_idx: u16,
    },
    RuntimeInvisibleTypeAnnotations {
        attr_name_idx: u16,
    },
}

/// https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.7.12
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LineNumberEntry {
    pub start_pc: u16,
    pub line_number: u16,
}

/// https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.7.13
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LocalVariableEntry {
    pub start_pc: u16,
    pub length: u16,
    pub name_index: u16,
    pub descriptor_index: u16,
    pub index: u16,
}

/// https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.7.14
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LocalVariableTypeEntry {
    pub start_pc: u16,
    pub length: u16,
    pub name_index: u16,
    pub signature_index: u16,
    pub index: u16,
}

/// https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.7.4
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StackMapFrame {
    Same {
        offset_delta: u16,
    },
    SameLocals1StackItem {
        offset_delta: u16,
        stack: VerificationTypeInfo,
    },
    SameLocals1StackItemExtended {
        offset_delta: u16,
        stack: VerificationTypeInfo,
    },
    Chop {
        k: u8,
        offset_delta: u16,
    },
    SameExtended {
        offset_delta: u16,
    },
    Append {
        k: u8,
        offset_delta: u16,
        locals: Vec<VerificationTypeInfo>,
    },
    Full {
        offset_delta: u16,
        locals: Vec<VerificationTypeInfo>,
        stack: Vec<VerificationTypeInfo>,
    },
}

#[derive(Debug, Clone, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)]
pub enum VerificationTypeTag {
    Top,
    Integer,
    Float,
    Double,
    Long,
    Null,
    UninitializedThis,
    Object,
    Uninitialized,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VerificationTypeInfo {
    Top,
    Integer,
    Float,
    Double,
    Long,
    Null,
    UninitializedThis,
    Object(u16),
    Uninitialized(u16),
}

impl<'a> StackMapFrame {
    pub(crate) fn read(cursor: &mut ByteCursor<'a>) -> Result<Self, ClassFormatErr> {
        let frame_type = cursor.u8()?;
        match frame_type {
            0..=63 => Ok(StackMapFrame::Same {
                offset_delta: u16::from(frame_type),
            }),
            64..=127 => Ok(StackMapFrame::SameLocals1StackItem {
                offset_delta: u16::from(frame_type - 64),
                stack: VerificationTypeInfo::read(cursor)?,
            }),
            247 => Ok(StackMapFrame::SameLocals1StackItemExtended {
                offset_delta: cursor.u16()?,
                stack: VerificationTypeInfo::read(cursor)?,
            }),
            248..=250 => Ok(StackMapFrame::Chop {
                k: 251 - frame_type,
                offset_delta: cursor.u16()?,
            }),
            251 => Ok(StackMapFrame::SameExtended {
                offset_delta: cursor.u16()?,
            }),
            252..=254 => Ok(StackMapFrame::Append {
                k: frame_type - 251,
                offset_delta: cursor.u16()?,
                locals: (0..usize::from(frame_type - 251))
                    .map(|_| VerificationTypeInfo::read(cursor)) // -> Result<_, E>
                    .collect::<Result<Vec<_>, _>>()?,
            }),
            255 => {
                let offset_delta = cursor.u16()?;
                let number_of_locals = cursor.u16()?;
                let mut locals = Vec::with_capacity(number_of_locals as usize);
                for _ in 0..number_of_locals {
                    locals.push(VerificationTypeInfo::read(cursor)?)
                }
                let number_of_stack_items = cursor.u16()?;
                let mut stack = Vec::with_capacity(number_of_stack_items as usize);
                for _ in 0..number_of_stack_items {
                    stack.push(VerificationTypeInfo::read(cursor)?)
                }
                Ok(StackMapFrame::Full {
                    offset_delta,
                    locals,
                    stack,
                })
            }
            other => Err(ClassFormatErr::UnknownStackFrameType(other)),
        }
    }
}

impl<'a> VerificationTypeInfo {
    pub(crate) fn read(cursor: &mut ByteCursor<'a>) -> Result<Self, ClassFormatErr> {
        let raw_tag = cursor.u8()?;
        let frame_type: VerificationTypeTag = VerificationTypeTag::try_from_primitive(raw_tag)
            .map_err(|_| ClassFormatErr::UnknownTag(raw_tag))?;
        Ok(match frame_type {
            VerificationTypeTag::Top => Self::Top,
            VerificationTypeTag::Integer => Self::Integer,
            VerificationTypeTag::Float => Self::Float,
            VerificationTypeTag::Double => Self::Double,
            VerificationTypeTag::Long => Self::Long,
            VerificationTypeTag::Null => Self::Null,
            VerificationTypeTag::UninitializedThis => Self::UninitializedThis,
            VerificationTypeTag::Object => Self::Object(cursor.u16()?),
            VerificationTypeTag::Uninitialized => Self::Uninitialized(cursor.u16()?),
        })
    }
}

impl CodeAttributeInfo {
    pub fn kind(&self) -> AttributeKind {
        match self {
            Self::LineNumberTable { .. } => AttributeKind::LineNumberTable,
            Self::LocalVariableTable { .. } => AttributeKind::LocalVariableTable,
            Self::StackMapTable { .. } => AttributeKind::StackMapTable,
            Self::LocalVariableTypeTable { .. } => AttributeKind::LocalVariableTypeTable,
            Self::RuntimeVisibleTypeAnnotations { .. } => {
                AttributeKind::RuntimeVisibleTypeAnnotations
            }
            Self::RuntimeInvisibleTypeAnnotations { .. } => {
                AttributeKind::RuntimeInvisibleTypeAnnotations
            }
        }
    }
}

impl<'a> CodeAttributeInfo {
    pub(crate) fn read(
        pool: &ConstantPool,
        cursor: &mut ByteCursor<'a>,
    ) -> Result<Self, ClassFormatErr> {
        let attr_name_idx = cursor.u16()?;
        let _attribute_length = cursor.u32()? as usize;

        let attribute_kind = AttributeKind::try_from(pool.get_utf8(&attr_name_idx)?)?;
        match attribute_kind {
            AttributeKind::LineNumberTable => {
                let line_number_table_length = cursor.u16()? as usize;
                let mut line_number_table = Vec::with_capacity(line_number_table_length);
                for _ in 0..line_number_table_length {
                    line_number_table.push(LineNumberEntry {
                        start_pc: cursor.u16()?,
                        line_number: cursor.u16()?,
                    });
                }
                Ok(CodeAttributeInfo::LineNumberTable {
                    attr_name_idx,
                    table: line_number_table,
                })
            }
            AttributeKind::LocalVariableTable => {
                let local_variable_table_length = cursor.u16()?;
                let mut local_variable_table =
                    Vec::with_capacity(local_variable_table_length as usize);
                for _ in 0..local_variable_table_length {
                    local_variable_table.push(LocalVariableEntry {
                        start_pc: cursor.u16()?,
                        length: cursor.u16()?,
                        name_index: cursor.u16()?,
                        descriptor_index: cursor.u16()?,
                        index: cursor.u16()?,
                    });
                }
                Ok(CodeAttributeInfo::LocalVariableTable {
                    attr_name_idx,
                    table: local_variable_table,
                })
            }
            AttributeKind::LocalVariableTypeTable => {
                let local_variable_table_type_length = cursor.u16()?;
                let mut local_variable_type_table =
                    Vec::with_capacity(local_variable_table_type_length as usize);
                for _ in 0..local_variable_table_type_length {
                    local_variable_type_table.push(LocalVariableTypeEntry {
                        start_pc: cursor.u16()?,
                        length: cursor.u16()?,
                        name_index: cursor.u16()?,
                        signature_index: cursor.u16()?,
                        index: cursor.u16()?,
                    });
                }
                Ok(CodeAttributeInfo::LocalVariableTypeTable {
                    attr_name_idx,
                    table: local_variable_type_table,
                })
            }
            AttributeKind::StackMapTable => {
                let frames_count = cursor.u16()?;
                let mut frames = Vec::with_capacity(frames_count as usize);
                for _ in 0..frames_count {
                    frames.push(StackMapFrame::read(cursor)?)
                }
                Ok(CodeAttributeInfo::StackMapTable {
                    attr_name_idx,
                    table: frames,
                })
            }
            other => unimplemented!("{other:?}"),
        }
    }
}