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