Skip to main content

jni_bindgen_reflection/
class.rs

1//! [Java SE 7 § 4](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html):  Lower level I/O for parsing .class files
2
3// https://en.wikipedia.org/wiki/Java_class_file
4// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
5
6use crate::*;
7use crate::io::*;
8
9use bitflags::bitflags;
10
11use std::io::{self, Read};
12
13
14
15bitflags! {
16    #[derive(Default)]
17    /// [Java SE 7 § 4.1](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1):  ClassFile::access_flags values.
18    pub struct Flags : u16 {
19        /// Declared `public`; may be accessed from outside its package.
20        const PUBLIC        = 0x0001;
21        /// Declared `static`.
22        const STATIC        = 0x0008;
23        /// Declared `final`; no subclasses allowed.
24        const FINAL         = 0x0010;
25        /// Treat superclass methods specifically when invoked by the *invokespecial* instruction.
26        const SUPER         = 0x0020;
27        /// Is an interface, not a class.
28        const INTERFACE     = 0x0200;
29        /// Declared `abstract`; must not be instantiated.
30        const ABSTRACT      = 0x0400;
31        /// Declared synthetic; not present in the source code.
32        const SYNTHETIC     = 0x1000;
33        /// Declared as an annotation type.
34        const ANNOTATION    = 0x2000;
35        /// Declared as an enum type.
36        const ENUM          = 0x4000;
37    }
38}
39
40impl Flags {
41    pub(crate) fn read(r: &mut impl Read) -> io::Result<Self> {
42        Ok(Self::from_bits_truncate(read_u2(r)?))
43    }
44}
45
46
47
48/// [Java SE 7 &sect; 4.1](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1):  The first few fields of a given ClassFile.
49#[repr(C)]
50#[derive(Clone, Copy, Debug, Default)]
51struct Header {
52    pub magic:          [u8; 4],
53    pub minor_version:  u16,
54    pub major_version:  version::Major,
55}
56
57impl Header {
58    pub(crate) fn read(reader: &mut impl Read) -> io::Result<Header> {
59        let mut h = Header::default();
60        reader.read_exact(&mut h.magic)?;
61        if h.magic != [0xCA, 0xFE, 0xBA, 0xBE] { return io_data_err!("Invalid header magic, not a class file"); }
62        h.minor_version = read_u2(reader)?;
63        h.major_version = version::Major(read_u2(reader)?);
64        Ok(h)
65    }
66}
67
68
69
70#[derive(Clone, Debug, Default)]
71pub struct Class {
72    pub flags:      Flags,
73    pub path:       IdBuf,
74    pub super_path: Option<IdBuf>,
75    pub interfaces: Vec<IdBuf>,
76    pub fields:     Vec<Field>,
77    pub methods:    Vec<Method>,
78    pub deprecated: bool,
79}
80
81#[allow(dead_code)]
82impl Class {
83    /// [Java SE 7 &sect; 4](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html):  Read a class File.
84    pub fn read(read: &mut impl Read) -> io::Result<Self> {
85        let _header     = Header::read(read)?;
86        let constants   = Constants::read(read)?;
87        let flags       = Flags::read(read)?;
88        let path        = IdBuf::new(constants.get_class(read_u2(read)?)?.to_owned());
89        let super_path  = constants.get_optional_class(read_u2(read)?)?.map(|s| IdBuf::new(s.to_owned()));
90
91        let interfaces_count = read_u2(read)? as usize;
92        let mut interfaces = Vec::with_capacity(interfaces_count);
93        for _ in 0..interfaces_count {
94            interfaces.push(IdBuf::new(constants.get_class(read_u2(read)?)?.to_owned()));
95        }
96
97        let fields  = Field::read_list(read, &constants)?;
98        let methods = Method::read_list(read, &constants)?;
99
100        let attributes_count = read_u2(read)?;
101        let mut deprecated = false;
102        for _ in 0..attributes_count {
103            match Attribute::read(read, &constants)? {
104                Attribute::Deprecated { .. } => { deprecated = true; },
105                _ => {},
106            }
107        }
108
109        Ok(Self {
110            flags,
111            path,
112            super_path,
113            interfaces,
114            fields,
115            methods,
116            deprecated,
117        })
118    }
119
120    pub fn is_public(&self)         -> bool { self.flags.contains(Flags::PUBLIC) }
121    pub fn is_static(&self)         -> bool { self.flags.contains(Flags::STATIC) }
122    pub fn is_final(&self)          -> bool { self.flags.contains(Flags::FINAL) }
123    pub fn is_super(&self)          -> bool { self.flags.contains(Flags::SUPER) }
124    pub fn is_interface(&self)      -> bool { self.flags.contains(Flags::INTERFACE) }
125    pub fn is_abstract(&self)       -> bool { self.flags.contains(Flags::ABSTRACT) }
126    pub fn is_synthetic(&self)      -> bool { self.flags.contains(Flags::SYNTHETIC) }
127    pub fn is_annotation(&self)     -> bool { self.flags.contains(Flags::ANNOTATION) }
128    pub fn is_enum(&self)           -> bool { self.flags.contains(Flags::ENUM) }
129}
130
131
132
133#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
134pub struct IdBuf(String);
135
136impl IdBuf {
137    pub fn new(s: String) -> Self { Self(s) }
138    pub fn as_str(&self) -> &str { self.0.as_str() }
139    pub fn as_id(&self) -> Id { Id(self.0.as_str()) }
140    #[allow(dead_code)] pub fn iter(&self) -> IdIter { IdIter::new(self.0.as_str()) }
141}
142
143// XXX: This should really be `#[repr(transparent)] pub struct Id(str);`, but I've banned unsafe for this lib...
144// Also, patterns apparently can't handle Id::new(...) even when it's a const fn.
145#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
146pub struct Id<'a>(pub &'a str);
147
148impl<'a> Id<'a> {
149    pub fn as_str(&self) -> &'a str { self.0 }
150    pub fn iter(&self) -> IdIter<'a> { IdIter::new(self.0) }
151}
152
153#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
154pub enum IdPart<'a> {
155    Namespace(&'a str),
156    ContainingClass(&'a str),
157    LeafClass(&'a str),
158}
159
160pub struct IdIter<'a> {
161    rest: &'a str,
162}
163
164impl<'a> IdIter<'a> {
165    pub fn new(path: &'a str) -> Self { IdIter { rest: path } }
166}
167
168impl<'a> Iterator for IdIter<'a> {
169    type Item = IdPart<'a>;
170    fn next(&mut self) -> Option<Self::Item> {
171        if let Some(slash) = self.rest.find('/') {
172            let (namespace, rest) = self.rest.split_at(slash);
173            self.rest = &rest[1..];
174            return Some(IdPart::Namespace(namespace));
175        }
176
177        if let Some(dollar) = self.rest.find('$') {
178            let (class, rest) = self.rest.split_at(dollar);
179            self.rest = &rest[1..];
180            return Some(IdPart::ContainingClass(class));
181        }
182
183        if !self.rest.is_empty() {
184            let class = self.rest;
185            self.rest = "";
186            return Some(IdPart::LeafClass(class));
187        }
188
189        None
190    }
191}
192
193#[test] fn id_iter_test() {
194    assert_eq!(Id("").iter().collect::<Vec<IdPart>>(), &[]);
195
196    assert_eq!(Id("Bar").iter().collect::<Vec<IdPart>>(), &[
197        IdPart::LeafClass("Bar"),
198    ]);
199
200    assert_eq!(Id("java/foo/Bar").iter().collect::<Vec<IdPart>>(), &[
201        IdPart::Namespace("java"),
202        IdPart::Namespace("foo"),
203        IdPart::LeafClass("Bar"),
204    ]);
205
206    assert_eq!(Id("java/foo/Bar$Inner").iter().collect::<Vec<IdPart>>(), &[
207        IdPart::Namespace("java"),
208        IdPart::Namespace("foo"),
209        IdPart::ContainingClass("Bar"),
210        IdPart::LeafClass("Inner"),
211    ]);
212
213    assert_eq!(Id("java/foo/Bar$Inner$MoreInner").iter().collect::<Vec<IdPart>>(), &[
214        IdPart::Namespace("java"),
215        IdPart::Namespace("foo"),
216        IdPart::ContainingClass("Bar"),
217        IdPart::ContainingClass("Inner"),
218        IdPart::LeafClass("MoreInner"),
219    ]);
220}