Skip to content

Commit c7e8f55

Browse files
committed
Add support for constant values in the constant tool
note that the serialization fails because some bytecode instructions are not yet recognized
1 parent 207468c commit c7e8f55

File tree

8 files changed

+206
-11
lines changed

8 files changed

+206
-11
lines changed

classes/ConstantValues.class

513 Bytes
Binary file not shown.

classes/ConstantValues.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// constant values represented in constant pool as values
2+
3+
class ConstantValues {
4+
int integer_constant = 65535;
5+
float float_constant = 42.0f;
6+
long long_constant = 42L;
7+
double double_constant = -1;
8+
String string_constant = "fourty two";
9+
}

src/constant_pool.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
1+
use util::FloatBuffer;
2+
13
#[derive(Debug)]
24
#[derive(Eq)]
35
#[derive(PartialEq)]
46
pub enum ConstantPoolEntry {
57
ConstantUtf8 {
68
string: String,
79
},
10+
ConstantInteger {
11+
val: i32,
12+
},
13+
ConstantFloat {
14+
val: FloatBuffer<[u8; 4]>,
15+
},
16+
ConstantLong {
17+
val: i64,
18+
},
19+
ConstantDouble {
20+
val: FloatBuffer<[u8; 8]>,
21+
},
822
ConstantClass {
923
name_index: u16,
1024
},
@@ -23,4 +37,6 @@ pub enum ConstantPoolEntry {
2337
name_index: u16,
2438
descriptor_index: u16,
2539
},
40+
// represents an empty slot in the constant pool table
41+
ConstantEmptySlot { },
2642
}

src/main.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use jvm_class_file_parser::{
1313
Method
1414
};
1515

16-
const CONSTURCTOR_NAME: &str = "<init>";
16+
const CONSTRUCTOR_NAME: &str = "<init>";
1717

1818
fn main() {
1919
let args: Vec<String> = env::args().collect();
@@ -140,6 +140,36 @@ fn format_constant_pool_entry(
140140
class_file.get_constant_utf8(string_index as usize)
141141
)
142142
},
143+
ConstantInteger { ref val } => {
144+
format!(
145+
"{:<20}{:<16}",
146+
"Integer",
147+
format!("={}", val)
148+
)
149+
},
150+
ConstantFloat { ref val } => {
151+
let as_f32: f32 = val.into();
152+
format!(
153+
"{:<20}{:<16}",
154+
"Float",
155+
format!("={}", as_f32)
156+
)
157+
},
158+
ConstantLong { val } => {
159+
format!(
160+
"{:<20}{:<16}",
161+
"Long",
162+
format!("={}", val)
163+
)
164+
},
165+
ConstantDouble { ref val } => {
166+
let as_f64: f64 = val.into();
167+
format!(
168+
"{:<20}{:<16}",
169+
"Double",
170+
format!("={}", as_f64)
171+
)
172+
},
143173
ConstantFieldref { class_index, name_and_type_index } => {
144174
format!(
145175
"{:<20}{:<16}// {}",
@@ -180,6 +210,9 @@ fn format_constant_pool_entry(
180210
)
181211
)
182212
},
213+
ConstantEmptySlot {} => {
214+
"<empty slot>".to_string()
215+
},
183216
}
184217
}
185218

@@ -188,7 +221,7 @@ fn print_method(class_file: &ClassFile, method: &Method) {
188221

189222
println!(
190223
" {}();",
191-
if method_name == CONSTURCTOR_NAME { class_file.get_class_name() }
224+
if method_name == CONSTRUCTOR_NAME { class_file.get_class_name() }
192225
else { method_name }
193226
);
194227

@@ -223,7 +256,7 @@ fn print_method(class_file: &ClassFile, method: &Method) {
223256
}
224257
}
225258

226-
fn print_bytecode(class_file: &ClassFile, code: &[(usize, Bytecode)]) {
259+
fn print_bytecode(_class_file: &ClassFile, code: &[(usize, Bytecode)]) {
227260
for (i, bytecode) in code {
228261
print!(
229262
" {:>3}: {:35}",

src/parsing.rs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ use constant_pool::*;
99
use field::*;
1010
use field_access::*;
1111
use method::*;
12-
use util::{Contextable, promote_result_to_io};
12+
use util::{Contextable, promote_result_to_io, FloatBuffer};
13+
use std::ops::Deref;
1314

1415
const EXPECTED_MAGIC: u32 = 0xCAFE_BABE;
1516

1617
const CONSTANT_TAG_UTF8: u8 = 1;
18+
const CONSTANT_TAG_INTEGER: u8 = 3;
19+
const CONSTANT_TAG_FLOAT: u8 = 4;
20+
const CONSTANT_TAG_LONG: u8 = 5;
21+
const CONSTANT_TAG_DOUBLE: u8 = 6;
1722
const CONSTANT_TAG_CLASS: u8 = 7;
1823
const CONSTANT_TAG_STRING: u8 = 8;
1924
const CONSTANT_TAG_FIELDREF: u8 = 9;
@@ -106,14 +111,31 @@ fn read_n_bytes<R: Read>(file: &mut R, length: usize) -> io::Result<Vec<u8>> {
106111

107112
#[allow(clippy::vec_box)]
108113
fn read_constant_pool<R: Read>(file: &mut R) -> io::Result<Vec<Box<ConstantPoolEntry>>> {
109-
let constant_pool_count = i32::from(read_u16(file)?);
114+
let mut constant_pool_count = read_u16(file)? - 1;
110115

111-
let mut constant_pool = Vec::<Box<ConstantPoolEntry>>::new();
116+
let mut constant_pool = Vec::<Box<ConstantPoolEntry>>::with_capacity(constant_pool_count as usize);
112117

113-
for _ in 0..(constant_pool_count - 1) {
118+
while constant_pool_count > 0 {
114119
let entry = read_constant_pool_entry(file)?;
115120

116121
constant_pool.push(entry);
122+
123+
// from https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.4.5
124+
// All 8-byte constants take up two entries in the constant_pool table of the class file.
125+
// If a CONSTANT_Long_info or CONSTANT_Double_info structure is the entry at index n in the
126+
// constant_pool table, then the next usable entry in the table is located at index n+2. The
127+
// constant_pool index n+1 must be valid but is considered unusable.
128+
// (we could abstract this slightly by placing the structure constraint in the enum)
129+
match constant_pool.get(constant_pool.len() - 1).unwrap().deref() {
130+
ConstantPoolEntry::ConstantLong { val: _ } | ConstantPoolEntry::ConstantDouble { val: _ } => {
131+
// we need this ensure proper indexes of all other entries
132+
constant_pool.push(Box::new(ConstantPoolEntry::ConstantEmptySlot {}));
133+
constant_pool_count = constant_pool_count - 1
134+
},
135+
_ => {},
136+
}
137+
138+
constant_pool_count = constant_pool_count - 1
117139
}
118140

119141
Ok(constant_pool)
@@ -129,6 +151,14 @@ fn read_constant_pool_entry<R: Read>(file: &mut R) -> io::Result<Box<ConstantPoo
129151
Box::new(read_constant_class(file)?),
130152
CONSTANT_TAG_STRING =>
131153
Box::new(read_constant_string(file)?),
154+
CONSTANT_TAG_INTEGER =>
155+
Box::new(read_constant_integer(file)?),
156+
CONSTANT_TAG_FLOAT =>
157+
Box::new(read_constant_float(file)?),
158+
CONSTANT_TAG_LONG =>
159+
Box::new(read_constant_long(file)?),
160+
CONSTANT_TAG_DOUBLE =>
161+
Box::new(read_constant_double(file)?),
132162
CONSTANT_TAG_FIELDREF =>
133163
Box::new(read_constant_fieldref(file)?),
134164
CONSTANT_TAG_METHODREF =>
@@ -154,6 +184,42 @@ fn read_constant_utf8<R: Read>(file: &mut R) -> io::Result<ConstantPoolEntry> {
154184
})
155185
}
156186

187+
fn read_constant_integer<R: Read>(file: &mut R) -> io::Result<ConstantPoolEntry> {
188+
let mut buffer = [0; 4];
189+
file.read_exact(&mut buffer)?;
190+
191+
Ok(ConstantPoolEntry::ConstantInteger {
192+
val: i32::from_be_bytes(buffer)
193+
})
194+
}
195+
196+
fn read_constant_float<R: Read>(file: &mut R) -> io::Result<ConstantPoolEntry> {
197+
let mut buffer = [0; 4];
198+
file.read_exact(&mut buffer)?;
199+
200+
Ok(ConstantPoolEntry::ConstantFloat {
201+
val: FloatBuffer { buf: buffer }
202+
})
203+
}
204+
205+
fn read_constant_long<R: Read>(file: &mut R) -> io::Result<ConstantPoolEntry> {
206+
let mut buffer = [0; 8];
207+
file.read_exact(&mut buffer)?;
208+
209+
Ok(ConstantPoolEntry::ConstantLong {
210+
val: i64::from_be_bytes(buffer)
211+
})
212+
}
213+
214+
fn read_constant_double<R: Read>(file: &mut R) -> io::Result<ConstantPoolEntry> {
215+
let mut buffer = [0; 8];
216+
file.read_exact(&mut buffer)?;
217+
218+
Ok(ConstantPoolEntry::ConstantDouble {
219+
val: FloatBuffer { buf: buffer }
220+
})
221+
}
222+
157223
fn read_constant_class<R: Read>(file: &mut R) -> io::Result<ConstantPoolEntry> {
158224
let name_index = read_u16(file)?;
159225

src/util.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,27 @@ impl<A> Contextable for Result<A, io::Error> {
3232
}
3333
}
3434

35+
/// Wrapper around a buffer representing a float or double. We can't use `f32`
36+
/// directly because it doesn't implement `Eq` which we depend on in containing structs.
37+
#[derive(Debug)]
38+
#[derive(PartialEq)]
39+
#[derive(Eq)]
40+
pub struct FloatBuffer<B: Eq> {
41+
pub buf: B,
42+
}
43+
44+
impl From<&FloatBuffer<[u8; 4]>> for f32 {
45+
fn from(float_buf: &FloatBuffer<[u8; 4]>) -> Self {
46+
f32::from_be_bytes(float_buf.buf)
47+
}
48+
}
49+
50+
impl From<&FloatBuffer<[u8; 8]>> for f64 {
51+
fn from(float_buf: &FloatBuffer<[u8; 8]>) -> Self {
52+
f64::from_be_bytes(float_buf.buf)
53+
}
54+
}
55+
3556
#[cfg(test)]
3657
mod tests {
3758
use util::flag_is_set;

src/writing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const MAGIC: u32 = 0xCAFE_BABE;
1111

1212
const CONSTANT_TAG_UTF8: u8 = 1;
1313
const CONSTANT_TAG_CLASS: u8 = 7;
14-
const CONSTANT_TAG_FIELDREF: u8 = 9;
14+
const _CONSTANT_TAG_FIELDREF: u8 = 9;
1515
const CONSTANT_TAG_METHODREF: u8 = 10;
1616
const CONSTANT_TAG_NAME_AND_TYPE: u8 = 12;
1717

tests/parse_classes.rs

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ extern crate jvm_class_file_parser;
33
use std::collections::HashSet;
44
use std::fs::File;
55

6-
use jvm_class_file_parser::{
7-
Attribute, Bytecode, ClassAccess, ClassFile, Code, Field, FieldAccess
8-
};
6+
use jvm_class_file_parser::{Attribute, Bytecode, ClassAccess, ClassFile, Code, Field, FieldAccess, ConstantPoolEntry};
7+
use std::ops::Deref;
98

109
#[test]
1110
fn parse_class_dummy() {
@@ -131,3 +130,54 @@ fn parse_class_intbox() {
131130
get_value_code.unwrap()
132131
);
133132
}
133+
134+
#[test]
135+
fn parse_class_constant_values() {
136+
let mut file = File::open("classes/ConstantValues.class").unwrap();
137+
let class_file = ClassFile::from_file(&mut file).unwrap();
138+
139+
match class_file.get_constant(2).deref() {
140+
ConstantPoolEntry::ConstantInteger { val } => {
141+
assert_eq!(65535, *val)
142+
},
143+
_ => {
144+
panic!("Expected an integer")
145+
}
146+
}
147+
148+
match class_file.get_constant(4).deref() {
149+
ConstantPoolEntry::ConstantFloat { ref val } => {
150+
assert_eq!(42.0 as f32, val.into())
151+
},
152+
_ => {
153+
panic!("Expected a float")
154+
}
155+
}
156+
157+
match class_file.get_constant(6).deref() {
158+
ConstantPoolEntry::ConstantLong { val } => {
159+
assert_eq!(42, *val)
160+
},
161+
_ => {
162+
panic!("Expected a long")
163+
}
164+
}
165+
166+
match class_file.get_constant(9).deref() {
167+
ConstantPoolEntry::ConstantDouble { ref val } => {
168+
assert_eq!(-1 as f64, val.into())
169+
},
170+
_ => {
171+
panic!("Expected a double")
172+
}
173+
}
174+
175+
match class_file.get_constant(37).deref() {
176+
ConstantPoolEntry::ConstantUtf8 { ref string } => {
177+
assert_eq!("fourty two".to_string(), *string)
178+
},
179+
_ => {
180+
panic!("Expected a utf8 string")
181+
}
182+
}
183+
}

0 commit comments

Comments
 (0)