Skip to main content

rust_asm/
class_writer.rs

1use std::collections::HashMap;
2
3use crate::class_reader::{
4    AttributeInfo, BootstrapMethod, CodeAttribute, CpInfo, ExceptionTableEntry, InnerClass,
5    LineNumber, LocalVariable, MethodParameter, StackMapFrame, VerificationTypeInfo,
6};
7use crate::constants;
8use crate::error::ClassWriteError;
9use crate::insn::{
10    AbstractInsnNode, BootstrapArgument, FieldInsnNode, Handle, Insn, InsnList, InsnNode,
11    JumpInsnNode, JumpLabelInsnNode, Label, LabelNode, LdcInsnNode, LdcValue, LineNumberInsnNode,
12    MemberRef, MethodInsnNode, NodeList, VarInsnNode,
13};
14use crate::nodes::{ClassNode, FieldNode, InnerClassNode, MethodNode};
15use crate::opcodes;
16
17/// Flag to automatically compute the stack map frames.
18///
19/// When this flag is passed to the `ClassWriter`, it calculates the `StackMapTable`
20/// attribute based on the bytecode instructions. This requires the `compute_maxs` logic as well.
21pub const COMPUTE_FRAMES: u32 = 0x1;
22
23/// Flag to automatically compute the maximum stack size and local variables.
24///
25/// When this flag is set, the writer will calculate `max_stack` and `max_locals`
26/// for methods, ignoring the values provided in `visit_maxs`.
27pub const COMPUTE_MAXS: u32 = 0x2;
28
29/// A builder for the constant pool of a class.
30///
31/// This struct manages the deduplication of constant pool entries, ensuring that
32/// strings, classes, and member references are stored efficiently.
33#[derive(Debug, Default)]
34pub struct ConstantPoolBuilder {
35    cp: Vec<CpInfo>,
36    utf8: HashMap<String, u16>,
37    class: HashMap<String, u16>,
38    string: HashMap<String, u16>,
39    name_and_type: HashMap<(String, String), u16>,
40    field_ref: HashMap<(String, String, String), u16>,
41    method_ref: HashMap<(String, String, String), u16>,
42    interface_method_ref: HashMap<(String, String, String), u16>,
43    method_type: HashMap<String, u16>,
44    method_handle: HashMap<(u8, String, String, String, bool), u16>,
45    invoke_dynamic: HashMap<(u16, String, String), u16>,
46}
47
48impl ConstantPoolBuilder {
49    /// Creates a new, empty `ConstantPoolBuilder`.
50    ///
51    /// The constant pool starts with a dummy entry at index 0, as per JVM spec.
52    pub fn new() -> Self {
53        Self {
54            cp: vec![CpInfo::Unusable],
55            ..Default::default()
56        }
57    }
58
59    /// Consumes the builder and returns the raw vector of `CpInfo` entries.
60    pub fn into_pool(self) -> Vec<CpInfo> {
61        self.cp
62    }
63
64    /// Adds a UTF-8 string to the constant pool if it doesn't exist.
65    ///
66    /// Returns the index of the entry.
67    pub fn utf8(&mut self, value: &str) -> u16 {
68        if let Some(index) = self.utf8.get(value) {
69            return *index;
70        }
71        let index = self.push(CpInfo::Utf8(value.to_string()));
72        self.utf8.insert(value.to_string(), index);
73        index
74    }
75
76    /// Adds a Class constant to the pool.
77    ///
78    /// This will recursively add the UTF-8 name of the class.
79    pub fn class(&mut self, name: &str) -> u16 {
80        if let Some(index) = self.class.get(name) {
81            return *index;
82        }
83        let name_index = self.utf8(name);
84        let index = self.push(CpInfo::Class { name_index });
85        self.class.insert(name.to_string(), index);
86        index
87    }
88
89    /// Adds a String constant to the pool.
90    ///
91    /// This is for string literals (e.g., `ldc "foo"`).
92    pub fn string(&mut self, value: &str) -> u16 {
93        if let Some(index) = self.string.get(value) {
94            return *index;
95        }
96        let string_index = self.utf8(value);
97        let index = self.push(CpInfo::String { string_index });
98        self.string.insert(value.to_string(), index);
99        index
100    }
101
102    pub fn integer(&mut self, value: i32) -> u16 {
103        self.push(CpInfo::Integer(value))
104    }
105
106    pub fn float(&mut self, value: f32) -> u16 {
107        self.push(CpInfo::Float(value))
108    }
109
110    pub fn long(&mut self, value: i64) -> u16 {
111        let index = self.push(CpInfo::Long(value));
112        // Long takes two entries
113        self.cp.push(CpInfo::Unusable);
114        index
115    }
116
117    pub fn double(&mut self, value: f64) -> u16 {
118        let index = self.push(CpInfo::Double(value));
119        // Double takes two entries
120        self.cp.push(CpInfo::Unusable);
121        index
122    }
123
124    /// Adds a NameAndType constant to the pool.
125    ///
126    /// Used for field and method descriptors.
127    pub fn name_and_type(&mut self, name: &str, descriptor: &str) -> u16 {
128        let key = (name.to_string(), descriptor.to_string());
129        if let Some(index) = self.name_and_type.get(&key) {
130            return *index;
131        }
132        let name_index = self.utf8(name);
133        let descriptor_index = self.utf8(descriptor);
134        let index = self.push(CpInfo::NameAndType {
135            name_index,
136            descriptor_index,
137        });
138        self.name_and_type.insert(key, index);
139        index
140    }
141
142    /// Adds a Fieldref constant to the pool.
143    pub fn field_ref(&mut self, owner: &str, name: &str, descriptor: &str) -> u16 {
144        let key = (owner.to_string(), name.to_string(), descriptor.to_string());
145        if let Some(index) = self.field_ref.get(&key) {
146            return *index;
147        }
148        let class_index = self.class(owner);
149        let name_and_type_index = self.name_and_type(name, descriptor);
150        let index = self.push(CpInfo::Fieldref {
151            class_index,
152            name_and_type_index,
153        });
154        self.field_ref.insert(key, index);
155        index
156    }
157
158    /// Adds a Methodref constant to the pool.
159    pub fn method_ref(&mut self, owner: &str, name: &str, descriptor: &str) -> u16 {
160        let key = (owner.to_string(), name.to_string(), descriptor.to_string());
161        if let Some(index) = self.method_ref.get(&key) {
162            return *index;
163        }
164        let class_index = self.class(owner);
165        let name_and_type_index = self.name_and_type(name, descriptor);
166        let index = self.push(CpInfo::Methodref {
167            class_index,
168            name_and_type_index,
169        });
170        self.method_ref.insert(key, index);
171        index
172    }
173
174    pub fn interface_method_ref(&mut self, owner: &str, name: &str, descriptor: &str) -> u16 {
175        let key = (owner.to_string(), name.to_string(), descriptor.to_string());
176        if let Some(index) = self.interface_method_ref.get(&key) {
177            return *index;
178        }
179        let class_index = self.class(owner);
180        let name_and_type_index = self.name_and_type(name, descriptor);
181        let index = self.push(CpInfo::InterfaceMethodref {
182            class_index,
183            name_and_type_index,
184        });
185        self.interface_method_ref.insert(key, index);
186        index
187    }
188
189    pub fn method_type(&mut self, descriptor: &str) -> u16 {
190        if let Some(index) = self.method_type.get(descriptor) {
191            return *index;
192        }
193        let descriptor_index = self.utf8(descriptor);
194        let index = self.push(CpInfo::MethodType { descriptor_index });
195        self.method_type
196            .insert(descriptor.to_string(), index);
197        index
198    }
199
200    pub fn method_handle(&mut self, handle: &Handle) -> u16 {
201        let key = (
202            handle.reference_kind,
203            handle.owner.clone(),
204            handle.name.clone(),
205            handle.descriptor.clone(),
206            handle.is_interface,
207        );
208        if let Some(index) = self.method_handle.get(&key) {
209            return *index;
210        }
211        let reference_index = match handle.reference_kind {
212            1 | 2 | 3 | 4 => self.field_ref(&handle.owner, &handle.name, &handle.descriptor),
213            9 => self.interface_method_ref(&handle.owner, &handle.name, &handle.descriptor),
214            _ => self.method_ref(&handle.owner, &handle.name, &handle.descriptor),
215        };
216        let index = self.push(CpInfo::MethodHandle {
217            reference_kind: handle.reference_kind,
218            reference_index,
219        });
220        self.method_handle.insert(key, index);
221        index
222    }
223
224    pub fn invoke_dynamic(&mut self, bsm_index: u16, name: &str, descriptor: &str) -> u16 {
225        let key = (bsm_index, name.to_string(), descriptor.to_string());
226        if let Some(index) = self.invoke_dynamic.get(&key) {
227            return *index;
228        }
229        let name_and_type_index = self.name_and_type(name, descriptor);
230        let index = self.push(CpInfo::InvokeDynamic {
231            bootstrap_method_attr_index: bsm_index,
232            name_and_type_index,
233        });
234        self.invoke_dynamic.insert(key, index);
235        index
236    }
237
238    fn push(&mut self, entry: CpInfo) -> u16 {
239        self.cp.push(entry);
240        (self.cp.len() - 1) as u16
241    }
242}
243
244struct FieldData {
245    access_flags: u16,
246    name: String,
247    descriptor: String,
248    attributes: Vec<AttributeInfo>,
249}
250
251struct MethodData {
252    access_flags: u16,
253    name: String,
254    descriptor: String,
255    has_code: bool,
256    max_stack: u16,
257    max_locals: u16,
258    instructions: InsnList,
259    exception_table: Vec<ExceptionTableEntry>,
260    code_attributes: Vec<AttributeInfo>,
261    attributes: Vec<AttributeInfo>,
262}
263
264/// A writer that generates a Java Class File structure.
265///
266/// This is the main entry point for creating class files programmatically.
267/// It allows visiting the class header, fields, methods, and attributes.
268///
269/// # Example
270///
271/// ```rust
272/// use rust_asm::{class_writer::{ClassWriter, COMPUTE_FRAMES}, opcodes};
273///
274/// let mut cw = ClassWriter::new(COMPUTE_FRAMES);
275/// cw.visit(52, 0, 1, "com/example/MyClass", Some("java/lang/Object"), &[]);
276///
277/// let mut mv = cw.visit_method(1, "myMethod", "()V");
278/// mv.visit_code();
279/// mv.visit_insn(opcodes::RETURN);
280/// mv.visit_maxs(0, 0); // Computed automatically due to COMPUTE_FRAMES
281///
282/// let bytes = cw.to_bytes().unwrap();
283/// ```
284pub struct ClassWriter {
285    options: u32,
286    minor_version: u16,
287    major_version: u16,
288    access_flags: u16,
289    name: String,
290    super_name: Option<String>,
291    interfaces: Vec<String>,
292    fields: Vec<FieldData>,
293    methods: Vec<MethodData>,
294    attributes: Vec<AttributeInfo>,
295    source_file: Option<String>,
296    cp: ConstantPoolBuilder,
297}
298
299impl ClassWriter {
300    /// Creates a new `ClassWriter`.
301    ///
302    /// # Arguments
303    ///
304    /// * `options` - Bitwise flags to control generation (e.g., `COMPUTE_FRAMES`, `COMPUTE_MAXS`).
305    pub fn new(options: u32) -> Self {
306        Self {
307            options,
308            minor_version: 0,
309            major_version: 52,
310            access_flags: 0,
311            name: String::new(),
312            super_name: None,
313            interfaces: Vec::new(),
314            fields: Vec::new(),
315            methods: Vec::new(),
316            attributes: Vec::new(),
317            source_file: None,
318            cp: ConstantPoolBuilder::new(),
319        }
320    }
321
322    /// Defines the header of the class.
323    ///
324    /// # Arguments
325    ///
326    /// * `major` - The major version (e.g., 52 for Java 8).
327    /// * `minor` - The minor version.
328    /// * `access_flags` - Access modifiers (e.g., public, final).
329    /// * `name` - The internal name of the class (e.g., "java/lang/String").
330    /// * `super_name` - The internal name of the super class (e.g., `java/lang/String`, `a/b/c`).
331    ///   Use `None` for `Object`.
332    /// * `interfaces` - A list of interfaces implemented by this class.
333    pub fn visit(
334        &mut self,
335        major: u16,
336        minor: u16,
337        access_flags: u16,
338        name: &str,
339        super_name: Option<&str>,
340        interfaces: &[&str],
341    ) -> &mut Self {
342        self.major_version = major;
343        self.minor_version = minor;
344        self.access_flags = access_flags;
345        self.name = name.to_string();
346        self.super_name = super_name.map(|value| value.to_string());
347        self.interfaces = interfaces
348            .iter()
349            .map(|value| (*value).to_string())
350            .collect();
351        self
352    }
353
354    /// Sets the source file name attribute for the class.
355    pub fn visit_source_file(&mut self, name: &str) -> &mut Self {
356        self.source_file = Some(name.to_string());
357        self
358    }
359
360    /// Adds an `InnerClasses` entry for this class.
361    ///
362    /// This encodes the entry into the `InnerClasses` attribute using constant pool indices.
363    pub fn visit_inner_class(
364        &mut self,
365        name: &str,
366        outer_name: Option<&str>,
367        inner_name: Option<&str>,
368        access_flags: u16,
369    ) -> &mut Self {
370        let inner_class_info_index = self.cp.class(name);
371        let outer_class_info_index = match outer_name {
372            Some(value) => self.cp.class(value),
373            None => 0,
374        };
375        let inner_name_index = match inner_name {
376            Some(value) => self.cp.utf8(value),
377            None => 0,
378        };
379        let entry = InnerClass {
380            inner_class_info_index,
381            outer_class_info_index,
382            inner_name_index,
383            inner_class_access_flags: access_flags,
384        };
385
386        for attr in &mut self.attributes {
387            if let AttributeInfo::InnerClasses { classes } = attr {
388                classes.push(entry);
389                return self;
390            }
391        }
392
393        self.attributes.push(AttributeInfo::InnerClasses {
394            classes: vec![entry],
395        });
396        self
397    }
398
399    /// Visits a method of the class.
400    ///
401    /// Returns a `MethodVisitor` that should be used to define the method body.
402    /// The `visit_end` method of the returned visitor must be called to attach it to the class.
403    pub fn visit_method(
404        &mut self,
405        access_flags: u16,
406        name: &str,
407        descriptor: &str,
408    ) -> MethodVisitor {
409        MethodVisitor::new(access_flags, name, descriptor)
410    }
411
412    /// Visits a field of the class.
413    ///
414    /// Returns a `FieldVisitor` to define field attributes.
415    /// If `visit_end` is not called, the field is still committed when the visitor is dropped.
416    pub fn visit_field(&mut self, access_flags: u16, name: &str, descriptor: &str) -> FieldVisitor {
417        FieldVisitor::new(access_flags, name, descriptor, self as *mut ClassWriter)
418    }
419
420    /// Adds a custom attribute to the class.
421    pub fn add_attribute(&mut self, attr: AttributeInfo) -> &mut Self {
422        self.attributes.push(attr);
423        self
424    }
425
426    fn ensure_bootstrap_method(
427        &mut self,
428        bootstrap_method: &Handle,
429        bootstrap_args: &[BootstrapArgument],
430    ) -> u16 {
431        let bootstrap_method_ref = self.cp.method_handle(bootstrap_method);
432        let mut bootstrap_arguments = Vec::with_capacity(bootstrap_args.len());
433        for arg in bootstrap_args {
434            let index = match arg {
435                BootstrapArgument::Integer(value) => self.cp.integer(*value),
436                BootstrapArgument::Float(value) => self.cp.float(*value),
437                BootstrapArgument::Long(value) => self.cp.long(*value),
438                BootstrapArgument::Double(value) => self.cp.double(*value),
439                BootstrapArgument::String(value) => self.cp.string(value),
440                BootstrapArgument::Class(value) => self.cp.class(value),
441                BootstrapArgument::MethodType(value) => self.cp.method_type(value),
442                BootstrapArgument::Handle(value) => self.cp.method_handle(value),
443            };
444            bootstrap_arguments.push(index);
445        }
446
447        let methods = if let Some(AttributeInfo::BootstrapMethods { methods }) = self
448            .attributes
449            .iter_mut()
450            .find(|attr| matches!(attr, AttributeInfo::BootstrapMethods { .. }))
451        {
452            methods
453        } else {
454            self.attributes
455                .push(AttributeInfo::BootstrapMethods { methods: Vec::new() });
456            match self.attributes.last_mut() {
457                Some(AttributeInfo::BootstrapMethods { methods }) => methods,
458                _ => unreachable!("bootstrap methods attribute missing"),
459            }
460        };
461
462        methods.push(BootstrapMethod {
463            bootstrap_method_ref,
464            bootstrap_arguments,
465        });
466        (methods.len() - 1) as u16
467    }
468
469    /// Converts the builder state into a `ClassNode` object model.
470    pub fn to_class_node(mut self) -> Result<ClassNode, String> {
471        if self.name.is_empty() {
472            return Err("missing class name, call visit() first".to_string());
473        }
474
475        let this_class = self.cp.class(&self.name);
476        if let Some(name) = self.super_name.as_deref() {
477            self.cp.class(name);
478        }
479
480        let mut interface_indices = Vec::with_capacity(self.interfaces.len());
481        for name in &self.interfaces {
482            interface_indices.push(self.cp.class(name));
483        }
484
485        let mut fields = Vec::with_capacity(self.fields.len());
486        for field in self.fields {
487            let name_index = self.cp.utf8(&field.name);
488            let descriptor_index = self.cp.utf8(&field.descriptor);
489            fields.push(FieldNode {
490                access_flags: field.access_flags,
491                name_index,
492                descriptor_index,
493                name: field.name,
494                descriptor: field.descriptor,
495                attributes: field.attributes,
496            });
497        }
498
499        let mut methods = Vec::with_capacity(self.methods.len());
500        for method in self.methods {
501            methods.push(MethodNode {
502                access_flags: method.access_flags,
503                name: method.name,
504                descriptor: method.descriptor,
505                has_code: method.has_code,
506                max_stack: method.max_stack,
507                max_locals: method.max_locals,
508                instructions: method.instructions,
509                exception_table: method.exception_table,
510                code_attributes: method.code_attributes,
511                attributes: method.attributes,
512            });
513        }
514
515        if let Some(source_name) = self.source_file.as_ref() {
516            let source_index = self.cp.utf8(source_name);
517            self.attributes.push(AttributeInfo::SourceFile {
518                sourcefile_index: source_index,
519            });
520        }
521
522        let constant_pool = self.cp.into_pool();
523
524        fn cp_utf8(cp: &[CpInfo], index: u16) -> Result<&str, String> {
525            match cp.get(index as usize) {
526                Some(CpInfo::Utf8(value)) => Ok(value.as_str()),
527                _ => Err(format!("invalid constant pool utf8 index {}", index)),
528            }
529        }
530        fn class_name(cp: &[CpInfo], index: u16) -> Result<&str, String> {
531            match cp.get(index as usize) {
532                Some(CpInfo::Class { name_index }) => cp_utf8(cp, *name_index),
533                _ => Err(format!("invalid constant pool class index {}", index)),
534            }
535        }
536
537        let mut inner_classes = Vec::new();
538        for attr in &self.attributes {
539            if let AttributeInfo::InnerClasses { classes } = attr {
540                for entry in classes {
541                    let name =
542                        class_name(&constant_pool, entry.inner_class_info_index)?.to_string();
543                    let outer_name = if entry.outer_class_info_index == 0 {
544                        None
545                    } else {
546                        Some(class_name(&constant_pool, entry.outer_class_info_index)?.to_string())
547                    };
548                    let inner_name = if entry.inner_name_index == 0 {
549                        None
550                    } else {
551                        Some(cp_utf8(&constant_pool, entry.inner_name_index)?.to_string())
552                    };
553                    inner_classes.push(InnerClassNode {
554                        name,
555                        outer_name,
556                        inner_name,
557                        access_flags: entry.inner_class_access_flags,
558                    });
559                }
560            }
561        }
562
563        let mut outer_class = String::new();
564        if let Some(class_index) = self.attributes.iter().find_map(|attr| match attr {
565            AttributeInfo::EnclosingMethod { class_index, .. } => Some(*class_index),
566            _ => None,
567        }) {
568            outer_class = class_name(&constant_pool, class_index)?.to_string();
569        }
570        if outer_class.is_empty() {
571            for attr in &self.attributes {
572                if let AttributeInfo::InnerClasses { classes } = attr
573                    && let Some(entry) = classes.iter().find(|entry| {
574                        entry.inner_class_info_index == this_class
575                            && entry.outer_class_info_index != 0
576                    })
577                {
578                    outer_class =
579                        class_name(&constant_pool, entry.outer_class_info_index)?.to_string();
580                    break;
581                }
582            }
583        }
584
585        Ok(ClassNode {
586            minor_version: self.minor_version,
587            major_version: self.major_version,
588            access_flags: self.access_flags,
589            constant_pool,
590            this_class,
591            name: self.name,
592            super_name: self.super_name,
593            source_file: self.source_file.clone(),
594            interfaces: self.interfaces,
595            interface_indices,
596            fields,
597            methods,
598            attributes: self.attributes,
599            inner_classes,
600            outer_class,
601        })
602    }
603
604    /// Generates the raw byte vector representing the .class file.
605    ///
606    /// This method performs all necessary computations (stack map frames, max stack size)
607    /// based on the options provided in `new`.
608    pub fn to_bytes(self) -> Result<Vec<u8>, ClassWriteError> {
609        let options = self.options;
610        let class_node = self
611            .to_class_node()
612            .map_err(ClassWriteError::FrameComputation)?;
613        ClassFileWriter::new(options).to_bytes(&class_node)
614    }
615
616    pub fn write_class_node(
617        class_node: &ClassNode,
618        options: u32,
619    ) -> Result<Vec<u8>, ClassWriteError> {
620        ClassFileWriter::new(options).to_bytes(class_node)
621    }
622}
623
624/// A visitor to visit a Java method.
625///
626/// Used to generate the bytecode instructions, exception tables, and attributes
627/// for a specific method.
628pub struct MethodVisitor {
629    access_flags: u16,
630    name: String,
631    descriptor: String,
632    has_code: bool,
633    max_stack: u16,
634    max_locals: u16,
635    insns: NodeList,
636    exception_table: Vec<ExceptionTableEntry>,
637    code_attributes: Vec<AttributeInfo>,
638    attributes: Vec<AttributeInfo>,
639}
640
641impl MethodVisitor {
642    pub fn new(access_flags: u16, name: &str, descriptor: &str) -> Self {
643        Self {
644            access_flags,
645            name: name.to_string(),
646            descriptor: descriptor.to_string(),
647            has_code: false,
648            max_stack: 0,
649            max_locals: 0,
650            insns: NodeList::new(),
651            exception_table: Vec::new(),
652            code_attributes: Vec::new(),
653            attributes: Vec::new(),
654        }
655    }
656
657    /// Starts the visit of the method's code.
658    pub fn visit_code(&mut self) -> &mut Self {
659        self.has_code = true;
660        self
661    }
662
663    /// Visits a zero-operand instruction (e.g., NOP, RETURN).
664    pub fn visit_insn(&mut self, opcode: u8) -> &mut Self {
665        self.insns.add(Insn::from(Into::<InsnNode>::into(opcode)));
666        self
667    }
668
669    /// Visits a local variable instruction (e.g., ILOAD, ASTORE).
670    pub fn visit_var_insn(&mut self, opcode: u8, var_index: u16) -> &mut Self {
671        self.insns.add(Insn::Var(VarInsnNode {
672            insn: opcode.into(),
673            var_index,
674        }));
675        self
676    }
677
678    /// Visits a field instruction (e.g., GETFIELD, PUTSTATIC).
679    pub fn visit_field_insn(
680        &mut self,
681        opcode: u8,
682        owner: &str,
683        name: &str,
684        descriptor: &str,
685    ) -> &mut Self {
686        self.insns.add(Insn::Field(FieldInsnNode::new(
687            opcode, owner, name, descriptor,
688        )));
689        self
690    }
691
692    /// Visits a method instruction (e.g., INVOKEVIRTUAL).
693    pub fn visit_method_insn(
694        &mut self,
695        opcode: u8,
696        owner: &str,
697        name: &str,
698        descriptor: &str,
699        _is_interface: bool,
700    ) -> &mut Self {
701        self.insns.add(Insn::Method(MethodInsnNode::new(
702            opcode, owner, name, descriptor,
703        )));
704        self
705    }
706
707    pub fn visit_invokedynamic_insn(
708        &mut self,
709        name: &str,
710        descriptor: &str,
711        bootstrap_method: Handle,
712        bootstrap_args: &[BootstrapArgument],
713    ) -> &mut Self {
714        self.insns.add(Insn::InvokeDynamic(
715            crate::insn::InvokeDynamicInsnNode::new(
716                name,
717                descriptor,
718                bootstrap_method,
719                bootstrap_args,
720            ),
721        ));
722        self
723    }
724
725    pub fn visit_jump_insn(&mut self, opcode: u8, target: Label) -> &mut Self {
726        self.insns.add(JumpLabelInsnNode {
727            insn: opcode.into(),
728            target: LabelNode::from_label(target),
729        });
730        self
731    }
732
733    pub fn visit_label(&mut self, label: Label) -> &mut Self {
734        self.insns.add(LabelNode::from_label(label));
735        self
736    }
737
738    pub fn visit_line_number(&mut self, line: u16, start: LabelNode) -> &mut Self {
739        self.insns.add(LineNumberInsnNode::new(line, start));
740        self
741    }
742
743    /// Visits a constant instruction (LDC).
744    pub fn visit_ldc_insn(&mut self, value: LdcInsnNode) -> &mut Self {
745        self.insns.add(Insn::Ldc(value));
746        self
747    }
748
749    /// Visits the maximum stack size and number of local variables.
750    ///
751    /// If `COMPUTE_MAXS` or `COMPUTE_FRAMES` was passed to the ClassWriter,
752    /// these values may be ignored or recomputed.
753    pub fn visit_maxs(&mut self, max_stack: u16, max_locals: u16) -> &mut Self {
754        self.max_stack = max_stack;
755        self.max_locals = max_locals;
756        self
757    }
758
759    /// Finalizes the method and attaches it to the parent `ClassWriter`.
760    pub fn visit_end(mut self, class: &mut ClassWriter) {
761        let mut resolved = NodeList::new();
762        for node in self.insns.into_nodes() {
763            let node = match node {
764                AbstractInsnNode::Insn(Insn::InvokeDynamic(mut insn)) => {
765                    if insn.method_index == 0 {
766                        if let (Some(name), Some(descriptor), Some(bootstrap_method)) = (
767                            insn.name.take(),
768                            insn.descriptor.take(),
769                            insn.bootstrap_method.take(),
770                        ) {
771                            let bsm_index = class.ensure_bootstrap_method(
772                                &bootstrap_method,
773                                &insn.bootstrap_args,
774                            );
775                            let method_index =
776                                class.cp.invoke_dynamic(bsm_index, &name, &descriptor);
777                            insn.method_index = method_index;
778                        }
779                    }
780                    AbstractInsnNode::Insn(Insn::InvokeDynamic(insn))
781                }
782                other => other,
783            };
784            resolved.add_node(node);
785        }
786        self.insns = resolved;
787        let code = if self.has_code || !self.insns.nodes().is_empty() {
788            Some(build_code_attribute(
789                self.max_stack,
790                self.max_locals,
791                self.insns,
792                &mut class.cp,
793                std::mem::take(&mut self.exception_table),
794                std::mem::take(&mut self.code_attributes),
795            ))
796        } else {
797            None
798        };
799        let (has_code, max_stack, max_locals, instructions, exception_table, code_attributes) =
800            if let Some(code) = code {
801                let CodeAttribute {
802                    max_stack,
803                    max_locals,
804                    instructions,
805                    exception_table,
806                    attributes,
807                    ..
808                } = code;
809                let mut list = InsnList::new();
810                for insn in instructions {
811                    list.add(insn);
812                }
813                (
814                    true,
815                    max_stack,
816                    max_locals,
817                    list,
818                    exception_table,
819                    attributes,
820                )
821            } else {
822                (false, 0, 0, InsnList::new(), Vec::new(), Vec::new())
823            };
824        class.methods.push(MethodData {
825            access_flags: self.access_flags,
826            name: self.name,
827            descriptor: self.descriptor,
828            has_code,
829            max_stack,
830            max_locals,
831            instructions,
832            exception_table,
833            code_attributes,
834            attributes: std::mem::take(&mut self.attributes),
835        });
836    }
837}
838
839/// A visitor to visit a Java field.
840pub struct FieldVisitor {
841    access_flags: u16,
842    name: String,
843    descriptor: String,
844    attributes: Vec<AttributeInfo>,
845    class_ptr: Option<*mut ClassWriter>,
846    committed: bool,
847}
848
849impl FieldVisitor {
850    pub fn new(
851        access_flags: u16,
852        name: &str,
853        descriptor: &str,
854        class_ptr: *mut ClassWriter,
855    ) -> Self {
856        Self {
857            access_flags,
858            name: name.to_string(),
859            descriptor: descriptor.to_string(),
860            attributes: Vec::new(),
861            class_ptr: Some(class_ptr),
862            committed: false,
863        }
864    }
865
866    /// Adds an attribute to the field.
867    pub fn add_attribute(&mut self, attr: AttributeInfo) -> &mut Self {
868        self.attributes.push(attr);
869        self
870    }
871
872    /// Finalizes the field and attaches it to the parent `ClassWriter`.
873    /// If you don't call this, the field is still attached when the visitor is dropped.
874    pub fn visit_end(mut self, class: &mut ClassWriter) {
875        class.fields.push(FieldData {
876            access_flags: self.access_flags,
877            name: std::mem::take(&mut self.name),
878            descriptor: std::mem::take(&mut self.descriptor),
879            attributes: std::mem::take(&mut self.attributes),
880        });
881        self.committed = true;
882        self.class_ptr = None;
883    }
884}
885
886impl Drop for FieldVisitor {
887    fn drop(&mut self) {
888        if self.committed {
889            return;
890        }
891        let Some(ptr) = self.class_ptr else {
892            return;
893        };
894        // Safety: FieldVisitor is expected to be dropped before the ClassWriter it was created from.
895        unsafe {
896            let class = &mut *ptr;
897            class.fields.push(FieldData {
898                access_flags: self.access_flags,
899                name: std::mem::take(&mut self.name),
900                descriptor: std::mem::take(&mut self.descriptor),
901                attributes: std::mem::take(&mut self.attributes),
902            });
903        }
904        self.committed = true;
905        self.class_ptr = None;
906    }
907}
908
909pub struct CodeBody {
910    max_stack: u16,
911    max_locals: u16,
912    insns: NodeList,
913    exception_table: Vec<ExceptionTableEntry>,
914    attributes: Vec<AttributeInfo>,
915}
916
917impl CodeBody {
918    pub fn new(max_stack: u16, max_locals: u16, insns: NodeList) -> Self {
919        Self {
920            max_stack,
921            max_locals,
922            insns,
923            exception_table: Vec::new(),
924            attributes: Vec::new(),
925        }
926    }
927
928    pub fn build(self, cp: &mut ConstantPoolBuilder) -> CodeAttribute {
929        let mut code = Vec::new();
930        let mut instructions = Vec::new();
931        let mut insn_nodes = Vec::new();
932        let mut label_offsets: HashMap<usize, u16> = HashMap::new();
933        let mut pending_lines: Vec<LineNumberInsnNode> = Vec::new();
934        let mut jump_fixups: Vec<JumpFixup> = Vec::new();
935        for node in self.insns.into_nodes() {
936            match node {
937                AbstractInsnNode::Insn(insn) => {
938                    let resolved = emit_insn(&mut code, insn, cp);
939                    instructions.push(resolved.clone());
940                    insn_nodes.push(AbstractInsnNode::Insn(resolved));
941                }
942                AbstractInsnNode::JumpLabel(node) => {
943                    let opcode = node.insn.opcode;
944                    let start = code.len();
945                    code.push(opcode);
946                    if is_wide_jump(opcode) {
947                        write_i4(&mut code, 0);
948                    } else {
949                        write_i2(&mut code, 0);
950                    }
951                    let insn = Insn::Jump(JumpInsnNode {
952                        insn: InsnNode { opcode },
953                        offset: 0,
954                    });
955                    instructions.push(insn.clone());
956                    insn_nodes.push(AbstractInsnNode::Insn(insn.clone()));
957                    jump_fixups.push(JumpFixup {
958                        start,
959                        opcode,
960                        target: node.target,
961                        insn_index: instructions.len() - 1,
962                        node_index: insn_nodes.len() - 1,
963                    });
964                }
965                AbstractInsnNode::Label(label) => {
966                    let offset = code.len();
967                    if offset <= u16::MAX as usize {
968                        label_offsets.insert(label.id, offset as u16);
969                    }
970                    insn_nodes.push(AbstractInsnNode::Label(label));
971                }
972                AbstractInsnNode::LineNumber(line) => {
973                    pending_lines.push(line);
974                    insn_nodes.push(AbstractInsnNode::LineNumber(line));
975                }
976            }
977        }
978        for fixup in jump_fixups {
979            if let Some(target_offset) = label_offsets.get(&fixup.target.id) {
980                let offset = *target_offset as i32 - fixup.start as i32;
981                if is_wide_jump(fixup.opcode) {
982                    write_i4_at(&mut code, fixup.start + 1, offset);
983                } else {
984                    write_i2_at(&mut code, fixup.start + 1, offset as i16);
985                }
986                let resolved = Insn::Jump(JumpInsnNode {
987                    insn: InsnNode {
988                        opcode: fixup.opcode,
989                    },
990                    offset,
991                });
992                instructions[fixup.insn_index] = resolved.clone();
993                insn_nodes[fixup.node_index] = AbstractInsnNode::Insn(resolved);
994            }
995        }
996        let mut attributes = self.attributes;
997        if !pending_lines.is_empty() {
998            let mut entries = Vec::new();
999            for line in pending_lines {
1000                if let Some(start_pc) = label_offsets.get(&line.start.id) {
1001                    entries.push(LineNumber {
1002                        start_pc: *start_pc,
1003                        line_number: line.line,
1004                    });
1005                }
1006            }
1007            if !entries.is_empty() {
1008                attributes.push(AttributeInfo::LineNumberTable { entries });
1009            }
1010        }
1011        CodeAttribute {
1012            max_stack: self.max_stack,
1013            max_locals: self.max_locals,
1014            code,
1015            instructions,
1016            insn_nodes,
1017            exception_table: self.exception_table,
1018            try_catch_blocks: Vec::new(),
1019            attributes,
1020        }
1021    }
1022}
1023
1024#[derive(Debug, Clone, Copy)]
1025struct JumpFixup {
1026    start: usize,
1027    opcode: u8,
1028    target: LabelNode,
1029    insn_index: usize,
1030    node_index: usize,
1031}
1032
1033fn is_wide_jump(opcode: u8) -> bool {
1034    matches!(opcode, opcodes::GOTO_W | opcodes::JSR_W)
1035}
1036
1037fn jump_size(opcode: u8) -> usize {
1038    if is_wide_jump(opcode) { 5 } else { 3 }
1039}
1040
1041fn build_code_attribute(
1042    max_stack: u16,
1043    max_locals: u16,
1044    insns: NodeList,
1045    cp: &mut ConstantPoolBuilder,
1046    exception_table: Vec<ExceptionTableEntry>,
1047    attributes: Vec<AttributeInfo>,
1048) -> CodeAttribute {
1049    CodeBody {
1050        max_stack,
1051        max_locals,
1052        insns,
1053        exception_table,
1054        attributes,
1055    }
1056    .build(cp)
1057}
1058
1059fn build_code_from_insn_list(insns: &InsnList) -> Result<(Vec<u8>, Vec<Insn>), ClassWriteError> {
1060    let mut code = Vec::new();
1061    let mut instructions = Vec::with_capacity(insns.insns().len());
1062    for insn in insns.insns() {
1063        let emitted = emit_insn_raw(&mut code, insn.clone())?;
1064        instructions.push(emitted);
1065    }
1066    Ok((code, instructions))
1067}
1068
1069fn emit_insn_raw(code: &mut Vec<u8>, insn: Insn) -> Result<Insn, ClassWriteError> {
1070    let offset = code.len();
1071    let out = match insn {
1072        Insn::Simple(node) => {
1073            code.push(node.opcode);
1074            Insn::Simple(node)
1075        }
1076        Insn::Int(node) => {
1077            code.push(node.insn.opcode);
1078            match node.insn.opcode {
1079                opcodes::BIPUSH => write_i1(code, node.operand as i8),
1080                opcodes::SIPUSH => write_i2(code, node.operand as i16),
1081                opcodes::NEWARRAY => write_u1(code, node.operand as u8),
1082                _ => write_i1(code, node.operand as i8),
1083            }
1084            Insn::Int(node)
1085        }
1086        Insn::Var(node) => {
1087            code.push(node.insn.opcode);
1088            write_u1(code, node.var_index as u8);
1089            Insn::Var(node)
1090        }
1091        Insn::Type(node) => {
1092            code.push(node.insn.opcode);
1093            write_u2(code, node.type_index);
1094            Insn::Type(node)
1095        }
1096        Insn::Field(node) => {
1097            let index = match node.field_ref {
1098                MemberRef::Index(index) => index,
1099                MemberRef::Symbolic { .. } => {
1100                    return Err(ClassWriteError::FrameComputation(
1101                        "symbolic field ref in method instructions".to_string(),
1102                    ));
1103                }
1104            };
1105            code.push(node.insn.opcode);
1106            write_u2(code, index);
1107            Insn::Field(node)
1108        }
1109        Insn::Method(node) => {
1110            let index = match node.method_ref {
1111                MemberRef::Index(index) => index,
1112                MemberRef::Symbolic { .. } => {
1113                    return Err(ClassWriteError::FrameComputation(
1114                        "symbolic method ref in method instructions".to_string(),
1115                    ));
1116                }
1117            };
1118            code.push(node.insn.opcode);
1119            write_u2(code, index);
1120            Insn::Method(node)
1121        }
1122        Insn::InvokeInterface(node) => {
1123            code.push(node.insn.opcode);
1124            write_u2(code, node.method_index);
1125            write_u1(code, node.count);
1126            write_u1(code, 0);
1127            Insn::InvokeInterface(node)
1128        }
1129        Insn::InvokeDynamic(node) => {
1130            code.push(node.insn.opcode);
1131            write_u2(code, node.method_index);
1132            write_u2(code, 0);
1133            Insn::InvokeDynamic(node)
1134        }
1135        Insn::Jump(node) => {
1136            code.push(node.insn.opcode);
1137            match node.insn.opcode {
1138                opcodes::GOTO_W | opcodes::JSR_W => write_i4(code, node.offset),
1139                _ => write_i2(code, node.offset as i16),
1140            }
1141            Insn::Jump(node)
1142        }
1143        Insn::Ldc(node) => {
1144            let index = match node.value {
1145                LdcValue::Index(index) => index,
1146                _ => {
1147                    return Err(ClassWriteError::FrameComputation(
1148                        "non-index ldc in method instructions".to_string(),
1149                    ));
1150                }
1151            };
1152            let opcode = if matches!(
1153                node.insn.opcode,
1154                opcodes::LDC | opcodes::LDC_W | opcodes::LDC2_W
1155            ) {
1156                node.insn.opcode
1157            } else if index <= 0xFF {
1158                opcodes::LDC
1159            } else {
1160                opcodes::LDC_W
1161            };
1162            code.push(opcode);
1163            if opcode == opcodes::LDC {
1164                write_u1(code, index as u8);
1165            } else {
1166                write_u2(code, index);
1167            }
1168            Insn::Ldc(LdcInsnNode {
1169                insn: opcode.into(),
1170                value: LdcValue::Index(index),
1171            })
1172        }
1173        Insn::Iinc(node) => {
1174            code.push(node.insn.opcode);
1175            write_u1(code, node.var_index as u8);
1176            write_i1(code, node.increment as i8);
1177            Insn::Iinc(node)
1178        }
1179        Insn::TableSwitch(node) => {
1180            code.push(node.insn.opcode);
1181            write_switch_padding(code, offset);
1182            write_i4(code, node.default_offset);
1183            write_i4(code, node.low);
1184            write_i4(code, node.high);
1185            for value in &node.offsets {
1186                write_i4(code, *value);
1187            }
1188            Insn::TableSwitch(node)
1189        }
1190        Insn::LookupSwitch(node) => {
1191            code.push(node.insn.opcode);
1192            write_switch_padding(code, offset);
1193            write_i4(code, node.default_offset);
1194            write_i4(code, node.pairs.len() as i32);
1195            for (key, value) in &node.pairs {
1196                write_i4(code, *key);
1197                write_i4(code, *value);
1198            }
1199            Insn::LookupSwitch(node)
1200        }
1201        Insn::MultiANewArray(node) => {
1202            code.push(node.insn.opcode);
1203            write_u2(code, node.type_index);
1204            write_u1(code, node.dimensions);
1205            Insn::MultiANewArray(node)
1206        }
1207    };
1208    Ok(out)
1209}
1210
1211fn emit_insn(code: &mut Vec<u8>, insn: Insn, cp: &mut ConstantPoolBuilder) -> Insn {
1212    let offset = code.len();
1213    match insn {
1214        Insn::Simple(node) => {
1215            code.push(node.opcode);
1216            Insn::Simple(node)
1217        }
1218        Insn::Int(node) => {
1219            code.push(node.insn.opcode);
1220            match node.insn.opcode {
1221                opcodes::BIPUSH => write_i1(code, node.operand as i8),
1222                opcodes::SIPUSH => write_i2(code, node.operand as i16),
1223                opcodes::NEWARRAY => write_u1(code, node.operand as u8),
1224                _ => write_i1(code, node.operand as i8),
1225            }
1226            Insn::Int(node)
1227        }
1228        Insn::Var(node) => {
1229            code.push(node.insn.opcode);
1230            write_u1(code, node.var_index as u8);
1231            Insn::Var(node)
1232        }
1233        Insn::Type(node) => {
1234            code.push(node.insn.opcode);
1235            write_u2(code, node.type_index);
1236            Insn::Type(node)
1237        }
1238        Insn::Field(node) => {
1239            code.push(node.insn.opcode);
1240            let (index, resolved) = resolve_field_ref(node, cp);
1241            write_u2(code, index);
1242            Insn::Field(resolved)
1243        }
1244        Insn::Method(node) => {
1245            code.push(node.insn.opcode);
1246            let (index, resolved) = resolve_method_ref(node, cp);
1247            write_u2(code, index);
1248            Insn::Method(resolved)
1249        }
1250        Insn::InvokeInterface(node) => {
1251            code.push(node.insn.opcode);
1252            write_u2(code, node.method_index);
1253            write_u1(code, node.count);
1254            write_u1(code, 0);
1255            Insn::InvokeInterface(node)
1256        }
1257        Insn::InvokeDynamic(node) => {
1258            code.push(node.insn.opcode);
1259            write_u2(code, node.method_index);
1260            write_u2(code, 0);
1261            Insn::InvokeDynamic(node)
1262        }
1263        Insn::Jump(node) => {
1264            code.push(node.insn.opcode);
1265            match node.insn.opcode {
1266                opcodes::GOTO_W | opcodes::JSR_W => write_i4(code, node.offset),
1267                _ => write_i2(code, node.offset as i16),
1268            }
1269            Insn::Jump(node)
1270        }
1271        Insn::Ldc(node) => {
1272            let (opcode, index, resolved) = resolve_ldc(node, cp);
1273            code.push(opcode);
1274            if opcode == opcodes::LDC {
1275                write_u1(code, index as u8);
1276            } else {
1277                write_u2(code, index);
1278            }
1279            Insn::Ldc(resolved)
1280        }
1281        Insn::Iinc(node) => {
1282            code.push(node.insn.opcode);
1283            write_u1(code, node.var_index as u8);
1284            write_i1(code, node.increment as i8);
1285            Insn::Iinc(node)
1286        }
1287        Insn::TableSwitch(node) => {
1288            code.push(node.insn.opcode);
1289            write_switch_padding(code, offset);
1290            write_i4(code, node.default_offset);
1291            write_i4(code, node.low);
1292            write_i4(code, node.high);
1293            for value in &node.offsets {
1294                write_i4(code, *value);
1295            }
1296            Insn::TableSwitch(node)
1297        }
1298        Insn::LookupSwitch(node) => {
1299            code.push(node.insn.opcode);
1300            write_switch_padding(code, offset);
1301            write_i4(code, node.default_offset);
1302            write_i4(code, node.pairs.len() as i32);
1303            for (key, value) in &node.pairs {
1304                write_i4(code, *key);
1305                write_i4(code, *value);
1306            }
1307            Insn::LookupSwitch(node)
1308        }
1309        Insn::MultiANewArray(node) => {
1310            code.push(node.insn.opcode);
1311            write_u2(code, node.type_index);
1312            write_u1(code, node.dimensions);
1313            Insn::MultiANewArray(node)
1314        }
1315    }
1316}
1317
1318fn resolve_field_ref(node: FieldInsnNode, cp: &mut ConstantPoolBuilder) -> (u16, FieldInsnNode) {
1319    match node.field_ref {
1320        MemberRef::Index(index) => (index, node),
1321        MemberRef::Symbolic {
1322            owner,
1323            name,
1324            descriptor,
1325        } => {
1326            let index = cp.field_ref(&owner, &name, &descriptor);
1327            (
1328                index,
1329                FieldInsnNode {
1330                    insn: node.insn,
1331                    field_ref: MemberRef::Index(index),
1332                },
1333            )
1334        }
1335    }
1336}
1337
1338fn resolve_method_ref(node: MethodInsnNode, cp: &mut ConstantPoolBuilder) -> (u16, MethodInsnNode) {
1339    match node.method_ref {
1340        MemberRef::Index(index) => (index, node),
1341        MemberRef::Symbolic {
1342            owner,
1343            name,
1344            descriptor,
1345        } => {
1346            let index = cp.method_ref(&owner, &name, &descriptor);
1347            (
1348                index,
1349                MethodInsnNode {
1350                    insn: node.insn,
1351                    method_ref: MemberRef::Index(index),
1352                },
1353            )
1354        }
1355    }
1356}
1357
1358fn resolve_ldc(node: LdcInsnNode, cp: &mut ConstantPoolBuilder) -> (u8, u16, LdcInsnNode) {
1359    match node.value {
1360        LdcValue::Index(index) => {
1361            let opcode = if index <= 0xFF {
1362                opcodes::LDC
1363            } else {
1364                opcodes::LDC_W
1365            };
1366            (
1367                opcode,
1368                index,
1369                LdcInsnNode {
1370                    insn: opcode.into(),
1371                    value: LdcValue::Index(index),
1372                },
1373            )
1374        }
1375        LdcValue::String(value) => {
1376            let index = cp.string(&value);
1377            let opcode = if index <= 0xFF {
1378                opcodes::LDC
1379            } else {
1380                opcodes::LDC_W
1381            };
1382            (
1383                opcode,
1384                index,
1385                LdcInsnNode {
1386                    insn: opcode.into(),
1387                    value: LdcValue::Index(index),
1388                },
1389            )
1390        }
1391        LdcValue::Int(value) => {
1392            let index = cp.integer(value);
1393            let opcode = if index <= 0xFF {
1394                opcodes::LDC
1395            } else {
1396                opcodes::LDC_W
1397            };
1398            (
1399                opcode,
1400                index,
1401                LdcInsnNode {
1402                    insn: opcode.into(),
1403                    value: LdcValue::Index(index),
1404                },
1405            )
1406        }
1407        LdcValue::Float(value) => {
1408            let index = cp.float(value);
1409            let opcode = if index <= 0xFF {
1410                opcodes::LDC
1411            } else {
1412                opcodes::LDC_W
1413            };
1414            (
1415                opcode,
1416                index,
1417                LdcInsnNode {
1418                    insn: opcode.into(),
1419                    value: LdcValue::Index(index),
1420                },
1421            )
1422        }
1423        LdcValue::Long(value) => {
1424            let index = cp.long(value);
1425            (
1426                opcodes::LDC2_W,
1427                index,
1428                LdcInsnNode {
1429                    insn: opcodes::LDC2_W.into(),
1430                    value: LdcValue::Index(index),
1431                },
1432            )
1433        }
1434        LdcValue::Double(value) => {
1435            let index = cp.double(value);
1436            (
1437                opcodes::LDC2_W,
1438                index,
1439                LdcInsnNode {
1440                    insn: opcodes::LDC2_W.into(),
1441                    value: LdcValue::Index(index),
1442                },
1443            )
1444        }
1445    }
1446}
1447
1448pub struct ClassFileWriter {
1449    options: u32,
1450}
1451
1452impl ClassFileWriter {
1453    pub fn new(compute_frames_options: u32) -> Self {
1454        Self {
1455            options: compute_frames_options,
1456        }
1457    }
1458
1459    pub fn to_bytes(&self, class_node: &ClassNode) -> Result<Vec<u8>, ClassWriteError> {
1460        if class_node.constant_pool.is_empty() {
1461            return Err(ClassWriteError::MissingConstantPool);
1462        }
1463
1464        let mut cp = class_node.constant_pool.clone();
1465        let mut out = Vec::new();
1466        write_u4(&mut out, 0xCAFEBABE);
1467        write_u2(&mut out, class_node.minor_version);
1468        write_u2(&mut out, class_node.major_version);
1469
1470        let mut class_attributes = class_node.attributes.clone();
1471        if let Some(source_file) = &class_node.source_file {
1472            class_attributes.retain(|attr| !matches!(attr, AttributeInfo::SourceFile { .. }));
1473            let source_index = ensure_utf8(&mut cp, source_file);
1474            class_attributes.push(AttributeInfo::SourceFile {
1475                sourcefile_index: source_index,
1476            });
1477        }
1478
1479        let mut attribute_names = Vec::new();
1480        collect_attribute_names(&class_attributes, &mut attribute_names);
1481        for field in &class_node.fields {
1482            collect_attribute_names(&field.attributes, &mut attribute_names);
1483        }
1484        for method in &class_node.methods {
1485            collect_attribute_names(&method.attributes, &mut attribute_names);
1486            if method.has_code {
1487                attribute_names.push("Code".to_string());
1488                collect_attribute_names(&method.code_attributes, &mut attribute_names);
1489            }
1490        }
1491        for name in attribute_names {
1492            ensure_utf8(&mut cp, &name);
1493        }
1494        for method in &class_node.methods {
1495            ensure_utf8(&mut cp, &method.name);
1496            ensure_utf8(&mut cp, &method.descriptor);
1497        }
1498
1499        let mut precomputed_stack_maps: Vec<Option<Vec<StackMapFrame>>> =
1500            Vec::with_capacity(class_node.methods.len());
1501        let mut precomputed_maxs: Vec<Option<(u16, u16)>> =
1502            Vec::with_capacity(class_node.methods.len());
1503        let compute_frames = self.options & COMPUTE_FRAMES != 0;
1504        let compute_maxs_flag = self.options & COMPUTE_MAXS != 0;
1505        if compute_frames {
1506            ensure_utf8(&mut cp, "StackMapTable");
1507            for method in &class_node.methods {
1508                if method.has_code {
1509                    let code = method_code_attribute(method)?;
1510                    let maxs = if compute_maxs_flag {
1511                        Some(compute_maxs(method, class_node, &code, &cp)?)
1512                    } else {
1513                        None
1514                    };
1515                    let max_locals = maxs.map(|item| item.1).unwrap_or(code.max_locals);
1516                    let stack_map =
1517                        compute_stack_map_table(method, class_node, &code, &mut cp, max_locals)?;
1518                    precomputed_stack_maps.push(Some(stack_map));
1519                    precomputed_maxs.push(maxs);
1520                } else {
1521                    precomputed_stack_maps.push(None);
1522                    precomputed_maxs.push(None);
1523                }
1524            }
1525        } else if compute_maxs_flag {
1526            for method in &class_node.methods {
1527                if method.has_code {
1528                    let code = method_code_attribute(method)?;
1529                    precomputed_maxs.push(Some(compute_maxs(method, class_node, &code, &cp)?));
1530                } else {
1531                    precomputed_maxs.push(None);
1532                }
1533            }
1534            precomputed_stack_maps.resize(class_node.methods.len(), None);
1535        } else {
1536            precomputed_stack_maps.resize(class_node.methods.len(), None);
1537            precomputed_maxs.resize(class_node.methods.len(), None);
1538        }
1539
1540        let super_class = match class_node.super_name.as_deref() {
1541            Some(name) => ensure_class(&mut cp, name),
1542            None => {
1543                if class_node.name == "java/lang/Object" {
1544                    0
1545                } else {
1546                    ensure_class(&mut cp, "java/lang/Object")
1547                }
1548            }
1549        };
1550
1551        write_constant_pool(&mut out, &cp)?;
1552        write_u2(&mut out, class_node.access_flags);
1553        write_u2(&mut out, class_node.this_class);
1554        write_u2(&mut out, super_class);
1555        write_u2(&mut out, class_node.interface_indices.len() as u16);
1556        for index in &class_node.interface_indices {
1557            write_u2(&mut out, *index);
1558        }
1559
1560        write_u2(&mut out, class_node.fields.len() as u16);
1561        for field in &class_node.fields {
1562            write_field(&mut out, field, &mut cp)?;
1563        }
1564
1565        write_u2(&mut out, class_node.methods.len() as u16);
1566        for (index, method) in class_node.methods.iter().enumerate() {
1567            let stack_map = precomputed_stack_maps
1568                .get(index)
1569                .and_then(|item| item.as_ref());
1570            let maxs = precomputed_maxs.get(index).and_then(|item| *item);
1571            write_method(
1572                &mut out,
1573                method,
1574                class_node,
1575                &mut cp,
1576                self.options,
1577                stack_map,
1578                maxs,
1579            )?;
1580        }
1581
1582        write_u2(&mut out, class_attributes.len() as u16);
1583        for attr in &class_attributes {
1584            write_attribute(&mut out, attr, &mut cp, None, self.options, None, None)?;
1585        }
1586
1587        Ok(out)
1588    }
1589}
1590
1591fn write_field(
1592    out: &mut Vec<u8>,
1593    field: &FieldNode,
1594    cp: &mut Vec<CpInfo>,
1595) -> Result<(), ClassWriteError> {
1596    write_u2(out, field.access_flags);
1597    write_u2(out, field.name_index);
1598    write_u2(out, field.descriptor_index);
1599    write_u2(out, field.attributes.len() as u16);
1600    for attr in &field.attributes {
1601        write_attribute(out, attr, cp, None, 0, None, None)?;
1602    }
1603    Ok(())
1604}
1605
1606fn write_method(
1607    out: &mut Vec<u8>,
1608    method: &MethodNode,
1609    class_node: &ClassNode,
1610    cp: &mut Vec<CpInfo>,
1611    options: u32,
1612    precomputed_stack_map: Option<&Vec<StackMapFrame>>,
1613    precomputed_maxs: Option<(u16, u16)>,
1614) -> Result<(), ClassWriteError> {
1615    let name_index = ensure_utf8(cp, &method.name);
1616    let descriptor_index = ensure_utf8(cp, &method.descriptor);
1617    write_u2(out, method.access_flags);
1618    write_u2(out, name_index);
1619    write_u2(out, descriptor_index);
1620
1621    let mut attributes = method.attributes.clone();
1622    if method.has_code {
1623        let code = method_code_attribute(method)?;
1624        attributes.retain(|attr| !matches!(attr, AttributeInfo::Code(_)));
1625        attributes.push(AttributeInfo::Code(code));
1626    }
1627
1628    write_u2(out, attributes.len() as u16);
1629    for attr in &attributes {
1630        write_attribute(
1631            out,
1632            attr,
1633            cp,
1634            Some((method, class_node)),
1635            options,
1636            precomputed_stack_map,
1637            precomputed_maxs,
1638        )?;
1639    }
1640    Ok(())
1641}
1642
1643fn method_code_attribute(method: &MethodNode) -> Result<CodeAttribute, ClassWriteError> {
1644    let (code, instructions) = build_code_from_insn_list(&method.instructions)?;
1645    Ok(CodeAttribute {
1646        max_stack: method.max_stack,
1647        max_locals: method.max_locals,
1648        code,
1649        instructions,
1650        insn_nodes: Vec::new(),
1651        exception_table: method.exception_table.clone(),
1652        try_catch_blocks: Vec::new(),
1653        attributes: method.code_attributes.clone(),
1654    })
1655}
1656
1657fn write_attribute(
1658    out: &mut Vec<u8>,
1659    attr: &AttributeInfo,
1660    cp: &mut Vec<CpInfo>,
1661    method_ctx: Option<(&MethodNode, &ClassNode)>,
1662    options: u32,
1663    precomputed_stack_map: Option<&Vec<StackMapFrame>>,
1664    precomputed_maxs: Option<(u16, u16)>,
1665) -> Result<(), ClassWriteError> {
1666    match attr {
1667        AttributeInfo::Code(code) => {
1668            let name_index = ensure_utf8(cp, "Code");
1669            let mut info = Vec::new();
1670            let mut code_attributes = code.attributes.clone();
1671            let (max_stack, max_locals) =
1672                precomputed_maxs.unwrap_or((code.max_stack, code.max_locals));
1673            if options & COMPUTE_FRAMES != 0 {
1674                code_attributes.retain(|item| !matches!(item, AttributeInfo::StackMapTable { .. }));
1675                let stack_map = if let Some(precomputed) = precomputed_stack_map {
1676                    precomputed.clone()
1677                } else {
1678                    let (method, class_node) = method_ctx.ok_or_else(|| {
1679                        ClassWriteError::FrameComputation("missing method".to_string())
1680                    })?;
1681                    compute_stack_map_table(method, class_node, code, cp, max_locals)?
1682                };
1683                code_attributes.push(AttributeInfo::StackMapTable { entries: stack_map });
1684            }
1685
1686            write_u2(&mut info, max_stack);
1687            write_u2(&mut info, max_locals);
1688            write_u4(&mut info, code.code.len() as u32);
1689            info.extend_from_slice(&code.code);
1690            write_u2(&mut info, code.exception_table.len() as u16);
1691            for entry in &code.exception_table {
1692                write_exception_table_entry(&mut info, entry);
1693            }
1694            write_u2(&mut info, code_attributes.len() as u16);
1695            for nested in &code_attributes {
1696                write_attribute(&mut info, nested, cp, method_ctx, options, None, None)?;
1697            }
1698            write_attribute_with_info(out, name_index, &info);
1699        }
1700        AttributeInfo::ConstantValue {
1701            constantvalue_index,
1702        } => {
1703            let name_index = ensure_utf8(cp, "ConstantValue");
1704            let mut info = Vec::new();
1705            write_u2(&mut info, *constantvalue_index);
1706            write_attribute_with_info(out, name_index, &info);
1707        }
1708        AttributeInfo::Exceptions {
1709            exception_index_table,
1710        } => {
1711            let name_index = ensure_utf8(cp, "Exceptions");
1712            let mut info = Vec::new();
1713            write_u2(&mut info, exception_index_table.len() as u16);
1714            for index in exception_index_table {
1715                write_u2(&mut info, *index);
1716            }
1717            write_attribute_with_info(out, name_index, &info);
1718        }
1719        AttributeInfo::SourceFile { sourcefile_index } => {
1720            let name_index = ensure_utf8(cp, "SourceFile");
1721            let mut info = Vec::new();
1722            write_u2(&mut info, *sourcefile_index);
1723            write_attribute_with_info(out, name_index, &info);
1724        }
1725        AttributeInfo::LineNumberTable { entries } => {
1726            let name_index = ensure_utf8(cp, "LineNumberTable");
1727            let mut info = Vec::new();
1728            write_u2(&mut info, entries.len() as u16);
1729            for entry in entries {
1730                write_line_number(&mut info, entry);
1731            }
1732            write_attribute_with_info(out, name_index, &info);
1733        }
1734        AttributeInfo::LocalVariableTable { entries } => {
1735            let name_index = ensure_utf8(cp, "LocalVariableTable");
1736            let mut info = Vec::new();
1737            write_u2(&mut info, entries.len() as u16);
1738            for entry in entries {
1739                write_local_variable(&mut info, entry);
1740            }
1741            write_attribute_with_info(out, name_index, &info);
1742        }
1743        AttributeInfo::Signature { signature_index } => {
1744            let name_index = ensure_utf8(cp, "Signature");
1745            let mut info = Vec::new();
1746            write_u2(&mut info, *signature_index);
1747            write_attribute_with_info(out, name_index, &info);
1748        }
1749        AttributeInfo::StackMapTable { entries } => {
1750            let name_index = ensure_utf8(cp, "StackMapTable");
1751            let mut info = Vec::new();
1752            write_u2(&mut info, entries.len() as u16);
1753            for entry in entries {
1754                write_stack_map_frame(&mut info, entry);
1755            }
1756            write_attribute_with_info(out, name_index, &info);
1757        }
1758        AttributeInfo::Deprecated => {
1759            let name_index = ensure_utf8(cp, "Deprecated");
1760            write_attribute_with_info(out, name_index, &[]);
1761        }
1762        AttributeInfo::Synthetic => {
1763            let name_index = ensure_utf8(cp, "Synthetic");
1764            write_attribute_with_info(out, name_index, &[]);
1765        }
1766        AttributeInfo::InnerClasses { classes } => {
1767            let name_index = ensure_utf8(cp, "InnerClasses");
1768            let mut info = Vec::new();
1769            write_u2(&mut info, classes.len() as u16);
1770            for class in classes {
1771                write_inner_class(&mut info, class);
1772            }
1773            write_attribute_with_info(out, name_index, &info);
1774        }
1775        AttributeInfo::EnclosingMethod {
1776            class_index,
1777            method_index,
1778        } => {
1779            let name_index = ensure_utf8(cp, "EnclosingMethod");
1780            let mut info = Vec::new();
1781            write_u2(&mut info, *class_index);
1782            write_u2(&mut info, *method_index);
1783            write_attribute_with_info(out, name_index, &info);
1784        }
1785        AttributeInfo::BootstrapMethods { methods } => {
1786            let name_index = ensure_utf8(cp, "BootstrapMethods");
1787            let mut info = Vec::new();
1788            write_u2(&mut info, methods.len() as u16);
1789            for method in methods {
1790                write_bootstrap_method(&mut info, method);
1791            }
1792            write_attribute_with_info(out, name_index, &info);
1793        }
1794        AttributeInfo::MethodParameters { parameters } => {
1795            let name_index = ensure_utf8(cp, "MethodParameters");
1796            let mut info = Vec::new();
1797            write_u1(&mut info, parameters.len() as u8);
1798            for parameter in parameters {
1799                write_method_parameter(&mut info, parameter);
1800            }
1801            write_attribute_with_info(out, name_index, &info);
1802        }
1803        AttributeInfo::Unknown { name, info } => {
1804            let name_index = ensure_utf8(cp, name);
1805            write_attribute_with_info(out, name_index, info);
1806        }
1807    }
1808
1809    Ok(())
1810}
1811
1812fn write_attribute_with_info(out: &mut Vec<u8>, name_index: u16, info: &[u8]) {
1813    write_u2(out, name_index);
1814    write_u4(out, info.len() as u32);
1815    out.extend_from_slice(info);
1816}
1817
1818fn write_exception_table_entry(out: &mut Vec<u8>, entry: &ExceptionTableEntry) {
1819    write_u2(out, entry.start_pc);
1820    write_u2(out, entry.end_pc);
1821    write_u2(out, entry.handler_pc);
1822    write_u2(out, entry.catch_type);
1823}
1824
1825fn write_line_number(out: &mut Vec<u8>, entry: &LineNumber) {
1826    write_u2(out, entry.start_pc);
1827    write_u2(out, entry.line_number);
1828}
1829
1830fn write_local_variable(out: &mut Vec<u8>, entry: &LocalVariable) {
1831    write_u2(out, entry.start_pc);
1832    write_u2(out, entry.length);
1833    write_u2(out, entry.name_index);
1834    write_u2(out, entry.descriptor_index);
1835    write_u2(out, entry.index);
1836}
1837
1838fn write_inner_class(out: &mut Vec<u8>, entry: &InnerClass) {
1839    write_u2(out, entry.inner_class_info_index);
1840    write_u2(out, entry.outer_class_info_index);
1841    write_u2(out, entry.inner_name_index);
1842    write_u2(out, entry.inner_class_access_flags);
1843}
1844
1845fn write_bootstrap_method(out: &mut Vec<u8>, entry: &BootstrapMethod) {
1846    write_u2(out, entry.bootstrap_method_ref);
1847    write_u2(out, entry.bootstrap_arguments.len() as u16);
1848    for arg in &entry.bootstrap_arguments {
1849        write_u2(out, *arg);
1850    }
1851}
1852
1853fn write_method_parameter(out: &mut Vec<u8>, entry: &MethodParameter) {
1854    write_u2(out, entry.name_index);
1855    write_u2(out, entry.access_flags);
1856}
1857
1858fn write_stack_map_frame(out: &mut Vec<u8>, frame: &StackMapFrame) {
1859    match frame {
1860        StackMapFrame::SameFrame { offset_delta } => {
1861            write_u1(out, *offset_delta as u8);
1862        }
1863        StackMapFrame::SameLocals1StackItemFrame {
1864            offset_delta,
1865            stack,
1866        } => {
1867            write_u1(out, (*offset_delta as u8) + 64);
1868            write_verification_type(out, stack);
1869        }
1870        StackMapFrame::SameLocals1StackItemFrameExtended {
1871            offset_delta,
1872            stack,
1873        } => {
1874            write_u1(out, 247);
1875            write_u2(out, *offset_delta);
1876            write_verification_type(out, stack);
1877        }
1878        StackMapFrame::ChopFrame { offset_delta, k } => {
1879            write_u1(out, 251 - *k);
1880            write_u2(out, *offset_delta);
1881        }
1882        StackMapFrame::SameFrameExtended { offset_delta } => {
1883            write_u1(out, 251);
1884            write_u2(out, *offset_delta);
1885        }
1886        StackMapFrame::AppendFrame {
1887            offset_delta,
1888            locals,
1889        } => {
1890            write_u1(out, 251 + locals.len() as u8);
1891            write_u2(out, *offset_delta);
1892            for local in locals {
1893                write_verification_type(out, local);
1894            }
1895        }
1896        StackMapFrame::FullFrame {
1897            offset_delta,
1898            locals,
1899            stack,
1900        } => {
1901            write_u1(out, 255);
1902            write_u2(out, *offset_delta);
1903            write_u2(out, locals.len() as u16);
1904            for local in locals {
1905                write_verification_type(out, local);
1906            }
1907            write_u2(out, stack.len() as u16);
1908            for value in stack {
1909                write_verification_type(out, value);
1910            }
1911        }
1912    }
1913}
1914
1915fn write_verification_type(out: &mut Vec<u8>, value: &VerificationTypeInfo) {
1916    match value {
1917        VerificationTypeInfo::Top => write_u1(out, 0),
1918        VerificationTypeInfo::Integer => write_u1(out, 1),
1919        VerificationTypeInfo::Float => write_u1(out, 2),
1920        VerificationTypeInfo::Double => write_u1(out, 3),
1921        VerificationTypeInfo::Long => write_u1(out, 4),
1922        VerificationTypeInfo::Null => write_u1(out, 5),
1923        VerificationTypeInfo::UninitializedThis => write_u1(out, 6),
1924        VerificationTypeInfo::Object { cpool_index } => {
1925            write_u1(out, 7);
1926            write_u2(out, *cpool_index);
1927        }
1928        VerificationTypeInfo::Uninitialized { offset } => {
1929            write_u1(out, 8);
1930            write_u2(out, *offset);
1931        }
1932    }
1933}
1934
1935fn collect_attribute_names(attributes: &[AttributeInfo], names: &mut Vec<String>) {
1936    for attr in attributes {
1937        match attr {
1938            AttributeInfo::Code(_) => names.push("Code".to_string()),
1939            AttributeInfo::ConstantValue { .. } => names.push("ConstantValue".to_string()),
1940            AttributeInfo::Exceptions { .. } => names.push("Exceptions".to_string()),
1941            AttributeInfo::SourceFile { .. } => names.push("SourceFile".to_string()),
1942            AttributeInfo::LineNumberTable { .. } => names.push("LineNumberTable".to_string()),
1943            AttributeInfo::LocalVariableTable { .. } => {
1944                names.push("LocalVariableTable".to_string())
1945            }
1946            AttributeInfo::Signature { .. } => names.push("Signature".to_string()),
1947            AttributeInfo::StackMapTable { .. } => names.push("StackMapTable".to_string()),
1948            AttributeInfo::Deprecated => names.push("Deprecated".to_string()),
1949            AttributeInfo::Synthetic => names.push("Synthetic".to_string()),
1950            AttributeInfo::InnerClasses { .. } => names.push("InnerClasses".to_string()),
1951            AttributeInfo::EnclosingMethod { .. } => names.push("EnclosingMethod".to_string()),
1952            AttributeInfo::BootstrapMethods { .. } => names.push("BootstrapMethods".to_string()),
1953            AttributeInfo::MethodParameters { .. } => names.push("MethodParameters".to_string()),
1954            AttributeInfo::Unknown { name, .. } => names.push(name.clone()),
1955        }
1956    }
1957}
1958
1959fn write_constant_pool(out: &mut Vec<u8>, cp: &[CpInfo]) -> Result<(), ClassWriteError> {
1960    write_u2(out, cp.len() as u16);
1961    for entry in cp.iter().skip(1) {
1962        match entry {
1963            CpInfo::Unusable => {}
1964            CpInfo::Utf8(value) => {
1965                let bytes = encode_modified_utf8(value);
1966                write_u1(out, 1);
1967                write_u2(out, bytes.len() as u16);
1968                out.extend_from_slice(&bytes);
1969            }
1970            CpInfo::Integer(value) => {
1971                write_u1(out, 3);
1972                write_u4(out, *value as u32);
1973            }
1974            CpInfo::Float(value) => {
1975                write_u1(out, 4);
1976                write_u4(out, value.to_bits());
1977            }
1978            CpInfo::Long(value) => {
1979                write_u1(out, 5);
1980                write_u8(out, *value as u64);
1981            }
1982            CpInfo::Double(value) => {
1983                write_u1(out, 6);
1984                write_u8(out, value.to_bits());
1985            }
1986            CpInfo::Class { name_index } => {
1987                write_u1(out, 7);
1988                write_u2(out, *name_index);
1989            }
1990            CpInfo::String { string_index } => {
1991                write_u1(out, 8);
1992                write_u2(out, *string_index);
1993            }
1994            CpInfo::Fieldref {
1995                class_index,
1996                name_and_type_index,
1997            } => {
1998                write_u1(out, 9);
1999                write_u2(out, *class_index);
2000                write_u2(out, *name_and_type_index);
2001            }
2002            CpInfo::Methodref {
2003                class_index,
2004                name_and_type_index,
2005            } => {
2006                write_u1(out, 10);
2007                write_u2(out, *class_index);
2008                write_u2(out, *name_and_type_index);
2009            }
2010            CpInfo::InterfaceMethodref {
2011                class_index,
2012                name_and_type_index,
2013            } => {
2014                write_u1(out, 11);
2015                write_u2(out, *class_index);
2016                write_u2(out, *name_and_type_index);
2017            }
2018            CpInfo::NameAndType {
2019                name_index,
2020                descriptor_index,
2021            } => {
2022                write_u1(out, 12);
2023                write_u2(out, *name_index);
2024                write_u2(out, *descriptor_index);
2025            }
2026            CpInfo::MethodHandle {
2027                reference_kind,
2028                reference_index,
2029            } => {
2030                write_u1(out, 15);
2031                write_u1(out, *reference_kind);
2032                write_u2(out, *reference_index);
2033            }
2034            CpInfo::MethodType { descriptor_index } => {
2035                write_u1(out, 16);
2036                write_u2(out, *descriptor_index);
2037            }
2038            CpInfo::Dynamic {
2039                bootstrap_method_attr_index,
2040                name_and_type_index,
2041            } => {
2042                write_u1(out, 17);
2043                write_u2(out, *bootstrap_method_attr_index);
2044                write_u2(out, *name_and_type_index);
2045            }
2046            CpInfo::InvokeDynamic {
2047                bootstrap_method_attr_index,
2048                name_and_type_index,
2049            } => {
2050                write_u1(out, 18);
2051                write_u2(out, *bootstrap_method_attr_index);
2052                write_u2(out, *name_and_type_index);
2053            }
2054            CpInfo::Module { name_index } => {
2055                write_u1(out, 19);
2056                write_u2(out, *name_index);
2057            }
2058            CpInfo::Package { name_index } => {
2059                write_u1(out, 20);
2060                write_u2(out, *name_index);
2061            }
2062        }
2063    }
2064    Ok(())
2065}
2066
2067fn encode_modified_utf8(value: &str) -> Vec<u8> {
2068    let mut out = Vec::new();
2069    for ch in value.chars() {
2070        let code = ch as u32;
2071        if code == 0 {
2072            out.push(0xC0);
2073            out.push(0x80);
2074        } else if code <= 0x7F {
2075            out.push(code as u8);
2076        } else if code <= 0x7FF {
2077            out.push((0xC0 | ((code >> 6) & 0x1F)) as u8);
2078            out.push((0x80 | (code & 0x3F)) as u8);
2079        } else if code <= 0xFFFF {
2080            out.push((0xE0 | ((code >> 12) & 0x0F)) as u8);
2081            out.push((0x80 | ((code >> 6) & 0x3F)) as u8);
2082            out.push((0x80 | (code & 0x3F)) as u8);
2083        } else {
2084            let u = code - 0x10000;
2085            let high = 0xD800 + ((u >> 10) & 0x3FF);
2086            let low = 0xDC00 + (u & 0x3FF);
2087            for cu in [high, low] {
2088                out.push((0xE0 | ((cu >> 12) & 0x0F)) as u8);
2089                out.push((0x80 | ((cu >> 6) & 0x3F)) as u8);
2090                out.push((0x80 | (cu & 0x3F)) as u8);
2091            }
2092        }
2093    }
2094    out
2095}
2096
2097fn ensure_utf8(cp: &mut Vec<CpInfo>, value: &str) -> u16 {
2098    if let Some(index) = cp_find_utf8(cp, value) {
2099        return index;
2100    }
2101    cp.push(CpInfo::Utf8(value.to_string()));
2102    (cp.len() - 1) as u16
2103}
2104
2105fn ensure_class(cp: &mut Vec<CpInfo>, name: &str) -> u16 {
2106    for (index, entry) in cp.iter().enumerate() {
2107        if let CpInfo::Class { name_index } = entry
2108            && let Some(CpInfo::Utf8(value)) = cp.get(*name_index as usize)
2109            && value == name
2110        {
2111            return index as u16;
2112        }
2113    }
2114    let name_index = ensure_utf8(cp, name);
2115    cp.push(CpInfo::Class { name_index });
2116    (cp.len() - 1) as u16
2117}
2118
2119fn cp_find_utf8(cp: &[CpInfo], value: &str) -> Option<u16> {
2120    for (index, entry) in cp.iter().enumerate() {
2121        if let CpInfo::Utf8(existing) = entry
2122            && existing == value
2123        {
2124            return Some(index as u16);
2125        }
2126    }
2127    None
2128}
2129
2130fn write_u1(out: &mut Vec<u8>, value: u8) {
2131    out.push(value);
2132}
2133
2134fn write_u2(out: &mut Vec<u8>, value: u16) {
2135    out.extend_from_slice(&value.to_be_bytes());
2136}
2137
2138fn write_u4(out: &mut Vec<u8>, value: u32) {
2139    out.extend_from_slice(&value.to_be_bytes());
2140}
2141
2142fn write_i1(out: &mut Vec<u8>, value: i8) {
2143    out.push(value as u8);
2144}
2145
2146fn write_i2(out: &mut Vec<u8>, value: i16) {
2147    out.extend_from_slice(&value.to_be_bytes());
2148}
2149
2150fn write_i4(out: &mut Vec<u8>, value: i32) {
2151    out.extend_from_slice(&value.to_be_bytes());
2152}
2153
2154fn write_i2_at(out: &mut [u8], pos: usize, value: i16) {
2155    let bytes = value.to_be_bytes();
2156    out[pos] = bytes[0];
2157    out[pos + 1] = bytes[1];
2158}
2159
2160fn write_i4_at(out: &mut [u8], pos: usize, value: i32) {
2161    let bytes = value.to_be_bytes();
2162    out[pos] = bytes[0];
2163    out[pos + 1] = bytes[1];
2164    out[pos + 2] = bytes[2];
2165    out[pos + 3] = bytes[3];
2166}
2167
2168fn write_switch_padding(out: &mut Vec<u8>, opcode_offset: usize) {
2169    let mut padding = (4 - ((opcode_offset + 1) % 4)) % 4;
2170    while padding > 0 {
2171        out.push(0);
2172        padding -= 1;
2173    }
2174}
2175
2176fn write_u8(out: &mut Vec<u8>, value: u64) {
2177    out.extend_from_slice(&value.to_be_bytes());
2178}
2179
2180#[derive(Debug, Clone, PartialEq, Eq)]
2181enum FrameType {
2182    Top,
2183    Integer,
2184    Float,
2185    Long,
2186    Double,
2187    Null,
2188    UninitializedThis,
2189    Object(String),
2190    Uninitialized(u16),
2191}
2192
2193fn compute_stack_map_table(
2194    method: &MethodNode,
2195    class_node: &ClassNode,
2196    code: &CodeAttribute,
2197    cp: &mut Vec<CpInfo>,
2198    max_locals: u16,
2199) -> Result<Vec<StackMapFrame>, ClassWriteError> {
2200    let insns = parse_instructions(&code.code)?;
2201    if insns.is_empty() {
2202        return Ok(Vec::new());
2203    }
2204
2205    let mut insn_index = std::collections::HashMap::new();
2206    for (index, insn) in insns.iter().enumerate() {
2207        insn_index.insert(insn.offset, index);
2208    }
2209
2210    let handlers = build_exception_handlers(code, cp)?;
2211    let handler_common = handler_common_types(&handlers);
2212    let mut frames: std::collections::HashMap<u16, FrameState> = std::collections::HashMap::new();
2213    let mut worklist = std::collections::VecDeque::new();
2214    let mut in_worklist = std::collections::HashSet::new();
2215
2216    let mut initial = initial_frame(method, class_node)?;
2217    pad_locals(&mut initial.locals, max_locals);
2218    frames.insert(0, initial.clone());
2219    worklist.push_back(0u16);
2220    in_worklist.insert(0u16);
2221
2222    let mut max_iterations = 0usize;
2223    while let Some(offset) = worklist.pop_front() {
2224        in_worklist.remove(&offset);
2225        max_iterations += 1;
2226        if max_iterations > 100000 {
2227            return Err(ClassWriteError::FrameComputation(
2228                "frame analysis exceeded iteration limit".to_string(),
2229            ));
2230        }
2231        let index = *insn_index.get(&offset).ok_or_else(|| {
2232            ClassWriteError::FrameComputation(format!("missing instruction at {offset}"))
2233        })?;
2234        let insn = &insns[index];
2235        let frame = frames
2236            .get(&offset)
2237            .ok_or_else(|| ClassWriteError::FrameComputation(format!("missing frame at {offset}")))?
2238            .clone();
2239        let insn1 = &insn;
2240        let out_frame = execute_instruction(insn1, &frame, class_node, cp)?;
2241
2242        for succ in instruction_successors(insn) {
2243            if let Some(next_frame) = merge_frame(&out_frame, frames.get(&succ)) {
2244                let changed = match frames.get(&succ) {
2245                    Some(existing) => existing != &next_frame,
2246                    None => true,
2247                };
2248                if changed {
2249                    frames.insert(succ, next_frame);
2250                    if in_worklist.insert(succ) {
2251                        worklist.push_back(succ);
2252                    }
2253                }
2254            }
2255        }
2256
2257        for handler in handlers.iter().filter(|item| item.covers(offset)) {
2258            let mut handler_frame = FrameState {
2259                locals: frame.locals.clone(),
2260                stack: Vec::new(),
2261            };
2262            let exception_type = handler_common
2263                .get(&handler.handler_pc)
2264                .cloned()
2265                .unwrap_or_else(|| handler.exception_type.clone());
2266            handler_frame.stack.push(exception_type);
2267            if let Some(next_frame) = merge_frame(&handler_frame, frames.get(&handler.handler_pc)) {
2268                let changed = match frames.get(&handler.handler_pc) {
2269                    Some(existing) => existing != &next_frame,
2270                    None => true,
2271                };
2272                if changed {
2273                    frames.insert(handler.handler_pc, next_frame);
2274                    if in_worklist.insert(handler.handler_pc) {
2275                        worklist.push_back(handler.handler_pc);
2276                    }
2277                }
2278            }
2279        }
2280    }
2281
2282    let mut frame_offsets: Vec<u16> = frames.keys().copied().collect();
2283    frame_offsets.sort_unstable();
2284    let mut result = Vec::new();
2285    let mut previous_offset: i32 = -1;
2286    for offset in frame_offsets {
2287        if offset == 0 {
2288            continue;
2289        }
2290        let frame = frames
2291            .get(&offset)
2292            .ok_or_else(|| ClassWriteError::FrameComputation(format!("missing frame at {offset}")))?
2293            .clone();
2294        let locals = compact_locals(&frame.locals);
2295        let stack = frame.stack;
2296        let offset_delta = (offset as i32 - previous_offset - 1) as u16;
2297        previous_offset = offset as i32;
2298        let locals_info = locals
2299            .iter()
2300            .map(|value| to_verification_type(value, cp))
2301            .collect();
2302        let stack_info = stack
2303            .iter()
2304            .map(|value| to_verification_type(value, cp))
2305            .collect();
2306        result.push(StackMapFrame::FullFrame {
2307            offset_delta,
2308            locals: locals_info,
2309            stack: stack_info,
2310        });
2311    }
2312
2313    Ok(result)
2314}
2315
2316#[derive(Debug, Clone, PartialEq, Eq)]
2317struct FrameState {
2318    locals: Vec<FrameType>,
2319    stack: Vec<FrameType>,
2320}
2321
2322fn merge_frame(frame: &FrameState, existing: Option<&FrameState>) -> Option<FrameState> {
2323    match existing {
2324        None => Some(frame.clone()),
2325        Some(other) => {
2326            let merged = FrameState {
2327                locals: merge_vec(&frame.locals, &other.locals),
2328                stack: merge_vec(&frame.stack, &other.stack),
2329            };
2330            if merged == *other { None } else { Some(merged) }
2331        }
2332    }
2333}
2334
2335fn merge_vec(a: &[FrameType], b: &[FrameType]) -> Vec<FrameType> {
2336    let len = a.len().max(b.len());
2337    let mut merged = Vec::with_capacity(len);
2338    for i in 0..len {
2339        let left = a.get(i).cloned().unwrap_or(FrameType::Top);
2340        let right = b.get(i).cloned().unwrap_or(FrameType::Top);
2341        merged.push(merge_type(&left, &right));
2342    }
2343    merged
2344}
2345
2346fn merge_type(a: &FrameType, b: &FrameType) -> FrameType {
2347    if a == b {
2348        return a.clone();
2349    }
2350    match (a, b) {
2351        (FrameType::Top, _) => FrameType::Top,
2352        (_, FrameType::Top) => FrameType::Top,
2353        (FrameType::Null, FrameType::Object(name)) | (FrameType::Object(name), FrameType::Null) => {
2354            FrameType::Object(name.clone())
2355        }
2356        (FrameType::Object(left), FrameType::Object(right)) => {
2357            FrameType::Object(common_superclass(left, right))
2358        }
2359        (FrameType::Object(_), FrameType::Uninitialized(_))
2360        | (FrameType::Uninitialized(_), FrameType::Object(_))
2361        | (FrameType::UninitializedThis, FrameType::Object(_))
2362        | (FrameType::Object(_), FrameType::UninitializedThis) => {
2363            FrameType::Object("java/lang/Object".to_string())
2364        }
2365        _ => FrameType::Top,
2366    }
2367}
2368
2369fn common_superclass(left: &str, right: &str) -> String {
2370    if left == right {
2371        return left.to_string();
2372    }
2373    if left.starts_with('[') || right.starts_with('[') {
2374        return "java/lang/Object".to_string();
2375    }
2376
2377    let mut ancestors = std::collections::HashSet::new();
2378    let mut current = left;
2379    ancestors.insert(current.to_string());
2380    while let Some(parent) = known_superclass(current) {
2381        if ancestors.insert(parent.to_string()) {
2382            current = parent;
2383        } else {
2384            break;
2385        }
2386    }
2387    ancestors.insert("java/lang/Object".to_string());
2388
2389    current = right;
2390    if ancestors.contains(current) {
2391        return current.to_string();
2392    }
2393    while let Some(parent) = known_superclass(current) {
2394        if ancestors.contains(parent) {
2395            return parent.to_string();
2396        }
2397        current = parent;
2398    }
2399    "java/lang/Object".to_string()
2400}
2401
2402fn known_superclass(name: &str) -> Option<&'static str> {
2403    match name {
2404        "java/lang/Throwable" => Some("java/lang/Object"),
2405        "java/lang/Exception" => Some("java/lang/Throwable"),
2406        "java/lang/RuntimeException" => Some("java/lang/Exception"),
2407        "java/lang/IllegalArgumentException" => Some("java/lang/RuntimeException"),
2408        "java/lang/IllegalStateException" => Some("java/lang/RuntimeException"),
2409        "java/security/GeneralSecurityException" => Some("java/lang/Exception"),
2410        "java/security/NoSuchAlgorithmException" => Some("java/security/GeneralSecurityException"),
2411        "java/security/InvalidKeyException" => Some("java/security/GeneralSecurityException"),
2412        "javax/crypto/NoSuchPaddingException" => Some("java/security/GeneralSecurityException"),
2413        "javax/crypto/IllegalBlockSizeException" => Some("java/security/GeneralSecurityException"),
2414        "javax/crypto/BadPaddingException" => Some("java/security/GeneralSecurityException"),
2415        _ => None,
2416    }
2417}
2418
2419fn pad_locals(locals: &mut Vec<FrameType>, max_locals: u16) {
2420    while locals.len() < max_locals as usize {
2421        locals.push(FrameType::Top);
2422    }
2423}
2424
2425fn compute_maxs(
2426    method: &MethodNode,
2427    class_node: &ClassNode,
2428    code: &CodeAttribute,
2429    cp: &[CpInfo],
2430) -> Result<(u16, u16), ClassWriteError> {
2431    let insns = parse_instructions(&code.code)?;
2432    if insns.is_empty() {
2433        let initial = initial_frame(method, class_node)?;
2434        return Ok((0, initial.locals.len() as u16));
2435    }
2436
2437    let mut insn_index = std::collections::HashMap::new();
2438    for (index, insn) in insns.iter().enumerate() {
2439        insn_index.insert(insn.offset, index);
2440    }
2441
2442    let handlers = build_exception_handlers(code, cp)?;
2443    let mut frames: std::collections::HashMap<u16, FrameState> = std::collections::HashMap::new();
2444    let mut worklist = std::collections::VecDeque::new();
2445    let mut in_worklist = std::collections::HashSet::new();
2446
2447    let initial = initial_frame(method, class_node)?;
2448    frames.insert(0, initial.clone());
2449    worklist.push_back(0u16);
2450    in_worklist.insert(0u16);
2451
2452    let mut max_stack = initial.stack.len();
2453    let mut max_locals = initial.locals.len();
2454    let mut max_iterations = 0usize;
2455    let mut offset_hits: std::collections::HashMap<u16, u32> = std::collections::HashMap::new();
2456    while let Some(offset) = worklist.pop_front() {
2457        in_worklist.remove(&offset);
2458        max_iterations += 1;
2459        *offset_hits.entry(offset).or_insert(0) += 1;
2460        if max_iterations > 100000 {
2461            return Err(ClassWriteError::FrameComputation(
2462                "frame analysis exceeded iteration limit".to_string(),
2463            ));
2464        }
2465        let index = *insn_index.get(&offset).ok_or_else(|| {
2466            ClassWriteError::FrameComputation(format!("missing instruction at {offset}"))
2467        })?;
2468        let insn = &insns[index];
2469        let frame = frames.get(&offset).cloned().ok_or_else(|| {
2470            ClassWriteError::FrameComputation(format!("missing frame at {offset}"))
2471        })?;
2472        max_stack = max_stack.max(stack_slots(&frame.stack));
2473        max_locals = max_locals.max(frame.locals.len());
2474
2475        let out_frame = execute_instruction(insn, &frame, class_node, cp)?;
2476        max_stack = max_stack.max(stack_slots(&out_frame.stack));
2477        max_locals = max_locals.max(out_frame.locals.len());
2478
2479        for succ in instruction_successors(insn) {
2480            if let Some(next_frame) = merge_frame(&out_frame, frames.get(&succ)) {
2481                let changed = match frames.get(&succ) {
2482                    Some(existing) => existing != &next_frame,
2483                    None => true,
2484                };
2485                if changed {
2486                    frames.insert(succ, next_frame);
2487                    if in_worklist.insert(succ) {
2488                        worklist.push_back(succ);
2489                    }
2490                }
2491            }
2492        }
2493
2494        for handler in handlers.iter().filter(|item| item.covers(offset)) {
2495            let mut handler_frame = FrameState {
2496                locals: frame.locals.clone(),
2497                stack: Vec::new(),
2498            };
2499            handler_frame.stack.push(handler.exception_type.clone());
2500            max_stack = max_stack.max(stack_slots(&handler_frame.stack));
2501            max_locals = max_locals.max(handler_frame.locals.len());
2502            if let Some(next_frame) = merge_frame(&handler_frame, frames.get(&handler.handler_pc)) {
2503                let changed = match frames.get(&handler.handler_pc) {
2504                    Some(existing) => existing != &next_frame,
2505                    None => true,
2506                };
2507                if changed {
2508                    frames.insert(handler.handler_pc, next_frame);
2509                    if in_worklist.insert(handler.handler_pc) {
2510                        worklist.push_back(handler.handler_pc);
2511                    }
2512                }
2513            }
2514        }
2515    }
2516
2517    Ok((max_stack as u16, max_locals as u16))
2518}
2519
2520fn stack_slots(stack: &[FrameType]) -> usize {
2521    let mut slots = 0usize;
2522    for value in stack {
2523        slots += if is_category2(value) { 2 } else { 1 };
2524    }
2525    slots
2526}
2527
2528fn compact_locals(locals: &[FrameType]) -> Vec<FrameType> {
2529    let mut out = Vec::new();
2530    let mut i = 0usize;
2531    while i < locals.len() {
2532        match locals[i] {
2533            FrameType::Top => {
2534                if i > 0 && matches!(locals[i - 1], FrameType::Long | FrameType::Double) {
2535                    i += 1;
2536                    continue;
2537                }
2538                out.push(FrameType::Top);
2539            }
2540            FrameType::Long | FrameType::Double => {
2541                out.push(locals[i].clone());
2542                if i + 1 < locals.len() && matches!(locals[i + 1], FrameType::Top) {
2543                    i += 1;
2544                }
2545            }
2546            _ => out.push(locals[i].clone()),
2547        }
2548        i += 1;
2549    }
2550
2551    while matches!(out.last(), Some(FrameType::Top)) {
2552        out.pop();
2553    }
2554    out
2555}
2556
2557fn to_verification_type(value: &FrameType, cp: &mut Vec<CpInfo>) -> VerificationTypeInfo {
2558    match value {
2559        FrameType::Top => VerificationTypeInfo::Top,
2560        FrameType::Integer => VerificationTypeInfo::Integer,
2561        FrameType::Float => VerificationTypeInfo::Float,
2562        FrameType::Long => VerificationTypeInfo::Long,
2563        FrameType::Double => VerificationTypeInfo::Double,
2564        FrameType::Null => VerificationTypeInfo::Null,
2565        FrameType::UninitializedThis => VerificationTypeInfo::UninitializedThis,
2566        FrameType::Uninitialized(offset) => VerificationTypeInfo::Uninitialized { offset: *offset },
2567        FrameType::Object(name) => {
2568            let index = ensure_class(cp, name);
2569            VerificationTypeInfo::Object { cpool_index: index }
2570        }
2571    }
2572}
2573
2574fn initial_frame(
2575    method: &MethodNode,
2576    class_node: &ClassNode,
2577) -> Result<FrameState, ClassWriteError> {
2578    let mut locals = Vec::new();
2579    let is_static = method.access_flags & constants::ACC_STATIC != 0;
2580    if !is_static {
2581        if method.name == "<init>" {
2582            locals.push(FrameType::UninitializedThis);
2583        } else {
2584            locals.push(FrameType::Object(class_node.name.clone()));
2585        }
2586    }
2587    let (params, _) = parse_method_descriptor(&method.descriptor)?;
2588    for param in params {
2589        push_local_type(&mut locals, param);
2590    }
2591    Ok(FrameState {
2592        locals,
2593        stack: Vec::new(),
2594    })
2595}
2596
2597fn push_local_type(locals: &mut Vec<FrameType>, ty: FieldType) {
2598    match ty {
2599        FieldType::Long => {
2600            locals.push(FrameType::Long);
2601            locals.push(FrameType::Top);
2602        }
2603        FieldType::Double => {
2604            locals.push(FrameType::Double);
2605            locals.push(FrameType::Top);
2606        }
2607        FieldType::Float => locals.push(FrameType::Float),
2608        FieldType::Boolean
2609        | FieldType::Byte
2610        | FieldType::Char
2611        | FieldType::Short
2612        | FieldType::Int => locals.push(FrameType::Integer),
2613        FieldType::Object(name) => locals.push(FrameType::Object(name)),
2614        FieldType::Array(desc) => locals.push(FrameType::Object(desc)),
2615        FieldType::Void => {}
2616    }
2617}
2618
2619#[derive(Debug, Clone)]
2620struct ExceptionHandlerInfo {
2621    start_pc: u16,
2622    end_pc: u16,
2623    handler_pc: u16,
2624    exception_type: FrameType,
2625}
2626
2627impl ExceptionHandlerInfo {
2628    fn covers(&self, offset: u16) -> bool {
2629        offset >= self.start_pc && offset < self.end_pc
2630    }
2631}
2632
2633fn build_exception_handlers(
2634    code: &CodeAttribute,
2635    cp: &[CpInfo],
2636) -> Result<Vec<ExceptionHandlerInfo>, ClassWriteError> {
2637    let mut handlers = Vec::new();
2638    for entry in &code.exception_table {
2639        let exception_type = if entry.catch_type == 0 {
2640            FrameType::Object("java/lang/Throwable".to_string())
2641        } else {
2642            let class_name = cp_class_name(cp, entry.catch_type)?;
2643            FrameType::Object(class_name.to_string())
2644        };
2645        handlers.push(ExceptionHandlerInfo {
2646            start_pc: entry.start_pc,
2647            end_pc: entry.end_pc,
2648            handler_pc: entry.handler_pc,
2649            exception_type,
2650        });
2651    }
2652    Ok(handlers)
2653}
2654
2655fn handler_common_types(
2656    handlers: &[ExceptionHandlerInfo],
2657) -> std::collections::HashMap<u16, FrameType> {
2658    let mut map: std::collections::HashMap<u16, FrameType> = std::collections::HashMap::new();
2659    for handler in handlers {
2660        map.entry(handler.handler_pc)
2661            .and_modify(|existing| {
2662                *existing = merge_exception_type(existing, &handler.exception_type);
2663            })
2664            .or_insert_with(|| handler.exception_type.clone());
2665    }
2666    map
2667}
2668
2669fn merge_exception_type(left: &FrameType, right: &FrameType) -> FrameType {
2670    match (left, right) {
2671        (FrameType::Object(l), FrameType::Object(r)) => FrameType::Object(common_superclass(l, r)),
2672        _ if left == right => left.clone(),
2673        _ => FrameType::Object("java/lang/Object".to_string()),
2674    }
2675}
2676
2677fn dump_frame_debug(
2678    method: &MethodNode,
2679    label: &str,
2680    iterations: usize,
2681    hits: &std::collections::HashMap<u16, u32>,
2682) {
2683    let mut entries: Vec<(u16, u32)> = hits.iter().map(|(k, v)| (*k, *v)).collect();
2684    entries.sort_by(|a, b| b.1.cmp(&a.1));
2685    let top = entries.into_iter().take(10).collect::<Vec<_>>();
2686    eprintln!(
2687        "[frame-debug] method={}{} label={} iterations={} top_offsets={:?}",
2688        method.name, method.descriptor, label, iterations, top
2689    );
2690}
2691
2692#[derive(Debug, Clone)]
2693struct ParsedInstruction {
2694    offset: u16,
2695    opcode: u8,
2696    operand: Operand,
2697}
2698
2699#[derive(Debug, Clone)]
2700enum Operand {
2701    None,
2702    I1(i8),
2703    I2(i16),
2704    I4(i32),
2705    U1(u8),
2706    U2(u16),
2707    U4(u32),
2708    Jump(i16),
2709    JumpWide(i32),
2710    TableSwitch {
2711        default_offset: i32,
2712        low: i32,
2713        high: i32,
2714        offsets: Vec<i32>,
2715    },
2716    LookupSwitch {
2717        default_offset: i32,
2718        pairs: Vec<(i32, i32)>,
2719    },
2720    Iinc {
2721        index: u16,
2722        increment: i16,
2723    },
2724    InvokeInterface {
2725        index: u16,
2726        count: u8,
2727    },
2728    InvokeDynamic {
2729        index: u16,
2730    },
2731    MultiANewArray {
2732        index: u16,
2733        dims: u8,
2734    },
2735    Wide {
2736        opcode: u8,
2737        index: u16,
2738        increment: Option<i16>,
2739    },
2740}
2741
2742fn parse_instructions(code: &[u8]) -> Result<Vec<ParsedInstruction>, ClassWriteError> {
2743    let mut insns = Vec::new();
2744    let mut pos = 0usize;
2745    while pos < code.len() {
2746        let offset = pos as u16;
2747        let opcode = code[pos];
2748        pos += 1;
2749        let operand = match opcode {
2750            opcodes::BIPUSH => {
2751                let value = read_i1(code, &mut pos)?;
2752                Operand::I1(value)
2753            }
2754            opcodes::SIPUSH => Operand::I2(read_i2(code, &mut pos)?),
2755            opcodes::LDC => Operand::U1(read_u1(code, &mut pos)?),
2756            opcodes::LDC_W | opcodes::LDC2_W => Operand::U2(read_u2(code, &mut pos)?),
2757            opcodes::ILOAD..=opcodes::ALOAD | opcodes::ISTORE..=opcodes::ASTORE | opcodes::RET => {
2758                Operand::U1(read_u1(code, &mut pos)?)
2759            }
2760            opcodes::IINC => {
2761                let index = read_u1(code, &mut pos)? as u16;
2762                let inc = read_i1(code, &mut pos)? as i16;
2763                Operand::Iinc {
2764                    index,
2765                    increment: inc,
2766                }
2767            }
2768            opcodes::IFEQ..=opcodes::JSR | opcodes::IFNULL | opcodes::IFNONNULL => {
2769                Operand::Jump(read_i2(code, &mut pos)?)
2770            }
2771            opcodes::GOTO_W | opcodes::JSR_W => Operand::JumpWide(read_i4(code, &mut pos)?),
2772            opcodes::TABLESWITCH => {
2773                let padding = (4 - (pos % 4)) % 4;
2774                pos += padding;
2775                let default_offset = read_i4(code, &mut pos)?;
2776                let low = read_i4(code, &mut pos)?;
2777                let high = read_i4(code, &mut pos)?;
2778                let count = if high < low {
2779                    0
2780                } else {
2781                    (high - low + 1) as usize
2782                };
2783                let mut offsets = Vec::with_capacity(count);
2784                for _ in 0..count {
2785                    offsets.push(read_i4(code, &mut pos)?);
2786                }
2787                Operand::TableSwitch {
2788                    default_offset,
2789                    low,
2790                    high,
2791                    offsets,
2792                }
2793            }
2794            opcodes::LOOKUPSWITCH => {
2795                let padding = (4 - (pos % 4)) % 4;
2796                pos += padding;
2797                let default_offset = read_i4(code, &mut pos)?;
2798                let npairs = read_i4(code, &mut pos)? as usize;
2799                let mut pairs = Vec::with_capacity(npairs);
2800                for _ in 0..npairs {
2801                    let key = read_i4(code, &mut pos)?;
2802                    let value = read_i4(code, &mut pos)?;
2803                    pairs.push((key, value));
2804                }
2805                Operand::LookupSwitch {
2806                    default_offset,
2807                    pairs,
2808                }
2809            }
2810            opcodes::GETSTATIC..=opcodes::INVOKESTATIC
2811            | opcodes::NEW
2812            | opcodes::ANEWARRAY
2813            | opcodes::CHECKCAST
2814            | opcodes::INSTANCEOF => Operand::U2(read_u2(code, &mut pos)?),
2815            opcodes::INVOKEINTERFACE => {
2816                let index = read_u2(code, &mut pos)?;
2817                let count = read_u1(code, &mut pos)?;
2818                let _ = read_u1(code, &mut pos)?;
2819                Operand::InvokeInterface { index, count }
2820            }
2821            opcodes::INVOKEDYNAMIC => {
2822                let index = read_u2(code, &mut pos)?;
2823                let _ = read_u2(code, &mut pos)?;
2824                Operand::InvokeDynamic { index }
2825            }
2826            opcodes::NEWARRAY => Operand::U1(read_u1(code, &mut pos)?),
2827            opcodes::WIDE => {
2828                let wide_opcode = read_u1(code, &mut pos)?;
2829                match wide_opcode {
2830                    opcodes::ILOAD..=opcodes::ALOAD
2831                    | opcodes::ISTORE..=opcodes::ASTORE
2832                    | opcodes::RET => {
2833                        let index = read_u2(code, &mut pos)?;
2834                        Operand::Wide {
2835                            opcode: wide_opcode,
2836                            index,
2837                            increment: None,
2838                        }
2839                    }
2840                    opcodes::IINC => {
2841                        let index = read_u2(code, &mut pos)?;
2842                        let increment = read_i2(code, &mut pos)?;
2843                        Operand::Wide {
2844                            opcode: wide_opcode,
2845                            index,
2846                            increment: Some(increment),
2847                        }
2848                    }
2849                    _ => {
2850                        return Err(ClassWriteError::InvalidOpcode {
2851                            opcode: wide_opcode,
2852                            offset: pos - 1,
2853                        });
2854                    }
2855                }
2856            }
2857            opcodes::MULTIANEWARRAY => {
2858                let index = read_u2(code, &mut pos)?;
2859                let dims = read_u1(code, &mut pos)?;
2860                Operand::MultiANewArray { index, dims }
2861            }
2862            _ => Operand::None,
2863        };
2864        insns.push(ParsedInstruction {
2865            offset,
2866            opcode,
2867            operand,
2868        });
2869    }
2870    Ok(insns)
2871}
2872
2873fn instruction_successors(insn: &ParsedInstruction) -> Vec<u16> {
2874    let mut successors = Vec::new();
2875    let next_offset = insn.offset.saturating_add(instruction_length(insn) as u16);
2876    match insn.opcode {
2877        opcodes::GOTO | opcodes::GOTO_W => {
2878            if let Some(target) = jump_target(insn) {
2879                successors.push(target);
2880            }
2881        }
2882        opcodes::JSR | opcodes::JSR_W => {
2883            if let Some(target) = jump_target(insn) {
2884                successors.push(target);
2885            }
2886            successors.push(next_offset);
2887        }
2888        opcodes::IFEQ..=opcodes::IF_ACMPNE | opcodes::IFNULL | opcodes::IFNONNULL => {
2889            if let Some(target) = jump_target(insn) {
2890                successors.push(target);
2891            }
2892            successors.push(next_offset);
2893        }
2894        opcodes::TABLESWITCH => {
2895            if let Operand::TableSwitch {
2896                default_offset,
2897                offsets,
2898                ..
2899            } = &insn.operand
2900            {
2901                successors.push((insn.offset as i32 + default_offset) as u16);
2902                for offset in offsets {
2903                    successors.push((insn.offset as i32 + *offset) as u16);
2904                }
2905            }
2906        }
2907        opcodes::LOOKUPSWITCH => {
2908            if let Operand::LookupSwitch {
2909                default_offset,
2910                pairs,
2911            } = &insn.operand
2912            {
2913                successors.push((insn.offset as i32 + default_offset) as u16);
2914                for (_, offset) in pairs {
2915                    successors.push((insn.offset as i32 + *offset) as u16);
2916                }
2917            }
2918        }
2919        opcodes::IRETURN..=opcodes::RETURN | opcodes::ATHROW => {}
2920        opcodes::MONITORENTER | opcodes::MONITOREXIT => {
2921            successors.push(next_offset);
2922        }
2923        _ => {
2924            if next_offset != insn.offset {
2925                successors.push(next_offset);
2926            }
2927        }
2928    }
2929    successors
2930}
2931
2932fn jump_target(insn: &ParsedInstruction) -> Option<u16> {
2933    match insn.operand {
2934        Operand::Jump(offset) => Some((insn.offset as i32 + offset as i32) as u16),
2935        Operand::JumpWide(offset) => Some((insn.offset as i32 + offset) as u16),
2936        _ => None,
2937    }
2938}
2939
2940fn instruction_length(insn: &ParsedInstruction) -> usize {
2941    match &insn.operand {
2942        Operand::None => 1,
2943        Operand::I1(_) | Operand::U1(_) => 2,
2944        Operand::I2(_) | Operand::U2(_) | Operand::Jump(_) => 3,
2945        Operand::I4(_) | Operand::U4(_) | Operand::JumpWide(_) => 5,
2946        Operand::Iinc { .. } => 3,
2947        Operand::InvokeInterface { .. } => 5,
2948        Operand::InvokeDynamic { .. } => 5,
2949        Operand::MultiANewArray { .. } => 4,
2950        Operand::Wide {
2951            opcode, increment, ..
2952        } => {
2953            if *opcode == opcodes::IINC && increment.is_some() {
2954                6
2955            } else {
2956                4
2957            }
2958        }
2959        Operand::TableSwitch { offsets, .. } => {
2960            1 + switch_padding(insn.offset) + 12 + offsets.len() * 4
2961        }
2962        Operand::LookupSwitch { pairs, .. } => {
2963            1 + switch_padding(insn.offset) + 8 + pairs.len() * 8
2964        }
2965    }
2966}
2967
2968fn switch_padding(offset: u16) -> usize {
2969    let pos = (offset as usize + 1) % 4;
2970    (4 - pos) % 4
2971}
2972
2973fn execute_instruction(
2974    insn: &ParsedInstruction,
2975    frame: &FrameState,
2976    class_node: &ClassNode,
2977    cp: &[CpInfo],
2978) -> Result<FrameState, ClassWriteError> {
2979    let mut locals = frame.locals.clone();
2980    let mut stack = frame.stack.clone();
2981
2982    let pop = |stack: &mut Vec<FrameType>| {
2983        stack.pop().ok_or_else(|| {
2984            ClassWriteError::FrameComputation(format!("stack underflow at {}", insn.offset))
2985        })
2986    };
2987
2988    match insn.opcode {
2989        opcodes::NOP => {}
2990        opcodes::ACONST_NULL => stack.push(FrameType::Null),
2991        opcodes::ICONST_M1..=opcodes::ICONST_5 => stack.push(FrameType::Integer),
2992        opcodes::LCONST_0 | opcodes::LCONST_1 => stack.push(FrameType::Long),
2993        opcodes::FCONST_0..=opcodes::FCONST_2 => stack.push(FrameType::Float),
2994        opcodes::DCONST_0 | opcodes::DCONST_1 => stack.push(FrameType::Double),
2995        opcodes::BIPUSH => stack.push(FrameType::Integer),
2996        opcodes::SIPUSH => stack.push(FrameType::Integer),
2997        opcodes::LDC..=opcodes::LDC2_W => {
2998            let ty = ldc_type(insn, cp)?;
2999            stack.push(ty);
3000        }
3001        opcodes::ILOAD..=opcodes::ALOAD => {
3002            let index = var_index(insn)?;
3003            if let Some(value) = locals.get(index as usize) {
3004                stack.push(value.clone());
3005            } else {
3006                stack.push(FrameType::Top);
3007            }
3008        }
3009        opcodes::ILOAD_0..=opcodes::ILOAD_3 => stack.push(load_local(
3010            &locals,
3011            (insn.opcode - opcodes::ILOAD_0) as u16,
3012            FrameType::Integer,
3013        )),
3014        opcodes::LLOAD_0..=opcodes::LLOAD_3 => stack.push(load_local(
3015            &locals,
3016            (insn.opcode - opcodes::LLOAD_0) as u16,
3017            FrameType::Long,
3018        )),
3019        opcodes::FLOAD_0..=opcodes::FLOAD_3 => stack.push(load_local(
3020            &locals,
3021            (insn.opcode - opcodes::FLOAD_0) as u16,
3022            FrameType::Float,
3023        )),
3024        opcodes::DLOAD_0..=opcodes::DLOAD_3 => stack.push(load_local(
3025            &locals,
3026            (insn.opcode - opcodes::DLOAD_0) as u16,
3027            FrameType::Double,
3028        )),
3029        opcodes::ALOAD_0..=opcodes::ALOAD_3 => stack.push(load_local(
3030            &locals,
3031            (insn.opcode - opcodes::ALOAD_0) as u16,
3032            FrameType::Object(class_node.name.clone()),
3033        )),
3034        opcodes::IALOAD..=opcodes::SALOAD => {
3035            pop(&mut stack)?;
3036            let array_ref = pop(&mut stack)?; //fixed: array -> java/lang/Object.
3037            let ty = match insn.opcode {
3038                opcodes::IALOAD => FrameType::Integer,
3039                opcodes::LALOAD => FrameType::Long,
3040                opcodes::FALOAD => FrameType::Float,
3041                opcodes::DALOAD => FrameType::Double,
3042                opcodes::AALOAD => array_element_type(&array_ref)
3043                    .unwrap_or_else(|| FrameType::Object("java/lang/Object".to_string())),
3044                opcodes::BALOAD..=opcodes::SALOAD => FrameType::Integer,
3045                _ => FrameType::Top,
3046            };
3047            stack.push(ty);
3048        }
3049        opcodes::ISTORE..=opcodes::ASTORE => {
3050            let index = var_index(insn)?;
3051            let value = pop(&mut stack)?;
3052            store_local(&mut locals, index, value);
3053        }
3054        opcodes::ISTORE_0..=opcodes::ISTORE_3 => {
3055            let value = pop(&mut stack)?;
3056            store_local(&mut locals, (insn.opcode - opcodes::ISTORE_0) as u16, value);
3057        }
3058        opcodes::LSTORE_0..=opcodes::LSTORE_3 => {
3059            let value = pop(&mut stack)?;
3060            store_local(&mut locals, (insn.opcode - opcodes::LSTORE_0) as u16, value);
3061        }
3062        opcodes::FSTORE_0..=opcodes::FSTORE_3 => {
3063            let value = pop(&mut stack)?;
3064            store_local(&mut locals, (insn.opcode - opcodes::FSTORE_0) as u16, value);
3065        }
3066        opcodes::DSTORE_0..=opcodes::DSTORE_3 => {
3067            let value = pop(&mut stack)?;
3068            store_local(&mut locals, (insn.opcode - opcodes::DSTORE_0) as u16, value);
3069        }
3070        opcodes::ASTORE_0..=opcodes::ASTORE_3 => {
3071            let value = pop(&mut stack)?;
3072            store_local(&mut locals, (insn.opcode - opcodes::ASTORE_0) as u16, value);
3073        }
3074        opcodes::IASTORE..=opcodes::SASTORE => {
3075            pop(&mut stack)?;
3076            pop(&mut stack)?;
3077            pop(&mut stack)?;
3078        }
3079        opcodes::POP => {
3080            pop(&mut stack)?;
3081        }
3082        opcodes::POP2 => {
3083            let v1 = pop(&mut stack)?;
3084            if is_category2(&v1) {
3085                // Form 2: ..., value2(cat2) -> ...
3086                // already popped
3087            } else {
3088                let v2 = pop(&mut stack)?;
3089                if is_category2(&v2) {
3090                    return Err(ClassWriteError::FrameComputation(
3091                        "pop2 invalid".to_string(),
3092                    ));
3093                }
3094            }
3095        }
3096        opcodes::DUP => {
3097            let v1 = pop(&mut stack)?;
3098            if is_category2(&v1) {
3099                return Err(ClassWriteError::FrameComputation(
3100                    "dup category2".to_string(),
3101                ));
3102            }
3103            stack.push(v1.clone());
3104            stack.push(v1);
3105        }
3106        opcodes::DUP_X1 => {
3107            let v1 = pop(&mut stack)?;
3108            let v2 = pop(&mut stack)?;
3109            if is_category2(&v1) || is_category2(&v2) {
3110                return Err(ClassWriteError::FrameComputation("dup_x1".to_string()));
3111            }
3112            stack.push(v1.clone());
3113            stack.push(v2);
3114            stack.push(v1);
3115        }
3116        opcodes::DUP_X2 => {
3117            let v1 = pop(&mut stack)?;
3118            if is_category2(&v1) {
3119                return Err(ClassWriteError::FrameComputation("dup_x2".to_string()));
3120            }
3121            let v2 = pop(&mut stack)?;
3122            if is_category2(&v2) {
3123                // Form 2: ..., v2(cat2), v1(cat1) -> ..., v1, v2, v1
3124                stack.push(v1.clone());
3125                stack.push(v2);
3126                stack.push(v1);
3127            } else {
3128                // Form 1/3: ..., v3(cat1|cat2), v2(cat1), v1(cat1) -> ..., v1, v3, v2, v1
3129                let v3 = pop(&mut stack)?;
3130                stack.push(v1.clone());
3131                stack.push(v3);
3132                stack.push(v2);
3133                stack.push(v1);
3134            }
3135        }
3136        opcodes::DUP2 => {
3137            let v1 = pop(&mut stack)?;
3138            if is_category2(&v1) {
3139                stack.push(v1.clone());
3140                stack.push(v1);
3141            } else {
3142                let v2 = pop(&mut stack)?;
3143                if is_category2(&v2) {
3144                    return Err(ClassWriteError::FrameComputation("dup2".to_string()));
3145                }
3146                stack.push(v2.clone());
3147                stack.push(v1.clone());
3148                stack.push(v2);
3149                stack.push(v1);
3150            }
3151        }
3152        opcodes::DUP2_X1 => {
3153            let v1 = pop(&mut stack)?;
3154            if is_category2(&v1) {
3155                let v2 = pop(&mut stack)?;
3156                stack.push(v1.clone());
3157                stack.push(v2);
3158                stack.push(v1);
3159            } else {
3160                let v2 = pop(&mut stack)?;
3161                let v3 = pop(&mut stack)?;
3162                stack.push(v2.clone());
3163                stack.push(v1.clone());
3164                stack.push(v3);
3165                stack.push(v2);
3166                stack.push(v1);
3167            }
3168        }
3169        opcodes::DUP2_X2 => {
3170            let v1 = pop(&mut stack)?;
3171            if is_category2(&v1) {
3172                let v2 = pop(&mut stack)?;
3173                let v3 = pop(&mut stack)?;
3174                stack.push(v1.clone());
3175                stack.push(v3);
3176                stack.push(v2);
3177                stack.push(v1);
3178            } else {
3179                let v2 = pop(&mut stack)?;
3180                let v3 = pop(&mut stack)?;
3181                let v4 = pop(&mut stack)?;
3182                stack.push(v2.clone());
3183                stack.push(v1.clone());
3184                stack.push(v4);
3185                stack.push(v3);
3186                stack.push(v2);
3187                stack.push(v1);
3188            }
3189        }
3190        opcodes::SWAP => {
3191            let v1 = pop(&mut stack)?;
3192            let v2 = pop(&mut stack)?;
3193            if is_category2(&v1) || is_category2(&v2) {
3194                return Err(ClassWriteError::FrameComputation("swap".to_string()));
3195            }
3196            stack.push(v1);
3197            stack.push(v2);
3198        }
3199        opcodes::IADD
3200        | opcodes::ISUB
3201        | opcodes::IMUL
3202        | opcodes::IDIV
3203        | opcodes::IREM
3204        | opcodes::ISHL
3205        | opcodes::ISHR
3206        | opcodes::IUSHR
3207        | opcodes::IAND
3208        | opcodes::IOR
3209        | opcodes::IXOR => {
3210            pop(&mut stack)?;
3211            pop(&mut stack)?;
3212            stack.push(FrameType::Integer);
3213        }
3214        opcodes::LADD
3215        | opcodes::LSUB
3216        | opcodes::LMUL
3217        | opcodes::LDIV
3218        | opcodes::LREM
3219        | opcodes::LSHL
3220        | opcodes::LSHR
3221        | opcodes::LUSHR
3222        | opcodes::LAND
3223        | opcodes::LOR
3224        | opcodes::LXOR => {
3225            pop(&mut stack)?;
3226            pop(&mut stack)?;
3227            stack.push(FrameType::Long);
3228        }
3229        opcodes::FADD | opcodes::FSUB | opcodes::FMUL | opcodes::FDIV | opcodes::FREM => {
3230            pop(&mut stack)?;
3231            pop(&mut stack)?;
3232            stack.push(FrameType::Float);
3233        }
3234        opcodes::DADD | opcodes::DSUB | opcodes::DMUL | opcodes::DDIV | opcodes::DREM => {
3235            pop(&mut stack)?;
3236            pop(&mut stack)?;
3237            stack.push(FrameType::Double);
3238        }
3239        opcodes::INEG => {
3240            pop(&mut stack)?;
3241            stack.push(FrameType::Integer);
3242        }
3243        opcodes::LNEG => {
3244            pop(&mut stack)?;
3245            stack.push(FrameType::Long);
3246        }
3247        opcodes::FNEG => {
3248            pop(&mut stack)?;
3249            stack.push(FrameType::Float);
3250        }
3251        opcodes::DNEG => {
3252            pop(&mut stack)?;
3253            stack.push(FrameType::Double);
3254        }
3255        opcodes::IINC => {}
3256        opcodes::I2L => {
3257            pop(&mut stack)?;
3258            stack.push(FrameType::Long);
3259        }
3260        opcodes::I2F => {
3261            pop(&mut stack)?;
3262            stack.push(FrameType::Float);
3263        }
3264        opcodes::I2D => {
3265            pop(&mut stack)?;
3266            stack.push(FrameType::Double);
3267        }
3268        opcodes::L2I => {
3269            pop(&mut stack)?;
3270            stack.push(FrameType::Integer);
3271        }
3272        opcodes::L2F => {
3273            pop(&mut stack)?;
3274            stack.push(FrameType::Float);
3275        }
3276        opcodes::L2D => {
3277            pop(&mut stack)?;
3278            stack.push(FrameType::Double);
3279        }
3280        opcodes::F2I => {
3281            pop(&mut stack)?;
3282            stack.push(FrameType::Integer);
3283        }
3284        opcodes::F2L => {
3285            pop(&mut stack)?;
3286            stack.push(FrameType::Long);
3287        }
3288        opcodes::F2D => {
3289            pop(&mut stack)?;
3290            stack.push(FrameType::Double);
3291        }
3292        opcodes::D2I => {
3293            pop(&mut stack)?;
3294            stack.push(FrameType::Integer);
3295        }
3296        opcodes::D2L => {
3297            pop(&mut stack)?;
3298            stack.push(FrameType::Long);
3299        }
3300        opcodes::D2F => {
3301            pop(&mut stack)?;
3302            stack.push(FrameType::Float);
3303        }
3304        opcodes::I2B..=opcodes::I2S => {
3305            pop(&mut stack)?;
3306            stack.push(FrameType::Integer);
3307        }
3308        opcodes::LCMP..=opcodes::DCMPG => {
3309            pop(&mut stack)?;
3310            pop(&mut stack)?;
3311            stack.push(FrameType::Integer);
3312        }
3313        opcodes::IFEQ..=opcodes::IFLE | opcodes::IFNULL | opcodes::IFNONNULL => {
3314            pop(&mut stack)?;
3315        }
3316        opcodes::IF_ICMPEQ..=opcodes::IF_ACMPNE => {
3317            pop(&mut stack)?;
3318            pop(&mut stack)?;
3319        }
3320        opcodes::GOTO | opcodes::GOTO_W => {}
3321        opcodes::JSR | opcodes::RET | opcodes::JSR_W => {
3322            return Err(ClassWriteError::FrameComputation(format!(
3323                "jsr/ret not supported at {}",
3324                insn.offset
3325            )));
3326        }
3327        opcodes::TABLESWITCH | opcodes::LOOKUPSWITCH => {
3328            pop(&mut stack)?;
3329        }
3330        opcodes::IRETURN => {
3331            pop(&mut stack)?;
3332        }
3333        opcodes::LRETURN => {
3334            pop(&mut stack)?;
3335        }
3336        opcodes::FRETURN => {
3337            pop(&mut stack)?;
3338        }
3339        opcodes::DRETURN => {
3340            pop(&mut stack)?;
3341        }
3342        opcodes::ARETURN => {
3343            pop(&mut stack)?;
3344        }
3345        opcodes::RETURN => {}
3346        opcodes::GETSTATIC => {
3347            let ty = field_type(insn, cp)?;
3348            stack.push(ty);
3349        }
3350        opcodes::PUTSTATIC => {
3351            pop(&mut stack)?;
3352        }
3353        opcodes::GETFIELD => {
3354            pop(&mut stack)?;
3355            let ty = field_type(insn, cp)?;
3356            stack.push(ty);
3357        }
3358        opcodes::PUTFIELD => {
3359            pop(&mut stack)?;
3360            pop(&mut stack)?;
3361        }
3362        opcodes::INVOKEVIRTUAL..=opcodes::INVOKEDYNAMIC => {
3363            let (args, ret, owner, is_init) = method_type(insn, cp)?;
3364            for _ in 0..args.len() {
3365                pop(&mut stack)?;
3366            }
3367            if insn.opcode != opcodes::INVOKESTATIC && insn.opcode != opcodes::INVOKEDYNAMIC {
3368                let receiver = pop(&mut stack)?;
3369                if is_init {
3370                    let init_owner = if receiver == FrameType::UninitializedThis {
3371                        class_node.name.clone()
3372                    } else {
3373                        owner
3374                    };
3375                    initialize_uninitialized(&mut locals, &mut stack, receiver, init_owner);
3376                }
3377            }
3378            if let Some(ret) = ret {
3379                stack.push(ret);
3380            }
3381        }
3382        opcodes::NEW => {
3383            if let Operand::U2(_index) = insn.operand {
3384                stack.push(FrameType::Uninitialized(insn.offset));
3385            }
3386        }
3387        opcodes::NEWARRAY => {
3388            pop(&mut stack)?;
3389            if let Operand::U1(atype) = insn.operand {
3390                let desc = newarray_descriptor(atype)?;
3391                stack.push(FrameType::Object(desc));
3392            } else {
3393                stack.push(FrameType::Object("[I".to_string()));
3394            }
3395        }
3396        opcodes::ANEWARRAY => {
3397            pop(&mut stack)?;
3398            if let Operand::U2(index) = insn.operand {
3399                let class_name = cp_class_name(cp, index)?;
3400                stack.push(FrameType::Object(format!("[L{class_name};")));
3401            }
3402        }
3403        opcodes::ARRAYLENGTH => {
3404            pop(&mut stack)?;
3405            stack.push(FrameType::Integer);
3406        }
3407        opcodes::ATHROW => {
3408            pop(&mut stack)?;
3409        }
3410        opcodes::CHECKCAST => {
3411            pop(&mut stack)?;
3412            if let Operand::U2(index) = insn.operand {
3413                let class_name = cp_class_name(cp, index)?;
3414                stack.push(FrameType::Object(class_name.to_string()));
3415            }
3416        }
3417        opcodes::INSTANCEOF => {
3418            pop(&mut stack)?;
3419            stack.push(FrameType::Integer);
3420        }
3421        opcodes::MONITORENTER | opcodes::MONITOREXIT => {
3422            pop(&mut stack)?;
3423        }
3424        opcodes::WIDE => {
3425            if let Operand::Wide {
3426                opcode,
3427                index,
3428                increment,
3429            } = insn.operand
3430            {
3431                match opcode {
3432                    opcodes::ILOAD..=opcodes::ALOAD => {
3433                        if let Some(value) = locals.get(index as usize) {
3434                            stack.push(value.clone());
3435                        }
3436                    }
3437                    opcodes::ISTORE..=opcodes::ASTORE => {
3438                        let value = pop(&mut stack)?;
3439                        store_local(&mut locals, index, value);
3440                    }
3441                    opcodes::IINC => {
3442                        let _ = increment;
3443                    }
3444                    opcodes::RET => {}
3445                    _ => {}
3446                }
3447            }
3448        }
3449        opcodes::MULTIANEWARRAY => {
3450            if let Operand::MultiANewArray { dims, .. } = insn.operand {
3451                for _ in 0..dims {
3452                    pop(&mut stack)?;
3453                }
3454                if let Operand::MultiANewArray { index, .. } = insn.operand {
3455                    let desc = cp_class_name(cp, index)?;
3456                    stack.push(FrameType::Object(desc.to_string()));
3457                } else {
3458                    stack.push(FrameType::Object("[Ljava/lang/Object;".to_string()));
3459                }
3460            }
3461        }
3462        opcodes::BREAKPOINT | opcodes::IMPDEP1 | opcodes::IMPDEP2 => {}
3463        _ => {}
3464    }
3465
3466    Ok(FrameState { locals, stack })
3467}
3468
3469fn initialize_uninitialized(
3470    locals: &mut [FrameType],
3471    stack: &mut [FrameType],
3472    receiver: FrameType,
3473    owner: String,
3474) {
3475    let init = FrameType::Object(owner);
3476    for value in locals.iter_mut().chain(stack.iter_mut()) {
3477        if *value == receiver {
3478            *value = init.clone();
3479        }
3480    }
3481}
3482
3483fn is_category2(value: &FrameType) -> bool {
3484    matches!(value, FrameType::Long | FrameType::Double)
3485}
3486
3487fn load_local(locals: &[FrameType], index: u16, fallback: FrameType) -> FrameType {
3488    locals.get(index as usize).cloned().unwrap_or(fallback)
3489}
3490
3491fn store_local(locals: &mut Vec<FrameType>, index: u16, value: FrameType) {
3492    let idx = index as usize;
3493    if locals.len() <= idx {
3494        locals.resize(idx + 1, FrameType::Top);
3495    }
3496    locals[idx] = value.clone();
3497    if is_category2(&value) {
3498        if locals.len() <= idx + 1 {
3499            locals.resize(idx + 2, FrameType::Top);
3500        }
3501        locals[idx + 1] = FrameType::Top;
3502    }
3503}
3504
3505fn array_element_type(value: &FrameType) -> Option<FrameType> {
3506    let FrameType::Object(desc) = value else {
3507        return None;
3508    };
3509    if !desc.starts_with('[') {
3510        return None;
3511    }
3512    let element = &desc[1..];
3513    if element.starts_with('[') {
3514        return Some(FrameType::Object(element.to_string()));
3515    }
3516    let mut chars = element.chars();
3517    match chars.next() {
3518        Some('L') => {
3519            let name = element
3520                .trim_start_matches('L')
3521                .trim_end_matches(';')
3522                .to_string();
3523            Some(FrameType::Object(name))
3524        }
3525        Some('Z') | Some('B') | Some('C') | Some('S') | Some('I') => Some(FrameType::Integer),
3526        Some('F') => Some(FrameType::Float),
3527        Some('J') => Some(FrameType::Long),
3528        Some('D') => Some(FrameType::Double),
3529        _ => None,
3530    }
3531}
3532
3533fn var_index(insn: &ParsedInstruction) -> Result<u16, ClassWriteError> {
3534    match insn.operand {
3535        Operand::U1(value) => Ok(value as u16),
3536        Operand::Wide { index, .. } => Ok(index),
3537        _ => Err(ClassWriteError::FrameComputation(format!(
3538            "missing var index at {}",
3539            insn.offset
3540        ))),
3541    }
3542}
3543
3544fn ldc_type(insn: &ParsedInstruction, cp: &[CpInfo]) -> Result<FrameType, ClassWriteError> {
3545    let index = match insn.operand {
3546        Operand::U1(value) => value as u16,
3547        Operand::U2(value) => value,
3548        _ => {
3549            return Err(ClassWriteError::FrameComputation(format!(
3550                "invalid ldc at {}",
3551                insn.offset
3552            )));
3553        }
3554    };
3555    match cp.get(index as usize) {
3556        Some(CpInfo::Integer(_)) => Ok(FrameType::Integer),
3557        Some(CpInfo::Float(_)) => Ok(FrameType::Float),
3558        Some(CpInfo::Long(_)) => Ok(FrameType::Long),
3559        Some(CpInfo::Double(_)) => Ok(FrameType::Double),
3560        Some(CpInfo::String { .. }) => Ok(FrameType::Object("java/lang/String".to_string())),
3561        Some(CpInfo::Class { .. }) => Ok(FrameType::Object("java/lang/Class".to_string())),
3562        Some(CpInfo::MethodType { .. }) => {
3563            Ok(FrameType::Object("java/lang/invoke/MethodType".to_string()))
3564        }
3565        Some(CpInfo::MethodHandle { .. }) => Ok(FrameType::Object(
3566            "java/lang/invoke/MethodHandle".to_string(),
3567        )),
3568        _ => Ok(FrameType::Top),
3569    }
3570}
3571
3572fn field_type(insn: &ParsedInstruction, cp: &[CpInfo]) -> Result<FrameType, ClassWriteError> {
3573    let index = match insn.operand {
3574        Operand::U2(value) => value,
3575        _ => {
3576            return Err(ClassWriteError::FrameComputation(format!(
3577                "invalid field operand at {}",
3578                insn.offset
3579            )));
3580        }
3581    };
3582    let descriptor = cp_field_descriptor(cp, index)?;
3583    let field_type = parse_field_descriptor(descriptor)?;
3584    Ok(field_type_to_frame(field_type))
3585}
3586
3587fn method_type(
3588    insn: &ParsedInstruction,
3589    cp: &[CpInfo],
3590) -> Result<(Vec<FieldType>, Option<FrameType>, String, bool), ClassWriteError> {
3591    let index = match insn.operand {
3592        Operand::U2(value) => value,
3593        Operand::InvokeInterface { index, .. } => index,
3594        Operand::InvokeDynamic { index } => index,
3595        _ => {
3596            return Err(ClassWriteError::FrameComputation(format!(
3597                "invalid method operand at {}",
3598                insn.offset
3599            )));
3600        }
3601    };
3602    let (owner, descriptor, name) = cp_method_descriptor(cp, index, insn.opcode)?;
3603    let (args, ret) = parse_method_descriptor(descriptor)?;
3604    let ret_frame = match ret {
3605        FieldType::Void => None,
3606        other => Some(field_type_to_frame(other)),
3607    };
3608    Ok((args, ret_frame, owner.to_string(), name == "<init>"))
3609}
3610
3611fn field_type_to_frame(field_type: FieldType) -> FrameType {
3612    match field_type {
3613        FieldType::Boolean
3614        | FieldType::Byte
3615        | FieldType::Char
3616        | FieldType::Short
3617        | FieldType::Int => FrameType::Integer,
3618        FieldType::Float => FrameType::Float,
3619        FieldType::Long => FrameType::Long,
3620        FieldType::Double => FrameType::Double,
3621        FieldType::Object(name) => FrameType::Object(name),
3622        FieldType::Array(desc) => FrameType::Object(desc),
3623        FieldType::Void => FrameType::Top,
3624    }
3625}
3626
3627fn cp_class_name(cp: &[CpInfo], index: u16) -> Result<&str, ClassWriteError> {
3628    match cp.get(index as usize) {
3629        Some(CpInfo::Class { name_index }) => match cp.get(*name_index as usize) {
3630            Some(CpInfo::Utf8(name)) => Ok(name),
3631            _ => Err(ClassWriteError::InvalidConstantPool),
3632        },
3633        _ => Err(ClassWriteError::InvalidConstantPool),
3634    }
3635}
3636
3637fn newarray_descriptor(atype: u8) -> Result<String, ClassWriteError> {
3638    let desc = match atype {
3639        4 => "[Z",
3640        5 => "[C",
3641        6 => "[F",
3642        7 => "[D",
3643        8 => "[B",
3644        9 => "[S",
3645        10 => "[I",
3646        11 => "[J",
3647        _ => {
3648            return Err(ClassWriteError::FrameComputation(
3649                "invalid newarray type".to_string(),
3650            ));
3651        }
3652    };
3653    Ok(desc.to_string())
3654}
3655
3656fn cp_field_descriptor(cp: &[CpInfo], index: u16) -> Result<&str, ClassWriteError> {
3657    match cp.get(index as usize) {
3658        Some(CpInfo::Fieldref {
3659            name_and_type_index,
3660            ..
3661        }) => match cp.get(*name_and_type_index as usize) {
3662            Some(CpInfo::NameAndType {
3663                descriptor_index, ..
3664            }) => match cp.get(*descriptor_index as usize) {
3665                Some(CpInfo::Utf8(desc)) => Ok(desc),
3666                _ => Err(ClassWriteError::InvalidConstantPool),
3667            },
3668            _ => Err(ClassWriteError::InvalidConstantPool),
3669        },
3670        _ => Err(ClassWriteError::InvalidConstantPool),
3671    }
3672}
3673
3674fn cp_method_descriptor(
3675    cp: &[CpInfo],
3676    index: u16,
3677    opcode: u8,
3678) -> Result<(&str, &str, &str), ClassWriteError> {
3679    match cp.get(index as usize) {
3680        Some(CpInfo::Methodref {
3681            class_index,
3682            name_and_type_index,
3683        })
3684        | Some(CpInfo::InterfaceMethodref {
3685            class_index,
3686            name_and_type_index,
3687        }) => {
3688            let owner = cp_class_name(cp, *class_index)?;
3689            match cp.get(*name_and_type_index as usize) {
3690                Some(CpInfo::NameAndType {
3691                    name_index,
3692                    descriptor_index,
3693                }) => {
3694                    let name = cp_utf8(cp, *name_index)?;
3695                    let desc = cp_utf8(cp, *descriptor_index)?;
3696                    Ok((owner, desc, name))
3697                }
3698                _ => Err(ClassWriteError::InvalidConstantPool),
3699            }
3700        }
3701        Some(CpInfo::InvokeDynamic {
3702            name_and_type_index,
3703            ..
3704        }) if opcode == opcodes::INVOKEDYNAMIC => match cp.get(*name_and_type_index as usize) {
3705            Some(CpInfo::NameAndType {
3706                name_index,
3707                descriptor_index,
3708            }) => {
3709                let name = cp_utf8(cp, *name_index)?;
3710                let desc = cp_utf8(cp, *descriptor_index)?;
3711                Ok(("java/lang/Object", desc, name))
3712            }
3713            _ => Err(ClassWriteError::InvalidConstantPool),
3714        },
3715        _ => Err(ClassWriteError::InvalidConstantPool),
3716    }
3717}
3718
3719fn cp_utf8(cp: &[CpInfo], index: u16) -> Result<&str, ClassWriteError> {
3720    match cp.get(index as usize) {
3721        Some(CpInfo::Utf8(value)) => Ok(value.as_str()),
3722        _ => Err(ClassWriteError::InvalidConstantPool),
3723    }
3724}
3725
3726#[derive(Debug, Clone)]
3727enum FieldType {
3728    Boolean,
3729    Byte,
3730    Char,
3731    Short,
3732    Int,
3733    Float,
3734    Long,
3735    Double,
3736    Object(String),
3737    Array(String),
3738    Void,
3739}
3740
3741fn parse_field_descriptor(desc: &str) -> Result<FieldType, ClassWriteError> {
3742    let mut chars = desc.chars().peekable();
3743    parse_field_type(&mut chars)
3744}
3745
3746fn parse_method_descriptor(desc: &str) -> Result<(Vec<FieldType>, FieldType), ClassWriteError> {
3747    let mut chars = desc.chars().peekable();
3748    if chars.next() != Some('(') {
3749        return Err(ClassWriteError::FrameComputation(
3750            "bad method descriptor".to_string(),
3751        ));
3752    }
3753    let mut params = Vec::new();
3754    while let Some(&ch) = chars.peek() {
3755        if ch == ')' {
3756            chars.next();
3757            break;
3758        }
3759        params.push(parse_field_type(&mut chars)?);
3760    }
3761    let ret = parse_return_type(&mut chars)?;
3762    Ok((params, ret))
3763}
3764
3765fn parse_field_type<I>(chars: &mut std::iter::Peekable<I>) -> Result<FieldType, ClassWriteError>
3766where
3767    I: Iterator<Item = char>,
3768{
3769    match chars.next() {
3770        Some('Z') => Ok(FieldType::Boolean),
3771        Some('B') => Ok(FieldType::Byte),
3772        Some('C') => Ok(FieldType::Char),
3773        Some('S') => Ok(FieldType::Short),
3774        Some('I') => Ok(FieldType::Int),
3775        Some('F') => Ok(FieldType::Float),
3776        Some('J') => Ok(FieldType::Long),
3777        Some('D') => Ok(FieldType::Double),
3778        Some('L') => {
3779            let mut name = String::new();
3780            for ch in chars.by_ref() {
3781                if ch == ';' {
3782                    break;
3783                }
3784                name.push(ch);
3785            }
3786            Ok(FieldType::Object(name))
3787        }
3788        Some('[') => {
3789            let mut desc = String::from("[");
3790            let inner = parse_field_type(chars)?;
3791            match inner {
3792                FieldType::Object(name) => {
3793                    desc.push('L');
3794                    desc.push_str(&name);
3795                    desc.push(';');
3796                }
3797                FieldType::Boolean => desc.push('Z'),
3798                FieldType::Byte => desc.push('B'),
3799                FieldType::Char => desc.push('C'),
3800                FieldType::Short => desc.push('S'),
3801                FieldType::Int => desc.push('I'),
3802                FieldType::Float => desc.push('F'),
3803                FieldType::Long => desc.push('J'),
3804                FieldType::Double => desc.push('D'),
3805                FieldType::Void => {}
3806                FieldType::Array(inner_desc) => desc.push_str(&inner_desc),
3807            }
3808            Ok(FieldType::Array(desc))
3809        }
3810        _ => Err(ClassWriteError::FrameComputation(
3811            "bad field descriptor".to_string(),
3812        )),
3813    }
3814}
3815
3816fn parse_return_type<I>(chars: &mut std::iter::Peekable<I>) -> Result<FieldType, ClassWriteError>
3817where
3818    I: Iterator<Item = char>,
3819{
3820    match chars.peek() {
3821        Some('V') => {
3822            chars.next();
3823            Ok(FieldType::Void)
3824        }
3825        _ => parse_field_type(chars),
3826    }
3827}
3828
3829fn read_u1(code: &[u8], pos: &mut usize) -> Result<u8, ClassWriteError> {
3830    if *pos >= code.len() {
3831        return Err(ClassWriteError::FrameComputation(
3832            "unexpected eof".to_string(),
3833        ));
3834    }
3835    let value = code[*pos];
3836    *pos += 1;
3837    Ok(value)
3838}
3839
3840fn read_i1(code: &[u8], pos: &mut usize) -> Result<i8, ClassWriteError> {
3841    Ok(read_u1(code, pos)? as i8)
3842}
3843
3844fn read_u2(code: &[u8], pos: &mut usize) -> Result<u16, ClassWriteError> {
3845    if *pos + 2 > code.len() {
3846        return Err(ClassWriteError::FrameComputation(
3847            "unexpected eof".to_string(),
3848        ));
3849    }
3850    let value = u16::from_be_bytes([code[*pos], code[*pos + 1]]);
3851    *pos += 2;
3852    Ok(value)
3853}
3854
3855fn read_i2(code: &[u8], pos: &mut usize) -> Result<i16, ClassWriteError> {
3856    Ok(read_u2(code, pos)? as i16)
3857}
3858
3859fn read_i4(code: &[u8], pos: &mut usize) -> Result<i32, ClassWriteError> {
3860    if *pos + 4 > code.len() {
3861        return Err(ClassWriteError::FrameComputation(
3862            "unexpected eof".to_string(),
3863        ));
3864    }
3865    let value = i32::from_be_bytes([code[*pos], code[*pos + 1], code[*pos + 2], code[*pos + 3]]);
3866    *pos += 4;
3867    Ok(value)
3868}
3869
3870#[cfg(test)]
3871mod tests {
3872    use super::*;
3873    use crate::opcodes;
3874
3875    #[test]
3876    fn test_constant_pool_deduplication() {
3877        let mut cp = ConstantPoolBuilder::new();
3878        let i1 = cp.utf8("Hello");
3879        let i2 = cp.utf8("World");
3880        let i3 = cp.utf8("Hello");
3881
3882        assert_eq!(i1, 1);
3883        assert_eq!(i2, 2);
3884        assert_eq!(i3, 1, "Duplicate UTF8 should return existing index");
3885
3886        let c1 = cp.class("java/lang/Object");
3887        let c2 = cp.class("java/lang/Object");
3888        assert_eq!(c1, c2, "Duplicate Class should return existing index");
3889    }
3890
3891    #[test]
3892    fn test_basic_class_generation() {
3893        let mut cw = ClassWriter::new(0);
3894        cw.visit(52, 0, 0x0001, "TestClass", Some("java/lang/Object"), &[]);
3895        cw.visit_source_file("TestClass.java");
3896
3897        // Add a field
3898        let fv = cw.visit_field(0x0002, "myField", "I");
3899        fv.visit_end(&mut cw);
3900
3901        // Add a default constructor
3902        let mut mv = cw.visit_method(0x0001, "<init>", "()V");
3903        mv.visit_code();
3904        mv.visit_var_insn(opcodes::ALOAD, 0);
3905        mv.visit_method_insn(
3906            opcodes::INVOKESPECIAL,
3907            "java/lang/Object",
3908            "<init>",
3909            "()V",
3910            false,
3911        );
3912        mv.visit_insn(opcodes::RETURN);
3913        mv.visit_maxs(1, 1);
3914        mv.visit_end(&mut cw);
3915
3916        let result = cw.to_bytes();
3917        assert!(result.is_ok(), "Should generate bytes successfully");
3918
3919        let bytes = result.unwrap();
3920        assert!(bytes.len() > 4);
3921        assert_eq!(&bytes[0..4], &[0xCA, 0xFE, 0xBA, 0xBE]); // Magic number
3922    }
3923
3924    #[test]
3925    fn test_compute_frames_flag() {
3926        // Simple linear code, but checking if logic runs without panic
3927        let mut cw = ClassWriter::new(COMPUTE_FRAMES);
3928        cw.visit(52, 0, 0x0001, "FrameTest", Some("java/lang/Object"), &[]);
3929
3930        let mut mv = cw.visit_method(0x0009, "main", "([Ljava/lang/String;)V");
3931        mv.visit_code();
3932        mv.visit_field_insn(
3933            opcodes::GETSTATIC,
3934            "java/lang/System",
3935            "out",
3936            "Ljava/io/PrintStream;",
3937        );
3938        mv.visit_ldc_insn(LdcInsnNode::string("Hello"));
3939        mv.visit_method_insn(
3940            opcodes::INVOKEVIRTUAL,
3941            "java/io/PrintStream",
3942            "println",
3943            "(Ljava/lang/String;)V",
3944            false,
3945        );
3946        mv.visit_insn(opcodes::RETURN);
3947        // maxs should be ignored/recomputed
3948        mv.visit_maxs(0, 0);
3949        mv.visit_end(&mut cw);
3950
3951        let result = cw.to_bytes();
3952        assert!(result.is_ok());
3953    }
3954
3955    #[test]
3956    fn test_class_node_structure() {
3957        let mut cw = ClassWriter::new(0);
3958        cw.visit(52, 0, 0, "MyNode", None, &[]);
3959
3960        let node = cw.to_class_node().expect("Should create class node");
3961        assert_eq!(node.name, "MyNode");
3962        assert_eq!(node.major_version, 52);
3963    }
3964}