Skip to content

Commit fefca09

Browse files
authored
Merge pull request #37 from emilycares/feature/LocalVariableTable
Implement LocalVariableTable
2 parents c1523dc + 7edb9bc commit fefca09

File tree

8 files changed

+127
-3
lines changed

8 files changed

+127
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,6 @@ fn main() {
108108
- [x] SourceFile
109109
- [ ] SourceDebugExtension
110110
- [ ] LineNumberTable
111-
- [ ] LocalVariableTable
111+
- [x] LocalVariableTable
112112
- [ ] LocalVariableTypeTable
113113
- [ ] Deprecated

java-assets/compile.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ javac -d java-assets/compiled-classes/ java-assets/src/Instructions.java
2626
javac -d java-assets/compiled-classes/ java-assets/src/UnicodeStrings.java
2727
javac -d java-assets/compiled-classes/ java-assets/src/DeprecatedAnnotation.java
2828

29+
javac -g -d java-assets/compiled-classes/ java-assets/src/LocalVariableTable.java
2930
javac -d java-assets/compiled-classes/ java-assets/src/HelloWorld.java
3031
printf '\xde\xad\xbe\xef' > java-assets/compiled-classes/malformed.class
3132
tail -c+5 java-assets/compiled-classes/HelloWorld.class >> java-assets/compiled-classes/malformed.class
Binary file not shown.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import java.util.*;
2+
public class LocalVariableTable {
3+
public void hereIsCode() {
4+
HashMap<Integer, String> a = new HashMap<>();
5+
a.put(1, "");
6+
int number = 0;
7+
}
8+
}

src/code_attribute/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ pub use self::types::*;
55

66
pub use self::parser::code_parser;
77
pub use self::parser::instruction_parser;
8+
pub use self::parser::local_variable_table_parser;

src/code_attribute/parser.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ use crate::code_attribute::types::Instruction;
22
use nom::{
33
bytes::complete::{tag, take},
44
combinator::{complete, fail, map, success},
5+
error::Error,
56
multi::{count, many0},
67
number::complete::{be_i16, be_i32, be_i8, be_u16, be_u32, be_u8},
78
sequence::{pair, preceded, tuple},
8-
IResult, Offset,
9+
Err as BaseErr, IResult, Offset,
910
};
1011

12+
use super::{LocalVariableTableAttribute, LocalVariableTableItem};
13+
type Err<E> = BaseErr<Error<E>>;
14+
1115
fn offset<'a>(remaining: &'a [u8], input: &[u8]) -> IResult<&'a [u8], usize> {
1216
Ok((remaining, input.offset(remaining)))
1317
}
@@ -290,3 +294,40 @@ pub fn instruction_parser(input: &[u8], address: usize) -> IResult<&[u8], Instru
290294
};
291295
Ok((input, instruction))
292296
}
297+
298+
pub fn local_variable_table_parser(
299+
input: &[u8],
300+
) -> Result<(&[u8], LocalVariableTableAttribute), Err<&[u8]>> {
301+
let (input, local_variable_table_length) = be_u16(input)?;
302+
let (input, items) = count(
303+
variable_table_item_parser,
304+
local_variable_table_length as usize,
305+
)(input)?;
306+
Ok((
307+
input,
308+
LocalVariableTableAttribute {
309+
local_variable_table_length,
310+
items,
311+
},
312+
))
313+
}
314+
315+
pub fn variable_table_item_parser(
316+
input: &[u8],
317+
) -> Result<(&[u8], LocalVariableTableItem), Err<&[u8]>> {
318+
let (input, start_pc) = be_u16(input)?;
319+
let (input, length) = be_u16(input)?;
320+
let (input, name_index) = be_u16(input)?;
321+
let (input, descriptor_index) = be_u16(input)?;
322+
let (input, index) = be_u16(input)?;
323+
Ok((
324+
input,
325+
LocalVariableTableItem {
326+
start_pc,
327+
length,
328+
name_index,
329+
descriptor_index,
330+
index,
331+
},
332+
))
333+
}

src/code_attribute/types.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,18 @@ pub enum Instruction {
234234
offsets: Vec<i32>,
235235
},
236236
}
237+
238+
#[derive(Clone, Debug)]
239+
pub struct LocalVariableTableAttribute {
240+
pub local_variable_table_length: u16,
241+
pub items: Vec<LocalVariableTableItem>,
242+
}
243+
244+
#[derive(Clone, Debug)]
245+
pub struct LocalVariableTableItem {
246+
pub start_pc: u16,
247+
pub length: u16,
248+
pub name_index: u16,
249+
pub descriptor_index: u16,
250+
pub index: u16,
251+
}

tests/code_attribute.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ extern crate classfile_parser;
22

33
use classfile_parser::attribute_info::{code_attribute_parser, method_parameters_attribute_parser};
44
use classfile_parser::class_parser;
5-
use classfile_parser::code_attribute::{code_parser, instruction_parser, Instruction};
5+
use classfile_parser::code_attribute::{
6+
code_parser, instruction_parser, Instruction, LocalVariableTableAttribute,
7+
};
68
use classfile_parser::method_info::MethodAccessFlags;
79

810
#[test]
@@ -115,3 +117,59 @@ fn method_parameters() {
115117
Some("b".to_string())
116118
);
117119
}
120+
121+
#[test]
122+
fn local_variable_table() {
123+
// The class was not compiled with "javac -g"
124+
let class_bytes = include_bytes!("../java-assets/compiled-classes/LocalVariableTable.class");
125+
let (_, class) = class_parser(class_bytes).unwrap();
126+
let method_info = &class.methods.iter().last().unwrap();
127+
128+
let code_attribute = method_info
129+
.attributes
130+
.iter()
131+
.find_map(|attribute_info| {
132+
match lookup_string(&class, attribute_info.attribute_name_index)?.as_str() {
133+
"Code" => {
134+
classfile_parser::attribute_info::code_attribute_parser(&attribute_info.info)
135+
.ok()
136+
}
137+
_ => None,
138+
}
139+
})
140+
.map(|i| i.1)
141+
.unwrap();
142+
143+
let local_variable_table_attribute: LocalVariableTableAttribute = code_attribute
144+
.attributes
145+
.iter()
146+
.find_map(|attribute_info| {
147+
match lookup_string(&class, attribute_info.attribute_name_index)?.as_str() {
148+
"LocalVariableTable" => {
149+
classfile_parser::code_attribute::local_variable_table_parser(
150+
&attribute_info.info,
151+
)
152+
.ok()
153+
}
154+
_ => None,
155+
}
156+
})
157+
.map(|a| a.1)
158+
.unwrap();
159+
160+
let types: Vec<String> = local_variable_table_attribute
161+
.items
162+
.iter()
163+
.filter_map(|i| lookup_string(&class, i.descriptor_index))
164+
.collect();
165+
166+
// All used types in method code block of last method
167+
assert_eq!(
168+
types,
169+
vec![
170+
"LLocalVariableTable;".to_string(),
171+
"Ljava/util/HashMap;".to_string(),
172+
"I".to_string()
173+
]
174+
);
175+
}

0 commit comments

Comments
 (0)