Skip to main content

rust_asm/
class_writer.rs

1use crate::class_reader::{
2    AttributeInfo, BootstrapMethod, CodeAttribute, CpInfo, ExceptionTableEntry, InnerClass,
3    LineNumber, LocalVariable, MethodParameter, StackMapFrame, VerificationTypeInfo,
4};
5use crate::nodes::{ClassNode, FieldNode, MethodNode};
6
7#[derive(Debug)]
8pub enum ClassWriteError {
9    MissingConstantPool,
10    InvalidConstantPool,
11    InvalidOpcode { opcode: u8, offset: usize },
12    FrameComputation(String),
13}
14
15impl std::fmt::Display for ClassWriteError {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            ClassWriteError::MissingConstantPool => write!(f, "missing constant pool"),
19            ClassWriteError::InvalidConstantPool => write!(f, "invalid constant pool"),
20            ClassWriteError::InvalidOpcode { opcode, offset } => {
21                write!(f, "invalid opcode 0x{opcode:02X} at offset {offset}")
22            }
23            ClassWriteError::FrameComputation(message) => {
24                write!(f, "frame computation error: {message}")
25            }
26        }
27    }
28}
29
30impl std::error::Error for ClassWriteError {}
31
32pub const COMPUTE_FRAMES: u32 = 0x1;
33pub const COMPUTE_MAXS: u32 = 0x2;
34
35pub struct ClassWriter {
36    options: u32,
37}
38
39impl ClassWriter {
40    pub fn new(options: u32) -> Self {
41        Self { options }
42    }
43
44    pub fn from_class_node(_class_node: &ClassNode) -> Self {
45        Self::new(0)
46    }
47
48    pub fn to_bytes(&self, class_node: &ClassNode) -> Result<Vec<u8>, ClassWriteError> {
49        if class_node.constant_pool.is_empty() {
50            return Err(ClassWriteError::MissingConstantPool);
51        }
52
53        let mut cp = class_node.constant_pool.clone();
54        let mut out = Vec::new();
55        write_u4(&mut out, 0xCAFEBABE);
56        write_u2(&mut out, class_node.minor_version);
57        write_u2(&mut out, class_node.major_version);
58
59        let mut class_attributes = class_node.attributes.clone();
60        if let Some(source_file) = &class_node.source_file {
61            class_attributes.retain(|attr| !matches!(attr, AttributeInfo::SourceFile { .. }));
62            let source_index = ensure_utf8(&mut cp, source_file);
63            class_attributes.push(AttributeInfo::SourceFile {
64                sourcefile_index: source_index,
65            });
66        }
67
68        let mut attribute_names = Vec::new();
69        collect_attribute_names(&class_attributes, &mut attribute_names);
70        for field in &class_node.fields {
71            collect_attribute_names(&field.attributes, &mut attribute_names);
72        }
73        for method in &class_node.methods {
74            collect_attribute_names(&method.attributes, &mut attribute_names);
75            if let Some(code) = &method.code {
76                attribute_names.push("Code".to_string());
77                collect_attribute_names(&code.attributes, &mut attribute_names);
78            }
79        }
80        for name in attribute_names {
81            ensure_utf8(&mut cp, &name);
82        }
83
84        let mut precomputed_stack_maps: Vec<Option<Vec<StackMapFrame>>> =
85            Vec::with_capacity(class_node.methods.len());
86        let mut precomputed_maxs: Vec<Option<(u16, u16)>> =
87            Vec::with_capacity(class_node.methods.len());
88        let compute_frames = self.options & COMPUTE_FRAMES != 0;
89        let compute_maxs_flag = self.options & COMPUTE_MAXS != 0;
90        if compute_frames {
91            ensure_utf8(&mut cp, "StackMapTable");
92            for method in &class_node.methods {
93                if let Some(code) = &method.code {
94                    let maxs = if compute_maxs_flag {
95                        Some(compute_maxs(method, class_node, code, &cp)?)
96                    } else {
97                        None
98                    };
99                    let max_locals = maxs.map(|item| item.1).unwrap_or(code.max_locals);
100                    let stack_map =
101                        compute_stack_map_table(method, class_node, code, &mut cp, max_locals)?;
102                    precomputed_stack_maps.push(Some(stack_map));
103                    precomputed_maxs.push(maxs);
104                } else {
105                    precomputed_stack_maps.push(None);
106                    precomputed_maxs.push(None);
107                }
108            }
109        } else if compute_maxs_flag {
110            for method in &class_node.methods {
111                if let Some(code) = &method.code {
112                    precomputed_maxs.push(Some(compute_maxs(method, class_node, code, &cp)?));
113                } else {
114                    precomputed_maxs.push(None);
115                }
116            }
117            precomputed_stack_maps.resize(class_node.methods.len(), None);
118        } else {
119            precomputed_stack_maps.resize(class_node.methods.len(), None);
120            precomputed_maxs.resize(class_node.methods.len(), None);
121        }
122
123        write_constant_pool(&mut out, &cp)?;
124        write_u2(&mut out, class_node.access_flags);
125        write_u2(&mut out, class_node.this_class);
126        write_u2(&mut out, class_node.super_class);
127        write_u2(&mut out, class_node.interface_indices.len() as u16);
128        for index in &class_node.interface_indices {
129            write_u2(&mut out, *index);
130        }
131
132        write_u2(&mut out, class_node.fields.len() as u16);
133        for field in &class_node.fields {
134            write_field(&mut out, field, &mut cp)?;
135        }
136
137        write_u2(&mut out, class_node.methods.len() as u16);
138        for (index, method) in class_node.methods.iter().enumerate() {
139            let stack_map = precomputed_stack_maps
140                .get(index)
141                .and_then(|item| item.as_ref());
142            let maxs = precomputed_maxs.get(index).and_then(|item| *item);
143            write_method(
144                &mut out,
145                method,
146                class_node,
147                &mut cp,
148                self.options,
149                stack_map,
150                maxs,
151            )?;
152        }
153
154        write_u2(&mut out, class_attributes.len() as u16);
155        for attr in &class_attributes {
156            write_attribute(&mut out, attr, &mut cp, None, self.options, None, None)?;
157        }
158
159        Ok(out)
160    }
161}
162
163fn write_field(
164    out: &mut Vec<u8>,
165    field: &FieldNode,
166    cp: &mut Vec<CpInfo>,
167) -> Result<(), ClassWriteError> {
168    write_u2(out, field.access_flags);
169    write_u2(out, field.name_index);
170    write_u2(out, field.descriptor_index);
171    write_u2(out, field.attributes.len() as u16);
172    for attr in &field.attributes {
173        write_attribute(out, attr, cp, None, 0, None, None)?;
174    }
175    Ok(())
176}
177
178fn write_method(
179    out: &mut Vec<u8>,
180    method: &MethodNode,
181    class_node: &ClassNode,
182    cp: &mut Vec<CpInfo>,
183    options: u32,
184    precomputed_stack_map: Option<&Vec<StackMapFrame>>,
185    precomputed_maxs: Option<(u16, u16)>,
186) -> Result<(), ClassWriteError> {
187    write_u2(out, method.access_flags);
188    write_u2(out, method.name_index);
189    write_u2(out, method.descriptor_index);
190
191    let mut attributes = method.attributes.clone();
192    if let Some(code) = &method.code {
193        attributes.retain(|attr| !matches!(attr, AttributeInfo::Code(_)));
194        attributes.push(AttributeInfo::Code(code.clone()));
195    }
196
197    write_u2(out, attributes.len() as u16);
198    for attr in &attributes {
199        write_attribute(
200            out,
201            attr,
202            cp,
203            Some((method, class_node)),
204            options,
205            precomputed_stack_map,
206            precomputed_maxs,
207        )?;
208    }
209    Ok(())
210}
211
212fn write_attribute(
213    out: &mut Vec<u8>,
214    attr: &AttributeInfo,
215    cp: &mut Vec<CpInfo>,
216    method_ctx: Option<(&MethodNode, &ClassNode)>,
217    options: u32,
218    precomputed_stack_map: Option<&Vec<StackMapFrame>>,
219    precomputed_maxs: Option<(u16, u16)>,
220) -> Result<(), ClassWriteError> {
221    match attr {
222        AttributeInfo::Code(code) => {
223            let name_index = ensure_utf8(cp, "Code");
224            let mut info = Vec::new();
225            let mut code_attributes = code.attributes.clone();
226            let (max_stack, max_locals) =
227                precomputed_maxs.unwrap_or((code.max_stack, code.max_locals));
228            if options & COMPUTE_FRAMES != 0 {
229                code_attributes.retain(|item| !matches!(item, AttributeInfo::StackMapTable { .. }));
230                let stack_map = if let Some(precomputed) = precomputed_stack_map {
231                    precomputed.clone()
232                } else {
233                    let (method, class_node) = method_ctx.ok_or_else(|| {
234                        ClassWriteError::FrameComputation("missing method".to_string())
235                    })?;
236                    compute_stack_map_table(method, class_node, code, cp, max_locals)?
237                };
238                code_attributes.push(AttributeInfo::StackMapTable { entries: stack_map });
239            }
240
241            write_u2(&mut info, max_stack);
242            write_u2(&mut info, max_locals);
243            write_u4(&mut info, code.code.len() as u32);
244            info.extend_from_slice(&code.code);
245            write_u2(&mut info, code.exception_table.len() as u16);
246            for entry in &code.exception_table {
247                write_exception_table_entry(&mut info, entry);
248            }
249            write_u2(&mut info, code_attributes.len() as u16);
250            for nested in &code_attributes {
251                write_attribute(&mut info, nested, cp, method_ctx, options, None, None)?;
252            }
253            write_attribute_with_info(out, name_index, &info);
254        }
255        AttributeInfo::ConstantValue {
256            constantvalue_index,
257        } => {
258            let name_index = ensure_utf8(cp, "ConstantValue");
259            let mut info = Vec::new();
260            write_u2(&mut info, *constantvalue_index);
261            write_attribute_with_info(out, name_index, &info);
262        }
263        AttributeInfo::Exceptions {
264            exception_index_table,
265        } => {
266            let name_index = ensure_utf8(cp, "Exceptions");
267            let mut info = Vec::new();
268            write_u2(&mut info, exception_index_table.len() as u16);
269            for index in exception_index_table {
270                write_u2(&mut info, *index);
271            }
272            write_attribute_with_info(out, name_index, &info);
273        }
274        AttributeInfo::SourceFile { sourcefile_index } => {
275            let name_index = ensure_utf8(cp, "SourceFile");
276            let mut info = Vec::new();
277            write_u2(&mut info, *sourcefile_index);
278            write_attribute_with_info(out, name_index, &info);
279        }
280        AttributeInfo::LineNumberTable { entries } => {
281            let name_index = ensure_utf8(cp, "LineNumberTable");
282            let mut info = Vec::new();
283            write_u2(&mut info, entries.len() as u16);
284            for entry in entries {
285                write_line_number(&mut info, entry);
286            }
287            write_attribute_with_info(out, name_index, &info);
288        }
289        AttributeInfo::LocalVariableTable { entries } => {
290            let name_index = ensure_utf8(cp, "LocalVariableTable");
291            let mut info = Vec::new();
292            write_u2(&mut info, entries.len() as u16);
293            for entry in entries {
294                write_local_variable(&mut info, entry);
295            }
296            write_attribute_with_info(out, name_index, &info);
297        }
298        AttributeInfo::Signature { signature_index } => {
299            let name_index = ensure_utf8(cp, "Signature");
300            let mut info = Vec::new();
301            write_u2(&mut info, *signature_index);
302            write_attribute_with_info(out, name_index, &info);
303        }
304        AttributeInfo::StackMapTable { entries } => {
305            let name_index = ensure_utf8(cp, "StackMapTable");
306            let mut info = Vec::new();
307            write_u2(&mut info, entries.len() as u16);
308            for entry in entries {
309                write_stack_map_frame(&mut info, entry);
310            }
311            write_attribute_with_info(out, name_index, &info);
312        }
313        AttributeInfo::Deprecated => {
314            let name_index = ensure_utf8(cp, "Deprecated");
315            write_attribute_with_info(out, name_index, &[]);
316        }
317        AttributeInfo::Synthetic => {
318            let name_index = ensure_utf8(cp, "Synthetic");
319            write_attribute_with_info(out, name_index, &[]);
320        }
321        AttributeInfo::InnerClasses { classes } => {
322            let name_index = ensure_utf8(cp, "InnerClasses");
323            let mut info = Vec::new();
324            write_u2(&mut info, classes.len() as u16);
325            for class in classes {
326                write_inner_class(&mut info, class);
327            }
328            write_attribute_with_info(out, name_index, &info);
329        }
330        AttributeInfo::EnclosingMethod {
331            class_index,
332            method_index,
333        } => {
334            let name_index = ensure_utf8(cp, "EnclosingMethod");
335            let mut info = Vec::new();
336            write_u2(&mut info, *class_index);
337            write_u2(&mut info, *method_index);
338            write_attribute_with_info(out, name_index, &info);
339        }
340        AttributeInfo::BootstrapMethods { methods } => {
341            let name_index = ensure_utf8(cp, "BootstrapMethods");
342            let mut info = Vec::new();
343            write_u2(&mut info, methods.len() as u16);
344            for method in methods {
345                write_bootstrap_method(&mut info, method);
346            }
347            write_attribute_with_info(out, name_index, &info);
348        }
349        AttributeInfo::MethodParameters { parameters } => {
350            let name_index = ensure_utf8(cp, "MethodParameters");
351            let mut info = Vec::new();
352            write_u1(&mut info, parameters.len() as u8);
353            for parameter in parameters {
354                write_method_parameter(&mut info, parameter);
355            }
356            write_attribute_with_info(out, name_index, &info);
357        }
358        AttributeInfo::Unknown { name, info } => {
359            let name_index = ensure_utf8(cp, name);
360            write_attribute_with_info(out, name_index, info);
361        }
362    }
363
364    Ok(())
365}
366
367fn write_attribute_with_info(out: &mut Vec<u8>, name_index: u16, info: &[u8]) {
368    write_u2(out, name_index);
369    write_u4(out, info.len() as u32);
370    out.extend_from_slice(info);
371}
372
373fn write_exception_table_entry(out: &mut Vec<u8>, entry: &ExceptionTableEntry) {
374    write_u2(out, entry.start_pc);
375    write_u2(out, entry.end_pc);
376    write_u2(out, entry.handler_pc);
377    write_u2(out, entry.catch_type);
378}
379
380fn write_line_number(out: &mut Vec<u8>, entry: &LineNumber) {
381    write_u2(out, entry.start_pc);
382    write_u2(out, entry.line_number);
383}
384
385fn write_local_variable(out: &mut Vec<u8>, entry: &LocalVariable) {
386    write_u2(out, entry.start_pc);
387    write_u2(out, entry.length);
388    write_u2(out, entry.name_index);
389    write_u2(out, entry.descriptor_index);
390    write_u2(out, entry.index);
391}
392
393fn write_inner_class(out: &mut Vec<u8>, entry: &InnerClass) {
394    write_u2(out, entry.inner_class_info_index);
395    write_u2(out, entry.outer_class_info_index);
396    write_u2(out, entry.inner_name_index);
397    write_u2(out, entry.inner_class_access_flags);
398}
399
400fn write_bootstrap_method(out: &mut Vec<u8>, entry: &BootstrapMethod) {
401    write_u2(out, entry.bootstrap_method_ref);
402    write_u2(out, entry.bootstrap_arguments.len() as u16);
403    for arg in &entry.bootstrap_arguments {
404        write_u2(out, *arg);
405    }
406}
407
408fn write_method_parameter(out: &mut Vec<u8>, entry: &MethodParameter) {
409    write_u2(out, entry.name_index);
410    write_u2(out, entry.access_flags);
411}
412
413fn write_stack_map_frame(out: &mut Vec<u8>, frame: &StackMapFrame) {
414    match frame {
415        StackMapFrame::SameFrame { offset_delta } => {
416            write_u1(out, *offset_delta as u8);
417        }
418        StackMapFrame::SameLocals1StackItemFrame {
419            offset_delta,
420            stack,
421        } => {
422            write_u1(out, (*offset_delta as u8) + 64);
423            write_verification_type(out, stack);
424        }
425        StackMapFrame::SameLocals1StackItemFrameExtended {
426            offset_delta,
427            stack,
428        } => {
429            write_u1(out, 247);
430            write_u2(out, *offset_delta);
431            write_verification_type(out, stack);
432        }
433        StackMapFrame::ChopFrame { offset_delta, k } => {
434            write_u1(out, 251 - *k);
435            write_u2(out, *offset_delta);
436        }
437        StackMapFrame::SameFrameExtended { offset_delta } => {
438            write_u1(out, 251);
439            write_u2(out, *offset_delta);
440        }
441        StackMapFrame::AppendFrame {
442            offset_delta,
443            locals,
444        } => {
445            write_u1(out, 251 + locals.len() as u8);
446            write_u2(out, *offset_delta);
447            for local in locals {
448                write_verification_type(out, local);
449            }
450        }
451        StackMapFrame::FullFrame {
452            offset_delta,
453            locals,
454            stack,
455        } => {
456            write_u1(out, 255);
457            write_u2(out, *offset_delta);
458            write_u2(out, locals.len() as u16);
459            for local in locals {
460                write_verification_type(out, local);
461            }
462            write_u2(out, stack.len() as u16);
463            for value in stack {
464                write_verification_type(out, value);
465            }
466        }
467    }
468}
469
470fn write_verification_type(out: &mut Vec<u8>, value: &VerificationTypeInfo) {
471    match value {
472        VerificationTypeInfo::Top => write_u1(out, 0),
473        VerificationTypeInfo::Integer => write_u1(out, 1),
474        VerificationTypeInfo::Float => write_u1(out, 2),
475        VerificationTypeInfo::Double => write_u1(out, 3),
476        VerificationTypeInfo::Long => write_u1(out, 4),
477        VerificationTypeInfo::Null => write_u1(out, 5),
478        VerificationTypeInfo::UninitializedThis => write_u1(out, 6),
479        VerificationTypeInfo::Object { cpool_index } => {
480            write_u1(out, 7);
481            write_u2(out, *cpool_index);
482        }
483        VerificationTypeInfo::Uninitialized { offset } => {
484            write_u1(out, 8);
485            write_u2(out, *offset);
486        }
487    }
488}
489
490fn collect_attribute_names(attributes: &[AttributeInfo], names: &mut Vec<String>) {
491    for attr in attributes {
492        match attr {
493            AttributeInfo::Code(_) => names.push("Code".to_string()),
494            AttributeInfo::ConstantValue { .. } => names.push("ConstantValue".to_string()),
495            AttributeInfo::Exceptions { .. } => names.push("Exceptions".to_string()),
496            AttributeInfo::SourceFile { .. } => names.push("SourceFile".to_string()),
497            AttributeInfo::LineNumberTable { .. } => names.push("LineNumberTable".to_string()),
498            AttributeInfo::LocalVariableTable { .. } => {
499                names.push("LocalVariableTable".to_string())
500            }
501            AttributeInfo::Signature { .. } => names.push("Signature".to_string()),
502            AttributeInfo::StackMapTable { .. } => names.push("StackMapTable".to_string()),
503            AttributeInfo::Deprecated => names.push("Deprecated".to_string()),
504            AttributeInfo::Synthetic => names.push("Synthetic".to_string()),
505            AttributeInfo::InnerClasses { .. } => names.push("InnerClasses".to_string()),
506            AttributeInfo::EnclosingMethod { .. } => names.push("EnclosingMethod".to_string()),
507            AttributeInfo::BootstrapMethods { .. } => names.push("BootstrapMethods".to_string()),
508            AttributeInfo::MethodParameters { .. } => names.push("MethodParameters".to_string()),
509            AttributeInfo::Unknown { name, .. } => names.push(name.clone()),
510        }
511    }
512}
513
514fn write_constant_pool(out: &mut Vec<u8>, cp: &[CpInfo]) -> Result<(), ClassWriteError> {
515    write_u2(out, cp.len() as u16);
516    for entry in cp.iter().skip(1) {
517        match entry {
518            CpInfo::Unusable => {}
519            CpInfo::Utf8(value) => {
520                write_u1(out, 1);
521                write_u2(out, value.len() as u16);
522                out.extend_from_slice(value.as_bytes());
523            }
524            CpInfo::Integer(value) => {
525                write_u1(out, 3);
526                write_u4(out, *value as u32);
527            }
528            CpInfo::Float(value) => {
529                write_u1(out, 4);
530                write_u4(out, value.to_bits());
531            }
532            CpInfo::Long(value) => {
533                write_u1(out, 5);
534                write_u8(out, *value as u64);
535            }
536            CpInfo::Double(value) => {
537                write_u1(out, 6);
538                write_u8(out, value.to_bits());
539            }
540            CpInfo::Class { name_index } => {
541                write_u1(out, 7);
542                write_u2(out, *name_index);
543            }
544            CpInfo::String { string_index } => {
545                write_u1(out, 8);
546                write_u2(out, *string_index);
547            }
548            CpInfo::Fieldref {
549                class_index,
550                name_and_type_index,
551            } => {
552                write_u1(out, 9);
553                write_u2(out, *class_index);
554                write_u2(out, *name_and_type_index);
555            }
556            CpInfo::Methodref {
557                class_index,
558                name_and_type_index,
559            } => {
560                write_u1(out, 10);
561                write_u2(out, *class_index);
562                write_u2(out, *name_and_type_index);
563            }
564            CpInfo::InterfaceMethodref {
565                class_index,
566                name_and_type_index,
567            } => {
568                write_u1(out, 11);
569                write_u2(out, *class_index);
570                write_u2(out, *name_and_type_index);
571            }
572            CpInfo::NameAndType {
573                name_index,
574                descriptor_index,
575            } => {
576                write_u1(out, 12);
577                write_u2(out, *name_index);
578                write_u2(out, *descriptor_index);
579            }
580            CpInfo::MethodHandle {
581                reference_kind,
582                reference_index,
583            } => {
584                write_u1(out, 15);
585                write_u1(out, *reference_kind);
586                write_u2(out, *reference_index);
587            }
588            CpInfo::MethodType { descriptor_index } => {
589                write_u1(out, 16);
590                write_u2(out, *descriptor_index);
591            }
592            CpInfo::Dynamic {
593                bootstrap_method_attr_index,
594                name_and_type_index,
595            } => {
596                write_u1(out, 17);
597                write_u2(out, *bootstrap_method_attr_index);
598                write_u2(out, *name_and_type_index);
599            }
600            CpInfo::InvokeDynamic {
601                bootstrap_method_attr_index,
602                name_and_type_index,
603            } => {
604                write_u1(out, 18);
605                write_u2(out, *bootstrap_method_attr_index);
606                write_u2(out, *name_and_type_index);
607            }
608            CpInfo::Module { name_index } => {
609                write_u1(out, 19);
610                write_u2(out, *name_index);
611            }
612            CpInfo::Package { name_index } => {
613                write_u1(out, 20);
614                write_u2(out, *name_index);
615            }
616        }
617    }
618    Ok(())
619}
620
621fn ensure_utf8(cp: &mut Vec<CpInfo>, value: &str) -> u16 {
622    if let Some(index) = cp_find_utf8(cp, value) {
623        return index;
624    }
625    cp.push(CpInfo::Utf8(value.to_string()));
626    (cp.len() - 1) as u16
627}
628
629fn ensure_class(cp: &mut Vec<CpInfo>, name: &str) -> u16 {
630    for (index, entry) in cp.iter().enumerate() {
631        if let CpInfo::Class { name_index } = entry
632            && let Some(CpInfo::Utf8(value)) = cp.get(*name_index as usize)
633            && value == name
634        {
635            return index as u16;
636        }
637    }
638    let name_index = ensure_utf8(cp, name);
639    cp.push(CpInfo::Class { name_index });
640    (cp.len() - 1) as u16
641}
642
643fn cp_find_utf8(cp: &[CpInfo], value: &str) -> Option<u16> {
644    for (index, entry) in cp.iter().enumerate() {
645        if let CpInfo::Utf8(existing) = entry
646            && existing == value
647        {
648            return Some(index as u16);
649        }
650    }
651    None
652}
653
654fn write_u1(out: &mut Vec<u8>, value: u8) {
655    out.push(value);
656}
657
658fn write_u2(out: &mut Vec<u8>, value: u16) {
659    out.extend_from_slice(&value.to_be_bytes());
660}
661
662fn write_u4(out: &mut Vec<u8>, value: u32) {
663    out.extend_from_slice(&value.to_be_bytes());
664}
665
666fn write_u8(out: &mut Vec<u8>, value: u64) {
667    out.extend_from_slice(&value.to_be_bytes());
668}
669
670#[derive(Debug, Clone, PartialEq, Eq)]
671enum FrameType {
672    Top,
673    Integer,
674    Float,
675    Long,
676    Double,
677    Null,
678    UninitializedThis,
679    Object(String),
680    Uninitialized(u16),
681}
682
683fn compute_stack_map_table(
684    method: &MethodNode,
685    class_node: &ClassNode,
686    code: &CodeAttribute,
687    cp: &mut Vec<CpInfo>,
688    max_locals: u16,
689) -> Result<Vec<StackMapFrame>, ClassWriteError> {
690    let insns = parse_instructions(&code.code)?;
691    if insns.is_empty() {
692        return Ok(Vec::new());
693    }
694
695    let mut insn_index = std::collections::HashMap::new();
696    for (index, insn) in insns.iter().enumerate() {
697        insn_index.insert(insn.offset, index);
698    }
699
700    let handlers = build_exception_handlers(code, cp)?;
701    let handler_common = handler_common_types(&handlers);
702    let mut frames: std::collections::HashMap<u16, FrameState> = std::collections::HashMap::new();
703    let mut worklist = std::collections::VecDeque::new();
704    let mut in_worklist = std::collections::HashSet::new();
705
706    let mut initial = initial_frame(method, class_node)?;
707    pad_locals(&mut initial.locals, max_locals);
708    frames.insert(0, initial.clone());
709    worklist.push_back(0u16);
710    in_worklist.insert(0u16);
711
712    let mut max_iterations = 0usize;
713    while let Some(offset) = worklist.pop_front() {
714        in_worklist.remove(&offset);
715        max_iterations += 1;
716        if max_iterations > 100000 {
717            return Err(ClassWriteError::FrameComputation(
718                "frame analysis exceeded iteration limit".to_string(),
719            ));
720        }
721        let index = *insn_index.get(&offset).ok_or_else(|| {
722            ClassWriteError::FrameComputation(format!("missing instruction at {offset}"))
723        })?;
724        let insn = &insns[index];
725        let frame = frames
726            .get(&offset)
727            .ok_or_else(|| ClassWriteError::FrameComputation(format!("missing frame at {offset}")))?
728            .clone();
729        let insn1 = &insn;
730        let out_frame = execute_instruction(insn1, &frame, class_node, cp)?;
731
732        for succ in instruction_successors(insn) {
733            if let Some(next_frame) = merge_frame(&out_frame, frames.get(&succ)) {
734                let changed = match frames.get(&succ) {
735                    Some(existing) => existing != &next_frame,
736                    None => true,
737                };
738                if changed {
739                    frames.insert(succ, next_frame);
740                    if in_worklist.insert(succ) {
741                        worklist.push_back(succ);
742                    }
743                }
744            }
745        }
746
747        for handler in handlers.iter().filter(|item| item.covers(offset)) {
748            let mut handler_frame = FrameState {
749                locals: frame.locals.clone(),
750                stack: Vec::new(),
751            };
752            let exception_type = handler_common
753                .get(&handler.handler_pc)
754                .cloned()
755                .unwrap_or_else(|| handler.exception_type.clone());
756            handler_frame.stack.push(exception_type);
757            if let Some(next_frame) = merge_frame(&handler_frame, frames.get(&handler.handler_pc)) {
758                let changed = match frames.get(&handler.handler_pc) {
759                    Some(existing) => existing != &next_frame,
760                    None => true,
761                };
762                if changed {
763                    frames.insert(handler.handler_pc, next_frame);
764                    if in_worklist.insert(handler.handler_pc) {
765                        worklist.push_back(handler.handler_pc);
766                    }
767                }
768            }
769        }
770    }
771
772    let mut frame_offsets: Vec<u16> = frames.keys().copied().collect();
773    frame_offsets.sort_unstable();
774    let mut result = Vec::new();
775    let mut previous_offset: i32 = -1;
776    for offset in frame_offsets {
777        if offset == 0 {
778            continue;
779        }
780        let frame = frames
781            .get(&offset)
782            .ok_or_else(|| ClassWriteError::FrameComputation(format!("missing frame at {offset}")))?
783            .clone();
784        let locals = compact_locals(&frame.locals);
785        let stack = frame.stack;
786        let offset_delta = (offset as i32 - previous_offset - 1) as u16;
787        previous_offset = offset as i32;
788        let locals_info = locals
789            .iter()
790            .map(|value| to_verification_type(value, cp))
791            .collect();
792        let stack_info = stack
793            .iter()
794            .map(|value| to_verification_type(value, cp))
795            .collect();
796        result.push(StackMapFrame::FullFrame {
797            offset_delta,
798            locals: locals_info,
799            stack: stack_info,
800        });
801    }
802
803    Ok(result)
804}
805
806#[derive(Debug, Clone, PartialEq, Eq)]
807struct FrameState {
808    locals: Vec<FrameType>,
809    stack: Vec<FrameType>,
810}
811
812fn merge_frame(frame: &FrameState, existing: Option<&FrameState>) -> Option<FrameState> {
813    match existing {
814        None => Some(frame.clone()),
815        Some(other) => {
816            let merged = FrameState {
817                locals: merge_vec(&frame.locals, &other.locals),
818                stack: merge_vec(&frame.stack, &other.stack),
819            };
820            if merged == *other { None } else { Some(merged) }
821        }
822    }
823}
824
825fn merge_vec(a: &[FrameType], b: &[FrameType]) -> Vec<FrameType> {
826    let len = a.len().max(b.len());
827    let mut merged = Vec::with_capacity(len);
828    for i in 0..len {
829        let left = a.get(i).cloned().unwrap_or(FrameType::Top);
830        let right = b.get(i).cloned().unwrap_or(FrameType::Top);
831        merged.push(merge_type(&left, &right));
832    }
833    merged
834}
835
836fn merge_type(a: &FrameType, b: &FrameType) -> FrameType {
837    if a == b {
838        return a.clone();
839    }
840    match (a, b) {
841        (FrameType::Top, _) => FrameType::Top,
842        (_, FrameType::Top) => FrameType::Top,
843        (FrameType::Null, FrameType::Object(name)) | (FrameType::Object(name), FrameType::Null) => {
844            FrameType::Object(name.clone())
845        }
846        (FrameType::Object(left), FrameType::Object(right)) => {
847            FrameType::Object(common_superclass(left, right))
848        }
849        (FrameType::Object(_), FrameType::Uninitialized(_))
850        | (FrameType::Uninitialized(_), FrameType::Object(_))
851        | (FrameType::UninitializedThis, FrameType::Object(_))
852        | (FrameType::Object(_), FrameType::UninitializedThis) => {
853            FrameType::Object("java/lang/Object".to_string())
854        }
855        _ => FrameType::Top,
856    }
857}
858
859fn common_superclass(left: &str, right: &str) -> String {
860    if left == right {
861        return left.to_string();
862    }
863    if left.starts_with('[') || right.starts_with('[') {
864        return "java/lang/Object".to_string();
865    }
866
867    let mut ancestors = std::collections::HashSet::new();
868    let mut current = left;
869    ancestors.insert(current.to_string());
870    while let Some(parent) = known_superclass(current) {
871        if ancestors.insert(parent.to_string()) {
872            current = parent;
873        } else {
874            break;
875        }
876    }
877    ancestors.insert("java/lang/Object".to_string());
878
879    current = right;
880    if ancestors.contains(current) {
881        return current.to_string();
882    }
883    while let Some(parent) = known_superclass(current) {
884        if ancestors.contains(parent) {
885            return parent.to_string();
886        }
887        current = parent;
888    }
889    "java/lang/Object".to_string()
890}
891
892fn known_superclass(name: &str) -> Option<&'static str> {
893    match name {
894        "java/lang/Throwable" => Some("java/lang/Object"),
895        "java/lang/Exception" => Some("java/lang/Throwable"),
896        "java/lang/RuntimeException" => Some("java/lang/Exception"),
897        "java/lang/IllegalArgumentException" => Some("java/lang/RuntimeException"),
898        "java/lang/IllegalStateException" => Some("java/lang/RuntimeException"),
899        "java/security/GeneralSecurityException" => Some("java/lang/Exception"),
900        "java/security/NoSuchAlgorithmException" => Some("java/security/GeneralSecurityException"),
901        "java/security/InvalidKeyException" => Some("java/security/GeneralSecurityException"),
902        "javax/crypto/NoSuchPaddingException" => Some("java/security/GeneralSecurityException"),
903        "javax/crypto/IllegalBlockSizeException" => Some("java/security/GeneralSecurityException"),
904        "javax/crypto/BadPaddingException" => Some("java/security/GeneralSecurityException"),
905        _ => None,
906    }
907}
908
909fn pad_locals(locals: &mut Vec<FrameType>, max_locals: u16) {
910    while locals.len() < max_locals as usize {
911        locals.push(FrameType::Top);
912    }
913}
914
915fn compute_maxs(
916    method: &MethodNode,
917    class_node: &ClassNode,
918    code: &CodeAttribute,
919    cp: &[CpInfo],
920) -> Result<(u16, u16), ClassWriteError> {
921    let insns = parse_instructions(&code.code)?;
922    if insns.is_empty() {
923        let initial = initial_frame(method, class_node)?;
924        return Ok((0, initial.locals.len() as u16));
925    }
926
927    let mut insn_index = std::collections::HashMap::new();
928    for (index, insn) in insns.iter().enumerate() {
929        insn_index.insert(insn.offset, index);
930    }
931
932    let handlers = build_exception_handlers(code, cp)?;
933    let mut frames: std::collections::HashMap<u16, FrameState> = std::collections::HashMap::new();
934    let mut worklist = std::collections::VecDeque::new();
935    let mut in_worklist = std::collections::HashSet::new();
936
937    let initial = initial_frame(method, class_node)?;
938    frames.insert(0, initial.clone());
939    worklist.push_back(0u16);
940    in_worklist.insert(0u16);
941
942    let mut max_stack = initial.stack.len();
943    let mut max_locals = initial.locals.len();
944    let mut max_iterations = 0usize;
945    let mut offset_hits: std::collections::HashMap<u16, u32> = std::collections::HashMap::new();
946    while let Some(offset) = worklist.pop_front() {
947        in_worklist.remove(&offset);
948        max_iterations += 1;
949        *offset_hits.entry(offset).or_insert(0) += 1;
950        if max_iterations > 100000 {
951            return Err(ClassWriteError::FrameComputation(
952                "frame analysis exceeded iteration limit".to_string(),
953            ));
954        }
955        let index = *insn_index.get(&offset).ok_or_else(|| {
956            ClassWriteError::FrameComputation(format!("missing instruction at {offset}"))
957        })?;
958        let insn = &insns[index];
959        let frame = frames.get(&offset).cloned().ok_or_else(|| {
960            ClassWriteError::FrameComputation(format!("missing frame at {offset}"))
961        })?;
962        max_stack = max_stack.max(stack_slots(&frame.stack));
963        max_locals = max_locals.max(frame.locals.len());
964
965        let out_frame = execute_instruction(insn, &frame, class_node, cp)?;
966        max_stack = max_stack.max(stack_slots(&out_frame.stack));
967        max_locals = max_locals.max(out_frame.locals.len());
968
969        for succ in instruction_successors(insn) {
970            if let Some(next_frame) = merge_frame(&out_frame, frames.get(&succ)) {
971                let changed = match frames.get(&succ) {
972                    Some(existing) => existing != &next_frame,
973                    None => true,
974                };
975                if changed {
976                    frames.insert(succ, next_frame);
977                    if in_worklist.insert(succ) {
978                        worklist.push_back(succ);
979                    }
980                }
981            }
982        }
983
984        for handler in handlers.iter().filter(|item| item.covers(offset)) {
985            let mut handler_frame = FrameState {
986                locals: frame.locals.clone(),
987                stack: Vec::new(),
988            };
989            handler_frame.stack.push(handler.exception_type.clone());
990            max_stack = max_stack.max(stack_slots(&handler_frame.stack));
991            max_locals = max_locals.max(handler_frame.locals.len());
992            if let Some(next_frame) = merge_frame(&handler_frame, frames.get(&handler.handler_pc)) {
993                let changed = match frames.get(&handler.handler_pc) {
994                    Some(existing) => existing != &next_frame,
995                    None => true,
996                };
997                if changed {
998                    frames.insert(handler.handler_pc, next_frame);
999                    if in_worklist.insert(handler.handler_pc) {
1000                        worklist.push_back(handler.handler_pc);
1001                    }
1002                }
1003            }
1004        }
1005    }
1006
1007    Ok((max_stack as u16, max_locals as u16))
1008}
1009
1010fn stack_slots(stack: &[FrameType]) -> usize {
1011    let mut slots = 0usize;
1012    for value in stack {
1013        slots += if is_category2(value) { 2 } else { 1 };
1014    }
1015    slots
1016}
1017
1018fn compact_locals(locals: &[FrameType]) -> Vec<FrameType> {
1019    let mut out = Vec::new();
1020    let mut i = 0usize;
1021    while i < locals.len() {
1022        match locals[i] {
1023            FrameType::Top => {
1024                if i > 0 && matches!(locals[i - 1], FrameType::Long | FrameType::Double) {
1025                    i += 1;
1026                    continue;
1027                }
1028                out.push(FrameType::Top);
1029            }
1030            FrameType::Long | FrameType::Double => {
1031                out.push(locals[i].clone());
1032                if i + 1 < locals.len() && matches!(locals[i + 1], FrameType::Top) {
1033                    i += 1;
1034                }
1035            }
1036            _ => out.push(locals[i].clone()),
1037        }
1038        i += 1;
1039    }
1040
1041    while matches!(out.last(), Some(FrameType::Top)) {
1042        out.pop();
1043    }
1044    out
1045}
1046
1047fn to_verification_type(value: &FrameType, cp: &mut Vec<CpInfo>) -> VerificationTypeInfo {
1048    match value {
1049        FrameType::Top => VerificationTypeInfo::Top,
1050        FrameType::Integer => VerificationTypeInfo::Integer,
1051        FrameType::Float => VerificationTypeInfo::Float,
1052        FrameType::Long => VerificationTypeInfo::Long,
1053        FrameType::Double => VerificationTypeInfo::Double,
1054        FrameType::Null => VerificationTypeInfo::Null,
1055        FrameType::UninitializedThis => VerificationTypeInfo::UninitializedThis,
1056        FrameType::Uninitialized(offset) => VerificationTypeInfo::Uninitialized { offset: *offset },
1057        FrameType::Object(name) => {
1058            let index = ensure_class(cp, name);
1059            VerificationTypeInfo::Object { cpool_index: index }
1060        }
1061    }
1062}
1063
1064fn initial_frame(
1065    method: &MethodNode,
1066    class_node: &ClassNode,
1067) -> Result<FrameState, ClassWriteError> {
1068    let mut locals = Vec::new();
1069    let is_static = method.access_flags & 0x0008 != 0;
1070    if !is_static {
1071        if method.name == "<init>" {
1072            locals.push(FrameType::UninitializedThis);
1073        } else {
1074            locals.push(FrameType::Object(class_node.name.clone()));
1075        }
1076    }
1077    let (params, _) = parse_method_descriptor(&method.descriptor)?;
1078    for param in params {
1079        push_local_type(&mut locals, param);
1080    }
1081    Ok(FrameState {
1082        locals,
1083        stack: Vec::new(),
1084    })
1085}
1086
1087fn push_local_type(locals: &mut Vec<FrameType>, ty: FieldType) {
1088    match ty {
1089        FieldType::Long => {
1090            locals.push(FrameType::Long);
1091            locals.push(FrameType::Top);
1092        }
1093        FieldType::Double => {
1094            locals.push(FrameType::Double);
1095            locals.push(FrameType::Top);
1096        }
1097        FieldType::Float => locals.push(FrameType::Float),
1098        FieldType::Boolean
1099        | FieldType::Byte
1100        | FieldType::Char
1101        | FieldType::Short
1102        | FieldType::Int => locals.push(FrameType::Integer),
1103        FieldType::Object(name) => locals.push(FrameType::Object(name)),
1104        FieldType::Array(desc) => locals.push(FrameType::Object(desc)),
1105        FieldType::Void => {}
1106    }
1107}
1108
1109#[derive(Debug, Clone)]
1110struct ExceptionHandlerInfo {
1111    start_pc: u16,
1112    end_pc: u16,
1113    handler_pc: u16,
1114    exception_type: FrameType,
1115}
1116
1117impl ExceptionHandlerInfo {
1118    fn covers(&self, offset: u16) -> bool {
1119        offset >= self.start_pc && offset < self.end_pc
1120    }
1121}
1122
1123fn build_exception_handlers(
1124    code: &CodeAttribute,
1125    cp: &[CpInfo],
1126) -> Result<Vec<ExceptionHandlerInfo>, ClassWriteError> {
1127    let mut handlers = Vec::new();
1128    for entry in &code.exception_table {
1129        let exception_type = if entry.catch_type == 0 {
1130            FrameType::Object("java/lang/Throwable".to_string())
1131        } else {
1132            let class_name = cp_class_name(cp, entry.catch_type)?;
1133            FrameType::Object(class_name.to_string())
1134        };
1135        handlers.push(ExceptionHandlerInfo {
1136            start_pc: entry.start_pc,
1137            end_pc: entry.end_pc,
1138            handler_pc: entry.handler_pc,
1139            exception_type,
1140        });
1141    }
1142    Ok(handlers)
1143}
1144
1145fn handler_common_types(
1146    handlers: &[ExceptionHandlerInfo],
1147) -> std::collections::HashMap<u16, FrameType> {
1148    let mut map: std::collections::HashMap<u16, FrameType> = std::collections::HashMap::new();
1149    for handler in handlers {
1150        map.entry(handler.handler_pc)
1151            .and_modify(|existing| {
1152                *existing = merge_exception_type(existing, &handler.exception_type);
1153            })
1154            .or_insert_with(|| handler.exception_type.clone());
1155    }
1156    map
1157}
1158
1159fn merge_exception_type(left: &FrameType, right: &FrameType) -> FrameType {
1160    match (left, right) {
1161        (FrameType::Object(l), FrameType::Object(r)) => FrameType::Object(common_superclass(l, r)),
1162        _ if left == right => left.clone(),
1163        _ => FrameType::Object("java/lang/Object".to_string()),
1164    }
1165}
1166
1167fn dump_frame_debug(
1168    method: &MethodNode,
1169    label: &str,
1170    iterations: usize,
1171    hits: &std::collections::HashMap<u16, u32>,
1172) {
1173    let mut entries: Vec<(u16, u32)> = hits.iter().map(|(k, v)| (*k, *v)).collect();
1174    entries.sort_by(|a, b| b.1.cmp(&a.1));
1175    let top = entries.into_iter().take(10).collect::<Vec<_>>();
1176    eprintln!(
1177        "[frame-debug] method={}{} label={} iterations={} top_offsets={:?}",
1178        method.name, method.descriptor, label, iterations, top
1179    );
1180}
1181
1182#[derive(Debug, Clone)]
1183struct ParsedInstruction {
1184    offset: u16,
1185    opcode: u8,
1186    operand: Operand,
1187}
1188
1189#[derive(Debug, Clone)]
1190enum Operand {
1191    None,
1192    I1(i8),
1193    I2(i16),
1194    I4(i32),
1195    U1(u8),
1196    U2(u16),
1197    U4(u32),
1198    Jump(i16),
1199    JumpWide(i32),
1200    TableSwitch {
1201        default_offset: i32,
1202        low: i32,
1203        high: i32,
1204        offsets: Vec<i32>,
1205    },
1206    LookupSwitch {
1207        default_offset: i32,
1208        pairs: Vec<(i32, i32)>,
1209    },
1210    Iinc {
1211        index: u16,
1212        increment: i16,
1213    },
1214    InvokeInterface {
1215        index: u16,
1216        count: u8,
1217    },
1218    InvokeDynamic {
1219        index: u16,
1220    },
1221    MultiANewArray {
1222        index: u16,
1223        dims: u8,
1224    },
1225    Wide {
1226        opcode: u8,
1227        index: u16,
1228        increment: Option<i16>,
1229    },
1230}
1231
1232fn parse_instructions(code: &[u8]) -> Result<Vec<ParsedInstruction>, ClassWriteError> {
1233    let mut insns = Vec::new();
1234    let mut pos = 0usize;
1235    while pos < code.len() {
1236        let offset = pos as u16;
1237        let opcode = code[pos];
1238        pos += 1;
1239        let operand = match opcode {
1240            0x10 => {
1241                let value = read_i1(code, &mut pos)?;
1242                Operand::I1(value)
1243            }
1244            0x11 => Operand::I2(read_i2(code, &mut pos)?),
1245            0x12 => Operand::U1(read_u1(code, &mut pos)?),
1246            0x13 | 0x14 => Operand::U2(read_u2(code, &mut pos)?),
1247            0x15..=0x19 | 0x36..=0x3A | 0xA9 => Operand::U1(read_u1(code, &mut pos)?),
1248            0x84 => {
1249                let index = read_u1(code, &mut pos)? as u16;
1250                let inc = read_i1(code, &mut pos)? as i16;
1251                Operand::Iinc {
1252                    index,
1253                    increment: inc,
1254                }
1255            }
1256            0x99..=0xA8 | 0xC6 | 0xC7 => Operand::Jump(read_i2(code, &mut pos)?),
1257            0xC8 | 0xC9 => Operand::JumpWide(read_i4(code, &mut pos)?),
1258            0xAA => {
1259                let padding = (4 - (pos % 4)) % 4;
1260                pos += padding;
1261                let default_offset = read_i4(code, &mut pos)?;
1262                let low = read_i4(code, &mut pos)?;
1263                let high = read_i4(code, &mut pos)?;
1264                let count = if high < low {
1265                    0
1266                } else {
1267                    (high - low + 1) as usize
1268                };
1269                let mut offsets = Vec::with_capacity(count);
1270                for _ in 0..count {
1271                    offsets.push(read_i4(code, &mut pos)?);
1272                }
1273                Operand::TableSwitch {
1274                    default_offset,
1275                    low,
1276                    high,
1277                    offsets,
1278                }
1279            }
1280            0xAB => {
1281                let padding = (4 - (pos % 4)) % 4;
1282                pos += padding;
1283                let default_offset = read_i4(code, &mut pos)?;
1284                let npairs = read_i4(code, &mut pos)? as usize;
1285                let mut pairs = Vec::with_capacity(npairs);
1286                for _ in 0..npairs {
1287                    let key = read_i4(code, &mut pos)?;
1288                    let value = read_i4(code, &mut pos)?;
1289                    pairs.push((key, value));
1290                }
1291                Operand::LookupSwitch {
1292                    default_offset,
1293                    pairs,
1294                }
1295            }
1296            0xB2..=0xB8 | 0xBB | 0xBD | 0xC0 | 0xC1 => Operand::U2(read_u2(code, &mut pos)?),
1297            0xB9 => {
1298                let index = read_u2(code, &mut pos)?;
1299                let count = read_u1(code, &mut pos)?;
1300                let _ = read_u1(code, &mut pos)?;
1301                Operand::InvokeInterface { index, count }
1302            }
1303            0xBA => {
1304                let index = read_u2(code, &mut pos)?;
1305                let _ = read_u2(code, &mut pos)?;
1306                Operand::InvokeDynamic { index }
1307            }
1308            0xBC => Operand::U1(read_u1(code, &mut pos)?),
1309            0xC4 => {
1310                let wide_opcode = read_u1(code, &mut pos)?;
1311                match wide_opcode {
1312                    0x15..=0x19 | 0x36..=0x3A | 0xA9 => {
1313                        let index = read_u2(code, &mut pos)?;
1314                        Operand::Wide {
1315                            opcode: wide_opcode,
1316                            index,
1317                            increment: None,
1318                        }
1319                    }
1320                    0x84 => {
1321                        let index = read_u2(code, &mut pos)?;
1322                        let increment = read_i2(code, &mut pos)?;
1323                        Operand::Wide {
1324                            opcode: wide_opcode,
1325                            index,
1326                            increment: Some(increment),
1327                        }
1328                    }
1329                    _ => {
1330                        return Err(ClassWriteError::InvalidOpcode {
1331                            opcode: wide_opcode,
1332                            offset: pos - 1,
1333                        });
1334                    }
1335                }
1336            }
1337            0xC5 => {
1338                let index = read_u2(code, &mut pos)?;
1339                let dims = read_u1(code, &mut pos)?;
1340                Operand::MultiANewArray { index, dims }
1341            }
1342            _ => Operand::None,
1343        };
1344        insns.push(ParsedInstruction {
1345            offset,
1346            opcode,
1347            operand,
1348        });
1349    }
1350    Ok(insns)
1351}
1352
1353fn instruction_successors(insn: &ParsedInstruction) -> Vec<u16> {
1354    let mut successors = Vec::new();
1355    let next_offset = insn.offset.saturating_add(instruction_length(insn) as u16);
1356    match insn.opcode {
1357        0xA7 | 0xC8 => {
1358            if let Some(target) = jump_target(insn) {
1359                successors.push(target);
1360            }
1361        }
1362        0xA8 | 0xC9 => {
1363            if let Some(target) = jump_target(insn) {
1364                successors.push(target);
1365            }
1366            successors.push(next_offset);
1367        }
1368        0x99..=0xA6 | 0xC6 | 0xC7 => {
1369            if let Some(target) = jump_target(insn) {
1370                successors.push(target);
1371            }
1372            successors.push(next_offset);
1373        }
1374        0xAA => {
1375            if let Operand::TableSwitch {
1376                default_offset,
1377                offsets,
1378                ..
1379            } = &insn.operand
1380            {
1381                successors.push((insn.offset as i32 + default_offset) as u16);
1382                for offset in offsets {
1383                    successors.push((insn.offset as i32 + *offset) as u16);
1384                }
1385            }
1386        }
1387        0xAB => {
1388            if let Operand::LookupSwitch {
1389                default_offset,
1390                pairs,
1391            } = &insn.operand
1392            {
1393                successors.push((insn.offset as i32 + default_offset) as u16);
1394                for (_, offset) in pairs {
1395                    successors.push((insn.offset as i32 + *offset) as u16);
1396                }
1397            }
1398        }
1399        0xAC..=0xB1 | 0xBF => {}
1400        0xC2 | 0xC3 => {
1401            successors.push(next_offset);
1402        }
1403        _ => {
1404            if next_offset != insn.offset {
1405                successors.push(next_offset);
1406            }
1407        }
1408    }
1409    successors
1410}
1411
1412fn jump_target(insn: &ParsedInstruction) -> Option<u16> {
1413    match insn.operand {
1414        Operand::Jump(offset) => Some((insn.offset as i32 + offset as i32) as u16),
1415        Operand::JumpWide(offset) => Some((insn.offset as i32 + offset) as u16),
1416        _ => None,
1417    }
1418}
1419
1420fn instruction_length(insn: &ParsedInstruction) -> usize {
1421    match &insn.operand {
1422        Operand::None => 1,
1423        Operand::I1(_) | Operand::U1(_) => 2,
1424        Operand::I2(_) | Operand::U2(_) | Operand::Jump(_) => 3,
1425        Operand::I4(_) | Operand::U4(_) | Operand::JumpWide(_) => 5,
1426        Operand::Iinc { .. } => 3,
1427        Operand::InvokeInterface { .. } => 5,
1428        Operand::InvokeDynamic { .. } => 5,
1429        Operand::MultiANewArray { .. } => 4,
1430        Operand::Wide {
1431            opcode, increment, ..
1432        } => {
1433            if *opcode == 0x84 && increment.is_some() {
1434                6
1435            } else {
1436                4
1437            }
1438        }
1439        Operand::TableSwitch { offsets, .. } => {
1440            1 + switch_padding(insn.offset) + 12 + offsets.len() * 4
1441        }
1442        Operand::LookupSwitch { pairs, .. } => {
1443            1 + switch_padding(insn.offset) + 8 + pairs.len() * 8
1444        }
1445    }
1446}
1447
1448fn switch_padding(offset: u16) -> usize {
1449    let pos = (offset as usize + 1) % 4;
1450    (4 - pos) % 4
1451}
1452
1453fn execute_instruction(
1454    insn: &ParsedInstruction,
1455    frame: &FrameState,
1456    class_node: &ClassNode,
1457    cp: &[CpInfo],
1458) -> Result<FrameState, ClassWriteError> {
1459    let mut locals = frame.locals.clone();
1460    let mut stack = frame.stack.clone();
1461
1462    let pop = |stack: &mut Vec<FrameType>| {
1463        stack.pop().ok_or_else(|| {
1464            ClassWriteError::FrameComputation(format!("stack underflow at {}", insn.offset))
1465        })
1466    };
1467
1468    match insn.opcode {
1469        0x00 => {}
1470        0x01 => stack.push(FrameType::Null),
1471        0x02..=0x08 => stack.push(FrameType::Integer),
1472        0x09 | 0x0A => stack.push(FrameType::Long),
1473        0x0B..=0x0D => stack.push(FrameType::Float),
1474        0x0E | 0x0F => stack.push(FrameType::Double),
1475        0x10 => stack.push(FrameType::Integer),
1476        0x11 => stack.push(FrameType::Integer),
1477        0x12..=0x14 => {
1478            let ty = ldc_type(insn, cp)?;
1479            stack.push(ty);
1480        }
1481        0x15..=0x19 => {
1482            let index = var_index(insn)?;
1483            if let Some(value) = locals.get(index as usize) {
1484                stack.push(value.clone());
1485            } else {
1486                stack.push(FrameType::Top);
1487            }
1488        }
1489        0x1A..=0x1D => stack.push(load_local(
1490            &locals,
1491            (insn.opcode - 0x1A) as u16,
1492            FrameType::Integer,
1493        )),
1494        0x1E..=0x21 => stack.push(load_local(
1495            &locals,
1496            (insn.opcode - 0x1E) as u16,
1497            FrameType::Long,
1498        )),
1499        0x22..=0x25 => stack.push(load_local(
1500            &locals,
1501            (insn.opcode - 0x22) as u16,
1502            FrameType::Float,
1503        )),
1504        0x26..=0x29 => stack.push(load_local(
1505            &locals,
1506            (insn.opcode - 0x26) as u16,
1507            FrameType::Double,
1508        )),
1509        0x2A..=0x2D => stack.push(load_local(
1510            &locals,
1511            (insn.opcode - 0x2A) as u16,
1512            FrameType::Object(class_node.name.clone()),
1513        )),
1514        0x2E..=0x35 => {
1515            pop(&mut stack)?;
1516            let array_ref = pop(&mut stack)?; //fixed: array -> java/lang/Object.
1517            let ty = match insn.opcode {
1518                0x2E => FrameType::Integer,
1519                0x2F => FrameType::Long,
1520                0x30 => FrameType::Float,
1521                0x31 => FrameType::Double,
1522                0x32 => array_element_type(&array_ref)
1523                    .unwrap_or_else(|| FrameType::Object("java/lang/Object".to_string())),
1524                0x33..=0x35 => FrameType::Integer,
1525                _ => FrameType::Top,
1526            };
1527            stack.push(ty);
1528        }
1529        0x36..=0x3A => {
1530            let index = var_index(insn)?;
1531            let value = pop(&mut stack)?;
1532            store_local(&mut locals, index, value);
1533        }
1534        0x3B..=0x3E => {
1535            let value = pop(&mut stack)?;
1536            store_local(&mut locals, (insn.opcode - 0x3B) as u16, value);
1537        }
1538        0x3F..=0x42 => {
1539            let value = pop(&mut stack)?;
1540            store_local(&mut locals, (insn.opcode - 0x3F) as u16, value);
1541        }
1542        0x43..=0x46 => {
1543            let value = pop(&mut stack)?;
1544            store_local(&mut locals, (insn.opcode - 0x43) as u16, value);
1545        }
1546        0x47..=0x4A => {
1547            let value = pop(&mut stack)?;
1548            store_local(&mut locals, (insn.opcode - 0x47) as u16, value);
1549        }
1550        0x4B..=0x4E => {
1551            let value = pop(&mut stack)?;
1552            store_local(&mut locals, (insn.opcode - 0x4B) as u16, value);
1553        }
1554        0x4F..=0x56 => {
1555            pop(&mut stack)?;
1556            pop(&mut stack)?;
1557            pop(&mut stack)?;
1558        }
1559        0x57 => {
1560            pop(&mut stack)?;
1561        }
1562        0x58 => {
1563            let v1 = pop(&mut stack)?;
1564            if is_category2(&v1) {
1565                return Err(ClassWriteError::FrameComputation(
1566                    "pop2 category2".to_string(),
1567                ));
1568            }
1569            let v2 = pop(&mut stack)?;
1570            if is_category2(&v2) {
1571                return Err(ClassWriteError::FrameComputation(
1572                    "pop2 invalid".to_string(),
1573                ));
1574            }
1575        }
1576        0x59 => {
1577            let v1 = pop(&mut stack)?;
1578            if is_category2(&v1) {
1579                return Err(ClassWriteError::FrameComputation(
1580                    "dup category2".to_string(),
1581                ));
1582            }
1583            stack.push(v1.clone());
1584            stack.push(v1);
1585        }
1586        0x5A => {
1587            let v1 = pop(&mut stack)?;
1588            let v2 = pop(&mut stack)?;
1589            if is_category2(&v1) || is_category2(&v2) {
1590                return Err(ClassWriteError::FrameComputation("dup_x1".to_string()));
1591            }
1592            stack.push(v1.clone());
1593            stack.push(v2);
1594            stack.push(v1);
1595        }
1596        0x5B => {
1597            let v1 = pop(&mut stack)?;
1598            let v2 = pop(&mut stack)?;
1599            let v3 = pop(&mut stack)?;
1600            if is_category2(&v1) || is_category2(&v2) {
1601                return Err(ClassWriteError::FrameComputation("dup_x2".to_string()));
1602            }
1603            stack.push(v1.clone());
1604            stack.push(v3);
1605            stack.push(v2);
1606            stack.push(v1);
1607        }
1608        0x5C => {
1609            let v1 = pop(&mut stack)?;
1610            if is_category2(&v1) {
1611                stack.push(v1.clone());
1612                stack.push(v1);
1613            } else {
1614                let v2 = pop(&mut stack)?;
1615                if is_category2(&v2) {
1616                    return Err(ClassWriteError::FrameComputation("dup2".to_string()));
1617                }
1618                stack.push(v2.clone());
1619                stack.push(v1.clone());
1620                stack.push(v2);
1621                stack.push(v1);
1622            }
1623        }
1624        0x5D => {
1625            let v1 = pop(&mut stack)?;
1626            if is_category2(&v1) {
1627                let v2 = pop(&mut stack)?;
1628                stack.push(v1.clone());
1629                stack.push(v2);
1630                stack.push(v1);
1631            } else {
1632                let v2 = pop(&mut stack)?;
1633                let v3 = pop(&mut stack)?;
1634                stack.push(v2.clone());
1635                stack.push(v1.clone());
1636                stack.push(v3);
1637                stack.push(v2);
1638                stack.push(v1);
1639            }
1640        }
1641        0x5E => {
1642            let v1 = pop(&mut stack)?;
1643            if is_category2(&v1) {
1644                let v2 = pop(&mut stack)?;
1645                let v3 = pop(&mut stack)?;
1646                stack.push(v1.clone());
1647                stack.push(v3);
1648                stack.push(v2);
1649                stack.push(v1);
1650            } else {
1651                let v2 = pop(&mut stack)?;
1652                let v3 = pop(&mut stack)?;
1653                let v4 = pop(&mut stack)?;
1654                stack.push(v2.clone());
1655                stack.push(v1.clone());
1656                stack.push(v4);
1657                stack.push(v3);
1658                stack.push(v2);
1659                stack.push(v1);
1660            }
1661        }
1662        0x5F => {
1663            let v1 = pop(&mut stack)?;
1664            let v2 = pop(&mut stack)?;
1665            if is_category2(&v1) || is_category2(&v2) {
1666                return Err(ClassWriteError::FrameComputation("swap".to_string()));
1667            }
1668            stack.push(v1);
1669            stack.push(v2);
1670        }
1671        0x60 | 0x64 | 0x68 | 0x6C | 0x70 | 0x78 | 0x7A | 0x7C | 0x7E | 0x80 | 0x82 => {
1672            pop(&mut stack)?;
1673            pop(&mut stack)?;
1674            stack.push(FrameType::Integer);
1675        }
1676        0x61 | 0x65 | 0x69 | 0x6D | 0x71 | 0x79 | 0x7B | 0x7D | 0x7F | 0x81 | 0x83 => {
1677            pop(&mut stack)?;
1678            pop(&mut stack)?;
1679            stack.push(FrameType::Long);
1680        }
1681        0x62 | 0x66 | 0x6A | 0x6E | 0x72 => {
1682            pop(&mut stack)?;
1683            pop(&mut stack)?;
1684            stack.push(FrameType::Float);
1685        }
1686        0x63 | 0x67 | 0x6B | 0x6F | 0x73 => {
1687            pop(&mut stack)?;
1688            pop(&mut stack)?;
1689            stack.push(FrameType::Double);
1690        }
1691        0x74 => {
1692            pop(&mut stack)?;
1693            stack.push(FrameType::Integer);
1694        }
1695        0x75 => {
1696            pop(&mut stack)?;
1697            stack.push(FrameType::Long);
1698        }
1699        0x76 => {
1700            pop(&mut stack)?;
1701            stack.push(FrameType::Float);
1702        }
1703        0x77 => {
1704            pop(&mut stack)?;
1705            stack.push(FrameType::Double);
1706        }
1707        0x84 => {}
1708        0x85 => {
1709            pop(&mut stack)?;
1710            stack.push(FrameType::Long);
1711        }
1712        0x86 => {
1713            pop(&mut stack)?;
1714            stack.push(FrameType::Float);
1715        }
1716        0x87 => {
1717            pop(&mut stack)?;
1718            stack.push(FrameType::Double);
1719        }
1720        0x88 => {
1721            pop(&mut stack)?;
1722            stack.push(FrameType::Integer);
1723        }
1724        0x89 => {
1725            pop(&mut stack)?;
1726            stack.push(FrameType::Float);
1727        }
1728        0x8A => {
1729            pop(&mut stack)?;
1730            stack.push(FrameType::Double);
1731        }
1732        0x8B => {
1733            pop(&mut stack)?;
1734            stack.push(FrameType::Integer);
1735        }
1736        0x8C => {
1737            pop(&mut stack)?;
1738            stack.push(FrameType::Long);
1739        }
1740        0x8D => {
1741            pop(&mut stack)?;
1742            stack.push(FrameType::Double);
1743        }
1744        0x8E => {
1745            pop(&mut stack)?;
1746            stack.push(FrameType::Integer);
1747        }
1748        0x8F => {
1749            pop(&mut stack)?;
1750            stack.push(FrameType::Long);
1751        }
1752        0x90 => {
1753            pop(&mut stack)?;
1754            stack.push(FrameType::Float);
1755        }
1756        0x91..=0x93 => {
1757            pop(&mut stack)?;
1758            stack.push(FrameType::Integer);
1759        }
1760        0x94..=0x98 => {
1761            pop(&mut stack)?;
1762            pop(&mut stack)?;
1763            stack.push(FrameType::Integer);
1764        }
1765        0x99..=0x9E | 0xC6 | 0xC7 => {
1766            pop(&mut stack)?;
1767        }
1768        0x9F..=0xA6 => {
1769            pop(&mut stack)?;
1770            pop(&mut stack)?;
1771        }
1772        0xA7 | 0xC8 => {}
1773        0xA8 | 0xA9 | 0xC9 => {
1774            return Err(ClassWriteError::FrameComputation(format!(
1775                "jsr/ret not supported at {}",
1776                insn.offset
1777            )));
1778        }
1779        0xAA | 0xAB => {
1780            pop(&mut stack)?;
1781        }
1782        0xAC => {
1783            pop(&mut stack)?;
1784        }
1785        0xAD => {
1786            pop(&mut stack)?;
1787        }
1788        0xAE => {
1789            pop(&mut stack)?;
1790        }
1791        0xAF => {
1792            pop(&mut stack)?;
1793        }
1794        0xB0 => {
1795            pop(&mut stack)?;
1796        }
1797        0xB1 => {}
1798        0xB2 => {
1799            let ty = field_type(insn, cp)?;
1800            stack.push(ty);
1801        }
1802        0xB3 => {
1803            pop(&mut stack)?;
1804        }
1805        0xB4 => {
1806            pop(&mut stack)?;
1807            let ty = field_type(insn, cp)?;
1808            stack.push(ty);
1809        }
1810        0xB5 => {
1811            pop(&mut stack)?;
1812            pop(&mut stack)?;
1813        }
1814        0xB6..=0xBA => {
1815            let (args, ret, owner, is_init) = method_type(insn, cp)?;
1816            for _ in 0..args.len() {
1817                pop(&mut stack)?;
1818            }
1819            if insn.opcode != 0xB8 && insn.opcode != 0xBA {
1820                let receiver = pop(&mut stack)?;
1821                if is_init {
1822                    let init_owner = if receiver == FrameType::UninitializedThis {
1823                        class_node.name.clone()
1824                    } else {
1825                        owner
1826                    };
1827                    initialize_uninitialized(&mut locals, &mut stack, receiver, init_owner);
1828                }
1829            }
1830            if let Some(ret) = ret {
1831                stack.push(ret);
1832            }
1833        }
1834        0xBB => {
1835            if let Operand::U2(_index) = insn.operand {
1836                stack.push(FrameType::Uninitialized(insn.offset));
1837            }
1838        }
1839        0xBC => {
1840            pop(&mut stack)?;
1841            if let Operand::U1(atype) = insn.operand {
1842                let desc = newarray_descriptor(atype)?;
1843                stack.push(FrameType::Object(desc));
1844            } else {
1845                stack.push(FrameType::Object("[I".to_string()));
1846            }
1847        }
1848        0xBD => {
1849            pop(&mut stack)?;
1850            if let Operand::U2(index) = insn.operand {
1851                let class_name = cp_class_name(cp, index)?;
1852                stack.push(FrameType::Object(format!("[L{class_name};")));
1853            }
1854        }
1855        0xBE => {
1856            pop(&mut stack)?;
1857            stack.push(FrameType::Integer);
1858        }
1859        0xBF => {
1860            pop(&mut stack)?;
1861        }
1862        0xC0 => {
1863            pop(&mut stack)?;
1864            if let Operand::U2(index) = insn.operand {
1865                let class_name = cp_class_name(cp, index)?;
1866                stack.push(FrameType::Object(class_name.to_string()));
1867            }
1868        }
1869        0xC1 => {
1870            pop(&mut stack)?;
1871            stack.push(FrameType::Integer);
1872        }
1873        0xC2 | 0xC3 => {
1874            pop(&mut stack)?;
1875        }
1876        0xC4 => {
1877            if let Operand::Wide {
1878                opcode,
1879                index,
1880                increment,
1881            } = insn.operand
1882            {
1883                match opcode {
1884                    0x15..=0x19 => {
1885                        if let Some(value) = locals.get(index as usize) {
1886                            stack.push(value.clone());
1887                        }
1888                    }
1889                    0x36..=0x3A => {
1890                        let value = pop(&mut stack)?;
1891                        store_local(&mut locals, index, value);
1892                    }
1893                    0x84 => {
1894                        let _ = increment;
1895                    }
1896                    0xA9 => {}
1897                    _ => {}
1898                }
1899            }
1900        }
1901        0xC5 => {
1902            if let Operand::MultiANewArray { dims, .. } = insn.operand {
1903                for _ in 0..dims {
1904                    pop(&mut stack)?;
1905                }
1906                if let Operand::MultiANewArray { index, .. } = insn.operand {
1907                    let desc = cp_class_name(cp, index)?;
1908                    stack.push(FrameType::Object(desc.to_string()));
1909                } else {
1910                    stack.push(FrameType::Object("[Ljava/lang/Object;".to_string()));
1911                }
1912            }
1913        }
1914        0xCA | 0xFE | 0xFF => {}
1915        _ => {}
1916    }
1917
1918    Ok(FrameState { locals, stack })
1919}
1920
1921fn initialize_uninitialized(
1922    locals: &mut [FrameType],
1923    stack: &mut [FrameType],
1924    receiver: FrameType,
1925    owner: String,
1926) {
1927    let init = FrameType::Object(owner);
1928    for value in locals.iter_mut().chain(stack.iter_mut()) {
1929        if *value == receiver {
1930            *value = init.clone();
1931        }
1932    }
1933}
1934
1935fn is_category2(value: &FrameType) -> bool {
1936    matches!(value, FrameType::Long | FrameType::Double)
1937}
1938
1939fn load_local(locals: &[FrameType], index: u16, fallback: FrameType) -> FrameType {
1940    locals.get(index as usize).cloned().unwrap_or(fallback)
1941}
1942
1943fn store_local(locals: &mut Vec<FrameType>, index: u16, value: FrameType) {
1944    let idx = index as usize;
1945    if locals.len() <= idx {
1946        locals.resize(idx + 1, FrameType::Top);
1947    }
1948    locals[idx] = value.clone();
1949    if is_category2(&value) {
1950        if locals.len() <= idx + 1 {
1951            locals.resize(idx + 2, FrameType::Top);
1952        }
1953        locals[idx + 1] = FrameType::Top;
1954    }
1955}
1956
1957fn array_element_type(value: &FrameType) -> Option<FrameType> {
1958    let FrameType::Object(desc) = value else {
1959        return None;
1960    };
1961    if !desc.starts_with('[') {
1962        return None;
1963    }
1964    let element = &desc[1..];
1965    if element.starts_with('[') {
1966        return Some(FrameType::Object(element.to_string()));
1967    }
1968    let mut chars = element.chars();
1969    match chars.next() {
1970        Some('L') => {
1971            let name = element
1972                .trim_start_matches('L')
1973                .trim_end_matches(';')
1974                .to_string();
1975            Some(FrameType::Object(name))
1976        }
1977        Some('Z') | Some('B') | Some('C') | Some('S') | Some('I') => Some(FrameType::Integer),
1978        Some('F') => Some(FrameType::Float),
1979        Some('J') => Some(FrameType::Long),
1980        Some('D') => Some(FrameType::Double),
1981        _ => None,
1982    }
1983}
1984
1985fn var_index(insn: &ParsedInstruction) -> Result<u16, ClassWriteError> {
1986    match insn.operand {
1987        Operand::U1(value) => Ok(value as u16),
1988        Operand::Wide { index, .. } => Ok(index),
1989        _ => Err(ClassWriteError::FrameComputation(format!(
1990            "missing var index at {}",
1991            insn.offset
1992        ))),
1993    }
1994}
1995
1996fn ldc_type(insn: &ParsedInstruction, cp: &[CpInfo]) -> Result<FrameType, ClassWriteError> {
1997    let index = match insn.operand {
1998        Operand::U1(value) => value as u16,
1999        Operand::U2(value) => value,
2000        _ => {
2001            return Err(ClassWriteError::FrameComputation(format!(
2002                "invalid ldc at {}",
2003                insn.offset
2004            )));
2005        }
2006    };
2007    match cp.get(index as usize) {
2008        Some(CpInfo::Integer(_)) => Ok(FrameType::Integer),
2009        Some(CpInfo::Float(_)) => Ok(FrameType::Float),
2010        Some(CpInfo::Long(_)) => Ok(FrameType::Long),
2011        Some(CpInfo::Double(_)) => Ok(FrameType::Double),
2012        Some(CpInfo::String { .. }) => Ok(FrameType::Object("java/lang/String".to_string())),
2013        Some(CpInfo::Class { .. }) => Ok(FrameType::Object("java/lang/Class".to_string())),
2014        Some(CpInfo::MethodType { .. }) => {
2015            Ok(FrameType::Object("java/lang/invoke/MethodType".to_string()))
2016        }
2017        Some(CpInfo::MethodHandle { .. }) => Ok(FrameType::Object(
2018            "java/lang/invoke/MethodHandle".to_string(),
2019        )),
2020        _ => Ok(FrameType::Top),
2021    }
2022}
2023
2024fn field_type(insn: &ParsedInstruction, cp: &[CpInfo]) -> Result<FrameType, ClassWriteError> {
2025    let index = match insn.operand {
2026        Operand::U2(value) => value,
2027        _ => {
2028            return Err(ClassWriteError::FrameComputation(format!(
2029                "invalid field operand at {}",
2030                insn.offset
2031            )));
2032        }
2033    };
2034    let descriptor = cp_field_descriptor(cp, index)?;
2035    let field_type = parse_field_descriptor(descriptor)?;
2036    Ok(field_type_to_frame(field_type))
2037}
2038
2039fn method_type(
2040    insn: &ParsedInstruction,
2041    cp: &[CpInfo],
2042) -> Result<(Vec<FieldType>, Option<FrameType>, String, bool), ClassWriteError> {
2043    let index = match insn.operand {
2044        Operand::U2(value) => value,
2045        Operand::InvokeInterface { index, .. } => index,
2046        Operand::InvokeDynamic { index } => index,
2047        _ => {
2048            return Err(ClassWriteError::FrameComputation(format!(
2049                "invalid method operand at {}",
2050                insn.offset
2051            )));
2052        }
2053    };
2054    let (owner, descriptor, name) = cp_method_descriptor(cp, index, insn.opcode)?;
2055    let (args, ret) = parse_method_descriptor(descriptor)?;
2056    let ret_frame = match ret {
2057        FieldType::Void => None,
2058        other => Some(field_type_to_frame(other)),
2059    };
2060    Ok((args, ret_frame, owner.to_string(), name == "<init>"))
2061}
2062
2063fn field_type_to_frame(field_type: FieldType) -> FrameType {
2064    match field_type {
2065        FieldType::Boolean
2066        | FieldType::Byte
2067        | FieldType::Char
2068        | FieldType::Short
2069        | FieldType::Int => FrameType::Integer,
2070        FieldType::Float => FrameType::Float,
2071        FieldType::Long => FrameType::Long,
2072        FieldType::Double => FrameType::Double,
2073        FieldType::Object(name) => FrameType::Object(name),
2074        FieldType::Array(desc) => FrameType::Object(desc),
2075        FieldType::Void => FrameType::Top,
2076    }
2077}
2078
2079fn cp_class_name(cp: &[CpInfo], index: u16) -> Result<&str, ClassWriteError> {
2080    match cp.get(index as usize) {
2081        Some(CpInfo::Class { name_index }) => match cp.get(*name_index as usize) {
2082            Some(CpInfo::Utf8(name)) => Ok(name),
2083            _ => Err(ClassWriteError::InvalidConstantPool),
2084        },
2085        _ => Err(ClassWriteError::InvalidConstantPool),
2086    }
2087}
2088
2089fn newarray_descriptor(atype: u8) -> Result<String, ClassWriteError> {
2090    let desc = match atype {
2091        4 => "[Z",
2092        5 => "[C",
2093        6 => "[F",
2094        7 => "[D",
2095        8 => "[B",
2096        9 => "[S",
2097        10 => "[I",
2098        11 => "[J",
2099        _ => {
2100            return Err(ClassWriteError::FrameComputation(
2101                "invalid newarray type".to_string(),
2102            ));
2103        }
2104    };
2105    Ok(desc.to_string())
2106}
2107
2108fn cp_field_descriptor(cp: &[CpInfo], index: u16) -> Result<&str, ClassWriteError> {
2109    match cp.get(index as usize) {
2110        Some(CpInfo::Fieldref {
2111            name_and_type_index,
2112            ..
2113        }) => match cp.get(*name_and_type_index as usize) {
2114            Some(CpInfo::NameAndType {
2115                descriptor_index, ..
2116            }) => match cp.get(*descriptor_index as usize) {
2117                Some(CpInfo::Utf8(desc)) => Ok(desc),
2118                _ => Err(ClassWriteError::InvalidConstantPool),
2119            },
2120            _ => Err(ClassWriteError::InvalidConstantPool),
2121        },
2122        _ => Err(ClassWriteError::InvalidConstantPool),
2123    }
2124}
2125
2126fn cp_method_descriptor(
2127    cp: &[CpInfo],
2128    index: u16,
2129    opcode: u8,
2130) -> Result<(&str, &str, &str), ClassWriteError> {
2131    match cp.get(index as usize) {
2132        Some(CpInfo::Methodref {
2133            class_index,
2134            name_and_type_index,
2135        })
2136        | Some(CpInfo::InterfaceMethodref {
2137            class_index,
2138            name_and_type_index,
2139        }) => {
2140            let owner = cp_class_name(cp, *class_index)?;
2141            match cp.get(*name_and_type_index as usize) {
2142                Some(CpInfo::NameAndType {
2143                    name_index,
2144                    descriptor_index,
2145                }) => {
2146                    let name = cp_utf8(cp, *name_index)?;
2147                    let desc = cp_utf8(cp, *descriptor_index)?;
2148                    Ok((owner, desc, name))
2149                }
2150                _ => Err(ClassWriteError::InvalidConstantPool),
2151            }
2152        }
2153        Some(CpInfo::InvokeDynamic {
2154            name_and_type_index,
2155            ..
2156        }) if opcode == 0xBA => match cp.get(*name_and_type_index as usize) {
2157            Some(CpInfo::NameAndType {
2158                name_index,
2159                descriptor_index,
2160            }) => {
2161                let name = cp_utf8(cp, *name_index)?;
2162                let desc = cp_utf8(cp, *descriptor_index)?;
2163                Ok(("java/lang/Object", desc, name))
2164            }
2165            _ => Err(ClassWriteError::InvalidConstantPool),
2166        },
2167        _ => Err(ClassWriteError::InvalidConstantPool),
2168    }
2169}
2170
2171fn cp_utf8(cp: &[CpInfo], index: u16) -> Result<&str, ClassWriteError> {
2172    match cp.get(index as usize) {
2173        Some(CpInfo::Utf8(value)) => Ok(value.as_str()),
2174        _ => Err(ClassWriteError::InvalidConstantPool),
2175    }
2176}
2177
2178#[derive(Debug, Clone)]
2179enum FieldType {
2180    Boolean,
2181    Byte,
2182    Char,
2183    Short,
2184    Int,
2185    Float,
2186    Long,
2187    Double,
2188    Object(String),
2189    Array(String),
2190    Void,
2191}
2192
2193fn parse_field_descriptor(desc: &str) -> Result<FieldType, ClassWriteError> {
2194    let mut chars = desc.chars().peekable();
2195    parse_field_type(&mut chars)
2196}
2197
2198fn parse_method_descriptor(desc: &str) -> Result<(Vec<FieldType>, FieldType), ClassWriteError> {
2199    let mut chars = desc.chars().peekable();
2200    if chars.next() != Some('(') {
2201        return Err(ClassWriteError::FrameComputation(
2202            "bad method descriptor".to_string(),
2203        ));
2204    }
2205    let mut params = Vec::new();
2206    while let Some(&ch) = chars.peek() {
2207        if ch == ')' {
2208            chars.next();
2209            break;
2210        }
2211        params.push(parse_field_type(&mut chars)?);
2212    }
2213    let ret = parse_return_type(&mut chars)?;
2214    Ok((params, ret))
2215}
2216
2217fn parse_field_type<I>(chars: &mut std::iter::Peekable<I>) -> Result<FieldType, ClassWriteError>
2218where
2219    I: Iterator<Item = char>,
2220{
2221    match chars.next() {
2222        Some('Z') => Ok(FieldType::Boolean),
2223        Some('B') => Ok(FieldType::Byte),
2224        Some('C') => Ok(FieldType::Char),
2225        Some('S') => Ok(FieldType::Short),
2226        Some('I') => Ok(FieldType::Int),
2227        Some('F') => Ok(FieldType::Float),
2228        Some('J') => Ok(FieldType::Long),
2229        Some('D') => Ok(FieldType::Double),
2230        Some('L') => {
2231            let mut name = String::new();
2232            for ch in chars.by_ref() {
2233                if ch == ';' {
2234                    break;
2235                }
2236                name.push(ch);
2237            }
2238            Ok(FieldType::Object(name))
2239        }
2240        Some('[') => {
2241            let mut desc = String::from("[");
2242            let inner = parse_field_type(chars)?;
2243            match inner {
2244                FieldType::Object(name) => {
2245                    desc.push('L');
2246                    desc.push_str(&name);
2247                    desc.push(';');
2248                }
2249                FieldType::Boolean => desc.push('Z'),
2250                FieldType::Byte => desc.push('B'),
2251                FieldType::Char => desc.push('C'),
2252                FieldType::Short => desc.push('S'),
2253                FieldType::Int => desc.push('I'),
2254                FieldType::Float => desc.push('F'),
2255                FieldType::Long => desc.push('J'),
2256                FieldType::Double => desc.push('D'),
2257                FieldType::Void => {}
2258                FieldType::Array(inner_desc) => desc.push_str(&inner_desc),
2259            }
2260            Ok(FieldType::Array(desc))
2261        }
2262        _ => Err(ClassWriteError::FrameComputation(
2263            "bad field descriptor".to_string(),
2264        )),
2265    }
2266}
2267
2268fn parse_return_type<I>(chars: &mut std::iter::Peekable<I>) -> Result<FieldType, ClassWriteError>
2269where
2270    I: Iterator<Item = char>,
2271{
2272    match chars.peek() {
2273        Some('V') => {
2274            chars.next();
2275            Ok(FieldType::Void)
2276        }
2277        _ => parse_field_type(chars),
2278    }
2279}
2280
2281fn read_u1(code: &[u8], pos: &mut usize) -> Result<u8, ClassWriteError> {
2282    if *pos >= code.len() {
2283        return Err(ClassWriteError::FrameComputation(
2284            "unexpected eof".to_string(),
2285        ));
2286    }
2287    let value = code[*pos];
2288    *pos += 1;
2289    Ok(value)
2290}
2291
2292fn read_i1(code: &[u8], pos: &mut usize) -> Result<i8, ClassWriteError> {
2293    Ok(read_u1(code, pos)? as i8)
2294}
2295
2296fn read_u2(code: &[u8], pos: &mut usize) -> Result<u16, ClassWriteError> {
2297    if *pos + 2 > code.len() {
2298        return Err(ClassWriteError::FrameComputation(
2299            "unexpected eof".to_string(),
2300        ));
2301    }
2302    let value = u16::from_be_bytes([code[*pos], code[*pos + 1]]);
2303    *pos += 2;
2304    Ok(value)
2305}
2306
2307fn read_i2(code: &[u8], pos: &mut usize) -> Result<i16, ClassWriteError> {
2308    Ok(read_u2(code, pos)? as i16)
2309}
2310
2311fn read_i4(code: &[u8], pos: &mut usize) -> Result<i32, ClassWriteError> {
2312    if *pos + 4 > code.len() {
2313        return Err(ClassWriteError::FrameComputation(
2314            "unexpected eof".to_string(),
2315        ));
2316    }
2317    let value = i32::from_be_bytes([code[*pos], code[*pos + 1], code[*pos + 2], code[*pos + 3]]);
2318    *pos += 4;
2319    Ok(value)
2320}