ristretto_classfile 0.31.0

A library for reading, writing and verifying Java classfiles.
Documentation
use crate::byte_reader::ByteReader;
use crate::error::Result;
use bitflags::bitflags;
use byteorder::{BigEndian, WriteBytesExt};
use std::fmt;

bitflags! {
    /// Method access flags used in Java class files to specify the access permissions and
    /// properties of methods.
    ///
    /// These flags determine visibility (public, private, protected), mutability (final), execution
    /// context (static, synchronized, native), and other characteristics of class methods. Multiple
    /// flags can be combined using bitwise OR operations.
    ///
    /// # Examples
    ///
    /// Creating method access flags for common method types:
    ///
    /// ```rust
    /// use ristretto_classfile::MethodAccessFlags;
    /// use ristretto_classfile::byte_reader::ByteReader;
    ///
    /// // A public static method
    /// let flags = MethodAccessFlags::PUBLIC | MethodAccessFlags::STATIC;
    ///
    /// // Check if specific flags are set
    /// assert!(flags.contains(MethodAccessFlags::PUBLIC));
    /// assert!(flags.contains(MethodAccessFlags::STATIC));
    /// assert!(!flags.contains(MethodAccessFlags::FINAL));
    /// assert!(!flags.contains(MethodAccessFlags::SYNCHRONIZED));
    ///
    /// // Get a code representation
    /// assert_eq!("public static", flags.as_code());
    ///
    /// // Serialize to bytes
    /// let mut bytes = Vec::new();
    /// flags.to_bytes(&mut bytes)?;
    /// assert_eq!(vec![0x00, 0x09], bytes); // 0x0009 = PUBLIC | STATIC
    ///
    /// // Deserialize from bytes
    /// let mut reader = ByteReader::new(&bytes);
    /// let deserialized = MethodAccessFlags::from_bytes(&mut reader)?;
    /// assert_eq!(flags, deserialized);
    ///
    /// // Display as string
    /// assert_eq!("(0x0009) ACC_PUBLIC, ACC_STATIC", flags.to_string());
    /// # Ok::<(), ristretto_classfile::Error>(())
    /// ```
    ///
    /// # References
    ///
    /// See: <https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.5:~:text=method_info%20structure%20are%20as%20follows%3A-,access_flags,-The%20value%20of%20the%20access_flags>
    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub struct MethodAccessFlags: u16 {
        /// Declared public; may be accessed from outside its package.
        const PUBLIC = 0x0001;
        /// Declared private; accessible only within the defining class and other classes belonging to the same nest (§5.4.4).
        const PRIVATE = 0x0002;
        /// Declared protected; may be accessed within subclasses.
        const PROTECTED = 0x0004;
        /// Declared static.
        const STATIC = 0x0008;
        /// Declared final; must not be overridden (§5.4.5).
        const FINAL = 0x0010;
        /// Declared synchronized; invocation is wrapped by a monitor use.
        const SYNCHRONIZED = 0x0020;
        /// A bridge method, generated by the compiler.
        const BRIDGE = 0x0040;
        /// Declared with variable number of arguments.
        const VARARGS = 0x0080;
        /// Declared native; implemented in a language other than the Java programming language.
        const NATIVE = 0x0100;
        /// Declared abstract; no implementation is provided.
        const ABSTRACT = 0x0400;
        /// In a class file whose major version number is at least 46 and at most 60: Declared strictfp.
        const STRICT = 0x0800;
        /// Declared synthetic; not present in the source code.
        const SYNTHETIC = 0x1000;
    }
}

impl Default for MethodAccessFlags {
    /// Returns an empty set of method access flags.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ristretto_classfile::MethodAccessFlags;
    ///
    /// let flags = MethodAccessFlags::default();
    /// assert!(flags.is_empty());
    /// assert_eq!(flags.bits(), 0);
    /// ```
    fn default() -> MethodAccessFlags {
        MethodAccessFlags::empty()
    }
}

impl MethodAccessFlags {
    /// Deserialize the `MethodAccessFlags` from bytes.
    ///
    /// This method reads a 16-bit big-endian value from the provided byte reader and constructs a
    /// `MethodAccessFlags` value from it.
    ///
    /// # Errors
    ///
    /// Returns an error if reading from the byte reader fails.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ristretto_classfile::MethodAccessFlags;
    /// use ristretto_classfile::byte_reader::ByteReader;
    ///
    /// // Create a reader with bytes representing PUBLIC | STATIC (0x0009)
    /// let mut bytes = ByteReader::new(&[0x00, 0x09]);
    /// let flags = MethodAccessFlags::from_bytes(&mut bytes)?;
    ///
    /// assert!(flags.contains(MethodAccessFlags::PUBLIC));
    /// assert!(flags.contains(MethodAccessFlags::STATIC));
    /// assert_eq!(flags.bits(), 0x0009);
    /// # Ok::<(), ristretto_classfile::Error>(())
    /// ```
    pub fn from_bytes(bytes: &mut ByteReader<'_>) -> Result<MethodAccessFlags> {
        let access_flags = bytes.read_u16()?;
        let method_access_flags = MethodAccessFlags::from_bits_truncate(access_flags);
        Ok(method_access_flags)
    }

    /// Serialize the `MethodAccessFlags` to bytes.
    ///
    /// This method writes the flags as a 16-bit big-endian value to the provided byte vector.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ristretto_classfile::MethodAccessFlags;
    ///
    /// let flags = MethodAccessFlags::PUBLIC | MethodAccessFlags::STATIC;
    /// let mut bytes = Vec::new();
    ///
    /// flags.to_bytes(&mut bytes)?;
    /// assert_eq!(bytes, vec![0x00, 0x09]); // 0x0009 in big-endian
    /// # Ok::<(), ristretto_classfile::Error>(())
    /// ```
    ///
    /// # Errors
    /// Returns an error if writing to the byte vector fails.
    pub fn to_bytes(&self, bytes: &mut Vec<u8>) -> Result<()> {
        bytes.write_u16::<BigEndian>(self.bits())?;
        Ok(())
    }

    /// Get the `MethodAccessFlags` as a string of Java modifiers.
    ///
    /// This method returns a string representation of the access flags as they would
    /// appear in Java source code. Note that not all flags (like BRIDGE, VARARGS, etc.)
    /// have a corresponding Java modifier and will be omitted from the result.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ristretto_classfile::MethodAccessFlags;
    ///
    /// // Single flags
    /// assert_eq!("public", MethodAccessFlags::PUBLIC.as_code());
    /// assert_eq!("static", MethodAccessFlags::STATIC.as_code());
    ///
    /// // Multiple flags
    /// let flags = MethodAccessFlags::PUBLIC | MethodAccessFlags::STATIC | MethodAccessFlags::FINAL;
    /// assert_eq!("public static final", flags.as_code());
    ///
    /// // Flags without Java modifiers return empty strings
    /// assert_eq!("", MethodAccessFlags::empty().as_code());
    /// ```
    #[must_use]
    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 {
    /// Formats the `MethodAccessFlags` as a string showing its hexadecimal value and a
    /// comma-separated list of flag names.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ristretto_classfile::MethodAccessFlags;
    ///
    /// // Public method
    /// let flags = MethodAccessFlags::PUBLIC;
    /// assert_eq!("(0x0001) ACC_PUBLIC", flags.to_string());
    ///
    /// // Public static final method
    /// let flags = MethodAccessFlags::PUBLIC | MethodAccessFlags::STATIC | MethodAccessFlags::FINAL;
    /// assert_eq!("(0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL", flags.to_string());
    /// ```
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut access_flags = Vec::new();
        if self.contains(MethodAccessFlags::PUBLIC) {
            access_flags.push("ACC_PUBLIC");
        }
        if self.contains(MethodAccessFlags::PRIVATE) {
            access_flags.push("ACC_PRIVATE");
        }
        if self.contains(MethodAccessFlags::PROTECTED) {
            access_flags.push("ACC_PROTECTED");
        }
        if self.contains(MethodAccessFlags::STATIC) {
            access_flags.push("ACC_STATIC");
        }
        if self.contains(MethodAccessFlags::FINAL) {
            access_flags.push("ACC_FINAL");
        }
        if self.contains(MethodAccessFlags::SYNCHRONIZED) {
            access_flags.push("ACC_SYNCHRONIZED");
        }
        if self.contains(MethodAccessFlags::BRIDGE) {
            access_flags.push("ACC_BRIDGE");
        }
        if self.contains(MethodAccessFlags::VARARGS) {
            access_flags.push("ACC_VARARGS");
        }
        if self.contains(MethodAccessFlags::NATIVE) {
            access_flags.push("ACC_NATIVE");
        }
        if self.contains(MethodAccessFlags::ABSTRACT) {
            access_flags.push("ACC_ABSTRACT");
        }
        if self.contains(MethodAccessFlags::STRICT) {
            access_flags.push("ACC_STRICT");
        }
        if self.contains(MethodAccessFlags::SYNTHETIC) {
            access_flags.push("ACC_SYNTHETIC");
        }
        write!(f, "({:#06X}) {}", self.bits(), access_flags.join(", "))
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_default() {
        assert_eq!(MethodAccessFlags::empty(), MethodAccessFlags::default());
    }

    #[test]
    fn test_all_access_flags() {
        let access_flags: u16 = u16::MAX;
        let binding = access_flags.to_be_bytes();
        let mut bytes = ByteReader::new(&binding);
        assert_eq!(
            Ok(MethodAccessFlags::PUBLIC
                | MethodAccessFlags::PRIVATE
                | MethodAccessFlags::PROTECTED
                | MethodAccessFlags::STATIC
                | MethodAccessFlags::FINAL
                | MethodAccessFlags::SYNCHRONIZED
                | MethodAccessFlags::BRIDGE
                | MethodAccessFlags::VARARGS
                | MethodAccessFlags::NATIVE
                | MethodAccessFlags::ABSTRACT
                | MethodAccessFlags::STRICT
                | MethodAccessFlags::SYNTHETIC),
            MethodAccessFlags::from_bytes(&mut bytes)
        );
    }

    #[test]
    fn test_access_flags() -> Result<()> {
        let access_flags = MethodAccessFlags::PUBLIC | MethodAccessFlags::FINAL;
        let mut bytes = Vec::new();
        access_flags.to_bytes(&mut bytes)?;
        let mut bytes = ByteReader::new(&bytes);
        assert_eq!(Ok(access_flags), MethodAccessFlags::from_bytes(&mut bytes));
        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());
        assert_eq!(
            "(0x0002) ACC_PRIVATE",
            MethodAccessFlags::PRIVATE.to_string()
        );
        assert_eq!(
            "(0x0004) ACC_PROTECTED",
            MethodAccessFlags::PROTECTED.to_string()
        );
        assert_eq!("(0x0008) ACC_STATIC", MethodAccessFlags::STATIC.to_string());
        assert_eq!("(0x0010) ACC_FINAL", MethodAccessFlags::FINAL.to_string());
        assert_eq!(
            "(0x0020) ACC_SYNCHRONIZED",
            MethodAccessFlags::SYNCHRONIZED.to_string()
        );
        assert_eq!("(0x0040) ACC_BRIDGE", MethodAccessFlags::BRIDGE.to_string());
        assert_eq!(
            "(0x0080) ACC_VARARGS",
            MethodAccessFlags::VARARGS.to_string()
        );
        assert_eq!("(0x0100) ACC_NATIVE", MethodAccessFlags::NATIVE.to_string());
        assert_eq!(
            "(0x0400) ACC_ABSTRACT",
            MethodAccessFlags::ABSTRACT.to_string()
        );
        assert_eq!("(0x0800) ACC_STRICT", MethodAccessFlags::STRICT.to_string());
        assert_eq!(
            "(0x1000) ACC_SYNTHETIC",
            MethodAccessFlags::SYNTHETIC.to_string()
        );

        let access_flags =
            MethodAccessFlags::PUBLIC | MethodAccessFlags::STATIC | MethodAccessFlags::FINAL;
        assert_eq!(
            "(0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL",
            access_flags.to_string()
        );
    }
}