Skip to main content

just_engine/runner/jit/
reg_compiler.rs

1//! AST-to-register-bytecode compiler.
2//!
3//! Emits a 3-address register bytecode representation that the register VM can execute.
4
5use crate::parser::ast::{
6    AssignmentOperator, BinaryOperator, BlockStatementData, DeclarationType,
7    ExpressionOrSpreadElement, ExpressionOrSuper, ExpressionPatternType, ExpressionType,
8    ForIteratorData, FunctionBodyData, LiteralData, LiteralType, LogicalOperator,
9    MemberExpressionType, NumberLiteralType, PatternOrExpression, PatternType, ProgramData,
10    StatementType, SwitchCaseData, UnaryOperator, UpdateOperator, VariableDeclarationData,
11    VariableDeclarationKind, VariableDeclarationOrExpression,
12};
13use crate::runner::ds::value::{JsNumberType, JsValue};
14use std::collections::{HashMap, HashSet};
15
16use super::reg_bytecode::{RegChunk, RegInstruction, RegOpCode};
17
18struct LoopContext {
19    continue_target: usize,
20    break_jumps: Vec<usize>,
21    continue_jumps: Vec<usize>,
22}
23
24pub struct RegCompiler {
25    chunk: RegChunk,
26    loop_stack: Vec<LoopContext>,
27    locals: HashMap<String, u32>,
28    local_regs: HashSet<u32>,
29    lexical_scopes: Vec<HashSet<String>>,
30    next_reg: u32,
31    free_regs: Vec<u32>,
32}
33
34impl RegCompiler {
35    pub fn new() -> Self {
36        RegCompiler {
37            chunk: RegChunk::new(),
38            loop_stack: Vec::new(),
39            locals: HashMap::new(),
40            local_regs: HashSet::new(),
41            lexical_scopes: vec![HashSet::new()],
42            next_reg: 0,
43            free_regs: Vec::new(),
44        }
45    }
46
47    pub fn compile_program(mut self, program: &ProgramData) -> RegChunk {
48        for stmt in &program.body {
49            self.compile_statement(stmt);
50        }
51        self.chunk.emit_op(RegOpCode::Halt);
52        self.chunk.set_register_count(self.next_reg);
53        self.chunk
54    }
55
56    fn alloc_reg(&mut self) -> u32 {
57        if let Some(reg) = self.free_regs.pop() {
58            return reg;
59        }
60        let reg = self.next_reg;
61        self.next_reg += 1;
62        reg
63    }
64
65    fn release_reg(&mut self, reg: u32) {
66        if self.local_regs.contains(&reg) {
67            return;
68        }
69        self.free_regs.push(reg);
70    }
71
72    fn get_local_reg(&self, name: &str) -> Option<u32> {
73        self.locals.get(name).copied()
74    }
75
76    fn get_or_add_local_reg(&mut self, name: &str) -> u32 {
77        if let Some(reg) = self.locals.get(name) {
78            return *reg;
79        }
80        let reg = self.alloc_reg();
81        let name_idx = self.chunk.add_name(name);
82        self.chunk.add_local(name_idx, reg);
83        self.locals.insert(name.to_string(), reg);
84        self.local_regs.insert(reg);
85        reg
86    }
87
88    fn push_lexical_scope(&mut self) {
89        self.lexical_scopes.push(HashSet::new());
90    }
91
92    fn pop_lexical_scope(&mut self) {
93        self.lexical_scopes.pop();
94    }
95
96    fn declare_lexical(&mut self, name: &str) {
97        if let Some(scope) = self.lexical_scopes.last_mut() {
98            scope.insert(name.to_string());
99        }
100    }
101
102    fn is_lexically_shadowed(&self, name: &str) -> bool {
103        self.lexical_scopes
104            .iter()
105            .rev()
106            .any(|scope| scope.contains(name))
107    }
108
109    // ════════════════════════════════════════════════════════════
110    // Statements
111    // ════════════════════════════════════════════════════════════
112
113    fn compile_statement(&mut self, stmt: &StatementType) {
114        match stmt {
115            StatementType::EmptyStatement { .. } => {}
116            StatementType::ExpressionStatement { expression, .. } => {
117                let reg = self.compile_expression(expression);
118                self.release_reg(reg);
119            }
120            StatementType::BlockStatement(block) => {
121                self.compile_block(block);
122            }
123            StatementType::DeclarationStatement(decl) => {
124                self.compile_declaration(decl);
125            }
126            StatementType::IfStatement { test, consequent, alternate, .. } => {
127                self.compile_if(test, consequent, alternate.as_ref().map(|a| a.as_ref()));
128            }
129            StatementType::WhileStatement { test, body, .. } => {
130                self.compile_while(test, body);
131            }
132            StatementType::DoWhileStatement { test, body, .. } => {
133                self.compile_do_while(body, test);
134            }
135            StatementType::ForStatement { init, test, update, body, .. } => {
136                self.compile_for(init.as_ref(), test.as_ref().map(|t| t.as_ref()), update.as_ref().map(|u| u.as_ref()), body);
137            }
138            StatementType::ForInStatement(data) => {
139                self.compile_for_in(data);
140            }
141            StatementType::ForOfStatement(data) => {
142                self.compile_for_of(data);
143            }
144            StatementType::SwitchStatement { discriminant, cases, .. } => {
145                self.compile_switch(discriminant, cases);
146            }
147            StatementType::BreakStatement { .. } => {
148                self.compile_break();
149            }
150            StatementType::ContinueStatement { .. } => {
151                self.compile_continue();
152            }
153            StatementType::ReturnStatement { argument, .. } => {
154                if let Some(arg) = argument {
155                    let reg = self.compile_expression(arg);
156                    self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Return, 0, reg));
157                } else {
158                    let reg = self.alloc_reg();
159                    self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadUndefined, reg));
160                    self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Return, 0, reg));
161                    self.release_reg(reg);
162                }
163            }
164            StatementType::ThrowStatement { argument, .. } => {
165                let reg = self.compile_expression(argument);
166                self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Return, 0, reg));
167            }
168            StatementType::TryStatement { block, handler: _, finalizer, .. } => {
169                self.compile_block(block);
170                if let Some(fin) = finalizer {
171                    self.compile_block(fin);
172                }
173            }
174            StatementType::DebuggerStatement { .. } => {}
175            StatementType::FunctionBody(body) => {
176                self.compile_function_body(body);
177            }
178        }
179    }
180
181    fn compile_block(&mut self, block: &BlockStatementData) {
182        self.push_lexical_scope();
183        for stmt in &block.body {
184            self.compile_statement(stmt);
185        }
186        self.pop_lexical_scope();
187    }
188
189    fn compile_declaration(&mut self, decl: &DeclarationType) {
190        match decl {
191            DeclarationType::VariableDeclaration(var_decl) => {
192                self.compile_var_declaration(var_decl);
193            }
194            DeclarationType::FunctionOrGeneratorDeclaration(func_data) => {
195                if let Some(ref id) = func_data.id {
196                    let reg = self.get_or_add_local_reg(&id.name);
197                    self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadUndefined, reg));
198                }
199            }
200            DeclarationType::ClassDeclaration(_) => {}
201        }
202    }
203
204    fn compile_var_declaration(&mut self, var_decl: &VariableDeclarationData) {
205        let (declare_op, init_op) = match var_decl.kind {
206            VariableDeclarationKind::Var => (RegOpCode::DeclareVar, RegOpCode::InitVar),
207            VariableDeclarationKind::Let => (RegOpCode::DeclareLet, RegOpCode::InitBinding),
208            VariableDeclarationKind::Const => (RegOpCode::DeclareConst, RegOpCode::InitBinding),
209        };
210
211        for declarator in &var_decl.declarations {
212            if let PatternType::PatternWhichCanBeExpression(ExpressionPatternType::Identifier(ref id)) = declarator.id.as_ref() {
213                if matches!(var_decl.kind, VariableDeclarationKind::Var) {
214                    let reg = self.get_or_add_local_reg(&id.name);
215                    if let Some(ref init_expr) = declarator.init {
216                        let init_reg = self.compile_expression(init_expr);
217                        if init_reg != reg {
218                            self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Move, reg, init_reg));
219                        }
220                        self.release_reg(init_reg);
221                    } else {
222                        self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadUndefined, reg));
223                    }
224                    continue;
225                }
226
227                self.declare_lexical(&id.name);
228                let name_idx = self.chunk.add_name(&id.name);
229                self.chunk.emit(RegInstruction::with_dst_imm(declare_op, 0, name_idx));
230
231                let init_reg = if let Some(ref init_expr) = declarator.init {
232                    self.compile_expression(init_expr)
233                } else {
234                    let reg = self.alloc_reg();
235                    self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadUndefined, reg));
236                    reg
237                };
238                self.chunk.emit(RegInstruction::with_src_imm(init_op, init_reg, name_idx));
239                self.release_reg(init_reg);
240            }
241        }
242    }
243
244    fn compile_if(&mut self, test: &ExpressionType, consequent: &StatementType, alternate: Option<&StatementType>) {
245        let test_reg = self.compile_expression(test);
246        let jump_to_else = self.emit_jump(RegOpCode::JumpIfFalse, test_reg, 0);
247        self.compile_statement(consequent);
248        if let Some(alt) = alternate {
249            let jump_over_else = self.emit_jump(RegOpCode::Jump, 0, 0);
250            self.patch_jump(jump_to_else);
251            self.compile_statement(alt);
252            self.patch_jump(jump_over_else);
253        } else {
254            self.patch_jump(jump_to_else);
255        }
256        self.release_reg(test_reg);
257    }
258
259    fn compile_while(&mut self, test: &ExpressionType, body: &StatementType) {
260        let loop_start = self.chunk.code.len();
261        self.loop_stack.push(LoopContext { continue_target: loop_start, break_jumps: Vec::new(), continue_jumps: Vec::new() });
262
263        let test_reg = self.compile_expression(test);
264        let exit_jump = self.emit_jump(RegOpCode::JumpIfFalse, test_reg, 0);
265        self.release_reg(test_reg);
266
267        self.compile_statement(body);
268        self.chunk.emit(RegInstruction::with_dst_imm(RegOpCode::Jump, 0, loop_start as u32));
269
270        self.patch_jump(exit_jump);
271        let ctx = self.loop_stack.pop().unwrap();
272        for bj in ctx.break_jumps {
273            self.patch_jump(bj);
274        }
275    }
276
277    fn compile_do_while(&mut self, body: &StatementType, test: &ExpressionType) {
278        let loop_start = self.chunk.code.len();
279        self.loop_stack.push(LoopContext { continue_target: loop_start, break_jumps: Vec::new(), continue_jumps: Vec::new() });
280
281        self.compile_statement(body);
282
283        let continue_target = self.chunk.code.len();
284        if let Some(ctx) = self.loop_stack.last_mut() {
285            ctx.continue_target = continue_target;
286        }
287        let test_reg = self.compile_expression(test);
288        self.chunk.emit(RegInstruction::with_src_imm(RegOpCode::JumpIfTrue, test_reg, loop_start as u32));
289        self.release_reg(test_reg);
290
291        let ctx = self.loop_stack.pop().unwrap();
292        for cj in &ctx.continue_jumps {
293            self.chunk.code[*cj].imm = continue_target as u32;
294        }
295        for bj in ctx.break_jumps {
296            self.patch_jump(bj);
297        }
298    }
299
300    fn compile_for(
301        &mut self,
302        init: Option<&VariableDeclarationOrExpression>,
303        test: Option<&ExpressionType>,
304        update: Option<&ExpressionType>,
305        body: &StatementType,
306    ) {
307        self.push_lexical_scope();
308
309        if let Some(init) = init {
310            match init {
311                VariableDeclarationOrExpression::VariableDeclaration(var_decl) => {
312                    self.compile_var_declaration(var_decl);
313                }
314                VariableDeclarationOrExpression::Expression(expr) => {
315                    let reg = self.compile_expression(expr);
316                    self.release_reg(reg);
317                }
318            }
319        }
320
321        let loop_start = self.chunk.code.len();
322        self.loop_stack.push(LoopContext { continue_target: loop_start, break_jumps: Vec::new(), continue_jumps: Vec::new() });
323
324        let exit_jump = if let Some(test) = test {
325            let test_reg = self.compile_expression(test);
326            let jump = self.emit_jump(RegOpCode::JumpIfFalse, test_reg, 0);
327            self.release_reg(test_reg);
328            Some(jump)
329        } else {
330            None
331        };
332
333        self.compile_statement(body);
334
335        let update_pos = self.chunk.code.len();
336        if let Some(ctx) = self.loop_stack.last_mut() {
337            ctx.continue_target = update_pos;
338        }
339
340        if let Some(update) = update {
341            let reg = self.compile_expression(update);
342            self.release_reg(reg);
343        }
344
345        self.chunk.emit(RegInstruction::with_dst_imm(RegOpCode::Jump, 0, loop_start as u32));
346
347        if let Some(jump) = exit_jump {
348            self.patch_jump(jump);
349        }
350
351        let ctx = self.loop_stack.pop().unwrap();
352        // Patch continue jumps to the update position
353        for cj in &ctx.continue_jumps {
354            self.chunk.code[*cj].imm = update_pos as u32;
355        }
356        for bj in ctx.break_jumps {
357            self.patch_jump(bj);
358        }
359
360        self.pop_lexical_scope();
361    }
362
363    fn compile_for_in(&mut self, _data: &ForIteratorData) {}
364
365    fn compile_for_of(&mut self, _data: &ForIteratorData) {}
366
367    fn compile_switch(&mut self, discriminant: &ExpressionType, cases: &[SwitchCaseData]) {
368        let disc_reg = self.compile_expression(discriminant);
369        let mut case_body_jumps: Vec<usize> = Vec::new();
370        let mut default_jump: Option<usize> = None;
371
372        for case in cases {
373            if let Some(ref test) = case.test {
374                let test_reg = self.compile_expression(test);
375                let res_reg = self.alloc_reg();
376                self.chunk.emit(RegInstruction::with_dst_srcs(RegOpCode::StrictEqual, res_reg, disc_reg, test_reg));
377                let jump = self.emit_jump(RegOpCode::JumpIfTrue, res_reg, 0);
378                self.release_reg(test_reg);
379                self.release_reg(res_reg);
380                case_body_jumps.push(jump);
381            } else {
382                default_jump = Some(case_body_jumps.len());
383                case_body_jumps.push(0);
384            }
385        }
386
387        let jump_to_default_or_end = self.emit_jump(RegOpCode::Jump, 0, 0);
388
389        self.loop_stack.push(LoopContext { continue_target: 0, break_jumps: Vec::new(), continue_jumps: Vec::new() });
390
391        for (i, case) in cases.iter().enumerate() {
392            let body_pos = self.chunk.code.len();
393            if i < case_body_jumps.len() && case_body_jumps[i] != 0 {
394                self.chunk.code[case_body_jumps[i]].imm = body_pos as u32;
395            }
396            if default_jump == Some(i) {
397                self.chunk.code[jump_to_default_or_end].imm = body_pos as u32;
398            }
399            for stmt in &case.consequent {
400                self.compile_statement(stmt);
401            }
402        }
403
404        let end_pos = self.chunk.code.len();
405        if default_jump.is_none() {
406            self.chunk.code[jump_to_default_or_end].imm = end_pos as u32;
407        }
408
409        let ctx = self.loop_stack.pop().unwrap();
410        for bj in ctx.break_jumps {
411            self.patch_jump(bj);
412        }
413
414        self.release_reg(disc_reg);
415    }
416
417    fn compile_break(&mut self) {
418        if self.loop_stack.is_empty() {
419            return;
420        }
421        let jump = self.emit_jump(RegOpCode::Jump, 0, 0);
422        if let Some(ctx) = self.loop_stack.last_mut() {
423            ctx.break_jumps.push(jump);
424        }
425    }
426
427    fn compile_continue(&mut self) {
428        if let Some(ctx) = self.loop_stack.last() {
429            let target = ctx.continue_target;
430            let jump = self.chunk.emit(RegInstruction::with_dst_imm(RegOpCode::Jump, 0, target as u32));
431            // Record the jump so it can be patched later (for for-loops where
432            // the update position isn't known yet when continue is compiled).
433            if let Some(ctx) = self.loop_stack.last_mut() {
434                ctx.continue_jumps.push(jump);
435            }
436        }
437    }
438
439    fn emit_jump(&mut self, op: RegOpCode, src1: u32, target: u32) -> usize {
440        let instr = match op {
441            RegOpCode::Jump => RegInstruction::with_dst_imm(op, 0, target),
442            RegOpCode::JumpIfFalse | RegOpCode::JumpIfTrue => RegInstruction::with_src_imm(op, src1, target),
443            _ => RegInstruction::with_dst_imm(op, 0, target),
444        };
445        self.chunk.emit(instr)
446    }
447
448    fn patch_jump(&mut self, jump_idx: usize) {
449        let target = self.chunk.code.len() as u32;
450        self.chunk.code[jump_idx].imm = target;
451    }
452
453    fn compile_function_body(&mut self, body: &FunctionBodyData) {
454        for stmt in &body.body {
455            self.compile_statement(stmt);
456        }
457        self.chunk.emit_op(RegOpCode::Halt);
458    }
459
460    // ════════════════════════════════════════════════════════════
461    // Expressions
462    // ════════════════════════════════════════════════════════════
463
464    fn compile_expression(&mut self, expr: &ExpressionType) -> u32 {
465        match expr {
466            ExpressionType::Literal(lit) => self.compile_literal(lit),
467            ExpressionType::ExpressionWhichCanBePattern(pattern) => self.compile_expression_pattern(pattern),
468            ExpressionType::BinaryExpression { operator, left, right, .. } => self.compile_binary(operator, left, right),
469            ExpressionType::LogicalExpression { operator, left, right, .. } => self.compile_logical(operator, left, right),
470            ExpressionType::AssignmentExpression { operator, left, right, .. } => self.compile_assignment(operator, left, right),
471            ExpressionType::UnaryExpression { operator, argument, .. } => self.compile_unary(operator, argument),
472            ExpressionType::MemberExpression(member) => self.compile_member_expression(member),
473            ExpressionType::CallExpression { callee, arguments, .. } => self.compile_call_expression(callee, arguments),
474            ExpressionType::UpdateExpression { operator, argument, prefix, .. } => self.compile_update(operator, argument, *prefix),
475            ExpressionType::SequenceExpression { expressions, .. } => {
476                let mut last_reg = self.alloc_reg();
477                self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadUndefined, last_reg));
478                for (i, expr) in expressions.iter().enumerate() {
479                    if i > 0 {
480                        self.release_reg(last_reg);
481                    }
482                    last_reg = self.compile_expression(expr);
483                }
484                last_reg
485            }
486            ExpressionType::ConditionalExpression { test, consequent, alternate, .. } => {
487                let test_reg = self.compile_expression(test);
488                let result_reg = self.alloc_reg();
489                let jump_to_alt = self.emit_jump(RegOpCode::JumpIfFalse, test_reg, 0);
490                self.release_reg(test_reg);
491                let cons_reg = self.compile_expression(consequent);
492                self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Move, result_reg, cons_reg));
493                self.release_reg(cons_reg);
494                let jump_to_end = self.emit_jump(RegOpCode::Jump, 0, 0);
495                self.patch_jump(jump_to_alt);
496                let alt_reg = self.compile_expression(alternate);
497                self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Move, result_reg, alt_reg));
498                self.release_reg(alt_reg);
499                self.patch_jump(jump_to_end);
500                result_reg
501            }
502            _ => {
503                let reg = self.alloc_reg();
504                self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadUndefined, reg));
505                reg
506            }
507        }
508    }
509
510    fn compile_literal(&mut self, lit: &LiteralData) -> u32 {
511        let reg = self.alloc_reg();
512        match &lit.value {
513            LiteralType::NullLiteral => {
514                self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadNull, reg));
515            }
516            LiteralType::BooleanLiteral(true) => {
517                self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadTrue, reg));
518            }
519            LiteralType::BooleanLiteral(false) => {
520                self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadFalse, reg));
521            }
522            LiteralType::StringLiteral(s) => {
523                let idx = self.chunk.add_constant(JsValue::String(s.clone()));
524                self.chunk.emit(RegInstruction::with_dst_imm(RegOpCode::LoadConst, reg, idx));
525            }
526            LiteralType::NumberLiteral(n) => {
527                let value = match n {
528                    NumberLiteralType::IntegerLiteral(i) => JsValue::Number(JsNumberType::Integer(*i)),
529                    NumberLiteralType::FloatLiteral(f) => JsValue::Number(JsNumberType::Float(*f)),
530                };
531                let idx = self.chunk.add_constant(value);
532                self.chunk.emit(RegInstruction::with_dst_imm(RegOpCode::LoadConst, reg, idx));
533            }
534            LiteralType::RegExpLiteral(_) => {
535                self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadUndefined, reg));
536            }
537        }
538        reg
539    }
540
541    fn compile_expression_pattern(&mut self, pattern: &ExpressionPatternType) -> u32 {
542        match pattern {
543            ExpressionPatternType::Identifier(id) => {
544                if !self.is_lexically_shadowed(&id.name) {
545                    if let Some(reg) = self.get_local_reg(&id.name) {
546                        return reg;
547                    }
548                }
549                let name_idx = self.chunk.add_name(&id.name);
550                let reg = self.alloc_reg();
551                self.chunk.emit(RegInstruction::with_dst_imm(RegOpCode::GetVar, reg, name_idx));
552                reg
553            }
554        }
555    }
556
557    fn compile_binary(&mut self, operator: &BinaryOperator, left: &ExpressionType, right: &ExpressionType) -> u32 {
558        let left_reg = self.compile_expression(left);
559        let right_reg = self.compile_expression(right);
560        let dst = self.alloc_reg();
561        let op = match operator {
562            BinaryOperator::Add => RegOpCode::Add,
563            BinaryOperator::Subtract => RegOpCode::Sub,
564            BinaryOperator::Multiply => RegOpCode::Mul,
565            BinaryOperator::Divide => RegOpCode::Div,
566            BinaryOperator::Modulo => RegOpCode::Mod,
567            BinaryOperator::BitwiseAnd => RegOpCode::BitAnd,
568            BinaryOperator::BitwiseOr => RegOpCode::BitOr,
569            BinaryOperator::BitwiseXor => RegOpCode::BitXor,
570            BinaryOperator::BitwiseLeftShift => RegOpCode::ShiftLeft,
571            BinaryOperator::BitwiseRightShift => RegOpCode::ShiftRight,
572            BinaryOperator::BitwiseUnsignedRightShift => RegOpCode::UShiftRight,
573            BinaryOperator::LessThan => RegOpCode::LessThan,
574            BinaryOperator::LessThanEqual => RegOpCode::LessEqual,
575            BinaryOperator::GreaterThan => RegOpCode::GreaterThan,
576            BinaryOperator::GreaterThanEqual => RegOpCode::GreaterEqual,
577            BinaryOperator::LooselyEqual => RegOpCode::Equal,
578            BinaryOperator::LooselyUnequal => RegOpCode::NotEqual,
579            BinaryOperator::StrictlyEqual => RegOpCode::StrictEqual,
580            BinaryOperator::StrictlyUnequal => RegOpCode::StrictNotEqual,
581            _ => RegOpCode::Add,
582        };
583        self.chunk.emit(RegInstruction::with_dst_srcs(op, dst, left_reg, right_reg));
584        self.release_reg(left_reg);
585        self.release_reg(right_reg);
586        dst
587    }
588
589    fn compile_logical(&mut self, operator: &LogicalOperator, left: &ExpressionType, right: &ExpressionType) -> u32 {
590        let left_reg = self.compile_expression(left);
591        let result_reg = self.alloc_reg();
592        let jump_to_else = match operator {
593            LogicalOperator::And => self.emit_jump(RegOpCode::JumpIfFalse, left_reg, 0),
594            LogicalOperator::Or => self.emit_jump(RegOpCode::JumpIfTrue, left_reg, 0),
595        };
596
597        let right_reg = self.compile_expression(right);
598        self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Move, result_reg, right_reg));
599        self.release_reg(right_reg);
600        let jump_to_end = self.emit_jump(RegOpCode::Jump, 0, 0);
601
602        self.patch_jump(jump_to_else);
603        self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Move, result_reg, left_reg));
604        self.patch_jump(jump_to_end);
605
606        self.release_reg(left_reg);
607        result_reg
608    }
609
610    fn compile_assignment(&mut self, op: &AssignmentOperator, left: &PatternOrExpression, right: &ExpressionType) -> u32 {
611        let name = self.extract_assignment_target_name(left);
612        if let Some(name) = name {
613            if !self.is_lexically_shadowed(&name) {
614                if let Some(local_reg) = self.get_local_reg(&name) {
615                    match op {
616                        AssignmentOperator::Equals => {
617                            let rhs = self.compile_expression(right);
618                            if rhs != local_reg {
619                                self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Move, local_reg, rhs));
620                            }
621                            return rhs;
622                        }
623                        _ => {
624                            let rhs = self.compile_expression(right);
625                            let op_code = self.assignment_op_to_reg(op);
626                            self.chunk.emit(RegInstruction::with_dst_srcs(op_code, local_reg, local_reg, rhs));
627                            self.release_reg(rhs);
628                            return local_reg;
629                        }
630                    }
631                }
632            }
633
634            let name_idx = self.chunk.add_name(&name);
635            match op {
636                AssignmentOperator::Equals => {
637                    let rhs = self.compile_expression(right);
638                    self.chunk.emit(RegInstruction::with_src_imm(RegOpCode::SetVar, rhs, name_idx));
639                    rhs
640                }
641                _ => {
642                    let cur = self.alloc_reg();
643                    self.chunk.emit(RegInstruction::with_dst_imm(RegOpCode::GetVar, cur, name_idx));
644                    let rhs = self.compile_expression(right);
645                    let op_code = self.assignment_op_to_reg(op);
646                    self.chunk.emit(RegInstruction::with_dst_srcs(op_code, cur, cur, rhs));
647                    self.chunk.emit(RegInstruction::with_src_imm(RegOpCode::SetVar, cur, name_idx));
648                    self.release_reg(rhs);
649                    cur
650                }
651            }
652        } else {
653            // Member expression assignment (fallback)
654            let rhs = self.compile_expression(right);
655            rhs
656        }
657    }
658
659    fn compile_unary(&mut self, operator: &UnaryOperator, argument: &ExpressionType) -> u32 {
660        let arg = self.compile_expression(argument);
661        let dst = self.alloc_reg();
662        let op = match operator {
663            UnaryOperator::Minus => RegOpCode::Negate,
664            UnaryOperator::Plus => RegOpCode::UnaryPlus,
665            UnaryOperator::LogicalNot => RegOpCode::Not,
666            UnaryOperator::TypeOf => RegOpCode::TypeOf,
667            _ => RegOpCode::Move,
668        };
669        match op {
670            RegOpCode::Move => {
671                self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Move, dst, arg));
672            }
673            _ => {
674                self.chunk.emit(RegInstruction::with_dst_src(op, dst, arg));
675            }
676        }
677        self.release_reg(arg);
678        dst
679    }
680
681    fn compile_member_expression(&mut self, member: &MemberExpressionType) -> u32 {
682        match member {
683            MemberExpressionType::SimpleMemberExpression { object, property, .. } => {
684                let obj_reg = self.compile_expression_or_super(object);
685                let dst = self.alloc_reg();
686                let name_idx = self.chunk.add_name(&property.name);
687                self.chunk.emit(RegInstruction::new(RegOpCode::GetProp, dst, obj_reg, 0, name_idx));
688                self.release_reg(obj_reg);
689                dst
690            }
691            MemberExpressionType::ComputedMemberExpression { object, property, .. } => {
692                let obj_reg = self.compile_expression_or_super(object);
693                let key_reg = self.compile_expression(property);
694                let dst = self.alloc_reg();
695                self.chunk.emit(RegInstruction::with_dst_srcs(RegOpCode::GetElem, dst, obj_reg, key_reg));
696                self.release_reg(obj_reg);
697                self.release_reg(key_reg);
698                dst
699            }
700        }
701    }
702
703    fn compile_call_expression(&mut self, callee: &ExpressionOrSuper, arguments: &[ExpressionOrSpreadElement]) -> u32 {
704        let dst = self.alloc_reg();
705        let callee_reg = self.compile_expression_or_super(callee);
706        let mut arg_regs: Vec<u32> = Vec::with_capacity(arguments.len());
707        for arg in arguments {
708            match arg {
709                ExpressionOrSpreadElement::Expression(e) => arg_regs.push(self.compile_expression(e)),
710                ExpressionOrSpreadElement::SpreadElement(e) => arg_regs.push(self.compile_expression(e)),
711            }
712        }
713        // For now, ignore arguments in register bytecode (future work)
714        self.chunk.emit(RegInstruction::new(
715            RegOpCode::Call,
716            dst,
717            callee_reg,
718            0,
719            arg_regs.len() as u32,
720        ));
721        self.release_reg(callee_reg);
722        for reg in arg_regs {
723            self.release_reg(reg);
724        }
725        dst
726    }
727
728    fn compile_expression_or_super(&mut self, expr_or_super: &ExpressionOrSuper) -> u32 {
729        match expr_or_super {
730            ExpressionOrSuper::Expression(expr) => self.compile_expression(expr),
731            ExpressionOrSuper::Super => {
732                let reg = self.alloc_reg();
733                self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadUndefined, reg));
734                reg
735            }
736        }
737    }
738
739    fn compile_update(&mut self, op: &UpdateOperator, argument: &ExpressionType, prefix: bool) -> u32 {
740        if let ExpressionType::ExpressionWhichCanBePattern(ExpressionPatternType::Identifier(ref id)) = argument {
741            if !self.is_lexically_shadowed(&id.name) {
742                if let Some(local_reg) = self.get_local_reg(&id.name) {
743                    let one = self.alloc_reg();
744                    let one_idx = self.chunk.add_constant(JsValue::Number(JsNumberType::Integer(1)));
745                    self.chunk.emit(RegInstruction::with_dst_imm(RegOpCode::LoadConst, one, one_idx));
746                    let op_code = match op {
747                        UpdateOperator::PlusPlus => RegOpCode::Add,
748                        UpdateOperator::MinusMinus => RegOpCode::Sub,
749                    };
750                    if prefix {
751                        self.chunk.emit(RegInstruction::with_dst_srcs(op_code, local_reg, local_reg, one));
752                        self.release_reg(one);
753                        return local_reg;
754                    }
755
756                    let old = self.alloc_reg();
757                    self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Move, old, local_reg));
758                    self.chunk.emit(RegInstruction::with_dst_srcs(op_code, local_reg, local_reg, one));
759                    self.release_reg(one);
760                    return old;
761                }
762            }
763            let name_idx = self.chunk.add_name(&id.name);
764            let cur = self.alloc_reg();
765            self.chunk.emit(RegInstruction::with_dst_imm(RegOpCode::GetVar, cur, name_idx));
766            let one = self.alloc_reg();
767            let one_idx = self.chunk.add_constant(JsValue::Number(JsNumberType::Integer(1)));
768            self.chunk.emit(RegInstruction::with_dst_imm(RegOpCode::LoadConst, one, one_idx));
769            let op_code = match op {
770                UpdateOperator::PlusPlus => RegOpCode::Add,
771                UpdateOperator::MinusMinus => RegOpCode::Sub,
772            };
773            if prefix {
774                self.chunk.emit(RegInstruction::with_dst_srcs(op_code, cur, cur, one));
775                self.chunk.emit(RegInstruction::with_src_imm(RegOpCode::SetVar, cur, name_idx));
776                self.release_reg(one);
777                cur
778            } else {
779                let old = self.alloc_reg();
780                self.chunk.emit(RegInstruction::with_dst_src(RegOpCode::Move, old, cur));
781                self.chunk.emit(RegInstruction::with_dst_srcs(op_code, cur, cur, one));
782                self.chunk.emit(RegInstruction::with_src_imm(RegOpCode::SetVar, cur, name_idx));
783                self.release_reg(one);
784                old
785            }
786        } else {
787            let reg = self.alloc_reg();
788            self.chunk.emit(RegInstruction::with_dst(RegOpCode::LoadUndefined, reg));
789            reg
790        }
791    }
792
793    fn assignment_op_to_reg(&self, op: &AssignmentOperator) -> RegOpCode {
794        match op {
795            AssignmentOperator::AddEquals => RegOpCode::Add,
796            AssignmentOperator::SubtractEquals => RegOpCode::Sub,
797            AssignmentOperator::MultiplyEquals => RegOpCode::Mul,
798            AssignmentOperator::DivideEquals => RegOpCode::Div,
799            AssignmentOperator::ModuloEquals => RegOpCode::Mod,
800            AssignmentOperator::BitwiseAndEquals => RegOpCode::BitAnd,
801            AssignmentOperator::BitwiseOrEquals => RegOpCode::BitOr,
802            AssignmentOperator::BitwiseXorEquals => RegOpCode::BitXor,
803            AssignmentOperator::BitwiseLeftShiftEquals => RegOpCode::ShiftLeft,
804            AssignmentOperator::BitwiseRightShiftEquals => RegOpCode::ShiftRight,
805            AssignmentOperator::BitwiseUnsignedRightShiftEquals => RegOpCode::UShiftRight,
806            AssignmentOperator::Equals => RegOpCode::Move,
807        }
808    }
809
810    fn extract_assignment_target_name(&self, target: &PatternOrExpression) -> Option<String> {
811        match target {
812            PatternOrExpression::Pattern(pat) => {
813                if let PatternType::PatternWhichCanBeExpression(ExpressionPatternType::Identifier(ref id)) = pat.as_ref() {
814                    Some(id.name.clone())
815                } else {
816                    None
817                }
818            }
819            PatternOrExpression::Expression(expr) => {
820                if let ExpressionType::ExpressionWhichCanBePattern(ExpressionPatternType::Identifier(ref id)) = expr.as_ref() {
821                    Some(id.name.clone())
822                } else {
823                    None
824                }
825            }
826        }
827    }
828}