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