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