1use rajac_ast::{
2 AstArena, AstType, EnumEntry, Expr as AstExpr, ExprId, Literal, LiteralKind, ParamId,
3 PrimitiveType, Stmt, StmtId, SwitchCase, SwitchLabel,
4};
5use rajac_base::result::RajacResult;
6use rajac_base::shared_string::SharedString;
7use rajac_symbols::{SymbolKind, SymbolTable};
8use rajac_types::{Ident, MethodId, Type, TypeArena, TypeId};
9use ristretto_classfile::ConstantPool;
10use ristretto_classfile::attributes::{
11 ArrayType as JvmArrayType, Instruction, LookupSwitch, TableSwitch,
12};
13
14#[derive(Clone, Debug)]
15pub struct UnsupportedFeature {
16 pub message: SharedString,
18 pub marker: SharedString,
20}
21
22pub struct BytecodeEmitter {
23 code_items: Vec<CodeItem>,
24}
25
26impl BytecodeEmitter {
27 pub fn new() -> Self {
28 Self {
29 code_items: Vec::new(),
30 }
31 }
32
33 fn emit(&mut self, instruction: Instruction) {
34 self.code_items.push(CodeItem::Instruction(instruction));
35 }
36
37 fn emit_branch(&mut self, kind: BranchKind, label: LabelId) {
38 self.code_items.push(CodeItem::Branch {
39 kind,
40 target: label,
41 });
42 }
43
44 fn bind_label(&mut self, label: LabelId) {
45 self.code_items.push(CodeItem::Label(label));
46 }
47
48 fn emit_switch(&mut self, switch: SwitchItem) {
49 self.code_items.push(CodeItem::Switch(switch));
50 }
51
52 fn is_empty(&self) -> bool {
53 self.code_items
54 .iter()
55 .all(|item| matches!(item, CodeItem::Label(_)))
56 }
57
58 fn last_instruction(&self) -> Option<&Instruction> {
59 self.code_items.iter().rev().find_map(|item| match item {
60 CodeItem::Instruction(instruction) => Some(instruction),
61 CodeItem::Branch { .. } | CodeItem::Switch(_) | CodeItem::Label(_) => None,
62 })
63 }
64
65 fn finalize(&self) -> Vec<Instruction> {
66 let mut offset = 0u16;
67 let mut labels = std::collections::HashMap::new();
68
69 for item in &self.code_items {
70 match item {
71 CodeItem::Instruction(_) | CodeItem::Branch { .. } => {
72 offset = offset.saturating_add(1);
73 }
74 CodeItem::Switch(_) => {
75 offset = offset.saturating_add(1);
76 }
77 CodeItem::Label(label) => {
78 labels.insert(*label, offset);
79 }
80 }
81 }
82
83 let mut instructions = Vec::new();
84 let terminal_offset = offset;
85 let needs_terminal_nop = self.code_items.iter().any(|item| match item {
93 CodeItem::Branch { target, .. } => labels.get(target).copied() == Some(terminal_offset),
94 CodeItem::Switch(switch) => switch
95 .targets()
96 .any(|target| labels.get(&target).copied() == Some(terminal_offset)),
97 CodeItem::Instruction(_) | CodeItem::Label(_) => false,
98 });
99
100 for item in &self.code_items {
101 match item {
102 CodeItem::Instruction(instruction) => instructions.push(instruction.clone()),
103 CodeItem::Branch { kind, target } => {
104 let offset = labels.get(target).copied().unwrap_or_default();
105 instructions.push(branch_instruction(*kind, offset));
106 }
107 CodeItem::Switch(switch) => {
108 instructions.push(switch_instruction(switch, &labels));
109 }
110 CodeItem::Label(_) => {}
111 }
112 }
113
114 if needs_terminal_nop {
115 instructions.push(Instruction::Nop);
116 }
117
118 instructions
119 }
120}
121
122impl Default for BytecodeEmitter {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128pub struct CodeGenerator<'arena> {
129 arena: &'arena AstArena,
130 type_arena: &'arena TypeArena,
131 symbol_table: &'arena SymbolTable,
132 constant_pool: &'arena mut ConstantPool,
133 emitter: BytecodeEmitter,
134 max_stack: u16,
135 current_stack: i32,
136 max_locals: u16,
137 next_local_slot: u16,
138 local_vars: std::collections::HashMap<String, LocalVar>,
139 current_class_internal_name: Option<SharedString>,
140 control_flow_stack: Vec<ControlFlowFrame>,
141 next_label_id: u32,
142 unsupported_features: Vec<UnsupportedFeature>,
143}
144
145impl<'arena> CodeGenerator<'arena> {
146 pub fn new(
147 arena: &'arena AstArena,
148 type_arena: &'arena TypeArena,
149 symbol_table: &'arena SymbolTable,
150 constant_pool: &'arena mut ConstantPool,
151 ) -> Self {
152 Self {
153 arena,
154 type_arena,
155 symbol_table,
156 constant_pool,
157 emitter: BytecodeEmitter::new(),
158 max_stack: 0,
159 current_stack: 0,
160 max_locals: 1,
161 next_local_slot: 1, local_vars: std::collections::HashMap::new(),
163 current_class_internal_name: None,
164 control_flow_stack: Vec::new(),
165 next_label_id: 0,
166 unsupported_features: Vec::new(),
167 }
168 }
169
170 pub fn set_current_class_internal_name(&mut self, internal_name: &str) {
171 self.current_class_internal_name = Some(SharedString::new(internal_name));
172 }
173
174 pub fn generate_method_body(
175 &mut self,
176 is_static: bool,
177 params: &[ParamId],
178 body_id: StmtId,
179 ) -> RajacResult<(Vec<Instruction>, u16, u16)> {
180 self.initialize_method_locals(is_static, params);
181
182 self.emit_statement(body_id)?;
183
184 if self.emulator().is_empty()
185 || !matches!(
186 self.emulator().last_instruction(),
187 Some(
188 Instruction::Return
189 | Instruction::Areturn
190 | Instruction::Athrow
191 | Instruction::Ireturn
192 | Instruction::Freturn
193 | Instruction::Dreturn
194 | Instruction::Lreturn
195 )
196 )
197 {
198 self.ensure_clean_stack("implicit return at end of method body")?;
199 self.emit(Instruction::Return);
200 }
201
202 Ok((self.emitter.finalize(), self.max_stack, self.max_locals))
203 }
204
205 pub fn generate_constructor_body(
206 &mut self,
207 params: &[ParamId],
208 body_id: Option<StmtId>,
209 super_internal_name: &str,
210 ) -> RajacResult<(Vec<Instruction>, u16, u16)> {
211 self.initialize_method_locals(false, params);
212
213 if let Some(body_id) = body_id {
214 if let Some((super_args, remaining_stmts)) = self.constructor_body_parts(body_id) {
215 self.emit_super_constructor_call(super_internal_name, &super_args)?;
216
217 for stmt_id in remaining_stmts {
218 self.emit_statement(stmt_id)?;
219 }
220 } else {
221 self.emit_super_constructor_call(super_internal_name, &[])?;
222 self.emit_statement(body_id)?;
223 }
224 } else {
225 self.emit_super_constructor_call(super_internal_name, &[])?;
226 }
227
228 if self.emulator().is_empty()
229 || !matches!(
230 self.emulator().last_instruction(),
231 Some(
232 Instruction::Return
233 | Instruction::Areturn
234 | Instruction::Athrow
235 | Instruction::Ireturn
236 | Instruction::Freturn
237 | Instruction::Dreturn
238 | Instruction::Lreturn
239 )
240 )
241 {
242 self.ensure_clean_stack("implicit return at end of constructor body")?;
243 self.emit(Instruction::Return);
244 }
245
246 Ok((self.emitter.finalize(), self.max_stack, self.max_locals))
247 }
248
249 pub fn generate_enum_constructor_body(
250 &mut self,
251 params: &[ParamId],
252 body_id: Option<StmtId>,
253 super_internal_name: &str,
254 ) -> RajacResult<(Vec<Instruction>, u16, u16)> {
255 self.initialize_enum_constructor_locals(params);
256
257 self.emit(Instruction::Aload_0);
258 self.emit(Instruction::Aload_1);
259 self.emit(Instruction::Iload_2);
260 let super_class = self.constant_pool.add_class(super_internal_name)?;
261 let super_ctor =
262 self.constant_pool
263 .add_method_ref(super_class, "<init>", "(Ljava/lang/String;I)V")?;
264 self.emit(Instruction::Invokespecial(super_ctor));
265 self.adjust_method_call_stack("(Ljava/lang/String;I)V", true);
266
267 if let Some(body_id) = body_id {
268 if let Some((_, remaining_stmts)) = self.constructor_body_parts(body_id) {
269 for stmt_id in remaining_stmts {
270 self.emit_statement(stmt_id)?;
271 }
272 } else {
273 self.emit_statement(body_id)?;
274 }
275 }
276
277 if self.emulator().is_empty()
278 || !matches!(
279 self.emulator().last_instruction(),
280 Some(
281 Instruction::Return
282 | Instruction::Areturn
283 | Instruction::Athrow
284 | Instruction::Ireturn
285 | Instruction::Freturn
286 | Instruction::Dreturn
287 | Instruction::Lreturn
288 )
289 )
290 {
291 self.ensure_clean_stack("implicit return at end of enum constructor body")?;
292 self.emit(Instruction::Return);
293 }
294
295 Ok((self.emitter.finalize(), self.max_stack, self.max_locals))
296 }
297
298 pub fn generate_enum_clinit_body(
299 &mut self,
300 this_internal_name: &str,
301 entries: &[EnumEntry],
302 constructor_descriptors: &[String],
303 ) -> RajacResult<(Vec<Instruction>, u16, u16)> {
304 self.initialize_method_locals(true, &[]);
305
306 let this_class = self.constant_pool.add_class(this_internal_name)?;
307 let values_array_descriptor = format!("[L{this_internal_name};");
308
309 for (ordinal, entry) in entries.iter().enumerate() {
310 self.emit(Instruction::New(this_class));
311 self.emit(Instruction::Dup);
312 self.emit_string_constant(entry.name.as_str())?;
313 self.emit_int_constant(ordinal as i32)?;
314 for arg in &entry.args {
315 self.emit_expression(*arg)?;
316 }
317 let ctor_ref = self.constant_pool.add_method_ref(
318 this_class,
319 "<init>",
320 &constructor_descriptors[ordinal],
321 )?;
322 self.emit(Instruction::Invokespecial(ctor_ref));
323 self.adjust_method_call_stack(&constructor_descriptors[ordinal], true);
324 let field_ref = self.constant_pool.add_field_ref(
325 this_class,
326 entry.name.as_str(),
327 &format!("L{this_internal_name};"),
328 )?;
329 self.emit(Instruction::Putstatic(field_ref));
330 }
331
332 self.emit_int_constant(entries.len() as i32)?;
333 self.emit(Instruction::Anewarray(this_class));
334 for (ordinal, entry) in entries.iter().enumerate() {
335 self.emit(Instruction::Dup);
336 self.emit_int_constant(ordinal as i32)?;
337 let field_ref = self.constant_pool.add_field_ref(
338 this_class,
339 entry.name.as_str(),
340 &format!("L{this_internal_name};"),
341 )?;
342 self.emit(Instruction::Getstatic(field_ref));
343 self.emit(Instruction::Aastore);
344 }
345 let values_field_ref =
346 self.constant_pool
347 .add_field_ref(this_class, "$VALUES", &values_array_descriptor)?;
348 self.emit(Instruction::Putstatic(values_field_ref));
349
350 self.ensure_clean_stack("implicit return at end of enum class initializer")?;
351 self.emit(Instruction::Return);
352
353 Ok((self.emitter.finalize(), self.max_stack, self.max_locals))
354 }
355
356 fn emit(&mut self, instruction: Instruction) {
357 let delta = stack_effect(&instruction);
358 self.current_stack = (self.current_stack + delta).max(0);
359 self.max_stack = self.max_stack.max(self.current_stack as u16);
360 self.emulator().emit(instruction);
361 }
362
363 fn emulator(&mut self) -> &mut BytecodeEmitter {
364 &mut self.emitter
365 }
366
367 fn new_label(&mut self) -> LabelId {
368 let label = LabelId(self.next_label_id);
369 self.next_label_id += 1;
370 label
371 }
372
373 pub fn take_unsupported_features(&mut self) -> Vec<UnsupportedFeature> {
374 std::mem::take(&mut self.unsupported_features)
375 }
376
377 fn bind_label(&mut self, label: LabelId) {
378 self.emitter.bind_label(label);
379 }
380
381 fn emit_branch(&mut self, kind: BranchKind, label: LabelId) {
382 let delta = stack_effect(&branch_instruction(kind, 0));
383 self.current_stack = (self.current_stack + delta).max(0);
384 self.max_stack = self.max_stack.max(self.current_stack as u16);
385 self.emitter.emit_branch(kind, label);
386 }
387
388 fn emit_switch_instruction(&mut self, switch: SwitchItem) {
389 self.current_stack = (self.current_stack - 1).max(0);
390 self.emitter.emit_switch(switch);
391 }
392
393 pub fn emit_statement(&mut self, stmt_id: StmtId) -> RajacResult<()> {
394 let stmt = self.arena.stmt(stmt_id);
395 match stmt {
396 Stmt::Empty => {}
397 Stmt::Block(stmts) => {
398 for &stmt_id in stmts {
399 self.emit_statement(stmt_id)?;
400 }
401 }
402 Stmt::Expr(expr_id) => {
403 let initial_stack = self.current_stack;
404 self.emit_expression(*expr_id)?;
405 self.discard_statement_expression_result(initial_stack)?;
406 }
407 Stmt::Return(None) => {
408 self.ensure_clean_stack("void return")?;
409 self.emit(Instruction::Return);
410 }
411 Stmt::Return(Some(expr_id)) => {
412 self.emit_expression(*expr_id)?;
413 let expr_ty = self.expression_type_id(*expr_id);
414 let expr_kind = self.kind_for_expr(*expr_id, expr_ty);
415 self.emit(self.return_instruction_for_kind(expr_kind));
416 self.ensure_clean_stack("value return")?;
417 }
418 Stmt::LocalVar {
419 ty,
420 name,
421 initializer,
422 } => self.emit_local_var_declaration(*ty, name, *initializer)?,
423 Stmt::If {
424 condition,
425 then_branch,
426 else_branch,
427 } => {
428 if let Some(else_branch) = else_branch {
429 let else_label = self.new_label();
430 let then_terminates = self.statement_terminates(*then_branch);
431
432 self.emit_false_branch_condition(*condition, else_label)?;
433 self.emit_statement(*then_branch)?;
434
435 if then_terminates {
436 self.current_stack = 0;
437 } else {
438 let end_label = self.new_label();
439 self.emit_branch(BranchKind::Goto, end_label);
440 self.current_stack = 0;
441 self.bind_label(else_label);
442 self.emit_statement(*else_branch)?;
443 self.bind_label(end_label);
444 return Ok(());
445 }
446
447 self.bind_label(else_label);
448 self.emit_statement(*else_branch)?;
449 } else {
450 let end_label = self.new_label();
451
452 self.emit_false_branch_condition(*condition, end_label)?;
453 self.emit_statement(*then_branch)?;
454 self.bind_label(end_label);
455 }
456 }
457 Stmt::While { condition, body } => {
458 self.emit_while_statement(*condition, *body, None)?;
459 }
460 Stmt::DoWhile { body, condition } => {
461 self.emit_do_while_statement(*body, *condition, None)?;
462 }
463 Stmt::For {
464 init,
465 condition,
466 update,
467 body,
468 } => {
469 self.emit_for_statement(init.clone(), *condition, *update, *body, None)?;
470 }
471 Stmt::Switch { expr, cases } => {
472 self.emit_switch_statement(*expr, cases)?;
473 }
474 Stmt::Break(label) => {
475 self.emit_break_statement(label.as_ref());
476 }
477 Stmt::Continue(label) => {
478 self.emit_continue_statement(label.as_ref());
479 }
480 Stmt::Label(name, body) => match self.arena.stmt(*body).clone() {
481 Stmt::While { condition, body } => {
482 self.emit_while_statement(condition, body, Some(name.clone()))?;
483 }
484 Stmt::DoWhile { body, condition } => {
485 self.emit_do_while_statement(body, condition, Some(name.clone()))?;
486 }
487 Stmt::For {
488 init,
489 condition,
490 update,
491 body,
492 } => {
493 self.emit_for_statement(init, condition, update, body, Some(name.clone()))?;
494 }
495 _ => {
496 let end_label = self.new_label();
497 self.push_control_flow(ControlFlowFrame {
498 label_name: Some(name.clone()),
499 break_label: end_label,
500 continue_label: None,
501 supports_unlabeled_break: false,
502 });
503 self.emit_statement(*body)?;
504 self.pop_control_flow();
505 self.bind_label(end_label);
506 }
507 },
508 Stmt::Throw(expr_id) => {
509 self.emit_expression(*expr_id)?;
510 self.emit(Instruction::Athrow);
511 }
512 Stmt::Try { .. } => {
513 self.emit_unsupported_feature("try statements", "try")?;
514 }
515 Stmt::Synchronized { .. } => {
516 self.emit_unsupported_feature("synchronized statements", "synchronized")?;
517 }
518 }
519 Ok(())
520 }
521
522 fn emit_while_statement(
523 &mut self,
524 condition: ExprId,
525 body: StmtId,
526 label_name: Option<Ident>,
527 ) -> RajacResult<()> {
528 let loop_head = self.new_label();
529 let loop_end = self.new_label();
530
531 self.push_control_flow(ControlFlowFrame {
532 label_name,
533 break_label: loop_end,
534 continue_label: Some(loop_head),
535 supports_unlabeled_break: true,
536 });
537
538 self.bind_label(loop_head);
539 self.emit_false_branch_condition(condition, loop_end)?;
540 self.emit_statement(body)?;
541
542 if !self.statement_terminates(body) {
543 self.emit_branch(BranchKind::Goto, loop_head);
544 self.current_stack = 0;
545 }
546
547 self.pop_control_flow();
548 self.bind_label(loop_end);
549 Ok(())
550 }
551
552 fn emit_do_while_statement(
553 &mut self,
554 body: StmtId,
555 condition: ExprId,
556 label_name: Option<Ident>,
557 ) -> RajacResult<()> {
558 let loop_body = self.new_label();
559 let loop_continue = self.new_label();
560 let loop_end = self.new_label();
561
562 self.push_control_flow(ControlFlowFrame {
563 label_name,
564 break_label: loop_end,
565 continue_label: Some(loop_continue),
566 supports_unlabeled_break: true,
567 });
568
569 self.bind_label(loop_body);
570 self.emit_statement(body)?;
571
572 if !self.statement_terminates(body) {
573 self.bind_label(loop_continue);
574 self.emit_true_branch_condition(condition, loop_body)?;
575 }
576
577 self.pop_control_flow();
578 self.bind_label(loop_end);
579 Ok(())
580 }
581
582 fn emit_for_statement(
583 &mut self,
584 init: Option<rajac_ast::ForInit>,
585 condition: Option<ExprId>,
586 update: Option<ExprId>,
587 body: StmtId,
588 label_name: Option<Ident>,
589 ) -> RajacResult<()> {
590 if let Some(init) = init.as_ref() {
591 self.emit_for_init(init)?;
592 }
593
594 let loop_head = self.new_label();
595 let loop_continue = self.new_label();
596 let loop_end = self.new_label();
597
598 self.push_control_flow(ControlFlowFrame {
599 label_name,
600 break_label: loop_end,
601 continue_label: Some(loop_continue),
602 supports_unlabeled_break: true,
603 });
604
605 self.bind_label(loop_head);
606
607 if let Some(condition) = condition {
608 self.emit_false_branch_condition(condition, loop_end)?;
609 }
610
611 self.emit_statement(body)?;
612
613 if !self.statement_terminates(body) {
614 self.bind_label(loop_continue);
615 if let Some(update) = update {
616 self.emit_expression(update)?;
617 }
618 self.emit_branch(BranchKind::Goto, loop_head);
619 self.current_stack = 0;
620 }
621
622 self.pop_control_flow();
623 self.bind_label(loop_end);
624 Ok(())
625 }
626
627 fn emit_switch_statement(&mut self, expr: ExprId, cases: &[SwitchCase]) -> RajacResult<()> {
628 let end_label = self.new_label();
629 let mut case_labels = Vec::with_capacity(cases.len());
630 let mut default_label = end_label;
631 let mut switch_pairs = Vec::new();
632
633 for case in cases {
634 let case_label = self.new_label();
635 case_labels.push(case_label);
636
637 for label in &case.labels {
638 match label {
639 SwitchLabel::Case(expr_id) => {
640 if let Some(value) = self.switch_case_value(*expr_id) {
641 switch_pairs.push((value, case_label));
642 }
643 }
644 SwitchLabel::Default => {
645 default_label = case_label;
646 }
647 }
648 }
649 }
650
651 switch_pairs.sort_by_key(|(value, _)| *value);
652
653 self.emit_expression(expr)?;
654 self.emit_switch_instruction(choose_switch_item(default_label, &switch_pairs));
655 self.push_control_flow(ControlFlowFrame {
656 label_name: None,
657 break_label: end_label,
658 continue_label: None,
659 supports_unlabeled_break: true,
660 });
661
662 for (case, case_label) in cases.iter().zip(case_labels.iter().copied()) {
663 self.bind_label(case_label);
664 for &stmt_id in &case.body {
665 self.emit_statement(stmt_id)?;
666 }
667 }
668
669 self.pop_control_flow();
670 self.bind_label(end_label);
671 Ok(())
672 }
673
674 fn emit_break_statement(&mut self, label: Option<&Ident>) {
675 if let Some(target) = self.resolve_break_target(label) {
676 self.emit_branch(BranchKind::Goto, target);
677 self.current_stack = 0;
678 }
679 }
680
681 fn emit_continue_statement(&mut self, label: Option<&Ident>) {
682 if let Some(target) = self.resolve_continue_target(label) {
683 self.emit_branch(BranchKind::Goto, target);
684 self.current_stack = 0;
685 }
686 }
687
688 fn push_control_flow(&mut self, frame: ControlFlowFrame) {
689 self.control_flow_stack.push(frame);
690 }
691
692 fn pop_control_flow(&mut self) {
693 let _ = self.control_flow_stack.pop();
694 }
695
696 fn resolve_break_target(&self, label: Option<&Ident>) -> Option<LabelId> {
697 match label {
698 Some(label) => self
699 .control_flow_stack
700 .iter()
701 .rev()
702 .find(|frame| {
703 frame
704 .label_name
705 .as_ref()
706 .is_some_and(|name| name.name == label.name)
707 })
708 .map(|frame| frame.break_label),
709 None => self
710 .control_flow_stack
711 .iter()
712 .rev()
713 .find(|frame| frame.supports_unlabeled_break)
714 .map(|frame| frame.break_label),
715 }
716 }
717
718 fn resolve_continue_target(&self, label: Option<&Ident>) -> Option<LabelId> {
719 match label {
720 Some(label) => self
721 .control_flow_stack
722 .iter()
723 .rev()
724 .find(|frame| {
725 frame.continue_label.is_some()
726 && frame
727 .label_name
728 .as_ref()
729 .is_some_and(|name| name.name == label.name)
730 })
731 .and_then(|frame| frame.continue_label),
732 None => self
733 .control_flow_stack
734 .iter()
735 .rev()
736 .find_map(|frame| frame.continue_label),
737 }
738 }
739
740 fn switch_case_value(&self, expr_id: ExprId) -> Option<i32> {
741 match self.arena.expr(expr_id) {
742 AstExpr::Literal(literal) => match literal.kind {
743 LiteralKind::Int => parse_int_literal(literal.value.as_str()),
744 LiteralKind::Char => {
745 parse_char_literal(literal.value.as_str()).map(|value| value as i32)
746 }
747 _ => None,
748 },
749 _ => None,
750 }
751 }
752
753 pub fn emit_expression(&mut self, expr_id: ExprId) -> RajacResult<()> {
754 let typed_expr = self.arena.expr_typed(expr_id);
755 let expr_ty = typed_expr.ty;
756 let expr_kind = self.kind_for_expr(expr_id, expr_ty);
757 let expr = &typed_expr.expr;
758 match expr {
759 AstExpr::Error => {}
760 AstExpr::Ident(ident) => {
761 if let Some(local) = self.local_vars.get(ident.as_str()) {
762 self.emit_load(local.slot, local.kind);
763 } else if let Some(field_id) = self.resolve_current_class_field_by_name(ident) {
764 self.emit_instance_field_load(field_id)?;
765 }
766 }
767 AstExpr::Literal(literal) => {
768 self.emit_literal(literal)?;
769 }
770 AstExpr::Unary { op, expr } => match op {
771 rajac_ast::UnaryOp::Minus => {
772 self.emit_expression(*expr)?;
773 self.emit(self.neg_instruction_for_kind(expr_kind));
774 }
775 rajac_ast::UnaryOp::Bang => {
776 self.emit_boolean_expression(expr_id)?;
777 }
778 _ => {
779 self.emit_expression(*expr)?;
780 }
781 },
782 AstExpr::Binary { op, lhs, rhs } => match op {
783 rajac_ast::BinaryOp::And => {
784 self.emit_logical_and(*lhs, *rhs)?;
785 }
786 rajac_ast::BinaryOp::Or => {
787 self.emit_logical_or(*lhs, *rhs)?;
788 }
789 _ => {
790 self.emit_binary_op(op, *lhs, *rhs, expr_kind)?;
791 }
792 },
793 AstExpr::Assign { lhs, rhs, .. } => {
794 self.emit_assignment(*lhs, *rhs)?;
795 }
796 AstExpr::Ternary {
797 condition,
798 then_expr,
799 else_expr,
800 } => {
801 let then_label = self.new_label();
802 let else_label = self.new_label();
803 let end_label = self.new_label();
804
805 self.emit_condition(*condition, then_label, else_label)?;
806 self.bind_label(then_label);
807 self.emit_expression(*then_expr)?;
808 self.emit_branch(BranchKind::Goto, end_label);
809 self.current_stack = 0;
810 self.bind_label(else_label);
811 self.emit_expression(*else_expr)?;
812 self.bind_label(end_label);
813 }
814 AstExpr::Cast { ty, expr } => {
815 self.emit_expression(*expr)?;
816 self.emit_cast(*ty)?;
817 }
818 AstExpr::InstanceOf { expr, ty } => {
819 self.emit_instanceof_expression(*expr, *ty)?;
820 }
821 AstExpr::FieldAccess {
822 expr,
823 name,
824 field_id,
825 } => {
826 self.emit_field_access(*expr, name, *field_id)?;
827 }
828 AstExpr::MethodCall {
829 expr,
830 name,
831 type_args: _,
832 args,
833 method_id,
834 ..
835 } => {
836 self.emit_method_call(expr.as_ref(), name, args, *method_id, expr_ty)?;
837 }
838 AstExpr::New { ty, args } => {
839 let class_name = type_id_to_internal_name(self.arena.ty(*ty).ty(), self.type_arena);
840 let class_index = self.constant_pool.add_class(&class_name)?;
841 self.emit(Instruction::New(class_index));
842 self.emit(Instruction::Dup);
843 for &arg in args {
844 self.emit_expression(arg)?;
845 }
846 let descriptor = method_descriptor_from_parts(
847 args.iter()
848 .map(|arg| self.expression_type_id(*arg))
849 .collect(),
850 self.symbol_table
851 .primitive_type_id("void")
852 .unwrap_or(TypeId::INVALID),
853 self.type_arena,
854 );
855 let constructor_ref =
856 self.constant_pool
857 .add_method_ref(class_index, "<init>", &descriptor)?;
858 self.emit(Instruction::Invokespecial(constructor_ref));
859 self.adjust_method_call_stack(&descriptor, true);
860 }
861 AstExpr::NewArray {
862 ty,
863 dimensions,
864 initializer,
865 } => {
866 self.emit_new_array_expression(*ty, dimensions, *initializer, expr_ty)?;
867 }
868 AstExpr::ArrayInitializer { .. } => {
869 self.emit_unsupported_feature("array initializer expressions", "{")?;
870 }
871 AstExpr::ArrayAccess { array, index } => {
872 self.emit_expression(*array)?;
873 self.emit_expression(*index)?;
874 self.emit(Instruction::Aaload);
875 }
876 AstExpr::ArrayLength { array } => {
877 self.emit_expression(*array)?;
878 self.emit(Instruction::Arraylength);
879 }
880 AstExpr::This(_) => {
881 self.emit(Instruction::Aload_0);
882 }
883 AstExpr::Super => {
884 self.emit(Instruction::Aload_0);
885 }
886 AstExpr::SuperCall {
887 name,
888 args,
889 method_id,
890 ..
891 } => {
892 self.emit_super_method_call(name, args, *method_id, expr_ty)?;
893 }
894 }
895 Ok(())
896 }
897
898 fn emit_unsupported_feature(&mut self, feature: &str, marker: &str) -> RajacResult<()> {
899 let message = format!("unsupported bytecode generation feature: {feature}");
900 self.unsupported_features.push(UnsupportedFeature {
901 message: SharedString::new(&message),
902 marker: SharedString::new(marker),
903 });
904
905 let exception_class = self
906 .constant_pool
907 .add_class("java/lang/UnsupportedOperationException")?;
908 let constructor_ref = self.constant_pool.add_method_ref(
909 exception_class,
910 "<init>",
911 "(Ljava/lang/String;)V",
912 )?;
913 let message_index = self.constant_pool.add_string(&message)?;
914
915 self.emit(Instruction::New(exception_class));
916 self.emit(Instruction::Dup);
917 self.emit_loadable_constant(message_index);
918 self.emit(Instruction::Invokespecial(constructor_ref));
919 self.adjust_method_call_stack("(Ljava/lang/String;)V", true);
920 self.emit(Instruction::Athrow);
921 Ok(())
922 }
923
924 fn emit_new_array_expression(
925 &mut self,
926 ast_type_id: rajac_ast::AstTypeId,
927 dimensions: &[ExprId],
928 initializer: Option<ExprId>,
929 expr_ty: TypeId,
930 ) -> RajacResult<()> {
931 if let Some(initializer) = initializer {
932 self.emit_array_initializer(expr_ty, initializer)?;
933 return Ok(());
934 }
935
936 if dimensions.is_empty() || expr_ty == TypeId::INVALID {
937 let _ = ast_type_id;
938 self.emit_unsupported_feature("array creation expressions", "new")?;
939 return Ok(());
940 }
941
942 for &dimension in dimensions {
943 self.emit_expression(dimension)?;
944 }
945
946 if dimensions.len() > 1 {
947 let array_descriptor = type_id_to_descriptor(expr_ty, self.type_arena);
948 let class_index = self.constant_pool.add_class(array_descriptor)?;
949 self.emit(Instruction::Multianewarray(
950 class_index,
951 dimensions.len() as u8,
952 ));
953 self.adjust_multianewarray_stack(dimensions.len() as i32);
954 return Ok(());
955 }
956
957 let component_type = array_component_type(expr_ty, self.type_arena);
958 if let Some(array_type) = primitive_array_type(component_type, self.type_arena) {
959 self.emit(Instruction::Newarray(array_type));
960 return Ok(());
961 }
962
963 let class_name = array_component_class_name(component_type, self.type_arena);
964 let class_index = self.constant_pool.add_class(class_name)?;
965 self.emit(Instruction::Anewarray(class_index));
966 Ok(())
967 }
968
969 fn emit_instanceof_expression(
970 &mut self,
971 expr: ExprId,
972 target_type: rajac_ast::AstTypeId,
973 ) -> RajacResult<()> {
974 let target_type_id = self.arena.ty(target_type).ty();
975 if target_type_id == TypeId::INVALID {
976 return self.emit_unsupported_feature("instanceof expressions", "instanceof");
977 }
978
979 self.emit_expression(expr)?;
980
981 let class_name = instanceof_target_class_name(target_type_id, self.type_arena);
982 let class_index = self.constant_pool.add_class(class_name)?;
983 self.emit(Instruction::Instanceof(class_index));
984 Ok(())
985 }
986
987 fn emit_array_initializer(&mut self, array_ty: TypeId, initializer: ExprId) -> RajacResult<()> {
988 let AstExpr::ArrayInitializer { elements } = self.arena.expr(initializer).clone() else {
989 self.emit_unsupported_feature("array initializer expressions", "{")?;
990 return Ok(());
991 };
992
993 self.emit_int_constant(elements.len() as i32)?;
994 self.emit_array_allocation(array_ty, 1)?;
995
996 let element_ty = array_component_type(array_ty, self.type_arena);
997 for (index, element_expr) in elements.into_iter().enumerate() {
998 self.emit(Instruction::Dup);
999 self.emit_int_constant(index as i32)?;
1000 self.emit_array_initializer_element(element_ty, element_expr)?;
1001 self.emit(array_store_instruction(element_ty, self.type_arena));
1002 }
1003
1004 Ok(())
1005 }
1006
1007 fn emit_array_initializer_element(
1008 &mut self,
1009 element_ty: TypeId,
1010 element_expr: ExprId,
1011 ) -> RajacResult<()> {
1012 match self.arena.expr(element_expr) {
1013 AstExpr::ArrayInitializer { .. } => {
1014 self.emit_array_initializer(element_ty, element_expr)
1015 }
1016 _ => self.emit_expression(element_expr),
1017 }
1018 }
1019
1020 fn emit_array_allocation(&mut self, array_ty: TypeId, dimensions: usize) -> RajacResult<()> {
1021 if dimensions > 1 {
1022 let array_descriptor = type_id_to_descriptor(array_ty, self.type_arena);
1023 let class_index = self.constant_pool.add_class(array_descriptor)?;
1024 self.emit(Instruction::Multianewarray(class_index, dimensions as u8));
1025 self.adjust_multianewarray_stack(dimensions as i32);
1026 return Ok(());
1027 }
1028
1029 let component_type = array_component_type(array_ty, self.type_arena);
1030 if let Some(array_type) = primitive_array_type(component_type, self.type_arena) {
1031 self.emit(Instruction::Newarray(array_type));
1032 return Ok(());
1033 }
1034
1035 let class_name = array_component_class_name(component_type, self.type_arena);
1036 let class_index = self.constant_pool.add_class(class_name)?;
1037 self.emit(Instruction::Anewarray(class_index));
1038 Ok(())
1039 }
1040
1041 fn emit_int_constant(&mut self, value: i32) -> RajacResult<()> {
1042 match value {
1043 -1 => self.emit(Instruction::Iconst_m1),
1044 0 => self.emit(Instruction::Iconst_0),
1045 1 => self.emit(Instruction::Iconst_1),
1046 2 => self.emit(Instruction::Iconst_2),
1047 3 => self.emit(Instruction::Iconst_3),
1048 4 => self.emit(Instruction::Iconst_4),
1049 5 => self.emit(Instruction::Iconst_5),
1050 -128..=127 => self.emit(Instruction::Bipush(value as i8)),
1051 -32768..=32767 => self.emit(Instruction::Sipush(value as i16)),
1052 _ => {
1053 let constant_index = self.constant_pool.add_integer(value)?;
1054 self.emit_loadable_constant(constant_index);
1055 }
1056 }
1057 Ok(())
1058 }
1059
1060 fn emit_literal(&mut self, literal: &Literal) -> RajacResult<()> {
1061 match literal.kind {
1062 LiteralKind::Int => {
1063 if let Some(value) = parse_int_literal(literal.value.as_str()) {
1064 match value {
1065 -1 => self.emit(Instruction::Iconst_m1),
1066 0 => self.emit(Instruction::Iconst_0),
1067 1 => self.emit(Instruction::Iconst_1),
1068 2 => self.emit(Instruction::Iconst_2),
1069 3 => self.emit(Instruction::Iconst_3),
1070 4 => self.emit(Instruction::Iconst_4),
1071 5 => self.emit(Instruction::Iconst_5),
1072 -128..=127 => self.emit(Instruction::Bipush(value as i8)),
1073 -32768..=32767 => self.emit(Instruction::Sipush(value as i16)),
1074 _ => {
1075 let constant_index = self.constant_pool.add_integer(value)?;
1076 self.emit_loadable_constant(constant_index);
1077 }
1078 }
1079 }
1080 }
1081 LiteralKind::Long => {
1082 if let Some(value) = parse_long_literal(literal.value.as_str()) {
1083 match value {
1084 0 => self.emit(Instruction::Lconst_0),
1085 1 => self.emit(Instruction::Lconst_1),
1086 _ => {
1087 let constant_index = self.constant_pool.add_long(value)?;
1088 self.emit(Instruction::Ldc2_w(constant_index));
1089 }
1090 }
1091 }
1092 }
1093 LiteralKind::Float => {
1094 if let Some(value) = parse_float_literal(literal.value.as_str()) {
1095 match value {
1096 0.0 => self.emit(Instruction::Fconst_0),
1097 1.0 => self.emit(Instruction::Fconst_1),
1098 2.0 => self.emit(Instruction::Fconst_2),
1099 _ => {
1100 let constant_index = self.constant_pool.add_float(value)?;
1101 self.emit_loadable_constant(constant_index);
1102 }
1103 }
1104 }
1105 }
1106 LiteralKind::Double => {
1107 if let Some(value) = parse_double_literal(literal.value.as_str()) {
1108 match value {
1109 0.0 => self.emit(Instruction::Dconst_0),
1110 1.0 => self.emit(Instruction::Dconst_1),
1111 _ => {
1112 let constant_index = self.constant_pool.add_double(value)?;
1113 self.emit(Instruction::Ldc2_w(constant_index));
1114 }
1115 }
1116 }
1117 }
1118 LiteralKind::Char => {
1119 if let Some(value) = parse_char_literal(literal.value.as_str()) {
1120 let code = value as i32;
1121 match code {
1122 0 => self.emit(Instruction::Iconst_0),
1123 1 => self.emit(Instruction::Iconst_1),
1124 2 => self.emit(Instruction::Iconst_2),
1125 3 => self.emit(Instruction::Iconst_3),
1126 4 => self.emit(Instruction::Iconst_4),
1127 5 => self.emit(Instruction::Iconst_5),
1128 -128..=127 => self.emit(Instruction::Bipush(code as i8)),
1129 -32768..=32767 => self.emit(Instruction::Sipush(code as i16)),
1130 _ => {
1131 let constant_index = self.constant_pool.add_integer(code)?;
1132 self.emit_loadable_constant(constant_index);
1133 }
1134 }
1135 }
1136 }
1137 LiteralKind::String => {
1138 let string_index = self.constant_pool.add_string(literal.value.as_str())?;
1139 if string_index <= u8::MAX as u16 {
1140 self.emit(Instruction::Ldc(string_index as u8));
1141 } else {
1142 self.emit(Instruction::Ldc_w(string_index));
1143 }
1144 }
1145 LiteralKind::Bool => {
1146 if literal.value.as_str() == "true" {
1147 self.emit(Instruction::Iconst_1);
1148 } else {
1149 self.emit(Instruction::Iconst_0);
1150 }
1151 }
1152 LiteralKind::Null => {
1153 self.emit(Instruction::Aconst_null);
1154 }
1155 }
1156 Ok(())
1157 }
1158
1159 fn emit_loadable_constant(&mut self, constant_index: u16) {
1160 if constant_index <= u8::MAX as u16 {
1161 self.emit(Instruction::Ldc(constant_index as u8));
1162 } else {
1163 self.emit(Instruction::Ldc_w(constant_index));
1164 }
1165 }
1166
1167 fn emit_string_constant(&mut self, value: &str) -> RajacResult<()> {
1168 let string_index = self.constant_pool.add_string(value)?;
1169 self.emit_loadable_constant(string_index);
1170 Ok(())
1171 }
1172
1173 fn emit_super_constructor_call(
1174 &mut self,
1175 super_internal_name: &str,
1176 args: &[ExprId],
1177 ) -> RajacResult<()> {
1178 self.emit(Instruction::Aload_0);
1179 let super_class = self.constant_pool.add_class(super_internal_name)?;
1180 let descriptor = method_descriptor_from_parts(
1181 args.iter()
1182 .map(|arg| self.expression_type_id(*arg))
1183 .collect(),
1184 self.symbol_table
1185 .primitive_type_id("void")
1186 .unwrap_or(TypeId::INVALID),
1187 self.symbol_table.type_arena(),
1188 );
1189 let invocation = ResolvedInvocation::special(
1190 SharedString::new(super_internal_name),
1191 SharedString::new("<init>"),
1192 SharedString::new(descriptor),
1193 );
1194 self.emit_invocation(&invocation, super_class, args)?;
1195 Ok(())
1196 }
1197
1198 fn emit_super_method_call(
1199 &mut self,
1200 name: &Ident,
1201 args: &[ExprId],
1202 method_id: Option<MethodId>,
1203 return_type: TypeId,
1204 ) -> RajacResult<()> {
1205 let Some(method_id) = method_id else {
1206 return self.emit_unsupported_feature("unresolved super method calls", "super");
1207 };
1208 let Some(mut invocation) =
1209 self.resolve_method_invocation(None, name, args, Some(method_id), return_type)?
1210 else {
1211 return self
1212 .emit_unsupported_feature("super method calls without resolved owners", "super");
1213 };
1214 invocation.kind = InvocationKind::Special;
1215 let owner_class = self
1216 .constant_pool
1217 .add_class(invocation.owner_internal_name.as_str())?;
1218 self.emit(Instruction::Aload_0);
1219 self.emit_invocation(&invocation, owner_class, args)?;
1220 Ok(())
1221 }
1222
1223 fn discard_statement_expression_result(&mut self, initial_stack: i32) -> RajacResult<()> {
1224 let leftover = self.current_stack - initial_stack;
1225 match leftover {
1226 0 => Ok(()),
1227 1 => {
1228 self.emit(Instruction::Pop);
1229 Ok(())
1230 }
1231 2 => {
1232 self.emit(Instruction::Pop2);
1233 Ok(())
1234 }
1235 _ => Err(rajac_base::err!(
1236 "internal bytecode error: statement expression left operand stack delta {}",
1237 leftover
1238 )),
1239 }
1240 }
1241
1242 fn emit_boolean_expression(&mut self, expr_id: ExprId) -> RajacResult<()> {
1243 let true_label = self.new_label();
1244 let false_label = self.new_label();
1245 let end_label = self.new_label();
1246
1247 self.emit_condition(expr_id, true_label, false_label)?;
1248 self.bind_label(true_label);
1249 self.emit(Instruction::Iconst_1);
1250 self.emit_branch(BranchKind::Goto, end_label);
1251 self.current_stack = 0;
1252 self.bind_label(false_label);
1253 self.emit(Instruction::Iconst_0);
1254 self.bind_label(end_label);
1255
1256 Ok(())
1257 }
1258
1259 fn emit_local_var_declaration(
1260 &mut self,
1261 ty: rajac_ast::AstTypeId,
1262 name: &Ident,
1263 initializer: Option<ExprId>,
1264 ) -> RajacResult<()> {
1265 let ty = self.arena.ty(ty);
1266 let kind = local_kind_from_ast_type(ty);
1267 let slot = self.allocate_local(kind);
1268
1269 self.local_vars
1270 .insert(name.as_str().to_string(), LocalVar { slot, kind });
1271
1272 if let Some(expr_id) = initializer {
1273 self.emit_expression(expr_id)?;
1274 self.emit_store(slot, kind);
1275 }
1276
1277 Ok(())
1278 }
1279
1280 fn emit_for_init(&mut self, init: &rajac_ast::ForInit) -> RajacResult<()> {
1281 match init {
1282 rajac_ast::ForInit::Expr(expr_id) => self.emit_expression(*expr_id),
1283 rajac_ast::ForInit::LocalVar {
1284 ty,
1285 name,
1286 initializer,
1287 } => self.emit_local_var_declaration(*ty, name, *initializer),
1288 }
1289 }
1290
1291 fn emit_assignment(&mut self, lhs: ExprId, rhs: ExprId) -> RajacResult<()> {
1292 match self.arena.expr(lhs) {
1293 AstExpr::Ident(ident) => {
1294 if let Some(local) = self.local_vars.get(ident.as_str()).copied() {
1295 self.emit_expression(rhs)?;
1296 self.emit_store(local.slot, local.kind);
1297 } else if let Some(field_id) = self.resolve_current_class_field_by_name(ident) {
1298 self.emit_instance_field_store(field_id, rhs)?;
1299 }
1300 Ok(())
1301 }
1302 AstExpr::FieldAccess {
1303 expr,
1304 field_id: Some(field_id),
1305 ..
1306 } => self.emit_field_assignment(*expr, *field_id, rhs),
1307 _ => Ok(()),
1308 }
1309 }
1310
1311 fn emit_field_assignment(
1312 &mut self,
1313 target: ExprId,
1314 field_id: rajac_types::FieldId,
1315 rhs: ExprId,
1316 ) -> RajacResult<()> {
1317 let Some((owner_internal_name, field)) = self.resolve_field_owner(field_id) else {
1318 return Ok(());
1319 };
1320
1321 let descriptor = type_id_to_descriptor(field.ty, self.type_arena);
1322 let owner_class = self.constant_pool.add_class(owner_internal_name.as_str())?;
1323 let field_ref =
1324 self.constant_pool
1325 .add_field_ref(owner_class, field.name.as_str(), &descriptor)?;
1326
1327 if field.modifiers.0 & rajac_types::FieldModifiers::STATIC != 0 {
1328 self.emit_expression(rhs)?;
1329 self.emit(Instruction::Putstatic(field_ref));
1330 } else {
1331 self.emit_expression(target)?;
1332 self.emit_expression(rhs)?;
1333 self.emit(Instruction::Putfield(field_ref));
1334 }
1335
1336 Ok(())
1337 }
1338
1339 fn emit_instance_field_store(
1340 &mut self,
1341 field_id: rajac_types::FieldId,
1342 rhs: ExprId,
1343 ) -> RajacResult<()> {
1344 let Some((owner_internal_name, field)) = self.resolve_field_owner(field_id) else {
1345 return Ok(());
1346 };
1347 let descriptor = type_id_to_descriptor(field.ty, self.type_arena);
1348 let owner_class = self.constant_pool.add_class(owner_internal_name.as_str())?;
1349 let field_ref =
1350 self.constant_pool
1351 .add_field_ref(owner_class, field.name.as_str(), &descriptor)?;
1352 self.emit(Instruction::Aload_0);
1353 self.emit_expression(rhs)?;
1354 self.emit(Instruction::Putfield(field_ref));
1355 Ok(())
1356 }
1357
1358 fn emit_instance_field_load(&mut self, field_id: rajac_types::FieldId) -> RajacResult<()> {
1359 let Some((owner_internal_name, field)) = self.resolve_field_owner(field_id) else {
1360 return Ok(());
1361 };
1362 let descriptor = type_id_to_descriptor(field.ty, self.type_arena);
1363 let owner_class = self.constant_pool.add_class(owner_internal_name.as_str())?;
1364 let field_ref =
1365 self.constant_pool
1366 .add_field_ref(owner_class, field.name.as_str(), &descriptor)?;
1367 self.emit(Instruction::Aload_0);
1368 self.emit(Instruction::Getfield(field_ref));
1369 Ok(())
1370 }
1371
1372 fn resolve_current_class_field_by_name(&self, ident: &Ident) -> Option<rajac_types::FieldId> {
1373 let current_class = self.current_class_internal_name.as_ref()?;
1374 let type_arena = self.symbol_table.type_arena();
1375
1376 for index in 0..type_arena.len() {
1377 let type_id = TypeId(index as u32);
1378 let Type::Class(class_type) = type_arena.get(type_id) else {
1379 continue;
1380 };
1381 if class_type.internal_name() != current_class.as_str() {
1382 continue;
1383 }
1384 return class_type
1385 .fields
1386 .get(&ident.name)
1387 .and_then(|field_ids| field_ids.first().copied());
1388 }
1389
1390 None
1391 }
1392
1393 fn resolve_field_owner(
1394 &self,
1395 field_id: rajac_types::FieldId,
1396 ) -> Option<(SharedString, rajac_types::FieldSignature)> {
1397 let type_arena = self.symbol_table.type_arena();
1398 for index in 0..type_arena.len() {
1399 let type_id = TypeId(index as u32);
1400 let Type::Class(class_type) = type_arena.get(type_id) else {
1401 continue;
1402 };
1403 if !class_type
1404 .fields
1405 .values()
1406 .any(|field_ids| field_ids.contains(&field_id))
1407 {
1408 continue;
1409 }
1410
1411 let owner_internal_name = SharedString::new(class_type.internal_name());
1412 let field = self.symbol_table.field_arena().get(field_id).clone();
1413 return Some((owner_internal_name, field));
1414 }
1415
1416 None
1417 }
1418
1419 fn ensure_clean_stack(&self, context: &str) -> RajacResult<()> {
1420 if self.current_stack == 0 {
1421 return Ok(());
1422 }
1423
1424 Err(rajac_base::err!(
1425 "internal bytecode error: operand stack depth {} at {}",
1426 self.current_stack,
1427 context
1428 ))
1429 }
1430
1431 fn emit_condition(
1432 &mut self,
1433 expr_id: ExprId,
1434 true_label: LabelId,
1435 false_label: LabelId,
1436 ) -> RajacResult<()> {
1437 let typed_expr = self.arena.expr_typed(expr_id);
1438 let expr = &typed_expr.expr;
1439
1440 match expr {
1441 AstExpr::Literal(literal) if matches!(literal.kind, LiteralKind::Bool) => {
1442 if literal.value.as_str() == "true" {
1443 self.emit_branch(BranchKind::Goto, true_label);
1444 } else {
1445 self.emit_branch(BranchKind::Goto, false_label);
1446 }
1447 }
1448 AstExpr::Unary {
1449 op: rajac_ast::UnaryOp::Bang,
1450 expr,
1451 } => {
1452 self.emit_condition(*expr, false_label, true_label)?;
1453 }
1454 AstExpr::Binary {
1455 op: rajac_ast::BinaryOp::And,
1456 lhs,
1457 rhs,
1458 } => {
1459 let rhs_label = self.new_label();
1460 self.emit_condition(*lhs, rhs_label, false_label)?;
1461 self.bind_label(rhs_label);
1462 self.emit_condition(*rhs, true_label, false_label)?;
1463 }
1464 AstExpr::Binary {
1465 op: rajac_ast::BinaryOp::Or,
1466 lhs,
1467 rhs,
1468 } => {
1469 let rhs_label = self.new_label();
1470 self.emit_condition(*lhs, true_label, rhs_label)?;
1471 self.bind_label(rhs_label);
1472 self.emit_condition(*rhs, true_label, false_label)?;
1473 }
1474 AstExpr::Binary { op, lhs, rhs }
1475 if matches!(
1476 op,
1477 rajac_ast::BinaryOp::EqEq
1478 | rajac_ast::BinaryOp::BangEq
1479 | rajac_ast::BinaryOp::Lt
1480 | rajac_ast::BinaryOp::LtEq
1481 | rajac_ast::BinaryOp::Gt
1482 | rajac_ast::BinaryOp::GtEq
1483 ) =>
1484 {
1485 self.emit_comparison_condition(op.clone(), *lhs, *rhs, true_label, false_label)?;
1486 }
1487 _ => {
1488 self.emit_expression(expr_id)?;
1489 self.emit_branch(BranchKind::IfNe, true_label);
1490 self.emit_branch(BranchKind::Goto, false_label);
1491 }
1492 }
1493
1494 Ok(())
1495 }
1496
1497 fn emit_false_branch_condition(
1498 &mut self,
1499 expr_id: ExprId,
1500 false_label: LabelId,
1501 ) -> RajacResult<()> {
1502 let typed_expr = self.arena.expr_typed(expr_id);
1503 let expr = &typed_expr.expr;
1504
1505 match expr {
1506 AstExpr::Literal(literal) if matches!(literal.kind, LiteralKind::Bool) => {
1507 if literal.value.as_str() == "false" {
1508 self.emit_branch(BranchKind::Goto, false_label);
1509 }
1510 }
1511 AstExpr::Binary { op, lhs, rhs }
1512 if matches!(
1513 op,
1514 rajac_ast::BinaryOp::EqEq
1515 | rajac_ast::BinaryOp::BangEq
1516 | rajac_ast::BinaryOp::Lt
1517 | rajac_ast::BinaryOp::LtEq
1518 | rajac_ast::BinaryOp::Gt
1519 | rajac_ast::BinaryOp::GtEq
1520 ) =>
1521 {
1522 self.emit_comparison_false_branch(op.clone(), *lhs, *rhs, false_label)?;
1523 }
1524 _ => {
1525 self.emit_expression(expr_id)?;
1526 self.emit_branch(BranchKind::IfEq, false_label);
1527 }
1528 }
1529
1530 Ok(())
1531 }
1532
1533 fn emit_true_branch_condition(
1534 &mut self,
1535 expr_id: ExprId,
1536 true_label: LabelId,
1537 ) -> RajacResult<()> {
1538 let typed_expr = self.arena.expr_typed(expr_id);
1539 let expr = &typed_expr.expr;
1540
1541 match expr {
1542 AstExpr::Literal(literal) if matches!(literal.kind, LiteralKind::Bool) => {
1543 if literal.value.as_str() == "true" {
1544 self.emit_branch(BranchKind::Goto, true_label);
1545 }
1546 }
1547 AstExpr::Unary {
1548 op: rajac_ast::UnaryOp::Bang,
1549 expr,
1550 } => {
1551 let false_label = self.new_label();
1552 self.emit_condition(*expr, false_label, true_label)?;
1553 self.bind_label(false_label);
1554 }
1555 AstExpr::Binary { op, lhs, rhs }
1556 if matches!(
1557 op,
1558 rajac_ast::BinaryOp::EqEq
1559 | rajac_ast::BinaryOp::BangEq
1560 | rajac_ast::BinaryOp::Lt
1561 | rajac_ast::BinaryOp::LtEq
1562 | rajac_ast::BinaryOp::Gt
1563 | rajac_ast::BinaryOp::GtEq
1564 ) =>
1565 {
1566 self.emit_expression(*lhs)?;
1567 self.emit_expression(*rhs)?;
1568
1569 let lhs_kind = self.kind_for_expr(*lhs, self.arena.expr_typed(*lhs).ty);
1570 let rhs_kind = self.kind_for_expr(*rhs, self.arena.expr_typed(*rhs).ty);
1571 let comparison_kind = promote_numeric_kind(lhs_kind, rhs_kind);
1572
1573 match comparison_kind {
1574 LocalVarKind::Long => {
1575 self.emit(Instruction::Lcmp);
1576 self.emit_branch(branch_kind_for_zero_compare(op.clone()), true_label);
1577 }
1578 LocalVarKind::Float => {
1579 self.emit(Instruction::Fcmpl);
1580 self.emit_branch(branch_kind_for_zero_compare(op.clone()), true_label);
1581 }
1582 LocalVarKind::Double => {
1583 self.emit(Instruction::Dcmpl);
1584 self.emit_branch(branch_kind_for_zero_compare(op.clone()), true_label);
1585 }
1586 LocalVarKind::IntLike => {
1587 self.emit_branch(branch_kind_for_int_compare(op.clone()), true_label);
1588 }
1589 LocalVarKind::Reference => {
1590 self.emit_branch(reference_branch_kind(op.clone()), true_label);
1591 }
1592 }
1593 }
1594 _ => {
1595 self.emit_expression(expr_id)?;
1596 self.emit_branch(BranchKind::IfNe, true_label);
1597 }
1598 }
1599
1600 self.current_stack = 0;
1601 Ok(())
1602 }
1603
1604 fn emit_comparison_condition(
1605 &mut self,
1606 op: rajac_ast::BinaryOp,
1607 lhs: ExprId,
1608 rhs: ExprId,
1609 true_label: LabelId,
1610 false_label: LabelId,
1611 ) -> RajacResult<()> {
1612 let lhs_kind = self.kind_for_expr(lhs, self.arena.expr_typed(lhs).ty);
1613 let rhs_kind = self.kind_for_expr(rhs, self.arena.expr_typed(rhs).ty);
1614 let comparison_kind = promote_numeric_kind(lhs_kind, rhs_kind);
1615 let lhs_is_null = is_null_literal(self.arena.expr(lhs));
1616 let rhs_is_null = is_null_literal(self.arena.expr(rhs));
1617
1618 if matches!(lhs_kind, LocalVarKind::Reference)
1619 || matches!(rhs_kind, LocalVarKind::Reference)
1620 {
1621 if rhs_is_null {
1622 self.emit_expression(lhs)?;
1623 self.emit_branch(
1624 if matches!(op, rajac_ast::BinaryOp::EqEq) {
1625 BranchKind::IfNull
1626 } else {
1627 BranchKind::IfNonNull
1628 },
1629 true_label,
1630 );
1631 } else if lhs_is_null {
1632 self.emit_expression(rhs)?;
1633 self.emit_branch(
1634 if matches!(op, rajac_ast::BinaryOp::EqEq) {
1635 BranchKind::IfNull
1636 } else {
1637 BranchKind::IfNonNull
1638 },
1639 true_label,
1640 );
1641 } else {
1642 self.emit_expression(lhs)?;
1643 self.emit_expression(rhs)?;
1644 self.emit_branch(
1645 match op {
1646 rajac_ast::BinaryOp::EqEq => BranchKind::IfAcmpEq,
1647 rajac_ast::BinaryOp::BangEq => BranchKind::IfAcmpNe,
1648 _ => unreachable!(),
1649 },
1650 true_label,
1651 );
1652 }
1653
1654 self.emit_branch(BranchKind::Goto, false_label);
1655 return Ok(());
1656 }
1657
1658 self.emit_expression(lhs)?;
1659 self.emit_expression(rhs)?;
1660
1661 match comparison_kind {
1662 LocalVarKind::Long => {
1663 self.emit(Instruction::Lcmp);
1664 self.emit_branch(branch_kind_for_zero_compare(op), true_label);
1665 }
1666 LocalVarKind::Float => {
1667 self.emit(Instruction::Fcmpl);
1668 self.emit_branch(branch_kind_for_zero_compare(op), true_label);
1669 }
1670 LocalVarKind::Double => {
1671 self.emit(Instruction::Dcmpl);
1672 self.emit_branch(branch_kind_for_zero_compare(op), true_label);
1673 }
1674 LocalVarKind::IntLike => {
1675 self.emit_branch(branch_kind_for_int_compare(op), true_label);
1676 }
1677 LocalVarKind::Reference => unreachable!(),
1678 }
1679
1680 self.emit_branch(BranchKind::Goto, false_label);
1681 Ok(())
1682 }
1683
1684 fn emit_comparison_false_branch(
1685 &mut self,
1686 op: rajac_ast::BinaryOp,
1687 lhs: ExprId,
1688 rhs: ExprId,
1689 false_label: LabelId,
1690 ) -> RajacResult<()> {
1691 let lhs_kind = self.kind_for_expr(lhs, self.arena.expr_typed(lhs).ty);
1692 let rhs_kind = self.kind_for_expr(rhs, self.arena.expr_typed(rhs).ty);
1693 let comparison_kind = promote_numeric_kind(lhs_kind, rhs_kind);
1694 let lhs_is_null = is_null_literal(self.arena.expr(lhs));
1695 let rhs_is_null = is_null_literal(self.arena.expr(rhs));
1696
1697 if matches!(lhs_kind, LocalVarKind::Reference)
1698 || matches!(rhs_kind, LocalVarKind::Reference)
1699 {
1700 if rhs_is_null {
1701 self.emit_expression(lhs)?;
1702 self.emit_branch(inverse_null_branch_kind(op), false_label);
1703 } else if lhs_is_null {
1704 self.emit_expression(rhs)?;
1705 self.emit_branch(inverse_null_branch_kind(op), false_label);
1706 } else {
1707 self.emit_expression(lhs)?;
1708 self.emit_expression(rhs)?;
1709 self.emit_branch(inverse_reference_branch_kind(op), false_label);
1710 }
1711 return Ok(());
1712 }
1713
1714 self.emit_expression(lhs)?;
1715 self.emit_expression(rhs)?;
1716
1717 match comparison_kind {
1718 LocalVarKind::Long => {
1719 self.emit(Instruction::Lcmp);
1720 self.emit_branch(inverse_zero_compare_branch_kind(op), false_label);
1721 }
1722 LocalVarKind::Float => {
1723 self.emit(Instruction::Fcmpl);
1724 self.emit_branch(inverse_zero_compare_branch_kind(op), false_label);
1725 }
1726 LocalVarKind::Double => {
1727 self.emit(Instruction::Dcmpl);
1728 self.emit_branch(inverse_zero_compare_branch_kind(op), false_label);
1729 }
1730 LocalVarKind::IntLike => {
1731 self.emit_branch(inverse_int_compare_branch_kind(op), false_label);
1732 }
1733 LocalVarKind::Reference => unreachable!(),
1734 }
1735
1736 Ok(())
1737 }
1738
1739 fn statement_terminates(&self, stmt_id: StmtId) -> bool {
1740 match self.arena.stmt(stmt_id) {
1741 Stmt::Return(_) | Stmt::Throw(_) => true,
1742 Stmt::Block(stmts) => stmts
1743 .last()
1744 .copied()
1745 .is_some_and(|last_stmt| self.statement_terminates(last_stmt)),
1746 Stmt::If {
1747 then_branch,
1748 else_branch,
1749 ..
1750 } => else_branch.as_ref().is_some_and(|else_branch| {
1751 self.statement_terminates(*then_branch) && self.statement_terminates(*else_branch)
1752 }),
1753 _ => false,
1754 }
1755 }
1756
1757 fn emit_binary_op(
1758 &mut self,
1759 op: &rajac_ast::BinaryOp,
1760 lhs: ExprId,
1761 rhs: ExprId,
1762 result_kind: LocalVarKind,
1763 ) -> RajacResult<()> {
1764 use rajac_ast::BinaryOp;
1765
1766 if matches!(op, BinaryOp::Add) {
1769 let lhs_expr = self.arena.expr(lhs);
1770 let rhs_expr = self.arena.expr(rhs);
1771
1772 let lhs_string = match lhs_expr {
1773 AstExpr::Literal(lit) if matches!(lit.kind, LiteralKind::String) => {
1774 Some(lit.value.as_str().to_string())
1775 }
1776 _ => None,
1777 };
1778 let rhs_string = match rhs_expr {
1779 AstExpr::Literal(lit) if matches!(lit.kind, LiteralKind::String) => {
1780 Some(lit.value.as_str().to_string())
1781 }
1782 _ => None,
1783 };
1784 let lhs_int = match lhs_expr {
1785 AstExpr::Literal(lit) if matches!(lit.kind, LiteralKind::Int) => {
1786 lit.value.as_str().parse::<i32>().ok()
1787 }
1788 _ => None,
1789 };
1790 let rhs_int = match rhs_expr {
1791 AstExpr::Literal(lit) if matches!(lit.kind, LiteralKind::Int) => {
1792 lit.value.as_str().parse::<i32>().ok()
1793 }
1794 _ => None,
1795 };
1796
1797 if let (Some(lhs_lit), Some(rhs_lit)) = (&lhs_string, &rhs_string) {
1800 let result = format!("{}{}", lhs_lit, rhs_lit);
1801 let string_index = self.constant_pool.add_string(&result)?;
1802 if string_index <= u8::MAX as u16 {
1803 self.emit(Instruction::Ldc(string_index as u8));
1804 } else {
1805 self.emit(Instruction::Ldc_w(string_index));
1806 }
1807 return Ok(());
1808 }
1809 if let (Some(lhs_lit), Some(rhs_int)) = (&lhs_string, rhs_int) {
1810 let result = format!("{}{}", lhs_lit, rhs_int);
1811 let string_index = self.constant_pool.add_string(&result)?;
1812 if string_index <= u8::MAX as u16 {
1813 self.emit(Instruction::Ldc(string_index as u8));
1814 } else {
1815 self.emit(Instruction::Ldc_w(string_index));
1816 }
1817 return Ok(());
1818 }
1819 if let (Some(rhs_lit), Some(lhs_int)) = (&rhs_string, lhs_int) {
1820 let result = format!("{}{}", lhs_int, rhs_lit);
1821 let string_index = self.constant_pool.add_string(&result)?;
1822 if string_index <= u8::MAX as u16 {
1823 self.emit(Instruction::Ldc(string_index as u8));
1824 } else {
1825 self.emit(Instruction::Ldc_w(string_index));
1826 }
1827 return Ok(());
1828 }
1829
1830 if lhs_string.is_some() || rhs_string.is_some() {
1833 self.emit_expression(lhs)?;
1835 self.emit_expression(rhs)?;
1836
1837 let invokedynamic = self.constant_pool.add_invoke_dynamic(
1840 0,
1841 "makeConcatWithConstants",
1842 "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
1843 )?;
1844 self.emit(Instruction::Invokedynamic(invokedynamic));
1845
1846 return Ok(());
1847 }
1848 }
1849
1850 match op {
1851 BinaryOp::Add => {
1852 self.emit_expression(lhs)?;
1853 self.emit_expression(rhs)?;
1854 self.emit(self.arithmetic_instruction(result_kind, ArithmeticOp::Add));
1855 }
1856 BinaryOp::Sub => {
1857 self.emit_expression(lhs)?;
1858 self.emit_expression(rhs)?;
1859 self.emit(self.arithmetic_instruction(result_kind, ArithmeticOp::Sub));
1860 }
1861 BinaryOp::Mul => {
1862 self.emit_expression(lhs)?;
1863 self.emit_expression(rhs)?;
1864 self.emit(self.arithmetic_instruction(result_kind, ArithmeticOp::Mul));
1865 }
1866 BinaryOp::Div => {
1867 self.emit_expression(lhs)?;
1868 self.emit_expression(rhs)?;
1869 self.emit(self.arithmetic_instruction(result_kind, ArithmeticOp::Div));
1870 }
1871 BinaryOp::Mod => {
1872 self.emit_expression(lhs)?;
1873 self.emit_expression(rhs)?;
1874 self.emit(self.arithmetic_instruction(result_kind, ArithmeticOp::Rem));
1875 }
1876 BinaryOp::BitAnd => {
1877 self.emit_expression(lhs)?;
1878 self.emit_expression(rhs)?;
1879 self.emit(self.bitwise_instruction(result_kind, BitwiseOp::And));
1880 }
1881 BinaryOp::BitOr => {
1882 self.emit_expression(lhs)?;
1883 self.emit_expression(rhs)?;
1884 self.emit(self.bitwise_instruction(result_kind, BitwiseOp::Or));
1885 }
1886 BinaryOp::BitXor => {
1887 self.emit_expression(lhs)?;
1888 self.emit_expression(rhs)?;
1889 self.emit(self.bitwise_instruction(result_kind, BitwiseOp::Xor));
1890 }
1891 BinaryOp::LShift => {
1892 self.emit_expression(lhs)?;
1893 self.emit_expression(rhs)?;
1894 self.emit(self.shift_instruction(result_kind, ShiftOp::Left));
1895 }
1896 BinaryOp::RShift => {
1897 self.emit_expression(lhs)?;
1898 self.emit_expression(rhs)?;
1899 self.emit(self.shift_instruction(result_kind, ShiftOp::Right));
1900 }
1901 BinaryOp::ARShift => {
1902 self.emit_expression(lhs)?;
1903 self.emit_expression(rhs)?;
1904 self.emit(self.shift_instruction(result_kind, ShiftOp::UnsignedRight));
1905 }
1906 BinaryOp::Lt
1907 | BinaryOp::LtEq
1908 | BinaryOp::Gt
1909 | BinaryOp::GtEq
1910 | BinaryOp::EqEq
1911 | BinaryOp::BangEq => {
1912 self.emit_boolean_expression_expr(op.clone(), lhs, rhs)?;
1913 }
1914 BinaryOp::And | BinaryOp::Or => {
1915 self.emit_expression(lhs)?;
1916 self.emit_expression(rhs)?;
1917 self.emit(self.bitwise_instruction(result_kind, BitwiseOp::And));
1918 }
1919 }
1920 Ok(())
1921 }
1922
1923 fn emit_boolean_expression_expr(
1924 &mut self,
1925 op: rajac_ast::BinaryOp,
1926 lhs: ExprId,
1927 rhs: ExprId,
1928 ) -> RajacResult<()> {
1929 let true_label = self.new_label();
1930 let false_label = self.new_label();
1931 let end_label = self.new_label();
1932
1933 self.emit_comparison_false_branch(op, lhs, rhs, false_label)?;
1934 self.bind_label(true_label);
1935 self.emit(Instruction::Iconst_1);
1936 self.emit_branch(BranchKind::Goto, end_label);
1937 self.current_stack = 0;
1938 self.bind_label(false_label);
1939 self.emit(Instruction::Iconst_0);
1940 self.bind_label(end_label);
1941
1942 Ok(())
1943 }
1944
1945 fn emit_cast(&mut self, target_ty: rajac_ast::AstTypeId) -> RajacResult<()> {
1946 let target = self.arena.ty(target_ty);
1947 match target {
1948 AstType::Primitive {
1949 kind: PrimitiveType::Byte,
1950 ty: _,
1951 } => {
1952 self.emit(Instruction::I2b);
1953 }
1954 AstType::Primitive {
1955 kind: PrimitiveType::Char,
1956 ty: _,
1957 } => {
1958 self.emit(Instruction::I2c);
1959 }
1960 AstType::Primitive {
1961 kind: PrimitiveType::Short,
1962 ty: _,
1963 } => {
1964 self.emit(Instruction::I2s);
1965 }
1966 AstType::Primitive {
1967 kind: PrimitiveType::Long,
1968 ty: _,
1969 } => {
1970 self.emit(Instruction::I2l);
1971 }
1972 AstType::Primitive {
1973 kind: PrimitiveType::Float,
1974 ty: _,
1975 } => {
1976 self.emit(Instruction::I2f);
1977 }
1978 AstType::Primitive {
1979 kind: PrimitiveType::Double,
1980 ty: _,
1981 } => {
1982 self.emit(Instruction::I2d);
1983 }
1984 _ => {}
1985 }
1986 Ok(())
1987 }
1988
1989 fn emit_field_access(
1990 &mut self,
1991 target: ExprId,
1992 name: &Ident,
1993 field_id: Option<rajac_types::FieldId>,
1994 ) -> RajacResult<()> {
1995 let target_expr = self.arena.expr(target);
1996
1997 let is_system_out = match target_expr {
1998 AstExpr::Ident(ident) => ident.as_str() == "System" && name.as_str() == "out",
1999 AstExpr::FieldAccess {
2000 expr: inner_target,
2001 name: field_name,
2002 ..
2003 } => {
2004 if field_name.as_str() == "out" {
2005 let inner = self.arena.expr(*inner_target);
2006 matches!(inner, AstExpr::Ident(ident) if ident.as_str() == "System")
2007 } else {
2008 false
2009 }
2010 }
2011 _ => false,
2012 };
2013
2014 if is_system_out {
2015 return self.emit_system_out();
2016 }
2017
2018 let Some(field_id) = field_id else {
2019 return Ok(());
2020 };
2021 let Some((owner_internal_name, field)) = self.resolve_field_owner(field_id) else {
2022 return Ok(());
2023 };
2024
2025 let descriptor = type_id_to_descriptor(field.ty, self.type_arena);
2026 let owner_class = self.constant_pool.add_class(owner_internal_name.as_str())?;
2027 let field_ref =
2028 self.constant_pool
2029 .add_field_ref(owner_class, field.name.as_str(), &descriptor)?;
2030
2031 if field.modifiers.0 & rajac_types::FieldModifiers::STATIC != 0 {
2032 self.emit(Instruction::Getstatic(field_ref));
2033 } else {
2034 self.emit_expression(target)?;
2035 self.emit(Instruction::Getfield(field_ref));
2036 }
2037
2038 Ok(())
2039 }
2040
2041 fn emit_system_out(&mut self) -> RajacResult<()> {
2042 let system_class = self.constant_pool.add_class("java/lang/System")?;
2043 let system_out =
2044 self.constant_pool
2045 .add_field_ref(system_class, "out", "Ljava/io/PrintStream;")?;
2046 self.emit(Instruction::Getstatic(system_out));
2047 Ok(())
2048 }
2049
2050 fn emit_method_call(
2051 &mut self,
2052 target: Option<&ExprId>,
2053 name: &Ident,
2054 args: &[ExprId],
2055 method_id: Option<MethodId>,
2056 return_type: TypeId,
2057 ) -> RajacResult<()> {
2058 let Some(invocation) =
2059 self.resolve_method_invocation(target, name, args, method_id, return_type)?
2060 else {
2061 return self.emit_unsupported_feature("unresolved method calls", name.as_str());
2062 };
2063
2064 if let Some(target_expr_id) = target {
2065 self.emit_expression(*target_expr_id)?;
2066 } else if invocation.has_receiver() {
2067 self.emit(Instruction::Aload_0);
2068 }
2069
2070 let owner_class = self
2071 .constant_pool
2072 .add_class(invocation.owner_internal_name.as_str())?;
2073 self.emit_invocation(&invocation, owner_class, args)?;
2074 Ok(())
2075 }
2076
2077 fn resolve_method_invocation(
2078 &self,
2079 target: Option<&ExprId>,
2080 name: &Ident,
2081 args: &[ExprId],
2082 method_id: Option<MethodId>,
2083 return_type: TypeId,
2084 ) -> RajacResult<Option<ResolvedInvocation>> {
2085 let Some(method_id) = method_id else {
2086 if let Some(descriptor) =
2087 infer_method_descriptor(name, args, self.symbol_table, self.type_arena)
2088 {
2089 return Ok(Some(ResolvedInvocation {
2090 kind: InvocationKind::Virtual,
2091 owner_internal_name: SharedString::new("java/lang/Object"),
2092 name: SharedString::new(name.as_str()),
2093 descriptor: SharedString::new(descriptor),
2094 interface_arg_count: None,
2095 }));
2096 }
2097 return Ok(Some(ResolvedInvocation {
2098 kind: InvocationKind::Virtual,
2099 owner_internal_name: SharedString::new("java/lang/Object"),
2100 name: SharedString::new(name.as_str()),
2101 descriptor: SharedString::new(method_descriptor_from_parts(
2102 args.iter()
2103 .map(|arg| self.expression_type_id(*arg))
2104 .collect(),
2105 return_type,
2106 self.type_arena,
2107 )),
2108 interface_arg_count: None,
2109 }));
2110 };
2111
2112 let signature = self.symbol_table.method_arena().get(method_id);
2113 let descriptor =
2114 SharedString::new(method_descriptor_from_signature(signature, self.type_arena));
2115 let Some(owner) = self.resolve_method_owner(method_id) else {
2116 return Ok(None);
2117 };
2118 let is_super_receiver =
2119 target.is_some_and(|expr_id| matches!(self.arena.expr(*expr_id), AstExpr::Super));
2120 let kind = if signature.modifiers.is_static() {
2121 InvocationKind::Static
2122 } else if is_super_receiver {
2123 InvocationKind::Special
2124 } else if matches!(owner.kind, SymbolKind::Interface) {
2125 InvocationKind::Interface
2126 } else {
2127 InvocationKind::Virtual
2128 };
2129
2130 let interface_arg_count = matches!(kind, InvocationKind::Interface)
2131 .then(|| interface_arg_count(signature, self.type_arena));
2132
2133 Ok(Some(ResolvedInvocation {
2134 kind,
2135 owner_internal_name: owner.internal_name,
2136 name: SharedString::new(signature.name.as_str()),
2137 descriptor,
2138 interface_arg_count,
2139 }))
2140 }
2141
2142 fn resolve_method_owner(&self, method_id: MethodId) -> Option<ResolvedMethodOwner> {
2143 let type_arena = self.symbol_table.type_arena();
2144 for index in 0..type_arena.len() {
2145 let type_id = TypeId(index as u32);
2146 let Type::Class(class_type) = type_arena.get(type_id) else {
2147 continue;
2148 };
2149 if !class_type
2150 .methods
2151 .values()
2152 .any(|method_ids| method_ids.contains(&method_id))
2153 {
2154 continue;
2155 }
2156
2157 let internal_name = SharedString::new(class_type.internal_name());
2158 let kind = self.lookup_symbol_kind_for_type(type_id)?;
2159 return Some(ResolvedMethodOwner {
2160 internal_name,
2161 kind,
2162 });
2163 }
2164 None
2165 }
2166
2167 fn lookup_symbol_kind_for_type(&self, type_id: TypeId) -> Option<SymbolKind> {
2168 for (_package_name, package) in self.symbol_table.iter() {
2169 for (_name, symbol) in package.iter() {
2170 if symbol.ty == type_id {
2171 return Some(symbol.kind);
2172 }
2173 }
2174 }
2175 None
2176 }
2177
2178 fn emit_invocation(
2179 &mut self,
2180 invocation: &ResolvedInvocation,
2181 owner_class: u16,
2182 args: &[ExprId],
2183 ) -> RajacResult<()> {
2184 for &arg in args {
2185 self.emit_expression(arg)?;
2186 }
2187
2188 let method_ref = match invocation.kind {
2189 InvocationKind::Interface => self.constant_pool.add_interface_method_ref(
2190 owner_class,
2191 invocation.name.as_str(),
2192 invocation.descriptor.as_str(),
2193 )?,
2194 InvocationKind::Static | InvocationKind::Virtual | InvocationKind::Special => {
2195 self.constant_pool.add_method_ref(
2196 owner_class,
2197 invocation.name.as_str(),
2198 invocation.descriptor.as_str(),
2199 )?
2200 }
2201 };
2202
2203 match invocation.kind {
2204 InvocationKind::Static => {
2205 self.emit(Instruction::Invokestatic(method_ref));
2206 }
2207 InvocationKind::Virtual => {
2208 self.emit(Instruction::Invokevirtual(method_ref));
2209 }
2210 InvocationKind::Special => {
2211 self.emit(Instruction::Invokespecial(method_ref));
2212 }
2213 InvocationKind::Interface => {
2214 self.emit(Instruction::Invokeinterface(
2215 method_ref,
2216 invocation.interface_arg_count.unwrap_or(1),
2217 ));
2218 }
2219 }
2220 self.adjust_method_call_stack(invocation.descriptor.as_str(), invocation.has_receiver());
2221 Ok(())
2222 }
2223
2224 fn initialize_method_locals(&mut self, is_static: bool, params: &[ParamId]) {
2225 self.local_vars.clear();
2226 if is_static {
2227 self.max_locals = 0;
2228 self.next_local_slot = 0;
2229 } else {
2230 self.max_locals = 1;
2231 self.next_local_slot = 1;
2232 }
2233
2234 for param_id in params {
2235 let param = self.arena.param(*param_id);
2236 let param_ty = self.arena.ty(param.ty);
2237 let kind = local_kind_from_ast_type(param_ty);
2238 let slot = self.allocate_local(kind);
2239 self.local_vars
2240 .insert(param.name.as_str().to_string(), LocalVar { slot, kind });
2241 }
2242 }
2243
2244 fn initialize_enum_constructor_locals(&mut self, params: &[ParamId]) {
2245 self.local_vars.clear();
2246 self.max_locals = 0;
2247 self.next_local_slot = 0;
2248
2249 self.allocate_local(LocalVarKind::Reference);
2250 self.allocate_local(LocalVarKind::Reference);
2251 self.allocate_local(LocalVarKind::IntLike);
2252
2253 for param_id in params {
2254 let param = self.arena.param(*param_id);
2255 let kind = local_kind_from_ast_type(self.arena.ty(param.ty));
2256 let slot = self.allocate_local(kind);
2257 self.local_vars
2258 .insert(param.name.as_str().to_string(), LocalVar { slot, kind });
2259 }
2260 }
2261
2262 fn constructor_body_parts(&self, body_id: StmtId) -> Option<(Vec<ExprId>, Vec<StmtId>)> {
2263 let Stmt::Block(statements) = self.arena.stmt(body_id) else {
2264 return None;
2265 };
2266 let first_stmt = statements.first()?;
2267 let Stmt::Expr(expr_id) = self.arena.stmt(*first_stmt) else {
2268 return None;
2269 };
2270 let AstExpr::SuperCall { args, .. } = self.arena.expr(*expr_id) else {
2271 return None;
2272 };
2273
2274 Some((args.clone(), statements.iter().skip(1).copied().collect()))
2275 }
2276
2277 fn adjust_method_call_stack(&mut self, descriptor: &str, has_receiver: bool) {
2278 let actual_delta =
2279 method_call_stack_delta(descriptor, has_receiver).unwrap_or(-i32::from(has_receiver));
2280 let generic_delta = -i32::from(has_receiver);
2281 self.current_stack += actual_delta - generic_delta;
2282 self.max_stack = self.max_stack.max(self.current_stack.max(0) as u16);
2283 }
2284
2285 fn adjust_multianewarray_stack(&mut self, dimensions: i32) {
2286 let actual_delta = 1 - dimensions;
2287 self.current_stack = (self.current_stack + actual_delta).max(0);
2288 self.max_stack = self.max_stack.max(self.current_stack as u16);
2289 }
2290
2291 fn allocate_local(&mut self, kind: LocalVarKind) -> u16 {
2292 let slot = self.next_local_slot;
2293 self.next_local_slot += kind.slot_size();
2294 self.max_locals = self.max_locals.max(self.next_local_slot);
2295 slot
2296 }
2297
2298 fn emit_load(&mut self, slot: u16, kind: LocalVarKind) {
2299 match kind {
2300 LocalVarKind::IntLike => match slot {
2301 0 => self.emit(Instruction::Iload_0),
2302 1 => self.emit(Instruction::Iload_1),
2303 2 => self.emit(Instruction::Iload_2),
2304 3 => self.emit(Instruction::Iload_3),
2305 _ => self.emit(Instruction::Iload(slot as u8)),
2306 },
2307 LocalVarKind::Long => match slot {
2308 0 => self.emit(Instruction::Lload_0),
2309 1 => self.emit(Instruction::Lload_1),
2310 2 => self.emit(Instruction::Lload_2),
2311 3 => self.emit(Instruction::Lload_3),
2312 _ => self.emit(Instruction::Lload(slot as u8)),
2313 },
2314 LocalVarKind::Float => match slot {
2315 0 => self.emit(Instruction::Fload_0),
2316 1 => self.emit(Instruction::Fload_1),
2317 2 => self.emit(Instruction::Fload_2),
2318 3 => self.emit(Instruction::Fload_3),
2319 _ => self.emit(Instruction::Fload(slot as u8)),
2320 },
2321 LocalVarKind::Double => match slot {
2322 0 => self.emit(Instruction::Dload_0),
2323 1 => self.emit(Instruction::Dload_1),
2324 2 => self.emit(Instruction::Dload_2),
2325 3 => self.emit(Instruction::Dload_3),
2326 _ => self.emit(Instruction::Dload(slot as u8)),
2327 },
2328 LocalVarKind::Reference => match slot {
2329 0 => self.emit(Instruction::Aload_0),
2330 1 => self.emit(Instruction::Aload_1),
2331 2 => self.emit(Instruction::Aload_2),
2332 3 => self.emit(Instruction::Aload_3),
2333 _ => self.emit(Instruction::Aload(slot as u8)),
2334 },
2335 }
2336 }
2337
2338 fn emit_store(&mut self, slot: u16, kind: LocalVarKind) {
2339 match kind {
2340 LocalVarKind::IntLike => match slot {
2341 0 => self.emit(Instruction::Istore_0),
2342 1 => self.emit(Instruction::Istore_1),
2343 2 => self.emit(Instruction::Istore_2),
2344 3 => self.emit(Instruction::Istore_3),
2345 _ => self.emit(Instruction::Istore(slot as u8)),
2346 },
2347 LocalVarKind::Long => match slot {
2348 0 => self.emit(Instruction::Lstore_0),
2349 1 => self.emit(Instruction::Lstore_1),
2350 2 => self.emit(Instruction::Lstore_2),
2351 3 => self.emit(Instruction::Lstore_3),
2352 _ => self.emit(Instruction::Lstore(slot as u8)),
2353 },
2354 LocalVarKind::Float => match slot {
2355 0 => self.emit(Instruction::Fstore_0),
2356 1 => self.emit(Instruction::Fstore_1),
2357 2 => self.emit(Instruction::Fstore_2),
2358 3 => self.emit(Instruction::Fstore_3),
2359 _ => self.emit(Instruction::Fstore(slot as u8)),
2360 },
2361 LocalVarKind::Double => match slot {
2362 0 => self.emit(Instruction::Dstore_0),
2363 1 => self.emit(Instruction::Dstore_1),
2364 2 => self.emit(Instruction::Dstore_2),
2365 3 => self.emit(Instruction::Dstore_3),
2366 _ => self.emit(Instruction::Dstore(slot as u8)),
2367 },
2368 LocalVarKind::Reference => match slot {
2369 0 => self.emit(Instruction::Astore_0),
2370 1 => self.emit(Instruction::Astore_1),
2371 2 => self.emit(Instruction::Astore_2),
2372 3 => self.emit(Instruction::Astore_3),
2373 _ => self.emit(Instruction::Astore(slot as u8)),
2374 },
2375 }
2376 }
2377
2378 fn return_instruction_for_kind(&self, kind: LocalVarKind) -> Instruction {
2379 match kind {
2380 LocalVarKind::IntLike => Instruction::Ireturn,
2381 LocalVarKind::Long => Instruction::Lreturn,
2382 LocalVarKind::Float => Instruction::Freturn,
2383 LocalVarKind::Double => Instruction::Dreturn,
2384 LocalVarKind::Reference => Instruction::Areturn,
2385 }
2386 }
2387
2388 fn neg_instruction_for_kind(&self, kind: LocalVarKind) -> Instruction {
2389 match kind {
2390 LocalVarKind::Long => Instruction::Lneg,
2391 LocalVarKind::Float => Instruction::Fneg,
2392 LocalVarKind::Double => Instruction::Dneg,
2393 _ => Instruction::Ineg,
2394 }
2395 }
2396
2397 fn arithmetic_instruction(&self, kind: LocalVarKind, op: ArithmeticOp) -> Instruction {
2398 match (kind, op) {
2399 (LocalVarKind::Long, ArithmeticOp::Add) => Instruction::Ladd,
2400 (LocalVarKind::Long, ArithmeticOp::Sub) => Instruction::Lsub,
2401 (LocalVarKind::Long, ArithmeticOp::Mul) => Instruction::Lmul,
2402 (LocalVarKind::Long, ArithmeticOp::Div) => Instruction::Ldiv,
2403 (LocalVarKind::Long, ArithmeticOp::Rem) => Instruction::Lrem,
2404 (LocalVarKind::Float, ArithmeticOp::Add) => Instruction::Fadd,
2405 (LocalVarKind::Float, ArithmeticOp::Sub) => Instruction::Fsub,
2406 (LocalVarKind::Float, ArithmeticOp::Mul) => Instruction::Fmul,
2407 (LocalVarKind::Float, ArithmeticOp::Div) => Instruction::Fdiv,
2408 (LocalVarKind::Float, ArithmeticOp::Rem) => Instruction::Frem,
2409 (LocalVarKind::Double, ArithmeticOp::Add) => Instruction::Dadd,
2410 (LocalVarKind::Double, ArithmeticOp::Sub) => Instruction::Dsub,
2411 (LocalVarKind::Double, ArithmeticOp::Mul) => Instruction::Dmul,
2412 (LocalVarKind::Double, ArithmeticOp::Div) => Instruction::Ddiv,
2413 (LocalVarKind::Double, ArithmeticOp::Rem) => Instruction::Drem,
2414 _ => match op {
2415 ArithmeticOp::Add => Instruction::Iadd,
2416 ArithmeticOp::Sub => Instruction::Isub,
2417 ArithmeticOp::Mul => Instruction::Imul,
2418 ArithmeticOp::Div => Instruction::Idiv,
2419 ArithmeticOp::Rem => Instruction::Irem,
2420 },
2421 }
2422 }
2423
2424 fn bitwise_instruction(&self, kind: LocalVarKind, op: BitwiseOp) -> Instruction {
2425 match (kind, op) {
2426 (LocalVarKind::Long, BitwiseOp::And) => Instruction::Land,
2427 (LocalVarKind::Long, BitwiseOp::Or) => Instruction::Lor,
2428 (LocalVarKind::Long, BitwiseOp::Xor) => Instruction::Lxor,
2429 _ => match op {
2430 BitwiseOp::And => Instruction::Iand,
2431 BitwiseOp::Or => Instruction::Ior,
2432 BitwiseOp::Xor => Instruction::Ixor,
2433 },
2434 }
2435 }
2436
2437 fn shift_instruction(&self, kind: LocalVarKind, op: ShiftOp) -> Instruction {
2438 match (kind, op) {
2439 (LocalVarKind::Long, ShiftOp::Left) => Instruction::Lshl,
2440 (LocalVarKind::Long, ShiftOp::Right) => Instruction::Lshr,
2441 (LocalVarKind::Long, ShiftOp::UnsignedRight) => Instruction::Lushr,
2442 _ => match op {
2443 ShiftOp::Left => Instruction::Ishl,
2444 ShiftOp::Right => Instruction::Ishr,
2445 ShiftOp::UnsignedRight => Instruction::Iushr,
2446 },
2447 }
2448 }
2449
2450 fn emit_logical_and(&mut self, lhs: ExprId, rhs: ExprId) -> RajacResult<()> {
2451 let false_label = self.new_label();
2452 let end_label = self.new_label();
2453
2454 self.emit_expression(lhs)?;
2455 self.emit_branch(BranchKind::IfEq, false_label);
2456 self.emit_expression(rhs)?;
2457 self.emit_branch(BranchKind::IfEq, false_label);
2458 self.emit(Instruction::Iconst_1);
2459 self.emit_branch(BranchKind::Goto, end_label);
2460 self.current_stack = 0;
2461 self.bind_label(false_label);
2462 self.emit(Instruction::Iconst_0);
2463 self.bind_label(end_label);
2464
2465 Ok(())
2466 }
2467
2468 fn emit_logical_or(&mut self, lhs: ExprId, rhs: ExprId) -> RajacResult<()> {
2469 let true_label = self.new_label();
2470 let false_label = self.new_label();
2471 let end_label = self.new_label();
2472
2473 self.emit_expression(lhs)?;
2474 self.emit_branch(BranchKind::IfNe, true_label);
2475 self.emit_expression(rhs)?;
2476 self.emit_branch(BranchKind::IfEq, false_label);
2477 self.bind_label(true_label);
2478 self.emit(Instruction::Iconst_1);
2479 self.emit_branch(BranchKind::Goto, end_label);
2480 self.current_stack = 0;
2481 self.bind_label(false_label);
2482 self.emit(Instruction::Iconst_0);
2483 self.bind_label(end_label);
2484
2485 Ok(())
2486 }
2487
2488 fn kind_for_expr(&self, expr_id: ExprId, expr_ty: TypeId) -> LocalVarKind {
2489 if expr_ty != TypeId::INVALID {
2490 return local_kind_from_type_id(expr_ty, self.type_arena);
2491 }
2492 self.infer_kind_from_expr(expr_id)
2493 }
2494
2495 fn expression_type_id(&self, expr_id: ExprId) -> TypeId {
2496 let expr_ty = self.arena.expr_typed(expr_id).ty;
2497 if expr_ty != TypeId::INVALID {
2498 return expr_ty;
2499 }
2500
2501 match self.arena.expr(expr_id) {
2502 AstExpr::MethodCall { name, args, .. } if is_object_equals_call(name, args) => self
2503 .symbol_table
2504 .primitive_type_id("boolean")
2505 .unwrap_or(TypeId::INVALID),
2506 _ => TypeId::INVALID,
2507 }
2508 }
2509
2510 fn infer_kind_from_expr(&self, expr_id: ExprId) -> LocalVarKind {
2511 let expr = self.arena.expr(expr_id);
2512 match expr {
2513 AstExpr::Literal(literal) => match literal.kind {
2514 LiteralKind::Long => LocalVarKind::Long,
2515 LiteralKind::Float => LocalVarKind::Float,
2516 LiteralKind::Double => LocalVarKind::Double,
2517 LiteralKind::String | LiteralKind::Null => LocalVarKind::Reference,
2518 _ => LocalVarKind::IntLike,
2519 },
2520 AstExpr::Ident(ident) => self
2521 .local_vars
2522 .get(ident.as_str())
2523 .map(|local| local.kind)
2524 .unwrap_or(LocalVarKind::Reference),
2525 AstExpr::Cast { ty, .. } => local_kind_from_ast_type(self.arena.ty(*ty)),
2526 AstExpr::Unary { expr, .. } => self.infer_kind_from_expr(*expr),
2527 AstExpr::Binary { op, lhs, rhs } => {
2528 let lhs_kind = self.infer_kind_from_expr(*lhs);
2529 let rhs_kind = self.infer_kind_from_expr(*rhs);
2530 match op {
2531 rajac_ast::BinaryOp::And | rajac_ast::BinaryOp::Or => LocalVarKind::IntLike,
2532 rajac_ast::BinaryOp::BitAnd
2533 | rajac_ast::BinaryOp::BitOr
2534 | rajac_ast::BinaryOp::BitXor
2535 | rajac_ast::BinaryOp::LShift
2536 | rajac_ast::BinaryOp::RShift
2537 | rajac_ast::BinaryOp::ARShift => {
2538 if matches!(lhs_kind, LocalVarKind::Long)
2539 || matches!(rhs_kind, LocalVarKind::Long)
2540 {
2541 LocalVarKind::Long
2542 } else {
2543 LocalVarKind::IntLike
2544 }
2545 }
2546 _ => promote_numeric_kind(lhs_kind, rhs_kind),
2547 }
2548 }
2549 AstExpr::Ternary {
2550 then_expr,
2551 else_expr,
2552 ..
2553 } => {
2554 let then_kind = self.infer_kind_from_expr(*then_expr);
2555 let else_kind = self.infer_kind_from_expr(*else_expr);
2556 promote_numeric_kind(then_kind, else_kind)
2557 }
2558 AstExpr::MethodCall { name, args, .. } if is_object_equals_call(name, args) => {
2559 LocalVarKind::IntLike
2560 }
2561 AstExpr::MethodCall { .. }
2562 | AstExpr::New { .. }
2563 | AstExpr::NewArray { .. }
2564 | AstExpr::ArrayInitializer { .. }
2565 | AstExpr::ArrayAccess { .. }
2566 | AstExpr::ArrayLength { .. }
2567 | AstExpr::This(_)
2568 | AstExpr::Super
2569 | AstExpr::FieldAccess { .. }
2570 | AstExpr::SuperCall { .. } => LocalVarKind::Reference,
2571 AstExpr::InstanceOf { .. } => LocalVarKind::IntLike,
2572 AstExpr::Assign { .. } | AstExpr::Error => LocalVarKind::Reference,
2573 }
2574 }
2575}
2576
2577#[derive(Clone, Copy, Debug)]
2578struct LocalVar {
2579 slot: u16,
2580 kind: LocalVarKind,
2581}
2582
2583#[derive(Clone, Debug)]
2584struct ResolvedMethodOwner {
2585 internal_name: SharedString,
2586 kind: SymbolKind,
2587}
2588
2589#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2590enum InvocationKind {
2591 Static,
2592 Virtual,
2593 Special,
2594 Interface,
2595}
2596
2597#[derive(Clone, Debug)]
2598struct ResolvedInvocation {
2599 kind: InvocationKind,
2600 owner_internal_name: SharedString,
2601 name: SharedString,
2602 descriptor: SharedString,
2603 interface_arg_count: Option<u8>,
2604}
2605
2606impl ResolvedInvocation {
2607 fn special(
2608 owner_internal_name: SharedString,
2609 name: SharedString,
2610 descriptor: SharedString,
2611 ) -> Self {
2612 Self {
2613 kind: InvocationKind::Special,
2614 owner_internal_name,
2615 name,
2616 descriptor,
2617 interface_arg_count: None,
2618 }
2619 }
2620
2621 fn has_receiver(&self) -> bool {
2622 !matches!(self.kind, InvocationKind::Static)
2623 }
2624}
2625
2626#[derive(Clone, Debug)]
2627struct ControlFlowFrame {
2628 label_name: Option<Ident>,
2629 break_label: LabelId,
2630 continue_label: Option<LabelId>,
2631 supports_unlabeled_break: bool,
2632}
2633
2634#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
2635struct LabelId(u32);
2636
2637#[derive(Clone, Debug)]
2638enum CodeItem {
2639 Instruction(Instruction),
2640 Branch { kind: BranchKind, target: LabelId },
2641 Switch(SwitchItem),
2642 Label(LabelId),
2643}
2644
2645#[derive(Clone, Debug)]
2646enum SwitchItem {
2647 Table {
2648 default: LabelId,
2649 low: i32,
2650 high: i32,
2651 offsets: Vec<LabelId>,
2652 },
2653 Lookup {
2654 default: LabelId,
2655 pairs: Vec<(i32, LabelId)>,
2656 },
2657}
2658
2659impl SwitchItem {
2660 fn targets(&self) -> impl Iterator<Item = LabelId> + '_ {
2661 let mut targets = Vec::new();
2662 match self {
2663 SwitchItem::Table {
2664 default, offsets, ..
2665 } => {
2666 targets.push(*default);
2667 targets.extend(offsets.iter().copied());
2668 }
2669 SwitchItem::Lookup { default, pairs } => {
2670 targets.push(*default);
2671 targets.extend(pairs.iter().map(|(_, label)| *label));
2672 }
2673 }
2674 targets.into_iter()
2675 }
2676}
2677
2678#[derive(Clone, Copy, Debug)]
2679enum LocalVarKind {
2680 IntLike,
2681 Long,
2682 Float,
2683 Double,
2684 Reference,
2685}
2686
2687impl LocalVarKind {
2688 fn slot_size(self) -> u16 {
2689 match self {
2690 LocalVarKind::Long | LocalVarKind::Double => 2,
2691 _ => 1,
2692 }
2693 }
2694}
2695
2696#[derive(Clone, Copy, Debug)]
2697enum ArithmeticOp {
2698 Add,
2699 Sub,
2700 Mul,
2701 Div,
2702 Rem,
2703}
2704
2705#[derive(Clone, Copy, Debug)]
2706enum BitwiseOp {
2707 And,
2708 Or,
2709 Xor,
2710}
2711
2712#[derive(Clone, Copy, Debug)]
2713enum ShiftOp {
2714 Left,
2715 Right,
2716 UnsignedRight,
2717}
2718
2719#[derive(Clone, Copy, Debug)]
2720enum BranchKind {
2721 IfEq,
2722 IfNe,
2723 IfLt,
2724 IfLe,
2725 IfGt,
2726 IfGe,
2727 IfIcmpEq,
2728 IfIcmpNe,
2729 IfIcmpLt,
2730 IfIcmpLe,
2731 IfIcmpGt,
2732 IfIcmpGe,
2733 IfAcmpEq,
2734 IfAcmpNe,
2735 IfNull,
2736 IfNonNull,
2737 Goto,
2738}
2739
2740fn branch_instruction(kind: BranchKind, target: u16) -> Instruction {
2741 match kind {
2742 BranchKind::IfEq => Instruction::Ifeq(target),
2743 BranchKind::IfNe => Instruction::Ifne(target),
2744 BranchKind::IfLt => Instruction::Iflt(target),
2745 BranchKind::IfLe => Instruction::Ifle(target),
2746 BranchKind::IfGt => Instruction::Ifgt(target),
2747 BranchKind::IfGe => Instruction::Ifge(target),
2748 BranchKind::IfIcmpEq => Instruction::If_icmpeq(target),
2749 BranchKind::IfIcmpNe => Instruction::If_icmpne(target),
2750 BranchKind::IfIcmpLt => Instruction::If_icmplt(target),
2751 BranchKind::IfIcmpLe => Instruction::If_icmple(target),
2752 BranchKind::IfIcmpGt => Instruction::If_icmpgt(target),
2753 BranchKind::IfIcmpGe => Instruction::If_icmpge(target),
2754 BranchKind::IfAcmpEq => Instruction::If_acmpeq(target),
2755 BranchKind::IfAcmpNe => Instruction::If_acmpne(target),
2756 BranchKind::IfNull => Instruction::Ifnull(target),
2757 BranchKind::IfNonNull => Instruction::Ifnonnull(target),
2758 BranchKind::Goto => Instruction::Goto(target),
2759 }
2760}
2761
2762fn choose_switch_item(default: LabelId, pairs: &[(i32, LabelId)]) -> SwitchItem {
2763 if pairs.is_empty() {
2764 return SwitchItem::Lookup {
2765 default,
2766 pairs: Vec::new(),
2767 };
2768 }
2769
2770 let low = pairs.first().map(|(value, _)| *value).unwrap_or_default();
2771 let high = pairs.last().map(|(value, _)| *value).unwrap_or_default();
2772 let table_space_cost = 4 + (high - low + 1);
2773 let table_time_cost = 3;
2774 let lookup_space_cost = 3 + 2 * pairs.len() as i32;
2775 let lookup_time_cost = pairs.len() as i32;
2776
2777 if table_space_cost + 3 * table_time_cost <= lookup_space_cost + 3 * lookup_time_cost {
2778 let mut offsets = vec![default; (high - low + 1) as usize];
2779 for (value, label) in pairs {
2780 offsets[(value - low) as usize] = *label;
2781 }
2782 SwitchItem::Table {
2783 default,
2784 low,
2785 high,
2786 offsets,
2787 }
2788 } else {
2789 SwitchItem::Lookup {
2790 default,
2791 pairs: pairs.to_vec(),
2792 }
2793 }
2794}
2795
2796fn switch_instruction(
2797 switch: &SwitchItem,
2798 labels: &std::collections::HashMap<LabelId, u16>,
2799) -> Instruction {
2800 match switch {
2801 SwitchItem::Table {
2802 default,
2803 low,
2804 high,
2805 offsets,
2806 } => Instruction::Tableswitch(TableSwitch {
2807 default: labels.get(default).copied().unwrap_or_default() as i32,
2808 low: *low,
2809 high: *high,
2810 offsets: offsets
2811 .iter()
2812 .map(|label| labels.get(label).copied().unwrap_or_default() as i32)
2813 .collect(),
2814 }),
2815 SwitchItem::Lookup { default, pairs } => Instruction::Lookupswitch(LookupSwitch {
2816 default: labels.get(default).copied().unwrap_or_default() as i32,
2817 pairs: pairs
2818 .iter()
2819 .map(|(value, label)| {
2820 (
2821 *value,
2822 labels.get(label).copied().unwrap_or_default() as i32,
2823 )
2824 })
2825 .collect(),
2826 }),
2827 }
2828}
2829
2830fn local_kind_from_ast_type(ty: &AstType) -> LocalVarKind {
2831 match ty {
2832 AstType::Primitive { kind, ty: _ } => match kind {
2833 PrimitiveType::Long => LocalVarKind::Long,
2834 PrimitiveType::Float => LocalVarKind::Float,
2835 PrimitiveType::Double => LocalVarKind::Double,
2836 PrimitiveType::Void => LocalVarKind::Reference,
2837 _ => LocalVarKind::IntLike,
2838 },
2839 _ => LocalVarKind::Reference,
2840 }
2841}
2842
2843fn local_kind_from_type_id(type_id: TypeId, type_arena: &TypeArena) -> LocalVarKind {
2844 if type_id == TypeId::INVALID {
2845 return LocalVarKind::Reference;
2846 }
2847 match type_arena.get(type_id) {
2848 Type::Primitive(primitive) => match primitive {
2849 rajac_types::PrimitiveType::Long => LocalVarKind::Long,
2850 rajac_types::PrimitiveType::Float => LocalVarKind::Float,
2851 rajac_types::PrimitiveType::Double => LocalVarKind::Double,
2852 rajac_types::PrimitiveType::Void => LocalVarKind::Reference,
2853 _ => LocalVarKind::IntLike,
2854 },
2855 Type::Class(_) | Type::Array(_) => LocalVarKind::Reference,
2856 Type::TypeVariable(_) | Type::Wildcard(_) | Type::Error => LocalVarKind::Reference,
2857 }
2858}
2859
2860fn promote_numeric_kind(lhs_kind: LocalVarKind, rhs_kind: LocalVarKind) -> LocalVarKind {
2861 if matches!(lhs_kind, LocalVarKind::Double) || matches!(rhs_kind, LocalVarKind::Double) {
2862 LocalVarKind::Double
2863 } else if matches!(lhs_kind, LocalVarKind::Float) || matches!(rhs_kind, LocalVarKind::Float) {
2864 LocalVarKind::Float
2865 } else if matches!(lhs_kind, LocalVarKind::Long) || matches!(rhs_kind, LocalVarKind::Long) {
2866 LocalVarKind::Long
2867 } else {
2868 LocalVarKind::IntLike
2869 }
2870}
2871
2872fn branch_kind_for_zero_compare(op: rajac_ast::BinaryOp) -> BranchKind {
2873 match op {
2874 rajac_ast::BinaryOp::EqEq => BranchKind::IfEq,
2875 rajac_ast::BinaryOp::BangEq => BranchKind::IfNe,
2876 rajac_ast::BinaryOp::Lt => BranchKind::IfLt,
2877 rajac_ast::BinaryOp::LtEq => BranchKind::IfLe,
2878 rajac_ast::BinaryOp::Gt => BranchKind::IfGt,
2879 rajac_ast::BinaryOp::GtEq => BranchKind::IfGe,
2880 _ => unreachable!(),
2881 }
2882}
2883
2884fn branch_kind_for_int_compare(op: rajac_ast::BinaryOp) -> BranchKind {
2885 match op {
2886 rajac_ast::BinaryOp::EqEq => BranchKind::IfIcmpEq,
2887 rajac_ast::BinaryOp::BangEq => BranchKind::IfIcmpNe,
2888 rajac_ast::BinaryOp::Lt => BranchKind::IfIcmpLt,
2889 rajac_ast::BinaryOp::LtEq => BranchKind::IfIcmpLe,
2890 rajac_ast::BinaryOp::Gt => BranchKind::IfIcmpGt,
2891 rajac_ast::BinaryOp::GtEq => BranchKind::IfIcmpGe,
2892 _ => unreachable!(),
2893 }
2894}
2895
2896fn inverse_zero_compare_branch_kind(op: rajac_ast::BinaryOp) -> BranchKind {
2897 match op {
2898 rajac_ast::BinaryOp::EqEq => BranchKind::IfNe,
2899 rajac_ast::BinaryOp::BangEq => BranchKind::IfEq,
2900 rajac_ast::BinaryOp::Lt => BranchKind::IfGe,
2901 rajac_ast::BinaryOp::LtEq => BranchKind::IfGt,
2902 rajac_ast::BinaryOp::Gt => BranchKind::IfLe,
2903 rajac_ast::BinaryOp::GtEq => BranchKind::IfLt,
2904 _ => unreachable!(),
2905 }
2906}
2907
2908fn inverse_int_compare_branch_kind(op: rajac_ast::BinaryOp) -> BranchKind {
2909 match op {
2910 rajac_ast::BinaryOp::EqEq => BranchKind::IfIcmpNe,
2911 rajac_ast::BinaryOp::BangEq => BranchKind::IfIcmpEq,
2912 rajac_ast::BinaryOp::Lt => BranchKind::IfIcmpGe,
2913 rajac_ast::BinaryOp::LtEq => BranchKind::IfIcmpGt,
2914 rajac_ast::BinaryOp::Gt => BranchKind::IfIcmpLe,
2915 rajac_ast::BinaryOp::GtEq => BranchKind::IfIcmpLt,
2916 _ => unreachable!(),
2917 }
2918}
2919
2920fn inverse_reference_branch_kind(op: rajac_ast::BinaryOp) -> BranchKind {
2921 match op {
2922 rajac_ast::BinaryOp::EqEq => BranchKind::IfAcmpNe,
2923 rajac_ast::BinaryOp::BangEq => BranchKind::IfAcmpEq,
2924 _ => unreachable!(),
2925 }
2926}
2927
2928fn reference_branch_kind(op: rajac_ast::BinaryOp) -> BranchKind {
2929 match op {
2930 rajac_ast::BinaryOp::EqEq => BranchKind::IfAcmpEq,
2931 rajac_ast::BinaryOp::BangEq => BranchKind::IfAcmpNe,
2932 _ => unreachable!(),
2933 }
2934}
2935
2936fn inverse_null_branch_kind(op: rajac_ast::BinaryOp) -> BranchKind {
2937 match op {
2938 rajac_ast::BinaryOp::EqEq => BranchKind::IfNonNull,
2939 rajac_ast::BinaryOp::BangEq => BranchKind::IfNull,
2940 _ => unreachable!(),
2941 }
2942}
2943
2944fn is_null_literal(expr: &AstExpr) -> bool {
2945 matches!(
2946 expr,
2947 AstExpr::Literal(Literal {
2948 kind: LiteralKind::Null,
2949 ..
2950 })
2951 )
2952}
2953
2954fn parse_int_literal(value: &str) -> Option<i32> {
2955 normalized_numeric_literal(value, &['l', 'L']).parse().ok()
2956}
2957
2958fn parse_long_literal(value: &str) -> Option<i64> {
2959 normalized_numeric_literal(value, &['l', 'L']).parse().ok()
2960}
2961
2962fn parse_float_literal(value: &str) -> Option<f32> {
2963 normalized_numeric_literal(value, &['f', 'F']).parse().ok()
2964}
2965
2966fn parse_double_literal(value: &str) -> Option<f64> {
2967 normalized_numeric_literal(value, &['d', 'D']).parse().ok()
2968}
2969
2970fn normalized_numeric_literal(value: &str, suffixes: &[char]) -> String {
2971 value
2972 .trim_end_matches(|c| suffixes.contains(&c))
2973 .replace('_', "")
2974}
2975
2976fn parse_char_literal(value: &str) -> Option<char> {
2977 let inner = value.strip_prefix('\'')?.strip_suffix('\'')?;
2978
2979 if let Some(hex) = inner.strip_prefix("\\u") {
2980 let code = u32::from_str_radix(hex, 16).ok()?;
2981 return char::from_u32(code);
2982 }
2983
2984 if let Some(escaped) = inner.strip_prefix('\\') {
2985 return match escaped {
2986 "n" => Some('\n'),
2987 "r" => Some('\r'),
2988 "t" => Some('\t'),
2989 "\\" => Some('\\'),
2990 "'" => Some('\''),
2991 "\"" => Some('"'),
2992 "0" => Some('\0'),
2993 _ => None,
2994 };
2995 }
2996
2997 let mut chars = inner.chars();
2998 let ch = chars.next()?;
2999 if chars.next().is_none() {
3000 Some(ch)
3001 } else {
3002 None
3003 }
3004}
3005
3006fn is_object_equals_call(name: &Ident, args: &[ExprId]) -> bool {
3007 name.as_str() == "equals" && args.len() == 1
3008}
3009
3010fn infer_method_descriptor(
3011 name: &Ident,
3012 args: &[ExprId],
3013 symbol_table: &SymbolTable,
3014 type_arena: &TypeArena,
3015) -> Option<String> {
3016 if is_object_equals_call(name, args) {
3017 let object_type = type_id_to_descriptor(TypeId::INVALID, type_arena);
3018 let boolean_type = symbol_table.primitive_type_id("boolean")?;
3019 return Some(format!(
3020 "({}){}",
3021 object_type,
3022 type_id_to_descriptor(boolean_type, type_arena)
3023 ));
3024 }
3025
3026 None
3027}
3028
3029fn type_id_to_descriptor(type_id: TypeId, type_arena: &TypeArena) -> String {
3030 if type_id == TypeId::INVALID {
3031 return "Ljava/lang/Object;".to_string();
3032 }
3033
3034 match type_arena.get(type_id) {
3035 Type::Primitive(primitive) => primitive.descriptor().to_string(),
3036 Type::Class(class_type) => format!("L{};", class_type.internal_name()),
3037 Type::Array(array_type) => {
3038 format!(
3039 "[{}",
3040 type_id_to_descriptor(array_type.element_type, type_arena)
3041 )
3042 }
3043 Type::TypeVariable(_) | Type::Wildcard(_) | Type::Error => "Ljava/lang/Object;".to_string(),
3044 }
3045}
3046
3047fn type_id_to_internal_name(type_id: TypeId, type_arena: &TypeArena) -> String {
3048 if type_id == TypeId::INVALID {
3049 return "java/lang/Object".to_string();
3050 }
3051 match type_arena.get(type_id) {
3052 Type::Class(class_type) => class_type.internal_name(),
3053 _ => "java/lang/Object".to_string(),
3054 }
3055}
3056
3057fn instanceof_target_class_name(type_id: TypeId, type_arena: &TypeArena) -> String {
3058 match type_arena.get(type_id) {
3059 Type::Class(class_type) => class_type.internal_name(),
3060 Type::Array(_) => type_id_to_descriptor(type_id, type_arena),
3061 Type::Primitive(_) | Type::TypeVariable(_) | Type::Wildcard(_) | Type::Error => {
3062 "java/lang/Object".to_string()
3063 }
3064 }
3065}
3066
3067fn array_component_type(array_type_id: TypeId, type_arena: &TypeArena) -> TypeId {
3068 match type_arena.get(array_type_id) {
3069 Type::Array(array_type) => array_type.element_type,
3070 _ => TypeId::INVALID,
3071 }
3072}
3073
3074fn primitive_array_type(type_id: TypeId, type_arena: &TypeArena) -> Option<JvmArrayType> {
3075 match type_arena.get(type_id) {
3076 Type::Primitive(rajac_types::PrimitiveType::Boolean) => Some(JvmArrayType::Boolean),
3077 Type::Primitive(rajac_types::PrimitiveType::Byte) => Some(JvmArrayType::Byte),
3078 Type::Primitive(rajac_types::PrimitiveType::Char) => Some(JvmArrayType::Char),
3079 Type::Primitive(rajac_types::PrimitiveType::Short) => Some(JvmArrayType::Short),
3080 Type::Primitive(rajac_types::PrimitiveType::Int) => Some(JvmArrayType::Int),
3081 Type::Primitive(rajac_types::PrimitiveType::Long) => Some(JvmArrayType::Long),
3082 Type::Primitive(rajac_types::PrimitiveType::Float) => Some(JvmArrayType::Float),
3083 Type::Primitive(rajac_types::PrimitiveType::Double) => Some(JvmArrayType::Double),
3084 Type::Primitive(rajac_types::PrimitiveType::Void)
3085 | Type::Class(_)
3086 | Type::Array(_)
3087 | Type::TypeVariable(_)
3088 | Type::Wildcard(_)
3089 | Type::Error => None,
3090 }
3091}
3092
3093fn array_component_class_name(type_id: TypeId, type_arena: &TypeArena) -> String {
3094 match type_arena.get(type_id) {
3095 Type::Class(class_type) => class_type.internal_name(),
3096 Type::Array(_) => type_id_to_descriptor(type_id, type_arena),
3097 Type::Primitive(_) | Type::TypeVariable(_) | Type::Wildcard(_) | Type::Error => {
3098 "java/lang/Object".to_string()
3099 }
3100 }
3101}
3102
3103fn array_store_instruction(type_id: TypeId, type_arena: &TypeArena) -> Instruction {
3104 match type_arena.get(type_id) {
3105 Type::Primitive(rajac_types::PrimitiveType::Boolean)
3106 | Type::Primitive(rajac_types::PrimitiveType::Byte) => Instruction::Bastore,
3107 Type::Primitive(rajac_types::PrimitiveType::Char) => Instruction::Castore,
3108 Type::Primitive(rajac_types::PrimitiveType::Short) => Instruction::Sastore,
3109 Type::Primitive(rajac_types::PrimitiveType::Int) => Instruction::Iastore,
3110 Type::Primitive(rajac_types::PrimitiveType::Long) => Instruction::Lastore,
3111 Type::Primitive(rajac_types::PrimitiveType::Float) => Instruction::Fastore,
3112 Type::Primitive(rajac_types::PrimitiveType::Double) => Instruction::Dastore,
3113 Type::Primitive(rajac_types::PrimitiveType::Void)
3114 | Type::Class(_)
3115 | Type::Array(_)
3116 | Type::TypeVariable(_)
3117 | Type::Wildcard(_)
3118 | Type::Error => Instruction::Aastore,
3119 }
3120}
3121
3122fn method_descriptor_from_signature(
3123 signature: &rajac_types::MethodSignature,
3124 type_arena: &TypeArena,
3125) -> String {
3126 method_descriptor_from_parts(signature.params.clone(), signature.return_type, type_arena)
3127}
3128
3129fn method_descriptor_from_parts(
3130 arg_types: Vec<TypeId>,
3131 return_type: TypeId,
3132 type_arena: &TypeArena,
3133) -> String {
3134 let args = arg_types
3135 .into_iter()
3136 .map(|type_id| type_id_to_descriptor(type_id, type_arena))
3137 .collect::<String>();
3138 let return_type = type_id_to_descriptor(return_type, type_arena);
3139 format!("({}){}", args, return_type)
3140}
3141
3142fn stack_effect(instr: &Instruction) -> i32 {
3143 use Instruction::*;
3144 match instr {
3145 Nop => 0,
3146 Aconst_null => 1,
3147 Iconst_m1 | Iconst_0 | Iconst_1 | Iconst_2 | Iconst_3 | Iconst_4 | Iconst_5 => 1,
3148 Lconst_0 | Lconst_1 => 2,
3149 Fconst_0 | Fconst_1 | Fconst_2 => 1,
3150 Dconst_0 | Dconst_1 => 2,
3151 Bipush(_) | Sipush(_) => 1,
3152 Ldc(_) => 1,
3153 Ldc_w(_) => 1,
3154 Ldc2_w(_) => 2,
3155 Iload(_) | Fload(_) | Aload(_) => 1,
3156 Lload(_) | Dload(_) => 2,
3157 Iload_0 | Iload_1 | Iload_2 | Iload_3 | Fload_0 | Fload_1 | Fload_2 | Fload_3 | Aload_0
3158 | Aload_1 | Aload_2 | Aload_3 => 1,
3159 Lload_0 | Lload_1 | Lload_2 | Lload_3 | Dload_0 | Dload_1 | Dload_2 | Dload_3 => 2,
3160 Istore(_) | Fstore(_) | Astore(_) => -1,
3161 Lstore(_) | Dstore(_) => -2,
3162 Istore_0 | Istore_1 | Istore_2 | Istore_3 | Fstore_0 | Fstore_1 | Fstore_2 | Fstore_3
3163 | Astore_0 | Astore_1 | Astore_2 | Astore_3 => -1,
3164 Lstore_0 | Lstore_1 | Lstore_2 | Lstore_3 | Dstore_0 | Dstore_1 | Dstore_2 | Dstore_3 => -2,
3165 Pop => -1,
3166 Pop2 => -2,
3167 Dup => 1,
3168 Dup_x1 => 0,
3169 Dup_x2 => -1,
3170 Dup2 => 2,
3171 Swap => 0,
3172 Iadd | Isub | Imul | Idiv | Irem | Iand | Ior | Ixor => -1,
3173 Ladd | Lsub | Lmul | Ldiv | Lrem | Land | Lor | Lxor => -2,
3174 Fadd | Fsub | Fmul | Fdiv | Frem => -1,
3175 Dadd | Dsub | Dmul | Ddiv | Drem => -2,
3176 Lcmp => -3,
3177 Fcmpl | Fcmpg => -1,
3178 Dcmpl | Dcmpg => -3,
3179 Ineg => 0,
3180 Lneg => 0,
3181 Fneg | Dneg => 0,
3182 Ishl | Lshl | Ishr | Lshr | Iushr | Lushr => -1,
3183 Ireturn | Freturn | Areturn => -1,
3184 Return => 0,
3185 Lreturn | Dreturn => -2,
3186 Getstatic(_) => 1,
3187 Putstatic(_) => -1,
3188 Getfield(_) => 0,
3189 Putfield(_) => -2,
3190 Invokevirtual(_) => -1,
3191 Invokeinterface(_, _) => -1,
3192 Invokespecial(_) => -1,
3193 Invokestatic(_) => 0,
3194 New(_) => 1,
3195 Newarray(_) => 0,
3196 Anewarray(_) => 0,
3197 Multianewarray(_, _) => 0,
3198 Iastore | Fastore | Aastore | Bastore | Castore | Sastore => -3,
3199 Lastore | Dastore => -4,
3200 Arraylength => 0,
3201 Athrow => -1,
3202 Checkcast(_) => 0,
3203 Instanceof(_) => 0,
3204 Ifeq(_) | Ifne(_) | Iflt(_) | Ifge(_) | Ifgt(_) | Ifle(_) | Ifnull(_) | Ifnonnull(_) => -1,
3205 If_icmpeq(_) | If_icmpne(_) | If_icmplt(_) | If_icmpge(_) | If_icmpgt(_) | If_icmple(_)
3206 | If_acmpeq(_) | If_acmpne(_) => -2,
3207 Tableswitch(_) | Lookupswitch(_) => -1,
3208 _ => 0,
3209 }
3210}
3211
3212fn method_call_stack_delta(descriptor: &str, has_receiver: bool) -> Option<i32> {
3213 let mut chars = descriptor.chars().peekable();
3214 if chars.next()? != '(' {
3215 return None;
3216 }
3217
3218 let mut arg_slots = if has_receiver { 1 } else { 0 };
3219 loop {
3220 match chars.peek().copied()? {
3221 ')' => {
3222 chars.next();
3223 break;
3224 }
3225 _ => {
3226 arg_slots += parse_descriptor_type_slots(&mut chars)?;
3227 }
3228 }
3229 }
3230
3231 let return_slots = parse_return_descriptor_slots(&mut chars)?;
3232 if chars.next().is_some() {
3233 return None;
3234 }
3235
3236 Some(return_slots - arg_slots)
3237}
3238
3239fn interface_arg_count(signature: &rajac_types::MethodSignature, type_arena: &TypeArena) -> u8 {
3240 let parameter_slots: usize = signature
3241 .params
3242 .iter()
3243 .map(|param_ty| local_kind_from_type_id(*param_ty, type_arena).slot_size() as usize)
3244 .sum();
3245 u8::try_from(parameter_slots + 1).unwrap_or(1)
3246}
3247
3248fn parse_return_descriptor_slots<I>(chars: &mut std::iter::Peekable<I>) -> Option<i32>
3249where
3250 I: Iterator<Item = char>,
3251{
3252 match chars.peek().copied()? {
3253 'V' => {
3254 chars.next();
3255 Some(0)
3256 }
3257 _ => parse_descriptor_type_slots(chars),
3258 }
3259}
3260
3261fn parse_descriptor_type_slots<I>(chars: &mut std::iter::Peekable<I>) -> Option<i32>
3262where
3263 I: Iterator<Item = char>,
3264{
3265 match chars.next()? {
3266 'B' | 'C' | 'F' | 'I' | 'S' | 'Z' => Some(1),
3267 'D' | 'J' => Some(2),
3268 'L' => {
3269 while chars.next()? != ';' {}
3270 Some(1)
3271 }
3272 '[' => {
3273 while matches!(chars.peek().copied(), Some('[')) {
3274 chars.next();
3275 }
3276 match chars.peek().copied()? {
3277 'L' => {
3278 chars.next();
3279 while chars.next()? != ';' {}
3280 }
3281 _ => {
3282 chars.next();
3283 }
3284 }
3285 Some(1)
3286 }
3287 _ => None,
3288 }
3289}
3290
3291#[cfg(test)]
3292mod tests {
3293 use super::*;
3294 use rajac_types::{ClassType, MethodModifiers, MethodSignature, Type};
3295 use ristretto_classfile::ConstantPool;
3296
3297 fn add_owner_method(
3298 symbol_table: &mut SymbolTable,
3299 package_name: &str,
3300 class_name: &str,
3301 kind: SymbolKind,
3302 signature: MethodSignature,
3303 ) -> MethodId {
3304 let type_id = symbol_table.add_class(
3305 package_name,
3306 class_name,
3307 Type::class(
3308 ClassType::new(SharedString::new(class_name))
3309 .with_package(SharedString::new(package_name)),
3310 ),
3311 kind,
3312 );
3313 let method_id = symbol_table.method_arena_mut().alloc(signature);
3314 let method_name = symbol_table.method_arena().get(method_id).name.clone();
3315 if let Type::Class(class_type) = symbol_table.type_arena_mut().get_mut(type_id) {
3316 class_type.add_method(method_name, method_id);
3317 }
3318 method_id
3319 }
3320
3321 #[test]
3322 fn finalize_adds_nop_for_branch_to_terminal_label() {
3323 let mut emitter = BytecodeEmitter::new();
3324 let end_label = LabelId(0);
3325
3326 emitter.emit(Instruction::Iconst_0);
3327 emitter.emit_branch(BranchKind::Goto, end_label);
3328 emitter.bind_label(end_label);
3329
3330 let instructions = emitter.finalize();
3331
3332 assert_eq!(
3333 instructions,
3334 vec![
3335 Instruction::Iconst_0,
3336 Instruction::Goto(2),
3337 Instruction::Nop
3338 ]
3339 );
3340 }
3341
3342 #[test]
3343 fn finalize_does_not_add_nop_for_unreferenced_terminal_label() {
3344 let mut emitter = BytecodeEmitter::new();
3345 let end_label = LabelId(0);
3346
3347 emitter.emit(Instruction::Iconst_0);
3348 emitter.bind_label(end_label);
3349
3350 let instructions = emitter.finalize();
3351
3352 assert_eq!(instructions, vec![Instruction::Iconst_0]);
3353 }
3354
3355 #[test]
3356 fn emit_literal_uses_constant_pool_for_large_numeric_values() -> RajacResult<()> {
3357 let arena = AstArena::new();
3358 let type_arena = TypeArena::new();
3359 let symbol_table = SymbolTable::new();
3360 let mut constant_pool = ConstantPool::new();
3361 let mut generator =
3362 CodeGenerator::new(&arena, &type_arena, &symbol_table, &mut constant_pool);
3363
3364 generator.emit_literal(&Literal {
3365 kind: LiteralKind::Int,
3366 value: "2147483647".into(),
3367 })?;
3368 generator.emit_literal(&Literal {
3369 kind: LiteralKind::Long,
3370 value: "9223372036854775807L".into(),
3371 })?;
3372 generator.emit_literal(&Literal {
3373 kind: LiteralKind::Float,
3374 value: "3.4028235e38f".into(),
3375 })?;
3376 generator.emit_literal(&Literal {
3377 kind: LiteralKind::Double,
3378 value: "1.7976931348623157e308d".into(),
3379 })?;
3380
3381 assert!(matches!(
3382 generator.emitter.code_items[0],
3383 CodeItem::Instruction(Instruction::Ldc(_))
3384 ));
3385 assert!(matches!(
3386 generator.emitter.code_items[1],
3387 CodeItem::Instruction(Instruction::Ldc2_w(_))
3388 ));
3389 assert!(matches!(
3390 generator.emitter.code_items[2],
3391 CodeItem::Instruction(Instruction::Ldc(_))
3392 ));
3393 assert!(matches!(
3394 generator.emitter.code_items[3],
3395 CodeItem::Instruction(Instruction::Ldc2_w(_))
3396 ));
3397
3398 Ok(())
3399 }
3400
3401 #[test]
3402 fn emit_literal_decodes_char_escape_sequences() -> RajacResult<()> {
3403 let arena = AstArena::new();
3404 let type_arena = TypeArena::new();
3405 let symbol_table = SymbolTable::new();
3406 let mut constant_pool = ConstantPool::new();
3407 let mut generator =
3408 CodeGenerator::new(&arena, &type_arena, &symbol_table, &mut constant_pool);
3409
3410 generator.emit_literal(&Literal {
3411 kind: LiteralKind::Char,
3412 value: "'\\u0005'".into(),
3413 })?;
3414 generator.emit_literal(&Literal {
3415 kind: LiteralKind::Char,
3416 value: "'\\uffff'".into(),
3417 })?;
3418
3419 assert!(matches!(
3420 generator.emitter.code_items[0],
3421 CodeItem::Instruction(Instruction::Iconst_5)
3422 ));
3423 assert!(matches!(
3424 generator.emitter.code_items[1],
3425 CodeItem::Instruction(Instruction::Ldc(_))
3426 ));
3427
3428 Ok(())
3429 }
3430
3431 #[test]
3432 fn expression_statements_discard_their_result_before_return() -> RajacResult<()> {
3433 let mut arena = AstArena::new();
3434 let type_arena = TypeArena::new();
3435 let symbol_table = SymbolTable::new();
3436 let mut constant_pool = ConstantPool::new();
3437
3438 let expr_id = arena.alloc_expr(AstExpr::Literal(Literal {
3439 kind: LiteralKind::Int,
3440 value: "7".into(),
3441 }));
3442 let expr_stmt = arena.alloc_stmt(Stmt::Expr(expr_id));
3443 let body = arena.alloc_stmt(Stmt::Block(vec![expr_stmt]));
3444
3445 let mut generator =
3446 CodeGenerator::new(&arena, &type_arena, &symbol_table, &mut constant_pool);
3447 let (instructions, _max_stack, _max_locals) =
3448 generator.generate_method_body(false, &[], body)?;
3449
3450 assert_eq!(
3451 instructions,
3452 vec![
3453 Instruction::Bipush(7),
3454 Instruction::Pop,
3455 Instruction::Return
3456 ]
3457 );
3458
3459 Ok(())
3460 }
3461
3462 #[test]
3463 fn assignment_expression_statements_do_not_emit_extra_pop() -> RajacResult<()> {
3464 let mut arena = AstArena::new();
3465 let type_arena = TypeArena::new();
3466 let symbol_table = SymbolTable::new();
3467 let mut constant_pool = ConstantPool::new();
3468
3469 let int_ty = arena.alloc_type(AstType::Primitive {
3470 kind: PrimitiveType::Int,
3471 ty: TypeId::INVALID,
3472 });
3473 let initializer = arena.alloc_expr(AstExpr::Literal(Literal {
3474 kind: LiteralKind::Int,
3475 value: "0".into(),
3476 }));
3477 let local_var = arena.alloc_stmt(Stmt::LocalVar {
3478 ty: int_ty,
3479 name: Ident::new("value".into()),
3480 initializer: Some(initializer),
3481 });
3482 let assigned = arena.alloc_expr(AstExpr::Literal(Literal {
3483 kind: LiteralKind::Int,
3484 value: "1".into(),
3485 }));
3486 let lhs = arena.alloc_expr(AstExpr::Ident(Ident::new("value".into())));
3487 let assign_expr = arena.alloc_expr(AstExpr::Assign {
3488 op: rajac_ast::AssignOp::Eq,
3489 lhs,
3490 rhs: assigned,
3491 });
3492 let expr_stmt = arena.alloc_stmt(Stmt::Expr(assign_expr));
3493 let body = arena.alloc_stmt(Stmt::Block(vec![local_var, expr_stmt]));
3494
3495 let mut generator =
3496 CodeGenerator::new(&arena, &type_arena, &symbol_table, &mut constant_pool);
3497 let (instructions, _max_stack, _max_locals) =
3498 generator.generate_method_body(false, &[], body)?;
3499
3500 assert_eq!(
3501 instructions,
3502 vec![
3503 Instruction::Iconst_0,
3504 Instruction::Istore_1,
3505 Instruction::Iconst_1,
3506 Instruction::Istore_1,
3507 Instruction::Return
3508 ]
3509 );
3510
3511 Ok(())
3512 }
3513
3514 #[test]
3515 fn throw_statements_emit_athrow_without_implicit_return() -> RajacResult<()> {
3516 let mut arena = AstArena::new();
3517 let type_arena = TypeArena::new();
3518 let symbol_table = SymbolTable::new();
3519 let mut constant_pool = ConstantPool::new();
3520
3521 let null_expr = arena.alloc_expr(AstExpr::Literal(Literal {
3522 kind: LiteralKind::Null,
3523 value: "null".into(),
3524 }));
3525 let throw_stmt = arena.alloc_stmt(Stmt::Throw(null_expr));
3526 let body = arena.alloc_stmt(Stmt::Block(vec![throw_stmt]));
3527
3528 let mut generator =
3529 CodeGenerator::new(&arena, &type_arena, &symbol_table, &mut constant_pool);
3530 let (instructions, _max_stack, _max_locals) =
3531 generator.generate_method_body(false, &[], body)?;
3532
3533 assert_eq!(
3534 instructions,
3535 vec![Instruction::Aconst_null, Instruction::Athrow]
3536 );
3537
3538 Ok(())
3539 }
3540
3541 #[test]
3542 fn constructor_throw_statements_emit_athrow_without_implicit_return() -> RajacResult<()> {
3543 let mut arena = AstArena::new();
3544 let type_arena = TypeArena::new();
3545 let symbol_table = SymbolTable::new();
3546 let mut constant_pool = ConstantPool::new();
3547
3548 let null_expr = arena.alloc_expr(AstExpr::Literal(Literal {
3549 kind: LiteralKind::Null,
3550 value: "null".into(),
3551 }));
3552 let throw_stmt = arena.alloc_stmt(Stmt::Throw(null_expr));
3553 let body = arena.alloc_stmt(Stmt::Block(vec![throw_stmt]));
3554
3555 let mut generator =
3556 CodeGenerator::new(&arena, &type_arena, &symbol_table, &mut constant_pool);
3557 let (instructions, _max_stack, _max_locals) =
3558 generator.generate_constructor_body(&[], Some(body), "java/lang/Object")?;
3559
3560 assert_eq!(instructions.len(), 4);
3561 assert!(matches!(instructions[0], Instruction::Aload_0));
3562 assert!(matches!(instructions[1], Instruction::Invokespecial(_)));
3563 assert!(matches!(instructions[2], Instruction::Aconst_null));
3564 assert!(matches!(instructions[3], Instruction::Athrow));
3565
3566 Ok(())
3567 }
3568
3569 #[test]
3570 fn unsupported_try_statements_emit_runtime_exception_and_report() -> RajacResult<()> {
3571 let mut arena = AstArena::new();
3572 let type_arena = TypeArena::new();
3573 let symbol_table = SymbolTable::new();
3574 let mut constant_pool = ConstantPool::new();
3575
3576 let try_block = arena.alloc_stmt(Stmt::Block(vec![]));
3577 let try_stmt = arena.alloc_stmt(Stmt::Try {
3578 try_block,
3579 catches: vec![],
3580 finally_block: None,
3581 });
3582 let body = arena.alloc_stmt(Stmt::Block(vec![try_stmt]));
3583
3584 let mut generator =
3585 CodeGenerator::new(&arena, &type_arena, &symbol_table, &mut constant_pool);
3586 let (instructions, _max_stack, _max_locals) =
3587 generator.generate_method_body(false, &[], body)?;
3588 let unsupported_features = generator.take_unsupported_features();
3589
3590 assert_eq!(unsupported_features.len(), 1);
3591 assert_eq!(
3592 unsupported_features[0].message.as_str(),
3593 "unsupported bytecode generation feature: try statements"
3594 );
3595 assert_eq!(unsupported_features[0].marker.as_str(), "try");
3596 assert!(matches!(instructions[0], Instruction::New(_)));
3597 assert!(matches!(instructions[1], Instruction::Dup));
3598 assert!(matches!(
3599 instructions[2],
3600 Instruction::Ldc(_) | Instruction::Ldc_w(_)
3601 ));
3602 assert!(matches!(instructions[3], Instruction::Invokespecial(_)));
3603 assert!(matches!(instructions[4], Instruction::Athrow));
3604
3605 Ok(())
3606 }
3607
3608 #[test]
3609 fn instanceof_expressions_emit_instanceof_for_class_targets() -> RajacResult<()> {
3610 let mut arena = AstArena::new();
3611 let mut symbol_table = SymbolTable::new();
3612 let mut constant_pool = ConstantPool::new();
3613
3614 let null_expr = arena.alloc_expr(AstExpr::Literal(Literal {
3615 kind: LiteralKind::Null,
3616 value: "null".into(),
3617 }));
3618 let object_type = symbol_table.type_arena_mut().alloc(Type::class(
3619 ClassType::new(SharedString::new("Object"))
3620 .with_package(SharedString::new("java.lang")),
3621 ));
3622 let object_ty = arena.alloc_type(AstType::Simple {
3623 name: SharedString::new("Object"),
3624 ty: object_type,
3625 type_args: vec![],
3626 });
3627 let instanceof_expr = arena.alloc_expr(AstExpr::InstanceOf {
3628 expr: null_expr,
3629 ty: object_ty,
3630 });
3631 let expr_stmt = arena.alloc_stmt(Stmt::Expr(instanceof_expr));
3632 let body = arena.alloc_stmt(Stmt::Block(vec![expr_stmt]));
3633
3634 let mut generator = CodeGenerator::new(
3635 &arena,
3636 symbol_table.type_arena(),
3637 &symbol_table,
3638 &mut constant_pool,
3639 );
3640 let (instructions, _max_stack, _max_locals) =
3641 generator.generate_method_body(false, &[], body)?;
3642 let unsupported_features = generator.take_unsupported_features();
3643
3644 assert!(unsupported_features.is_empty());
3645 assert_eq!(instructions[0], Instruction::Aconst_null);
3646 let Instruction::Instanceof(class_index) = instructions[1] else {
3647 panic!("expected instanceof");
3648 };
3649 assert_eq!(
3650 constant_pool
3651 .try_get_class(class_index)
3652 .expect("class constant"),
3653 "java/lang/Object"
3654 );
3655 assert_eq!(instructions[2], Instruction::Pop);
3656 assert_eq!(instructions[3], Instruction::Return);
3657
3658 Ok(())
3659 }
3660
3661 #[test]
3662 fn instanceof_expressions_emit_instanceof_for_array_targets() -> RajacResult<()> {
3663 let mut arena = AstArena::new();
3664 let mut symbol_table = SymbolTable::new();
3665 let mut constant_pool = ConstantPool::new();
3666
3667 let null_expr = arena.alloc_expr(AstExpr::Literal(Literal {
3668 kind: LiteralKind::Null,
3669 value: "null".into(),
3670 }));
3671 let int_type = symbol_table
3672 .primitive_type_id("int")
3673 .expect("missing int type");
3674 let int_array_type = symbol_table.type_arena_mut().alloc(Type::array(int_type));
3675 let ast_int_type = arena.alloc_type(AstType::Primitive {
3676 kind: PrimitiveType::Int,
3677 ty: int_type,
3678 });
3679 let ast_array_type = arena.alloc_type(AstType::Array {
3680 element_type: ast_int_type,
3681 dimensions: 1,
3682 ty: int_array_type,
3683 });
3684 let instanceof_expr = arena.alloc_expr(AstExpr::InstanceOf {
3685 expr: null_expr,
3686 ty: ast_array_type,
3687 });
3688 let expr_stmt = arena.alloc_stmt(Stmt::Expr(instanceof_expr));
3689 let body = arena.alloc_stmt(Stmt::Block(vec![expr_stmt]));
3690
3691 let mut generator = CodeGenerator::new(
3692 &arena,
3693 symbol_table.type_arena(),
3694 &symbol_table,
3695 &mut constant_pool,
3696 );
3697 let (instructions, _max_stack, _max_locals) =
3698 generator.generate_method_body(false, &[], body)?;
3699 let unsupported_features = generator.take_unsupported_features();
3700
3701 assert!(unsupported_features.is_empty());
3702 assert_eq!(instructions[0], Instruction::Aconst_null);
3703 let Instruction::Instanceof(class_index) = instructions[1] else {
3704 panic!("expected instanceof");
3705 };
3706 assert_eq!(
3707 constant_pool
3708 .try_get_class(class_index)
3709 .expect("class constant"),
3710 "[I"
3711 );
3712 assert_eq!(instructions[2], Instruction::Pop);
3713 assert_eq!(instructions[3], Instruction::Return);
3714
3715 Ok(())
3716 }
3717
3718 #[test]
3719 fn primitive_new_array_expressions_emit_newarray() -> RajacResult<()> {
3720 let mut arena = AstArena::new();
3721 let mut symbol_table = SymbolTable::new();
3722 let mut constant_pool = ConstantPool::new();
3723
3724 let int_type = symbol_table
3725 .primitive_type_id("int")
3726 .expect("missing int type");
3727 let array_type = symbol_table.type_arena_mut().alloc(Type::array(int_type));
3728 let ast_int_type = arena.alloc_type(AstType::Primitive {
3729 kind: PrimitiveType::Int,
3730 ty: int_type,
3731 });
3732 let dimension = arena.alloc_expr(AstExpr::Literal(Literal {
3733 kind: LiteralKind::Int,
3734 value: "3".into(),
3735 }));
3736 let new_array = arena.alloc_expr(AstExpr::NewArray {
3737 ty: ast_int_type,
3738 dimensions: vec![dimension],
3739 initializer: None,
3740 });
3741 arena.expr_typed_mut(new_array).ty = array_type;
3742 let expr_stmt = arena.alloc_stmt(Stmt::Expr(new_array));
3743 let body = arena.alloc_stmt(Stmt::Block(vec![expr_stmt]));
3744
3745 let mut generator = CodeGenerator::new(
3746 &arena,
3747 symbol_table.type_arena(),
3748 &symbol_table,
3749 &mut constant_pool,
3750 );
3751 let (instructions, _max_stack, _max_locals) =
3752 generator.generate_method_body(false, &[], body)?;
3753
3754 assert_eq!(
3755 instructions,
3756 vec![
3757 Instruction::Iconst_3,
3758 Instruction::Newarray(JvmArrayType::Int),
3759 Instruction::Pop,
3760 Instruction::Return,
3761 ]
3762 );
3763
3764 Ok(())
3765 }
3766
3767 #[test]
3768 fn reference_new_array_expressions_emit_anewarray() -> RajacResult<()> {
3769 let mut arena = AstArena::new();
3770 let mut symbol_table = SymbolTable::new();
3771 let mut constant_pool = ConstantPool::new();
3772
3773 let string_type = symbol_table.type_arena_mut().alloc(Type::class(
3774 ClassType::new(SharedString::new("String"))
3775 .with_package(SharedString::new("java.lang")),
3776 ));
3777 let array_type = symbol_table
3778 .type_arena_mut()
3779 .alloc(Type::array(string_type));
3780 let ast_string_type = arena.alloc_type(AstType::Simple {
3781 name: SharedString::new("String"),
3782 ty: string_type,
3783 type_args: vec![],
3784 });
3785 let dimension = arena.alloc_expr(AstExpr::Literal(Literal {
3786 kind: LiteralKind::Int,
3787 value: "4".into(),
3788 }));
3789 let new_array = arena.alloc_expr(AstExpr::NewArray {
3790 ty: ast_string_type,
3791 dimensions: vec![dimension],
3792 initializer: None,
3793 });
3794 arena.expr_typed_mut(new_array).ty = array_type;
3795 let expr_stmt = arena.alloc_stmt(Stmt::Expr(new_array));
3796 let body = arena.alloc_stmt(Stmt::Block(vec![expr_stmt]));
3797
3798 let mut generator = CodeGenerator::new(
3799 &arena,
3800 symbol_table.type_arena(),
3801 &symbol_table,
3802 &mut constant_pool,
3803 );
3804 let (instructions, _max_stack, _max_locals) =
3805 generator.generate_method_body(false, &[], body)?;
3806
3807 assert!(matches!(instructions[0], Instruction::Iconst_4));
3808 let Instruction::Anewarray(class_index) = instructions[1] else {
3809 panic!("expected anewarray");
3810 };
3811 assert_eq!(
3812 constant_pool
3813 .try_get_class(class_index)
3814 .expect("class constant"),
3815 "java/lang/String"
3816 );
3817 assert_eq!(instructions[2], Instruction::Pop);
3818 assert_eq!(instructions[3], Instruction::Return);
3819
3820 Ok(())
3821 }
3822
3823 #[test]
3824 fn multidimensional_new_array_expressions_emit_multianewarray() -> RajacResult<()> {
3825 let mut arena = AstArena::new();
3826 let mut symbol_table = SymbolTable::new();
3827 let mut constant_pool = ConstantPool::new();
3828
3829 let int_type = symbol_table
3830 .primitive_type_id("int")
3831 .expect("missing int type");
3832 let nested_array = symbol_table.type_arena_mut().alloc(Type::array(int_type));
3833 let array_type = symbol_table
3834 .type_arena_mut()
3835 .alloc(Type::array(nested_array));
3836 let ast_int_type = arena.alloc_type(AstType::Primitive {
3837 kind: PrimitiveType::Int,
3838 ty: int_type,
3839 });
3840 let rows = arena.alloc_expr(AstExpr::Literal(Literal {
3841 kind: LiteralKind::Int,
3842 value: "2".into(),
3843 }));
3844 let cols = arena.alloc_expr(AstExpr::Literal(Literal {
3845 kind: LiteralKind::Int,
3846 value: "5".into(),
3847 }));
3848 let new_array = arena.alloc_expr(AstExpr::NewArray {
3849 ty: ast_int_type,
3850 dimensions: vec![rows, cols],
3851 initializer: None,
3852 });
3853 arena.expr_typed_mut(new_array).ty = array_type;
3854 let expr_stmt = arena.alloc_stmt(Stmt::Expr(new_array));
3855 let body = arena.alloc_stmt(Stmt::Block(vec![expr_stmt]));
3856
3857 let mut generator = CodeGenerator::new(
3858 &arena,
3859 symbol_table.type_arena(),
3860 &symbol_table,
3861 &mut constant_pool,
3862 );
3863 let (instructions, _max_stack, _max_locals) =
3864 generator.generate_method_body(false, &[], body)?;
3865
3866 assert!(matches!(instructions[0], Instruction::Iconst_2));
3867 assert!(matches!(instructions[1], Instruction::Iconst_5));
3868 let Instruction::Multianewarray(class_index, dimensions) = instructions[2] else {
3869 panic!("expected multianewarray");
3870 };
3871 assert_eq!(dimensions, 2);
3872 assert_eq!(
3873 constant_pool
3874 .try_get_class(class_index)
3875 .expect("class constant"),
3876 "[[I"
3877 );
3878 assert_eq!(instructions[3], Instruction::Pop);
3879 assert_eq!(instructions[4], Instruction::Return);
3880
3881 Ok(())
3882 }
3883
3884 #[test]
3885 fn primitive_array_initializer_expressions_emit_stores() -> RajacResult<()> {
3886 let mut arena = AstArena::new();
3887 let mut symbol_table = SymbolTable::new();
3888 let mut constant_pool = ConstantPool::new();
3889
3890 let int_type = symbol_table
3891 .primitive_type_id("int")
3892 .expect("missing int type");
3893 let array_type = symbol_table.type_arena_mut().alloc(Type::array(int_type));
3894 let ast_element_type = arena.alloc_type(AstType::Primitive {
3895 kind: PrimitiveType::Int,
3896 ty: int_type,
3897 });
3898 let ast_int_type = arena.alloc_type(AstType::array(ast_element_type, 1));
3899 let first = arena.alloc_expr(AstExpr::Literal(Literal {
3900 kind: LiteralKind::Int,
3901 value: "1".into(),
3902 }));
3903 let second = arena.alloc_expr(AstExpr::Literal(Literal {
3904 kind: LiteralKind::Int,
3905 value: "2".into(),
3906 }));
3907 let initializer = arena.alloc_expr(AstExpr::ArrayInitializer {
3908 elements: vec![first, second],
3909 });
3910 let new_array = arena.alloc_expr(AstExpr::NewArray {
3911 ty: ast_int_type,
3912 dimensions: vec![],
3913 initializer: Some(initializer),
3914 });
3915 arena.expr_typed_mut(new_array).ty = array_type;
3916 let expr_stmt = arena.alloc_stmt(Stmt::Expr(new_array));
3917 let body = arena.alloc_stmt(Stmt::Block(vec![expr_stmt]));
3918
3919 let mut generator = CodeGenerator::new(
3920 &arena,
3921 symbol_table.type_arena(),
3922 &symbol_table,
3923 &mut constant_pool,
3924 );
3925 let (instructions, _, _) = generator.generate_method_body(false, &[], body)?;
3926
3927 assert_eq!(
3928 instructions,
3929 vec![
3930 Instruction::Iconst_2,
3931 Instruction::Newarray(JvmArrayType::Int),
3932 Instruction::Dup,
3933 Instruction::Iconst_0,
3934 Instruction::Iconst_1,
3935 Instruction::Iastore,
3936 Instruction::Dup,
3937 Instruction::Iconst_1,
3938 Instruction::Iconst_2,
3939 Instruction::Iastore,
3940 Instruction::Pop,
3941 Instruction::Return,
3942 ]
3943 );
3944
3945 Ok(())
3946 }
3947
3948 #[test]
3949 fn nested_array_initializer_expressions_emit_recursive_array_construction() -> RajacResult<()> {
3950 let mut arena = AstArena::new();
3951 let mut symbol_table = SymbolTable::new();
3952 let mut constant_pool = ConstantPool::new();
3953
3954 let int_type = symbol_table
3955 .primitive_type_id("int")
3956 .expect("missing int type");
3957 let inner_array_type = symbol_table.type_arena_mut().alloc(Type::array(int_type));
3958 let outer_array_type = symbol_table
3959 .type_arena_mut()
3960 .alloc(Type::array(inner_array_type));
3961 let ast_int_type = arena.alloc_type(AstType::Primitive {
3962 kind: PrimitiveType::Int,
3963 ty: int_type,
3964 });
3965 let ast_array_type = arena.alloc_type(AstType::array(ast_int_type, 2));
3966 let first_value = arena.alloc_expr(AstExpr::Literal(Literal {
3967 kind: LiteralKind::Int,
3968 value: "1".into(),
3969 }));
3970 let first_inner = arena.alloc_expr(AstExpr::ArrayInitializer {
3971 elements: vec![first_value],
3972 });
3973 let second_value = arena.alloc_expr(AstExpr::Literal(Literal {
3974 kind: LiteralKind::Int,
3975 value: "2".into(),
3976 }));
3977 let third_value = arena.alloc_expr(AstExpr::Literal(Literal {
3978 kind: LiteralKind::Int,
3979 value: "3".into(),
3980 }));
3981 let second_inner = arena.alloc_expr(AstExpr::ArrayInitializer {
3982 elements: vec![second_value, third_value],
3983 });
3984 let outer_initializer = arena.alloc_expr(AstExpr::ArrayInitializer {
3985 elements: vec![first_inner, second_inner],
3986 });
3987 let new_array = arena.alloc_expr(AstExpr::NewArray {
3988 ty: ast_array_type,
3989 dimensions: vec![],
3990 initializer: Some(outer_initializer),
3991 });
3992 arena.expr_typed_mut(new_array).ty = outer_array_type;
3993 let expr_stmt = arena.alloc_stmt(Stmt::Expr(new_array));
3994 let body = arena.alloc_stmt(Stmt::Block(vec![expr_stmt]));
3995
3996 let mut generator = CodeGenerator::new(
3997 &arena,
3998 symbol_table.type_arena(),
3999 &symbol_table,
4000 &mut constant_pool,
4001 );
4002 let (instructions, _, _) = generator.generate_method_body(false, &[], body)?;
4003
4004 assert!(matches!(instructions[0], Instruction::Iconst_2));
4005 let Instruction::Anewarray(class_index) = instructions[1] else {
4006 panic!("expected outer anewarray");
4007 };
4008 assert_eq!(
4009 constant_pool
4010 .try_get_class(class_index)
4011 .expect("class constant"),
4012 "[I"
4013 );
4014 assert!(instructions.contains(&Instruction::Iastore));
4015 assert!(instructions.contains(&Instruction::Aastore));
4016 assert_eq!(instructions.last(), Some(&Instruction::Return));
4017
4018 Ok(())
4019 }
4020
4021 #[test]
4022 fn method_call_stack_delta_handles_void_and_wide_descriptors() {
4023 assert_eq!(
4024 method_call_stack_delta("(Ljava/lang/String;)V", true),
4025 Some(-2)
4026 );
4027 assert_eq!(method_call_stack_delta("()I", true), Some(0));
4028 assert_eq!(method_call_stack_delta("(JD)J", true), Some(-3));
4029 assert_eq!(method_call_stack_delta("(I)V", false), Some(-1));
4030 }
4031
4032 #[test]
4033 fn resolved_static_method_calls_emit_invokestatic() -> RajacResult<()> {
4034 let arena = AstArena::new();
4035 let mut symbol_table = SymbolTable::new();
4036 let mut constant_pool = ConstantPool::new();
4037 let void_type = symbol_table
4038 .primitive_type_id_by_kind(rajac_types::PrimitiveType::Void)
4039 .expect("void type");
4040 let method_id = add_owner_method(
4041 &mut symbol_table,
4042 "example",
4043 "Util",
4044 SymbolKind::Class,
4045 MethodSignature::new(
4046 SharedString::new("ping"),
4047 vec![],
4048 void_type,
4049 MethodModifiers(MethodModifiers::STATIC | MethodModifiers::PUBLIC),
4050 ),
4051 );
4052
4053 let mut generator = CodeGenerator::new(
4054 &arena,
4055 symbol_table.type_arena(),
4056 &symbol_table,
4057 &mut constant_pool,
4058 );
4059 generator.emit_method_call(
4060 None,
4061 &Ident::new("ping".into()),
4062 &[],
4063 Some(method_id),
4064 void_type,
4065 )?;
4066
4067 assert!(matches!(
4068 generator.emitter.code_items.as_slice(),
4069 [CodeItem::Instruction(Instruction::Invokestatic(_))]
4070 ));
4071 Ok(())
4072 }
4073
4074 #[test]
4075 fn resolved_interface_method_calls_emit_invokeinterface() -> RajacResult<()> {
4076 let mut arena = AstArena::new();
4077 let mut symbol_table = SymbolTable::new();
4078 let mut constant_pool = ConstantPool::new();
4079 let void_type = symbol_table
4080 .primitive_type_id_by_kind(rajac_types::PrimitiveType::Void)
4081 .expect("void type");
4082 let method_id = add_owner_method(
4083 &mut symbol_table,
4084 "example",
4085 "RunnableLike",
4086 SymbolKind::Interface,
4087 MethodSignature::new(
4088 SharedString::new("run"),
4089 vec![],
4090 void_type,
4091 MethodModifiers(MethodModifiers::PUBLIC),
4092 ),
4093 );
4094 let receiver_expr = arena.alloc_expr(AstExpr::Literal(Literal {
4095 kind: LiteralKind::Null,
4096 value: "null".into(),
4097 }));
4098
4099 let mut generator = CodeGenerator::new(
4100 &arena,
4101 symbol_table.type_arena(),
4102 &symbol_table,
4103 &mut constant_pool,
4104 );
4105 generator.emit_method_call(
4106 Some(&receiver_expr),
4107 &Ident::new("run".into()),
4108 &[],
4109 Some(method_id),
4110 void_type,
4111 )?;
4112
4113 assert!(matches!(
4114 generator.emitter.code_items.as_slice(),
4115 [
4116 CodeItem::Instruction(Instruction::Aconst_null),
4117 CodeItem::Instruction(Instruction::Invokeinterface(_, 1))
4118 ]
4119 ));
4120 Ok(())
4121 }
4122
4123 #[test]
4124 fn implicit_private_method_calls_emit_invokevirtual() -> RajacResult<()> {
4125 let arena = AstArena::new();
4126 let mut symbol_table = SymbolTable::new();
4127 let mut constant_pool = ConstantPool::new();
4128 let void_type = symbol_table
4129 .primitive_type_id_by_kind(rajac_types::PrimitiveType::Void)
4130 .expect("void type");
4131 let method_id = add_owner_method(
4132 &mut symbol_table,
4133 "example",
4134 "Widget",
4135 SymbolKind::Class,
4136 MethodSignature::new(
4137 SharedString::new("tick"),
4138 vec![],
4139 void_type,
4140 MethodModifiers(MethodModifiers::PRIVATE),
4141 ),
4142 );
4143
4144 let mut generator = CodeGenerator::new(
4145 &arena,
4146 symbol_table.type_arena(),
4147 &symbol_table,
4148 &mut constant_pool,
4149 );
4150 generator.emit_method_call(
4151 None,
4152 &Ident::new("tick".into()),
4153 &[],
4154 Some(method_id),
4155 void_type,
4156 )?;
4157
4158 assert!(matches!(
4159 generator.emitter.code_items.as_slice(),
4160 [
4161 CodeItem::Instruction(Instruction::Aload_0),
4162 CodeItem::Instruction(Instruction::Invokevirtual(_))
4163 ]
4164 ));
4165 Ok(())
4166 }
4167
4168 #[test]
4169 fn explicit_super_method_calls_emit_invokespecial() -> RajacResult<()> {
4170 let mut arena = AstArena::new();
4171 let mut symbol_table = SymbolTable::new();
4172 let mut constant_pool = ConstantPool::new();
4173 let void_type = symbol_table
4174 .primitive_type_id_by_kind(rajac_types::PrimitiveType::Void)
4175 .expect("void type");
4176 let method_id = add_owner_method(
4177 &mut symbol_table,
4178 "example",
4179 "Base",
4180 SymbolKind::Class,
4181 MethodSignature::new(
4182 SharedString::new("tick"),
4183 vec![],
4184 void_type,
4185 MethodModifiers(MethodModifiers::PUBLIC),
4186 ),
4187 );
4188 let super_expr = arena.alloc_expr(AstExpr::Super);
4189
4190 let mut generator = CodeGenerator::new(
4191 &arena,
4192 symbol_table.type_arena(),
4193 &symbol_table,
4194 &mut constant_pool,
4195 );
4196 generator.emit_method_call(
4197 Some(&super_expr),
4198 &Ident::new("tick".into()),
4199 &[],
4200 Some(method_id),
4201 void_type,
4202 )?;
4203
4204 assert!(matches!(
4205 generator.emitter.code_items.as_slice(),
4206 [
4207 CodeItem::Instruction(Instruction::Aload_0),
4208 CodeItem::Instruction(Instruction::Invokespecial(_))
4209 ]
4210 ));
4211 Ok(())
4212 }
4213}