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