Skip to content

Commit bb68464

Browse files
committed
Add better support for class access flags
1 parent 129e305 commit bb68464

File tree

7 files changed

+182
-3
lines changed

7 files changed

+182
-3
lines changed

src/class_access.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use std::collections::HashSet;
2+
3+
use util::flag_is_set;
4+
5+
// Access flag masks are from Table 4.1-B of the JVM specification
6+
//
7+
// https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.1-200-E.1
8+
const PUBLIC_FLAG: u16 = 0x0001;
9+
const FINAL_FLAG: u16 = 0x0010;
10+
const SUPER_FLAG: u16 = 0x0020;
11+
const INTERFACE_FLAG: u16 = 0x0200;
12+
const ABSTRACT_FLAG: u16 = 0x0400;
13+
const SYNTHETIC_FLAG: u16 = 0x1000;
14+
const ANNOTATION_FLAG: u16 = 0x2000;
15+
const ENUM_FLAG: u16 = 0x4000;
16+
const MODULE_FLAG: u16 = 0x8000;
17+
18+
/// A flag that denotes an access level or property of a class.
19+
///
20+
/// See the `access_flags` section of Chapter 4.1 of the JVM specification for
21+
/// details.
22+
///
23+
/// https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.1-200-E
24+
#[derive(Debug)]
25+
#[derive(Eq)]
26+
#[derive(Hash)]
27+
#[derive(PartialEq)]
28+
pub enum ClassAccess {
29+
Public,
30+
Final,
31+
Super,
32+
Interface,
33+
Abstract,
34+
Synthetic,
35+
Annotation,
36+
Enum,
37+
Module,
38+
}
39+
40+
impl ClassAccess {
41+
/// Extracts the list of class access flags that are embedded in the given
42+
/// access flag value.
43+
///
44+
/// Returns an error message if the extracted combination of access flags
45+
/// are inconsistent. (This validation has not yet been implemented)
46+
///
47+
/// See Table 4.1-B of the JVM specification for more details.
48+
///
49+
/// https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.1-200-E.1
50+
///
51+
/// ```
52+
/// # use std::collections::HashSet;
53+
/// # use jvm_class_file_parser::ClassAccess;
54+
/// #
55+
/// let access_flags = 0b0000_0010_0000_0001;
56+
///
57+
/// let mut expected = HashSet::new();
58+
/// expected.insert(ClassAccess::Public);
59+
/// expected.insert(ClassAccess::Interface);
60+
///
61+
/// assert_eq!(Ok(expected), ClassAccess::from_access_flags(access_flags));
62+
/// ```
63+
pub fn from_access_flags(access_flags: u16) -> Result<HashSet<ClassAccess>, String> {
64+
use ClassAccess::*;
65+
66+
let mut access = HashSet::new();
67+
68+
let is_public = flag_is_set(PUBLIC_FLAG, access_flags);
69+
let is_final = flag_is_set(FINAL_FLAG, access_flags);
70+
let is_super = flag_is_set(SUPER_FLAG, access_flags);
71+
let is_interface = flag_is_set(INTERFACE_FLAG, access_flags);
72+
let is_abstract = flag_is_set(ABSTRACT_FLAG, access_flags);
73+
let is_synthetic = flag_is_set(SYNTHETIC_FLAG, access_flags);
74+
let is_annotation = flag_is_set(ANNOTATION_FLAG, access_flags);
75+
let is_enum = flag_is_set(ENUM_FLAG, access_flags);
76+
let is_module = flag_is_set(MODULE_FLAG, access_flags);
77+
78+
// TODO: Add validation for inconsistent access flags
79+
80+
if is_public { access.insert(Public); }
81+
if is_final { access.insert(Final); }
82+
if is_super { access.insert(Super); }
83+
if is_interface { access.insert(Interface); }
84+
if is_abstract { access.insert(Abstract); }
85+
if is_synthetic { access.insert(Synthetic); }
86+
if is_annotation { access.insert(Annotation); }
87+
if is_enum { access.insert(Enum); }
88+
if is_module { access.insert(Module); }
89+
90+
Ok(access)
91+
}
92+
}

src/class_file.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use std::collections::HashSet;
12
use std::fs::File;
23
use std::io;
34
use std::ops::Deref;
45

56
use attribute::*;
7+
use class_access::*;
68
use constant_pool::*;
79
use field::*;
810
use method::*;
@@ -19,7 +21,7 @@ pub struct ClassFile {
1921
pub minor_version: u16,
2022
pub major_version: u16,
2123
pub constant_pool: Vec<Box<ConstantPoolEntry>>,
22-
pub access_flags: u16,
24+
pub access_flags: HashSet<ClassAccess>,
2325
pub this_class: u16,
2426
pub super_class: u16,
2527
pub interfaces: Vec<u16>,

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
mod attribute;
1717
mod bytecode;
1818
mod class_file;
19+
mod class_access;
1920
mod constant_pool;
2021
mod field;
2122
mod method;
2223
mod parsing;
24+
mod util;
2325

2426
pub use attribute::*;
2527
pub use bytecode::*;
2628
pub use class_file::*;
29+
pub use class_access::*;
2730
pub use constant_pool::*;
2831
pub use field::*;
2932
pub use method::*;

src/main.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
extern crate jvm_class_file_parser;
22

3+
use std::collections::HashSet;
34
use std::env;
45
use std::fs;
56
use std::fs::File;
@@ -8,7 +9,8 @@ use std::ops::Deref;
89
use std::path::PathBuf;
910

1011
use jvm_class_file_parser::{
11-
Bytecode, ClassFile, ConstantPoolEntry, ExceptionTableEntry, Method
12+
Bytecode, ClassAccess, ClassFile, ConstantPoolEntry, ExceptionTableEntry,
13+
Method
1214
};
1315

1416
const CONSTURCTOR_NAME: &str = "<init>";
@@ -39,6 +41,8 @@ fn javap(filepath: &str) {
3941
println!(" minor version: {}", class_file.minor_version);
4042
println!(" major version: {}", class_file.major_version);
4143

44+
print_access_flags(&class_file.access_flags);
45+
4246
print_constant_pool(&class_file);
4347

4448
println!("{}", "{");
@@ -62,6 +66,31 @@ fn to_absolute_filepath(filepath: &str) -> io::Result<PathBuf> {
6266
fs::canonicalize(path)
6367
}
6468

69+
fn print_access_flags(access_flags: &HashSet<ClassAccess>) {
70+
let flags_str = access_flags.iter()
71+
.map(access_flag_to_name)
72+
.collect::<Vec<&str>>()
73+
.join(", ");
74+
75+
println!(" flags: {}", flags_str);
76+
}
77+
78+
fn access_flag_to_name(flag: &ClassAccess) -> &'static str {
79+
use ClassAccess::*;
80+
81+
match flag {
82+
Public => "PUBLIC_FLAG",
83+
Final => "FINAL_FLAG",
84+
Super => "SUPER_FLAG",
85+
Interface => "INTERFACE_FLAG",
86+
Abstract => "ABSTRACT_FLAG",
87+
Synthetic => "SYNTHETIC_FLAG",
88+
Annotation => "ANNOTATION_FLAG",
89+
Enum => "ENUM_FLAG",
90+
Module => "MODULE_FLAG",
91+
}
92+
}
93+
6594
fn print_constant_pool(class_file: &ClassFile) {
6695
println!("Constant pool:");
6796

src/parsing.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
use std::collections::HashSet;
12
use std::fs::File;
23
use std::io;
34
use std::io::{Error, ErrorKind, Read};
45
use std::str;
56

67
use attribute::*;
8+
use class_access::*;
79
use class_file::ClassFile;
810
use constant_pool::*;
911
use field::*;
1012
use method::*;
13+
use util::promote_result_to_io;
1114

1215
const EXPECTED_MAGIC: u32 = 0xCAFE_BABE;
1316

@@ -40,6 +43,10 @@ pub fn read_class_file(file: &mut File) -> io::Result<ClassFile> {
4043
let methods = read_methods(file)?;
4144
let attributes = read_attributes(file)?;
4245

46+
let access_flags = promote_result_to_io(
47+
ClassAccess::from_access_flags(access_flags)
48+
)?;
49+
4350
Ok(ClassFile {
4451
minor_version,
4552
major_version,

src/util.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use std::io;
2+
use std::io::{Error, ErrorKind, Read};
3+
4+
/// Checks if the given unary flag is set within the given binary encoding of a
5+
/// list of flags.
6+
pub fn flag_is_set(flag_to_check: u16, flags: u16) -> bool {
7+
let check = flags & flag_to_check;
8+
9+
check > 0
10+
}
11+
12+
pub fn promote_result_to_io<A>(result: Result<A, String>) -> io::Result<A> {
13+
match result {
14+
Ok(v) => Ok(v),
15+
Err(s) => Err(Error::new(ErrorKind::Other, s)),
16+
}
17+
}
18+
19+
#[cfg(test)]
20+
mod tests {
21+
use util::flag_is_set;
22+
23+
#[test]
24+
fn flag_is_set_finds_a_set_flag() {
25+
let public_flag = 0b0000_0000_0000_0001;
26+
let access_flags = 0b0000_0000_0000_1001;
27+
28+
assert_eq!(true, flag_is_set(public_flag, access_flags))
29+
}
30+
31+
#[test]
32+
fn flag_is_set_does_not_find_a_missing_flag() {
33+
let volatile_flag = 0b0000_0000_0100_0000;
34+
let access_flags = 0b0000_0000_0000_1001;
35+
36+
assert_eq!(false, flag_is_set(volatile_flag, access_flags))
37+
}
38+
}

tests/parse_classes.rs

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

33
use std::fs::File;
4-
use jvm_class_file_parser::{Bytecode, ClassFile, Code};
4+
use jvm_class_file_parser::{Bytecode, ClassAccess, ClassFile, Code};
55

66
#[test]
77
fn parse_class_dummy() {
@@ -12,6 +12,10 @@ fn parse_class_dummy() {
1212

1313
assert_eq!(Some("Dummy.java"), class_file.get_source_file_name());
1414

15+
assert_eq!(2, class_file.access_flags.len());
16+
assert!(class_file.access_flags.contains(&ClassAccess::Public));
17+
assert!(class_file.access_flags.contains(&ClassAccess::Super));
18+
1519
assert_eq!(1, class_file.methods.len());
1620

1721
let constructor = &class_file.methods[0];
@@ -42,6 +46,10 @@ fn parse_class_intbox() {
4246

4347
assert_eq!(Some("IntBox.java"), class_file.get_source_file_name());
4448

49+
assert_eq!(2, class_file.access_flags.len());
50+
assert!(class_file.access_flags.contains(&ClassAccess::Public));
51+
assert!(class_file.access_flags.contains(&ClassAccess::Super));
52+
4553
assert_eq!(2, class_file.methods.len());
4654

4755
let constructor = &class_file.methods[0];

0 commit comments

Comments
 (0)