use std::fmt; use crate::opcode::{self, OpCode}; #[derive(Debug, Clone, PartialEq)] pub enum Constant { Number(f64), Int(i32), String(String), Boolean(bool), Null, Undefined, BytecodeFunc(u32), RegExp(String, String), } pub struct CompiledModule { pub bytecode: Vec, pub constants: Vec, pub n_registers: u8, pub n_args: u8, pub param_base: u8, pub builtin_reg_map: Vec<(String, u8)>, pub sub_modules: Vec, /// True when this module is an arrow function body. /// Arrow functions capture lexical `this` from the enclosing scope. pub is_arrow: bool, /// Index into `constants` holding the captured `this` JsValue. /// 0 means "not captured - use standard this binding". pub captured_this_const_idx: u16, /// Function name inferred from assignment context. /// Set at the VariableDeclaration / ObjectProperty assignment site. pub function_name: Option, /// True when this bytecode function is a class constructor. /// Ordinary CALL must reject it, while NEW_EXPRESSION may construct through it. pub is_class_constructor: bool, /// True when this class constructor has an `extends` clause. /// `this` stays uninitialized until SUPER_CALL completes. pub is_derived_constructor: bool, /// True for prototype methods whose function object needs a runtime home_object. pub needs_home_object: bool, } impl CompiledModule { pub fn new() -> Self { Self { bytecode: Vec::new(), constants: Vec::new(), n_registers: 0, n_args: 0, param_base: 0, builtin_reg_map: Vec::new(), sub_modules: Vec::new(), is_arrow: false, captured_this_const_idx: 0, function_name: None, is_class_constructor: false, is_derived_constructor: false, needs_home_object: false, } } } impl Default for CompiledModule { fn default() -> Self { Self::new() } } impl Clone for CompiledModule { fn clone(&self) -> Self { Self { bytecode: self.bytecode.clone(), constants: self.constants.clone(), n_registers: self.n_registers, n_args: self.n_args, param_base: self.param_base, builtin_reg_map: self.builtin_reg_map.clone(), sub_modules: self.sub_modules.clone(), is_arrow: self.is_arrow, captured_this_const_idx: self.captured_this_const_idx, function_name: self.function_name.clone(), is_class_constructor: self.is_class_constructor, is_derived_constructor: self.is_derived_constructor, needs_home_object: self.needs_home_object, } } } impl fmt::Display for CompiledModule { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "; n_registers = {}", self.n_registers)?; writeln!(f, "; constants:")?; for (i, c) in self.constants.iter().enumerate() { match c { Constant::BytecodeFunc(idx) => writeln!(f, "; [{i}] = BytecodeFunc(sub_module[{idx}])")?, other => writeln!(f, "; [{i}] = {other:?}")?, } } writeln!(f)?; for (offset, &instr) in self.bytecode.iter().enumerate() { let op = opcode::opcode(instr); let rd = opcode::rd(instr); let a = opcode::a(instr); let b = opcode::b(instr); write!(f, " {offset:04} {op}")?; match op { OpCode::LOAD_CONST => { write!(f, " r{rd}, const[{}]", opcode::imm16(instr))?; } OpCode::JMP | OpCode::JMP_IF_FALSE | OpCode::JMP_IF_TRUE => { write!(f, " r{rd}, {offset:+}", offset = opcode::offset16(instr))?; } OpCode::SWITCH_TABLE => { let n_cases = rd as u16 | ((b as u16) << 8); write!(f, " r{disc_reg}={a}, {n_cases} cases", disc_reg = a)?; } OpCode::RETURN | OpCode::HALT | OpCode::NOP => { write!(f, " r{rd}")?; } OpCode::NEG => { write!(f, " r{rd}, r{a}")?; } _ => { write!(f, " r{rd}, r{a}, r{b}")?; } } writeln!(f)?; } Ok(()) } }