use crate::ClassFormatErr;
use crate::attribute::AttributeKind;
use crate::constant_pool::ConstantPool;
use lvm_common::utils::cursor::ByteCursor;
use num_enum::TryFromPrimitive;
#[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,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LineNumberEntry {
pub start_pc: u16,
pub line_number: u16,
}
#[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,
}
#[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,
}
#[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)) .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<'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:?}"),
}
}
}