fixed cp entry indices and added double constant

This commit is contained in:
Sander Hautvast 2023-09-26 18:31:14 +02:00
parent 6bad19c244
commit 0fbe00c157
12 changed files with 100 additions and 140 deletions

Binary file not shown.

View file

@ -1,8 +0,0 @@
public class Dummy {
private final static double answer = 42.0;
public static double get(){
return answer;
}
}

View file

@ -1,3 +1,6 @@
use std::fs::{self, File};
use std::io::Read;
pub(crate) fn read_u16(data: &[u8], pos: usize) -> u16 { pub(crate) fn read_u16(data: &[u8], pos: usize) -> u16 {
u16::from_be_bytes(data[pos..pos + 2].try_into().expect("slice with incorrect length")) u16::from_be_bytes(data[pos..pos + 2].try_into().expect("slice with incorrect length"))
} }
@ -21,3 +24,11 @@ pub(crate) fn read_i64(data: &[u8], pos: usize) -> i64 {
pub(crate) fn read_f64(data: &[u8], pos: usize) -> f64 { pub(crate) fn read_f64(data: &[u8], pos: usize) -> f64 {
f64::from_be_bytes(data[pos..pos + 8].try_into().expect("slice with incorrect length")) f64::from_be_bytes(data[pos..pos + 8].try_into().expect("slice with incorrect length"))
} }
pub fn read_class_file(name: &str) -> Vec<u8> {
let mut f = File::open(name).expect("no file found");
let metadata = fs::metadata(name).expect("unable to read metadata");
let mut buffer = vec![0; metadata.len() as usize];
let _ = f.read(&mut buffer).expect("buffer overflow");
buffer
}

View file

@ -1,5 +1,5 @@
pub mod types; pub mod types;
mod io; pub mod io;
pub mod opcodes; pub mod opcodes;
use std::collections::HashMap; use std::collections::HashMap;
@ -11,12 +11,12 @@ pub fn get_class(bytecode: Vec<u8>) -> Option<Class> {
check_magic(&bytecode); check_magic(&bytecode);
let constant_pool_count = read_u16(&bytecode, 8); let constant_pool_count = read_u16(&bytecode, 8);
// println!("cp count: {}", constant_pool_count); println!("cp count: {}", constant_pool_count);
let mut index = 10; let mut index = 10;
let mut constant_pool: HashMap<usize, CpEntry> = HashMap::with_capacity(constant_pool_count as usize); let mut constant_pool: HashMap<usize, CpEntry> = HashMap::with_capacity(constant_pool_count as usize);
let mut cp_index: usize = 1; let mut cp_index = 1;
for _ in 1..constant_pool_count - 1 while cp_index < constant_pool_count as usize {
{ println!("cp#{}", cp_index);
constant_pool.insert(cp_index, read_constant_pool_entry(&mut cp_index, &mut index, &bytecode)); constant_pool.insert(cp_index, read_constant_pool_entry(&mut cp_index, &mut index, &bytecode));
cp_index += 1; cp_index += 1;
} }
@ -28,6 +28,7 @@ pub fn get_class(bytecode: Vec<u8>) -> Option<Class> {
let super_class = read_u16(&bytecode, index + 4); let super_class = read_u16(&bytecode, index + 4);
let interfaces_count = read_u16(&bytecode, index + 6); let interfaces_count = read_u16(&bytecode, index + 6);
println!("interfaces count: {}", interfaces_count);
index += 8; index += 8;
let mut interfaces = vec![]; let mut interfaces = vec![];
for _ in 0..interfaces_count { for _ in 0..interfaces_count {
@ -84,71 +85,71 @@ fn check_magic(bytecode: &[u8]) {
fn read_constant_pool_entry(cp_index: &mut usize, index: &mut usize, bytecode: &[u8]) -> CpEntry { fn read_constant_pool_entry(cp_index: &mut usize, index: &mut usize, bytecode: &[u8]) -> CpEntry {
let tag = bytecode[*index]; let tag = bytecode[*index];
// println!("#{}: {}", cp_index, tag); println!("#{}: {}", cp_index, tag);
match tag { match tag {
1 => { 1 => {
let len = read_u16(bytecode, *index + 1) as usize; let len = read_u16(bytecode, *index + 1) as usize;
let utf: Vec<u8> = Vec::from(&bytecode[*index + 3..*index + 3 + len]); let utf: Vec<u8> = Vec::from(&bytecode[*index + 3..*index + 3 + len]);
*index += len + 3; *index += len + 3;
CpEntry::Utf8(*cp_index, String::from_utf8(utf).unwrap()) CpEntry::Utf8(String::from_utf8(utf).unwrap())
} }
3 => { 3 => {
let value = read_i32(bytecode, *index + 1); let value = read_i32(bytecode, *index + 1);
*index += 5; *index += 5;
CpEntry::Integer(*cp_index, value) CpEntry::Integer(value)
} }
4 => { 4 => {
let value = read_f32(bytecode, *index + 1); let value = read_f32(bytecode, *index + 1);
*index += 5; *index += 5;
CpEntry::Float(*cp_index, value) CpEntry::Float(value)
} }
5 => { 5 => {
let value = read_i64(bytecode, *index + 1); let value = read_i64(bytecode, *index + 1);
*index += 9; *index += 9;
let r = CpEntry::Long(*cp_index, value); let r = CpEntry::Long(value);
*cp_index += 1; *cp_index += 1;
r r
} }
6 => { 6 => {
let value = read_f64(bytecode, *index + 1); let value = read_f64(bytecode, *index + 1);
*index += 9; *index += 9;
let r = CpEntry::Double(*cp_index, value); let r = CpEntry::Double(value);
*cp_index += 1; *cp_index += 1;
r r
} }
7 => { 7 => {
let name_index = read_u16(bytecode, *index + 1); let name_index = read_u16(bytecode, *index + 1);
*index += 3; *index += 3;
CpEntry::ClassRef(*cp_index, name_index) CpEntry::ClassRef(name_index)
} }
8 => { 8 => {
let string_index = read_u16(bytecode, *index + 1); let string_index = read_u16(bytecode, *index + 1);
*index += 3; *index += 3;
CpEntry::StringRef(*cp_index, string_index) CpEntry::StringRef(string_index)
} }
9 => { 9 => {
let class_index = read_u16(bytecode, *index + 1); let class_index = read_u16(bytecode, *index + 1);
let name_and_type_index = read_u16(bytecode, *index + 3); let name_and_type_index = read_u16(bytecode, *index + 3);
*index += 5; *index += 5;
CpEntry::Fieldref(*cp_index, class_index, name_and_type_index) CpEntry::Fieldref(class_index, name_and_type_index)
} }
10 => { 10 => {
let class_index = read_u16(bytecode, *index + 1); let class_index = read_u16(bytecode, *index + 1);
let name_and_type_index = read_u16(bytecode, *index + 3); let name_and_type_index = read_u16(bytecode, *index + 3);
*index += 5; *index += 5;
CpEntry::MethodRef(*cp_index, class_index, name_and_type_index) CpEntry::MethodRef(class_index, name_and_type_index)
} }
11 => { 11 => {
let class_index = read_u16(bytecode, *index + 1); let class_index = read_u16(bytecode, *index + 1);
let name_and_type_index = read_u16(bytecode, *index + 3); let name_and_type_index = read_u16(bytecode, *index + 3);
*index += 5; *index += 5;
CpEntry::InterfaceMethodref(*cp_index, class_index, name_and_type_index) CpEntry::InterfaceMethodref(class_index, name_and_type_index)
} }
12 => { 12 => {
let name_index = read_u16(bytecode, *index + 1) as usize; let name_index = read_u16(bytecode, *index + 1) as usize;
let descriptor_index = read_u16(bytecode, *index + 3) as usize; let descriptor_index = read_u16(bytecode, *index + 3) as usize;
*index += 5; *index += 5;
CpEntry::NameAndType(*cp_index, name_index, descriptor_index) CpEntry::NameAndType(name_index, descriptor_index)
} }
// 15 MethodHandle, // 15 MethodHandle,
// 16 MethodType, // 16 MethodType,
@ -216,7 +217,7 @@ fn read_attribute(constant_pool: Rc<HashMap<usize, CpEntry>>, bytecode: &[u8], i
*index += attribute_length; *index += attribute_length;
if let CpEntry::Utf8(_, s) = &constant_pool.get(&attribute_name_index).unwrap() { if let CpEntry::Utf8(s) = &constant_pool.get(&attribute_name_index).unwrap() {
// println!("Att [{}]", s); // println!("Att [{}]", s);
return match s.as_str() { return match s.as_str() {
"ConstantValue" => { "ConstantValue" => {
@ -256,16 +257,16 @@ fn read_attribute(constant_pool: Rc<HashMap<usize, CpEntry>>, bytecode: &[u8], i
#[derive(Debug)] #[derive(Debug)]
pub enum CpEntry { pub enum CpEntry {
Utf8(usize, String), Utf8(String),
Integer(usize, i32), Integer(i32),
Float(usize, f32), Float(f32),
Long(usize, i64), Long(i64),
Double(usize, f64), Double(f64),
ClassRef(usize, u16), ClassRef(u16),
StringRef(usize, u16), StringRef(u16),
Fieldref(usize, u16, u16), Fieldref(u16, u16),
MethodRef(usize, u16, u16), MethodRef(u16, u16),
InterfaceMethodref(usize, u16, u16), InterfaceMethodref(u16, u16),
NameAndType(usize, usize, usize), NameAndType(usize, usize),
} }

View file

@ -1,20 +1,10 @@
use std::fs::{self, File};
use std::io::Read;
fn main() { fn main() {
let bytecode = read_class_file("./Dummy.class"); if let Some(class) = classfile_reader::get_class(classfile_reader::io::read_class_file("./Dummy.class")){
if let Some(class) = classfile_reader::get_class(bytecode){
println!("{:?}", class); println!("{:?}", class);
let ret = class.execute("public static get()I"); let ret = class.execute("public static get()D");
println!("{:?}", ret); println!("{:?}", ret);
} }
} }
fn read_class_file(name: &str) -> Vec<u8> {
let mut f = File::open(name).expect("no file found");
let metadata = fs::metadata(name).expect("unable to read metadata");
let mut buffer = vec![0; metadata.len() as usize];
let _ = f.read(&mut buffer).expect("buffer overflow");
buffer
}

View file

@ -6,7 +6,7 @@ pub const fconst_2: u8 = 13; // (0xd) Push float 2
pub const dconst_0:u8 = 14; // (0xe) push double 0 pub const dconst_0:u8 = 14; // (0xe) push double 0
pub const dconst_1:u8 = 15; // (0xf) push double 1 pub const dconst_1:u8 = 15; // (0xf) push double 1
pub const bipush:u8 = 16; // (0x10) Push byte pub const bipush:u8 = 16; // (0x10) Push byte
pub const ldc2_w:u8 = 20; // (0x14) Push long or double from run-time constant pool (wide index)
pub const ldc: u8 = 18; // (0x12) Push item from run-time pub constant pool pub const ldc: u8 = 18; // (0x12) Push item from run-time pub constant pool
pub const fload:u8 = 23; // (0x17) Load float from local variable pub const fload:u8 = 23; // (0x17) Load float from local variable
pub const dload:u8 = 24; // (0x18) load double from local variable pub const dload:u8 = 24; // (0x18) load double from local variable

View file

@ -1,6 +1,10 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::rc::Rc; use std::rc::Rc;
use crate::{CpEntry, opcodes}; use crate::{CpEntry, opcodes};
use crate::io::read_u16;
#[derive(Debug)] #[derive(Debug)]
//TODO create factory function //TODO create factory function
@ -54,10 +58,10 @@ impl Method {
pub fn name(&self) -> String { pub fn name(&self) -> String {
let mut full_name = get_modifier(self.access_flags); let mut full_name = get_modifier(self.access_flags);
if let CpEntry::Utf8(_, s) = &self.constant_pool.get(&self.name_index).unwrap() { if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() {
full_name.push_str(s); full_name.push_str(s);
} }
if let CpEntry::Utf8(_, s) = &self.constant_pool.get(&self.descriptor_index).unwrap() { if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() {
full_name.push_str(s); full_name.push_str(s);
} }
@ -71,19 +75,31 @@ impl Method {
let mut pc: usize = 0; let mut pc: usize = 0;
while pc < code.opcodes.len() { while pc < code.opcodes.len() {
let opcode = &code.opcodes[pc]; let opcode = &code.opcodes[pc];
pc += 1;
println!("{}", opcode);
match opcode { match opcode {
&opcodes::bipush => { &opcodes::bipush => {
pc += 1;
let c = code.opcodes[pc] as i32; let c = code.opcodes[pc] as i32;
stack.push(Value::I32(c)); stack.push(Value::I32(c));
pc += 1;
}
&opcodes::ldc2_w => {
let cp_index = read_u16(&code.opcodes, pc) as usize;
if let CpEntry::Double(d) = self.constant_pool.get(&cp_index).unwrap() {
stack.push(Value::F64(*d));
}
pc += 2;
} }
&opcodes::ireturn => { &opcodes::ireturn => {
return stack.pop(); return stack.pop();
} }
&opcodes::dreturn => {
return stack.pop();
}
//TODO implement all opcodes //TODO implement all opcodes
_ => {} _ => { panic!("opcode not implemented") }
} }
pc += 1;
} }
} }
None // TODO error situation None // TODO error situation
@ -98,10 +114,6 @@ pub struct Field {
attributes: HashMap<String, AttributeType>, attributes: HashMap<String, AttributeType>,
} }
use std::fmt;
use std::hash::Hash;
use crate::io::read_u16;
impl fmt::Debug for Field { impl fmt::Debug for Field {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Field {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}", write!(f, "Field {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}",
@ -121,11 +133,11 @@ impl Field {
pub fn name(&self) -> String { pub fn name(&self) -> String {
let mut full_name = get_modifier(self.access_flags); let mut full_name = get_modifier(self.access_flags);
if let CpEntry::Utf8(_, s) = &self.constant_pool.get(&self.descriptor_index).unwrap() { if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() {
full_name.push_str(s); full_name.push_str(s);
} }
full_name.push(' '); full_name.push(' ');
if let CpEntry::Utf8(_, s) = &self.constant_pool.get(&self.name_index).unwrap() { if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() {
full_name.push_str(s); full_name.push_str(s);
} }
@ -251,4 +263,5 @@ impl Stack {
pub enum Value { pub enum Value {
Void, Void,
I32(i32), I32(i32),
F64(f64),
} }

BIN
tests/Double.class Normal file

Binary file not shown.

8
tests/Double.java Normal file
View file

@ -0,0 +1,8 @@
public class Double {
private final static double d =42.0D;
public static double get() {
return d;
}
}

BIN
tests/Int.class Normal file

Binary file not shown.

6
tests/Int.java Normal file
View file

@ -0,0 +1,6 @@
public class Int {
public static int get() {
return 42;
}
}

View file

@ -1,88 +1,27 @@
mod test { mod test {
use std::rc::Rc; use classfile_reader::{get_class, io};
use classfile_reader::CpEntry::*; use classfile_reader::CpEntry::*;
use classfile_reader::types::{AttributeType, Class, Field, Method}; use classfile_reader::types::Value;
#[test] #[test]
fn get_version() { fn get_constant_int() {
assert_eq!((55, 0), get_class().get_version()); let class = get_class(io::read_class_file("tests/Int.class")).unwrap();
assert_eq!((55, 0), class.get_version());
if let Value::I32(v) = class.methods.get("public static get()I").unwrap().execute().unwrap() {
assert_eq!(v, 42);
} else {
panic!("fail");
}
} }
#[test] #[test]
fn get_methods() { fn get_constant_double() {
let class = get_class(); let class = get_class(io::read_class_file("tests/Double.class")).unwrap();
assert_eq!("public <init>(Ljava/lang/String;)V", &class.methods[0].name()); assert_eq!((55, 0), class.get_version());
assert_eq!("public getName()Ljava/lang/String;", &class.methods[1].name()); if let Value::F64(v) = class.methods.get("public static get()D").unwrap().execute().unwrap() {
assert_eq!("public print()V", &class.methods[2].name()); assert_eq!(v, 42.0);
} } else {
panic!("fail");
#[test]
fn get_fields() {
let class = get_class();
assert_eq!("private final Ljava/lang/String; name", &class.fields[0].name())
}
#[test]
fn get_code() {
let class = get_class();
// println!("{:?}", &class.methods[0].get_code());
// println!("{:?}", &class.methods[1].get_code());
}
fn get_class() -> Class {
let cp = Rc::new(vec![
MethodRef(1, 6, 19),
Fieldref(2, 5, 20),
Fieldref(3, 21, 22),
MethodRef(4, 23, 24),
ClassRef(5, 25),
ClassRef(6, 26),
Utf8(7, "name".to_owned()),
Utf8(8, "Ljava/lang/String;".to_owned()),
Utf8(9, "<init>".to_owned()),
Utf8(10, "(Ljava/lang/String;)V".to_owned()), //10
Utf8(11, "Code".to_owned()),
Utf8(12, "LineNumberTable".to_owned()),
Utf8(13, "getName".to_owned()),
Utf8(14, "()Ljava/lang/String;".to_owned()),
Utf8(15, "print".to_owned()),
Utf8(16, "()V".to_owned()),
Utf8(17, "SourceFile".to_owned()),
Utf8(18, "Dummy.java".to_owned()),
NameAndType(19, 9, 16),
NameAndType(20, 7, 8), //20
ClassRef(21, 27),
NameAndType(22, 28, 29),
ClassRef(23, 30),
NameAndType(24, 31, 10),
Utf8(25, "dummy/Dummy".to_owned()),
Utf8(26, "java/lang/Object".to_owned()),
Utf8(27, "java/lang/System".to_owned()),
Utf8(28, "out".to_owned()),
Utf8(29, "Ljava/io/PrintStream;".to_owned()),
Utf8(30, "java/io/PrintStream".to_owned()),
Utf8(31, "println".to_owned()),
]);
Class {
minor_version: 0,
major_version: 55,
constant_pool: cp.clone(),
access_flags: 33,
this_class: 5,
super_class: 6,
interfaces: vec![],
methods: vec![
Method::new(
cp.clone(), 1, 9, 10, vec![AttributeType::Deprecated
],
),
Method::new(
cp.clone(), 1, 13, 14, vec![AttributeType::Deprecated]),
Method::new(cp.clone(), 1, 15, 16, vec![AttributeType::Deprecated]),
],
fields: vec![Field::new(cp, 18, 7, 8, vec![])],
attributes: vec![AttributeType::SourceFile],
} }
} }
} }