From 0fbe00c15738c373dca5397df8ae2bc01f429312 Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Tue, 26 Sep 2023 18:31:14 +0200 Subject: [PATCH] fixed cp entry indices and added double constant --- Dummy.class | Bin 292 -> 0 bytes Dummy.java | 8 ---- src/io.rs | 11 +++++ src/lib.rs | 59 ++++++++++++++------------- src/main.rs | 16 ++------ src/opcodes.rs | 2 +- src/types.rs | 35 +++++++++++----- tests/Double.class | Bin 0 -> 289 bytes tests/Double.java | 8 ++++ tests/Int.class | Bin 0 -> 233 bytes tests/Int.java | 6 +++ tests/class_tests.rs | 95 ++++++++----------------------------------- 12 files changed, 100 insertions(+), 140 deletions(-) delete mode 100644 Dummy.class delete mode 100644 Dummy.java create mode 100644 tests/Double.class create mode 100644 tests/Double.java create mode 100644 tests/Int.class create mode 100644 tests/Int.java diff --git a/Dummy.class b/Dummy.class deleted file mode 100644 index 8294f5ee2680ac8d3f78bcbf1595ef4ce0f4eaba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 292 zcmYL@%}T>S6ot=CnzY8&{;ot9?$m`{x)Bljvnsl%bU&#>Nl7Ma}V>~b1wJy`27Rm5rYT;x^?uzmk9x@j=msNX0z3WK*oaBC^s9cj9sX-)Eqt~ zCb7=~|MqSnsEzUu()KJd`nAkrUA(I}CGl6 u16 { u16::from_be_bytes(data[pos..pos + 2].try_into().expect("slice with incorrect length")) } @@ -20,4 +23,12 @@ pub(crate) fn read_i64(data: &[u8], pos: usize) -> i64 { 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")) +} + +pub fn read_class_file(name: &str) -> Vec { + 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 } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 057222b..1cabfc5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ pub mod types; -mod io; +pub mod io; pub mod opcodes; use std::collections::HashMap; @@ -11,12 +11,12 @@ pub fn get_class(bytecode: Vec) -> Option { check_magic(&bytecode); 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 constant_pool: HashMap = HashMap::with_capacity(constant_pool_count as usize); - let mut cp_index: usize = 1; - for _ in 1..constant_pool_count - 1 - { + let mut cp_index = 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)); cp_index += 1; } @@ -28,6 +28,7 @@ pub fn get_class(bytecode: Vec) -> Option { let super_class = read_u16(&bytecode, index + 4); let interfaces_count = read_u16(&bytecode, index + 6); + println!("interfaces count: {}", interfaces_count); index += 8; let mut interfaces = vec![]; 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 { let tag = bytecode[*index]; - // println!("#{}: {}", cp_index, tag); + println!("#{}: {}", cp_index, tag); match tag { 1 => { let len = read_u16(bytecode, *index + 1) as usize; let utf: Vec = Vec::from(&bytecode[*index + 3..*index + 3 + len]); *index += len + 3; - CpEntry::Utf8(*cp_index, String::from_utf8(utf).unwrap()) + CpEntry::Utf8(String::from_utf8(utf).unwrap()) } 3 => { let value = read_i32(bytecode, *index + 1); *index += 5; - CpEntry::Integer(*cp_index, value) + CpEntry::Integer(value) } 4 => { let value = read_f32(bytecode, *index + 1); *index += 5; - CpEntry::Float(*cp_index, value) + CpEntry::Float(value) } 5 => { let value = read_i64(bytecode, *index + 1); *index += 9; - let r = CpEntry::Long(*cp_index, value); + let r = CpEntry::Long(value); *cp_index += 1; r } 6 => { let value = read_f64(bytecode, *index + 1); *index += 9; - let r = CpEntry::Double(*cp_index, value); + let r = CpEntry::Double(value); *cp_index += 1; r } 7 => { let name_index = read_u16(bytecode, *index + 1); *index += 3; - CpEntry::ClassRef(*cp_index, name_index) + CpEntry::ClassRef(name_index) } 8 => { let string_index = read_u16(bytecode, *index + 1); *index += 3; - CpEntry::StringRef(*cp_index, string_index) + CpEntry::StringRef(string_index) } 9 => { let class_index = read_u16(bytecode, *index + 1); let name_and_type_index = read_u16(bytecode, *index + 3); *index += 5; - CpEntry::Fieldref(*cp_index, class_index, name_and_type_index) + CpEntry::Fieldref(class_index, name_and_type_index) } 10 => { let class_index = read_u16(bytecode, *index + 1); let name_and_type_index = read_u16(bytecode, *index + 3); *index += 5; - CpEntry::MethodRef(*cp_index, class_index, name_and_type_index) + CpEntry::MethodRef(class_index, name_and_type_index) } 11 => { let class_index = read_u16(bytecode, *index + 1); let name_and_type_index = read_u16(bytecode, *index + 3); *index += 5; - CpEntry::InterfaceMethodref(*cp_index, class_index, name_and_type_index) + CpEntry::InterfaceMethodref(class_index, name_and_type_index) } 12 => { let name_index = read_u16(bytecode, *index + 1) as usize; let descriptor_index = read_u16(bytecode, *index + 3) as usize; *index += 5; - CpEntry::NameAndType(*cp_index, name_index, descriptor_index) + CpEntry::NameAndType(name_index, descriptor_index) } // 15 MethodHandle, // 16 MethodType, @@ -216,7 +217,7 @@ fn read_attribute(constant_pool: Rc>, bytecode: &[u8], i *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); return match s.as_str() { "ConstantValue" => { @@ -256,16 +257,16 @@ fn read_attribute(constant_pool: Rc>, bytecode: &[u8], i #[derive(Debug)] pub enum CpEntry { - Utf8(usize, String), - Integer(usize, i32), - Float(usize, f32), - Long(usize, i64), - Double(usize, f64), - ClassRef(usize, u16), - StringRef(usize, u16), - Fieldref(usize, u16, u16), - MethodRef(usize, u16, u16), - InterfaceMethodref(usize, u16, u16), - NameAndType(usize, usize, usize), + Utf8(String), + Integer(i32), + Float(f32), + Long(i64), + Double(f64), + ClassRef(u16), + StringRef(u16), + Fieldref(u16, u16), + MethodRef(u16, u16), + InterfaceMethodref(u16, u16), + NameAndType(usize, usize), } diff --git a/src/main.rs b/src/main.rs index cef4a18..98be196 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,10 @@ -use std::fs::{self, File}; -use std::io::Read; - fn main() { - let bytecode = read_class_file("./Dummy.class"); - if let Some(class) = classfile_reader::get_class(bytecode){ + if let Some(class) = classfile_reader::get_class(classfile_reader::io::read_class_file("./Dummy.class")){ println!("{:?}", class); - let ret = class.execute("public static get()I"); + let ret = class.execute("public static get()D"); println!("{:?}", ret); } } -fn read_class_file(name: &str) -> Vec { - 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 -} + diff --git a/src/opcodes.rs b/src/opcodes.rs index 1b332aa..99fe366 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -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_1:u8 = 15; // (0xf) push double 1 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 fload:u8 = 23; // (0x17) Load float from local variable pub const dload:u8 = 24; // (0x18) load double from local variable diff --git a/src/types.rs b/src/types.rs index 525a27a..85094b3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,10 @@ use std::collections::HashMap; +use std::fmt; +use std::hash::Hash; use std::rc::Rc; + use crate::{CpEntry, opcodes}; +use crate::io::read_u16; #[derive(Debug)] //TODO create factory function @@ -54,10 +58,10 @@ impl Method { pub fn name(&self) -> String { 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); } - 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); } @@ -71,19 +75,31 @@ impl Method { let mut pc: usize = 0; while pc < code.opcodes.len() { let opcode = &code.opcodes[pc]; + pc += 1; + println!("{}", opcode); match opcode { &opcodes::bipush => { - pc += 1; let c = code.opcodes[pc] as i32; 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 => { return stack.pop(); } + &opcodes::dreturn => { + return stack.pop(); + } //TODO implement all opcodes - _ => {} + _ => { panic!("opcode not implemented") } } - pc += 1; + } } None // TODO error situation @@ -98,10 +114,6 @@ pub struct Field { attributes: HashMap, } -use std::fmt; -use std::hash::Hash; -use crate::io::read_u16; - impl fmt::Debug for Field { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Field {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}", @@ -121,11 +133,11 @@ impl Field { pub fn name(&self) -> String { 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(' '); - 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); } @@ -251,4 +263,5 @@ impl Stack { pub enum Value { Void, I32(i32), + F64(f64), } \ No newline at end of file diff --git a/tests/Double.class b/tests/Double.class new file mode 100644 index 0000000000000000000000000000000000000000..3ec3dce8014a5f55eb8f986757ba487a9f01c166 GIT binary patch literal 289 zcmYL@KWhR(5XIlz>1odY#wsL*ok^jkjRfH|RY-~i`#oLcAx-5s5IAtV3L@`J`4Q8 z<3do1vK49TH8J`v|BChYQ^hHXzt*-eaYy)(<=drxN!&+mmgRH|H)^NqxIxIBXC!Dl zI7*dSkKW^rURw0w@jO6qhk%y}s(g9y+2YciHIfIaP|Ht0kV6rua|Q);#s-=f>pi2c RNeU zIdI>f=L^6Bt_>BAiGhhB!B}QxCRYS?Iy(`xb#)d5cbk=B*IZ&zAEUTnqNYN2p^#u7 zs-{lFCTk5VEag0pu8{)`I!9O&yp{w-RHnf`&P5_Ifx>^=Qow(p^E6nEc@uBle}L|M d5KNv%2MIk`+`s6kV}R=Ux4)pqmd#8-{|ib7A(H?A literal 0 HcmV?d00001 diff --git a/tests/Int.java b/tests/Int.java new file mode 100644 index 0000000..5b40836 --- /dev/null +++ b/tests/Int.java @@ -0,0 +1,6 @@ +public class Int { + + public static int get() { + return 42; + } +} diff --git a/tests/class_tests.rs b/tests/class_tests.rs index c4935e4..5b8a3d7 100644 --- a/tests/class_tests.rs +++ b/tests/class_tests.rs @@ -1,88 +1,27 @@ mod test { - use std::rc::Rc; + use classfile_reader::{get_class, io}; use classfile_reader::CpEntry::*; - use classfile_reader::types::{AttributeType, Class, Field, Method}; + use classfile_reader::types::Value; #[test] - fn get_version() { - assert_eq!((55, 0), get_class().get_version()); + fn get_constant_int() { + 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] - fn get_methods() { - let class = get_class(); - assert_eq!("public (Ljava/lang/String;)V", &class.methods[0].name()); - assert_eq!("public getName()Ljava/lang/String;", &class.methods[1].name()); - assert_eq!("public print()V", &class.methods[2].name()); - } - - #[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, "".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], + fn get_constant_double() { + let class = get_class(io::read_class_file("tests/Double.class")).unwrap(); + assert_eq!((55, 0), class.get_version()); + if let Value::F64(v) = class.methods.get("public static get()D").unwrap().execute().unwrap() { + assert_eq!(v, 42.0); + } else { + panic!("fail"); } } } \ No newline at end of file