fixed cp entry indices and added double constant
This commit is contained in:
parent
6bad19c244
commit
0fbe00c157
12 changed files with 100 additions and 140 deletions
BIN
Dummy.class
BIN
Dummy.class
Binary file not shown.
|
|
@ -1,8 +0,0 @@
|
|||
public class Dummy {
|
||||
|
||||
private final static double answer = 42.0;
|
||||
|
||||
public static double get(){
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
11
src/io.rs
11
src/io.rs
|
|
@ -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
|
||||
}
|
||||
59
src/lib.rs
59
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<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),
|
||||
}
|
||||
|
||||
|
|
|
|||
16
src/main.rs
16
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<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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
35
src/types.rs
35
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<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
BIN
tests/Double.class
Normal file
Binary file not shown.
8
tests/Double.java
Normal file
8
tests/Double.java
Normal 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
BIN
tests/Int.class
Normal file
Binary file not shown.
6
tests/Int.java
Normal file
6
tests/Int.java
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
public class Int {
|
||||
|
||||
public static int get() {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue