Skip to main content

rust_asm/
class_reader.rs

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