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