More Bytecode and Attributes support
This commit is contained in:
parent
8a79a28b5f
commit
b751fc3588
3 changed files with 274 additions and 22 deletions
68
src/bytecode.rs
Normal file
68
src/bytecode.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
use core::fmt::Debug;
|
||||
use core::fmt;
|
||||
|
||||
pub struct Bytecode {
|
||||
pub code: Box<[u8]>
|
||||
}
|
||||
|
||||
impl Bytecode {
|
||||
pub fn opcodes(&self) -> Box<[Instruction]> {
|
||||
let mut v = Vec::with_capacity(self.code.len());
|
||||
|
||||
let mut i = 0;
|
||||
while i < self.code.len() {
|
||||
let opcode = self.code[i];
|
||||
|
||||
let (instruction, offset) = match opcode {
|
||||
0x12 => (Instruction::LoadConstant(self.code[i+1]), 2),
|
||||
0x2A => (Instruction::LoadReference0(), 1),
|
||||
0x2B => (Instruction::LoadReference1(), 1),
|
||||
0x2C => (Instruction::LoadReference2(), 1),
|
||||
0x2D => (Instruction::LoadReference3(), 1),
|
||||
0x59 => (Instruction::Duplicate(), 1),
|
||||
0xB0 => (Instruction::ReturnReference(), 1),
|
||||
0xB1 => (Instruction::ReturnVoid(), 1),
|
||||
0xB2 => (Instruction::GetStatic(self.code[i+1], self.code[i+2]), 3),
|
||||
0xB4 => (Instruction::GetField(self.code[i+1], self.code[i+2]), 3),
|
||||
0xB5 => (Instruction::PutField(self.code[i+1], self.code[i+2]), 3),
|
||||
0xB6 => (Instruction::InvokeVirtual(self.code[i+1], self.code[i+2]), 3),
|
||||
0xB7 => (Instruction::InvokeSpecial(self.code[i+1], self.code[i+2]), 3),
|
||||
0xBB => (Instruction::NewObject(self.code[i+1], self.code[i+2]), 3),
|
||||
_ => (Instruction::Unknown(opcode), 1)
|
||||
};
|
||||
|
||||
v.push(instruction);
|
||||
i += offset;
|
||||
}
|
||||
|
||||
v.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Bytecode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
f.debug_list()
|
||||
.entries(self.opcodes())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum Instruction {
|
||||
LoadConstant(u8) = 0x12, // Push from constant pool
|
||||
LoadReference0() = 0x2A, // Load local variable reference onto stack
|
||||
LoadReference1() = 0x2B, // Load local variable reference onto stack
|
||||
LoadReference2() = 0x2C, // Load local variable reference onto stack
|
||||
LoadReference3() = 0x2D, // Load local variable reference onto stack
|
||||
Duplicate() = 0x59, // duplicate top stack value
|
||||
ReturnReference() = 0xB0, // return top-ref from current function
|
||||
ReturnVoid() = 0xB1, // return void from function
|
||||
GetStatic(u8, u8) = 0xB2, // get static field from class
|
||||
GetField(u8, u8) = 0xB4, // get field from class
|
||||
PutField(u8, u8) = 0xB5, // set field to a value
|
||||
InvokeVirtual(u8, u8) = 0xB6, // invoke function on a class
|
||||
InvokeSpecial(u8, u8) = 0xB7, // invoke instance method
|
||||
NewObject(u8, u8) = 0xBB, // Create a new object from a constant-pool reference
|
||||
Unknown(u8),
|
||||
}
|
225
src/classfile.rs
225
src/classfile.rs
|
@ -3,6 +3,7 @@ use std::error::Error as ErrorTrait;
|
|||
use core::fmt::{Display, Formatter, Debug};
|
||||
use core::str::Utf8Error;
|
||||
|
||||
use crate::bytecode::Bytecode;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
|
@ -135,7 +136,7 @@ impl JavaClassFile {
|
|||
|
||||
methods_vec.into_boxed_slice()
|
||||
};
|
||||
let attributes = AttributeInfo::array_from_reader(reader, &constant_pool)?;
|
||||
let attributes = AttributeInfo::array_from_reader(reader, &constant_pool, true)?;
|
||||
|
||||
Ok(
|
||||
JavaClassFile {
|
||||
|
@ -472,7 +473,7 @@ impl FieldInfo {
|
|||
access_flags: AccessFlagMask { mask: read_u16(reader)? },
|
||||
name_index: read_u16(reader)?,
|
||||
descriptor_index: read_u16(reader)?,
|
||||
attributes: AttributeInfo::array_from_reader(reader, pool)?,
|
||||
attributes: AttributeInfo::array_from_reader(reader, pool, true)?,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -530,11 +531,178 @@ impl LineNumberTableAttributeData {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExceptionTableEntry {
|
||||
start_pc: u16,
|
||||
end_pc: u16,
|
||||
handler_pc: u16,
|
||||
catch_type: u16,
|
||||
}
|
||||
|
||||
impl ExceptionTableEntry {
|
||||
fn from_reader(reader: &mut dyn Read) -> Result<Self, Error> {
|
||||
Ok(
|
||||
ExceptionTableEntry {
|
||||
start_pc: read_u16(reader)?,
|
||||
end_pc: read_u16(reader)?,
|
||||
handler_pc: read_u16(reader)?,
|
||||
catch_type: read_u16(reader)?,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CodeAttributeData {
|
||||
max_stack: u16,
|
||||
max_locals: u16,
|
||||
code: Bytecode,
|
||||
exception_table: Box<[ExceptionTableEntry]>,
|
||||
attributes: Box<[AttributeInfo]>,
|
||||
}
|
||||
|
||||
impl CodeAttributeData {
|
||||
fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result<Self, Error> {
|
||||
let max_stack = read_u16(reader)?;
|
||||
let max_locals = read_u16(reader)?;
|
||||
|
||||
let code_length = read_u32(reader)?;
|
||||
let code = read_buffer(reader, code_length.try_into()?)?;
|
||||
|
||||
let exception_length = read_u16(reader)?;
|
||||
let exception_table = {
|
||||
let mut v = Vec::with_capacity(exception_length.into());
|
||||
|
||||
for _i in 0..exception_length {
|
||||
v.push(ExceptionTableEntry::from_reader(reader)?);
|
||||
}
|
||||
|
||||
v.into_boxed_slice()
|
||||
};
|
||||
|
||||
let attributes = AttributeInfo::array_from_reader(reader, pool, false)?;
|
||||
|
||||
Ok(
|
||||
CodeAttributeData {
|
||||
max_stack,
|
||||
max_locals,
|
||||
code: Bytecode { code },
|
||||
exception_table,
|
||||
attributes
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SourceFileAttributeData {
|
||||
source_file_index: u16,
|
||||
}
|
||||
|
||||
impl SourceFileAttributeData {
|
||||
fn from_reader(reader: &mut dyn Read) -> Result<Self, Error> {
|
||||
Ok(
|
||||
SourceFileAttributeData {
|
||||
source_file_index: read_u16(reader)?,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SignatureAttributeData {
|
||||
signature_index: u16,
|
||||
}
|
||||
|
||||
impl SignatureAttributeData {
|
||||
fn from_reader(reader: &mut dyn Read) -> Result<Self, Error> {
|
||||
Ok(
|
||||
SignatureAttributeData {
|
||||
signature_index: read_u16(reader)?,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InnerClassesAttributeEntry {
|
||||
inner_class_info_index: u16,
|
||||
outer_class_info_index: u16,
|
||||
inner_name_index: u16,
|
||||
outer_name_index: u16,
|
||||
}
|
||||
|
||||
impl InnerClassesAttributeEntry {
|
||||
fn from_reader(reader: &mut dyn Read) -> Result<Self, Error> {
|
||||
Ok(
|
||||
InnerClassesAttributeEntry {
|
||||
inner_class_info_index: read_u16(reader)?,
|
||||
outer_class_info_index: read_u16(reader)?,
|
||||
inner_name_index: read_u16(reader)?,
|
||||
outer_name_index: read_u16(reader)?,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InnerClassesAttributeData {
|
||||
classes: Box<[InnerClassesAttributeEntry]>
|
||||
}
|
||||
|
||||
impl InnerClassesAttributeData {
|
||||
fn from_reader(reader: &mut dyn Read) -> Result<Self, Error> {
|
||||
Ok(
|
||||
InnerClassesAttributeData {
|
||||
classes: {
|
||||
let length = read_u16(reader)?;
|
||||
let mut v = Vec::with_capacity(length.into());
|
||||
|
||||
for _i in 0..length {
|
||||
v.push(InnerClassesAttributeEntry::from_reader(reader)?);
|
||||
}
|
||||
|
||||
v.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NestMembersAttributeData {
|
||||
class_indices: Box<[u16]>
|
||||
}
|
||||
|
||||
impl NestMembersAttributeData {
|
||||
fn from_reader(reader: &mut dyn Read) -> Result<Self, Error> {
|
||||
Ok(
|
||||
NestMembersAttributeData {
|
||||
class_indices: {
|
||||
let length = read_u16(reader)?;
|
||||
let mut v = Vec::with_capacity(length.into());
|
||||
|
||||
for _i in 0..length {
|
||||
v.push(read_u16(reader)?);
|
||||
}
|
||||
|
||||
v.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AttributeData {
|
||||
Code(CodeAttributeData),
|
||||
Signature(SignatureAttributeData),
|
||||
NestMembers(NestMembersAttributeData),
|
||||
SourceFile(SourceFileAttributeData),
|
||||
InnerClasses(InnerClassesAttributeData),
|
||||
ConstantValue(ConstantValueAttributeData),
|
||||
LineNumberTable(LineNumberTableAttributeData),
|
||||
UnknownAttribute(UnknownAttributeData),
|
||||
Unknown(UnknownAttributeData),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -544,22 +712,21 @@ pub struct AttributeInfo {
|
|||
}
|
||||
|
||||
impl AttributeInfo {
|
||||
fn array_from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result<Box<[Self]>, Error> {
|
||||
fn array_from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>, allow_code_attr: bool) -> Result<Box<[Self]>, Error> {
|
||||
let length = read_u16(reader)?;
|
||||
let mut attr_vec = Vec::with_capacity(length.into());
|
||||
|
||||
for _i in 0..length {
|
||||
let attribute = AttributeInfo::from_reader(reader, &pool)?;
|
||||
println!("{:#?}", attribute);
|
||||
let attribute = AttributeInfo::from_reader(reader, &pool, allow_code_attr)?;
|
||||
attr_vec.push(attribute);
|
||||
}
|
||||
|
||||
Ok(attr_vec.into_boxed_slice())
|
||||
}
|
||||
|
||||
fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result<Self, Error> {
|
||||
let attribute_name_index: u16 = read_u16(reader)?;
|
||||
let attribute_byte_size: usize = read_u32(reader)?.try_into()?;
|
||||
fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>, allow_code_attr: bool) -> Result<Self, Error> {
|
||||
let attribute_name_index: u16 = read_u16(reader)? - 1;
|
||||
let _attribute_byte_size: usize = read_u32(reader)?.try_into()?;
|
||||
|
||||
let data = {
|
||||
let name_entry = &pool[attribute_name_index as usize];
|
||||
|
@ -569,19 +736,35 @@ impl AttributeInfo {
|
|||
};
|
||||
|
||||
match &utf8[..] {
|
||||
// "ConstantValue" => AttributeData::ConstantValue(
|
||||
// ConstantValueAttributeData {
|
||||
// constant_value_index: read_u16(reader)?,
|
||||
// }
|
||||
// ),
|
||||
//
|
||||
// "LineNumberTable" => AttributeData::LineNumberTable(
|
||||
// LineNumberTableAttributeData::from_reader(reader)?
|
||||
// ),
|
||||
"ConstantValue" => AttributeData::ConstantValue(
|
||||
ConstantValueAttributeData {
|
||||
constant_value_index: read_u16(reader)?,
|
||||
}
|
||||
),
|
||||
|
||||
&_ => AttributeData::UnknownAttribute(
|
||||
"LineNumberTable" => AttributeData::LineNumberTable(
|
||||
LineNumberTableAttributeData::from_reader(reader)?
|
||||
),
|
||||
|
||||
"Code" => if allow_code_attr {
|
||||
AttributeData::Code(
|
||||
CodeAttributeData::from_reader(reader, pool)?
|
||||
)
|
||||
} else {
|
||||
return Err(Error::BadFileError("Nested Code attributes are forbidden.".to_string()));
|
||||
},
|
||||
|
||||
"SourceFile" => AttributeData::SourceFile(SourceFileAttributeData::from_reader(reader)?),
|
||||
|
||||
"Signature" => AttributeData::Signature(SignatureAttributeData::from_reader(reader)?),
|
||||
|
||||
"InnerClasses" => AttributeData::InnerClasses(InnerClassesAttributeData::from_reader(reader)?),
|
||||
|
||||
"NestMembers" => AttributeData::NestMembers(NestMembersAttributeData::from_reader(reader)?),
|
||||
|
||||
&_ => AttributeData::Unknown(
|
||||
UnknownAttributeData {
|
||||
info: read_buffer(reader, attribute_byte_size)?,
|
||||
info: read_buffer(reader, _attribute_byte_size)?,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -611,7 +794,7 @@ impl MethodInfo {
|
|||
access_flags: AccessFlagMask { mask: read_u16(reader)? },
|
||||
name_index: read_u16(reader)?,
|
||||
descriptor_index: read_u16(reader)?,
|
||||
attributes: AttributeInfo::array_from_reader(reader, pool)?
|
||||
attributes: AttributeInfo::array_from_reader(reader, pool, true)?
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::fs::File;
|
||||
|
||||
mod classfile;
|
||||
mod bytecode;
|
||||
|
||||
fn main() {
|
||||
let class_file = classfile::JavaClassFile::new(&mut File::open("Main.class").unwrap()).unwrap();
|
||||
let class_file = classfile::JavaClassFile::new(&mut File::open("class/HashMap.class").unwrap()).unwrap();
|
||||
|
||||
println!("{:#?}", class_file);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue