tests and fixes

This commit is contained in:
nora 2021-08-19 21:49:36 +02:00
parent ef797d6fa2
commit 5d435a208c
10 changed files with 345 additions and 135 deletions

3
.gitignore vendored
View file

@ -3,5 +3,4 @@
/target
*.class
*.java
testing

View file

@ -1 +1,2 @@
mod model;
#[allow(dead_code)]
mod model;

View file

@ -3,6 +3,6 @@ use crate::parse::{ClassFile, ParseErr};
mod execute;
mod parse;
pub fn parse_class_file(file: Vec<u8>) -> Result<ClassFile, ParseErr> {
pub fn parse_class_file(file: &[u8]) -> Result<ClassFile, ParseErr> {
parse::parse_class_file(file)
}

View file

@ -4,7 +4,7 @@ fn main() {
let file = "Test.class";
let file = std::fs::read(file).unwrap();
let class_file = parse_class_file(file).unwrap();
let class_file = parse_class_file(&file).unwrap();
println!("{:?}", class_file);
}

View file

@ -1,29 +1,26 @@
mod model;
#[cfg(test)]
mod test;
pub use model::*;
use std::path::Prefix::Verbatim;
#[derive(Debug)]
pub struct ParseErr(String);
pub type Result<T> = std::result::Result<T, ParseErr>;
struct Data {
data: Vec<u1>,
#[derive(Copy, Clone)]
struct Data<'a> {
data: &'a [u1],
pointer: usize,
}
pub fn parse_class_file(data: Vec<u1>) -> Result<ClassFile> {
let mut data = Data { data, pointer: 0 };
ClassFile::parse(&mut data)
pub fn parse_class_file(data: &[u1]) -> Result<ClassFile> {
let data = Data { data, pointer: 0 };
ClassFile::parse(data)
}
impl From<Vec<u8>> for Data {
fn from(data: Vec<u1>) -> Self {
Self { data, pointer: 0 }
}
}
impl Data {
impl<'a> Data<'a> {
fn u1(&mut self) -> Result<u1> {
let item = self.data.get(self.pointer).cloned();
self.pointer += 1;
@ -69,65 +66,55 @@ impl Data {
}
}
pub trait Parse {
fn parse(data: &mut Data) -> Result<Self>
trait Parse {
fn parse(data: Data) -> Result<Self>
where
Self: Sized;
}
pub trait ParseVec<T> {
fn parse_vec(data: &mut Data, len: usize) -> Result<Self>
where
Self: Sized;
}
impl<T: Parse> ParseVec<T> for Vec<T> {
fn parse_vec(data: &mut Data, len: usize) -> Result<Self> {
let mut vec = Vec::with_capacity(len);
for _ in 0..len {
vec.push(T::parse(data)?);
}
Ok(vec)
fn parse_vec<T: Parse, S: Into<usize>>(data: Data, len: S) -> Result<Vec<T>> {
let len = len.into();
let mut vec = Vec::with_capacity(len);
for _ in 0..len {
vec.push(T::parse(data)?);
}
Ok(vec)
}
macro_rules! parse_primitive {
($value:ident) => {
impl Parse for $value {
fn parse(data: &mut Data) -> Result<Self>
($($value:ident),*) => {
$(impl Parse for $value {
fn parse(mut data: Data) -> Result<Self>
where
Self: Sized,
{
data.$value()
}
}
})*
};
}
parse_primitive!(u1);
parse_primitive!(u2);
parse_primitive!(u4);
parse_primitive!(u1, u2, u4);
impl Parse for ClassFile {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
let magic = data.u4()?;
assert_eq!(magic, 0xCAFEBABE);
let minor_version = data.u2()?;
let major_version = data.u2()?;
let constant_pool_count = data.u2()?;
let constant_pool = Vec::parse_vec(data, constant_pool_count as usize)?;
let constant_pool = parse_vec(data, constant_pool_count - 1)?; // the minus one is important
let access_flags = data.u2()?;
let this_class = data.u2()?;
let super_class = data.u2()?;
let interfaces_count = data.u2()?;
let interfaces = Vec::parse_vec(data, interfaces_count as usize)?;
let interfaces = parse_vec(data, interfaces_count)?;
let fields_count = data.u2()?;
let fields = Vec::parse_vec(data, fields_count as usize)?;
let fields = parse_vec(data, fields_count)?;
let method_count = data.u2()?;
let methods = Vec::parse_vec(data, method_count as usize)?;
let methods = parse_vec(data, method_count)?;
let attributes_count = data.u2()?;
let attributes = Vec::parse_vec(data, attributes_count as usize)?;
println!("los");
let attributes = parse_vec(data, attributes_count)?;
Ok(Self {
magic,
minor_version,
@ -150,7 +137,7 @@ impl Parse for ClassFile {
}
impl Parse for CpInfo {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
let tag = data.u1()?;
Ok(match tag {
@ -163,7 +150,7 @@ impl Parse for CpInfo {
class_index: data.u2()?,
name_and_type_index: data.u2()?,
},
10 => Self::Methodref {
10 => Self::MethodRef {
tag,
class_index: data.u2()?,
name_and_type_index: data.u2()?,
@ -203,7 +190,7 @@ impl Parse for CpInfo {
1 => Self::Utf8 {
tag,
length: data.u2()?,
bytes: Vec::parse_vec(data, data.last_u2()? as usize)?,
bytes: parse_vec(data, data.last_u2()?)?,
},
15 => Self::MethodHandle {
tag,
@ -225,41 +212,41 @@ impl Parse for CpInfo {
}
impl Parse for FieldInfo {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
access_flags: data.u2()?,
name_index: data.u2()?,
descriptor_index: data.u2()?,
attributes_count: data.u2()?,
attributes: Vec::parse_vec(data, data.last_u2()? as usize)?,
attributes: parse_vec(data, data.last_u2()?)?,
})
}
}
impl Parse for MethodInfo {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
access_flags: data.u2()?,
name_index: data.u2()?,
descriptor_index: data.u2()?,
attributes_count: data.u2()?,
attributes: Vec::parse_vec(data, data.last_u2()? as usize)?,
attributes: parse_vec(data, data.last_u2()?)?,
})
}
}
impl Parse for Attribute {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
attribute_name_index: data.u2()?,
attribute_length: data.u4()?,
attribute_content: Vec::parse_vec(data, data.last_u4()? as usize)?,
attribute_content: parse_vec(data, data.last_u4()? as usize)?,
})
}
}
impl Parse for AttributeCodeException {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(data: Data) -> Result<Self> {
Ok(Self {
start_pc: data.last_u2()?,
end_pc: data.last_u2()?,
@ -270,7 +257,7 @@ impl Parse for AttributeCodeException {
}
impl Parse for StackMapFrame {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
let frame_type = data.u1()?;
Ok(match frame_type {
@ -295,15 +282,15 @@ impl Parse for StackMapFrame {
252..=254 => Self::AppendFrame {
frame_type,
offset_delta: data.u2()?,
locals: Vec::parse_vec(data, data.last_u2()? as usize)?,
locals: parse_vec(data, data.last_u2()?)?,
},
255 => Self::FullFrame {
frame_type,
offset_delta: data.u2()?,
number_of_locals: data.u2()?,
locals: Vec::parse_vec(data, data.last_u2()? as usize)?,
locals: parse_vec(data, data.last_u2()?)?,
number_of_stack_items: data.u2()?,
stack: Vec::parse_vec(data, data.last_u2()? as usize)?,
stack: parse_vec(data, data.last_u2()?)?,
},
_ => Err(ParseErr(format!(
"Invalid StackMapFrame type: {}",
@ -314,7 +301,7 @@ impl Parse for StackMapFrame {
}
impl Parse for VerificationTypeInfo {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
let tag = data.u1()?;
Ok(match tag {
0 => Self::Top { tag },
@ -341,7 +328,7 @@ impl Parse for VerificationTypeInfo {
}
impl Parse for AttributeInnerClass {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
inner_class_info_index: data.u2()?,
outer_class_info_index: data.u2()?,
@ -352,7 +339,7 @@ impl Parse for AttributeInnerClass {
}
impl Parse for AttributeLineNumber {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
start_pc: data.u2()?,
line_number: data.u2()?,
@ -361,7 +348,7 @@ impl Parse for AttributeLineNumber {
}
impl Parse for AttributeLocalVariableTable {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
start_pc: data.u2()?,
length: data.u2()?,
@ -373,17 +360,17 @@ impl Parse for AttributeLocalVariableTable {
}
impl Parse for Annotation {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
type_index: data.u2()?,
num_element_value_pairs: data.u2()?,
element_value_pairs: Vec::parse_vec(data, data.last_u2()? as usize)?,
element_value_pairs: parse_vec(data, data.last_u2()?)?,
})
}
}
impl Parse for AnnotationElementValuePair {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
element_name_index: data.u2()?,
element_name_name: AnnotationElementValue::parse(data)?,
@ -392,7 +379,7 @@ impl Parse for AnnotationElementValuePair {
}
impl Parse for AnnotationElementValue {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
tag: data.u1()?,
value: AnnotationElementValueValue::parse(data)?,
@ -401,7 +388,7 @@ impl Parse for AnnotationElementValue {
}
impl Parse for AnnotationElementValueValue {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
let tag = data.last_u1()? as char;
Ok(match tag {
'B' | 'C' | 'D' | 'F' | 'I' | 'J' | 'S' | 'Z' | 's' => {
@ -417,7 +404,7 @@ impl Parse for AnnotationElementValueValue {
},
'[' => Self::ArrayValue {
num_values: data.u2()?,
values: Vec::parse_vec(data, data.last_u2()? as usize)?,
values: parse_vec(data, data.last_u2()?)?,
},
_ => Err(ParseErr(format!(
"Invalid AnnotationElementValueValue tag: {}",
@ -428,57 +415,20 @@ impl Parse for AnnotationElementValueValue {
}
impl Parse for ParameterAnnotation {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
num_annotations: data.u2()?,
annotations: Vec::parse_vec(data, data.last_u2()? as usize)?,
annotations: parse_vec(data, data.last_u2()?)?,
})
}
}
impl Parse for BootstrapMethod {
fn parse(data: &mut Data) -> Result<Self> {
fn parse(mut data: Data) -> Result<Self> {
Ok(Self {
bootstrap_method_ref: data.u2()?,
num_bootstrap_arguments: data.u2()?,
bootstrap_arguments: Vec::parse_vec(data, data.last_u2()? as usize)?,
bootstrap_arguments: parse_vec(data, data.last_u2()?)?,
})
}
}
#[cfg(test)]
mod test {
use crate::parse::Data;
#[test]
fn data_u1() {
let mut data = Data {
data: vec![0xff, 0x00],
pointer: 0,
};
assert_eq!(data.u1().unwrap(), 0xff);
assert_eq!(data.u1().unwrap(), 0x00);
assert_eq!(data.last_u1().unwrap(), 0x00);
}
#[test]
fn data_u2() {
let mut data = Data {
data: vec![0xff, 0x33, 0x11, 0x00],
pointer: 0,
};
assert_eq!(data.u2().unwrap(), 0xff33);
assert_eq!(data.u2().unwrap(), 0x1100);
assert_eq!(data.last_u2().unwrap(), 0x1100);
}
#[test]
fn data_u4() {
let mut data = Data {
data: vec![0xff, 0x33, 0x11, 0x00],
pointer: 0,
};
assert_eq!(data.u4().unwrap(), 0xff331100);
assert_eq!(data.last_u4().unwrap(), 0xff331100);
}
}

View file

@ -2,6 +2,7 @@
//! The models for a .class file
//!
//! [The .class specs](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html)
#![allow(dead_code)]
// The types used in the specs
#[allow(non_camel_case_types)]
@ -14,7 +15,7 @@ pub type u4 = u32;
///
/// # Represents a .class file
///
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct ClassFile {
/// Magic number identifying the format (= 0xCAFEBABE)
pub magic: u4,
@ -53,7 +54,7 @@ pub struct ClassFile {
/// A constant from the constant pool
/// May have indices back to the constant pool, with expected types
/// _index: A valid index into the `constant_pool` table.
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum CpInfo {
Class {
tag: u1, // 7
@ -67,7 +68,7 @@ pub enum CpInfo {
/// Entry must be `NameAndType`
name_and_type_index: u2,
},
Methodref {
MethodRef {
tag: u1, // 10
/// Must be a class type
class_index: u2,
@ -150,7 +151,7 @@ pub enum CpInfo {
}
/// Information about a field
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct FieldInfo {
pub access_flags: u2,
pub name_index: u2,
@ -160,7 +161,7 @@ pub struct FieldInfo {
}
/// Information about a method
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct MethodInfo {
/// Mask of `MethodAccessFlag` used to denote access permissions
pub access_flags: u2,
@ -175,7 +176,7 @@ pub struct MethodInfo {
}
/// See `AttributeInfo`
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Attribute {
pub attribute_name_index: u2,
pub attribute_length: u4,
@ -188,7 +189,7 @@ pub struct Attribute {
/// `attribute_length`: The length of the subsequent bytes, does not include the first 6
///
/// _index: Index to the `constant_pool` table of any type
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
#[allow(dead_code)] // todo yeah lol
pub enum AttributeInfo {
/// Only on fields, the constant value of that field
@ -350,7 +351,7 @@ pub enum AttributeInfo {
}
/// An exception handler in the JVM bytecode array
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct AttributeCodeException {
/// The ranges in the code in which the handler is active. Must be a valid index into the code array.
/// The `start_pc` is inclusive
@ -367,7 +368,7 @@ pub struct AttributeCodeException {
/// Specifies the type state at a particular bytecode offset
/// Has a offset_delta, the offset is calculated by adding offset_delta + 1 to the previous offset
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum StackMapFrame {
/// Exactly the same locals as the previous frame and zero stack items, offset_delta is frame_type
SameFrame {
@ -413,7 +414,7 @@ pub enum StackMapFrame {
}
/// A stack value/local variable type `StackMapFrame`
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum VerificationTypeInfo {
Top {
tag: u1, // 0
@ -448,7 +449,7 @@ pub enum VerificationTypeInfo {
}
/// A struct for the `AttributeInfo::InnerClasses`
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct AttributeInnerClass {
/// Must be a `Class`
pub inner_class_info_index: u2,
@ -461,7 +462,7 @@ pub struct AttributeInnerClass {
}
/// Line number information for `AttributeInfo::LineNumberTable`
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct AttributeLineNumber {
/// Index into the code array where a new line in the source begins
pub start_pc: u2,
@ -470,7 +471,7 @@ pub struct AttributeLineNumber {
}
/// Local variable information for `AttributeInfo::LocalVariableTable` and `AttributeInfo::LocalVariableTypeTable`
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct AttributeLocalVariableTable {
/// The local variable must have a value between `start_pc` and `start_pc + length`. Must be a valid opcode
pub start_pc: u2,
@ -485,7 +486,7 @@ pub struct AttributeLocalVariableTable {
}
/// A runtime-visible annotation to the program
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Annotation {
/// Must be `Utf8`
pub type_index: u2,
@ -496,7 +497,7 @@ pub struct Annotation {
// these type names have just become java at this point. no shame.
/// A element-value pair in the `Annotation`
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct AnnotationElementValuePair {
/// Must be `Utf8`
pub element_name_index: u2,
@ -504,7 +505,7 @@ pub struct AnnotationElementValuePair {
}
/// The value of an `AnnotationElementValuePair`
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct AnnotationElementValue {
/// B, C, D, F, I, J, S, Z or s, e, c, @,
pub tag: u1,
@ -512,7 +513,7 @@ pub struct AnnotationElementValue {
}
/// The value of a `AnnotationElementValue`
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum AnnotationElementValueValue {
/// If the tag is B, C, D, F, I, J, S, Z, or s.
ConstValueIndex {
@ -544,14 +545,14 @@ pub enum AnnotationElementValueValue {
}
/// Used in `AttributeInfo::RuntimeVisibleParameterAnnotations`
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct ParameterAnnotation {
pub num_annotations: u2,
pub annotations: Vec<Annotation>,
}
/// Used in `AttributeInfo::BootstrapMethods `
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct BootstrapMethod {
/// Must be a `MethodHandle`
pub bootstrap_method_ref: u2,
@ -564,7 +565,7 @@ pub struct BootstrapMethod {
/// Access Flags of a class
#[repr(u16)]
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum ClassAccessFlag {
/// Declared public; may be accessed from outside its package.
Public = 0x0001,
@ -586,7 +587,7 @@ pub enum ClassAccessFlag {
/// Access Flags of a method
#[repr(u16)]
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum MethodAccessFlag {
// Declared public; may be accessed from outside its package.
PUBLIC = 0x0001,
@ -616,7 +617,7 @@ pub enum MethodAccessFlag {
/// Access flags for an inner class
#[repr(u16)]
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum InnerClassAccessFlags {
/// Marked or implicitly public in source.
PUBLIC = 0x0001,
@ -642,7 +643,7 @@ pub enum InnerClassAccessFlags {
/// Access flags for a field
#[repr(u16)]
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum FieldAccessFlags {
/// Declared public; may be accessed from outside its package.
PUBLIC = 0x0001,

125
src/parse/test.rs Normal file
View file

@ -0,0 +1,125 @@
use super::*;
#[test]
fn data_u1() {
let bytes = [0xff, 0x00];
let mut data = Data {
data: &bytes,
pointer: 0,
};
assert_eq!(data.u1().unwrap(), 0xff);
assert_eq!(data.u1().unwrap(), 0x00);
assert_eq!(data.last_u1().unwrap(), 0x00);
}
#[test]
fn data_u2() {
let bytes = [0xff, 0x33, 0x11, 0x00];
let mut data = Data {
data: &bytes,
pointer: 0,
};
assert_eq!(data.u2().unwrap(), 0xff33);
assert_eq!(data.u2().unwrap(), 0x1100);
assert_eq!(data.last_u2().unwrap(), 0x1100);
}
#[test]
fn data_u4() {
let bytes = [0xff, 0x33, 0x11, 0x00];
let mut data = Data {
data: &bytes,
pointer: 0,
};
assert_eq!(data.u4().unwrap(), 0xff331100);
assert_eq!(data.last_u4().unwrap(), 0xff331100);
}
#[test]
fn parse_empty_class() {
let class = include_bytes!("../../testdata/Test.class");
println!("Starting test...");
let parsed = parse_class_file(class).unwrap();
assert_eq!(parsed.minor_version, 0);
assert_eq!(parsed.major_version, 0x003b);
assert_eq!(parsed.constant_pool_count, 0x000d);
assert_eq!(parsed.constant_pool.len(), 12);
assert_eq!(
parsed.constant_pool,
vec![
CpInfo::MethodRef {
tag: 0x0a,
class_index: 2,
name_and_type_index: 3
},
CpInfo::Class {
tag: 7,
name_index: 4
},
CpInfo::NameAndType {
tag: 0xc,
name_index: 5,
descriptor_index: 6
},
CpInfo::Utf8 {
tag: 1,
length: 0x10,
bytes: "java/lang/Object".bytes().collect()
},
CpInfo::Utf8 {
tag: 1,
length: 6,
bytes: "init".bytes().collect()
},
CpInfo::Utf8 {
tag: 1,
length: 3,
bytes: "()V".bytes().collect()
},
CpInfo::Class {
tag: 7,
name_index: 8
},
CpInfo::Utf8 {
tag: 1,
length: 4,
bytes: "Test".bytes().collect()
},
CpInfo::Utf8 {
tag: 1,
length: 4,
bytes: "Code".bytes().collect()
},
CpInfo::Utf8 {
tag: 1,
length: 15,
bytes: "LineNumberTable".bytes().collect()
},
CpInfo::Utf8 {
tag: 1,
length: 10,
bytes: "SourceFile".bytes().collect()
},
CpInfo::Utf8 {
tag: 1,
length: 9,
bytes: "Test.java".bytes().collect()
}
]
);
assert_eq!(parsed.access_flags, 0x0021);
assert_eq!(parsed.this_class, 7);
assert_eq!(parsed.super_class, 2);
assert_eq!(parsed.interfaces_count, 0);
assert_eq!(parsed.interfaces, vec![]);
assert_eq!(parsed.fields_count, 0);
assert_eq!(parsed.fields, vec![]);
assert_eq!(parsed.method_count, 1);
assert_eq!(parsed.methods[0].access_flags, 1);
assert_eq!(parsed.methods[0].name_index, 5);
assert_eq!(parsed.methods[0].descriptor_index, 6);
assert_eq!(parsed.methods[0].attributes_count, 1);
assert_eq!(parsed.methods[0].attributes[0].attribute_name_index, 9);
assert_eq!(parsed.methods[0].attributes[0].attribute_length, 0x1d);
}

BIN
testdata/Test.class vendored Normal file

Binary file not shown.

133
testdata/Test.class.txt vendored Normal file
View file

@ -0,0 +1,133 @@
Manually parsed by hand
hexdump -C Test.class
00000000 |ca fe ba be|00 00|00 3b |00 0d|0a.00 02.00 03|07. |.......;........|
00000010 00 04|0c.00 05.00 06|01 .00 10.6a 61 76 61 2f 6c |..........java/l|
00000020 61 6e 67 2f 4f 62 6a 65 63 74|01.00 06.3c 69 6e |ang/Object...<in|
00000030 69 74 3e|01.00 03.28 29 56|07.00 08|01.00 04.54 |it>...()V......T|
00000040 65 73 74|01.00 04.43 6f 64 65|01.00 0f.4c 69 6e |est...Code...Lin|
00000050 65 4e 75 6d 62 65 72 54 61 62 6c 65|01.00 0a.53 |eNumberTable...S|
00000060 6f 75 72 63 65 46 69 6c 65|01.00 09.54 65 73 74 |ourceFile...Test|
00000070 2e 6a 61 76 61|00 21|00 07|00 02|00 00|00 00|00 |.java.!.........|
00000080 01|00 01.00 05.00 06.00 01:00 09.00 00 00 1d.00 |................|
00000090 01.00 01.00 00 00 05.2a b7 00 01 b1.00 00.00 01: |.......*........|
000000a0 00 0a.00 00 00 06.00 01 :00 00.00 01|00 01|00 0b. |................|
000000b0 00 00 00 02.00 0c |......|
000000b6
Magic: ca fe ba be
Minor: 00 00
Major: 00 3b
CpCount: 00 0d (13) (13 - 1 = 12)
Cp: [
1: {
tag: 0a (10, MethodRef)
class_index: 00 02 (2)
name_and_type_index: 00 03 (2)
}
2: {
tag: 07 (7, Class)
name_index: 00 04 (4) (java/lang/Object)
}
3: {
tag: 0c (12, NameAndType)
name_index: 00 05 (05)
descriptor_index: 00 06 (6)
}
4: {
tag: 01 (1, Utf8)
length: 00 10 (16)
string: 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 (java/lang/Object)
}
5: {
tag: 01 (1, Utf8)
length: 00 06 (6)
string: 3c 69 6e 69 74 3e (<init>)
}
6: {
tag: 01 (1, Utf8)
length: 00 03 (3)
bytes: 28 29 56 (()V)
}
7: {
tag: 07 (7, Class)
name_index: 00 08 (8) (Test)
}
8: {
tag: 01 (1, Utf8)
length: 00 04 (4)
bytes: 54 65 73 74 (Test)
}
9: {
tag: 01 (1, Utf8)
length: 00 04 (4)
bytes: 43 6f 64 65 (Code)
}
10: {
tag: 01 (1, Utf8)
length: 00 0f (15)
bytes: 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 (LineNumberTable)
}
11: {
tag: 01 (1, Utf8)
length: 00 0a (10)
bytes: 53 6f 75 72 63 65 46 69 6c 65 (SourceFile)
}
12: {
tag: 01 (1, Utf8)
length: 00 09 (9)
bytes: 54 65 73 74 2e 6a 61 76 61 (Test.java)
}
]
access_flags: 00 21
this_class: 00 07 (Test)
super_class: 00 02 (java/lang/Object)
interfaces_count: 00 00
interfaces: []
fields_count: 00 00
fields: []
methods_count: 00 01
methods: [
{
access_flags: 00 01
name_index: 00 05
descriptor_index: 00 06
attributes_count: 00 01
attributes: [
{
name_index: 00 09 (Code)
attribute_length: 00 00 00 1d (29)
max_stack: 00 01
max_locals: 00 01
code_length: 00 00 00 05
code: 2a b7 00 01 b1
exception_table_length: 00 00
exception_table: []
attributes_count: 00 01
attributes: [
{
attribute_name_index: 00 0a (LineNumberTable)
attribute_length: 00 00 00 06
line_number_table_length: 00 01
line_number_table: [
{
start_pc: 00 00
line_number: 00 01
}
]
}
]
}
]
}
]
attributes_count: 00 01
attributes: [
{
attribute_name_index: 00 0b (SourceFile)
attribute_length: 00 00 00 02
sourcefile_index: 00 0c
}
]

1
testdata/Test.java vendored Normal file
View file

@ -0,0 +1 @@
public class Test {}