1use crate::class_reader::{
2 AttributeInfo, BootstrapMethod, CodeAttribute, CpInfo, ExceptionTableEntry, InnerClass,
3 LineNumber, LocalVariable, MethodParameter, StackMapFrame, VerificationTypeInfo,
4};
5use crate::nodes::{ClassNode, FieldNode, MethodNode};
6
7#[derive(Debug)]
8pub enum ClassWriteError {
9 MissingConstantPool,
10 InvalidConstantPool,
11 InvalidOpcode { opcode: u8, offset: usize },
12 FrameComputation(String),
13}
14
15impl std::fmt::Display for ClassWriteError {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 match self {
18 ClassWriteError::MissingConstantPool => write!(f, "missing constant pool"),
19 ClassWriteError::InvalidConstantPool => write!(f, "invalid constant pool"),
20 ClassWriteError::InvalidOpcode { opcode, offset } => {
21 write!(f, "invalid opcode 0x{opcode:02X} at offset {offset}")
22 }
23 ClassWriteError::FrameComputation(message) => {
24 write!(f, "frame computation error: {message}")
25 }
26 }
27 }
28}
29
30impl std::error::Error for ClassWriteError {}
31
32pub const COMPUTE_FRAMES: u32 = 0x1;
33pub const COMPUTE_MAXS: u32 = 0x2;
34
35pub struct ClassWriter {
36 options: u32,
37}
38
39impl ClassWriter {
40 pub fn new(options: u32) -> Self {
41 Self { options }
42 }
43
44 pub fn from_class_node(_class_node: &ClassNode) -> Self {
45 Self::new(0)
46 }
47
48 pub fn to_bytes(&self, class_node: &ClassNode) -> Result<Vec<u8>, ClassWriteError> {
49 if class_node.constant_pool.is_empty() {
50 return Err(ClassWriteError::MissingConstantPool);
51 }
52
53 let mut cp = class_node.constant_pool.clone();
54 let mut out = Vec::new();
55 write_u4(&mut out, 0xCAFEBABE);
56 write_u2(&mut out, class_node.minor_version);
57 write_u2(&mut out, class_node.major_version);
58
59 let mut class_attributes = class_node.attributes.clone();
60 if let Some(source_file) = &class_node.source_file {
61 class_attributes.retain(|attr| !matches!(attr, AttributeInfo::SourceFile { .. }));
62 let source_index = ensure_utf8(&mut cp, source_file);
63 class_attributes.push(AttributeInfo::SourceFile {
64 sourcefile_index: source_index,
65 });
66 }
67
68 let mut attribute_names = Vec::new();
69 collect_attribute_names(&class_attributes, &mut attribute_names);
70 for field in &class_node.fields {
71 collect_attribute_names(&field.attributes, &mut attribute_names);
72 }
73 for method in &class_node.methods {
74 collect_attribute_names(&method.attributes, &mut attribute_names);
75 if let Some(code) = &method.code {
76 attribute_names.push("Code".to_string());
77 collect_attribute_names(&code.attributes, &mut attribute_names);
78 }
79 }
80 for name in attribute_names {
81 ensure_utf8(&mut cp, &name);
82 }
83
84 let mut precomputed_stack_maps: Vec<Option<Vec<StackMapFrame>>> =
85 Vec::with_capacity(class_node.methods.len());
86 let mut precomputed_maxs: Vec<Option<(u16, u16)>> =
87 Vec::with_capacity(class_node.methods.len());
88 let compute_frames = self.options & COMPUTE_FRAMES != 0;
89 let compute_maxs_flag = self.options & COMPUTE_MAXS != 0;
90 if compute_frames {
91 ensure_utf8(&mut cp, "StackMapTable");
92 for method in &class_node.methods {
93 if let Some(code) = &method.code {
94 let maxs = if compute_maxs_flag {
95 Some(compute_maxs(method, class_node, code, &cp)?)
96 } else {
97 None
98 };
99 let max_locals = maxs.map(|item| item.1).unwrap_or(code.max_locals);
100 let stack_map =
101 compute_stack_map_table(method, class_node, code, &mut cp, max_locals)?;
102 precomputed_stack_maps.push(Some(stack_map));
103 precomputed_maxs.push(maxs);
104 } else {
105 precomputed_stack_maps.push(None);
106 precomputed_maxs.push(None);
107 }
108 }
109 } else if compute_maxs_flag {
110 for method in &class_node.methods {
111 if let Some(code) = &method.code {
112 precomputed_maxs.push(Some(compute_maxs(method, class_node, code, &cp)?));
113 } else {
114 precomputed_maxs.push(None);
115 }
116 }
117 precomputed_stack_maps.resize(class_node.methods.len(), None);
118 } else {
119 precomputed_stack_maps.resize(class_node.methods.len(), None);
120 precomputed_maxs.resize(class_node.methods.len(), None);
121 }
122
123 write_constant_pool(&mut out, &cp)?;
124 write_u2(&mut out, class_node.access_flags);
125 write_u2(&mut out, class_node.this_class);
126 write_u2(&mut out, class_node.super_class);
127 write_u2(&mut out, class_node.interface_indices.len() as u16);
128 for index in &class_node.interface_indices {
129 write_u2(&mut out, *index);
130 }
131
132 write_u2(&mut out, class_node.fields.len() as u16);
133 for field in &class_node.fields {
134 write_field(&mut out, field, &mut cp)?;
135 }
136
137 write_u2(&mut out, class_node.methods.len() as u16);
138 for (index, method) in class_node.methods.iter().enumerate() {
139 let stack_map = precomputed_stack_maps
140 .get(index)
141 .and_then(|item| item.as_ref());
142 let maxs = precomputed_maxs.get(index).and_then(|item| *item);
143 write_method(
144 &mut out,
145 method,
146 class_node,
147 &mut cp,
148 self.options,
149 stack_map,
150 maxs,
151 )?;
152 }
153
154 write_u2(&mut out, class_attributes.len() as u16);
155 for attr in &class_attributes {
156 write_attribute(&mut out, attr, &mut cp, None, self.options, None, None)?;
157 }
158
159 Ok(out)
160 }
161}
162
163fn write_field(
164 out: &mut Vec<u8>,
165 field: &FieldNode,
166 cp: &mut Vec<CpInfo>,
167) -> Result<(), ClassWriteError> {
168 write_u2(out, field.access_flags);
169 write_u2(out, field.name_index);
170 write_u2(out, field.descriptor_index);
171 write_u2(out, field.attributes.len() as u16);
172 for attr in &field.attributes {
173 write_attribute(out, attr, cp, None, 0, None, None)?;
174 }
175 Ok(())
176}
177
178fn write_method(
179 out: &mut Vec<u8>,
180 method: &MethodNode,
181 class_node: &ClassNode,
182 cp: &mut Vec<CpInfo>,
183 options: u32,
184 precomputed_stack_map: Option<&Vec<StackMapFrame>>,
185 precomputed_maxs: Option<(u16, u16)>,
186) -> Result<(), ClassWriteError> {
187 write_u2(out, method.access_flags);
188 write_u2(out, method.name_index);
189 write_u2(out, method.descriptor_index);
190
191 let mut attributes = method.attributes.clone();
192 if let Some(code) = &method.code {
193 attributes.retain(|attr| !matches!(attr, AttributeInfo::Code(_)));
194 attributes.push(AttributeInfo::Code(code.clone()));
195 }
196
197 write_u2(out, attributes.len() as u16);
198 for attr in &attributes {
199 write_attribute(
200 out,
201 attr,
202 cp,
203 Some((method, class_node)),
204 options,
205 precomputed_stack_map,
206 precomputed_maxs,
207 )?;
208 }
209 Ok(())
210}
211
212fn write_attribute(
213 out: &mut Vec<u8>,
214 attr: &AttributeInfo,
215 cp: &mut Vec<CpInfo>,
216 method_ctx: Option<(&MethodNode, &ClassNode)>,
217 options: u32,
218 precomputed_stack_map: Option<&Vec<StackMapFrame>>,
219 precomputed_maxs: Option<(u16, u16)>,
220) -> Result<(), ClassWriteError> {
221 match attr {
222 AttributeInfo::Code(code) => {
223 let name_index = ensure_utf8(cp, "Code");
224 let mut info = Vec::new();
225 let mut code_attributes = code.attributes.clone();
226 let (max_stack, max_locals) =
227 precomputed_maxs.unwrap_or((code.max_stack, code.max_locals));
228 if options & COMPUTE_FRAMES != 0 {
229 code_attributes.retain(|item| !matches!(item, AttributeInfo::StackMapTable { .. }));
230 let stack_map = if let Some(precomputed) = precomputed_stack_map {
231 precomputed.clone()
232 } else {
233 let (method, class_node) = method_ctx.ok_or_else(|| {
234 ClassWriteError::FrameComputation("missing method".to_string())
235 })?;
236 compute_stack_map_table(method, class_node, code, cp, max_locals)?
237 };
238 code_attributes.push(AttributeInfo::StackMapTable { entries: stack_map });
239 }
240
241 write_u2(&mut info, max_stack);
242 write_u2(&mut info, max_locals);
243 write_u4(&mut info, code.code.len() as u32);
244 info.extend_from_slice(&code.code);
245 write_u2(&mut info, code.exception_table.len() as u16);
246 for entry in &code.exception_table {
247 write_exception_table_entry(&mut info, entry);
248 }
249 write_u2(&mut info, code_attributes.len() as u16);
250 for nested in &code_attributes {
251 write_attribute(&mut info, nested, cp, method_ctx, options, None, None)?;
252 }
253 write_attribute_with_info(out, name_index, &info);
254 }
255 AttributeInfo::ConstantValue {
256 constantvalue_index,
257 } => {
258 let name_index = ensure_utf8(cp, "ConstantValue");
259 let mut info = Vec::new();
260 write_u2(&mut info, *constantvalue_index);
261 write_attribute_with_info(out, name_index, &info);
262 }
263 AttributeInfo::Exceptions {
264 exception_index_table,
265 } => {
266 let name_index = ensure_utf8(cp, "Exceptions");
267 let mut info = Vec::new();
268 write_u2(&mut info, exception_index_table.len() as u16);
269 for index in exception_index_table {
270 write_u2(&mut info, *index);
271 }
272 write_attribute_with_info(out, name_index, &info);
273 }
274 AttributeInfo::SourceFile { sourcefile_index } => {
275 let name_index = ensure_utf8(cp, "SourceFile");
276 let mut info = Vec::new();
277 write_u2(&mut info, *sourcefile_index);
278 write_attribute_with_info(out, name_index, &info);
279 }
280 AttributeInfo::LineNumberTable { entries } => {
281 let name_index = ensure_utf8(cp, "LineNumberTable");
282 let mut info = Vec::new();
283 write_u2(&mut info, entries.len() as u16);
284 for entry in entries {
285 write_line_number(&mut info, entry);
286 }
287 write_attribute_with_info(out, name_index, &info);
288 }
289 AttributeInfo::LocalVariableTable { entries } => {
290 let name_index = ensure_utf8(cp, "LocalVariableTable");
291 let mut info = Vec::new();
292 write_u2(&mut info, entries.len() as u16);
293 for entry in entries {
294 write_local_variable(&mut info, entry);
295 }
296 write_attribute_with_info(out, name_index, &info);
297 }
298 AttributeInfo::Signature { signature_index } => {
299 let name_index = ensure_utf8(cp, "Signature");
300 let mut info = Vec::new();
301 write_u2(&mut info, *signature_index);
302 write_attribute_with_info(out, name_index, &info);
303 }
304 AttributeInfo::StackMapTable { entries } => {
305 let name_index = ensure_utf8(cp, "StackMapTable");
306 let mut info = Vec::new();
307 write_u2(&mut info, entries.len() as u16);
308 for entry in entries {
309 write_stack_map_frame(&mut info, entry);
310 }
311 write_attribute_with_info(out, name_index, &info);
312 }
313 AttributeInfo::Deprecated => {
314 let name_index = ensure_utf8(cp, "Deprecated");
315 write_attribute_with_info(out, name_index, &[]);
316 }
317 AttributeInfo::Synthetic => {
318 let name_index = ensure_utf8(cp, "Synthetic");
319 write_attribute_with_info(out, name_index, &[]);
320 }
321 AttributeInfo::InnerClasses { classes } => {
322 let name_index = ensure_utf8(cp, "InnerClasses");
323 let mut info = Vec::new();
324 write_u2(&mut info, classes.len() as u16);
325 for class in classes {
326 write_inner_class(&mut info, class);
327 }
328 write_attribute_with_info(out, name_index, &info);
329 }
330 AttributeInfo::EnclosingMethod {
331 class_index,
332 method_index,
333 } => {
334 let name_index = ensure_utf8(cp, "EnclosingMethod");
335 let mut info = Vec::new();
336 write_u2(&mut info, *class_index);
337 write_u2(&mut info, *method_index);
338 write_attribute_with_info(out, name_index, &info);
339 }
340 AttributeInfo::BootstrapMethods { methods } => {
341 let name_index = ensure_utf8(cp, "BootstrapMethods");
342 let mut info = Vec::new();
343 write_u2(&mut info, methods.len() as u16);
344 for method in methods {
345 write_bootstrap_method(&mut info, method);
346 }
347 write_attribute_with_info(out, name_index, &info);
348 }
349 AttributeInfo::MethodParameters { parameters } => {
350 let name_index = ensure_utf8(cp, "MethodParameters");
351 let mut info = Vec::new();
352 write_u1(&mut info, parameters.len() as u8);
353 for parameter in parameters {
354 write_method_parameter(&mut info, parameter);
355 }
356 write_attribute_with_info(out, name_index, &info);
357 }
358 AttributeInfo::Unknown { name, info } => {
359 let name_index = ensure_utf8(cp, name);
360 write_attribute_with_info(out, name_index, info);
361 }
362 }
363
364 Ok(())
365}
366
367fn write_attribute_with_info(out: &mut Vec<u8>, name_index: u16, info: &[u8]) {
368 write_u2(out, name_index);
369 write_u4(out, info.len() as u32);
370 out.extend_from_slice(info);
371}
372
373fn write_exception_table_entry(out: &mut Vec<u8>, entry: &ExceptionTableEntry) {
374 write_u2(out, entry.start_pc);
375 write_u2(out, entry.end_pc);
376 write_u2(out, entry.handler_pc);
377 write_u2(out, entry.catch_type);
378}
379
380fn write_line_number(out: &mut Vec<u8>, entry: &LineNumber) {
381 write_u2(out, entry.start_pc);
382 write_u2(out, entry.line_number);
383}
384
385fn write_local_variable(out: &mut Vec<u8>, entry: &LocalVariable) {
386 write_u2(out, entry.start_pc);
387 write_u2(out, entry.length);
388 write_u2(out, entry.name_index);
389 write_u2(out, entry.descriptor_index);
390 write_u2(out, entry.index);
391}
392
393fn write_inner_class(out: &mut Vec<u8>, entry: &InnerClass) {
394 write_u2(out, entry.inner_class_info_index);
395 write_u2(out, entry.outer_class_info_index);
396 write_u2(out, entry.inner_name_index);
397 write_u2(out, entry.inner_class_access_flags);
398}
399
400fn write_bootstrap_method(out: &mut Vec<u8>, entry: &BootstrapMethod) {
401 write_u2(out, entry.bootstrap_method_ref);
402 write_u2(out, entry.bootstrap_arguments.len() as u16);
403 for arg in &entry.bootstrap_arguments {
404 write_u2(out, *arg);
405 }
406}
407
408fn write_method_parameter(out: &mut Vec<u8>, entry: &MethodParameter) {
409 write_u2(out, entry.name_index);
410 write_u2(out, entry.access_flags);
411}
412
413fn write_stack_map_frame(out: &mut Vec<u8>, frame: &StackMapFrame) {
414 match frame {
415 StackMapFrame::SameFrame { offset_delta } => {
416 write_u1(out, *offset_delta as u8);
417 }
418 StackMapFrame::SameLocals1StackItemFrame {
419 offset_delta,
420 stack,
421 } => {
422 write_u1(out, (*offset_delta as u8) + 64);
423 write_verification_type(out, stack);
424 }
425 StackMapFrame::SameLocals1StackItemFrameExtended {
426 offset_delta,
427 stack,
428 } => {
429 write_u1(out, 247);
430 write_u2(out, *offset_delta);
431 write_verification_type(out, stack);
432 }
433 StackMapFrame::ChopFrame { offset_delta, k } => {
434 write_u1(out, 251 - *k);
435 write_u2(out, *offset_delta);
436 }
437 StackMapFrame::SameFrameExtended { offset_delta } => {
438 write_u1(out, 251);
439 write_u2(out, *offset_delta);
440 }
441 StackMapFrame::AppendFrame {
442 offset_delta,
443 locals,
444 } => {
445 write_u1(out, 251 + locals.len() as u8);
446 write_u2(out, *offset_delta);
447 for local in locals {
448 write_verification_type(out, local);
449 }
450 }
451 StackMapFrame::FullFrame {
452 offset_delta,
453 locals,
454 stack,
455 } => {
456 write_u1(out, 255);
457 write_u2(out, *offset_delta);
458 write_u2(out, locals.len() as u16);
459 for local in locals {
460 write_verification_type(out, local);
461 }
462 write_u2(out, stack.len() as u16);
463 for value in stack {
464 write_verification_type(out, value);
465 }
466 }
467 }
468}
469
470fn write_verification_type(out: &mut Vec<u8>, value: &VerificationTypeInfo) {
471 match value {
472 VerificationTypeInfo::Top => write_u1(out, 0),
473 VerificationTypeInfo::Integer => write_u1(out, 1),
474 VerificationTypeInfo::Float => write_u1(out, 2),
475 VerificationTypeInfo::Double => write_u1(out, 3),
476 VerificationTypeInfo::Long => write_u1(out, 4),
477 VerificationTypeInfo::Null => write_u1(out, 5),
478 VerificationTypeInfo::UninitializedThis => write_u1(out, 6),
479 VerificationTypeInfo::Object { cpool_index } => {
480 write_u1(out, 7);
481 write_u2(out, *cpool_index);
482 }
483 VerificationTypeInfo::Uninitialized { offset } => {
484 write_u1(out, 8);
485 write_u2(out, *offset);
486 }
487 }
488}
489
490fn collect_attribute_names(attributes: &[AttributeInfo], names: &mut Vec<String>) {
491 for attr in attributes {
492 match attr {
493 AttributeInfo::Code(_) => names.push("Code".to_string()),
494 AttributeInfo::ConstantValue { .. } => names.push("ConstantValue".to_string()),
495 AttributeInfo::Exceptions { .. } => names.push("Exceptions".to_string()),
496 AttributeInfo::SourceFile { .. } => names.push("SourceFile".to_string()),
497 AttributeInfo::LineNumberTable { .. } => names.push("LineNumberTable".to_string()),
498 AttributeInfo::LocalVariableTable { .. } => {
499 names.push("LocalVariableTable".to_string())
500 }
501 AttributeInfo::Signature { .. } => names.push("Signature".to_string()),
502 AttributeInfo::StackMapTable { .. } => names.push("StackMapTable".to_string()),
503 AttributeInfo::Deprecated => names.push("Deprecated".to_string()),
504 AttributeInfo::Synthetic => names.push("Synthetic".to_string()),
505 AttributeInfo::InnerClasses { .. } => names.push("InnerClasses".to_string()),
506 AttributeInfo::EnclosingMethod { .. } => names.push("EnclosingMethod".to_string()),
507 AttributeInfo::BootstrapMethods { .. } => names.push("BootstrapMethods".to_string()),
508 AttributeInfo::MethodParameters { .. } => names.push("MethodParameters".to_string()),
509 AttributeInfo::Unknown { name, .. } => names.push(name.clone()),
510 }
511 }
512}
513
514fn write_constant_pool(out: &mut Vec<u8>, cp: &[CpInfo]) -> Result<(), ClassWriteError> {
515 write_u2(out, cp.len() as u16);
516 for entry in cp.iter().skip(1) {
517 match entry {
518 CpInfo::Unusable => {}
519 CpInfo::Utf8(value) => {
520 write_u1(out, 1);
521 write_u2(out, value.len() as u16);
522 out.extend_from_slice(value.as_bytes());
523 }
524 CpInfo::Integer(value) => {
525 write_u1(out, 3);
526 write_u4(out, *value as u32);
527 }
528 CpInfo::Float(value) => {
529 write_u1(out, 4);
530 write_u4(out, value.to_bits());
531 }
532 CpInfo::Long(value) => {
533 write_u1(out, 5);
534 write_u8(out, *value as u64);
535 }
536 CpInfo::Double(value) => {
537 write_u1(out, 6);
538 write_u8(out, value.to_bits());
539 }
540 CpInfo::Class { name_index } => {
541 write_u1(out, 7);
542 write_u2(out, *name_index);
543 }
544 CpInfo::String { string_index } => {
545 write_u1(out, 8);
546 write_u2(out, *string_index);
547 }
548 CpInfo::Fieldref {
549 class_index,
550 name_and_type_index,
551 } => {
552 write_u1(out, 9);
553 write_u2(out, *class_index);
554 write_u2(out, *name_and_type_index);
555 }
556 CpInfo::Methodref {
557 class_index,
558 name_and_type_index,
559 } => {
560 write_u1(out, 10);
561 write_u2(out, *class_index);
562 write_u2(out, *name_and_type_index);
563 }
564 CpInfo::InterfaceMethodref {
565 class_index,
566 name_and_type_index,
567 } => {
568 write_u1(out, 11);
569 write_u2(out, *class_index);
570 write_u2(out, *name_and_type_index);
571 }
572 CpInfo::NameAndType {
573 name_index,
574 descriptor_index,
575 } => {
576 write_u1(out, 12);
577 write_u2(out, *name_index);
578 write_u2(out, *descriptor_index);
579 }
580 CpInfo::MethodHandle {
581 reference_kind,
582 reference_index,
583 } => {
584 write_u1(out, 15);
585 write_u1(out, *reference_kind);
586 write_u2(out, *reference_index);
587 }
588 CpInfo::MethodType { descriptor_index } => {
589 write_u1(out, 16);
590 write_u2(out, *descriptor_index);
591 }
592 CpInfo::Dynamic {
593 bootstrap_method_attr_index,
594 name_and_type_index,
595 } => {
596 write_u1(out, 17);
597 write_u2(out, *bootstrap_method_attr_index);
598 write_u2(out, *name_and_type_index);
599 }
600 CpInfo::InvokeDynamic {
601 bootstrap_method_attr_index,
602 name_and_type_index,
603 } => {
604 write_u1(out, 18);
605 write_u2(out, *bootstrap_method_attr_index);
606 write_u2(out, *name_and_type_index);
607 }
608 CpInfo::Module { name_index } => {
609 write_u1(out, 19);
610 write_u2(out, *name_index);
611 }
612 CpInfo::Package { name_index } => {
613 write_u1(out, 20);
614 write_u2(out, *name_index);
615 }
616 }
617 }
618 Ok(())
619}
620
621fn ensure_utf8(cp: &mut Vec<CpInfo>, value: &str) -> u16 {
622 if let Some(index) = cp_find_utf8(cp, value) {
623 return index;
624 }
625 cp.push(CpInfo::Utf8(value.to_string()));
626 (cp.len() - 1) as u16
627}
628
629fn ensure_class(cp: &mut Vec<CpInfo>, name: &str) -> u16 {
630 for (index, entry) in cp.iter().enumerate() {
631 if let CpInfo::Class { name_index } = entry
632 && let Some(CpInfo::Utf8(value)) = cp.get(*name_index as usize)
633 && value == name
634 {
635 return index as u16;
636 }
637 }
638 let name_index = ensure_utf8(cp, name);
639 cp.push(CpInfo::Class { name_index });
640 (cp.len() - 1) as u16
641}
642
643fn cp_find_utf8(cp: &[CpInfo], value: &str) -> Option<u16> {
644 for (index, entry) in cp.iter().enumerate() {
645 if let CpInfo::Utf8(existing) = entry
646 && existing == value
647 {
648 return Some(index as u16);
649 }
650 }
651 None
652}
653
654fn write_u1(out: &mut Vec<u8>, value: u8) {
655 out.push(value);
656}
657
658fn write_u2(out: &mut Vec<u8>, value: u16) {
659 out.extend_from_slice(&value.to_be_bytes());
660}
661
662fn write_u4(out: &mut Vec<u8>, value: u32) {
663 out.extend_from_slice(&value.to_be_bytes());
664}
665
666fn write_u8(out: &mut Vec<u8>, value: u64) {
667 out.extend_from_slice(&value.to_be_bytes());
668}
669
670#[derive(Debug, Clone, PartialEq, Eq)]
671enum FrameType {
672 Top,
673 Integer,
674 Float,
675 Long,
676 Double,
677 Null,
678 UninitializedThis,
679 Object(String),
680 Uninitialized(u16),
681}
682
683fn compute_stack_map_table(
684 method: &MethodNode,
685 class_node: &ClassNode,
686 code: &CodeAttribute,
687 cp: &mut Vec<CpInfo>,
688 max_locals: u16,
689) -> Result<Vec<StackMapFrame>, ClassWriteError> {
690 let insns = parse_instructions(&code.code)?;
691 if insns.is_empty() {
692 return Ok(Vec::new());
693 }
694
695 let mut insn_index = std::collections::HashMap::new();
696 for (index, insn) in insns.iter().enumerate() {
697 insn_index.insert(insn.offset, index);
698 }
699
700 let handlers = build_exception_handlers(code, cp)?;
701 let handler_common = handler_common_types(&handlers);
702 let mut frames: std::collections::HashMap<u16, FrameState> = std::collections::HashMap::new();
703 let mut worklist = std::collections::VecDeque::new();
704 let mut in_worklist = std::collections::HashSet::new();
705
706 let mut initial = initial_frame(method, class_node)?;
707 pad_locals(&mut initial.locals, max_locals);
708 frames.insert(0, initial.clone());
709 worklist.push_back(0u16);
710 in_worklist.insert(0u16);
711
712 let mut max_iterations = 0usize;
713 while let Some(offset) = worklist.pop_front() {
714 in_worklist.remove(&offset);
715 max_iterations += 1;
716 if max_iterations > 100000 {
717 return Err(ClassWriteError::FrameComputation(
718 "frame analysis exceeded iteration limit".to_string(),
719 ));
720 }
721 let index = *insn_index.get(&offset).ok_or_else(|| {
722 ClassWriteError::FrameComputation(format!("missing instruction at {offset}"))
723 })?;
724 let insn = &insns[index];
725 let frame = frames
726 .get(&offset)
727 .ok_or_else(|| ClassWriteError::FrameComputation(format!("missing frame at {offset}")))?
728 .clone();
729 let insn1 = &insn;
730 let out_frame = execute_instruction(insn1, &frame, class_node, cp)?;
731
732 for succ in instruction_successors(insn) {
733 if let Some(next_frame) = merge_frame(&out_frame, frames.get(&succ)) {
734 let changed = match frames.get(&succ) {
735 Some(existing) => existing != &next_frame,
736 None => true,
737 };
738 if changed {
739 frames.insert(succ, next_frame);
740 if in_worklist.insert(succ) {
741 worklist.push_back(succ);
742 }
743 }
744 }
745 }
746
747 for handler in handlers.iter().filter(|item| item.covers(offset)) {
748 let mut handler_frame = FrameState {
749 locals: frame.locals.clone(),
750 stack: Vec::new(),
751 };
752 let exception_type = handler_common
753 .get(&handler.handler_pc)
754 .cloned()
755 .unwrap_or_else(|| handler.exception_type.clone());
756 handler_frame.stack.push(exception_type);
757 if let Some(next_frame) = merge_frame(&handler_frame, frames.get(&handler.handler_pc)) {
758 let changed = match frames.get(&handler.handler_pc) {
759 Some(existing) => existing != &next_frame,
760 None => true,
761 };
762 if changed {
763 frames.insert(handler.handler_pc, next_frame);
764 if in_worklist.insert(handler.handler_pc) {
765 worklist.push_back(handler.handler_pc);
766 }
767 }
768 }
769 }
770 }
771
772 let mut frame_offsets: Vec<u16> = frames.keys().copied().collect();
773 frame_offsets.sort_unstable();
774 let mut result = Vec::new();
775 let mut previous_offset: i32 = -1;
776 for offset in frame_offsets {
777 if offset == 0 {
778 continue;
779 }
780 let frame = frames
781 .get(&offset)
782 .ok_or_else(|| ClassWriteError::FrameComputation(format!("missing frame at {offset}")))?
783 .clone();
784 let locals = compact_locals(&frame.locals);
785 let stack = frame.stack;
786 let offset_delta = (offset as i32 - previous_offset - 1) as u16;
787 previous_offset = offset as i32;
788 let locals_info = locals
789 .iter()
790 .map(|value| to_verification_type(value, cp))
791 .collect();
792 let stack_info = stack
793 .iter()
794 .map(|value| to_verification_type(value, cp))
795 .collect();
796 result.push(StackMapFrame::FullFrame {
797 offset_delta,
798 locals: locals_info,
799 stack: stack_info,
800 });
801 }
802
803 Ok(result)
804}
805
806#[derive(Debug, Clone, PartialEq, Eq)]
807struct FrameState {
808 locals: Vec<FrameType>,
809 stack: Vec<FrameType>,
810}
811
812fn merge_frame(frame: &FrameState, existing: Option<&FrameState>) -> Option<FrameState> {
813 match existing {
814 None => Some(frame.clone()),
815 Some(other) => {
816 let merged = FrameState {
817 locals: merge_vec(&frame.locals, &other.locals),
818 stack: merge_vec(&frame.stack, &other.stack),
819 };
820 if merged == *other { None } else { Some(merged) }
821 }
822 }
823}
824
825fn merge_vec(a: &[FrameType], b: &[FrameType]) -> Vec<FrameType> {
826 let len = a.len().max(b.len());
827 let mut merged = Vec::with_capacity(len);
828 for i in 0..len {
829 let left = a.get(i).cloned().unwrap_or(FrameType::Top);
830 let right = b.get(i).cloned().unwrap_or(FrameType::Top);
831 merged.push(merge_type(&left, &right));
832 }
833 merged
834}
835
836fn merge_type(a: &FrameType, b: &FrameType) -> FrameType {
837 if a == b {
838 return a.clone();
839 }
840 match (a, b) {
841 (FrameType::Top, _) => FrameType::Top,
842 (_, FrameType::Top) => FrameType::Top,
843 (FrameType::Null, FrameType::Object(name)) | (FrameType::Object(name), FrameType::Null) => {
844 FrameType::Object(name.clone())
845 }
846 (FrameType::Object(left), FrameType::Object(right)) => {
847 FrameType::Object(common_superclass(left, right))
848 }
849 (FrameType::Object(_), FrameType::Uninitialized(_))
850 | (FrameType::Uninitialized(_), FrameType::Object(_))
851 | (FrameType::UninitializedThis, FrameType::Object(_))
852 | (FrameType::Object(_), FrameType::UninitializedThis) => {
853 FrameType::Object("java/lang/Object".to_string())
854 }
855 _ => FrameType::Top,
856 }
857}
858
859fn common_superclass(left: &str, right: &str) -> String {
860 if left == right {
861 return left.to_string();
862 }
863 if left.starts_with('[') || right.starts_with('[') {
864 return "java/lang/Object".to_string();
865 }
866
867 let mut ancestors = std::collections::HashSet::new();
868 let mut current = left;
869 ancestors.insert(current.to_string());
870 while let Some(parent) = known_superclass(current) {
871 if ancestors.insert(parent.to_string()) {
872 current = parent;
873 } else {
874 break;
875 }
876 }
877 ancestors.insert("java/lang/Object".to_string());
878
879 current = right;
880 if ancestors.contains(current) {
881 return current.to_string();
882 }
883 while let Some(parent) = known_superclass(current) {
884 if ancestors.contains(parent) {
885 return parent.to_string();
886 }
887 current = parent;
888 }
889 "java/lang/Object".to_string()
890}
891
892fn known_superclass(name: &str) -> Option<&'static str> {
893 match name {
894 "java/lang/Throwable" => Some("java/lang/Object"),
895 "java/lang/Exception" => Some("java/lang/Throwable"),
896 "java/lang/RuntimeException" => Some("java/lang/Exception"),
897 "java/lang/IllegalArgumentException" => Some("java/lang/RuntimeException"),
898 "java/lang/IllegalStateException" => Some("java/lang/RuntimeException"),
899 "java/security/GeneralSecurityException" => Some("java/lang/Exception"),
900 "java/security/NoSuchAlgorithmException" => Some("java/security/GeneralSecurityException"),
901 "java/security/InvalidKeyException" => Some("java/security/GeneralSecurityException"),
902 "javax/crypto/NoSuchPaddingException" => Some("java/security/GeneralSecurityException"),
903 "javax/crypto/IllegalBlockSizeException" => Some("java/security/GeneralSecurityException"),
904 "javax/crypto/BadPaddingException" => Some("java/security/GeneralSecurityException"),
905 _ => None,
906 }
907}
908
909fn pad_locals(locals: &mut Vec<FrameType>, max_locals: u16) {
910 while locals.len() < max_locals as usize {
911 locals.push(FrameType::Top);
912 }
913}
914
915fn compute_maxs(
916 method: &MethodNode,
917 class_node: &ClassNode,
918 code: &CodeAttribute,
919 cp: &[CpInfo],
920) -> Result<(u16, u16), ClassWriteError> {
921 let insns = parse_instructions(&code.code)?;
922 if insns.is_empty() {
923 let initial = initial_frame(method, class_node)?;
924 return Ok((0, initial.locals.len() as u16));
925 }
926
927 let mut insn_index = std::collections::HashMap::new();
928 for (index, insn) in insns.iter().enumerate() {
929 insn_index.insert(insn.offset, index);
930 }
931
932 let handlers = build_exception_handlers(code, cp)?;
933 let mut frames: std::collections::HashMap<u16, FrameState> = std::collections::HashMap::new();
934 let mut worklist = std::collections::VecDeque::new();
935 let mut in_worklist = std::collections::HashSet::new();
936
937 let initial = initial_frame(method, class_node)?;
938 frames.insert(0, initial.clone());
939 worklist.push_back(0u16);
940 in_worklist.insert(0u16);
941
942 let mut max_stack = initial.stack.len();
943 let mut max_locals = initial.locals.len();
944 let mut max_iterations = 0usize;
945 let mut offset_hits: std::collections::HashMap<u16, u32> = std::collections::HashMap::new();
946 while let Some(offset) = worklist.pop_front() {
947 in_worklist.remove(&offset);
948 max_iterations += 1;
949 *offset_hits.entry(offset).or_insert(0) += 1;
950 if max_iterations > 100000 {
951 return Err(ClassWriteError::FrameComputation(
952 "frame analysis exceeded iteration limit".to_string(),
953 ));
954 }
955 let index = *insn_index.get(&offset).ok_or_else(|| {
956 ClassWriteError::FrameComputation(format!("missing instruction at {offset}"))
957 })?;
958 let insn = &insns[index];
959 let frame = frames.get(&offset).cloned().ok_or_else(|| {
960 ClassWriteError::FrameComputation(format!("missing frame at {offset}"))
961 })?;
962 max_stack = max_stack.max(stack_slots(&frame.stack));
963 max_locals = max_locals.max(frame.locals.len());
964
965 let out_frame = execute_instruction(insn, &frame, class_node, cp)?;
966 max_stack = max_stack.max(stack_slots(&out_frame.stack));
967 max_locals = max_locals.max(out_frame.locals.len());
968
969 for succ in instruction_successors(insn) {
970 if let Some(next_frame) = merge_frame(&out_frame, frames.get(&succ)) {
971 let changed = match frames.get(&succ) {
972 Some(existing) => existing != &next_frame,
973 None => true,
974 };
975 if changed {
976 frames.insert(succ, next_frame);
977 if in_worklist.insert(succ) {
978 worklist.push_back(succ);
979 }
980 }
981 }
982 }
983
984 for handler in handlers.iter().filter(|item| item.covers(offset)) {
985 let mut handler_frame = FrameState {
986 locals: frame.locals.clone(),
987 stack: Vec::new(),
988 };
989 handler_frame.stack.push(handler.exception_type.clone());
990 max_stack = max_stack.max(stack_slots(&handler_frame.stack));
991 max_locals = max_locals.max(handler_frame.locals.len());
992 if let Some(next_frame) = merge_frame(&handler_frame, frames.get(&handler.handler_pc)) {
993 let changed = match frames.get(&handler.handler_pc) {
994 Some(existing) => existing != &next_frame,
995 None => true,
996 };
997 if changed {
998 frames.insert(handler.handler_pc, next_frame);
999 if in_worklist.insert(handler.handler_pc) {
1000 worklist.push_back(handler.handler_pc);
1001 }
1002 }
1003 }
1004 }
1005 }
1006
1007 Ok((max_stack as u16, max_locals as u16))
1008}
1009
1010fn stack_slots(stack: &[FrameType]) -> usize {
1011 let mut slots = 0usize;
1012 for value in stack {
1013 slots += if is_category2(value) { 2 } else { 1 };
1014 }
1015 slots
1016}
1017
1018fn compact_locals(locals: &[FrameType]) -> Vec<FrameType> {
1019 let mut out = Vec::new();
1020 let mut i = 0usize;
1021 while i < locals.len() {
1022 match locals[i] {
1023 FrameType::Top => {
1024 if i > 0 && matches!(locals[i - 1], FrameType::Long | FrameType::Double) {
1025 i += 1;
1026 continue;
1027 }
1028 out.push(FrameType::Top);
1029 }
1030 FrameType::Long | FrameType::Double => {
1031 out.push(locals[i].clone());
1032 if i + 1 < locals.len() && matches!(locals[i + 1], FrameType::Top) {
1033 i += 1;
1034 }
1035 }
1036 _ => out.push(locals[i].clone()),
1037 }
1038 i += 1;
1039 }
1040
1041 while matches!(out.last(), Some(FrameType::Top)) {
1042 out.pop();
1043 }
1044 out
1045}
1046
1047fn to_verification_type(value: &FrameType, cp: &mut Vec<CpInfo>) -> VerificationTypeInfo {
1048 match value {
1049 FrameType::Top => VerificationTypeInfo::Top,
1050 FrameType::Integer => VerificationTypeInfo::Integer,
1051 FrameType::Float => VerificationTypeInfo::Float,
1052 FrameType::Long => VerificationTypeInfo::Long,
1053 FrameType::Double => VerificationTypeInfo::Double,
1054 FrameType::Null => VerificationTypeInfo::Null,
1055 FrameType::UninitializedThis => VerificationTypeInfo::UninitializedThis,
1056 FrameType::Uninitialized(offset) => VerificationTypeInfo::Uninitialized { offset: *offset },
1057 FrameType::Object(name) => {
1058 let index = ensure_class(cp, name);
1059 VerificationTypeInfo::Object { cpool_index: index }
1060 }
1061 }
1062}
1063
1064fn initial_frame(
1065 method: &MethodNode,
1066 class_node: &ClassNode,
1067) -> Result<FrameState, ClassWriteError> {
1068 let mut locals = Vec::new();
1069 let is_static = method.access_flags & 0x0008 != 0;
1070 if !is_static {
1071 if method.name == "<init>" {
1072 locals.push(FrameType::UninitializedThis);
1073 } else {
1074 locals.push(FrameType::Object(class_node.name.clone()));
1075 }
1076 }
1077 let (params, _) = parse_method_descriptor(&method.descriptor)?;
1078 for param in params {
1079 push_local_type(&mut locals, param);
1080 }
1081 Ok(FrameState {
1082 locals,
1083 stack: Vec::new(),
1084 })
1085}
1086
1087fn push_local_type(locals: &mut Vec<FrameType>, ty: FieldType) {
1088 match ty {
1089 FieldType::Long => {
1090 locals.push(FrameType::Long);
1091 locals.push(FrameType::Top);
1092 }
1093 FieldType::Double => {
1094 locals.push(FrameType::Double);
1095 locals.push(FrameType::Top);
1096 }
1097 FieldType::Float => locals.push(FrameType::Float),
1098 FieldType::Boolean
1099 | FieldType::Byte
1100 | FieldType::Char
1101 | FieldType::Short
1102 | FieldType::Int => locals.push(FrameType::Integer),
1103 FieldType::Object(name) => locals.push(FrameType::Object(name)),
1104 FieldType::Array(desc) => locals.push(FrameType::Object(desc)),
1105 FieldType::Void => {}
1106 }
1107}
1108
1109#[derive(Debug, Clone)]
1110struct ExceptionHandlerInfo {
1111 start_pc: u16,
1112 end_pc: u16,
1113 handler_pc: u16,
1114 exception_type: FrameType,
1115}
1116
1117impl ExceptionHandlerInfo {
1118 fn covers(&self, offset: u16) -> bool {
1119 offset >= self.start_pc && offset < self.end_pc
1120 }
1121}
1122
1123fn build_exception_handlers(
1124 code: &CodeAttribute,
1125 cp: &[CpInfo],
1126) -> Result<Vec<ExceptionHandlerInfo>, ClassWriteError> {
1127 let mut handlers = Vec::new();
1128 for entry in &code.exception_table {
1129 let exception_type = if entry.catch_type == 0 {
1130 FrameType::Object("java/lang/Throwable".to_string())
1131 } else {
1132 let class_name = cp_class_name(cp, entry.catch_type)?;
1133 FrameType::Object(class_name.to_string())
1134 };
1135 handlers.push(ExceptionHandlerInfo {
1136 start_pc: entry.start_pc,
1137 end_pc: entry.end_pc,
1138 handler_pc: entry.handler_pc,
1139 exception_type,
1140 });
1141 }
1142 Ok(handlers)
1143}
1144
1145fn handler_common_types(
1146 handlers: &[ExceptionHandlerInfo],
1147) -> std::collections::HashMap<u16, FrameType> {
1148 let mut map: std::collections::HashMap<u16, FrameType> = std::collections::HashMap::new();
1149 for handler in handlers {
1150 map.entry(handler.handler_pc)
1151 .and_modify(|existing| {
1152 *existing = merge_exception_type(existing, &handler.exception_type);
1153 })
1154 .or_insert_with(|| handler.exception_type.clone());
1155 }
1156 map
1157}
1158
1159fn merge_exception_type(left: &FrameType, right: &FrameType) -> FrameType {
1160 match (left, right) {
1161 (FrameType::Object(l), FrameType::Object(r)) => FrameType::Object(common_superclass(l, r)),
1162 _ if left == right => left.clone(),
1163 _ => FrameType::Object("java/lang/Object".to_string()),
1164 }
1165}
1166
1167fn dump_frame_debug(
1168 method: &MethodNode,
1169 label: &str,
1170 iterations: usize,
1171 hits: &std::collections::HashMap<u16, u32>,
1172) {
1173 let mut entries: Vec<(u16, u32)> = hits.iter().map(|(k, v)| (*k, *v)).collect();
1174 entries.sort_by(|a, b| b.1.cmp(&a.1));
1175 let top = entries.into_iter().take(10).collect::<Vec<_>>();
1176 eprintln!(
1177 "[frame-debug] method={}{} label={} iterations={} top_offsets={:?}",
1178 method.name, method.descriptor, label, iterations, top
1179 );
1180}
1181
1182#[derive(Debug, Clone)]
1183struct ParsedInstruction {
1184 offset: u16,
1185 opcode: u8,
1186 operand: Operand,
1187}
1188
1189#[derive(Debug, Clone)]
1190enum Operand {
1191 None,
1192 I1(i8),
1193 I2(i16),
1194 I4(i32),
1195 U1(u8),
1196 U2(u16),
1197 U4(u32),
1198 Jump(i16),
1199 JumpWide(i32),
1200 TableSwitch {
1201 default_offset: i32,
1202 low: i32,
1203 high: i32,
1204 offsets: Vec<i32>,
1205 },
1206 LookupSwitch {
1207 default_offset: i32,
1208 pairs: Vec<(i32, i32)>,
1209 },
1210 Iinc {
1211 index: u16,
1212 increment: i16,
1213 },
1214 InvokeInterface {
1215 index: u16,
1216 count: u8,
1217 },
1218 InvokeDynamic {
1219 index: u16,
1220 },
1221 MultiANewArray {
1222 index: u16,
1223 dims: u8,
1224 },
1225 Wide {
1226 opcode: u8,
1227 index: u16,
1228 increment: Option<i16>,
1229 },
1230}
1231
1232fn parse_instructions(code: &[u8]) -> Result<Vec<ParsedInstruction>, ClassWriteError> {
1233 let mut insns = Vec::new();
1234 let mut pos = 0usize;
1235 while pos < code.len() {
1236 let offset = pos as u16;
1237 let opcode = code[pos];
1238 pos += 1;
1239 let operand = match opcode {
1240 0x10 => {
1241 let value = read_i1(code, &mut pos)?;
1242 Operand::I1(value)
1243 }
1244 0x11 => Operand::I2(read_i2(code, &mut pos)?),
1245 0x12 => Operand::U1(read_u1(code, &mut pos)?),
1246 0x13 | 0x14 => Operand::U2(read_u2(code, &mut pos)?),
1247 0x15..=0x19 | 0x36..=0x3A | 0xA9 => Operand::U1(read_u1(code, &mut pos)?),
1248 0x84 => {
1249 let index = read_u1(code, &mut pos)? as u16;
1250 let inc = read_i1(code, &mut pos)? as i16;
1251 Operand::Iinc {
1252 index,
1253 increment: inc,
1254 }
1255 }
1256 0x99..=0xA8 | 0xC6 | 0xC7 => Operand::Jump(read_i2(code, &mut pos)?),
1257 0xC8 | 0xC9 => Operand::JumpWide(read_i4(code, &mut pos)?),
1258 0xAA => {
1259 let padding = (4 - (pos % 4)) % 4;
1260 pos += padding;
1261 let default_offset = read_i4(code, &mut pos)?;
1262 let low = read_i4(code, &mut pos)?;
1263 let high = read_i4(code, &mut pos)?;
1264 let count = if high < low {
1265 0
1266 } else {
1267 (high - low + 1) as usize
1268 };
1269 let mut offsets = Vec::with_capacity(count);
1270 for _ in 0..count {
1271 offsets.push(read_i4(code, &mut pos)?);
1272 }
1273 Operand::TableSwitch {
1274 default_offset,
1275 low,
1276 high,
1277 offsets,
1278 }
1279 }
1280 0xAB => {
1281 let padding = (4 - (pos % 4)) % 4;
1282 pos += padding;
1283 let default_offset = read_i4(code, &mut pos)?;
1284 let npairs = read_i4(code, &mut pos)? as usize;
1285 let mut pairs = Vec::with_capacity(npairs);
1286 for _ in 0..npairs {
1287 let key = read_i4(code, &mut pos)?;
1288 let value = read_i4(code, &mut pos)?;
1289 pairs.push((key, value));
1290 }
1291 Operand::LookupSwitch {
1292 default_offset,
1293 pairs,
1294 }
1295 }
1296 0xB2..=0xB8 | 0xBB | 0xBD | 0xC0 | 0xC1 => Operand::U2(read_u2(code, &mut pos)?),
1297 0xB9 => {
1298 let index = read_u2(code, &mut pos)?;
1299 let count = read_u1(code, &mut pos)?;
1300 let _ = read_u1(code, &mut pos)?;
1301 Operand::InvokeInterface { index, count }
1302 }
1303 0xBA => {
1304 let index = read_u2(code, &mut pos)?;
1305 let _ = read_u2(code, &mut pos)?;
1306 Operand::InvokeDynamic { index }
1307 }
1308 0xBC => Operand::U1(read_u1(code, &mut pos)?),
1309 0xC4 => {
1310 let wide_opcode = read_u1(code, &mut pos)?;
1311 match wide_opcode {
1312 0x15..=0x19 | 0x36..=0x3A | 0xA9 => {
1313 let index = read_u2(code, &mut pos)?;
1314 Operand::Wide {
1315 opcode: wide_opcode,
1316 index,
1317 increment: None,
1318 }
1319 }
1320 0x84 => {
1321 let index = read_u2(code, &mut pos)?;
1322 let increment = read_i2(code, &mut pos)?;
1323 Operand::Wide {
1324 opcode: wide_opcode,
1325 index,
1326 increment: Some(increment),
1327 }
1328 }
1329 _ => {
1330 return Err(ClassWriteError::InvalidOpcode {
1331 opcode: wide_opcode,
1332 offset: pos - 1,
1333 });
1334 }
1335 }
1336 }
1337 0xC5 => {
1338 let index = read_u2(code, &mut pos)?;
1339 let dims = read_u1(code, &mut pos)?;
1340 Operand::MultiANewArray { index, dims }
1341 }
1342 _ => Operand::None,
1343 };
1344 insns.push(ParsedInstruction {
1345 offset,
1346 opcode,
1347 operand,
1348 });
1349 }
1350 Ok(insns)
1351}
1352
1353fn instruction_successors(insn: &ParsedInstruction) -> Vec<u16> {
1354 let mut successors = Vec::new();
1355 let next_offset = insn.offset.saturating_add(instruction_length(insn) as u16);
1356 match insn.opcode {
1357 0xA7 | 0xC8 => {
1358 if let Some(target) = jump_target(insn) {
1359 successors.push(target);
1360 }
1361 }
1362 0xA8 | 0xC9 => {
1363 if let Some(target) = jump_target(insn) {
1364 successors.push(target);
1365 }
1366 successors.push(next_offset);
1367 }
1368 0x99..=0xA6 | 0xC6 | 0xC7 => {
1369 if let Some(target) = jump_target(insn) {
1370 successors.push(target);
1371 }
1372 successors.push(next_offset);
1373 }
1374 0xAA => {
1375 if let Operand::TableSwitch {
1376 default_offset,
1377 offsets,
1378 ..
1379 } = &insn.operand
1380 {
1381 successors.push((insn.offset as i32 + default_offset) as u16);
1382 for offset in offsets {
1383 successors.push((insn.offset as i32 + *offset) as u16);
1384 }
1385 }
1386 }
1387 0xAB => {
1388 if let Operand::LookupSwitch {
1389 default_offset,
1390 pairs,
1391 } = &insn.operand
1392 {
1393 successors.push((insn.offset as i32 + default_offset) as u16);
1394 for (_, offset) in pairs {
1395 successors.push((insn.offset as i32 + *offset) as u16);
1396 }
1397 }
1398 }
1399 0xAC..=0xB1 | 0xBF => {}
1400 0xC2 | 0xC3 => {
1401 successors.push(next_offset);
1402 }
1403 _ => {
1404 if next_offset != insn.offset {
1405 successors.push(next_offset);
1406 }
1407 }
1408 }
1409 successors
1410}
1411
1412fn jump_target(insn: &ParsedInstruction) -> Option<u16> {
1413 match insn.operand {
1414 Operand::Jump(offset) => Some((insn.offset as i32 + offset as i32) as u16),
1415 Operand::JumpWide(offset) => Some((insn.offset as i32 + offset) as u16),
1416 _ => None,
1417 }
1418}
1419
1420fn instruction_length(insn: &ParsedInstruction) -> usize {
1421 match &insn.operand {
1422 Operand::None => 1,
1423 Operand::I1(_) | Operand::U1(_) => 2,
1424 Operand::I2(_) | Operand::U2(_) | Operand::Jump(_) => 3,
1425 Operand::I4(_) | Operand::U4(_) | Operand::JumpWide(_) => 5,
1426 Operand::Iinc { .. } => 3,
1427 Operand::InvokeInterface { .. } => 5,
1428 Operand::InvokeDynamic { .. } => 5,
1429 Operand::MultiANewArray { .. } => 4,
1430 Operand::Wide {
1431 opcode, increment, ..
1432 } => {
1433 if *opcode == 0x84 && increment.is_some() {
1434 6
1435 } else {
1436 4
1437 }
1438 }
1439 Operand::TableSwitch { offsets, .. } => {
1440 1 + switch_padding(insn.offset) + 12 + offsets.len() * 4
1441 }
1442 Operand::LookupSwitch { pairs, .. } => {
1443 1 + switch_padding(insn.offset) + 8 + pairs.len() * 8
1444 }
1445 }
1446}
1447
1448fn switch_padding(offset: u16) -> usize {
1449 let pos = (offset as usize + 1) % 4;
1450 (4 - pos) % 4
1451}
1452
1453fn execute_instruction(
1454 insn: &ParsedInstruction,
1455 frame: &FrameState,
1456 class_node: &ClassNode,
1457 cp: &[CpInfo],
1458) -> Result<FrameState, ClassWriteError> {
1459 let mut locals = frame.locals.clone();
1460 let mut stack = frame.stack.clone();
1461
1462 let pop = |stack: &mut Vec<FrameType>| {
1463 stack.pop().ok_or_else(|| {
1464 ClassWriteError::FrameComputation(format!("stack underflow at {}", insn.offset))
1465 })
1466 };
1467
1468 match insn.opcode {
1469 0x00 => {}
1470 0x01 => stack.push(FrameType::Null),
1471 0x02..=0x08 => stack.push(FrameType::Integer),
1472 0x09 | 0x0A => stack.push(FrameType::Long),
1473 0x0B..=0x0D => stack.push(FrameType::Float),
1474 0x0E | 0x0F => stack.push(FrameType::Double),
1475 0x10 => stack.push(FrameType::Integer),
1476 0x11 => stack.push(FrameType::Integer),
1477 0x12..=0x14 => {
1478 let ty = ldc_type(insn, cp)?;
1479 stack.push(ty);
1480 }
1481 0x15..=0x19 => {
1482 let index = var_index(insn)?;
1483 if let Some(value) = locals.get(index as usize) {
1484 stack.push(value.clone());
1485 } else {
1486 stack.push(FrameType::Top);
1487 }
1488 }
1489 0x1A..=0x1D => stack.push(load_local(
1490 &locals,
1491 (insn.opcode - 0x1A) as u16,
1492 FrameType::Integer,
1493 )),
1494 0x1E..=0x21 => stack.push(load_local(
1495 &locals,
1496 (insn.opcode - 0x1E) as u16,
1497 FrameType::Long,
1498 )),
1499 0x22..=0x25 => stack.push(load_local(
1500 &locals,
1501 (insn.opcode - 0x22) as u16,
1502 FrameType::Float,
1503 )),
1504 0x26..=0x29 => stack.push(load_local(
1505 &locals,
1506 (insn.opcode - 0x26) as u16,
1507 FrameType::Double,
1508 )),
1509 0x2A..=0x2D => stack.push(load_local(
1510 &locals,
1511 (insn.opcode - 0x2A) as u16,
1512 FrameType::Object(class_node.name.clone()),
1513 )),
1514 0x2E..=0x35 => {
1515 pop(&mut stack)?;
1516 let array_ref = pop(&mut stack)?; let ty = match insn.opcode {
1518 0x2E => FrameType::Integer,
1519 0x2F => FrameType::Long,
1520 0x30 => FrameType::Float,
1521 0x31 => FrameType::Double,
1522 0x32 => array_element_type(&array_ref)
1523 .unwrap_or_else(|| FrameType::Object("java/lang/Object".to_string())),
1524 0x33..=0x35 => FrameType::Integer,
1525 _ => FrameType::Top,
1526 };
1527 stack.push(ty);
1528 }
1529 0x36..=0x3A => {
1530 let index = var_index(insn)?;
1531 let value = pop(&mut stack)?;
1532 store_local(&mut locals, index, value);
1533 }
1534 0x3B..=0x3E => {
1535 let value = pop(&mut stack)?;
1536 store_local(&mut locals, (insn.opcode - 0x3B) as u16, value);
1537 }
1538 0x3F..=0x42 => {
1539 let value = pop(&mut stack)?;
1540 store_local(&mut locals, (insn.opcode - 0x3F) as u16, value);
1541 }
1542 0x43..=0x46 => {
1543 let value = pop(&mut stack)?;
1544 store_local(&mut locals, (insn.opcode - 0x43) as u16, value);
1545 }
1546 0x47..=0x4A => {
1547 let value = pop(&mut stack)?;
1548 store_local(&mut locals, (insn.opcode - 0x47) as u16, value);
1549 }
1550 0x4B..=0x4E => {
1551 let value = pop(&mut stack)?;
1552 store_local(&mut locals, (insn.opcode - 0x4B) as u16, value);
1553 }
1554 0x4F..=0x56 => {
1555 pop(&mut stack)?;
1556 pop(&mut stack)?;
1557 pop(&mut stack)?;
1558 }
1559 0x57 => {
1560 pop(&mut stack)?;
1561 }
1562 0x58 => {
1563 let v1 = pop(&mut stack)?;
1564 if is_category2(&v1) {
1565 return Err(ClassWriteError::FrameComputation(
1566 "pop2 category2".to_string(),
1567 ));
1568 }
1569 let v2 = pop(&mut stack)?;
1570 if is_category2(&v2) {
1571 return Err(ClassWriteError::FrameComputation(
1572 "pop2 invalid".to_string(),
1573 ));
1574 }
1575 }
1576 0x59 => {
1577 let v1 = pop(&mut stack)?;
1578 if is_category2(&v1) {
1579 return Err(ClassWriteError::FrameComputation(
1580 "dup category2".to_string(),
1581 ));
1582 }
1583 stack.push(v1.clone());
1584 stack.push(v1);
1585 }
1586 0x5A => {
1587 let v1 = pop(&mut stack)?;
1588 let v2 = pop(&mut stack)?;
1589 if is_category2(&v1) || is_category2(&v2) {
1590 return Err(ClassWriteError::FrameComputation("dup_x1".to_string()));
1591 }
1592 stack.push(v1.clone());
1593 stack.push(v2);
1594 stack.push(v1);
1595 }
1596 0x5B => {
1597 let v1 = pop(&mut stack)?;
1598 let v2 = pop(&mut stack)?;
1599 let v3 = pop(&mut stack)?;
1600 if is_category2(&v1) || is_category2(&v2) {
1601 return Err(ClassWriteError::FrameComputation("dup_x2".to_string()));
1602 }
1603 stack.push(v1.clone());
1604 stack.push(v3);
1605 stack.push(v2);
1606 stack.push(v1);
1607 }
1608 0x5C => {
1609 let v1 = pop(&mut stack)?;
1610 if is_category2(&v1) {
1611 stack.push(v1.clone());
1612 stack.push(v1);
1613 } else {
1614 let v2 = pop(&mut stack)?;
1615 if is_category2(&v2) {
1616 return Err(ClassWriteError::FrameComputation("dup2".to_string()));
1617 }
1618 stack.push(v2.clone());
1619 stack.push(v1.clone());
1620 stack.push(v2);
1621 stack.push(v1);
1622 }
1623 }
1624 0x5D => {
1625 let v1 = pop(&mut stack)?;
1626 if is_category2(&v1) {
1627 let v2 = pop(&mut stack)?;
1628 stack.push(v1.clone());
1629 stack.push(v2);
1630 stack.push(v1);
1631 } else {
1632 let v2 = pop(&mut stack)?;
1633 let v3 = pop(&mut stack)?;
1634 stack.push(v2.clone());
1635 stack.push(v1.clone());
1636 stack.push(v3);
1637 stack.push(v2);
1638 stack.push(v1);
1639 }
1640 }
1641 0x5E => {
1642 let v1 = pop(&mut stack)?;
1643 if is_category2(&v1) {
1644 let v2 = pop(&mut stack)?;
1645 let v3 = pop(&mut stack)?;
1646 stack.push(v1.clone());
1647 stack.push(v3);
1648 stack.push(v2);
1649 stack.push(v1);
1650 } else {
1651 let v2 = pop(&mut stack)?;
1652 let v3 = pop(&mut stack)?;
1653 let v4 = pop(&mut stack)?;
1654 stack.push(v2.clone());
1655 stack.push(v1.clone());
1656 stack.push(v4);
1657 stack.push(v3);
1658 stack.push(v2);
1659 stack.push(v1);
1660 }
1661 }
1662 0x5F => {
1663 let v1 = pop(&mut stack)?;
1664 let v2 = pop(&mut stack)?;
1665 if is_category2(&v1) || is_category2(&v2) {
1666 return Err(ClassWriteError::FrameComputation("swap".to_string()));
1667 }
1668 stack.push(v1);
1669 stack.push(v2);
1670 }
1671 0x60 | 0x64 | 0x68 | 0x6C | 0x70 | 0x78 | 0x7A | 0x7C | 0x7E | 0x80 | 0x82 => {
1672 pop(&mut stack)?;
1673 pop(&mut stack)?;
1674 stack.push(FrameType::Integer);
1675 }
1676 0x61 | 0x65 | 0x69 | 0x6D | 0x71 | 0x79 | 0x7B | 0x7D | 0x7F | 0x81 | 0x83 => {
1677 pop(&mut stack)?;
1678 pop(&mut stack)?;
1679 stack.push(FrameType::Long);
1680 }
1681 0x62 | 0x66 | 0x6A | 0x6E | 0x72 => {
1682 pop(&mut stack)?;
1683 pop(&mut stack)?;
1684 stack.push(FrameType::Float);
1685 }
1686 0x63 | 0x67 | 0x6B | 0x6F | 0x73 => {
1687 pop(&mut stack)?;
1688 pop(&mut stack)?;
1689 stack.push(FrameType::Double);
1690 }
1691 0x74 => {
1692 pop(&mut stack)?;
1693 stack.push(FrameType::Integer);
1694 }
1695 0x75 => {
1696 pop(&mut stack)?;
1697 stack.push(FrameType::Long);
1698 }
1699 0x76 => {
1700 pop(&mut stack)?;
1701 stack.push(FrameType::Float);
1702 }
1703 0x77 => {
1704 pop(&mut stack)?;
1705 stack.push(FrameType::Double);
1706 }
1707 0x84 => {}
1708 0x85 => {
1709 pop(&mut stack)?;
1710 stack.push(FrameType::Long);
1711 }
1712 0x86 => {
1713 pop(&mut stack)?;
1714 stack.push(FrameType::Float);
1715 }
1716 0x87 => {
1717 pop(&mut stack)?;
1718 stack.push(FrameType::Double);
1719 }
1720 0x88 => {
1721 pop(&mut stack)?;
1722 stack.push(FrameType::Integer);
1723 }
1724 0x89 => {
1725 pop(&mut stack)?;
1726 stack.push(FrameType::Float);
1727 }
1728 0x8A => {
1729 pop(&mut stack)?;
1730 stack.push(FrameType::Double);
1731 }
1732 0x8B => {
1733 pop(&mut stack)?;
1734 stack.push(FrameType::Integer);
1735 }
1736 0x8C => {
1737 pop(&mut stack)?;
1738 stack.push(FrameType::Long);
1739 }
1740 0x8D => {
1741 pop(&mut stack)?;
1742 stack.push(FrameType::Double);
1743 }
1744 0x8E => {
1745 pop(&mut stack)?;
1746 stack.push(FrameType::Integer);
1747 }
1748 0x8F => {
1749 pop(&mut stack)?;
1750 stack.push(FrameType::Long);
1751 }
1752 0x90 => {
1753 pop(&mut stack)?;
1754 stack.push(FrameType::Float);
1755 }
1756 0x91..=0x93 => {
1757 pop(&mut stack)?;
1758 stack.push(FrameType::Integer);
1759 }
1760 0x94..=0x98 => {
1761 pop(&mut stack)?;
1762 pop(&mut stack)?;
1763 stack.push(FrameType::Integer);
1764 }
1765 0x99..=0x9E | 0xC6 | 0xC7 => {
1766 pop(&mut stack)?;
1767 }
1768 0x9F..=0xA6 => {
1769 pop(&mut stack)?;
1770 pop(&mut stack)?;
1771 }
1772 0xA7 | 0xC8 => {}
1773 0xA8 | 0xA9 | 0xC9 => {
1774 return Err(ClassWriteError::FrameComputation(format!(
1775 "jsr/ret not supported at {}",
1776 insn.offset
1777 )));
1778 }
1779 0xAA | 0xAB => {
1780 pop(&mut stack)?;
1781 }
1782 0xAC => {
1783 pop(&mut stack)?;
1784 }
1785 0xAD => {
1786 pop(&mut stack)?;
1787 }
1788 0xAE => {
1789 pop(&mut stack)?;
1790 }
1791 0xAF => {
1792 pop(&mut stack)?;
1793 }
1794 0xB0 => {
1795 pop(&mut stack)?;
1796 }
1797 0xB1 => {}
1798 0xB2 => {
1799 let ty = field_type(insn, cp)?;
1800 stack.push(ty);
1801 }
1802 0xB3 => {
1803 pop(&mut stack)?;
1804 }
1805 0xB4 => {
1806 pop(&mut stack)?;
1807 let ty = field_type(insn, cp)?;
1808 stack.push(ty);
1809 }
1810 0xB5 => {
1811 pop(&mut stack)?;
1812 pop(&mut stack)?;
1813 }
1814 0xB6..=0xBA => {
1815 let (args, ret, owner, is_init) = method_type(insn, cp)?;
1816 for _ in 0..args.len() {
1817 pop(&mut stack)?;
1818 }
1819 if insn.opcode != 0xB8 && insn.opcode != 0xBA {
1820 let receiver = pop(&mut stack)?;
1821 if is_init {
1822 let init_owner = if receiver == FrameType::UninitializedThis {
1823 class_node.name.clone()
1824 } else {
1825 owner
1826 };
1827 initialize_uninitialized(&mut locals, &mut stack, receiver, init_owner);
1828 }
1829 }
1830 if let Some(ret) = ret {
1831 stack.push(ret);
1832 }
1833 }
1834 0xBB => {
1835 if let Operand::U2(_index) = insn.operand {
1836 stack.push(FrameType::Uninitialized(insn.offset));
1837 }
1838 }
1839 0xBC => {
1840 pop(&mut stack)?;
1841 if let Operand::U1(atype) = insn.operand {
1842 let desc = newarray_descriptor(atype)?;
1843 stack.push(FrameType::Object(desc));
1844 } else {
1845 stack.push(FrameType::Object("[I".to_string()));
1846 }
1847 }
1848 0xBD => {
1849 pop(&mut stack)?;
1850 if let Operand::U2(index) = insn.operand {
1851 let class_name = cp_class_name(cp, index)?;
1852 stack.push(FrameType::Object(format!("[L{class_name};")));
1853 }
1854 }
1855 0xBE => {
1856 pop(&mut stack)?;
1857 stack.push(FrameType::Integer);
1858 }
1859 0xBF => {
1860 pop(&mut stack)?;
1861 }
1862 0xC0 => {
1863 pop(&mut stack)?;
1864 if let Operand::U2(index) = insn.operand {
1865 let class_name = cp_class_name(cp, index)?;
1866 stack.push(FrameType::Object(class_name.to_string()));
1867 }
1868 }
1869 0xC1 => {
1870 pop(&mut stack)?;
1871 stack.push(FrameType::Integer);
1872 }
1873 0xC2 | 0xC3 => {
1874 pop(&mut stack)?;
1875 }
1876 0xC4 => {
1877 if let Operand::Wide {
1878 opcode,
1879 index,
1880 increment,
1881 } = insn.operand
1882 {
1883 match opcode {
1884 0x15..=0x19 => {
1885 if let Some(value) = locals.get(index as usize) {
1886 stack.push(value.clone());
1887 }
1888 }
1889 0x36..=0x3A => {
1890 let value = pop(&mut stack)?;
1891 store_local(&mut locals, index, value);
1892 }
1893 0x84 => {
1894 let _ = increment;
1895 }
1896 0xA9 => {}
1897 _ => {}
1898 }
1899 }
1900 }
1901 0xC5 => {
1902 if let Operand::MultiANewArray { dims, .. } = insn.operand {
1903 for _ in 0..dims {
1904 pop(&mut stack)?;
1905 }
1906 if let Operand::MultiANewArray { index, .. } = insn.operand {
1907 let desc = cp_class_name(cp, index)?;
1908 stack.push(FrameType::Object(desc.to_string()));
1909 } else {
1910 stack.push(FrameType::Object("[Ljava/lang/Object;".to_string()));
1911 }
1912 }
1913 }
1914 0xCA | 0xFE | 0xFF => {}
1915 _ => {}
1916 }
1917
1918 Ok(FrameState { locals, stack })
1919}
1920
1921fn initialize_uninitialized(
1922 locals: &mut [FrameType],
1923 stack: &mut [FrameType],
1924 receiver: FrameType,
1925 owner: String,
1926) {
1927 let init = FrameType::Object(owner);
1928 for value in locals.iter_mut().chain(stack.iter_mut()) {
1929 if *value == receiver {
1930 *value = init.clone();
1931 }
1932 }
1933}
1934
1935fn is_category2(value: &FrameType) -> bool {
1936 matches!(value, FrameType::Long | FrameType::Double)
1937}
1938
1939fn load_local(locals: &[FrameType], index: u16, fallback: FrameType) -> FrameType {
1940 locals.get(index as usize).cloned().unwrap_or(fallback)
1941}
1942
1943fn store_local(locals: &mut Vec<FrameType>, index: u16, value: FrameType) {
1944 let idx = index as usize;
1945 if locals.len() <= idx {
1946 locals.resize(idx + 1, FrameType::Top);
1947 }
1948 locals[idx] = value.clone();
1949 if is_category2(&value) {
1950 if locals.len() <= idx + 1 {
1951 locals.resize(idx + 2, FrameType::Top);
1952 }
1953 locals[idx + 1] = FrameType::Top;
1954 }
1955}
1956
1957fn array_element_type(value: &FrameType) -> Option<FrameType> {
1958 let FrameType::Object(desc) = value else {
1959 return None;
1960 };
1961 if !desc.starts_with('[') {
1962 return None;
1963 }
1964 let element = &desc[1..];
1965 if element.starts_with('[') {
1966 return Some(FrameType::Object(element.to_string()));
1967 }
1968 let mut chars = element.chars();
1969 match chars.next() {
1970 Some('L') => {
1971 let name = element
1972 .trim_start_matches('L')
1973 .trim_end_matches(';')
1974 .to_string();
1975 Some(FrameType::Object(name))
1976 }
1977 Some('Z') | Some('B') | Some('C') | Some('S') | Some('I') => Some(FrameType::Integer),
1978 Some('F') => Some(FrameType::Float),
1979 Some('J') => Some(FrameType::Long),
1980 Some('D') => Some(FrameType::Double),
1981 _ => None,
1982 }
1983}
1984
1985fn var_index(insn: &ParsedInstruction) -> Result<u16, ClassWriteError> {
1986 match insn.operand {
1987 Operand::U1(value) => Ok(value as u16),
1988 Operand::Wide { index, .. } => Ok(index),
1989 _ => Err(ClassWriteError::FrameComputation(format!(
1990 "missing var index at {}",
1991 insn.offset
1992 ))),
1993 }
1994}
1995
1996fn ldc_type(insn: &ParsedInstruction, cp: &[CpInfo]) -> Result<FrameType, ClassWriteError> {
1997 let index = match insn.operand {
1998 Operand::U1(value) => value as u16,
1999 Operand::U2(value) => value,
2000 _ => {
2001 return Err(ClassWriteError::FrameComputation(format!(
2002 "invalid ldc at {}",
2003 insn.offset
2004 )));
2005 }
2006 };
2007 match cp.get(index as usize) {
2008 Some(CpInfo::Integer(_)) => Ok(FrameType::Integer),
2009 Some(CpInfo::Float(_)) => Ok(FrameType::Float),
2010 Some(CpInfo::Long(_)) => Ok(FrameType::Long),
2011 Some(CpInfo::Double(_)) => Ok(FrameType::Double),
2012 Some(CpInfo::String { .. }) => Ok(FrameType::Object("java/lang/String".to_string())),
2013 Some(CpInfo::Class { .. }) => Ok(FrameType::Object("java/lang/Class".to_string())),
2014 Some(CpInfo::MethodType { .. }) => {
2015 Ok(FrameType::Object("java/lang/invoke/MethodType".to_string()))
2016 }
2017 Some(CpInfo::MethodHandle { .. }) => Ok(FrameType::Object(
2018 "java/lang/invoke/MethodHandle".to_string(),
2019 )),
2020 _ => Ok(FrameType::Top),
2021 }
2022}
2023
2024fn field_type(insn: &ParsedInstruction, cp: &[CpInfo]) -> Result<FrameType, ClassWriteError> {
2025 let index = match insn.operand {
2026 Operand::U2(value) => value,
2027 _ => {
2028 return Err(ClassWriteError::FrameComputation(format!(
2029 "invalid field operand at {}",
2030 insn.offset
2031 )));
2032 }
2033 };
2034 let descriptor = cp_field_descriptor(cp, index)?;
2035 let field_type = parse_field_descriptor(descriptor)?;
2036 Ok(field_type_to_frame(field_type))
2037}
2038
2039fn method_type(
2040 insn: &ParsedInstruction,
2041 cp: &[CpInfo],
2042) -> Result<(Vec<FieldType>, Option<FrameType>, String, bool), ClassWriteError> {
2043 let index = match insn.operand {
2044 Operand::U2(value) => value,
2045 Operand::InvokeInterface { index, .. } => index,
2046 Operand::InvokeDynamic { index } => index,
2047 _ => {
2048 return Err(ClassWriteError::FrameComputation(format!(
2049 "invalid method operand at {}",
2050 insn.offset
2051 )));
2052 }
2053 };
2054 let (owner, descriptor, name) = cp_method_descriptor(cp, index, insn.opcode)?;
2055 let (args, ret) = parse_method_descriptor(descriptor)?;
2056 let ret_frame = match ret {
2057 FieldType::Void => None,
2058 other => Some(field_type_to_frame(other)),
2059 };
2060 Ok((args, ret_frame, owner.to_string(), name == "<init>"))
2061}
2062
2063fn field_type_to_frame(field_type: FieldType) -> FrameType {
2064 match field_type {
2065 FieldType::Boolean
2066 | FieldType::Byte
2067 | FieldType::Char
2068 | FieldType::Short
2069 | FieldType::Int => FrameType::Integer,
2070 FieldType::Float => FrameType::Float,
2071 FieldType::Long => FrameType::Long,
2072 FieldType::Double => FrameType::Double,
2073 FieldType::Object(name) => FrameType::Object(name),
2074 FieldType::Array(desc) => FrameType::Object(desc),
2075 FieldType::Void => FrameType::Top,
2076 }
2077}
2078
2079fn cp_class_name(cp: &[CpInfo], index: u16) -> Result<&str, ClassWriteError> {
2080 match cp.get(index as usize) {
2081 Some(CpInfo::Class { name_index }) => match cp.get(*name_index as usize) {
2082 Some(CpInfo::Utf8(name)) => Ok(name),
2083 _ => Err(ClassWriteError::InvalidConstantPool),
2084 },
2085 _ => Err(ClassWriteError::InvalidConstantPool),
2086 }
2087}
2088
2089fn newarray_descriptor(atype: u8) -> Result<String, ClassWriteError> {
2090 let desc = match atype {
2091 4 => "[Z",
2092 5 => "[C",
2093 6 => "[F",
2094 7 => "[D",
2095 8 => "[B",
2096 9 => "[S",
2097 10 => "[I",
2098 11 => "[J",
2099 _ => {
2100 return Err(ClassWriteError::FrameComputation(
2101 "invalid newarray type".to_string(),
2102 ));
2103 }
2104 };
2105 Ok(desc.to_string())
2106}
2107
2108fn cp_field_descriptor(cp: &[CpInfo], index: u16) -> Result<&str, ClassWriteError> {
2109 match cp.get(index as usize) {
2110 Some(CpInfo::Fieldref {
2111 name_and_type_index,
2112 ..
2113 }) => match cp.get(*name_and_type_index as usize) {
2114 Some(CpInfo::NameAndType {
2115 descriptor_index, ..
2116 }) => match cp.get(*descriptor_index as usize) {
2117 Some(CpInfo::Utf8(desc)) => Ok(desc),
2118 _ => Err(ClassWriteError::InvalidConstantPool),
2119 },
2120 _ => Err(ClassWriteError::InvalidConstantPool),
2121 },
2122 _ => Err(ClassWriteError::InvalidConstantPool),
2123 }
2124}
2125
2126fn cp_method_descriptor(
2127 cp: &[CpInfo],
2128 index: u16,
2129 opcode: u8,
2130) -> Result<(&str, &str, &str), ClassWriteError> {
2131 match cp.get(index as usize) {
2132 Some(CpInfo::Methodref {
2133 class_index,
2134 name_and_type_index,
2135 })
2136 | Some(CpInfo::InterfaceMethodref {
2137 class_index,
2138 name_and_type_index,
2139 }) => {
2140 let owner = cp_class_name(cp, *class_index)?;
2141 match cp.get(*name_and_type_index as usize) {
2142 Some(CpInfo::NameAndType {
2143 name_index,
2144 descriptor_index,
2145 }) => {
2146 let name = cp_utf8(cp, *name_index)?;
2147 let desc = cp_utf8(cp, *descriptor_index)?;
2148 Ok((owner, desc, name))
2149 }
2150 _ => Err(ClassWriteError::InvalidConstantPool),
2151 }
2152 }
2153 Some(CpInfo::InvokeDynamic {
2154 name_and_type_index,
2155 ..
2156 }) if opcode == 0xBA => match cp.get(*name_and_type_index as usize) {
2157 Some(CpInfo::NameAndType {
2158 name_index,
2159 descriptor_index,
2160 }) => {
2161 let name = cp_utf8(cp, *name_index)?;
2162 let desc = cp_utf8(cp, *descriptor_index)?;
2163 Ok(("java/lang/Object", desc, name))
2164 }
2165 _ => Err(ClassWriteError::InvalidConstantPool),
2166 },
2167 _ => Err(ClassWriteError::InvalidConstantPool),
2168 }
2169}
2170
2171fn cp_utf8(cp: &[CpInfo], index: u16) -> Result<&str, ClassWriteError> {
2172 match cp.get(index as usize) {
2173 Some(CpInfo::Utf8(value)) => Ok(value.as_str()),
2174 _ => Err(ClassWriteError::InvalidConstantPool),
2175 }
2176}
2177
2178#[derive(Debug, Clone)]
2179enum FieldType {
2180 Boolean,
2181 Byte,
2182 Char,
2183 Short,
2184 Int,
2185 Float,
2186 Long,
2187 Double,
2188 Object(String),
2189 Array(String),
2190 Void,
2191}
2192
2193fn parse_field_descriptor(desc: &str) -> Result<FieldType, ClassWriteError> {
2194 let mut chars = desc.chars().peekable();
2195 parse_field_type(&mut chars)
2196}
2197
2198fn parse_method_descriptor(desc: &str) -> Result<(Vec<FieldType>, FieldType), ClassWriteError> {
2199 let mut chars = desc.chars().peekable();
2200 if chars.next() != Some('(') {
2201 return Err(ClassWriteError::FrameComputation(
2202 "bad method descriptor".to_string(),
2203 ));
2204 }
2205 let mut params = Vec::new();
2206 while let Some(&ch) = chars.peek() {
2207 if ch == ')' {
2208 chars.next();
2209 break;
2210 }
2211 params.push(parse_field_type(&mut chars)?);
2212 }
2213 let ret = parse_return_type(&mut chars)?;
2214 Ok((params, ret))
2215}
2216
2217fn parse_field_type<I>(chars: &mut std::iter::Peekable<I>) -> Result<FieldType, ClassWriteError>
2218where
2219 I: Iterator<Item = char>,
2220{
2221 match chars.next() {
2222 Some('Z') => Ok(FieldType::Boolean),
2223 Some('B') => Ok(FieldType::Byte),
2224 Some('C') => Ok(FieldType::Char),
2225 Some('S') => Ok(FieldType::Short),
2226 Some('I') => Ok(FieldType::Int),
2227 Some('F') => Ok(FieldType::Float),
2228 Some('J') => Ok(FieldType::Long),
2229 Some('D') => Ok(FieldType::Double),
2230 Some('L') => {
2231 let mut name = String::new();
2232 for ch in chars.by_ref() {
2233 if ch == ';' {
2234 break;
2235 }
2236 name.push(ch);
2237 }
2238 Ok(FieldType::Object(name))
2239 }
2240 Some('[') => {
2241 let mut desc = String::from("[");
2242 let inner = parse_field_type(chars)?;
2243 match inner {
2244 FieldType::Object(name) => {
2245 desc.push('L');
2246 desc.push_str(&name);
2247 desc.push(';');
2248 }
2249 FieldType::Boolean => desc.push('Z'),
2250 FieldType::Byte => desc.push('B'),
2251 FieldType::Char => desc.push('C'),
2252 FieldType::Short => desc.push('S'),
2253 FieldType::Int => desc.push('I'),
2254 FieldType::Float => desc.push('F'),
2255 FieldType::Long => desc.push('J'),
2256 FieldType::Double => desc.push('D'),
2257 FieldType::Void => {}
2258 FieldType::Array(inner_desc) => desc.push_str(&inner_desc),
2259 }
2260 Ok(FieldType::Array(desc))
2261 }
2262 _ => Err(ClassWriteError::FrameComputation(
2263 "bad field descriptor".to_string(),
2264 )),
2265 }
2266}
2267
2268fn parse_return_type<I>(chars: &mut std::iter::Peekable<I>) -> Result<FieldType, ClassWriteError>
2269where
2270 I: Iterator<Item = char>,
2271{
2272 match chars.peek() {
2273 Some('V') => {
2274 chars.next();
2275 Ok(FieldType::Void)
2276 }
2277 _ => parse_field_type(chars),
2278 }
2279}
2280
2281fn read_u1(code: &[u8], pos: &mut usize) -> Result<u8, ClassWriteError> {
2282 if *pos >= code.len() {
2283 return Err(ClassWriteError::FrameComputation(
2284 "unexpected eof".to_string(),
2285 ));
2286 }
2287 let value = code[*pos];
2288 *pos += 1;
2289 Ok(value)
2290}
2291
2292fn read_i1(code: &[u8], pos: &mut usize) -> Result<i8, ClassWriteError> {
2293 Ok(read_u1(code, pos)? as i8)
2294}
2295
2296fn read_u2(code: &[u8], pos: &mut usize) -> Result<u16, ClassWriteError> {
2297 if *pos + 2 > code.len() {
2298 return Err(ClassWriteError::FrameComputation(
2299 "unexpected eof".to_string(),
2300 ));
2301 }
2302 let value = u16::from_be_bytes([code[*pos], code[*pos + 1]]);
2303 *pos += 2;
2304 Ok(value)
2305}
2306
2307fn read_i2(code: &[u8], pos: &mut usize) -> Result<i16, ClassWriteError> {
2308 Ok(read_u2(code, pos)? as i16)
2309}
2310
2311fn read_i4(code: &[u8], pos: &mut usize) -> Result<i32, ClassWriteError> {
2312 if *pos + 4 > code.len() {
2313 return Err(ClassWriteError::FrameComputation(
2314 "unexpected eof".to_string(),
2315 ));
2316 }
2317 let value = i32::from_be_bytes([code[*pos], code[*pos + 1], code[*pos + 2], code[*pos + 3]]);
2318 *pos += 4;
2319 Ok(value)
2320}