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 {
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 {
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;
mod io;
pub mod io;
pub mod opcodes;
use std::collections::HashMap;
@ -11,12 +11,12 @@ pub fn get_class(bytecode: Vec<u8>) -> Option<Class> {
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<usize, CpEntry> = 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<u8>) -> Option<Class> {
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<u8> = 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<HashMap<usize, CpEntry>>, 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<HashMap<usize, CpEntry>>, 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),
}

View file

@ -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<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_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

View file

@ -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();
}
//TODO implement all opcodes
_ => {}
&opcodes::dreturn => {
return stack.pop();
}
pc += 1;
//TODO implement all opcodes
_ => { panic!("opcode not implemented") }
}
}
}
None // TODO error situation
@ -98,10 +114,6 @@ pub struct Field {
attributes: HashMap<String, AttributeType>,
}
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),
}

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 {
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 <init>(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, "<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],
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");
}
}
}