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