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::fmt::{Display, Formatter, Debug};
|
||||||
use core::str::Utf8Error;
|
use core::str::Utf8Error;
|
||||||
|
|
||||||
|
use crate::bytecode::Bytecode;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -135,7 +136,7 @@ impl JavaClassFile {
|
||||||
|
|
||||||
methods_vec.into_boxed_slice()
|
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(
|
Ok(
|
||||||
JavaClassFile {
|
JavaClassFile {
|
||||||
|
@ -472,7 +473,7 @@ impl FieldInfo {
|
||||||
access_flags: AccessFlagMask { mask: read_u16(reader)? },
|
access_flags: AccessFlagMask { mask: read_u16(reader)? },
|
||||||
name_index: read_u16(reader)?,
|
name_index: read_u16(reader)?,
|
||||||
descriptor_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)]
|
#[derive(Debug)]
|
||||||
pub enum AttributeData {
|
pub enum AttributeData {
|
||||||
|
Code(CodeAttributeData),
|
||||||
|
Signature(SignatureAttributeData),
|
||||||
|
NestMembers(NestMembersAttributeData),
|
||||||
|
SourceFile(SourceFileAttributeData),
|
||||||
|
InnerClasses(InnerClassesAttributeData),
|
||||||
ConstantValue(ConstantValueAttributeData),
|
ConstantValue(ConstantValueAttributeData),
|
||||||
LineNumberTable(LineNumberTableAttributeData),
|
LineNumberTable(LineNumberTableAttributeData),
|
||||||
UnknownAttribute(UnknownAttributeData),
|
Unknown(UnknownAttributeData),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -544,22 +712,21 @@ pub struct AttributeInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 length = read_u16(reader)?;
|
||||||
let mut attr_vec = Vec::with_capacity(length.into());
|
let mut attr_vec = Vec::with_capacity(length.into());
|
||||||
|
|
||||||
for _i in 0..length {
|
for _i in 0..length {
|
||||||
let attribute = AttributeInfo::from_reader(reader, &pool)?;
|
let attribute = AttributeInfo::from_reader(reader, &pool, allow_code_attr)?;
|
||||||
println!("{:#?}", attribute);
|
|
||||||
attr_vec.push(attribute);
|
attr_vec.push(attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(attr_vec.into_boxed_slice())
|
Ok(attr_vec.into_boxed_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result<Self, Error> {
|
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)?;
|
let attribute_name_index: u16 = read_u16(reader)? - 1;
|
||||||
let attribute_byte_size: usize = read_u32(reader)?.try_into()?;
|
let _attribute_byte_size: usize = read_u32(reader)?.try_into()?;
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
let name_entry = &pool[attribute_name_index as usize];
|
let name_entry = &pool[attribute_name_index as usize];
|
||||||
|
@ -569,19 +736,35 @@ impl AttributeInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
match &utf8[..] {
|
match &utf8[..] {
|
||||||
// "ConstantValue" => AttributeData::ConstantValue(
|
"ConstantValue" => AttributeData::ConstantValue(
|
||||||
// ConstantValueAttributeData {
|
ConstantValueAttributeData {
|
||||||
// constant_value_index: read_u16(reader)?,
|
constant_value_index: read_u16(reader)?,
|
||||||
// }
|
}
|
||||||
// ),
|
),
|
||||||
//
|
|
||||||
// "LineNumberTable" => AttributeData::LineNumberTable(
|
|
||||||
// LineNumberTableAttributeData::from_reader(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 {
|
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)? },
|
access_flags: AccessFlagMask { mask: read_u16(reader)? },
|
||||||
name_index: read_u16(reader)?,
|
name_index: read_u16(reader)?,
|
||||||
descriptor_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;
|
use std::fs::File;
|
||||||
|
|
||||||
mod classfile;
|
mod classfile;
|
||||||
|
mod bytecode;
|
||||||
|
|
||||||
fn main() {
|
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);
|
println!("{:#?}", class_file);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue