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