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