Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,6 @@ fn main() {
- [x] SourceFile
- [ ] SourceDebugExtension
- [ ] LineNumberTable
- [ ] LocalVariableTable
- [x] LocalVariableTable
- [ ] LocalVariableTypeTable
- [ ] Deprecated
1 change: 1 addition & 0 deletions java-assets/compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ javac -d java-assets/compiled-classes/ java-assets/src/Instructions.java
javac -d java-assets/compiled-classes/ java-assets/src/UnicodeStrings.java
javac -d java-assets/compiled-classes/ java-assets/src/DeprecatedAnnotation.java

javac -g -d java-assets/compiled-classes/ java-assets/src/LocalVariableTable.java
javac -d java-assets/compiled-classes/ java-assets/src/HelloWorld.java
printf '\xde\xad\xbe\xef' > java-assets/compiled-classes/malformed.class
tail -c+5 java-assets/compiled-classes/HelloWorld.class >> java-assets/compiled-classes/malformed.class
Expand Down
Binary file not shown.
8 changes: 8 additions & 0 deletions java-assets/src/LocalVariableTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import java.util.*;
public class LocalVariableTable {
public void hereIsCode() {
HashMap<Integer, String> a = new HashMap<>();
a.put(1, "");
int number = 0;
}
}
1 change: 1 addition & 0 deletions src/code_attribute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub use self::types::*;

pub use self::parser::code_parser;
pub use self::parser::instruction_parser;
pub use self::parser::local_variable_table_parser;
43 changes: 42 additions & 1 deletion src/code_attribute/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ use crate::code_attribute::types::Instruction;
use nom::{
bytes::complete::{tag, take},
combinator::{complete, fail, map, success},
error::Error,
multi::{count, many0},
number::complete::{be_i16, be_i32, be_i8, be_u16, be_u32, be_u8},
sequence::{pair, preceded, tuple},
IResult, Offset,
Err as BaseErr, IResult, Offset,
};

use super::{LocalVariableTableAttribute, LocalVariableTableItem};
type Err<E> = BaseErr<Error<E>>;

fn offset<'a>(remaining: &'a [u8], input: &[u8]) -> IResult<&'a [u8], usize> {
Ok((remaining, input.offset(remaining)))
}
Expand Down Expand Up @@ -290,3 +294,40 @@ pub fn instruction_parser(input: &[u8], address: usize) -> IResult<&[u8], Instru
};
Ok((input, instruction))
}

pub fn local_variable_table_parser(
input: &[u8],
) -> Result<(&[u8], LocalVariableTableAttribute), Err<&[u8]>> {
let (input, local_variable_table_length) = be_u16(input)?;
let (input, items) = count(
variable_table_item_parser,
local_variable_table_length as usize,
)(input)?;
Ok((
input,
LocalVariableTableAttribute {
local_variable_table_length,
items,
},
))
}

pub fn variable_table_item_parser(
input: &[u8],
) -> Result<(&[u8], LocalVariableTableItem), Err<&[u8]>> {
let (input, start_pc) = be_u16(input)?;
let (input, length) = be_u16(input)?;
let (input, name_index) = be_u16(input)?;
let (input, descriptor_index) = be_u16(input)?;
let (input, index) = be_u16(input)?;
Ok((
input,
LocalVariableTableItem {
start_pc,
length,
name_index,
descriptor_index,
index,
},
))
}
15 changes: 15 additions & 0 deletions src/code_attribute/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,18 @@ pub enum Instruction {
offsets: Vec<i32>,
},
}

#[derive(Clone, Debug)]
pub struct LocalVariableTableAttribute {
pub local_variable_table_length: u16,
pub items: Vec<LocalVariableTableItem>,
}

#[derive(Clone, Debug)]
pub struct LocalVariableTableItem {
pub start_pc: u16,
pub length: u16,
pub name_index: u16,
pub descriptor_index: u16,
pub index: u16,
}
60 changes: 59 additions & 1 deletion tests/code_attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ extern crate classfile_parser;

use classfile_parser::attribute_info::{code_attribute_parser, method_parameters_attribute_parser};
use classfile_parser::class_parser;
use classfile_parser::code_attribute::{code_parser, instruction_parser, Instruction};
use classfile_parser::code_attribute::{
code_parser, instruction_parser, Instruction, LocalVariableTableAttribute,
};
use classfile_parser::method_info::MethodAccessFlags;

#[test]
Expand Down Expand Up @@ -115,3 +117,59 @@ fn method_parameters() {
Some("b".to_string())
);
}

#[test]
fn local_variable_table() {
// The class was not compiled with "javac -g"
let class_bytes = include_bytes!("../java-assets/compiled-classes/LocalVariableTable.class");
let (_, class) = class_parser(class_bytes).unwrap();
let method_info = &class.methods.iter().last().unwrap();

let code_attribute = method_info
.attributes
.iter()
.find_map(|attribute_info| {
match lookup_string(&class, attribute_info.attribute_name_index)?.as_str() {
"Code" => {
classfile_parser::attribute_info::code_attribute_parser(&attribute_info.info)
.ok()
}
_ => None,
}
})
.map(|i| i.1)
.unwrap();

let local_variable_table_attribute: LocalVariableTableAttribute = code_attribute
.attributes
.iter()
.find_map(|attribute_info| {
match lookup_string(&class, attribute_info.attribute_name_index)?.as_str() {
"LocalVariableTable" => {
classfile_parser::code_attribute::local_variable_table_parser(
&attribute_info.info,
)
.ok()
}
_ => None,
}
})
.map(|a| a.1)
.unwrap();

let types: Vec<String> = local_variable_table_attribute
.items
.iter()
.filter_map(|i| lookup_string(&class, i.descriptor_index))
.collect();

// All used types in method code block of last method
assert_eq!(
types,
vec![
"LLocalVariableTable;".to_string(),
"Ljava/util/HashMap;".to_string(),
"I".to_string()
]
);
}