Skip to main content

rust_asm/
class_reader.rs

1use crate::error::ClassReadError;
2use crate::insn::{
3    AbstractInsnNode, FieldInsnNode, IincInsnNode, Insn, InsnList, InsnNode, IntInsnNode,
4    InvokeDynamicInsnNode, InvokeInterfaceInsnNode, JumpInsnNode, LabelNode, LdcInsnNode, LdcValue,
5    LookupSwitchInsnNode, MemberRef, MethodInsnNode, MultiANewArrayInsnNode, TableSwitchInsnNode,
6    TryCatchBlockNode, TypeInsnNode, VarInsnNode,
7};
8use crate::{constants, opcodes};
9
10/// Represents a constant value loadable by the `LDC` (Load Constant) instruction.
11///
12/// This enum wraps various types of constants that can be stored in the constant pool
13/// and pushed onto the operand stack.
14#[derive(Debug, Clone)]
15pub enum LdcConstant {
16    /// A 32-bit integer constant.
17    Integer(i32),
18    /// A 32-bit floating-point constant.
19    Float(f32),
20    /// A 64-bit integer constant.
21    Long(i64),
22    /// A 64-bit floating-point constant.
23    Double(f64),
24    /// A string literal constant.
25    String(String),
26    /// A class constant (e.g., `String.class`).
27    Class(String),
28    /// A method type constant (MethodDescriptor).
29    MethodType(String),
30    /// A method handle constant.
31    MethodHandle {
32        reference_kind: u8,
33        reference_index: u16,
34    },
35    /// A dynamic constant (computed via `invokedynamic` bootstrap methods).
36    Dynamic,
37}
38
39/// A visitor to visit a Java field.
40///
41/// The methods of this trait must be called in the following order:
42/// `visit_end`.
43pub trait FieldVisitor {
44    /// Visits the end of the field.
45    ///
46    /// This method, which is the last one to be called, is used to inform the
47    /// visitor that all the annotations and attributes of the field have been visited.
48    fn visit_end(&mut self) {}
49}
50
51pub trait MethodVisitor {
52    /// Starts the visit of the method's code.
53    fn visit_code(&mut self) {}
54
55    /// Visits a zero-operand instruction.
56    ///
57    /// # Arguments
58    /// * `opcode` - The opcode of the instruction to be visited.
59    fn visit_insn(&mut self, _opcode: u8) {}
60
61    /// Visits an instruction with a single int operand.
62    fn visit_int_insn(&mut self, _opcode: u8, _operand: i32) {}
63
64    /// Visits a local variable instruction.
65    fn visit_var_insn(&mut self, _opcode: u8, _var_index: u16) {}
66
67    /// Visits a type instruction.
68    ///
69    /// # Arguments
70    /// * `opcode` - The opcode of the instruction.
71    /// * `type_name` - The internal name of the object or array class.
72    fn visit_type_insn(&mut self, _opcode: u8, _type_name: &str) {}
73
74    /// Visits a field instruction.
75    ///
76    /// # Arguments
77    /// * `opcode` - The opcode of the instruction.
78    /// * `owner` - The internal name of the field's owner class.
79    /// * `name` - The field's name.
80    /// * `desc` - The field's descriptor.
81    fn visit_field_insn(&mut self, _opcode: u8, _owner: &str, _name: &str, _desc: &str) {}
82    fn visit_method_insn(
83        &mut self,
84        _opcode: u8,
85        _owner: &str,
86        _name: &str,
87        _desc: &str,
88        _is_interface: bool,
89    ) {
90    }
91    fn visit_invoke_dynamic_insn(&mut self, _name: &str, _desc: &str) {}
92    /// Visits a jump instruction.
93    ///
94    /// # Arguments
95    /// * `opcode` - The opcode of the instruction.
96    /// * `target_offset` - The offset of the target instruction relative to the current instruction.
97    fn visit_jump_insn(&mut self, _opcode: u8, _target_offset: i32) {}
98
99    /// Visits an `LDC` instruction.
100    fn visit_ldc_insn(&mut self, _value: LdcConstant) {}
101    fn visit_iinc_insn(&mut self, _var_index: u16, _increment: i16) {}
102    fn visit_table_switch(&mut self, _default: i32, _low: i32, _high: i32, _targets: &[i32]) {}
103    fn visit_lookup_switch(&mut self, _default: i32, _pairs: &[(i32, i32)]) {}
104    fn visit_multi_anewarray_insn(&mut self, _type_name: &str, _dims: u8) {}
105    fn visit_maxs(&mut self, _max_stack: u16, _max_locals: u16) {}
106    fn visit_end(&mut self) {}
107}
108
109/// A visitor to visit a Java class.
110///
111/// The methods of this trait must be called in the following order:
112/// `visit` -> `visit_source` -> (`visit_field` | `visit_method`)* -> `visit_end`.
113pub trait ClassVisitor {
114    /// Visits the header of the class.
115    ///
116    /// # Arguments
117    /// * `major` - The major version number of the class file.
118    /// * `minor` - The minor version number of the class file.
119    /// * `access_flags` - The class's access flags (see `Opcodes`).
120    /// * `name` - The internal name of the class.
121    /// * `super_name` - The internal name of the super class (e.g., `java/lang/String`, `a/b/c`).
122    ///   Use `None` for `Object`.
123    /// * `interfaces` - The internal names of the class's interfaces.
124    fn visit(
125        &mut self,
126        _major: u16,
127        _minor: u16,
128        _access_flags: u16,
129        _name: &str,
130        _super_name: Option<&str>,
131        _interfaces: &[String],
132    ) {
133    }
134
135    /// Visits the source file name of the class.
136    fn visit_source(&mut self, _source: &str) {}
137
138    /// Visits a field of the class.
139    ///
140    /// Returns an optional `FieldVisitor` to visit the field's content.
141    fn visit_field(
142        &mut self,
143        _access_flags: u16,
144        _name: &str,
145        _descriptor: &str,
146    ) -> Option<Box<dyn FieldVisitor>> {
147        None
148    }
149
150    /// Visits a method of the class.
151    ///
152    /// Returns an optional `MethodVisitor` to visit the method's code.
153    fn visit_method(
154        &mut self,
155        _access_flags: u16,
156        _name: &str,
157        _descriptor: &str,
158    ) -> Option<Box<dyn MethodVisitor>> {
159        None
160    }
161
162    /// Visits the end of the class.
163    fn visit_end(&mut self) {}
164}
165
166/// A parser to make a [`ClassVisitor`] visit a `ClassFile` structure.
167///
168/// This class parses a byte array conforming to the Java class file format and
169/// calls the appropriate methods of a given class visitor for each field, method,
170/// and bytecode instruction encountered.
171pub struct ClassReader {
172    bytes: Vec<u8>,
173}
174
175impl ClassReader {
176    /// Constructs a new `ClassReader` with the given class file bytes.
177    ///
178    /// # Arguments
179    ///
180    /// * `bytes` - A byte slice containing the JVM class file data.
181    pub fn new(bytes: &[u8]) -> Self {
182        Self {
183            bytes: bytes.to_vec(),
184        }
185    }
186
187    /// Makes the given visitor visit the Java class of this `ClassReader`.
188    ///
189    /// This method parses the class file data and drives the visitor events.
190    ///
191    /// # Arguments
192    ///
193    /// * `visitor` - The visitor that must visit this class.
194    /// * `_options` - Option flags (currently unused, reserve for future parsing options like skipping debug info).
195    ///
196    /// # Errors
197    ///
198    /// Returns a [`ClassReadError`] if the class file is malformed or contains unsupported versions.
199    pub fn accept(
200        &self,
201        visitor: &mut dyn ClassVisitor,
202        _options: u32,
203    ) -> Result<(), ClassReadError> {
204        let class_file = read_class_file(&self.bytes)?;
205        let name = class_file.class_name(class_file.this_class)?.to_string();
206        let super_name = if class_file.super_class == 0 {
207            None
208        } else {
209            Some(class_file.class_name(class_file.super_class)?.to_string())
210        };
211        let mut interfaces = Vec::with_capacity(class_file.interfaces.len());
212        for index in &class_file.interfaces {
213            interfaces.push(class_file.class_name(*index)?.to_string());
214        }
215
216        visitor.visit(
217            class_file.major_version,
218            class_file.minor_version,
219            class_file.access_flags,
220            &name,
221            super_name.as_deref(),
222            &interfaces,
223        );
224
225        for attr in &class_file.attributes {
226            if let AttributeInfo::SourceFile { sourcefile_index } = attr {
227                let source = class_file.cp_utf8(*sourcefile_index)?;
228                visitor.visit_source(source);
229            }
230        }
231
232        for field in &class_file.fields {
233            let field_name = class_file.cp_utf8(field.name_index)?;
234            let field_desc = class_file.cp_utf8(field.descriptor_index)?;
235            if let Some(mut fv) = visitor.visit_field(field.access_flags, field_name, field_desc) {
236                fv.visit_end();
237            }
238        }
239
240        for method in &class_file.methods {
241            let method_name = class_file.cp_utf8(method.name_index)?;
242            let method_desc = class_file.cp_utf8(method.descriptor_index)?;
243            if let Some(mut mv) =
244                visitor.visit_method(method.access_flags, method_name, method_desc)
245            {
246                let code = method.attributes.iter().find_map(|attr| match attr {
247                    AttributeInfo::Code(code) => Some(code),
248                    _ => None,
249                });
250                if let Some(code) = code {
251                    mv.visit_code();
252                    let instructions = parse_code_instructions_with_offsets(&code.code)?;
253                    for instruction in instructions {
254                        visit_instruction(
255                            &class_file.constant_pool,
256                            instruction.offset as i32,
257                            instruction.insn,
258                            &mut *mv,
259                        )?;
260                    }
261                    mv.visit_maxs(code.max_stack, code.max_locals);
262                }
263                mv.visit_end();
264            }
265        }
266
267        visitor.visit_end();
268        Ok(())
269    }
270
271    /// Converts the read class data directly into a `ClassNode`.
272    ///
273    /// This is a convenience method that parses the bytes and builds a
274    /// complete object model of the class.
275    pub fn to_class_node(&self) -> Result<crate::nodes::ClassNode, ClassReadError> {
276        let class_file = read_class_file(&self.bytes)?;
277        class_file.to_class_node()
278    }
279}
280
281#[derive(Debug, Clone)]
282pub enum CpInfo {
283    Unusable,
284    Utf8(String),
285    Integer(i32),
286    Float(f32),
287    Long(i64),
288    Double(f64),
289    Class {
290        name_index: u16,
291    },
292    String {
293        string_index: u16,
294    },
295    Fieldref {
296        class_index: u16,
297        name_and_type_index: u16,
298    },
299    Methodref {
300        class_index: u16,
301        name_and_type_index: u16,
302    },
303    InterfaceMethodref {
304        class_index: u16,
305        name_and_type_index: u16,
306    },
307    NameAndType {
308        name_index: u16,
309        descriptor_index: u16,
310    },
311    MethodHandle {
312        reference_kind: u8,
313        reference_index: u16,
314    },
315    MethodType {
316        descriptor_index: u16,
317    },
318    Dynamic {
319        bootstrap_method_attr_index: u16,
320        name_and_type_index: u16,
321    },
322    InvokeDynamic {
323        bootstrap_method_attr_index: u16,
324        name_and_type_index: u16,
325    },
326    Module {
327        name_index: u16,
328    },
329    Package {
330        name_index: u16,
331    },
332}
333
334#[derive(Debug, Clone)]
335pub struct ClassFile {
336    pub minor_version: u16,
337    pub major_version: u16,
338    pub constant_pool: Vec<CpInfo>,
339    pub access_flags: u16,
340    pub this_class: u16,
341    pub super_class: u16,
342    pub interfaces: Vec<u16>,
343    pub fields: Vec<FieldInfo>,
344    pub methods: Vec<MethodInfo>,
345    pub attributes: Vec<AttributeInfo>,
346}
347
348impl ClassFile {
349    pub fn cp_utf8(&self, index: u16) -> Result<&str, ClassReadError> {
350        match self
351            .constant_pool
352            .get(index as usize)
353            .ok_or(ClassReadError::InvalidIndex(index))?
354        {
355            CpInfo::Utf8(value) => Ok(value.as_str()),
356            _ => Err(ClassReadError::InvalidIndex(index)),
357        }
358    }
359
360    pub fn class_name(&self, index: u16) -> Result<&str, ClassReadError> {
361        match self
362            .constant_pool
363            .get(index as usize)
364            .ok_or(ClassReadError::InvalidIndex(index))?
365        {
366            CpInfo::Class { name_index } => self.cp_utf8(*name_index),
367            _ => Err(ClassReadError::InvalidIndex(index)),
368        }
369    }
370
371    pub fn to_class_node(&self) -> Result<crate::nodes::ClassNode, ClassReadError> {
372        let name = self.class_name(self.this_class)?.to_string();
373        let super_name = if self.super_class == 0 {
374            None
375        } else {
376            Some(self.class_name(self.super_class)?.to_string())
377        };
378        let source_file = self.attributes.iter().find_map(|attr| match attr {
379            AttributeInfo::SourceFile { sourcefile_index } => self
380                .cp_utf8(*sourcefile_index)
381                .ok()
382                .map(|value| value.to_string()),
383            _ => None,
384        });
385        let mut interfaces = Vec::with_capacity(self.interfaces.len());
386        for index in &self.interfaces {
387            interfaces.push(self.class_name(*index)?.to_string());
388        }
389
390        let mut fields = Vec::with_capacity(self.fields.len());
391        for field in &self.fields {
392            let name = self.cp_utf8(field.name_index)?.to_string();
393            let descriptor = self.cp_utf8(field.descriptor_index)?.to_string();
394            fields.push(crate::nodes::FieldNode {
395                access_flags: field.access_flags,
396                name_index: field.name_index,
397                descriptor_index: field.descriptor_index,
398                name,
399                descriptor,
400                attributes: field.attributes.clone(),
401            });
402        }
403
404        let mut methods = Vec::with_capacity(self.methods.len());
405        for method in &self.methods {
406            let name = self.cp_utf8(method.name_index)?.to_string();
407            let descriptor = self.cp_utf8(method.descriptor_index)?.to_string();
408            let mut method_attributes = method.attributes.clone();
409            method_attributes.retain(|attr| !matches!(attr, AttributeInfo::Code(_)));
410            let code = method.attributes.iter().find_map(|attr| match attr {
411                AttributeInfo::Code(code) => Some(code),
412                _ => None,
413            });
414
415            let (has_code, max_stack, max_locals, instructions, exception_table, code_attributes) =
416                if let Some(code) = code {
417                    let mut list = InsnList::new();
418                    for insn in &code.instructions {
419                        list.add(insn.clone());
420                    }
421                    (
422                        true,
423                        code.max_stack,
424                        code.max_locals,
425                        list,
426                        code.exception_table.clone(),
427                        code.attributes.clone(),
428                    )
429                } else {
430                    (false, 0, 0, InsnList::new(), Vec::new(), Vec::new())
431                };
432
433            methods.push(crate::nodes::MethodNode {
434                access_flags: method.access_flags,
435                name,
436                descriptor,
437                has_code,
438                max_stack,
439                max_locals,
440                instructions,
441                exception_table,
442                code_attributes,
443                attributes: method_attributes,
444            });
445        }
446
447        let mut inner_classes = Vec::new();
448        for attr in &self.attributes {
449            if let AttributeInfo::InnerClasses { classes } = attr {
450                for entry in classes {
451                    let name = self.class_name(entry.inner_class_info_index)?.to_string();
452                    let outer_name = if entry.outer_class_info_index == 0 {
453                        None
454                    } else {
455                        Some(self.class_name(entry.outer_class_info_index)?.to_string())
456                    };
457                    let inner_name = if entry.inner_name_index == 0 {
458                        None
459                    } else {
460                        Some(self.cp_utf8(entry.inner_name_index)?.to_string())
461                    };
462                    inner_classes.push(crate::nodes::InnerClassNode {
463                        name,
464                        outer_name,
465                        inner_name,
466                        access_flags: entry.inner_class_access_flags,
467                    });
468                }
469            }
470        }
471
472        let mut outer_class = String::new();
473        if let Some(class_index) = self.attributes.iter().find_map(|attr| match attr {
474            AttributeInfo::EnclosingMethod { class_index, .. } => Some(*class_index),
475            _ => None,
476        }) {
477            outer_class = self.class_name(class_index)?.to_string();
478        }
479        if outer_class.is_empty() {
480            for attr in &self.attributes {
481                if let AttributeInfo::InnerClasses { classes } = attr
482                    && let Some(entry) = classes.iter().find(|entry| {
483                        entry.inner_class_info_index == self.this_class
484                            && entry.outer_class_info_index != 0
485                    })
486                {
487                    outer_class = self.class_name(entry.outer_class_info_index)?.to_string();
488                    break;
489                }
490            }
491        }
492
493        Ok(crate::nodes::ClassNode {
494            minor_version: self.minor_version,
495            major_version: self.major_version,
496            access_flags: self.access_flags,
497            constant_pool: self.constant_pool.clone(),
498            this_class: self.this_class,
499            name,
500            super_name,
501            source_file,
502            interfaces,
503            interface_indices: self.interfaces.clone(),
504            fields,
505            methods,
506            attributes: self.attributes.clone(),
507            inner_classes,
508            outer_class,
509        })
510    }
511}
512
513#[derive(Debug, Clone)]
514pub struct FieldInfo {
515    pub access_flags: u16,
516    pub name_index: u16,
517    pub descriptor_index: u16,
518    pub attributes: Vec<AttributeInfo>,
519}
520
521#[derive(Debug, Clone)]
522pub struct MethodInfo {
523    pub access_flags: u16,
524    pub name_index: u16,
525    pub descriptor_index: u16,
526    pub attributes: Vec<AttributeInfo>,
527}
528
529#[derive(Debug, Clone)]
530pub enum AttributeInfo {
531    Code(CodeAttribute),
532    ConstantValue { constantvalue_index: u16 },
533    Exceptions { exception_index_table: Vec<u16> },
534    SourceFile { sourcefile_index: u16 },
535    LineNumberTable { entries: Vec<LineNumber> },
536    LocalVariableTable { entries: Vec<LocalVariable> },
537    Signature { signature_index: u16 },
538    StackMapTable { entries: Vec<StackMapFrame> },
539    Deprecated,
540    Synthetic,
541    InnerClasses { classes: Vec<InnerClass> },
542    EnclosingMethod { class_index: u16, method_index: u16 },
543    BootstrapMethods { methods: Vec<BootstrapMethod> },
544    MethodParameters { parameters: Vec<MethodParameter> },
545    Unknown { name: String, info: Vec<u8> },
546}
547
548#[derive(Debug, Clone)]
549pub struct CodeAttribute {
550    pub max_stack: u16,
551    pub max_locals: u16,
552    pub code: Vec<u8>,
553    pub instructions: Vec<Insn>,
554    pub insn_nodes: Vec<AbstractInsnNode>,
555    pub exception_table: Vec<ExceptionTableEntry>,
556    pub try_catch_blocks: Vec<TryCatchBlockNode>,
557    pub attributes: Vec<AttributeInfo>,
558}
559
560#[derive(Debug, Clone)]
561pub struct ExceptionTableEntry {
562    pub start_pc: u16,
563    pub end_pc: u16,
564    pub handler_pc: u16,
565    pub catch_type: u16,
566}
567
568#[derive(Debug, Clone)]
569pub struct LineNumber {
570    pub start_pc: u16,
571    pub line_number: u16,
572}
573
574#[derive(Debug, Clone)]
575pub struct LocalVariable {
576    pub start_pc: u16,
577    pub length: u16,
578    pub name_index: u16,
579    pub descriptor_index: u16,
580    pub index: u16,
581}
582
583#[derive(Debug, Clone)]
584pub struct InnerClass {
585    pub inner_class_info_index: u16,
586    pub outer_class_info_index: u16,
587    pub inner_name_index: u16,
588    pub inner_class_access_flags: u16,
589}
590
591#[derive(Debug, Clone)]
592pub struct BootstrapMethod {
593    pub bootstrap_method_ref: u16,
594    pub bootstrap_arguments: Vec<u16>,
595}
596
597#[derive(Debug, Clone)]
598pub struct MethodParameter {
599    pub name_index: u16,
600    pub access_flags: u16,
601}
602
603#[derive(Debug, Clone, PartialEq, Eq)]
604pub enum VerificationTypeInfo {
605    Top,
606    Integer,
607    Float,
608    Long,
609    Double,
610    Null,
611    UninitializedThis,
612    Object { cpool_index: u16 },
613    Uninitialized { offset: u16 },
614}
615
616#[derive(Debug, Clone)]
617pub enum StackMapFrame {
618    SameFrame {
619        offset_delta: u16,
620    },
621    SameLocals1StackItemFrame {
622        offset_delta: u16,
623        stack: VerificationTypeInfo,
624    },
625    SameLocals1StackItemFrameExtended {
626        offset_delta: u16,
627        stack: VerificationTypeInfo,
628    },
629    ChopFrame {
630        offset_delta: u16,
631        k: u8,
632    },
633    SameFrameExtended {
634        offset_delta: u16,
635    },
636    AppendFrame {
637        offset_delta: u16,
638        locals: Vec<VerificationTypeInfo>,
639    },
640    FullFrame {
641        offset_delta: u16,
642        locals: Vec<VerificationTypeInfo>,
643        stack: Vec<VerificationTypeInfo>,
644    },
645}
646
647pub fn read_class_file(bytes: &[u8]) -> Result<ClassFile, ClassReadError> {
648    let mut reader = ByteReader::new(bytes);
649    let magic = reader.read_u4()?;
650    if magic != 0xCAFEBABE {
651        return Err(ClassReadError::InvalidMagic(magic));
652    }
653    let minor_version = reader.read_u2()?;
654    let major_version = reader.read_u2()?;
655    if major_version > constants::V25 {
656        return Err(ClassReadError::InvalidClassVersion(major_version));
657    }
658    let constant_pool = read_constant_pool(&mut reader)?;
659    let access_flags = reader.read_u2()?;
660    let this_class = reader.read_u2()?;
661    let super_class = reader.read_u2()?;
662    let interfaces = read_u2_table(&mut reader)?;
663    let fields = read_fields(&mut reader, &constant_pool)?;
664    let methods = read_methods(&mut reader, &constant_pool)?;
665    let attributes = read_attributes(&mut reader, &constant_pool)?;
666
667    Ok(ClassFile {
668        minor_version,
669        major_version,
670        constant_pool,
671        access_flags,
672        this_class,
673        super_class,
674        interfaces,
675        fields,
676        methods,
677        attributes,
678    })
679}
680
681fn read_constant_pool(reader: &mut ByteReader<'_>) -> Result<Vec<CpInfo>, ClassReadError> {
682    let count = reader.read_u2()? as usize;
683    let mut pool = Vec::with_capacity(count);
684    pool.push(CpInfo::Unusable);
685
686    let mut index = 1;
687    while index < count {
688        let tag = reader.read_u1()?;
689        let entry = match tag {
690            1 => {
691                let len = reader.read_u2()? as usize;
692                let bytes = reader.read_bytes(len)?;
693                let value = decode_modified_utf8(&bytes)?;
694                CpInfo::Utf8(value)
695            }
696            3 => {
697                let value = reader.read_u4()? as i32;
698                CpInfo::Integer(value)
699            }
700            4 => {
701                let value = f32::from_bits(reader.read_u4()?);
702                CpInfo::Float(value)
703            }
704            5 => {
705                let value = reader.read_u8()? as i64;
706                CpInfo::Long(value)
707            }
708            6 => {
709                let value = f64::from_bits(reader.read_u8()?);
710                CpInfo::Double(value)
711            }
712            7 => CpInfo::Class {
713                name_index: reader.read_u2()?,
714            },
715            8 => CpInfo::String {
716                string_index: reader.read_u2()?,
717            },
718            9 => CpInfo::Fieldref {
719                class_index: reader.read_u2()?,
720                name_and_type_index: reader.read_u2()?,
721            },
722            10 => CpInfo::Methodref {
723                class_index: reader.read_u2()?,
724                name_and_type_index: reader.read_u2()?,
725            },
726            11 => CpInfo::InterfaceMethodref {
727                class_index: reader.read_u2()?,
728                name_and_type_index: reader.read_u2()?,
729            },
730            12 => CpInfo::NameAndType {
731                name_index: reader.read_u2()?,
732                descriptor_index: reader.read_u2()?,
733            },
734            15 => CpInfo::MethodHandle {
735                reference_kind: reader.read_u1()?,
736                reference_index: reader.read_u2()?,
737            },
738            16 => CpInfo::MethodType {
739                descriptor_index: reader.read_u2()?,
740            },
741            17 => CpInfo::Dynamic {
742                bootstrap_method_attr_index: reader.read_u2()?,
743                name_and_type_index: reader.read_u2()?,
744            },
745            18 => CpInfo::InvokeDynamic {
746                bootstrap_method_attr_index: reader.read_u2()?,
747                name_and_type_index: reader.read_u2()?,
748            },
749            19 => CpInfo::Module {
750                name_index: reader.read_u2()?,
751            },
752            20 => CpInfo::Package {
753                name_index: reader.read_u2()?,
754            },
755            _ => return Err(ClassReadError::InvalidConstantPoolTag(tag)),
756        };
757
758        pool.push(entry);
759
760        if tag == 5 || tag == 6 {
761            pool.push(CpInfo::Unusable);
762            index += 2;
763        } else {
764            index += 1;
765        }
766    }
767
768    Ok(pool)
769}
770
771fn read_u2_table(reader: &mut ByteReader<'_>) -> Result<Vec<u16>, ClassReadError> {
772    let count = reader.read_u2()? as usize;
773    let mut values = Vec::with_capacity(count);
774    for _ in 0..count {
775        values.push(reader.read_u2()?);
776    }
777    Ok(values)
778}
779
780fn read_fields(
781    reader: &mut ByteReader<'_>,
782    cp: &[CpInfo],
783) -> Result<Vec<FieldInfo>, ClassReadError> {
784    let count = reader.read_u2()? as usize;
785    let mut fields = Vec::with_capacity(count);
786    for _ in 0..count {
787        let access_flags = reader.read_u2()?;
788        let name_index = reader.read_u2()?;
789        let descriptor_index = reader.read_u2()?;
790        let attributes = read_attributes(reader, cp)?;
791        fields.push(FieldInfo {
792            access_flags,
793            name_index,
794            descriptor_index,
795            attributes,
796        });
797    }
798    Ok(fields)
799}
800
801fn read_methods(
802    reader: &mut ByteReader<'_>,
803    cp: &[CpInfo],
804) -> Result<Vec<MethodInfo>, ClassReadError> {
805    let count = reader.read_u2()? as usize;
806    let mut methods = Vec::with_capacity(count);
807    for _ in 0..count {
808        let access_flags = reader.read_u2()?;
809        let name_index = reader.read_u2()?;
810        let descriptor_index = reader.read_u2()?;
811        let attributes = read_attributes(reader, cp)?;
812        methods.push(MethodInfo {
813            access_flags,
814            name_index,
815            descriptor_index,
816            attributes,
817        });
818    }
819    Ok(methods)
820}
821
822fn read_attributes(
823    reader: &mut ByteReader<'_>,
824    cp: &[CpInfo],
825) -> Result<Vec<AttributeInfo>, ClassReadError> {
826    let count = reader.read_u2()? as usize;
827    let mut attributes = Vec::with_capacity(count);
828    for _ in 0..count {
829        let name_index = reader.read_u2()?;
830        let length = reader.read_u4()? as usize;
831        let name = cp_utf8(cp, name_index)?;
832        let info = reader.read_bytes(length)?;
833        let attribute = parse_attribute(name, info, cp)?;
834        attributes.push(attribute);
835    }
836    Ok(attributes)
837}
838
839fn parse_attribute(
840    name: &str,
841    info: Vec<u8>,
842    cp: &[CpInfo],
843) -> Result<AttributeInfo, ClassReadError> {
844    let mut reader = ByteReader::new(&info);
845    let attribute = match name {
846        "Code" => {
847            let max_stack = reader.read_u2()?;
848            let max_locals = reader.read_u2()?;
849            let code_length = reader.read_u4()? as usize;
850            let code = reader.read_bytes(code_length)?;
851            let instructions = parse_code_instructions(&code)?;
852            let exception_table_length = reader.read_u2()? as usize;
853            let mut exception_table = Vec::with_capacity(exception_table_length);
854            for _ in 0..exception_table_length {
855                exception_table.push(ExceptionTableEntry {
856                    start_pc: reader.read_u2()?,
857                    end_pc: reader.read_u2()?,
858                    handler_pc: reader.read_u2()?,
859                    catch_type: reader.read_u2()?,
860                });
861            }
862            let attributes = read_attributes(&mut reader, cp)?;
863            let (insn_nodes, try_catch_blocks) = build_insn_nodes(&code, &exception_table, cp)?;
864            AttributeInfo::Code(CodeAttribute {
865                max_stack,
866                max_locals,
867                code,
868                instructions,
869                insn_nodes,
870                exception_table,
871                try_catch_blocks,
872                attributes,
873            })
874        }
875        "ConstantValue" => AttributeInfo::ConstantValue {
876            constantvalue_index: reader.read_u2()?,
877        },
878        "Exceptions" => {
879            let count = reader.read_u2()? as usize;
880            let mut exception_index_table = Vec::with_capacity(count);
881            for _ in 0..count {
882                exception_index_table.push(reader.read_u2()?);
883            }
884            AttributeInfo::Exceptions {
885                exception_index_table,
886            }
887        }
888        "SourceFile" => AttributeInfo::SourceFile {
889            sourcefile_index: reader.read_u2()?,
890        },
891        "LineNumberTable" => {
892            let count = reader.read_u2()? as usize;
893            let mut entries = Vec::with_capacity(count);
894            for _ in 0..count {
895                entries.push(LineNumber {
896                    start_pc: reader.read_u2()?,
897                    line_number: reader.read_u2()?,
898                });
899            }
900            AttributeInfo::LineNumberTable { entries }
901        }
902        "LocalVariableTable" => {
903            let count = reader.read_u2()? as usize;
904            let mut entries = Vec::with_capacity(count);
905            for _ in 0..count {
906                entries.push(LocalVariable {
907                    start_pc: reader.read_u2()?,
908                    length: reader.read_u2()?,
909                    name_index: reader.read_u2()?,
910                    descriptor_index: reader.read_u2()?,
911                    index: reader.read_u2()?,
912                });
913            }
914            AttributeInfo::LocalVariableTable { entries }
915        }
916        "Signature" => AttributeInfo::Signature {
917            signature_index: reader.read_u2()?,
918        },
919        "StackMapTable" => {
920            let count = reader.read_u2()? as usize;
921            let mut entries = Vec::with_capacity(count);
922            for _ in 0..count {
923                let frame_type = reader.read_u1()?;
924                let frame = match frame_type {
925                    0..=63 => StackMapFrame::SameFrame {
926                        offset_delta: frame_type as u16,
927                    },
928                    64..=127 => StackMapFrame::SameLocals1StackItemFrame {
929                        offset_delta: (frame_type - 64) as u16,
930                        stack: parse_verification_type(&mut reader)?,
931                    },
932                    247 => StackMapFrame::SameLocals1StackItemFrameExtended {
933                        offset_delta: reader.read_u2()?,
934                        stack: parse_verification_type(&mut reader)?,
935                    },
936                    248..=250 => StackMapFrame::ChopFrame {
937                        offset_delta: reader.read_u2()?,
938                        k: 251 - frame_type,
939                    },
940                    251 => StackMapFrame::SameFrameExtended {
941                        offset_delta: reader.read_u2()?,
942                    },
943                    252..=254 => {
944                        let offset_delta = reader.read_u2()?;
945                        let locals_count = (frame_type - 251) as usize;
946                        let mut locals = Vec::with_capacity(locals_count);
947                        for _ in 0..locals_count {
948                            locals.push(parse_verification_type(&mut reader)?);
949                        }
950                        StackMapFrame::AppendFrame {
951                            offset_delta,
952                            locals,
953                        }
954                    }
955                    255 => {
956                        let offset_delta = reader.read_u2()?;
957                        let locals_count = reader.read_u2()? as usize;
958                        let mut locals = Vec::with_capacity(locals_count);
959                        for _ in 0..locals_count {
960                            locals.push(parse_verification_type(&mut reader)?);
961                        }
962                        let stack_count = reader.read_u2()? as usize;
963                        let mut stack = Vec::with_capacity(stack_count);
964                        for _ in 0..stack_count {
965                            stack.push(parse_verification_type(&mut reader)?);
966                        }
967                        StackMapFrame::FullFrame {
968                            offset_delta,
969                            locals,
970                            stack,
971                        }
972                    }
973                    _ => {
974                        return Err(ClassReadError::InvalidAttribute(
975                            "StackMapTable".to_string(),
976                        ));
977                    }
978                };
979                entries.push(frame);
980            }
981            AttributeInfo::StackMapTable { entries }
982        }
983        "Deprecated" => AttributeInfo::Deprecated,
984        "Synthetic" => AttributeInfo::Synthetic,
985        "InnerClasses" => {
986            let count = reader.read_u2()? as usize;
987            let mut classes = Vec::with_capacity(count);
988            for _ in 0..count {
989                classes.push(InnerClass {
990                    inner_class_info_index: reader.read_u2()?,
991                    outer_class_info_index: reader.read_u2()?,
992                    inner_name_index: reader.read_u2()?,
993                    inner_class_access_flags: reader.read_u2()?,
994                });
995            }
996            AttributeInfo::InnerClasses { classes }
997        }
998        "EnclosingMethod" => AttributeInfo::EnclosingMethod {
999            class_index: reader.read_u2()?,
1000            method_index: reader.read_u2()?,
1001        },
1002        "BootstrapMethods" => {
1003            let count = reader.read_u2()? as usize;
1004            let mut methods = Vec::with_capacity(count);
1005            for _ in 0..count {
1006                let bootstrap_method_ref = reader.read_u2()?;
1007                let arg_count = reader.read_u2()? as usize;
1008                let mut bootstrap_arguments = Vec::with_capacity(arg_count);
1009                for _ in 0..arg_count {
1010                    bootstrap_arguments.push(reader.read_u2()?);
1011                }
1012                methods.push(BootstrapMethod {
1013                    bootstrap_method_ref,
1014                    bootstrap_arguments,
1015                });
1016            }
1017            AttributeInfo::BootstrapMethods { methods }
1018        }
1019        "MethodParameters" => {
1020            let count = reader.read_u1()? as usize;
1021            let mut parameters = Vec::with_capacity(count);
1022            for _ in 0..count {
1023                parameters.push(MethodParameter {
1024                    name_index: reader.read_u2()?,
1025                    access_flags: reader.read_u2()?,
1026                });
1027            }
1028            AttributeInfo::MethodParameters { parameters }
1029        }
1030        _ => {
1031            return Ok(AttributeInfo::Unknown {
1032                name: name.to_string(),
1033                info,
1034            });
1035        }
1036    };
1037
1038    if reader.remaining() != 0 {
1039        return Err(ClassReadError::InvalidAttribute(name.to_string()));
1040    }
1041
1042    Ok(attribute)
1043}
1044
1045fn parse_verification_type(
1046    reader: &mut ByteReader<'_>,
1047) -> Result<VerificationTypeInfo, ClassReadError> {
1048    let tag = reader.read_u1()?;
1049    let kind = match tag {
1050        0 => VerificationTypeInfo::Top,
1051        1 => VerificationTypeInfo::Integer,
1052        2 => VerificationTypeInfo::Float,
1053        3 => VerificationTypeInfo::Double,
1054        4 => VerificationTypeInfo::Long,
1055        5 => VerificationTypeInfo::Null,
1056        6 => VerificationTypeInfo::UninitializedThis,
1057        7 => VerificationTypeInfo::Object {
1058            cpool_index: reader.read_u2()?,
1059        },
1060        8 => VerificationTypeInfo::Uninitialized {
1061            offset: reader.read_u2()?,
1062        },
1063        _ => {
1064            return Err(ClassReadError::InvalidAttribute(
1065                "StackMapTable".to_string(),
1066            ));
1067        }
1068    };
1069    Ok(kind)
1070}
1071
1072fn cp_utf8(cp: &[CpInfo], index: u16) -> Result<&str, ClassReadError> {
1073    match cp.get(index as usize) {
1074        Some(CpInfo::Utf8(value)) => Ok(value.as_str()),
1075        _ => Err(ClassReadError::InvalidIndex(index)),
1076    }
1077}
1078
1079fn cp_class_name(cp: &[CpInfo], index: u16) -> Result<&str, ClassReadError> {
1080    match cp.get(index as usize) {
1081        Some(CpInfo::Class { name_index }) => cp_utf8(cp, *name_index),
1082        _ => Err(ClassReadError::InvalidIndex(index)),
1083    }
1084}
1085
1086fn cp_name_and_type(cp: &[CpInfo], index: u16) -> Result<(&str, &str), ClassReadError> {
1087    match cp.get(index as usize) {
1088        Some(CpInfo::NameAndType {
1089            name_index,
1090            descriptor_index,
1091        }) => Ok((cp_utf8(cp, *name_index)?, cp_utf8(cp, *descriptor_index)?)),
1092        _ => Err(ClassReadError::InvalidIndex(index)),
1093    }
1094}
1095
1096fn cp_field_ref(cp: &[CpInfo], index: u16) -> Result<(&str, &str, &str), ClassReadError> {
1097    match cp.get(index as usize) {
1098        Some(CpInfo::Fieldref {
1099            class_index,
1100            name_and_type_index,
1101        }) => {
1102            let owner = cp_class_name(cp, *class_index)?;
1103            let (name, desc) = cp_name_and_type(cp, *name_and_type_index)?;
1104            Ok((owner, name, desc))
1105        }
1106        _ => Err(ClassReadError::InvalidIndex(index)),
1107    }
1108}
1109
1110fn cp_method_ref(cp: &[CpInfo], index: u16) -> Result<(&str, &str, &str, bool), ClassReadError> {
1111    match cp.get(index as usize) {
1112        Some(CpInfo::Methodref {
1113            class_index,
1114            name_and_type_index,
1115        }) => {
1116            let owner = cp_class_name(cp, *class_index)?;
1117            let (name, desc) = cp_name_and_type(cp, *name_and_type_index)?;
1118            Ok((owner, name, desc, false))
1119        }
1120        Some(CpInfo::InterfaceMethodref {
1121            class_index,
1122            name_and_type_index,
1123        }) => {
1124            let owner = cp_class_name(cp, *class_index)?;
1125            let (name, desc) = cp_name_and_type(cp, *name_and_type_index)?;
1126            Ok((owner, name, desc, true))
1127        }
1128        _ => Err(ClassReadError::InvalidIndex(index)),
1129    }
1130}
1131
1132fn cp_invoke_dynamic(cp: &[CpInfo], index: u16) -> Result<(&str, &str), ClassReadError> {
1133    match cp.get(index as usize) {
1134        Some(CpInfo::InvokeDynamic {
1135            name_and_type_index,
1136            ..
1137        }) => cp_name_and_type(cp, *name_and_type_index),
1138        _ => Err(ClassReadError::InvalidIndex(index)),
1139    }
1140}
1141
1142fn cp_ldc_constant(cp: &[CpInfo], index: u16) -> Result<LdcConstant, ClassReadError> {
1143    match cp.get(index as usize) {
1144        Some(CpInfo::Integer(value)) => Ok(LdcConstant::Integer(*value)),
1145        Some(CpInfo::Float(value)) => Ok(LdcConstant::Float(*value)),
1146        Some(CpInfo::Long(value)) => Ok(LdcConstant::Long(*value)),
1147        Some(CpInfo::Double(value)) => Ok(LdcConstant::Double(*value)),
1148        Some(CpInfo::String { string_index }) => {
1149            Ok(LdcConstant::String(cp_utf8(cp, *string_index)?.to_string()))
1150        }
1151        Some(CpInfo::Class { name_index }) => {
1152            Ok(LdcConstant::Class(cp_utf8(cp, *name_index)?.to_string()))
1153        }
1154        Some(CpInfo::MethodType { descriptor_index }) => Ok(LdcConstant::MethodType(
1155            cp_utf8(cp, *descriptor_index)?.to_string(),
1156        )),
1157        Some(CpInfo::MethodHandle {
1158            reference_kind,
1159            reference_index,
1160        }) => Ok(LdcConstant::MethodHandle {
1161            reference_kind: *reference_kind,
1162            reference_index: *reference_index,
1163        }),
1164        Some(CpInfo::Dynamic { .. }) => Ok(LdcConstant::Dynamic),
1165        _ => Err(ClassReadError::InvalidIndex(index)),
1166    }
1167}
1168
1169fn decode_modified_utf8(bytes: &[u8]) -> Result<String, ClassReadError> {
1170    let mut code_units = Vec::with_capacity(bytes.len());
1171    let mut i = 0;
1172    while i < bytes.len() {
1173        let byte = bytes[i];
1174        if byte & 0x80 == 0 {
1175            code_units.push(byte as u16);
1176            i += 1;
1177        } else if byte & 0xE0 == 0xC0 {
1178            if i + 1 >= bytes.len() {
1179                return Err(ClassReadError::Utf8Error("truncated 2-byte".to_string()));
1180            }
1181            let byte2 = bytes[i + 1];
1182            if byte2 & 0xC0 != 0x80 {
1183                return Err(ClassReadError::Utf8Error("invalid 2-byte".to_string()));
1184            }
1185            let value = (((byte & 0x1F) as u16) << 6) | ((byte2 & 0x3F) as u16);
1186            code_units.push(value);
1187            i += 2;
1188        } else if byte & 0xF0 == 0xE0 {
1189            if i + 2 >= bytes.len() {
1190                return Err(ClassReadError::Utf8Error("truncated 3-byte".to_string()));
1191            }
1192            let byte2 = bytes[i + 1];
1193            let byte3 = bytes[i + 2];
1194            if byte2 & 0xC0 != 0x80 || byte3 & 0xC0 != 0x80 {
1195                return Err(ClassReadError::Utf8Error("invalid 3-byte".to_string()));
1196            }
1197            let value = (((byte & 0x0F) as u16) << 12)
1198                | (((byte2 & 0x3F) as u16) << 6)
1199                | ((byte3 & 0x3F) as u16);
1200            code_units.push(value);
1201            i += 3;
1202        } else {
1203            return Err(ClassReadError::Utf8Error(
1204                "invalid leading byte".to_string(),
1205            ));
1206        }
1207    }
1208
1209    String::from_utf16(&code_units)
1210        .map_err(|_| ClassReadError::Utf8Error("invalid utf16".to_string()))
1211}
1212
1213fn parse_code_instructions(code: &[u8]) -> Result<Vec<Insn>, ClassReadError> {
1214    let mut reader = ByteReader::new(code);
1215    let mut insns = Vec::new();
1216
1217    while reader.remaining() > 0 {
1218        let opcode_offset = reader.pos();
1219        let opcode = reader.read_u1()?;
1220        let insn = match opcode {
1221            opcodes::NOP..=opcodes::DCONST_1 => Insn::Simple(opcode.into()),
1222            opcodes::BIPUSH => Insn::Int(IntInsnNode {
1223                insn: opcode.into(),
1224                operand: reader.read_i1()? as i32,
1225            }),
1226            opcodes::SIPUSH => Insn::Int(IntInsnNode {
1227                insn: opcode.into(),
1228                operand: reader.read_i2()? as i32,
1229            }),
1230            opcodes::LDC => Insn::Ldc(LdcInsnNode {
1231                insn: opcode.into(),
1232                value: LdcValue::Index(reader.read_u1()? as u16),
1233            }),
1234            opcodes::LDC_W | opcodes::LDC2_W => Insn::Ldc(LdcInsnNode {
1235                insn: opcode.into(),
1236                value: LdcValue::Index(reader.read_u2()?),
1237            }),
1238            opcodes::ILOAD..=opcodes::ALOAD => Insn::Var(VarInsnNode {
1239                insn: opcode.into(),
1240                var_index: reader.read_u1()? as u16,
1241            }),
1242            opcodes::ILOAD_0..=opcodes::SALOAD => Insn::Simple(opcode.into()),
1243            opcodes::ISTORE..=opcodes::ASTORE => Insn::Var(VarInsnNode {
1244                insn: opcode.into(),
1245                var_index: reader.read_u1()? as u16,
1246            }),
1247            opcodes::ISTORE_0..=opcodes::SASTORE => Insn::Simple(opcode.into()),
1248            opcodes::POP..=opcodes::LXOR => Insn::Simple(opcode.into()),
1249            opcodes::IINC => Insn::Iinc(IincInsnNode {
1250                insn: opcode.into(),
1251                var_index: reader.read_u1()? as u16,
1252                increment: reader.read_i1()? as i16,
1253            }),
1254            opcodes::I2L..=opcodes::DCMPG => Insn::Simple(opcode.into()),
1255            opcodes::IFEQ..=opcodes::JSR => Insn::Jump(JumpInsnNode {
1256                insn: opcode.into(),
1257                offset: reader.read_i2()? as i32,
1258            }),
1259            opcodes::RET => Insn::Var(VarInsnNode {
1260                insn: opcode.into(),
1261                var_index: reader.read_u1()? as u16,
1262            }),
1263            opcodes::TABLESWITCH => read_table_switch(&mut reader, opcode_offset)?,
1264            opcodes::LOOKUPSWITCH => read_lookup_switch(&mut reader, opcode_offset)?,
1265            opcodes::IRETURN..=opcodes::RETURN => Insn::Simple(InsnNode { opcode }),
1266            opcodes::GETSTATIC..=opcodes::PUTFIELD => Insn::Field(FieldInsnNode {
1267                insn: opcode.into(),
1268                field_ref: MemberRef::Index(reader.read_u2()?),
1269            }),
1270            opcodes::INVOKEVIRTUAL..=opcodes::INVOKESTATIC => Insn::Method(MethodInsnNode {
1271                insn: opcode.into(),
1272                method_ref: MemberRef::Index(reader.read_u2()?),
1273            }),
1274            opcodes::INVOKEINTERFACE => {
1275                let method_index = reader.read_u2()?;
1276                let count = reader.read_u1()?;
1277                let _ = reader.read_u1()?;
1278                Insn::InvokeInterface(InvokeInterfaceInsnNode {
1279                    insn: opcode.into(),
1280                    method_index,
1281                    count,
1282                })
1283            }
1284            opcodes::INVOKEDYNAMIC => {
1285                let method_index = reader.read_u2()?;
1286                let _ = reader.read_u2()?;
1287                Insn::InvokeDynamic(InvokeDynamicInsnNode {
1288                    insn: opcode.into(),
1289                    method_index,
1290                })
1291            }
1292            opcodes::NEW => Insn::Type(TypeInsnNode {
1293                insn: opcode.into(),
1294                type_index: reader.read_u2()?,
1295            }),
1296            opcodes::NEWARRAY => Insn::Int(IntInsnNode {
1297                insn: opcode.into(),
1298                operand: reader.read_u1()? as i32,
1299            }),
1300            opcodes::ANEWARRAY => Insn::Type(TypeInsnNode {
1301                insn: opcode.into(),
1302                type_index: reader.read_u2()?,
1303            }),
1304            opcodes::ARRAYLENGTH | opcodes::ATHROW => Insn::Simple(opcode.into()),
1305            opcodes::CHECKCAST | opcodes::INSTANCEOF => Insn::Type(TypeInsnNode {
1306                insn: opcode.into(),
1307                type_index: reader.read_u2()?,
1308            }),
1309            opcodes::MONITORENTER | opcodes::MONITOREXIT => Insn::Simple(opcode.into()),
1310            opcodes::WIDE => read_wide(&mut reader)?,
1311            opcodes::MULTIANEWARRAY => Insn::MultiANewArray(MultiANewArrayInsnNode {
1312                insn: opcode.into(),
1313                type_index: reader.read_u2()?,
1314                dimensions: reader.read_u1()?,
1315            }),
1316            opcodes::IFNULL | opcodes::IFNONNULL => Insn::Jump(JumpInsnNode {
1317                insn: opcode.into(),
1318                offset: reader.read_i2()? as i32,
1319            }),
1320            opcodes::GOTO_W | opcodes::JSR_W => Insn::Jump(JumpInsnNode {
1321                insn: opcode.into(),
1322                offset: reader.read_i4()?,
1323            }),
1324            opcodes::BREAKPOINT => Insn::Simple(opcode.into()),
1325            opcodes::IMPDEP1 | opcodes::IMPDEP2 => Insn::Simple(opcode.into()),
1326            _ => {
1327                return Err(ClassReadError::InvalidOpcode {
1328                    opcode,
1329                    offset: opcode_offset,
1330                });
1331            }
1332        };
1333
1334        insns.push(insn);
1335    }
1336
1337    Ok(insns)
1338}
1339
1340pub(crate) fn parse_code_instructions_public(code: &[u8]) -> Result<Vec<Insn>, ClassReadError> {
1341    parse_code_instructions(code)
1342}
1343
1344#[derive(Debug, Clone)]
1345struct ParsedInstruction {
1346    offset: u16,
1347    insn: Insn,
1348}
1349
1350fn parse_code_instructions_with_offsets(
1351    code: &[u8],
1352) -> Result<Vec<ParsedInstruction>, ClassReadError> {
1353    let mut reader = ByteReader::new(code);
1354    let mut insns = Vec::new();
1355
1356    while reader.remaining() > 0 {
1357        let opcode_offset = reader.pos();
1358        let opcode = reader.read_u1()?;
1359        let insn = match opcode {
1360            opcodes::NOP..=opcodes::DCONST_1 => Insn::Simple(opcode.into()),
1361            opcodes::BIPUSH => Insn::Int(IntInsnNode {
1362                insn: opcode.into(),
1363                operand: reader.read_i1()? as i32,
1364            }),
1365            opcodes::SIPUSH => Insn::Int(IntInsnNode {
1366                insn: opcode.into(),
1367                operand: reader.read_i2()? as i32,
1368            }),
1369            opcodes::LDC => Insn::Ldc(LdcInsnNode {
1370                insn: opcode.into(),
1371                value: LdcValue::Index(reader.read_u1()? as u16),
1372            }),
1373            opcodes::LDC_W | opcodes::LDC2_W => Insn::Ldc(LdcInsnNode {
1374                insn: opcode.into(),
1375                value: LdcValue::Index(reader.read_u2()?),
1376            }),
1377            opcodes::ILOAD..=opcodes::ALOAD => Insn::Var(VarInsnNode {
1378                insn: opcode.into(),
1379                var_index: reader.read_u1()? as u16,
1380            }),
1381            opcodes::ILOAD_0..=opcodes::SALOAD => Insn::Simple(opcode.into()),
1382            opcodes::ISTORE..=opcodes::ASTORE => Insn::Var(VarInsnNode {
1383                insn: opcode.into(),
1384                var_index: reader.read_u1()? as u16,
1385            }),
1386            opcodes::ISTORE_0..=opcodes::SASTORE => Insn::Simple(opcode.into()),
1387            opcodes::POP..=opcodes::LXOR => Insn::Simple(opcode.into()),
1388            opcodes::IINC => Insn::Iinc(IincInsnNode {
1389                insn: opcode.into(),
1390                var_index: reader.read_u1()? as u16,
1391                increment: reader.read_i1()? as i16,
1392            }),
1393            opcodes::I2L..=opcodes::DCMPG => Insn::Simple(opcode.into()),
1394            opcodes::IFEQ..=opcodes::JSR => Insn::Jump(JumpInsnNode {
1395                insn: opcode.into(),
1396                offset: reader.read_i2()? as i32,
1397            }),
1398            opcodes::RET => Insn::Var(VarInsnNode {
1399                insn: opcode.into(),
1400                var_index: reader.read_u1()? as u16,
1401            }),
1402            opcodes::TABLESWITCH => read_table_switch(&mut reader, opcode_offset)?,
1403            opcodes::LOOKUPSWITCH => read_lookup_switch(&mut reader, opcode_offset)?,
1404            opcodes::IRETURN..=opcodes::RETURN => Insn::Simple(opcode.into()),
1405            opcodes::GETSTATIC..=opcodes::PUTFIELD => Insn::Field(FieldInsnNode {
1406                insn: opcode.into(),
1407                field_ref: MemberRef::Index(reader.read_u2()?),
1408            }),
1409            opcodes::INVOKEVIRTUAL..=opcodes::INVOKESTATIC => Insn::Method(MethodInsnNode {
1410                insn: opcode.into(),
1411                method_ref: MemberRef::Index(reader.read_u2()?),
1412            }),
1413            opcodes::INVOKEINTERFACE => {
1414                let method_index = reader.read_u2()?;
1415                let count = reader.read_u1()?;
1416                let _ = reader.read_u1()?;
1417                Insn::InvokeInterface(InvokeInterfaceInsnNode {
1418                    insn: opcode.into(),
1419                    method_index,
1420                    count,
1421                })
1422            }
1423            opcodes::INVOKEDYNAMIC => {
1424                let method_index = reader.read_u2()?;
1425                let _ = reader.read_u2()?;
1426                Insn::InvokeDynamic(InvokeDynamicInsnNode {
1427                    insn: opcode.into(),
1428                    method_index,
1429                })
1430            }
1431            opcodes::NEW => Insn::Type(TypeInsnNode {
1432                insn: opcode.into(),
1433                type_index: reader.read_u2()?,
1434            }),
1435            opcodes::NEWARRAY => Insn::Int(IntInsnNode {
1436                insn: opcode.into(),
1437                operand: reader.read_u1()? as i32,
1438            }),
1439            opcodes::ANEWARRAY => Insn::Type(TypeInsnNode {
1440                insn: opcode.into(),
1441                type_index: reader.read_u2()?,
1442            }),
1443            opcodes::ARRAYLENGTH | opcodes::ATHROW => Insn::Simple(opcode.into()),
1444            opcodes::CHECKCAST | opcodes::INSTANCEOF => Insn::Type(TypeInsnNode {
1445                insn: opcode.into(),
1446                type_index: reader.read_u2()?,
1447            }),
1448            opcodes::MONITORENTER | opcodes::MONITOREXIT => Insn::Simple(opcode.into()),
1449            opcodes::WIDE => read_wide(&mut reader)?,
1450            opcodes::MULTIANEWARRAY => Insn::MultiANewArray(MultiANewArrayInsnNode {
1451                insn: opcode.into(),
1452                type_index: reader.read_u2()?,
1453                dimensions: reader.read_u1()?,
1454            }),
1455            opcodes::IFNULL | opcodes::IFNONNULL => Insn::Jump(JumpInsnNode {
1456                insn: opcode.into(),
1457                offset: reader.read_i2()? as i32,
1458            }),
1459            opcodes::GOTO_W | opcodes::JSR_W => Insn::Jump(JumpInsnNode {
1460                insn: opcode.into(),
1461                offset: reader.read_i4()?,
1462            }),
1463            opcodes::BREAKPOINT => Insn::Simple(opcode.into()),
1464            opcodes::IMPDEP1 | opcodes::IMPDEP2 => Insn::Simple(opcode.into()),
1465            _ => {
1466                return Err(ClassReadError::InvalidOpcode {
1467                    opcode,
1468                    offset: opcode_offset,
1469                });
1470            }
1471        };
1472
1473        insns.push(ParsedInstruction {
1474            offset: opcode_offset as u16,
1475            insn,
1476        });
1477    }
1478
1479    Ok(insns)
1480}
1481
1482fn build_insn_nodes(
1483    code: &[u8],
1484    exception_table: &[ExceptionTableEntry],
1485    cp: &[CpInfo],
1486) -> Result<(Vec<AbstractInsnNode>, Vec<TryCatchBlockNode>), ClassReadError> {
1487    let instructions = parse_code_instructions_with_offsets(code)?;
1488    let mut offsets = std::collections::HashSet::new();
1489    for instruction in &instructions {
1490        offsets.insert(instruction.offset);
1491        match &instruction.insn {
1492            Insn::Jump(node) => {
1493                offsets.insert((instruction.offset as i32 + node.offset) as u16);
1494            }
1495            Insn::TableSwitch(node) => {
1496                offsets.insert((instruction.offset as i32 + node.default_offset) as u16);
1497                for offset in &node.offsets {
1498                    offsets.insert((instruction.offset as i32 + *offset) as u16);
1499                }
1500            }
1501            Insn::LookupSwitch(node) => {
1502                offsets.insert((instruction.offset as i32 + node.default_offset) as u16);
1503                for (_, offset) in &node.pairs {
1504                    offsets.insert((instruction.offset as i32 + *offset) as u16);
1505                }
1506            }
1507            _ => {}
1508        }
1509    }
1510    for entry in exception_table {
1511        offsets.insert(entry.start_pc);
1512        offsets.insert(entry.end_pc);
1513        offsets.insert(entry.handler_pc);
1514    }
1515    offsets.insert(code.len() as u16);
1516
1517    let mut label_by_offset = std::collections::HashMap::new();
1518    for (next_id, offset) in offsets.into_iter().enumerate() {
1519        label_by_offset.insert(offset, LabelNode { id: next_id });
1520    }
1521
1522    let mut nodes = Vec::new();
1523    for instruction in instructions {
1524        if let Some(label) = label_by_offset.get(&{ instruction.offset }) {
1525            nodes.push(AbstractInsnNode::Label(*label));
1526        }
1527        nodes.push(AbstractInsnNode::Insn(instruction.insn));
1528    }
1529    if let Some(label) = label_by_offset.get(&(code.len() as u16)) {
1530        nodes.push(AbstractInsnNode::Label(*label));
1531    }
1532
1533    let mut try_catch_blocks = Vec::new();
1534    for entry in exception_table {
1535        let start = *label_by_offset
1536            .get(&entry.start_pc)
1537            .ok_or_else(|| ClassReadError::InvalidAttribute("missing start label".to_string()))?;
1538        let end = *label_by_offset
1539            .get(&entry.end_pc)
1540            .ok_or_else(|| ClassReadError::InvalidAttribute("missing end label".to_string()))?;
1541        let handler = *label_by_offset
1542            .get(&entry.handler_pc)
1543            .ok_or_else(|| ClassReadError::InvalidAttribute("missing handler label".to_string()))?;
1544        let catch_type = if entry.catch_type == 0 {
1545            None
1546        } else {
1547            Some(cp_class_name(cp, entry.catch_type)?.to_string())
1548        };
1549        try_catch_blocks.push(TryCatchBlockNode {
1550            start,
1551            end,
1552            handler,
1553            catch_type,
1554        });
1555    }
1556
1557    Ok((nodes, try_catch_blocks))
1558}
1559
1560pub(crate) fn build_insn_nodes_public(
1561    code: &[u8],
1562    exception_table: &[ExceptionTableEntry],
1563    cp: &[CpInfo],
1564) -> Result<(Vec<AbstractInsnNode>, Vec<TryCatchBlockNode>), ClassReadError> {
1565    build_insn_nodes(code, exception_table, cp)
1566}
1567
1568fn read_table_switch(
1569    reader: &mut ByteReader<'_>,
1570    opcode_offset: usize,
1571) -> Result<Insn, ClassReadError> {
1572    reader.align4(opcode_offset)?;
1573    let default_offset = reader.read_i4()?;
1574    let low = reader.read_i4()?;
1575    let high = reader.read_i4()?;
1576    let count = if high < low {
1577        0
1578    } else {
1579        (high - low + 1) as usize
1580    };
1581    let mut offsets = Vec::with_capacity(count);
1582    for _ in 0..count {
1583        offsets.push(reader.read_i4()?);
1584    }
1585    Ok(Insn::TableSwitch(TableSwitchInsnNode {
1586        insn: opcodes::TABLESWITCH.into(),
1587        default_offset,
1588        low,
1589        high,
1590        offsets,
1591    }))
1592}
1593
1594fn read_lookup_switch(
1595    reader: &mut ByteReader<'_>,
1596    opcode_offset: usize,
1597) -> Result<Insn, ClassReadError> {
1598    reader.align4(opcode_offset)?;
1599    let default_offset = reader.read_i4()?;
1600    let npairs = reader.read_i4()? as usize;
1601    let mut pairs = Vec::with_capacity(npairs);
1602    for _ in 0..npairs {
1603        let key = reader.read_i4()?;
1604        let offset = reader.read_i4()?;
1605        pairs.push((key, offset));
1606    }
1607    Ok(Insn::LookupSwitch(LookupSwitchInsnNode {
1608        insn: opcodes::LOOKUPSWITCH.into(),
1609        default_offset,
1610        pairs,
1611    }))
1612}
1613
1614fn read_wide(reader: &mut ByteReader<'_>) -> Result<Insn, ClassReadError> {
1615    let opcode = reader.read_u1()?;
1616    match opcode {
1617        opcodes::ILOAD..=opcodes::ALOAD | opcodes::ISTORE..=opcodes::ASTORE | opcodes::RET => {
1618            Ok(Insn::Var(VarInsnNode {
1619                insn: opcode.into(),
1620                var_index: reader.read_u2()?,
1621            }))
1622        }
1623        opcodes::IINC => Ok(Insn::Iinc(IincInsnNode {
1624            insn: opcode.into(),
1625            var_index: reader.read_u2()?,
1626            increment: reader.read_i2()?,
1627        })),
1628        _ => Err(ClassReadError::InvalidOpcode {
1629            opcode,
1630            offset: reader.pos().saturating_sub(1),
1631        }),
1632    }
1633}
1634
1635fn visit_instruction(
1636    cp: &[CpInfo],
1637    offset: i32,
1638    insn: Insn,
1639    mv: &mut dyn MethodVisitor,
1640) -> Result<(), ClassReadError> {
1641    match insn {
1642        Insn::Simple(node) => {
1643            mv.visit_insn(node.opcode);
1644        }
1645        Insn::Int(node) => {
1646            mv.visit_int_insn(node.insn.opcode, node.operand);
1647        }
1648        Insn::Var(node) => {
1649            mv.visit_var_insn(node.insn.opcode, node.var_index);
1650        }
1651        Insn::Type(node) => {
1652            let type_name = cp_class_name(cp, node.type_index)?;
1653            mv.visit_type_insn(node.insn.opcode, type_name);
1654        }
1655        Insn::Field(node) => {
1656            let index = match node.field_ref {
1657                MemberRef::Index(index) => index,
1658                MemberRef::Symbolic { .. } => {
1659                    return Err(ClassReadError::InvalidIndex(0));
1660                }
1661            };
1662            let (owner, name, desc) = cp_field_ref(cp, index)?;
1663            mv.visit_field_insn(node.insn.opcode, owner, name, desc);
1664        }
1665        Insn::Method(node) => {
1666            let index = match node.method_ref {
1667                MemberRef::Index(index) => index,
1668                MemberRef::Symbolic { .. } => {
1669                    return Err(ClassReadError::InvalidIndex(0));
1670                }
1671            };
1672            let (owner, name, desc, is_interface) = cp_method_ref(cp, index)?;
1673            mv.visit_method_insn(node.insn.opcode, owner, name, desc, is_interface);
1674        }
1675        Insn::InvokeInterface(node) => {
1676            let (owner, name, desc, _is_interface) = cp_method_ref(cp, node.method_index)?;
1677            mv.visit_method_insn(node.insn.opcode, owner, name, desc, true);
1678        }
1679        Insn::InvokeDynamic(node) => {
1680            let (name, desc) = cp_invoke_dynamic(cp, node.method_index)?;
1681            mv.visit_invoke_dynamic_insn(name, desc);
1682        }
1683        Insn::Jump(node) => {
1684            let target = offset + node.offset;
1685            mv.visit_jump_insn(node.insn.opcode, target);
1686        }
1687        Insn::Ldc(node) => {
1688            let index = match node.value {
1689                LdcValue::Index(index) => index,
1690                LdcValue::String(value) => {
1691                    mv.visit_ldc_insn(LdcConstant::String(value));
1692                    return Ok(());
1693                }
1694                LdcValue::Int(value) => {
1695                    mv.visit_ldc_insn(LdcConstant::Integer(value));
1696                    return Ok(());
1697                }
1698                LdcValue::Float(value) => {
1699                    mv.visit_ldc_insn(LdcConstant::Float(value));
1700                    return Ok(());
1701                }
1702                LdcValue::Long(value) => {
1703                    mv.visit_ldc_insn(LdcConstant::Long(value));
1704                    return Ok(());
1705                }
1706                LdcValue::Double(value) => {
1707                    mv.visit_ldc_insn(LdcConstant::Double(value));
1708                    return Ok(());
1709                }
1710            };
1711            let constant = cp_ldc_constant(cp, index)?;
1712            mv.visit_ldc_insn(constant);
1713        }
1714        Insn::Iinc(node) => {
1715            mv.visit_iinc_insn(node.var_index, node.increment);
1716        }
1717        Insn::TableSwitch(node) => {
1718            let targets = node
1719                .offsets
1720                .iter()
1721                .map(|value| offset + *value)
1722                .collect::<Vec<_>>();
1723            mv.visit_table_switch(offset + node.default_offset, node.low, node.high, &targets);
1724        }
1725        Insn::LookupSwitch(node) => {
1726            let pairs = node
1727                .pairs
1728                .iter()
1729                .map(|(key, value)| (*key, offset + *value))
1730                .collect::<Vec<_>>();
1731            mv.visit_lookup_switch(offset + node.default_offset, &pairs);
1732        }
1733        Insn::MultiANewArray(node) => {
1734            let type_name = cp_class_name(cp, node.type_index)?;
1735            mv.visit_multi_anewarray_insn(type_name, node.dimensions);
1736        }
1737    }
1738    Ok(())
1739}
1740
1741pub struct ByteReader<'a> {
1742    data: &'a [u8],
1743    pos: usize,
1744}
1745
1746impl<'a> ByteReader<'a> {
1747    pub fn new(data: &'a [u8]) -> Self {
1748        Self { data, pos: 0 }
1749    }
1750
1751    pub fn remaining(&self) -> usize {
1752        self.data.len().saturating_sub(self.pos)
1753    }
1754
1755    pub fn pos(&self) -> usize {
1756        self.pos
1757    }
1758
1759    pub fn align4(&mut self, opcode_offset: usize) -> Result<(), ClassReadError> {
1760        let mut padding = (4 - ((opcode_offset + 1) % 4)) % 4;
1761        while padding > 0 {
1762            self.read_u1()?;
1763            padding -= 1;
1764        }
1765        Ok(())
1766    }
1767
1768    pub fn read_u1(&mut self) -> Result<u8, ClassReadError> {
1769        if self.pos >= self.data.len() {
1770            return Err(ClassReadError::UnexpectedEof);
1771        }
1772        let value = self.data[self.pos];
1773        self.pos += 1;
1774        Ok(value)
1775    }
1776
1777    pub fn read_i1(&mut self) -> Result<i8, ClassReadError> {
1778        Ok(self.read_u1()? as i8)
1779    }
1780
1781    pub fn read_u2(&mut self) -> Result<u16, ClassReadError> {
1782        let bytes = self.read_bytes(2)?;
1783        Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
1784    }
1785
1786    pub fn read_i2(&mut self) -> Result<i16, ClassReadError> {
1787        Ok(self.read_u2()? as i16)
1788    }
1789
1790    pub fn read_u4(&mut self) -> Result<u32, ClassReadError> {
1791        let bytes = self.read_bytes(4)?;
1792        Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
1793    }
1794
1795    pub fn read_i4(&mut self) -> Result<i32, ClassReadError> {
1796        let bytes = self.read_bytes(4)?;
1797        Ok(i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
1798    }
1799
1800    pub fn read_u8(&mut self) -> Result<u64, ClassReadError> {
1801        let bytes = self.read_bytes(8)?;
1802        Ok(u64::from_be_bytes([
1803            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
1804        ]))
1805    }
1806
1807    pub fn read_bytes(&mut self, len: usize) -> Result<Vec<u8>, ClassReadError> {
1808        if self.pos + len > self.data.len() {
1809            return Err(ClassReadError::UnexpectedEof);
1810        }
1811        let bytes = self.data[self.pos..self.pos + len].to_vec();
1812        self.pos += len;
1813        Ok(bytes)
1814    }
1815}
1816
1817#[cfg(test)]
1818mod tests {
1819    use super::*;
1820    use std::cell::RefCell;
1821    use std::rc::Rc;
1822
1823    // A mock visitor to capture parsing results
1824    struct MockClassVisitor {
1825        pub visited_name: Rc<RefCell<Option<String>>>,
1826        pub visited_methods: Rc<RefCell<Vec<String>>>,
1827    }
1828
1829    impl MockClassVisitor {
1830        fn new() -> Self {
1831            Self {
1832                visited_name: Rc::new(RefCell::new(None)),
1833                visited_methods: Rc::new(RefCell::new(Vec::new())),
1834            }
1835        }
1836    }
1837
1838    impl ClassVisitor for MockClassVisitor {
1839        fn visit(
1840            &mut self,
1841            _major: u16,
1842            _minor: u16,
1843            _access_flags: u16,
1844            name: &str,
1845            _super_name: Option<&str>,
1846            _interfaces: &[String],
1847        ) {
1848            *self.visited_name.borrow_mut() = Some(name.to_string());
1849        }
1850
1851        fn visit_method(
1852            &mut self,
1853            _access_flags: u16,
1854            name: &str,
1855            _descriptor: &str,
1856        ) -> Option<Box<dyn MethodVisitor>> {
1857            self.visited_methods.borrow_mut().push(name.to_string());
1858            None
1859        }
1860    }
1861
1862    /// Helper to generate a minimal valid class file byte array (Java 8).
1863    /// Class Name: "TestClass"
1864    fn generate_minimal_class() -> Vec<u8> {
1865        let mut w = Vec::new();
1866        // Magic
1867        w.extend_from_slice(&0xCAFEBABE_u32.to_be_bytes());
1868        // Version (Java 8 = 52.0)
1869        w.extend_from_slice(&0_u16.to_be_bytes()); // minor
1870        w.extend_from_slice(&52_u16.to_be_bytes()); // major
1871
1872        // Constant Pool (Count: 5)
1873        // 1: UTF8 "TestClass"
1874        // 2: Class #1
1875        // 3: UTF8 "java/lang/Object"
1876        // 4: Class #3
1877        w.extend_from_slice(&5_u16.to_be_bytes()); // Count (N+1)
1878
1879        // #1 UTF8
1880        w.push(1);
1881        let name = "TestClass";
1882        w.extend_from_slice(&(name.len() as u16).to_be_bytes());
1883        w.extend_from_slice(name.as_bytes());
1884
1885        // #2 Class
1886        w.push(7);
1887        w.extend_from_slice(&1_u16.to_be_bytes());
1888
1889        // #3 UTF8
1890        w.push(1);
1891        let obj = "java/lang/Object";
1892        w.extend_from_slice(&(obj.len() as u16).to_be_bytes());
1893        w.extend_from_slice(obj.as_bytes());
1894
1895        // #4 Class
1896        w.push(7);
1897        w.extend_from_slice(&3_u16.to_be_bytes());
1898
1899        // Access Flags (PUBLIC)
1900        w.extend_from_slice(&0x0021_u16.to_be_bytes());
1901        // This Class (#2)
1902        w.extend_from_slice(&2_u16.to_be_bytes());
1903        // Super Class (#4)
1904        w.extend_from_slice(&4_u16.to_be_bytes());
1905
1906        // Interfaces Count
1907        w.extend_from_slice(&0_u16.to_be_bytes());
1908        // Fields Count
1909        w.extend_from_slice(&0_u16.to_be_bytes());
1910        // Methods Count
1911        w.extend_from_slice(&0_u16.to_be_bytes());
1912        // Attributes Count
1913        w.extend_from_slice(&0_u16.to_be_bytes());
1914
1915        w
1916    }
1917
1918    #[test]
1919    fn test_class_reader_header() {
1920        let bytes = generate_minimal_class();
1921        let reader = ClassReader::new(&bytes);
1922        let mut visitor = MockClassVisitor::new();
1923
1924        let result = reader.accept(&mut visitor, 0);
1925
1926        assert!(result.is_ok(), "Should parse valid class file");
1927        assert_eq!(
1928            *visitor.visited_name.borrow(),
1929            Some("TestClass".to_string())
1930        );
1931    }
1932
1933    #[test]
1934    fn test_invalid_magic() {
1935        // expected CA FE BA BE
1936        let bytes = vec![0x00, 0x00, 0x00, 0x00];
1937        let reader = ClassReader::new(&bytes);
1938        let mut visitor = MockClassVisitor::new();
1939
1940        let result = reader.accept(&mut visitor, 0);
1941        assert!(matches!(result, Err(ClassReadError::InvalidMagic(_))));
1942    }
1943
1944    #[test]
1945    fn test_code_reader_alignment() {
1946        // Test internal alignment logic for switch instructions
1947        let data = vec![0x00, 0x00, 0x00, 0x00]; // 4 bytes
1948        let mut reader = super::ByteReader::new(&data);
1949
1950        // If we are at pos 1, padding to 4-byte boundary
1951        reader.pos = 1;
1952        // 1 -> align 4 -> skips 3 bytes -> pos 4
1953        assert!(reader.align4(0).is_ok());
1954        assert_eq!(reader.pos(), 4);
1955    }
1956}