Skip to main content

rust_asm/
class_writer.rs

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