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