Skip to main content

jvm_class/
lib.rs

1//! Zero-copy parser for JVM class files (Java 1.1 - 8, major versions 45-52).
2//!
3//! The entry point is [`Class::parse`]; the returned tree borrows from the
4//! input buffer instead of copying it.
5//!
6//! Everything here follows chapter 4 of
7//! [The Java Virtual Machine Specification, Java SE 8 Edition](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html).
8
9#![allow(dead_code)]
10
11use std::{
12    borrow::Cow,
13    fmt::{Debug, Formatter},
14};
15
16pub use crate::{
17    attribute::*,
18    cursor::{Cursor, ReadBytes},
19    error::{Error, Result},
20    field::*,
21    method::*,
22    utf8::Cesu8Bytes,
23};
24
25mod cursor;
26
27/// Magic number every class file starts with.
28const MAGIC: u32 = 0xCAFE_BABE;
29
30/// Lowest supported major version (Java 1.1).
31const MIN_MAJOR_VERSION: u16 = 45;
32/// Highest supported major version (Java 8).
33const MAX_MAJOR_VERSION: u16 = 52;
34
35/// Index into the [`ConstantPool`], starting at 1.
36///
37/// See [JVMS §4.4](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4).
38pub type CpIndex = u16;
39
40mod error {
41    use std::fmt::{Display, Formatter};
42
43    use cesu8::Cesu8DecodingError;
44
45    use crate::CpIndex;
46
47    /// Result of any parsing operation in this crate.
48    pub type Result<T> = std::result::Result<T, Error>;
49
50    /// Anything that can go wrong while parsing a class file.
51    #[derive(Debug)]
52    pub enum Error {
53        InvalidMagic {
54            magic: u32,
55        },
56        UnsupportedVersion {
57            major_version: u16,
58            minor_version: u16,
59            supported: std::ops::RangeInclusive<u16>,
60        },
61        InvalidConstantPoolCount {
62            count: u16,
63        },
64        InvalidConstantPoolTag {
65            tag: u8,
66        },
67        ConstantPoolOverflow {
68            index: CpIndex,
69        },
70        ConstantPoolIndexOutOfBounds {
71            index: CpIndex,
72            count: usize,
73        },
74        ConstantPoolIndexUnusable {
75            index: CpIndex,
76        },
77        UnexpectedConstantPoolEntry {
78            index: CpIndex,
79            expected: &'static str,
80            found: &'static str,
81        },
82        AttributeLengthMismatch {
83            name: String,
84            expected: usize,
85            consumed: usize,
86        },
87        InvalidReferenceKind {
88            kind: u8,
89        },
90        InvalidVerificationTypeTag {
91            tag: u8,
92        },
93        InvalidStackMapFrameTag {
94            tag: u8,
95        },
96        InvalidElementValueTag {
97            tag: u8,
98        },
99        InvalidTypeAnnotationTarget {
100            target_type: u8,
101        },
102        TrailingBytes {
103            count: usize,
104        },
105        InvalidCesu8(Cesu8DecodingError),
106        UnexpectedEof {
107            needed: usize,
108            available: usize,
109        },
110    }
111
112    impl Display for Error {
113        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
114            match self {
115                Self::InvalidMagic { magic } => write!(f, "invalid class magic 0x{magic:08X}"),
116                Self::UnsupportedVersion {
117                    major_version,
118                    minor_version,
119                    supported,
120                } => write!(
121                    f,
122                    "unsupported class version {major_version}.{minor_version}, \
123                     supported major versions are {}..={}",
124                    supported.start(),
125                    supported.end()
126                ),
127                Self::InvalidConstantPoolCount { count } => {
128                    write!(
129                        f,
130                        "invalid constant pool count {count}, expected at least 1"
131                    )
132                }
133                Self::InvalidConstantPoolTag { tag } => {
134                    write!(f, "invalid constant pool tag {tag}")
135                }
136                Self::ConstantPoolOverflow { index } => write!(
137                    f,
138                    "wide constant at index {index} overflows the constant pool"
139                ),
140                Self::ConstantPoolIndexOutOfBounds { index, count } => write!(
141                    f,
142                    "constant pool index {index} is out of bounds (pool holds {count} slots)"
143                ),
144                Self::ConstantPoolIndexUnusable { index } => write!(
145                    f,
146                    "constant pool index {index} points at the unusable slot of a wide constant"
147                ),
148                Self::UnexpectedConstantPoolEntry {
149                    index,
150                    expected,
151                    found,
152                } => write!(
153                    f,
154                    "constant pool index {index} refers to {found}, expected {expected}"
155                ),
156                Self::AttributeLengthMismatch {
157                    name,
158                    expected,
159                    consumed,
160                } => write!(
161                    f,
162                    "attribute {name} declares {expected} bytes but {consumed} were consumed"
163                ),
164                Self::InvalidReferenceKind { kind } => {
165                    write!(f, "invalid method handle reference kind {kind}")
166                }
167                Self::InvalidVerificationTypeTag { tag } => {
168                    write!(f, "invalid verification type tag {tag}")
169                }
170                Self::InvalidStackMapFrameTag { tag } => {
171                    write!(f, "invalid stack map frame tag {tag}")
172                }
173                Self::InvalidElementValueTag { tag } => {
174                    write!(f, "invalid element value tag {tag} ({:?})", *tag as char)
175                }
176                Self::InvalidTypeAnnotationTarget { target_type } => {
177                    write!(f, "invalid type annotation target type 0x{target_type:02X}")
178                }
179                Self::TrailingBytes { count } => {
180                    write!(f, "{count} trailing bytes after the class file")
181                }
182                Self::InvalidCesu8(error) => write!(f, "invalid CESU-8 string: {error}"),
183                Self::UnexpectedEof { needed, available } => write!(
184                    f,
185                    "unexpected EOF: needed {needed} bytes, {available} available"
186                ),
187            }
188        }
189    }
190
191    impl std::error::Error for Error {
192        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
193            match self {
194                Self::InvalidCesu8(error) => Some(error),
195                _ => None,
196            }
197        }
198    }
199
200    impl From<Cesu8DecodingError> for Error {
201        fn from(value: Cesu8DecodingError) -> Self {
202            Self::InvalidCesu8(value)
203        }
204    }
205}
206
207mod utf8 {
208    use std::{
209        borrow::Cow,
210        fmt::{Debug, Display, Formatter},
211    };
212
213    use crate::error::Result;
214
215    /// Borrowed CESU-8 ("modified UTF-8") bytes of a `Utf8` constant.
216    ///
217    /// See [JVMS §4.4.7](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.7).
218    #[derive(Clone, Copy, PartialEq, Eq)]
219    pub struct Cesu8Bytes<'a>(&'a [u8]);
220
221    impl<'a> Cesu8Bytes<'a> {
222        /// Returns the undecoded bytes.
223        pub fn as_bytes(&self) -> &'a [u8] {
224            self.0
225        }
226
227        /// Decodes to a string, borrowing when the bytes are already valid UTF-8.
228        pub fn decode(&self) -> Result<Cow<'a, str>> {
229            Ok(cesu8::from_java_cesu8(self.0)?)
230        }
231
232        /// Decodes to an owned string.
233        pub fn decode_owned(&self) -> Result<String> {
234            Ok(self.decode()?.into_owned())
235        }
236    }
237
238    impl Debug for Cesu8Bytes<'_> {
239        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
240            match self.decode() {
241                Ok(string) => Debug::fmt(&string, f),
242                Err(_) => write!(f, "<invalid cesu-8: {:02X?}>", self.0),
243            }
244        }
245    }
246
247    impl Display for Cesu8Bytes<'_> {
248        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
249            match self.decode() {
250                Ok(string) => f.write_str(&string),
251                Err(_) => write!(f, "<invalid cesu-8>"),
252            }
253        }
254    }
255
256    impl<'a> From<&'a [u8]> for Cesu8Bytes<'a> {
257        fn from(value: &'a [u8]) -> Self {
258            Self(value)
259        }
260    }
261
262    impl<'a> TryFrom<Cesu8Bytes<'a>> for String {
263        type Error = crate::error::Error;
264
265        fn try_from(value: Cesu8Bytes<'a>) -> Result<Self> {
266            value.decode_owned()
267        }
268    }
269}
270
271/// A parsed class file, borrowing from the bytes it was read from.
272///
273/// See [JVMS §4.1](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1).
274#[derive(Debug)]
275pub struct Class<'a> {
276    pub minor_version: u16,
277    pub major_version: u16,
278    pub constant_pool: ConstantPool<'a>,
279    pub access_flags: ClassAccessFlags,
280    pub this_class: CpIndex,
281    pub super_class: Option<CpIndex>,
282    pub interfaces: Vec<CpIndex>,
283    pub fields: Vec<FieldInfo<'a>>,
284    pub methods: Vec<MethodInfo<'a>>,
285    pub attributes: Vec<AttributeInfo<'a>>,
286}
287
288impl<'a> Class<'a> {
289    /// Parses a complete class file, rejecting any trailing bytes.
290    pub fn parse(bytes: &'a [u8]) -> Result<Self> {
291        let mut cursor = Cursor::new(bytes);
292        let class = Self::try_read(&mut cursor)?;
293
294        if !cursor.is_empty() {
295            return Err(Error::TrailingBytes {
296                count: cursor.len(),
297            });
298        }
299
300        Ok(class)
301    }
302
303    /// Reads a class file from `cursor`, leaving whatever follows it untouched.
304    pub fn try_read(cursor: &mut Cursor<'a>) -> Result<Self> {
305        let magic = cursor.read_u32()?;
306
307        if magic != MAGIC {
308            return Err(Error::InvalidMagic { magic });
309        }
310
311        let minor_version = cursor.read_u16()?;
312        let major_version = cursor.read_u16()?;
313
314        if !(MIN_MAJOR_VERSION..=MAX_MAJOR_VERSION).contains(&major_version) {
315            return Err(Error::UnsupportedVersion {
316                major_version,
317                minor_version,
318                supported: MIN_MAJOR_VERSION..=MAX_MAJOR_VERSION,
319            });
320        }
321
322        let constant_pool = ConstantPool::try_read(cursor)?;
323
324        let access_flags = ClassAccessFlags::from_bits_truncate(cursor.read_u16()?);
325
326        let this_class = cursor.read_u16()?;
327        constant_pool.class_name_index(this_class)?;
328
329        let super_class = cursor.read_optional_index()?;
330        if let Some(super_class) = super_class {
331            constant_pool.class_name_index(super_class)?;
332        }
333
334        let interfaces = cursor.read_indices()?;
335        for &interface in &interfaces {
336            constant_pool.class_name_index(interface)?;
337        }
338
339        let fields = cursor.repeat_by_u16(|cursor| FieldInfo::try_read(&constant_pool, cursor))?;
340        let methods =
341            cursor.repeat_by_u16(|cursor| MethodInfo::try_read(&constant_pool, cursor))?;
342        let attributes = AttributeInfo::try_read_all(&constant_pool, cursor)?;
343
344        Ok(Self {
345            minor_version,
346            major_version,
347            constant_pool,
348            access_flags,
349            this_class,
350            super_class,
351            interfaces,
352            fields,
353            methods,
354            attributes,
355        })
356    }
357
358    /// Returns the internal name of this class, e.g. `java/lang/String`.
359    pub fn name(&self) -> Result<Cow<'a, str>> {
360        let name_index = self.constant_pool.class_name_index(self.this_class)?;
361
362        self.constant_pool.str(name_index)
363    }
364
365    /// Returns the generic signature from the `Signature` attribute, if present.
366    pub fn signature(&self, constant_pool: &ConstantPool<'a>) -> Result<Option<Cow<'a, str>>> {
367        let signature_index = match self
368            .attributes
369            .iter()
370            .find(|attr| matches!(attr, AttributeInfo::Signature { .. }))
371        {
372            Some(AttributeInfo::Signature { signature_index }) => *signature_index,
373            Some(_) => unreachable!(),
374            None => return Ok(None),
375        };
376
377        Ok(Some(constant_pool.str(signature_index)?))
378    }
379
380    /// Returns the `InnerClasses` attribute entries, if present.
381    pub fn inner_classes(&self) -> Result<Option<&Vec<InnerClass>>> {
382        for attribute in &self.attributes {
383            match attribute {
384                AttributeInfo::InnerClasses { classes } => return Ok(Some(classes)),
385                _ => continue,
386            }
387        }
388
389        Ok(None)
390    }
391}
392
393/// Constant pool of a class file, indexed from 1.
394///
395/// `Long` and `Double` constants take two slots; the second one is unusable.
396///
397/// See [JVMS §4.4](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4).
398pub struct ConstantPool<'a> {
399    slots: Vec<Option<ConstantPoolInfo<'a>>>,
400}
401
402impl<'a> ConstantPool<'a> {
403    /// Reads the whole pool, including the padding slots of wide constants.
404    fn try_read(cursor: &mut Cursor<'a>) -> Result<Self> {
405        let count = cursor.read_u16()?;
406
407        if count < 1 {
408            return Err(Error::InvalidConstantPoolCount { count });
409        }
410
411        let count = count as usize;
412        let mut slots = Vec::with_capacity(count);
413        slots.push(None);
414
415        while slots.len() < count {
416            let index = slots.len() as CpIndex;
417            let entry = ConstantPoolInfo::try_read(cursor)?;
418            let width = entry.slot_width();
419
420            slots.push(Some(entry));
421
422            if width == 2 {
423                if slots.len() >= count {
424                    return Err(Error::ConstantPoolOverflow { index });
425                }
426
427                slots.push(None);
428            }
429        }
430
431        Ok(Self { slots })
432    }
433
434    /// Returns the number of slots, including the reserved slot 0.
435    pub fn len(&self) -> usize {
436        self.slots.len()
437    }
438
439    /// Returns `true` if the pool holds no entries.
440    pub fn is_empty(&self) -> bool {
441        self.slots.len() <= 1
442    }
443
444    /// Iterates over the usable entries together with their indices.
445    pub fn iter(&self) -> impl Iterator<Item = (CpIndex, &ConstantPoolInfo<'a>)> {
446        self.slots
447            .iter()
448            .enumerate()
449            .filter_map(|(index, entry)| Some((index as CpIndex, entry.as_ref()?)))
450    }
451
452    /// Returns the entry at `index`.
453    pub fn get(&self, index: CpIndex) -> Result<&ConstantPoolInfo<'a>> {
454        match self.slots.get(index as usize) {
455            Some(Some(entry)) => Ok(entry),
456            Some(None) if index != 0 => Err(Error::ConstantPoolIndexUnusable { index }),
457            _ => Err(Error::ConstantPoolIndexOutOfBounds {
458                index,
459                count: self.slots.len(),
460            }),
461        }
462    }
463
464    /// Returns the name index of the `Class` entry at `index`.
465    pub fn class_name_index(&self, index: CpIndex) -> Result<CpIndex> {
466        match self.get(index)? {
467            ConstantPoolInfo::Class { name_index } => Ok(*name_index),
468            found => Err(unexpected_entry(index, "Class", found)),
469        }
470    }
471
472    /// Returns the raw bytes of the `Utf8` entry at `index`.
473    pub fn utf8(&self, index: CpIndex) -> Result<Cesu8Bytes<'a>> {
474        match self.get(index)? {
475            ConstantPoolInfo::Utf8 { bytes } => Ok(*bytes),
476            found => Err(unexpected_entry(index, "Utf8", found)),
477        }
478    }
479
480    /// Returns the decoded `Utf8` entry at `index`.
481    pub fn str(&self, index: CpIndex) -> Result<Cow<'a, str>> {
482        self.utf8(index)?.decode()
483    }
484
485    /// Returns the name and descriptor indices of the `NameAndType` entry at `index`.
486    pub fn name_and_type(&self, index: CpIndex) -> Result<(CpIndex, CpIndex)> {
487        match self.get(index)? {
488            ConstantPoolInfo::NameAndType {
489                name_index,
490                descriptor_index,
491            } => Ok((*name_index, *descriptor_index)),
492            found => Err(unexpected_entry(index, "NameAndType", found)),
493        }
494    }
495
496    /// Checks that `index` refers to an entry usable as a `ConstantValue`.
497    pub fn expect_constant_value(&self, index: CpIndex) -> Result<()> {
498        match self.get(index)? {
499            ConstantPoolInfo::Integer { .. }
500            | ConstantPoolInfo::Float { .. }
501            | ConstantPoolInfo::Long { .. }
502            | ConstantPoolInfo::Double { .. }
503            | ConstantPoolInfo::String { .. } => Ok(()),
504            found => Err(unexpected_entry(
505                index,
506                "Integer, Float, Long, Double or String",
507                found,
508            )),
509        }
510    }
511}
512
513/// Builds the error for an entry of the wrong kind.
514fn unexpected_entry(index: CpIndex, expected: &'static str, found: &ConstantPoolInfo<'_>) -> Error {
515    Error::UnexpectedConstantPoolEntry {
516        index,
517        expected,
518        found: found.kind(),
519    }
520}
521
522impl Debug for ConstantPool<'_> {
523    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
524        f.debug_map().entries(self.iter()).finish()
525    }
526}
527
528/// A single constant pool entry.
529///
530/// See [JVMS §4.4](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4).
531#[derive(Debug)]
532pub enum ConstantPoolInfo<'a> {
533    Utf8 {
534        bytes: Cesu8Bytes<'a>,
535    },
536    Integer {
537        value: i32,
538    },
539    Float {
540        value: f32,
541    },
542    Long {
543        value: i64,
544    },
545    Double {
546        value: f64,
547    },
548    Class {
549        name_index: CpIndex,
550    },
551    String {
552        string_index: CpIndex,
553    },
554    FieldRef {
555        class_index: CpIndex,
556        name_and_type_index: CpIndex,
557    },
558    MethodRef {
559        class_index: CpIndex,
560        name_and_type_index: CpIndex,
561    },
562    InterfaceMethodRef {
563        class_index: CpIndex,
564        name_and_type_index: CpIndex,
565    },
566    NameAndType {
567        name_index: CpIndex,
568        descriptor_index: CpIndex,
569    },
570    MethodHandle {
571        reference_kind: ReferenceKind,
572        reference_index: CpIndex,
573    },
574    MethodType {
575        descriptor_index: CpIndex,
576    },
577    InvokeDynamic {
578        bootstrap_method_attr_index: u16,
579        name_and_type_index: CpIndex,
580    },
581}
582
583impl<'a> ConstantPoolInfo<'a> {
584    /// Reads a tag byte and the entry body that follows it.
585    fn try_read(cursor: &mut Cursor<'a>) -> Result<Self> {
586        let info = match ConstantPoolTag::try_read(cursor)? {
587            ConstantPoolTag::Utf8 => {
588                let length = cursor.read_u16()? as usize;
589
590                Self::Utf8 {
591                    bytes: Cesu8Bytes::from(cursor.read_bytes(length)?),
592                }
593            }
594            ConstantPoolTag::Integer => Self::Integer {
595                value: cursor.read_i32()?,
596            },
597            ConstantPoolTag::Float => Self::Float {
598                value: cursor.read_f32()?,
599            },
600            ConstantPoolTag::Long => Self::Long {
601                value: cursor.read_i64()?,
602            },
603            ConstantPoolTag::Double => Self::Double {
604                value: cursor.read_f64()?,
605            },
606            ConstantPoolTag::Class => Self::Class {
607                name_index: cursor.read_u16()?,
608            },
609            ConstantPoolTag::String => Self::String {
610                string_index: cursor.read_u16()?,
611            },
612            ConstantPoolTag::FieldRef => Self::FieldRef {
613                class_index: cursor.read_u16()?,
614                name_and_type_index: cursor.read_u16()?,
615            },
616            ConstantPoolTag::MethodRef => Self::MethodRef {
617                class_index: cursor.read_u16()?,
618                name_and_type_index: cursor.read_u16()?,
619            },
620            ConstantPoolTag::InterfaceMethodRef => Self::InterfaceMethodRef {
621                class_index: cursor.read_u16()?,
622                name_and_type_index: cursor.read_u16()?,
623            },
624            ConstantPoolTag::NameAndType => Self::NameAndType {
625                name_index: cursor.read_u16()?,
626                descriptor_index: cursor.read_u16()?,
627            },
628            ConstantPoolTag::MethodHandle => Self::MethodHandle {
629                reference_kind: ReferenceKind::try_read(cursor)?,
630                reference_index: cursor.read_u16()?,
631            },
632            ConstantPoolTag::MethodType => Self::MethodType {
633                descriptor_index: cursor.read_u16()?,
634            },
635            ConstantPoolTag::InvokeDynamic => Self::InvokeDynamic {
636                bootstrap_method_attr_index: cursor.read_u16()?,
637                name_and_type_index: cursor.read_u16()?,
638            },
639        };
640
641        Ok(info)
642    }
643
644    /// Number of pool slots this entry occupies (2 for `Long` and `Double`).
645    pub fn slot_width(&self) -> u16 {
646        match self {
647            Self::Long { .. } | Self::Double { .. } => 2,
648            _ => 1,
649        }
650    }
651
652    /// Name of this entry's kind, used in error messages.
653    pub fn kind(&self) -> &'static str {
654        match self {
655            Self::Utf8 { .. } => "Utf8",
656            Self::Integer { .. } => "Integer",
657            Self::Float { .. } => "Float",
658            Self::Long { .. } => "Long",
659            Self::Double { .. } => "Double",
660            Self::Class { .. } => "Class",
661            Self::String { .. } => "String",
662            Self::FieldRef { .. } => "FieldRef",
663            Self::MethodRef { .. } => "MethodRef",
664            Self::InterfaceMethodRef { .. } => "InterfaceMethodRef",
665            Self::NameAndType { .. } => "NameAndType",
666            Self::MethodHandle { .. } => "MethodHandle",
667            Self::MethodType { .. } => "MethodType",
668            Self::InvokeDynamic { .. } => "InvokeDynamic",
669        }
670    }
671}
672
673/// Tag byte identifying the kind of a [`ConstantPoolInfo`].
674///
675/// See [JVMS §4.4, Table 4.4-A](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4).
676#[derive(Debug, Clone, Copy, PartialEq, Eq)]
677#[repr(u8)]
678pub enum ConstantPoolTag {
679    Utf8 = 1,
680    Integer = 3,
681    Float = 4,
682    Long = 5,
683    Double = 6,
684    Class = 7,
685    String = 8,
686    FieldRef = 9,
687    MethodRef = 10,
688    InterfaceMethodRef = 11,
689    NameAndType = 12,
690    MethodHandle = 15,
691    MethodType = 16,
692    InvokeDynamic = 18,
693}
694
695impl TryFrom<u8> for ConstantPoolTag {
696    type Error = Error;
697
698    fn try_from(value: u8) -> Result<Self> {
699        let tag = match value {
700            1 => Self::Utf8,
701            3 => Self::Integer,
702            4 => Self::Float,
703            5 => Self::Long,
704            6 => Self::Double,
705            7 => Self::Class,
706            8 => Self::String,
707            9 => Self::FieldRef,
708            10 => Self::MethodRef,
709            11 => Self::InterfaceMethodRef,
710            12 => Self::NameAndType,
711            15 => Self::MethodHandle,
712            16 => Self::MethodType,
713            18 => Self::InvokeDynamic,
714            tag => return Err(Error::InvalidConstantPoolTag { tag }),
715        };
716
717        Ok(tag)
718    }
719}
720
721impl ConstantPoolTag {
722    /// Reads and validates a tag byte.
723    pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
724        Self::try_from(cursor.read_u8()?)
725    }
726}
727
728/// Kind of a `MethodHandle` reference.
729///
730/// See [JVMS §4.4.8](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.8).
731#[derive(Debug, Clone, Copy, PartialEq, Eq)]
732#[repr(u8)]
733pub enum ReferenceKind {
734    GetField = 1,
735    GetStatic = 2,
736    PutField = 3,
737    PutStatic = 4,
738    InvokeVirtual = 5,
739    InvokeStatic = 6,
740    InvokeSpecial = 7,
741    NewInvokeSpecial = 8,
742    InvokeInterface = 9,
743}
744
745impl TryFrom<u8> for ReferenceKind {
746    type Error = Error;
747
748    fn try_from(value: u8) -> Result<Self> {
749        let kind = match value {
750            1 => Self::GetField,
751            2 => Self::GetStatic,
752            3 => Self::PutField,
753            4 => Self::PutStatic,
754            5 => Self::InvokeVirtual,
755            6 => Self::InvokeStatic,
756            7 => Self::InvokeSpecial,
757            8 => Self::NewInvokeSpecial,
758            9 => Self::InvokeInterface,
759            kind => return Err(Error::InvalidReferenceKind { kind }),
760        };
761
762        Ok(kind)
763    }
764}
765
766impl ReferenceKind {
767    /// Reads and validates a reference kind byte.
768    pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
769        Self::try_from(cursor.read_u8()?)
770    }
771}
772
773bitflags::bitflags! {
774    /// Access flags of a class.
775    ///
776    /// See [JVMS §4.1, Table 4.1-A](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1).
777    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
778    pub struct ClassAccessFlags: u16 {
779        const PUBLIC = 0x0001;
780        const FINAL = 0x0010;
781        const SUPER = 0x0020;
782        const INTERFACE = 0x0200;
783        const ABSTRACT = 0x0400;
784        const SYNTHETIC = 0x1000;
785        const ANNOTATION = 0x2000;
786        const ENUM = 0x4000;
787    }
788}
789
790mod field {
791    use std::borrow::Cow;
792
793    use crate::{
794        AttributeInfo, ConstantPool, CpIndex,
795        cursor::{Cursor, ReadBytes},
796        error::Result,
797    };
798
799    /// A single field of a class.
800    ///
801    /// See [JVMS §4.5](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5).
802    #[derive(Debug)]
803    pub struct FieldInfo<'a> {
804        pub access_flags: FieldAccessFlags,
805        pub name_index: CpIndex,
806        pub descriptor_index: CpIndex,
807        pub attributes: Vec<AttributeInfo<'a>>,
808    }
809
810    impl<'a> FieldInfo<'a> {
811        /// Reads one field, validating its constant pool references.
812        pub fn try_read(constant_pool: &ConstantPool<'a>, cursor: &mut Cursor<'a>) -> Result<Self> {
813            let access_flags = FieldAccessFlags::from_bits_truncate(cursor.read_u16()?);
814
815            let name_index = cursor.read_u16()?;
816            constant_pool.utf8(name_index)?;
817
818            let descriptor_index = cursor.read_u16()?;
819            constant_pool.utf8(descriptor_index)?;
820
821            Ok(Self {
822                access_flags,
823                name_index,
824                descriptor_index,
825                attributes: AttributeInfo::try_read_all(constant_pool, cursor)?,
826            })
827        }
828
829        /// Returns the generic signature from the `Signature` attribute, if present.
830        pub fn signature(&self, constant_pool: &ConstantPool<'a>) -> Result<Option<Cow<'a, str>>> {
831            let signature_index = match self
832                .attributes
833                .iter()
834                .find(|attr| matches!(attr, AttributeInfo::Signature { .. }))
835            {
836                Some(AttributeInfo::Signature { signature_index }) => *signature_index,
837                Some(_) => unreachable!(),
838                None => return Ok(None),
839            };
840
841            Ok(Some(constant_pool.str(signature_index)?))
842        }
843
844        /// Returns the field name.
845        pub fn name(&self, constant_pool: &ConstantPool<'a>) -> Result<Cow<'a, str>> {
846            constant_pool.str(self.name_index)
847        }
848
849        /// Returns the field type descriptor.
850        pub fn descriptor(&self, constant_pool: &ConstantPool<'a>) -> Result<Cow<'a, str>> {
851            constant_pool.str(self.descriptor_index)
852        }
853    }
854
855    bitflags::bitflags! {
856        /// Access flags of a field.
857        ///
858        /// See [JVMS §4.5, Table 4.5-A](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5).
859        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
860        pub struct FieldAccessFlags: u16 {
861            const PUBLIC = 0x0001;
862            const PRIVATE = 0x0002;
863            const PROTECTED = 0x0004;
864            const STATIC = 0x0008;
865            const FINAL = 0x0010;
866            const VOLATILE = 0x0040;
867            const TRANSIENT = 0x0080;
868            const SYNTHETIC = 0x1000;
869            const ENUM = 0x4000;
870        }
871    }
872}
873
874mod method {
875    use std::borrow::Cow;
876
877    use crate::{
878        AttributeInfo, ConstantPool, CpIndex,
879        cursor::{Cursor, ReadBytes},
880        error::Result,
881    };
882
883    /// A single method of a class.
884    ///
885    /// See [JVMS §4.6](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6).
886    #[derive(Debug)]
887    pub struct MethodInfo<'a> {
888        pub access_flags: MethodAccessFlags,
889        pub name_index: CpIndex,
890        pub descriptor_index: CpIndex,
891        pub attributes: Vec<AttributeInfo<'a>>,
892    }
893
894    impl<'a> MethodInfo<'a> {
895        /// Reads one method, validating its constant pool references.
896        pub fn try_read(constant_pool: &ConstantPool<'a>, cursor: &mut Cursor<'a>) -> Result<Self> {
897            let access_flags = MethodAccessFlags::from_bits_truncate(cursor.read_u16()?);
898
899            let name_index = cursor.read_u16()?;
900            constant_pool.utf8(name_index)?;
901
902            let descriptor_index = cursor.read_u16()?;
903            constant_pool.utf8(descriptor_index)?;
904
905            Ok(Self {
906                access_flags,
907                name_index,
908                descriptor_index,
909                attributes: AttributeInfo::try_read_all(constant_pool, cursor)?,
910            })
911        }
912
913        /// Returns the generic signature from the `Signature` attribute, if present.
914        pub fn signature(&self, constant_pool: &ConstantPool<'a>) -> Result<Option<Cow<'a, str>>> {
915            let signature_index = match self
916                .attributes
917                .iter()
918                .find(|attr| matches!(attr, AttributeInfo::Signature { .. }))
919            {
920                Some(AttributeInfo::Signature { signature_index }) => *signature_index,
921                Some(_) => unreachable!(),
922                None => return Ok(None),
923            };
924
925            Ok(Some(constant_pool.str(signature_index)?))
926        }
927
928        /// Returns the method name.
929        pub fn name(&self, constant_pool: &ConstantPool<'a>) -> Result<Cow<'a, str>> {
930            constant_pool.str(self.name_index)
931        }
932
933        /// Returns the method descriptor.
934        pub fn descriptor(&self, constant_pool: &ConstantPool<'a>) -> Result<Cow<'a, str>> {
935            constant_pool.str(self.descriptor_index)
936        }
937    }
938
939    bitflags::bitflags! {
940        /// Access flags of a method.
941        ///
942        /// See [JVMS §4.6, Table 4.6-A](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6).
943        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
944        pub struct MethodAccessFlags: u16 {
945            const PUBLIC = 0x0001;
946            const PRIVATE = 0x0002;
947            const PROTECTED = 0x0004;
948            const STATIC = 0x0008;
949            const FINAL = 0x0010;
950            const SYNCHRONIZED = 0x0020;
951            const BRIDGE = 0x0040;
952            const VARARGS = 0x0080;
953            const NATIVE = 0x0100;
954            const ABSTRACT = 0x0400;
955            const STRICT = 0x0800;
956            const SYNTHETIC = 0x1000;
957        }
958    }
959}
960
961mod attribute {
962    use crate::{
963        ConstantPool, CpIndex,
964        cursor::{Cursor, ReadBytes},
965        error::{Error, Result},
966    };
967
968    /// An attribute of a class, field, method or `Code` block.
969    ///
970    /// Attributes this crate does not model are kept raw as [`AttributeInfo::Unknown`].
971    ///
972    /// See [JVMS §4.7](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7).
973    #[derive(Debug)]
974    pub enum AttributeInfo<'a> {
975        SourceFile {
976            sourcefile_index: CpIndex,
977        },
978        InnerClasses {
979            classes: Vec<InnerClass>,
980        },
981        EnclosingMethod {
982            class_index: CpIndex,
983            method_index: Option<CpIndex>,
984        },
985        SourceDebugExtension {
986            debug_extension: &'a [u8],
987        },
988        BootstrapMethods {
989            bootstrap_methods: Vec<BootstrapMethod>,
990        },
991        ConstantValue {
992            constantvalue_index: CpIndex,
993        },
994        Code {
995            max_stack: u16,
996            max_locals: u16,
997            code: &'a [u8],
998            exception_table: Vec<ExceptionHandler>,
999            attributes: Vec<AttributeInfo<'a>>,
1000        },
1001        Exceptions {
1002            exception_index_table: Vec<CpIndex>,
1003        },
1004        RuntimeVisibleParameterAnnotations {
1005            parameter_annotations: Vec<Vec<Annotation>>,
1006        },
1007        RuntimeInvisibleParameterAnnotations {
1008            parameter_annotations: Vec<Vec<Annotation>>,
1009        },
1010        AnnotationDefault {
1011            default_value: ElementValue,
1012        },
1013        MethodParameters {
1014            parameters: Vec<MethodParameter>,
1015        },
1016        Synthetic,
1017        Deprecated,
1018        Signature {
1019            signature_index: CpIndex,
1020        },
1021        RuntimeVisibleAnnotations {
1022            annotations: Vec<Annotation>,
1023        },
1024        RuntimeInvisibleAnnotations {
1025            annotations: Vec<Annotation>,
1026        },
1027        LineNumberTable {
1028            line_number_table: Vec<LineNumber>,
1029        },
1030        LocalVariableTable {
1031            local_variable_table: Vec<LocalVariable>,
1032        },
1033        LocalVariableTypeTable {
1034            local_variable_type_table: Vec<LocalVariableType>,
1035        },
1036        StackMapTable {
1037            entries: Vec<StackMapFrame>,
1038        },
1039        RuntimeVisibleTypeAnnotations {
1040            annotations: Vec<TypeAnnotation>,
1041        },
1042        RuntimeInvisibleTypeAnnotations {
1043            annotations: Vec<TypeAnnotation>,
1044        },
1045        Unknown {
1046            name: String,
1047            info: &'a [u8],
1048        },
1049    }
1050
1051    impl<'a> AttributeInfo<'a> {
1052        /// Reads a `u16`-prefixed attribute table.
1053        pub fn try_read_all(
1054            constant_pool: &ConstantPool<'a>,
1055            cursor: &mut Cursor<'a>,
1056        ) -> Result<Vec<Self>> {
1057            cursor.repeat_by_u16(|cursor| Self::try_read(constant_pool, cursor))
1058        }
1059
1060        /// Reads one attribute, checking that its body matches the declared length.
1061        pub fn try_read(constant_pool: &ConstantPool<'a>, cursor: &mut Cursor<'a>) -> Result<Self> {
1062            let attribute_name_index = cursor.read_u16()?;
1063            let name = constant_pool.utf8(attribute_name_index)?;
1064
1065            let attribute_length = cursor.read_u32()? as usize;
1066
1067            let mut info = Cursor::new(cursor.read_bytes(attribute_length)?);
1068
1069            let attribute = Self::try_read_body(constant_pool, name.as_bytes(), &mut info)?;
1070
1071            if !info.is_empty() {
1072                return Err(Error::AttributeLengthMismatch {
1073                    name: name.decode_owned()?,
1074                    expected: attribute_length,
1075                    consumed: attribute_length - info.len(),
1076                });
1077            }
1078
1079            Ok(attribute)
1080        }
1081
1082        /// Reads the body of an attribute picked by its `name`.
1083        fn try_read_body(
1084            constant_pool: &ConstantPool<'a>,
1085            name: &[u8],
1086            cursor: &mut Cursor<'a>,
1087        ) -> Result<Self> {
1088            let attribute = match name {
1089                b"SourceFile" => {
1090                    let sourcefile_index = cursor.read_u16()?;
1091                    constant_pool.utf8(sourcefile_index)?;
1092
1093                    Self::SourceFile { sourcefile_index }
1094                }
1095                b"InnerClasses" => Self::InnerClasses {
1096                    classes: cursor
1097                        .repeat_by_u16(|cursor| InnerClass::try_read(constant_pool, cursor))?,
1098                },
1099                b"EnclosingMethod" => {
1100                    let class_index = cursor.read_u16()?;
1101                    constant_pool.class_name_index(class_index)?;
1102
1103                    let method_index = cursor.read_optional_index()?;
1104                    if let Some(method_index) = method_index {
1105                        constant_pool.name_and_type(method_index)?;
1106                    }
1107
1108                    Self::EnclosingMethod {
1109                        class_index,
1110                        method_index,
1111                    }
1112                }
1113                b"SourceDebugExtension" => Self::SourceDebugExtension {
1114                    debug_extension: cursor.read_rest(),
1115                },
1116                b"BootstrapMethods" => Self::BootstrapMethods {
1117                    bootstrap_methods: cursor.repeat_by_u16(BootstrapMethod::try_read)?,
1118                },
1119                b"ConstantValue" => {
1120                    let constantvalue_index = cursor.read_u16()?;
1121                    constant_pool.expect_constant_value(constantvalue_index)?;
1122
1123                    Self::ConstantValue {
1124                        constantvalue_index,
1125                    }
1126                }
1127                b"Code" => {
1128                    let max_stack = cursor.read_u16()?;
1129                    let max_locals = cursor.read_u16()?;
1130
1131                    let code_length = cursor.read_u32()? as usize;
1132                    let code = cursor.read_bytes(code_length)?;
1133
1134                    let exception_table = cursor.repeat_by_u16(|cursor| {
1135                        ExceptionHandler::try_read(constant_pool, cursor)
1136                    })?;
1137
1138                    Self::Code {
1139                        max_stack,
1140                        max_locals,
1141                        code,
1142                        exception_table,
1143                        attributes: Self::try_read_all(constant_pool, cursor)?,
1144                    }
1145                }
1146                b"Exceptions" => {
1147                    let exception_index_table = cursor.read_indices()?;
1148                    for &index in &exception_index_table {
1149                        constant_pool.class_name_index(index)?;
1150                    }
1151
1152                    Self::Exceptions {
1153                        exception_index_table,
1154                    }
1155                }
1156                b"RuntimeVisibleParameterAnnotations" => Self::RuntimeVisibleParameterAnnotations {
1157                    parameter_annotations: cursor
1158                        .repeat_by_u8(|cursor| cursor.repeat_by_u16(Annotation::try_read))?,
1159                },
1160                b"RuntimeInvisibleParameterAnnotations" => {
1161                    Self::RuntimeInvisibleParameterAnnotations {
1162                        parameter_annotations: cursor
1163                            .repeat_by_u8(|cursor| cursor.repeat_by_u16(Annotation::try_read))?,
1164                    }
1165                }
1166                b"AnnotationDefault" => Self::AnnotationDefault {
1167                    default_value: ElementValue::try_read(cursor)?,
1168                },
1169                b"MethodParameters" => Self::MethodParameters {
1170                    parameters: cursor
1171                        .repeat_by_u8(|cursor| MethodParameter::try_read(constant_pool, cursor))?,
1172                },
1173                b"Synthetic" => Self::Synthetic,
1174                b"Deprecated" => Self::Deprecated,
1175                b"Signature" => {
1176                    let signature_index = cursor.read_u16()?;
1177                    constant_pool.utf8(signature_index)?;
1178
1179                    Self::Signature { signature_index }
1180                }
1181                b"RuntimeVisibleAnnotations" => Self::RuntimeVisibleAnnotations {
1182                    annotations: cursor.repeat_by_u16(Annotation::try_read)?,
1183                },
1184                b"RuntimeInvisibleAnnotations" => Self::RuntimeInvisibleAnnotations {
1185                    annotations: cursor.repeat_by_u16(Annotation::try_read)?,
1186                },
1187                b"LineNumberTable" => Self::LineNumberTable {
1188                    line_number_table: cursor.repeat_by_u16(LineNumber::try_read)?,
1189                },
1190                b"LocalVariableTable" => Self::LocalVariableTable {
1191                    local_variable_table: cursor
1192                        .repeat_by_u16(|cursor| LocalVariable::try_read(constant_pool, cursor))?,
1193                },
1194                b"LocalVariableTypeTable" => Self::LocalVariableTypeTable {
1195                    local_variable_type_table: cursor.repeat_by_u16(|cursor| {
1196                        LocalVariableType::try_read(constant_pool, cursor)
1197                    })?,
1198                },
1199                b"StackMapTable" => Self::StackMapTable {
1200                    entries: cursor.repeat_by_u16(StackMapFrame::try_read)?,
1201                },
1202                b"RuntimeVisibleTypeAnnotations" => Self::RuntimeVisibleTypeAnnotations {
1203                    annotations: cursor.repeat_by_u16(TypeAnnotation::try_read)?,
1204                },
1205                b"RuntimeInvisibleTypeAnnotations" => Self::RuntimeInvisibleTypeAnnotations {
1206                    annotations: cursor.repeat_by_u16(TypeAnnotation::try_read)?,
1207                },
1208                _ => Self::Unknown {
1209                    name: String::from_utf8_lossy(name).into_owned(),
1210                    info: cursor.read_rest(),
1211                },
1212            };
1213
1214            Ok(attribute)
1215        }
1216    }
1217
1218    /// One entry of a `Code` exception table.
1219    ///
1220    /// See [JVMS §4.7.3](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.3).
1221    #[derive(Debug)]
1222    pub struct ExceptionHandler {
1223        pub start_pc: u16,
1224        pub end_pc: u16,
1225        pub handler_pc: u16,
1226        pub catch_type: Option<CpIndex>,
1227    }
1228
1229    impl ExceptionHandler {
1230        /// Reads one handler; `catch_type` of `0` means "catch anything".
1231        pub fn try_read(constant_pool: &ConstantPool<'_>, cursor: &mut Cursor<'_>) -> Result<Self> {
1232            let start_pc = cursor.read_u16()?;
1233            let end_pc = cursor.read_u16()?;
1234            let handler_pc = cursor.read_u16()?;
1235
1236            let catch_type = cursor.read_optional_index()?;
1237            if let Some(catch_type) = catch_type {
1238                constant_pool.class_name_index(catch_type)?;
1239            }
1240
1241            Ok(Self {
1242                start_pc,
1243                end_pc,
1244                handler_pc,
1245                catch_type,
1246            })
1247        }
1248    }
1249
1250    /// One frame of a `StackMapTable` attribute.
1251    ///
1252    /// See [JVMS §4.7.4](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.4).
1253    #[derive(Debug)]
1254    pub enum StackMapFrame {
1255        Same {
1256            offset_delta: u16,
1257        },
1258        SameLocals1StackItem {
1259            offset_delta: u16,
1260            stack: VerificationTypeInfo,
1261        },
1262        SameLocals1StackItemExtended {
1263            offset_delta: u16,
1264            stack: VerificationTypeInfo,
1265        },
1266        Chop {
1267            offset_delta: u16,
1268            absent_locals: u8,
1269        },
1270        SameExtended {
1271            offset_delta: u16,
1272        },
1273        Append {
1274            offset_delta: u16,
1275            locals: Vec<VerificationTypeInfo>,
1276        },
1277        Full {
1278            offset_delta: u16,
1279            locals: Vec<VerificationTypeInfo>,
1280            stack: Vec<VerificationTypeInfo>,
1281        },
1282    }
1283
1284    impl StackMapFrame {
1285        /// Reads one frame, whose shape is encoded in its leading tag byte.
1286        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1287            let tag = cursor.read_u8()?;
1288
1289            let frame = match tag {
1290                0..=63 => Self::Same {
1291                    offset_delta: tag as u16,
1292                },
1293                64..=127 => Self::SameLocals1StackItem {
1294                    offset_delta: (tag - 64) as u16,
1295                    stack: VerificationTypeInfo::try_read(cursor)?,
1296                },
1297                247 => Self::SameLocals1StackItemExtended {
1298                    offset_delta: cursor.read_u16()?,
1299                    stack: VerificationTypeInfo::try_read(cursor)?,
1300                },
1301                248..=250 => Self::Chop {
1302                    offset_delta: cursor.read_u16()?,
1303                    absent_locals: 251 - tag,
1304                },
1305                251 => Self::SameExtended {
1306                    offset_delta: cursor.read_u16()?,
1307                },
1308                252..=254 => {
1309                    let offset_delta = cursor.read_u16()?;
1310                    let locals = (0..tag - 251)
1311                        .map(|_| VerificationTypeInfo::try_read(cursor))
1312                        .collect::<Result<Vec<_>>>()?;
1313
1314                    Self::Append {
1315                        offset_delta,
1316                        locals,
1317                    }
1318                }
1319                255 => Self::Full {
1320                    offset_delta: cursor.read_u16()?,
1321                    locals: cursor.repeat_by_u16(VerificationTypeInfo::try_read)?,
1322                    stack: cursor.repeat_by_u16(VerificationTypeInfo::try_read)?,
1323                },
1324                tag => return Err(Error::InvalidStackMapFrameTag { tag }),
1325            };
1326
1327            Ok(frame)
1328        }
1329    }
1330
1331    /// Type of a local variable or stack slot inside a stack map frame.
1332    ///
1333    /// See [JVMS §4.7.4](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.4).
1334    #[derive(Debug)]
1335    pub enum VerificationTypeInfo {
1336        Top,
1337        Integer,
1338        Float,
1339        Double,
1340        Long,
1341        Null,
1342        UninitializedThis,
1343        Object { cpool_index: CpIndex },
1344        Uninitialized { offset: u16 },
1345    }
1346
1347    impl VerificationTypeInfo {
1348        /// Reads a tag byte and the payload it implies.
1349        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1350            let info = match VerificationTypeTag::try_read(cursor)? {
1351                VerificationTypeTag::Top => Self::Top,
1352                VerificationTypeTag::Integer => Self::Integer,
1353                VerificationTypeTag::Float => Self::Float,
1354                VerificationTypeTag::Double => Self::Double,
1355                VerificationTypeTag::Long => Self::Long,
1356                VerificationTypeTag::Null => Self::Null,
1357                VerificationTypeTag::UninitializedThis => Self::UninitializedThis,
1358                VerificationTypeTag::Object => Self::Object {
1359                    cpool_index: cursor.read_u16()?,
1360                },
1361                VerificationTypeTag::Uninitialized => Self::Uninitialized {
1362                    offset: cursor.read_u16()?,
1363                },
1364            };
1365
1366            Ok(info)
1367        }
1368    }
1369
1370    /// Tag byte identifying the kind of a [`VerificationTypeInfo`].
1371    ///
1372    /// See [JVMS §4.7.4](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.4).
1373    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1374    #[repr(u8)]
1375    pub enum VerificationTypeTag {
1376        Top = 0,
1377        Integer = 1,
1378        Float = 2,
1379        Double = 3,
1380        Long = 4,
1381        Null = 5,
1382        UninitializedThis = 6,
1383        Object = 7,
1384        Uninitialized = 8,
1385    }
1386
1387    impl TryFrom<u8> for VerificationTypeTag {
1388        type Error = Error;
1389
1390        fn try_from(value: u8) -> Result<Self> {
1391            let tag = match value {
1392                0 => Self::Top,
1393                1 => Self::Integer,
1394                2 => Self::Float,
1395                3 => Self::Double,
1396                4 => Self::Long,
1397                5 => Self::Null,
1398                6 => Self::UninitializedThis,
1399                7 => Self::Object,
1400                8 => Self::Uninitialized,
1401                tag => return Err(Error::InvalidVerificationTypeTag { tag }),
1402            };
1403
1404            Ok(tag)
1405        }
1406    }
1407
1408    impl VerificationTypeTag {
1409        /// Reads and validates a tag byte.
1410        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1411            Self::try_from(cursor.read_u8()?)
1412        }
1413    }
1414
1415    /// One entry of an `InnerClasses` attribute.
1416    ///
1417    /// See [JVMS §4.7.6](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6).
1418    #[derive(Debug)]
1419    pub struct InnerClass {
1420        pub inner_class_info_index: CpIndex,
1421        pub outer_class_info_index: Option<CpIndex>,
1422        pub inner_name_index: Option<CpIndex>,
1423        pub inner_class_access_flags: InnerClassAccessFlags,
1424    }
1425
1426    impl InnerClass {
1427        /// Reads one entry; absent outer class and name are encoded as `0`.
1428        pub fn try_read(constant_pool: &ConstantPool<'_>, cursor: &mut Cursor<'_>) -> Result<Self> {
1429            let inner_class_info_index = cursor.read_u16()?;
1430            constant_pool.class_name_index(inner_class_info_index)?;
1431
1432            let outer_class_info_index = cursor.read_optional_index()?;
1433            if let Some(index) = outer_class_info_index {
1434                constant_pool.class_name_index(index)?;
1435            }
1436
1437            let inner_name_index = cursor.read_optional_index()?;
1438            if let Some(index) = inner_name_index {
1439                constant_pool.utf8(index)?;
1440            }
1441
1442            Ok(Self {
1443                inner_class_info_index,
1444                outer_class_info_index,
1445                inner_name_index,
1446                inner_class_access_flags: InnerClassAccessFlags::from_bits_truncate(
1447                    cursor.read_u16()?,
1448                ),
1449            })
1450        }
1451    }
1452
1453    bitflags::bitflags! {
1454        /// Access flags of an inner class.
1455        ///
1456        /// See [JVMS §4.7.6, Table 4.7.6-A](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6).
1457        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1458        pub struct InnerClassAccessFlags: u16 {
1459            const PUBLIC = 0x0001;
1460            const PRIVATE = 0x0002;
1461            const PROTECTED = 0x0004;
1462            const STATIC = 0x0008;
1463            const FINAL = 0x0010;
1464            const INTERFACE = 0x0200;
1465            const ABSTRACT = 0x0400;
1466            const SYNTHETIC = 0x1000;
1467            const ANNOTATION = 0x2000;
1468            const ENUM = 0x4000;
1469        }
1470    }
1471
1472    /// Mapping of a bytecode offset to a source line.
1473    ///
1474    /// See [JVMS §4.7.12](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.12).
1475    #[derive(Debug)]
1476    pub struct LineNumber {
1477        pub start_pc: u16,
1478        pub line_number: u16,
1479    }
1480
1481    impl LineNumber {
1482        /// Reads one entry.
1483        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1484            Ok(Self {
1485                start_pc: cursor.read_u16()?,
1486                line_number: cursor.read_u16()?,
1487            })
1488        }
1489    }
1490
1491    /// One entry of a `LocalVariableTable` attribute.
1492    ///
1493    /// See [JVMS §4.7.13](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.13).
1494    #[derive(Debug)]
1495    pub struct LocalVariable {
1496        pub start_pc: u16,
1497        pub length: u16,
1498        pub name_index: CpIndex,
1499        pub descriptor_index: CpIndex,
1500        pub index: u16,
1501    }
1502
1503    impl LocalVariable {
1504        /// Reads one entry, validating its name and descriptor.
1505        pub fn try_read(constant_pool: &ConstantPool<'_>, cursor: &mut Cursor<'_>) -> Result<Self> {
1506            let start_pc = cursor.read_u16()?;
1507            let length = cursor.read_u16()?;
1508
1509            let name_index = cursor.read_u16()?;
1510            constant_pool.utf8(name_index)?;
1511
1512            let descriptor_index = cursor.read_u16()?;
1513            constant_pool.utf8(descriptor_index)?;
1514
1515            Ok(Self {
1516                start_pc,
1517                length,
1518                name_index,
1519                descriptor_index,
1520                index: cursor.read_u16()?,
1521            })
1522        }
1523    }
1524
1525    /// One entry of a `LocalVariableTypeTable` attribute.
1526    ///
1527    /// See [JVMS §4.7.14](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.14).
1528    #[derive(Debug)]
1529    pub struct LocalVariableType {
1530        pub start_pc: u16,
1531        pub length: u16,
1532        pub name_index: CpIndex,
1533        pub signature_index: CpIndex,
1534        pub index: u16,
1535    }
1536
1537    impl LocalVariableType {
1538        /// Reads one entry, validating its name and signature.
1539        pub fn try_read(constant_pool: &ConstantPool<'_>, cursor: &mut Cursor<'_>) -> Result<Self> {
1540            let start_pc = cursor.read_u16()?;
1541            let length = cursor.read_u16()?;
1542
1543            let name_index = cursor.read_u16()?;
1544            constant_pool.utf8(name_index)?;
1545
1546            let signature_index = cursor.read_u16()?;
1547            constant_pool.utf8(signature_index)?;
1548
1549            Ok(Self {
1550                start_pc,
1551                length,
1552                name_index,
1553                signature_index,
1554                index: cursor.read_u16()?,
1555            })
1556        }
1557    }
1558
1559    /// A single annotation with its element values.
1560    ///
1561    /// See [JVMS §4.7.16](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16).
1562    #[derive(Debug)]
1563    pub struct Annotation {
1564        pub type_index: CpIndex,
1565        pub element_value_pairs: Vec<ElementValuePair>,
1566    }
1567
1568    impl Annotation {
1569        /// Reads one annotation.
1570        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1571            Ok(Self {
1572                type_index: cursor.read_u16()?,
1573                element_value_pairs: cursor.repeat_by_u16(ElementValuePair::try_read)?,
1574            })
1575        }
1576    }
1577
1578    /// A named element of an annotation.
1579    ///
1580    /// See [JVMS §4.7.16](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16).
1581    #[derive(Debug)]
1582    pub struct ElementValuePair {
1583        pub element_name_index: CpIndex,
1584        pub value: ElementValue,
1585    }
1586
1587    impl ElementValuePair {
1588        /// Reads one name/value pair.
1589        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1590            Ok(Self {
1591                element_name_index: cursor.read_u16()?,
1592                value: ElementValue::try_read(cursor)?,
1593            })
1594        }
1595    }
1596
1597    /// Value of an annotation element.
1598    ///
1599    /// See [JVMS §4.7.16.1](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16.1).
1600    #[derive(Debug)]
1601    pub enum ElementValue {
1602        Const {
1603            tag: u8,
1604            const_value_index: CpIndex,
1605        },
1606        Enum {
1607            type_name_index: CpIndex,
1608            const_name_index: CpIndex,
1609        },
1610        Class {
1611            class_info_index: CpIndex,
1612        },
1613        Annotation {
1614            annotation: Annotation,
1615        },
1616        Array {
1617            values: Vec<ElementValue>,
1618        },
1619    }
1620
1621    impl ElementValue {
1622        /// Reads a value, whose kind is given by its leading tag character.
1623        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1624            let tag = cursor.read_u8()?;
1625
1626            let value = match tag {
1627                b'B' | b'C' | b'D' | b'F' | b'I' | b'J' | b'S' | b'Z' | b's' => Self::Const {
1628                    tag,
1629                    const_value_index: cursor.read_u16()?,
1630                },
1631                b'e' => Self::Enum {
1632                    type_name_index: cursor.read_u16()?,
1633                    const_name_index: cursor.read_u16()?,
1634                },
1635                b'c' => Self::Class {
1636                    class_info_index: cursor.read_u16()?,
1637                },
1638                b'@' => Self::Annotation {
1639                    annotation: Annotation::try_read(cursor)?,
1640                },
1641                b'[' => Self::Array {
1642                    values: cursor.repeat_by_u16(Self::try_read)?,
1643                },
1644                tag => return Err(Error::InvalidElementValueTag { tag }),
1645            };
1646
1647            Ok(value)
1648        }
1649    }
1650
1651    /// An annotation attached to a use of a type.
1652    ///
1653    /// See [JVMS §4.7.20](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.20).
1654    #[derive(Debug)]
1655    pub struct TypeAnnotation {
1656        pub target: TypeAnnotationTarget,
1657        pub target_path: TypePath,
1658        pub type_index: CpIndex,
1659        pub element_value_pairs: Vec<ElementValuePair>,
1660    }
1661
1662    impl TypeAnnotation {
1663        /// Reads one type annotation.
1664        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1665            Ok(Self {
1666                target: TypeAnnotationTarget::try_read(cursor)?,
1667                target_path: TypePath::try_read(cursor)?,
1668                type_index: cursor.read_u16()?,
1669                element_value_pairs: cursor.repeat_by_u16(ElementValuePair::try_read)?,
1670            })
1671        }
1672    }
1673
1674    /// What a [`TypeAnnotation`] is attached to.
1675    ///
1676    /// See [JVMS §4.7.20.1](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.20.1).
1677    #[derive(Debug)]
1678    pub enum TypeAnnotationTarget {
1679        TypeParameter {
1680            type_parameter_index: u8,
1681        },
1682        SuperType {
1683            supertype_index: u16,
1684        },
1685        TypeParameterBound {
1686            type_parameter_index: u8,
1687            bound_index: u8,
1688        },
1689        Empty,
1690        FormalParameter {
1691            formal_parameter_index: u8,
1692        },
1693        Throws {
1694            throws_type_index: u16,
1695        },
1696        LocalVar {
1697            table: Vec<LocalVarTarget>,
1698        },
1699        Catch {
1700            exception_table_index: u16,
1701        },
1702        Offset {
1703            offset: u16,
1704        },
1705        TypeArgument {
1706            offset: u16,
1707            type_argument_index: u8,
1708        },
1709    }
1710
1711    impl TypeAnnotationTarget {
1712        /// Reads a target, whose shape is given by its leading target type byte.
1713        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1714            let target = match cursor.read_u8()? {
1715                0x00 | 0x01 => Self::TypeParameter {
1716                    type_parameter_index: cursor.read_u8()?,
1717                },
1718                0x10 => Self::SuperType {
1719                    supertype_index: cursor.read_u16()?,
1720                },
1721                0x11 | 0x12 => Self::TypeParameterBound {
1722                    type_parameter_index: cursor.read_u8()?,
1723                    bound_index: cursor.read_u8()?,
1724                },
1725                0x13..=0x15 => Self::Empty,
1726                0x16 => Self::FormalParameter {
1727                    formal_parameter_index: cursor.read_u8()?,
1728                },
1729                0x17 => Self::Throws {
1730                    throws_type_index: cursor.read_u16()?,
1731                },
1732                0x40 | 0x41 => Self::LocalVar {
1733                    table: cursor.repeat_by_u16(LocalVarTarget::try_read)?,
1734                },
1735                0x42 => Self::Catch {
1736                    exception_table_index: cursor.read_u16()?,
1737                },
1738                0x43..=0x46 => Self::Offset {
1739                    offset: cursor.read_u16()?,
1740                },
1741                0x47..=0x4B => Self::TypeArgument {
1742                    offset: cursor.read_u16()?,
1743                    type_argument_index: cursor.read_u8()?,
1744                },
1745                target_type => return Err(Error::InvalidTypeAnnotationTarget { target_type }),
1746            };
1747
1748            Ok(target)
1749        }
1750    }
1751
1752    /// Range of a local variable targeted by a type annotation.
1753    ///
1754    /// See [JVMS §4.7.20.1](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.20.1).
1755    #[derive(Debug)]
1756    pub struct LocalVarTarget {
1757        pub start_pc: u16,
1758        pub length: u16,
1759        pub index: u16,
1760    }
1761
1762    impl LocalVarTarget {
1763        /// Reads one entry.
1764        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1765            Ok(Self {
1766                start_pc: cursor.read_u16()?,
1767                length: cursor.read_u16()?,
1768                index: cursor.read_u16()?,
1769            })
1770        }
1771    }
1772
1773    /// Path from a type to the nested part an annotation applies to.
1774    ///
1775    /// See [JVMS §4.7.20.2](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.20.2).
1776    #[derive(Debug)]
1777    pub struct TypePath {
1778        pub path: Vec<TypePathEntry>,
1779    }
1780
1781    impl TypePath {
1782        /// Reads a `u8`-prefixed path.
1783        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1784            Ok(Self {
1785                path: cursor.repeat_by_u8(TypePathEntry::try_read)?,
1786            })
1787        }
1788    }
1789
1790    /// One step of a [`TypePath`].
1791    ///
1792    /// See [JVMS §4.7.20.2, Table 4.7.20.2-A](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.20.2).
1793    #[derive(Debug)]
1794    pub struct TypePathEntry {
1795        pub type_path_kind: u8,
1796        pub type_argument_index: u8,
1797    }
1798
1799    impl TypePathEntry {
1800        /// Reads one step.
1801        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1802            Ok(Self {
1803                type_path_kind: cursor.read_u8()?,
1804                type_argument_index: cursor.read_u8()?,
1805            })
1806        }
1807    }
1808
1809    /// One entry of a `BootstrapMethods` attribute.
1810    ///
1811    /// See [JVMS §4.7.23](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.23).
1812    #[derive(Debug)]
1813    pub struct BootstrapMethod {
1814        pub bootstrap_method_ref: CpIndex,
1815        pub bootstrap_arguments: Vec<CpIndex>,
1816    }
1817
1818    impl BootstrapMethod {
1819        /// Reads one entry.
1820        pub fn try_read(cursor: &mut Cursor<'_>) -> Result<Self> {
1821            Ok(Self {
1822                bootstrap_method_ref: cursor.read_u16()?,
1823                bootstrap_arguments: cursor.read_indices()?,
1824            })
1825        }
1826    }
1827
1828    /// One entry of a `MethodParameters` attribute.
1829    ///
1830    /// See [JVMS §4.7.24](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24).
1831    #[derive(Debug)]
1832    pub struct MethodParameter {
1833        pub name_index: Option<CpIndex>,
1834        pub access_flags: MethodParameterAccessFlags,
1835    }
1836
1837    impl MethodParameter {
1838        /// Reads one entry; an unnamed parameter has a name index of `0`.
1839        pub fn try_read(constant_pool: &ConstantPool<'_>, cursor: &mut Cursor<'_>) -> Result<Self> {
1840            let name_index = cursor.read_optional_index()?;
1841            if let Some(name_index) = name_index {
1842                constant_pool.utf8(name_index)?;
1843            }
1844
1845            Ok(Self {
1846                name_index,
1847                access_flags: MethodParameterAccessFlags::from_bits_truncate(cursor.read_u16()?),
1848            })
1849        }
1850    }
1851
1852    bitflags::bitflags! {
1853        /// Access flags of a method parameter.
1854        ///
1855        /// See [JVMS §4.7.24](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24).
1856        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1857        pub struct MethodParameterAccessFlags: u16 {
1858            const FINAL = 0x0010;
1859            const SYNTHETIC = 0x1000;
1860            const MANDATED = 0x8000;
1861        }
1862    }
1863}