Skip to content

Commit

Permalink
Merge pull request #2 from theseus-rs/add-as-code-to-access-flags
Browse files Browse the repository at this point in the history
feat: add as_code() function to class, method and field access flags
  • Loading branch information
brianheineman authored Jul 11, 2024
2 parents 6089850 + ca84c35 commit 9a34f88
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 20 deletions.
45 changes: 45 additions & 0 deletions ristretto_classfile/src/class_access_flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,38 @@ impl ClassAccessFlags {
Ok(access_flags)
}

/// Get the Class Access Flags as a string of code.
#[must_use]
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn as_code(&self) -> String {
let mut modifiers = Vec::new();
if self.contains(ClassAccessFlags::PUBLIC) {
modifiers.push("public");
}
if self.contains(ClassAccessFlags::ABSTRACT) {
modifiers.push("abstract");
}
if self.contains(ClassAccessFlags::FINAL) {
modifiers.push("final");
}
if self.contains(ClassAccessFlags::SYNTHETIC) {
modifiers.push("synthetic");
}
if self.contains(ClassAccessFlags::ANNOTATION) {
modifiers.push("annotation");
} else if self.contains(ClassAccessFlags::ENUM) {
modifiers.push("enum");
} else if self.contains(ClassAccessFlags::INTERFACE) {
modifiers.push("interface");
} else if self.contains(ClassAccessFlags::MODULE) {
modifiers.push("module");
} else {
modifiers.push("class");
}

modifiers.join(" ")
}

/// Serialize the `ClassAccessFlags` to bytes.
///
/// # Errors
Expand Down Expand Up @@ -129,6 +161,19 @@ mod test {
Ok(())
}

#[test]
fn test_as_code() {
assert_eq!("public class", ClassAccessFlags::PUBLIC.as_code());
assert_eq!("final class", ClassAccessFlags::FINAL.as_code());
assert_eq!("class", ClassAccessFlags::SUPER.as_code());
assert_eq!("interface", ClassAccessFlags::INTERFACE.as_code());
assert_eq!("abstract class", ClassAccessFlags::ABSTRACT.as_code());
assert_eq!("synthetic class", ClassAccessFlags::SYNTHETIC.as_code());
assert_eq!("annotation", ClassAccessFlags::ANNOTATION.as_code());
assert_eq!("enum", ClassAccessFlags::ENUM.as_code());
assert_eq!("module", ClassAccessFlags::MODULE.as_code());
}

#[test]
fn test_to_string() {
assert_eq!("(0x0001) ACC_PUBLIC", ClassAccessFlags::PUBLIC.to_string());
Expand Down
42 changes: 22 additions & 20 deletions ristretto_classfile/src/class_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,18 +180,21 @@ impl ClassFile {

impl fmt::Display for ClassFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(source_file) = self.source_file().map_err(|_| fmt::Error)? {
writeln!(f, r#"Compiled from "{source_file}""#)?;
}
writeln!(f, "version: {}", self.version)?;
writeln!(f, "major version: {}", self.version.major())?;
writeln!(f, "minor version: {}", self.version.minor())?;
writeln!(f, "access_flags: {}", self.access_flags)?;
writeln!(f, "this_class: #{}", self.this_class)?;
writeln!(f, "super_class: #{}", self.super_class)?;
let class_name = self.class_name().map_err(|_| fmt::Error)?;
writeln!(f, "{} {class_name}", self.access_flags.as_code())?;
writeln!(
f,
" major version: {} ({})",
self.version.major(),
self.version
)?;
writeln!(f, " minor version: {}", self.version.minor())?;
writeln!(f, " access_flags: {}", self.access_flags)?;
writeln!(f, " this_class: #{}", self.this_class)?;
writeln!(f, " super_class: #{}", self.super_class)?;
writeln!(
f,
"interfaces: {}, fields: {}, methods: {}, attributes: {}",
" interfaces: {}, fields: {}, methods: {}, attributes: {}",
self.interfaces.len(),
self.fields.len(),
self.methods.len(),
Expand Down Expand Up @@ -335,15 +338,14 @@ mod test {
let class_bytes = include_bytes!("../classes/Minimum.class");
let expected_bytes = class_bytes.to_vec();
let class_file = ClassFile::from_bytes(&mut Cursor::new(expected_bytes.clone()))?;
let expected = indoc! {r#"
Compiled from "Minimum.java"
version: Java 21
major version: 65
minor version: 0
access_flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #7
super_class: #2
interfaces: 0, fields: 0, methods: 1, attributes: 1
let expected = indoc! {r"
public class Minimum
major version: 65 (Java 21)
minor version: 0
access_flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #7
super_class: #2
interfaces: 0, fields: 0, methods: 1, attributes: 1
Constant Pool:
#1 = MethodRef #2.#3
#2 = Class #4
Expand Down Expand Up @@ -373,7 +375,7 @@ mod test {
line 0: 1
attributes:
SourceFile { name_index: 11, source_file_index: 12 }
"#};
"};

assert_eq!(expected, class_file.to_string());
Ok(())
Expand Down
52 changes: 52 additions & 0 deletions ristretto_classfile/src/field_access_flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,42 @@ impl FieldAccessFlags {
bytes.write_u16::<BigEndian>(self.bits())?;
Ok(())
}

/// Get the Field Access Flags as a string of code.
#[must_use]
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn as_code(&self) -> String {
let mut modifiers = Vec::new();
if self.contains(FieldAccessFlags::PUBLIC) {
modifiers.push("public");
}
if self.contains(FieldAccessFlags::PRIVATE) {
modifiers.push("private");
}
if self.contains(FieldAccessFlags::PROTECTED) {
modifiers.push("protected");
}
if self.contains(FieldAccessFlags::STATIC) {
modifiers.push("static");
}
if self.contains(FieldAccessFlags::VOLATILE) {
modifiers.push("volatile");
}
if self.contains(FieldAccessFlags::TRANSIENT) {
modifiers.push("transient");
}
if self.contains(FieldAccessFlags::FINAL) {
modifiers.push("final");
}
if self.contains(FieldAccessFlags::SYNTHETIC) {
modifiers.push("synthetic");
}
if self.contains(FieldAccessFlags::ENUM) {
modifiers.push("enum");
}

modifiers.join(" ")
}
}

impl fmt::Display for FieldAccessFlags {
Expand Down Expand Up @@ -129,6 +165,22 @@ mod test {
Ok(())
}

#[test]
fn test_as_code() {
assert_eq!("public", FieldAccessFlags::PUBLIC.as_code());
assert_eq!("private", FieldAccessFlags::PRIVATE.as_code());
assert_eq!("protected", FieldAccessFlags::PROTECTED.as_code());
assert_eq!("static", FieldAccessFlags::STATIC.as_code());
assert_eq!("final", FieldAccessFlags::FINAL.as_code());
assert_eq!("volatile", FieldAccessFlags::VOLATILE.as_code());
assert_eq!("transient", FieldAccessFlags::TRANSIENT.as_code());
assert_eq!("synthetic", FieldAccessFlags::SYNTHETIC.as_code());
assert_eq!("enum", FieldAccessFlags::ENUM.as_code());
let access_flags =
FieldAccessFlags::PUBLIC | FieldAccessFlags::STATIC | FieldAccessFlags::FINAL;
assert_eq!("public static final", access_flags.as_code());
}

#[test]
fn test_to_string() {
assert_eq!("(0x0001) ACC_PUBLIC", FieldAccessFlags::PUBLIC.to_string());
Expand Down
53 changes: 53 additions & 0 deletions ristretto_classfile/src/method_access_flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,39 @@ impl MethodAccessFlags {
bytes.write_u16::<BigEndian>(self.bits())?;
Ok(())
}

/// Get the Method Access Flags as a string of code.
#[must_use]
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn as_code(&self) -> String {
let mut modifiers = Vec::new();
if self.contains(MethodAccessFlags::PUBLIC) {
modifiers.push("public");
}
if self.contains(MethodAccessFlags::PRIVATE) {
modifiers.push("private");
}
if self.contains(MethodAccessFlags::PROTECTED) {
modifiers.push("protected");
}
if self.contains(MethodAccessFlags::STATIC) {
modifiers.push("static");
}
if self.contains(MethodAccessFlags::ABSTRACT) {
modifiers.push("abstract");
}
if self.contains(MethodAccessFlags::FINAL) {
modifiers.push("final");
}
if self.contains(MethodAccessFlags::SYNCHRONIZED) {
modifiers.push("synchronized");
}
if self.contains(MethodAccessFlags::NATIVE) {
modifiers.push("native");
}

modifiers.join(" ")
}
}

impl fmt::Display for MethodAccessFlags {
Expand Down Expand Up @@ -147,6 +180,26 @@ mod test {
Ok(())
}

#[test]
fn test_as_code() {
assert_eq!("public", MethodAccessFlags::PUBLIC.as_code());
assert_eq!("private", MethodAccessFlags::PRIVATE.as_code());
assert_eq!("protected", MethodAccessFlags::PROTECTED.as_code());
assert_eq!("static", MethodAccessFlags::STATIC.as_code());
assert_eq!("final", MethodAccessFlags::FINAL.as_code());
assert_eq!("synchronized", MethodAccessFlags::SYNCHRONIZED.as_code());
assert_eq!("", MethodAccessFlags::BRIDGE.as_code());
assert_eq!("", MethodAccessFlags::VARARGS.as_code());
assert_eq!("native", MethodAccessFlags::NATIVE.as_code());
assert_eq!("abstract", MethodAccessFlags::ABSTRACT.as_code());
assert_eq!("", MethodAccessFlags::STRICT.as_code());
assert_eq!("", MethodAccessFlags::SYNTHETIC.as_code());

let access_flags =
MethodAccessFlags::PUBLIC | MethodAccessFlags::STATIC | MethodAccessFlags::FINAL;
assert_eq!("public static final", access_flags.as_code());
}

#[test]
fn test_to_string() {
assert_eq!("(0x0001) ACC_PUBLIC", MethodAccessFlags::PUBLIC.to_string());
Expand Down

0 comments on commit 9a34f88

Please sign in to comment.