Skip to main content

rajac_bytecode/
bytecode.rs

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    /// User-facing message shared between diagnostics and runtime stubs.
17    pub message: SharedString,
18    /// Source marker used to anchor the generation-stage diagnostic.
19    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        /* 📖 # Why append a terminal nop for some label targets?
86        The classfile library expects branch targets to resolve to an actual instruction index.
87        Control-flow lowering can bind a label after the last emitted instruction, for example when
88        both `if` branches return but the shared end label is still referenced by a `goto`.
89        Appending a `nop` turns that terminal label into a concrete instruction boundary without
90        changing program behavior, which keeps serialization valid.
91        */
92        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, // slot 0 is for 'this'
162            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        // Handle string concatenation: if either operand is a string literal
1767        // and the operation is Add, compute the result at compile time
1768        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            // String + anything or anything + String = string concatenation
1798            // Check for compile-time constant string concatenation
1799            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            // String + anything or anything + String = string concatenation (non-constant)
1831            // Use invokedynamic (same as OpenJDK 11+)
1832            if lhs_string.is_some() || rhs_string.is_some() {
1833                // Emit both operands
1834                self.emit_expression(lhs)?;
1835                self.emit_expression(rhs)?;
1836
1837                // invokedynamic for string concatenation
1838                // Bootstrap method: StringConcatFactory.makeConcatWithConstants
1839                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}