refactor(compiler): 拆分 oxide_emit 为独立 emit pass crate(依赖倒置)

This commit is contained in:
爱特每羊队
2026-08-05 20:13:18 +08:00
parent 9f02985a82
commit bbdfca1a1c
62 changed files with 1045 additions and 993 deletions
Generated
+10
View File
@@ -1111,6 +1111,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_code_cache", "oxide_code_cache",
"oxide_emit",
"oxide_ir", "oxide_ir",
"oxide_log", "oxide_log",
"oxide_parser", "oxide_parser",
@@ -1119,6 +1120,15 @@ dependencies = [
"serde_json", "serde_json",
] ]
[[package]]
name = "oxide_emit"
version = "0.1.0"
dependencies = [
"oxide_bytecode",
"oxide_ir",
"oxide_parser",
]
[[package]] [[package]]
name = "oxide_ir" name = "oxide_ir"
version = "0.1.0" version = "0.1.0"
+2
View File
@@ -6,6 +6,7 @@ members = [
"crates/oxide_code_cache", "crates/oxide_code_cache",
"crates/oxide_log", "crates/oxide_log",
"crates/oxide_compiler", "crates/oxide_compiler",
"crates/oxide_emit",
"crates/oxide_ir", "crates/oxide_ir",
"crates/oxide_types", "crates/oxide_types",
"crates/oxide_vm", "crates/oxide_vm",
@@ -31,6 +32,7 @@ oxc_span = "0.134.0"
oxc_diagnostics = "0.134.0" oxc_diagnostics = "0.134.0"
oxc_syntax = "0.134.0" oxc_syntax = "0.134.0"
oxide_ir = { path = "crates/oxide_ir" } oxide_ir = { path = "crates/oxide_ir" }
oxide_emit = { path = "crates/oxide_emit" }
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
rustyline = "14" rustyline = "14"
ansi_term = "0.12" ansi_term = "0.12"
+1
View File
@@ -11,6 +11,7 @@ oxide_bytecode = { path = "../oxide_bytecode" }
oxide_parser = { path = "../oxide_parser" } oxide_parser = { path = "../oxide_parser" }
oxide_log = { path = "../oxide_log" } oxide_log = { path = "../oxide_log" }
oxide_ir.workspace = true oxide_ir.workspace = true
oxide_emit.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
rustc-hash.workspace = true rustc-hash.workspace = true
+4 -884
View File
@@ -1,900 +1,20 @@
use std::collections::{BTreeMap, HashMap, HashSet};
use crate::emit_ctx::{LabelCtx, ScopeCtx};
use oxide_ir::inst::Inst;
use oxide_ir::lower::lower;
use oxide_ir::operand::{LabelId, Operand};
use oxide_ir::IRFunction;
use crate::symbol_table::{Binding, SymbolTable};
use oxide_bytecode::module::CompiledModule; use oxide_bytecode::module::CompiledModule;
use oxide_bytecode::module::UpvalueCapture; use oxide_emit::Emitter;
use oxide_bytecode::opcode::OpCode; use oxide_ir::lower::lower;
pub use crate::hash::{compiled_module_hash, structural_hash}; pub use crate::hash::{compiled_module_hash, structural_hash};
use crate::symbol_table::ScopeKind;
pub use oxide_bytecode::module::Constant;
pub use oxide_parser::VariableDeclarationKind;
pub use oxide_parser::{AssignmentOperator, BinaryOperator, Expression, Statement, UnaryOperator};
pub struct Compiler; pub struct Compiler;
pub(crate) fn is_int_literal(value: f64) -> bool {
value.fract() == 0.0 && value >= i32::MIN as f64 && value <= i32::MAX as f64
}
pub(crate) fn is_side_effect_free(expr: &Expression) -> bool {
let mut stack = vec![expr];
while let Some(expr) = stack.pop() {
match expr {
Expression::NumericLiteral(_)
| Expression::StringLiteral(_)
| Expression::BooleanLiteral(_)
| Expression::NullLiteral(_)
| Expression::Identifier(_)
| Expression::RegExpLiteral(_)
| Expression::ThisExpression(_) => {}
Expression::ParenthesizedExpression(p) => stack.push(&p.expression),
Expression::BinaryExpression(bin) => {
stack.push(&bin.left);
stack.push(&bin.right);
}
Expression::UnaryExpression(un) if !matches!(un.operator, UnaryOperator::Delete) => {
stack.push(&un.argument);
}
_ => return false,
}
}
true
}
const BUILTIN_GLOBALS: &[&str] = &[
"NaN",
"undefined",
"Infinity",
"globalThis",
"Object",
"Array",
"String",
"Number",
"Boolean",
"Function",
"Error",
"TypeError",
"ReferenceError",
"RangeError",
"SyntaxError",
"URIError",
"EvalError",
"Math",
"JSON",
"Promise",
"Date",
"Set",
"Map",
"RegExp",
"Symbol",
"parseInt",
"parseFloat",
"isNaN",
"isFinite",
"Proxy",
"WeakMap",
"WeakSet",
"WeakRef",
"FinalizationRegistry",
"Atomics",
"SharedArrayBuffer",
"ArrayBuffer",
"DataView",
"Iterator",
"BigInt",
"Int8Array",
"Uint8Array",
"Uint8ClampedArray",
"Int16Array",
"Uint16Array",
"Int32Array",
"Uint32Array",
"Float32Array",
"Float64Array",
"BigInt64Array",
"BigUint64Array",
"Reflect",
"escape",
"unescape",
"encodeURI",
"decodeURI",
"encodeURIComponent",
"decodeURIComponent",
];
pub(crate) struct FieldBuffer {
pub(crate) insts: Vec<Inst>,
pub(crate) labels: Vec<(LabelId, usize)>,
}
/// A labeled-statement scope active during emission. `break label` targets
/// `break_label`; `continue label` targets `continue_label` (only set when the
/// labeled statement directly wraps an iteration statement).
#[derive(Debug, Clone)]
pub(crate) struct LabelScope {
pub(crate) name: String,
pub(crate) break_label: LabelId,
pub(crate) continue_label: Option<LabelId>,
}
pub(crate) struct CompileCtx {
pub(crate) insts: Vec<Inst>,
pub(crate) constants: Vec<Constant>,
constant_map: HashMap<ConstantKey, u16>,
next_reg: u8,
pub(crate) max_regs: u8,
reserved_reg_start: u8,
pub(crate) labels: LabelCtx,
pub(crate) scopes: ScopeCtx,
pub(crate) nested: Vec<IRFunction>,
/// Register holding `this` in the enclosing function context.
/// Used by arrow functions to capture lexical `this`.
/// Initialized to 254 (conventional this register) at the top level.
pub(crate) enclosing_this_reg: u8,
pub(crate) in_derived_constructor: bool,
pub(crate) in_instance_method: bool,
pub(crate) in_static_method: bool,
pub(crate) static_block_this_reg: Option<u8>,
pub(crate) field_buffer: Option<FieldBuffer>,
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
/// 本函数作用域声明的绑定名(参数 + 变量/函数声明,AST 收集,emit 前确定)。
pub(crate) own_bindings: HashSet<String>,
/// 本函数被嵌套函数捕获的绑定名 → cell_idx(名字排序分配,稳定跨 run)。
/// 捕获判断(MAKE_CELL / CELL_GET / CELL_SET)与子函数 upvalue cell_idx 统一查此映射,
/// 消除符号表时序依赖与 cell 索引错位。
pub(crate) captured_bindings: BTreeMap<String, u8>,
/// Set when alloc_reg() overflows into the reserved this/new.target range (≥254).
/// Checked after each emit phase to produce a compile error rather than silent corruption.
pub(crate) reg_overflow: bool,
pub(crate) const_overflow: bool,
}
#[derive(Clone, Copy)]
pub(crate) enum FunctionBodyContext {
Ordinary,
Arrow,
ClassElement,
}
pub(crate) enum ParamSpec<'a> {
Identifier(String),
Pattern {
synthetic_name: String,
pattern: &'a oxide_parser::BindingPattern<'a>,
},
}
impl ParamSpec<'_> {
pub(crate) fn register_name(&self) -> &str {
match self {
Self::Identifier(name) => name,
Self::Pattern { synthetic_name, .. } => synthetic_name,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum ConstantKey {
Number(u64),
Int(i32),
String(String),
Boolean(bool),
Null,
Undefined,
}
impl CompileCtx {
pub(crate) fn new() -> Self {
Self {
insts: Vec::new(),
constants: Vec::new(),
constant_map: HashMap::new(),
next_reg: 1,
max_regs: 1,
reserved_reg_start: 1,
labels: LabelCtx {
label_pos: Vec::new(),
loop_stack: Vec::new(),
switch_stack: Vec::new(),
label_scopes: Vec::new(),
pending_loop_labels: Vec::new(),
label_counter: 0,
},
scopes: ScopeCtx {
symbols: SymbolTable::new(),
builtin_reg_map: Vec::new(),
private_name_map: Vec::new(),
next_private_name_id: 1,
},
nested: Vec::new(),
enclosing_this_reg: 254, // conventional this register at top level
in_derived_constructor: false,
in_instance_method: false,
in_static_method: false,
static_block_this_reg: None,
field_buffer: None,
current_upvalue_captures: Vec::new(),
own_bindings: HashSet::new(),
captured_bindings: BTreeMap::new(),
reg_overflow: false,
const_overflow: false,
}
}
pub(crate) fn inst(&mut self, inst: Inst) {
self.insts.push(inst);
}
pub(crate) fn alloc_reg(&mut self) -> u8 {
let r = self.next_reg;
// Registers 254 (this) and 255 (new.target) are reserved by the VM.
// Allocating into them silently corrupts the call convention, turning
// method calls' `this` into garbage. Clamp to 253 and set a flag so
// the compiler can surface a proper error after the emit pass.
if r >= 254 {
self.reg_overflow = true;
return 253; // clamp to last safe register; emit continues but reg_overflow triggers error
}
self.next_reg = self.next_reg.wrapping_add(1);
if self.next_reg > self.max_regs {
self.max_regs = self.next_reg;
}
r
}
pub(crate) fn reset_regs(&mut self) {
self.next_reg = self.builtin_reg_floor().max(self.reserved_reg_start);
self.labels.label_counter = 0;
}
pub(crate) fn reg_checkpoint(&self) -> u8 {
self.next_reg
}
pub(crate) fn restore_reg_checkpoint(&mut self, checkpoint: u8) {
self.next_reg = checkpoint;
}
pub(crate) fn reserve_reg(&mut self, reg: u8) {
let next = reg.wrapping_add(1);
if self.next_reg <= reg {
self.next_reg = next;
}
if self.max_regs < next {
self.max_regs = next;
}
}
pub(crate) fn add_constant(&mut self, c: Constant) -> u16 {
if let Some(key) = ConstantKey::from_constant(&c) {
if let Some(&idx) = self.constant_map.get(&key) {
return idx;
}
if self.constants.len() >= u16::MAX as usize {
self.const_overflow = true;
return u16::MAX;
}
let idx = self.constants.len() as u16;
self.constants.push(c);
self.constant_map.insert(key, idx);
return idx;
}
let idx = self.constants.len();
if idx >= u16::MAX as usize {
self.const_overflow = true;
return u16::MAX;
}
self.constants.push(c);
idx as u16
}
pub(crate) fn push_scope(&mut self) {
self.scopes.symbols.push_scope();
}
pub(crate) fn pop_scope(&mut self) {
self.scopes.symbols.pop_scope();
}
pub(crate) fn declare(
&mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
) -> Result<(), String> {
self.scopes.symbols.declare(name, reg, kind, is_const)
}
pub(crate) fn declare_initialized(
&mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
) -> Result<(), String> {
self.scopes.symbols.declare_initialized(name, reg, kind, is_const)
}
pub(crate) fn push_scope_with_kind(&mut self, kind: ScopeKind) {
self.scopes.symbols.push_scope_with_kind(kind);
}
pub(crate) fn lookup(&self, name: &str) -> Result<u8, String> {
self.scopes.symbols.lookup(name)
}
pub(crate) fn lookup_or_builtin(&mut self, name: &str) -> Result<u8, String> {
match self.scopes.symbols.lookup(name) {
Ok(reg) => Ok(reg),
Err(err) if Self::is_known_builtin(name) && err.contains("is not defined") => {
let reg = self.alloc_reg();
self.scopes.symbols.pre_register_global(name, reg);
self.scopes.builtin_reg_map.push((name.to_string(), reg));
Ok(reg)
}
Err(err) => Err(err),
}
}
pub(crate) fn lookup_or_global(&mut self, name: &str) -> u8 {
if let Some(reg) = self.scopes.symbols.lookup_any(name) {
return reg;
}
let reg = self.alloc_reg();
self.scopes.symbols.lookup_or_global(name, reg)
}
pub(crate) fn lookup_const_flag(&self, name: &str) -> bool {
self.scopes.symbols.lookup_is_const(name)
}
pub(crate) fn init_var(&mut self, name: &str) {
self.scopes.symbols.init_var(name);
}
pub(crate) fn next_label_id(&mut self) -> u32 {
let id = self.labels.label_counter;
self.labels.label_counter += 1;
id
}
pub(crate) fn push_loop(&mut self, break_label: LabelId, continue_label: LabelId) {
self.labels.loop_stack.push((break_label, continue_label));
}
pub(crate) fn pop_loop(&mut self) {
self.labels.loop_stack.pop();
}
pub(crate) fn current_loop(&self) -> Option<&(LabelId, LabelId)> {
self.labels.loop_stack.last()
}
pub(crate) fn push_switch(&mut self, break_label: LabelId) {
self.labels.switch_stack.push(break_label);
}
pub(crate) fn pop_switch(&mut self) {
self.labels.switch_stack.pop();
}
pub(crate) fn current_switch(&self) -> Option<&LabelId> {
self.labels.switch_stack.last()
}
pub(crate) fn push_label_scope(
&mut self, name: &str, break_label: LabelId, continue_label: Option<LabelId>,
) -> Result<(), String> {
if self.labels.label_scopes.iter().any(|s| s.name == name) {
return Err(format!("SyntaxError: Label '{name}' has already been declared"));
}
self.labels.label_scopes.push(LabelScope {
name: name.to_string(),
break_label,
continue_label,
});
Ok(())
}
pub(crate) fn pop_label_scope(&mut self) {
self.labels.label_scopes.pop();
}
pub(crate) fn find_label(&self, name: &str) -> Option<&LabelScope> {
self.labels.label_scopes.iter().rev().find(|s| s.name == name)
}
/// Queue a label name to be bound to the continue target of the next loop
/// emitted as the labeled statement's body. Rejects duplicates in the active
/// or pending sets.
pub(crate) fn queue_loop_label(&mut self, name: &str) -> Result<(), String> {
if self.labels.label_scopes.iter().any(|s| s.name == name)
|| self.labels.pending_loop_labels.iter().any(|n| n == name)
{
return Err(format!("SyntaxError: Label '{name}' has already been declared"));
}
self.labels.pending_loop_labels.push(name.to_string());
Ok(())
}
/// Drain queued loop labels into active scopes bound to this loop's break and
/// continue targets. Returns how many scopes were pushed (to pop after).
pub(crate) fn take_pending_loop_labels(&mut self, break_label: LabelId, continue_label: LabelId) -> usize {
let names = std::mem::take(&mut self.labels.pending_loop_labels);
let count = names.len();
for name in names {
self.labels.label_scopes.push(LabelScope {
name,
break_label,
continue_label: Some(continue_label),
});
}
count
}
pub(crate) fn pop_label_scopes(&mut self, n: usize) {
for _ in 0..n {
self.labels.label_scopes.pop();
}
}
pub(crate) fn is_builtin(&self, name: &str) -> bool {
self.scopes.builtin_reg_map.iter().any(|(n, _)| n == name)
}
pub(crate) fn is_known_builtin(name: &str) -> bool {
BUILTIN_GLOBALS.contains(&name)
}
fn builtin_reg_floor(&self) -> u8 {
self.scopes
.builtin_reg_map
.iter()
.map(|(_, reg)| reg.saturating_add(1))
.max()
.unwrap_or(0)
}
pub(crate) fn pre_register_builtins(&mut self) {
// Builtin globals are resolved lazily by lookup_or_builtin(). Keeping this
// hook preserves the compile pipeline without reserving ~60 registers in
// every module.
}
/// 组装 IRFunction(两出口共用),take 走编译产物状态。
/// `parent_ctx` 用于补全 upvalue_captures 的 enclosing_reg(父符号表在父 emit 完成后完整)。
fn assemble_ir(&mut self, param_layout: oxide_ir::ParamLayout, parent_ctx: Option<&CompileCtx>) -> IRFunction {
let upvalue_captures = self
.current_upvalue_captures
.iter()
.map(|u| {
let enclosing_reg = parent_ctx
.and_then(|p| p.scopes.symbols.lookup_any(u.name.as_str()))
.unwrap_or(0);
UpvalueCapture {
name: u.name.clone(),
enclosing_reg,
cell_idx: u.cell_idx,
}
})
.collect();
IRFunction {
insts: std::mem::take(&mut self.insts),
label_pos: std::mem::take(&mut self.labels.label_pos),
label_count: self.labels.label_counter,
constants: std::mem::take(&mut self.constants),
param_layout,
builtin_reg_map: std::mem::take(&mut self.scopes.builtin_reg_map),
upvalue_captures,
cells_needed: self.captured_bindings.len() as u8,
n_registers: self.max_regs,
is_arrow: false,
is_class_constructor: false,
is_derived_constructor: false,
needs_home_object: false,
captured_this_const_idx: 0,
function_name: None,
reg_overflow: self.reg_overflow,
const_overflow: self.const_overflow,
nested: std::mem::take(&mut self.nested),
}
}
}
impl ConstantKey {
fn from_constant(value: &Constant) -> Option<Self> {
match value {
Constant::Number(v) => Some(Self::Number(v.to_bits())),
Constant::Int(v) => Some(Self::Int(*v)),
Constant::String(v) => Some(Self::String(v.clone())),
Constant::Boolean(v) => Some(Self::Boolean(*v)),
Constant::Null => Some(Self::Null),
Constant::Undefined => Some(Self::Undefined),
}
}
}
impl Compiler { impl Compiler {
pub fn new() -> Self { pub fn new() -> Self {
Self Self
} }
// ── 闭包捕获分析(AST 级,时序无关)──
/// 收集函数参数的绑定名(BindingIdentifier 形态)。
pub(crate) fn extract_function_parts<'a>(
&self, function: &'a oxide_parser::Function<'a>,
) -> Result<(Vec<ParamSpec<'a>>, &'a [Statement<'a>]), String> {
let mut param_specs = Vec::new();
for (idx, param) in function.params.items.iter().enumerate() {
match &param.pattern {
oxide_parser::BindingPattern::BindingIdentifier(bi) => {
param_specs.push(ParamSpec::Identifier(bi.name.to_string()));
}
pattern => {
param_specs.push(ParamSpec::Pattern {
synthetic_name: format!("@@param_{idx}"),
pattern,
});
}
}
}
let body_stmts: &[Statement] = if let Some(body) = &function.body { &body.statements } else { &[] };
Ok((param_specs, body_stmts))
}
/// Compile a function body (used for FD, FE, and arrow functions).
/// This performs both counting and emitting in one pass.
/// When `is_expression_body` is true (arrow function with expression body),
/// the last expression's value is returned instead of undefined.
/// `is_arrow` controls whether super flags are inherited from the parent scope
/// (true for arrow functions, which have lexical super) or reset to false
/// (false for regular functions, which create a new super scope).
pub(crate) fn compile_function_body<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, is_arrow: bool,
) -> Result<IRFunction, String> {
let body_context = if is_arrow {
FunctionBodyContext::Arrow
} else {
FunctionBodyContext::Ordinary
};
self.compile_function_body_with_bindings(
param_specs,
body_stmts,
parent_ctx,
is_expression_body,
&[],
body_context,
)
}
pub(crate) fn compile_function_body_with_bindings<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, extra_bindings: &[(&str, u8)], body_context: FunctionBodyContext,
) -> Result<IRFunction, String> {
self.compile_function_body_with_field_hooks(
param_specs,
body_stmts,
parent_ctx,
is_expression_body,
extra_bindings,
body_context,
None::<fn(&Compiler, &mut CompileCtx) -> Result<(), String>>,
false,
)
}
/// Pre-register all builtin identifiers referenced anywhere in this body
/// (expressions, member objects, call args, class fields, etc.) so their
/// register slots are reserved *before* any temporary register is emitted.
///
/// Without this, single-pass emission lazily allocates builtin slots via
/// `lookup_or_builtin` at first use, which can collide with a temporary
/// register already reused by `restore_reg_checkpoint`. Pre-scanning moves
/// builtin slot allocation ahead of the temporary register pool.
#[allow(clippy::too_many_arguments)]
pub(crate) fn compile_function_body_with_field_hooks<'a, E>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, extra_bindings: &[(&str, u8)], body_context: FunctionBodyContext,
mut emit_fields: Option<E>, fields_after_super: bool,
) -> Result<IRFunction, String>
where
E: FnMut(&Compiler, &mut CompileCtx) -> Result<(), String>,
{
let mut ctx = CompileCtx::new();
// Inherit parent's builtin_reg_map so builtin identifiers (Math, Object, etc.)
// resolve to the correct pre-allocated registers in the sub-module's register file.
ctx.scopes.builtin_reg_map = parent_ctx.scopes.builtin_reg_map.clone();
ctx.scopes.private_name_map = parent_ctx.scopes.private_name_map.clone();
ctx.scopes.next_private_name_id = parent_ctx.scopes.next_private_name_id;
// Propagate enclosing_this_reg so nested arrow functions capture the correct `this`.
ctx.enclosing_this_reg = parent_ctx.enclosing_this_reg;
// Arrow functions inherit lexical super. Class method bodies also need the
// class-provided super context for their top-level body compilation.
if matches!(body_context, FunctionBodyContext::Arrow | FunctionBodyContext::ClassElement) {
ctx.in_derived_constructor = parent_ctx.in_derived_constructor;
ctx.in_instance_method = parent_ctx.in_instance_method;
ctx.in_static_method = parent_ctx.in_static_method;
} else {
ctx.in_derived_constructor = false;
ctx.in_instance_method = false;
ctx.in_static_method = false;
}
// Inherit parent's global scope entries so previously-declared function names
// are visible from within the body.
let mut inherited_reg_start = 1u8.max(ctx.builtin_reg_floor());
for (name, binding) in &parent_ctx.scopes.symbols.scopes[0].bindings {
ctx.scopes.symbols.scopes[0].bindings.insert(
name.clone(),
Binding {
reg: binding.reg,
initialized: binding.initialized,
is_const: binding.is_const,
},
);
inherited_reg_start = inherited_reg_start.max(binding.reg.saturating_add(1));
}
for (name, reg) in extra_bindings {
ctx.scopes.symbols.scopes[0].bindings.insert(
(*name).to_string(),
Binding {
reg: *reg,
initialized: true,
is_const: true,
},
);
inherited_reg_start = inherited_reg_start.max(reg.saturating_add(1));
}
ctx.reserved_reg_start = inherited_reg_start.max(1);
// Align next_reg with builtin count so both count and emit passes start at the
// same register offset (params go after builtin slots).
ctx.reset_regs();
let param_base = self.emit_params_prologue(param_specs, body_stmts, parent_ctx, &mut ctx, body_context)?;
self.predeclare_function_declarations(body_stmts, &mut ctx);
// Pre-register builtin identifier references before emitting any temporary
// register, so builtin slots never collide with reused temporaries.
self.pre_register_builtin_references(body_stmts, &mut ctx);
// Pre-declare `var` names so hoisted function declarations (emitted in the
// first sub-pass below) can resolve the outer vars they close over.
self.predeclare_var_declarations(body_stmts, &mut ctx);
if let Some(emit) = emit_fields.as_mut() {
if fields_after_super {
let mut parent_insts = Vec::new();
let mut parent_label_pos = Vec::new();
std::mem::swap(&mut ctx.insts, &mut parent_insts);
std::mem::swap(&mut ctx.labels.label_pos, &mut parent_label_pos);
emit(self, &mut ctx)?;
let field_buffer = FieldBuffer {
insts: std::mem::take(&mut ctx.insts),
labels: std::mem::take(&mut ctx.labels.label_pos)
.into_iter()
.enumerate()
.filter_map(|(id, pos)| pos.map(|p| (id as LabelId, p)))
.collect(),
};
ctx.insts = parent_insts;
ctx.labels.label_pos = parent_label_pos;
ctx.field_buffer = Some(field_buffer);
} else {
emit(self, &mut ctx)?;
}
}
// Emit body statements(先函数声明 hoisting,再其余)。
let last_result_reg = self.emit_body_stmts(body_stmts, &mut ctx)?;
// Emit implicit RETURN: expression body returns the last expression,
// statement body returns undefined.
if is_expression_body {
if let Some(reg) = last_result_reg {
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(reg as u32), Operand::None, Operand::None));
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg as u32), undef_idx));
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg as u32), Operand::None, Operand::None));
}
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg as u32), undef_idx));
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg as u32), Operand::None, Operand::None));
}
let ir = ctx.assemble_ir(
oxide_ir::ParamLayout {
base: param_base as u32,
count: param_specs.len() as u32,
},
Some(parent_ctx),
);
Ok(ir)
}
/// 参数 prologue:函数作用域 + 参数声明/解构 + 闭包捕获与 upvalue 分析。返回 param_base。
fn emit_params_prologue<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
ctx: &mut CompileCtx, body_context: FunctionBodyContext,
) -> Result<u8, String> {
ctx.push_scope_with_kind(ScopeKind::FunctionScope);
let param_base = ctx.next_reg;
// Emit parameters and destructuring prologue.
for spec in param_specs {
let name = spec.register_name();
let reg = ctx.alloc_reg();
ctx.declare_initialized(name, reg, VariableDeclarationKind::Var, false)?;
}
for spec in param_specs {
if let ParamSpec::Pattern { synthetic_name, pattern } = spec {
let src_reg = ctx.lookup(synthetic_name)?;
self.emit_binding_pattern(pattern, src_reg, VariableDeclarationKind::Var, false, ctx)?;
}
}
// 闭包捕获分析(AST 级,emit 前确定,时序无关)
let param_names: Vec<&str> = param_specs.iter().map(|s| s.register_name()).collect();
ctx.own_bindings = self.collect_own_binding_names(&param_names, body_stmts);
ctx.captured_bindings = self.collect_captured_bindings(body_stmts, &ctx.own_bindings);
// Free variable analysis for upvalue capture (Ordinary + Arrow functions only)
if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) {
ctx.current_upvalue_captures =
self.collect_upvalue_names(body_stmts, &parent_ctx.captured_bindings, &ctx.own_bindings);
}
Ok(param_base)
}
/// 双 sub-pass emit body:先函数声明(hoisting),再其余语句。返回最后结果寄存器。
fn emit_body_stmts(&self, body_stmts: &[Statement], ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let mut last_result_reg = None;
for stmt in body_stmts {
if matches!(stmt, Statement::FunctionDeclaration(_)) {
if let Some(reg) = self.emit_statement(stmt, ctx)? {
last_result_reg = Some(reg);
}
}
}
for stmt in body_stmts {
if matches!(stmt, Statement::FunctionDeclaration(_)) {
continue;
}
if let Some(reg) = self.emit_statement(stmt, ctx)? {
last_result_reg = Some(reg);
}
}
Ok(last_result_reg)
}
pub(crate) fn emit_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
match stmt {
Statement::ExpressionStatement(_) | Statement::ReturnStatement(_) | Statement::EmptyStatement(_) => {
self.emit_basic_domain(stmt, ctx)
}
Statement::BlockStatement(_) => self.emit_block_domain(stmt, ctx),
Statement::VariableDeclaration(_) | Statement::FunctionDeclaration(_) | Statement::ClassDeclaration(_) => {
self.emit_declaration_domain(stmt, ctx)
}
Statement::IfStatement(_) => self.emit_control_domain(stmt, ctx),
Statement::WhileStatement(_)
| Statement::DoWhileStatement(_)
| Statement::ForStatement(_)
| Statement::ForInStatement(_)
| Statement::ForOfStatement(_) => self.emit_iteration_domain(stmt, ctx),
Statement::SwitchStatement(_) => self.emit_switch_domain(stmt, ctx),
Statement::ThrowStatement(_) | Statement::TryStatement(_) => self.emit_exception_domain(stmt, ctx),
Statement::BreakStatement(b) => self.emit_break_statement(b, ctx),
Statement::ContinueStatement(c) => self.emit_continue_statement(c, ctx),
Statement::LabeledStatement(ls) => self.emit_labeled_statement(ls, ctx),
_ => Ok(None),
}
}
pub(crate) fn emit_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> {
match expr {
Expression::NumericLiteral(_)
| Expression::StringLiteral(_)
| Expression::BooleanLiteral(_)
| Expression::NullLiteral(_)
| Expression::RegExpLiteral(_) => self.emit_literal(expr, ctx),
Expression::BinaryExpression(_)
| Expression::PrivateInExpression(_)
| Expression::UnaryExpression(_)
| Expression::ConditionalExpression(_)
| Expression::LogicalExpression(_)
| Expression::UpdateExpression(_) => self.emit_operator(expr, ctx),
Expression::StaticMemberExpression(_)
| Expression::ComputedMemberExpression(_)
| Expression::PrivateFieldExpression(_)
| Expression::ChainExpression(_) => self.emit_member_domain(expr, ctx),
Expression::ObjectExpression(_) | Expression::ArrayExpression(_) => self.emit_object_domain(expr, ctx),
Expression::AssignmentExpression(assign) => self.emit_assignment_expression(assign, ctx),
Expression::TemplateLiteral(_) | Expression::TaggedTemplateExpression(_) => {
self.emit_template_domain(expr, ctx)
}
Expression::ArrowFunctionExpression(_)
| Expression::FunctionExpression(_)
| Expression::ClassExpression(_)
| Expression::NewExpression(_) => self.emit_function_domain(expr, ctx),
Expression::Identifier(ident) => self.emit_identifier_expression(ident, ctx),
Expression::CallExpression(_) => self.emit_call_domain(expr, ctx),
Expression::ThisExpression(_) => self.emit_this_expression(ctx),
Expression::SequenceExpression(seq) => self.emit_sequence_expression(seq, ctx),
Expression::ParenthesizedExpression(p) => self.emit_parenthesized_expression(p, ctx),
_ => self.emit_unsupported_expression(expr, ctx),
}
}
pub fn compile(&self, program: &oxide_parser::Program) -> Result<CompiledModule, String> { pub fn compile(&self, program: &oxide_parser::Program) -> Result<CompiledModule, String> {
crate::compiler_debug!("compile: starting..."); crate::compiler_debug!("compile: starting...");
let mut ctx = CompileCtx::new(); let ir = Emitter::new().emit_program(program)?;
ctx.pre_register_builtins(); crate::compiler_debug!("compile: done, {} instructions", ir.insts.len());
self.predeclare_function_declarations(&program.body, &mut ctx);
// Pre-register builtin identifier references before any temporary register
// is emitted, keeping builtin slots clear of the temporary register pool.
self.pre_register_builtin_references(&program.body, &mut ctx);
// Pre-declare top-level `var` names so hoisted function declarations
// (emitted in the first sub-pass below) can resolve the outer vars.
self.predeclare_var_declarations(&program.body, &mut ctx);
// 闭包捕获分析(AST 级,emit 前确定)
ctx.own_bindings = self.collect_own_binding_names(&[], &program.body);
ctx.captured_bindings = self.collect_captured_bindings(&program.body, &ctx.own_bindings);
// First sub-pass: emit FunctionDeclarations (hoisting)
// This ensures function objects are available before any code runs.
for stmt in &program.body {
if matches!(stmt, Statement::FunctionDeclaration(_)) {
self.emit_statement(stmt, &mut ctx)?;
}
}
// Second sub-pass: emit all other statements
let mut last_result: Option<u8> = None;
for stmt in &program.body {
if matches!(stmt, Statement::FunctionDeclaration(_)) {
continue; // Already emitted above
}
match self.emit_statement(stmt, &mut ctx)? {
Some(r) => last_result = Some(r),
None => last_result = None,
}
}
if let Some(r) = last_result {
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::None, Operand::Reg(r as u32), Operand::None));
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.inst(Inst::load_const(Operand::None, undef_idx));
}
ctx.inst(Inst::new(OpCode::HALT, Operand::None, Operand::None, Operand::None));
crate::compiler_debug!("compile: done, {} instructions, {} constants", ctx.insts.len(), ctx.constants.len());
let ir = ctx.assemble_ir(
oxide_ir::ParamLayout {
base: ctx.scopes.builtin_reg_map.len() as u32,
count: 0,
},
None,
);
lower(&ir) lower(&ir)
} }
} }
-8
View File
@@ -1,13 +1,5 @@
#![doc = "OxideJS - AST to bytecode compiler"] #![doc = "OxideJS - AST to bytecode compiler"]
pub mod class;
pub mod closure;
pub mod compiler; pub mod compiler;
pub mod compiler_log; pub mod compiler_log;
pub mod emit_ctx;
pub mod expr;
pub mod hash; pub mod hash;
pub mod prepass;
pub mod shared;
pub mod stmt;
pub mod symbol_table;
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "oxide_emit"
version.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
description = "OxideJS - AST to IR emit pass"
[dependencies]
oxide_parser = { path = "../oxide_parser" }
oxide_bytecode = { path = "../oxide_bytecode" }
oxide_ir = { path = "../oxide_ir" }
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler, FunctionBodyContext}; use crate::{CompileCtx, Emitter, FunctionBodyContext};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{Class, ClassElement, MethodDefinitionKind, PropertyKey}; use oxide_parser::{Class, ClassElement, MethodDefinitionKind, PropertyKey};
impl Compiler { impl Emitter {
pub(crate) fn emit_class(&self, class: &Class, ctx: &mut CompileCtx) -> Result<u8, String> { pub(crate) fn emit_class(&self, class: &Class, ctx: &mut CompileCtx) -> Result<u8, String> {
let mut constructor_method = None; let mut constructor_method = None;
let mut instance_fields = Vec::new(); let mut instance_fields = Vec::new();
@@ -59,7 +59,7 @@ impl Compiler {
ctx.in_derived_constructor = is_derived; ctx.in_derived_constructor = is_derived;
ctx.scopes.private_name_map = private_names.clone(); ctx.scopes.private_name_map = private_names.clone();
let emit_instance_fields = |compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> { let emit_instance_fields = |compiler: &Emitter, field_ctx: &mut CompileCtx| -> Result<(), String> {
for field in &instance_fields { for field in &instance_fields {
if let PropertyKey::PrivateIdentifier(private) = &field.key { if let PropertyKey::PrivateIdentifier(private) = &field.key {
compiler.emit_private_field_init(Operand::This, private.name.as_str(), field.value.as_ref(), field_ctx)?; compiler.emit_private_field_init(Operand::This, private.name.as_str(), field.value.as_ref(), field_ctx)?;
@@ -1,8 +1,8 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_parser::{ClassElement, PropertyKey}; use oxide_parser::{ClassElement, PropertyKey};
impl Compiler { impl Emitter {
/// Emit static field initializers and static blocks in source order. /// Emit static field initializers and static blocks in source order.
/// JS class semantics interleave them (e.g. `static x = 1; static { /// JS class semantics interleave them (e.g. `static x = 1; static {
/// this.y = 1 } static z = this.y + 1` must run in that order). Emitting /// this.y = 1 } static z = this.y + 1` must run in that order). Emitting
@@ -1,6 +1,6 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
impl Compiler { impl Emitter {
pub(crate) fn emit_class_header( pub(crate) fn emit_class_header(
&self, class: &oxide_parser::Class, ctx: &mut CompileCtx, &self, class: &oxide_parser::Class, ctx: &mut CompileCtx,
) -> Result<(u8, u8, Option<u8>), String> { ) -> Result<(u8, u8, Option<u8>), String> {
@@ -1,11 +1,11 @@
use crate::compiler::{CompileCtx, Compiler, FunctionBodyContext}; use crate::{CompileCtx, Emitter, FunctionBodyContext};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{ClassElement, MethodDefinitionKind, PropertyKey}; use oxide_parser::{ClassElement, MethodDefinitionKind, PropertyKey};
impl Compiler { impl Emitter {
pub(crate) fn emit_class_methods( pub(crate) fn emit_class_methods(
&self, elements: &[ClassElement], ctor_reg: u8, proto_reg: u8, self_binding: &[(&str, u8)], &self, elements: &[ClassElement], ctor_reg: u8, proto_reg: u8, self_binding: &[(&str, u8)],
ctx: &mut CompileCtx, ctx: &mut CompileCtx,
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
impl Compiler { impl Emitter {
pub(crate) fn emit_class_prototype( pub(crate) fn emit_class_prototype(
&self, ctor_reg: u8, proto_reg: u8, super_reg: Option<u8>, sub_idx: u16, ctx: &mut CompileCtx, &self, ctor_reg: u8, proto_reg: u8, super_reg: Option<u8>, sub_idx: u16, ctx: &mut CompileCtx,
) -> Result<(), String> { ) -> Result<(), String> {
@@ -3,9 +3,9 @@ use std::collections::{BTreeMap, HashSet};
use oxide_bytecode::module::UpvalueCapture; use oxide_bytecode::module::UpvalueCapture;
use oxide_parser::{Expression, Statement}; use oxide_parser::{Expression, Statement};
use crate::compiler::Compiler; use crate::Emitter;
impl Compiler { impl Emitter {
fn collect_fn_param_names(&self, params: &oxide_parser::FormalParameters) -> HashSet<String> { fn collect_fn_param_names(&self, params: &oxide_parser::FormalParameters) -> HashSet<String> {
let mut names = HashSet::new(); let mut names = HashSet::new();
@@ -515,4 +515,3 @@ impl Compiler {
.collect() .collect()
} }
} }
+903
View File
@@ -0,0 +1,903 @@
use std::collections::{BTreeMap, HashMap, HashSet};
use oxide_bytecode::module::UpvalueCapture;
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::{LabelId, Operand};
use oxide_ir::IRFunction;
use crate::emit_ctx::{LabelCtx, ScopeCtx};
use crate::symbol_table::{Binding, ScopeKind, SymbolTable};
pub use oxide_bytecode::module::Constant;
pub use oxide_parser::VariableDeclarationKind;
pub use oxide_parser::{AssignmentOperator, BinaryOperator, Expression, Statement, UnaryOperator};
pub struct Emitter;
pub fn is_int_literal(value: f64) -> bool {
value.fract() == 0.0 && value >= i32::MIN as f64 && value <= i32::MAX as f64
}
pub fn is_side_effect_free(expr: &Expression) -> bool {
let mut stack = vec![expr];
while let Some(expr) = stack.pop() {
match expr {
Expression::NumericLiteral(_)
| Expression::StringLiteral(_)
| Expression::BooleanLiteral(_)
| Expression::NullLiteral(_)
| Expression::Identifier(_)
| Expression::RegExpLiteral(_)
| Expression::ThisExpression(_) => {}
Expression::ParenthesizedExpression(p) => stack.push(&p.expression),
Expression::BinaryExpression(bin) => {
stack.push(&bin.left);
stack.push(&bin.right);
}
Expression::UnaryExpression(un) if !matches!(un.operator, UnaryOperator::Delete) => {
stack.push(&un.argument);
}
_ => return false,
}
}
true
}
const BUILTIN_GLOBALS: &[&str] = &[
"NaN",
"undefined",
"Infinity",
"globalThis",
"Object",
"Array",
"String",
"Number",
"Boolean",
"Function",
"Error",
"TypeError",
"ReferenceError",
"RangeError",
"SyntaxError",
"URIError",
"EvalError",
"Math",
"JSON",
"Promise",
"Date",
"Set",
"Map",
"RegExp",
"Symbol",
"parseInt",
"parseFloat",
"isNaN",
"isFinite",
"Proxy",
"WeakMap",
"WeakSet",
"WeakRef",
"FinalizationRegistry",
"Atomics",
"SharedArrayBuffer",
"ArrayBuffer",
"DataView",
"Iterator",
"BigInt",
"Int8Array",
"Uint8Array",
"Uint8ClampedArray",
"Int16Array",
"Uint16Array",
"Int32Array",
"Uint32Array",
"Float32Array",
"Float64Array",
"BigInt64Array",
"BigUint64Array",
"Reflect",
"escape",
"unescape",
"encodeURI",
"decodeURI",
"encodeURIComponent",
"decodeURIComponent",
];
pub(crate) struct FieldBuffer {
pub(crate) insts: Vec<Inst>,
pub(crate) labels: Vec<(LabelId, usize)>,
}
/// A labeled-statement scope active during emission. `break label` targets
/// `break_label`; `continue label` targets `continue_label` (only set when the
/// labeled statement directly wraps an iteration statement).
#[derive(Debug, Clone)]
pub struct LabelScope {
pub(crate) name: String,
pub(crate) break_label: LabelId,
pub(crate) continue_label: Option<LabelId>,
}
pub struct CompileCtx {
pub(crate) insts: Vec<Inst>,
pub(crate) constants: Vec<Constant>,
constant_map: HashMap<ConstantKey, u16>,
next_reg: u8,
pub(crate) max_regs: u8,
reserved_reg_start: u8,
pub(crate) labels: LabelCtx,
pub(crate) scopes: ScopeCtx,
pub(crate) nested: Vec<IRFunction>,
/// Register holding `this` in the enclosing function context.
/// Used by arrow functions to capture lexical `this`.
/// Initialized to 254 (conventional this register) at the top level.
pub(crate) enclosing_this_reg: u8,
pub(crate) in_derived_constructor: bool,
pub(crate) in_instance_method: bool,
pub(crate) in_static_method: bool,
pub(crate) static_block_this_reg: Option<u8>,
pub(crate) field_buffer: Option<FieldBuffer>,
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
/// 本函数作用域声明的绑定名(参数 + 变量/函数声明,AST 收集,emit 前确定)。
pub(crate) own_bindings: HashSet<String>,
/// 本函数被嵌套函数捕获的绑定名 → cell_idx(名字排序分配,稳定跨 run)。
/// 捕获判断(MAKE_CELL / CELL_GET / CELL_SET)与子函数 upvalue cell_idx 统一查此映射,
/// 消除符号表时序依赖与 cell 索引错位。
pub(crate) captured_bindings: BTreeMap<String, u8>,
/// Set when alloc_reg() overflows into the reserved this/new.target range (≥254).
/// Checked after each emit phase to produce a compile error rather than silent corruption.
pub(crate) reg_overflow: bool,
pub(crate) const_overflow: bool,
}
#[derive(Clone, Copy)]
pub enum FunctionBodyContext {
Ordinary,
Arrow,
ClassElement,
}
pub enum ParamSpec<'a> {
Identifier(String),
Pattern {
synthetic_name: String,
pattern: &'a oxide_parser::BindingPattern<'a>,
},
}
impl ParamSpec<'_> {
pub(crate) fn register_name(&self) -> &str {
match self {
Self::Identifier(name) => name,
Self::Pattern { synthetic_name, .. } => synthetic_name,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum ConstantKey {
Number(u64),
Int(i32),
String(String),
Boolean(bool),
Null,
Undefined,
}
impl CompileCtx {
pub(crate) fn new() -> Self {
Self {
insts: Vec::new(),
constants: Vec::new(),
constant_map: HashMap::new(),
next_reg: 1,
max_regs: 1,
reserved_reg_start: 1,
labels: LabelCtx {
label_pos: Vec::new(),
loop_stack: Vec::new(),
switch_stack: Vec::new(),
label_scopes: Vec::new(),
pending_loop_labels: Vec::new(),
label_counter: 0,
},
scopes: ScopeCtx {
symbols: SymbolTable::new(),
builtin_reg_map: Vec::new(),
private_name_map: Vec::new(),
next_private_name_id: 1,
},
nested: Vec::new(),
enclosing_this_reg: 254, // conventional this register at top level
in_derived_constructor: false,
in_instance_method: false,
in_static_method: false,
static_block_this_reg: None,
field_buffer: None,
current_upvalue_captures: Vec::new(),
own_bindings: HashSet::new(),
captured_bindings: BTreeMap::new(),
reg_overflow: false,
const_overflow: false,
}
}
pub(crate) fn inst(&mut self, inst: Inst) {
self.insts.push(inst);
}
pub(crate) fn alloc_reg(&mut self) -> u8 {
let r = self.next_reg;
// Registers 254 (this) and 255 (new.target) are reserved by the VM.
// Allocating into them silently corrupts the call convention, turning
// method calls' `this` into garbage. Clamp to 253 and set a flag so
// the compiler can surface a proper error after the emit pass.
if r >= 254 {
self.reg_overflow = true;
return 253; // clamp to last safe register; emit continues but reg_overflow triggers error
}
self.next_reg = self.next_reg.wrapping_add(1);
if self.next_reg > self.max_regs {
self.max_regs = self.next_reg;
}
r
}
pub(crate) fn reset_regs(&mut self) {
self.next_reg = self.builtin_reg_floor().max(self.reserved_reg_start);
self.labels.label_counter = 0;
}
pub(crate) fn reg_checkpoint(&self) -> u8 {
self.next_reg
}
pub(crate) fn restore_reg_checkpoint(&mut self, checkpoint: u8) {
self.next_reg = checkpoint;
}
pub(crate) fn reserve_reg(&mut self, reg: u8) {
let next = reg.wrapping_add(1);
if self.next_reg <= reg {
self.next_reg = next;
}
if self.max_regs < next {
self.max_regs = next;
}
}
pub(crate) fn add_constant(&mut self, c: Constant) -> u16 {
if let Some(key) = ConstantKey::from_constant(&c) {
if let Some(&idx) = self.constant_map.get(&key) {
return idx;
}
if self.constants.len() >= u16::MAX as usize {
self.const_overflow = true;
return u16::MAX;
}
let idx = self.constants.len() as u16;
self.constants.push(c);
self.constant_map.insert(key, idx);
return idx;
}
let idx = self.constants.len();
if idx >= u16::MAX as usize {
self.const_overflow = true;
return u16::MAX;
}
self.constants.push(c);
idx as u16
}
pub(crate) fn push_scope(&mut self) {
self.scopes.symbols.push_scope();
}
pub(crate) fn pop_scope(&mut self) {
self.scopes.symbols.pop_scope();
}
pub(crate) fn declare(
&mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
) -> Result<(), String> {
self.scopes.symbols.declare(name, reg, kind, is_const)
}
pub(crate) fn declare_initialized(
&mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
) -> Result<(), String> {
self.scopes.symbols.declare_initialized(name, reg, kind, is_const)
}
pub(crate) fn push_scope_with_kind(&mut self, kind: ScopeKind) {
self.scopes.symbols.push_scope_with_kind(kind);
}
pub(crate) fn lookup(&self, name: &str) -> Result<u8, String> {
self.scopes.symbols.lookup(name)
}
pub(crate) fn lookup_or_builtin(&mut self, name: &str) -> Result<u8, String> {
match self.scopes.symbols.lookup(name) {
Ok(reg) => Ok(reg),
Err(err) if Self::is_known_builtin(name) && err.contains("is not defined") => {
let reg = self.alloc_reg();
self.scopes.symbols.pre_register_global(name, reg);
self.scopes.builtin_reg_map.push((name.to_string(), reg));
Ok(reg)
}
Err(err) => Err(err),
}
}
pub(crate) fn lookup_or_global(&mut self, name: &str) -> u8 {
if let Some(reg) = self.scopes.symbols.lookup_any(name) {
return reg;
}
let reg = self.alloc_reg();
self.scopes.symbols.lookup_or_global(name, reg)
}
pub(crate) fn lookup_const_flag(&self, name: &str) -> bool {
self.scopes.symbols.lookup_is_const(name)
}
pub(crate) fn init_var(&mut self, name: &str) {
self.scopes.symbols.init_var(name);
}
pub(crate) fn next_label_id(&mut self) -> u32 {
let id = self.labels.label_counter;
self.labels.label_counter += 1;
id
}
pub(crate) fn push_loop(&mut self, break_label: LabelId, continue_label: LabelId) {
self.labels.loop_stack.push((break_label, continue_label));
}
pub(crate) fn pop_loop(&mut self) {
self.labels.loop_stack.pop();
}
pub(crate) fn current_loop(&self) -> Option<&(LabelId, LabelId)> {
self.labels.loop_stack.last()
}
pub(crate) fn push_switch(&mut self, break_label: LabelId) {
self.labels.switch_stack.push(break_label);
}
pub(crate) fn pop_switch(&mut self) {
self.labels.switch_stack.pop();
}
pub(crate) fn current_switch(&self) -> Option<&LabelId> {
self.labels.switch_stack.last()
}
pub(crate) fn push_label_scope(
&mut self, name: &str, break_label: LabelId, continue_label: Option<LabelId>,
) -> Result<(), String> {
if self.labels.label_scopes.iter().any(|s| s.name == name) {
return Err(format!("SyntaxError: Label '{name}' has already been declared"));
}
self.labels.label_scopes.push(LabelScope {
name: name.to_string(),
break_label,
continue_label,
});
Ok(())
}
pub(crate) fn pop_label_scope(&mut self) {
self.labels.label_scopes.pop();
}
pub(crate) fn find_label(&self, name: &str) -> Option<&LabelScope> {
self.labels.label_scopes.iter().rev().find(|s| s.name == name)
}
/// Queue a label name to be bound to the continue target of the next loop
/// emitted as the labeled statement's body. Rejects duplicates in the active
/// or pending sets.
pub(crate) fn queue_loop_label(&mut self, name: &str) -> Result<(), String> {
if self.labels.label_scopes.iter().any(|s| s.name == name)
|| self.labels.pending_loop_labels.iter().any(|n| n == name)
{
return Err(format!("SyntaxError: Label '{name}' has already been declared"));
}
self.labels.pending_loop_labels.push(name.to_string());
Ok(())
}
/// Drain queued loop labels into active scopes bound to this loop's break and
/// continue targets. Returns how many scopes were pushed (to pop after).
pub(crate) fn take_pending_loop_labels(&mut self, break_label: LabelId, continue_label: LabelId) -> usize {
let names = std::mem::take(&mut self.labels.pending_loop_labels);
let count = names.len();
for name in names {
self.labels.label_scopes.push(LabelScope {
name,
break_label,
continue_label: Some(continue_label),
});
}
count
}
pub(crate) fn pop_label_scopes(&mut self, n: usize) {
for _ in 0..n {
self.labels.label_scopes.pop();
}
}
pub(crate) fn is_builtin(&self, name: &str) -> bool {
self.scopes.builtin_reg_map.iter().any(|(n, _)| n == name)
}
pub(crate) fn is_known_builtin(name: &str) -> bool {
BUILTIN_GLOBALS.contains(&name)
}
fn builtin_reg_floor(&self) -> u8 {
self.scopes
.builtin_reg_map
.iter()
.map(|(_, reg)| reg.saturating_add(1))
.max()
.unwrap_or(0)
}
pub(crate) fn pre_register_builtins(&mut self) {
// Builtin globals are resolved lazily by lookup_or_builtin(). Keeping this
// hook preserves the compile pipeline without reserving ~60 registers in
// every module.
}
/// 组装 IRFunction(两出口共用),take 走编译产物状态。
/// `parent_ctx` 用于补全 upvalue_captures 的 enclosing_reg(父符号表在父 emit 完成后完整)。
fn assemble_ir(&mut self, param_layout: oxide_ir::ParamLayout, parent_ctx: Option<&CompileCtx>) -> IRFunction {
let upvalue_captures = self
.current_upvalue_captures
.iter()
.map(|u| {
let enclosing_reg = parent_ctx
.and_then(|p| p.scopes.symbols.lookup_any(u.name.as_str()))
.unwrap_or(0);
UpvalueCapture {
name: u.name.clone(),
enclosing_reg,
cell_idx: u.cell_idx,
}
})
.collect();
IRFunction {
insts: std::mem::take(&mut self.insts),
label_pos: std::mem::take(&mut self.labels.label_pos),
label_count: self.labels.label_counter,
constants: std::mem::take(&mut self.constants),
param_layout,
builtin_reg_map: std::mem::take(&mut self.scopes.builtin_reg_map),
upvalue_captures,
cells_needed: self.captured_bindings.len() as u8,
n_registers: self.max_regs,
is_arrow: false,
is_class_constructor: false,
is_derived_constructor: false,
needs_home_object: false,
captured_this_const_idx: 0,
function_name: None,
reg_overflow: self.reg_overflow,
const_overflow: self.const_overflow,
nested: std::mem::take(&mut self.nested),
}
}
}
impl ConstantKey {
fn from_constant(value: &Constant) -> Option<Self> {
match value {
Constant::Number(v) => Some(Self::Number(v.to_bits())),
Constant::Int(v) => Some(Self::Int(*v)),
Constant::String(v) => Some(Self::String(v.clone())),
Constant::Boolean(v) => Some(Self::Boolean(*v)),
Constant::Null => Some(Self::Null),
Constant::Undefined => Some(Self::Undefined),
}
}
}
impl Emitter {
pub fn new() -> Self {
Self
}
// ── 闭包捕获分析(AST 级,时序无关)──
/// 收集函数参数的绑定名(BindingIdentifier 形态)。
pub(crate) fn extract_function_parts<'a>(
&self, function: &'a oxide_parser::Function<'a>,
) -> Result<(Vec<ParamSpec<'a>>, &'a [Statement<'a>]), String> {
let mut param_specs = Vec::new();
for (idx, param) in function.params.items.iter().enumerate() {
match &param.pattern {
oxide_parser::BindingPattern::BindingIdentifier(bi) => {
param_specs.push(ParamSpec::Identifier(bi.name.to_string()));
}
pattern => {
param_specs.push(ParamSpec::Pattern {
synthetic_name: format!("@@param_{idx}"),
pattern,
});
}
}
}
let body_stmts: &[Statement] = if let Some(body) = &function.body { &body.statements } else { &[] };
Ok((param_specs, body_stmts))
}
/// Compile a function body (used for FD, FE, and arrow functions).
/// This performs both counting and emitting in one pass.
/// When `is_expression_body` is true (arrow function with expression body),
/// the last expression's value is returned instead of undefined.
/// `is_arrow` controls whether super flags are inherited from the parent scope
/// (true for arrow functions, which have lexical super) or reset to false
/// (false for regular functions, which create a new super scope).
pub(crate) fn compile_function_body<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, is_arrow: bool,
) -> Result<IRFunction, String> {
let body_context = if is_arrow {
FunctionBodyContext::Arrow
} else {
FunctionBodyContext::Ordinary
};
self.compile_function_body_with_bindings(
param_specs,
body_stmts,
parent_ctx,
is_expression_body,
&[],
body_context,
)
}
pub(crate) fn compile_function_body_with_bindings<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, extra_bindings: &[(&str, u8)], body_context: FunctionBodyContext,
) -> Result<IRFunction, String> {
self.compile_function_body_with_field_hooks(
param_specs,
body_stmts,
parent_ctx,
is_expression_body,
extra_bindings,
body_context,
None::<fn(&Emitter, &mut CompileCtx) -> Result<(), String>>,
false,
)
}
/// Pre-register all builtin identifiers referenced anywhere in this body
/// (expressions, member objects, call args, class fields, etc.) so their
/// register slots are reserved *before* any temporary register is emitted.
///
/// Without this, single-pass emission lazily allocates builtin slots via
/// `lookup_or_builtin` at first use, which can collide with a temporary
/// register already reused by `restore_reg_checkpoint`. Pre-scanning moves
/// builtin slot allocation ahead of the temporary register pool.
#[allow(clippy::too_many_arguments)]
pub(crate) fn compile_function_body_with_field_hooks<'a, E>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, extra_bindings: &[(&str, u8)], body_context: FunctionBodyContext,
mut emit_fields: Option<E>, fields_after_super: bool,
) -> Result<IRFunction, String>
where
E: FnMut(&Emitter, &mut CompileCtx) -> Result<(), String>,
{
let mut ctx = CompileCtx::new();
// Inherit parent's builtin_reg_map so builtin identifiers (Math, Object, etc.)
// resolve to the correct pre-allocated registers in the sub-module's register file.
ctx.scopes.builtin_reg_map = parent_ctx.scopes.builtin_reg_map.clone();
ctx.scopes.private_name_map = parent_ctx.scopes.private_name_map.clone();
ctx.scopes.next_private_name_id = parent_ctx.scopes.next_private_name_id;
// Propagate enclosing_this_reg so nested arrow functions capture the correct `this`.
ctx.enclosing_this_reg = parent_ctx.enclosing_this_reg;
// Arrow functions inherit lexical super. Class method bodies also need the
// class-provided super context for their top-level body compilation.
if matches!(body_context, FunctionBodyContext::Arrow | FunctionBodyContext::ClassElement) {
ctx.in_derived_constructor = parent_ctx.in_derived_constructor;
ctx.in_instance_method = parent_ctx.in_instance_method;
ctx.in_static_method = parent_ctx.in_static_method;
} else {
ctx.in_derived_constructor = false;
ctx.in_instance_method = false;
ctx.in_static_method = false;
}
// Inherit parent's global scope entries so previously-declared function names
// are visible from within the body.
let mut inherited_reg_start = 1u8.max(ctx.builtin_reg_floor());
for (name, binding) in &parent_ctx.scopes.symbols.scopes[0].bindings {
ctx.scopes.symbols.scopes[0].bindings.insert(
name.clone(),
Binding {
reg: binding.reg,
initialized: binding.initialized,
is_const: binding.is_const,
},
);
inherited_reg_start = inherited_reg_start.max(binding.reg.saturating_add(1));
}
for (name, reg) in extra_bindings {
ctx.scopes.symbols.scopes[0].bindings.insert(
(*name).to_string(),
Binding {
reg: *reg,
initialized: true,
is_const: true,
},
);
inherited_reg_start = inherited_reg_start.max(reg.saturating_add(1));
}
ctx.reserved_reg_start = inherited_reg_start.max(1);
// Align next_reg with builtin count so both count and emit passes start at the
// same register offset (params go after builtin slots).
ctx.reset_regs();
let param_base = self.emit_params_prologue(param_specs, body_stmts, parent_ctx, &mut ctx, body_context)?;
self.predeclare_function_declarations(body_stmts, &mut ctx);
// Pre-register builtin identifier references before emitting any temporary
// register, so builtin slots never collide with reused temporaries.
self.pre_register_builtin_references(body_stmts, &mut ctx);
// Pre-declare `var` names so hoisted function declarations (emitted in the
// first sub-pass below) can resolve the outer vars they close over.
self.predeclare_var_declarations(body_stmts, &mut ctx);
if let Some(emit) = emit_fields.as_mut() {
if fields_after_super {
let mut parent_insts = Vec::new();
let mut parent_label_pos = Vec::new();
std::mem::swap(&mut ctx.insts, &mut parent_insts);
std::mem::swap(&mut ctx.labels.label_pos, &mut parent_label_pos);
emit(self, &mut ctx)?;
let field_buffer = FieldBuffer {
insts: std::mem::take(&mut ctx.insts),
labels: std::mem::take(&mut ctx.labels.label_pos)
.into_iter()
.enumerate()
.filter_map(|(id, pos)| pos.map(|p| (id as LabelId, p)))
.collect(),
};
ctx.insts = parent_insts;
ctx.labels.label_pos = parent_label_pos;
ctx.field_buffer = Some(field_buffer);
} else {
emit(self, &mut ctx)?;
}
}
// Emit body statements(先函数声明 hoisting,再其余)。
let last_result_reg = self.emit_body_stmts(body_stmts, &mut ctx)?;
// Emit implicit RETURN: expression body returns the last expression,
// statement body returns undefined.
if is_expression_body {
if let Some(reg) = last_result_reg {
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(reg as u32), Operand::None, Operand::None));
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg as u32), undef_idx));
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg as u32), Operand::None, Operand::None));
}
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg as u32), undef_idx));
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg as u32), Operand::None, Operand::None));
}
let ir = ctx.assemble_ir(
oxide_ir::ParamLayout {
base: param_base as u32,
count: param_specs.len() as u32,
},
Some(parent_ctx),
);
Ok(ir)
}
/// 参数 prologue:函数作用域 + 参数声明/解构 + 闭包捕获与 upvalue 分析。返回 param_base。
fn emit_params_prologue<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
ctx: &mut CompileCtx, body_context: FunctionBodyContext,
) -> Result<u8, String> {
ctx.push_scope_with_kind(ScopeKind::FunctionScope);
let param_base = ctx.next_reg;
// Emit parameters and destructuring prologue.
for spec in param_specs {
let name = spec.register_name();
let reg = ctx.alloc_reg();
ctx.declare_initialized(name, reg, VariableDeclarationKind::Var, false)?;
}
for spec in param_specs {
if let ParamSpec::Pattern { synthetic_name, pattern } = spec {
let src_reg = ctx.lookup(synthetic_name)?;
self.emit_binding_pattern(pattern, src_reg, VariableDeclarationKind::Var, false, ctx)?;
}
}
// 闭包捕获分析(AST 级,emit 前确定,时序无关)
let param_names: Vec<&str> = param_specs.iter().map(|s| s.register_name()).collect();
ctx.own_bindings = self.collect_own_binding_names(&param_names, body_stmts);
ctx.captured_bindings = self.collect_captured_bindings(body_stmts, &ctx.own_bindings);
// Free variable analysis for upvalue capture (Ordinary + Arrow functions only)
if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) {
ctx.current_upvalue_captures =
self.collect_upvalue_names(body_stmts, &parent_ctx.captured_bindings, &ctx.own_bindings);
}
Ok(param_base)
}
/// 双 sub-pass emit body:先函数声明(hoisting),再其余语句。返回最后结果寄存器。
fn emit_body_stmts(&self, body_stmts: &[Statement], ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let mut last_result_reg = None;
for stmt in body_stmts {
if matches!(stmt, Statement::FunctionDeclaration(_)) {
if let Some(reg) = self.emit_statement(stmt, ctx)? {
last_result_reg = Some(reg);
}
}
}
for stmt in body_stmts {
if matches!(stmt, Statement::FunctionDeclaration(_)) {
continue;
}
if let Some(reg) = self.emit_statement(stmt, ctx)? {
last_result_reg = Some(reg);
}
}
Ok(last_result_reg)
}
pub(crate) fn emit_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
match stmt {
Statement::ExpressionStatement(_) | Statement::ReturnStatement(_) | Statement::EmptyStatement(_) => {
self.emit_basic_domain(stmt, ctx)
}
Statement::BlockStatement(_) => self.emit_block_domain(stmt, ctx),
Statement::VariableDeclaration(_) | Statement::FunctionDeclaration(_) | Statement::ClassDeclaration(_) => {
self.emit_declaration_domain(stmt, ctx)
}
Statement::IfStatement(_) => self.emit_control_domain(stmt, ctx),
Statement::WhileStatement(_)
| Statement::DoWhileStatement(_)
| Statement::ForStatement(_)
| Statement::ForInStatement(_)
| Statement::ForOfStatement(_) => self.emit_iteration_domain(stmt, ctx),
Statement::SwitchStatement(_) => self.emit_switch_domain(stmt, ctx),
Statement::ThrowStatement(_) | Statement::TryStatement(_) => self.emit_exception_domain(stmt, ctx),
Statement::BreakStatement(b) => self.emit_break_statement(b, ctx),
Statement::ContinueStatement(c) => self.emit_continue_statement(c, ctx),
Statement::LabeledStatement(ls) => self.emit_labeled_statement(ls, ctx),
_ => Ok(None),
}
}
pub(crate) fn emit_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> {
match expr {
Expression::NumericLiteral(_)
| Expression::StringLiteral(_)
| Expression::BooleanLiteral(_)
| Expression::NullLiteral(_)
| Expression::RegExpLiteral(_) => self.emit_literal(expr, ctx),
Expression::BinaryExpression(_)
| Expression::PrivateInExpression(_)
| Expression::UnaryExpression(_)
| Expression::ConditionalExpression(_)
| Expression::LogicalExpression(_)
| Expression::UpdateExpression(_) => self.emit_operator(expr, ctx),
Expression::StaticMemberExpression(_)
| Expression::ComputedMemberExpression(_)
| Expression::PrivateFieldExpression(_)
| Expression::ChainExpression(_) => self.emit_member_domain(expr, ctx),
Expression::ObjectExpression(_) | Expression::ArrayExpression(_) => self.emit_object_domain(expr, ctx),
Expression::AssignmentExpression(assign) => self.emit_assignment_expression(assign, ctx),
Expression::TemplateLiteral(_) | Expression::TaggedTemplateExpression(_) => {
self.emit_template_domain(expr, ctx)
}
Expression::ArrowFunctionExpression(_)
| Expression::FunctionExpression(_)
| Expression::ClassExpression(_)
| Expression::NewExpression(_) => self.emit_function_domain(expr, ctx),
Expression::Identifier(ident) => self.emit_identifier_expression(ident, ctx),
Expression::CallExpression(_) => self.emit_call_domain(expr, ctx),
Expression::ThisExpression(_) => self.emit_this_expression(ctx),
Expression::SequenceExpression(seq) => self.emit_sequence_expression(seq, ctx),
Expression::ParenthesizedExpression(p) => self.emit_parenthesized_expression(p, ctx),
_ => self.emit_unsupported_expression(expr, ctx),
}
}
/// Emit a full program into an IRFunction (top-level module body).
/// Split from the original `Compiler::compile`: this is the emit half;
/// oxide_compiler::Compiler::compile calls this then `oxide_ir::lower::lower`.
pub fn emit_program(&self, program: &oxide_parser::Program) -> Result<IRFunction, String> {
let mut ctx = CompileCtx::new();
ctx.pre_register_builtins();
self.predeclare_function_declarations(&program.body, &mut ctx);
// Pre-register builtin identifier references before any temporary register
// is emitted, keeping builtin slots clear of the temporary register pool.
self.pre_register_builtin_references(&program.body, &mut ctx);
// Pre-declare top-level `var` names so hoisted function declarations
// (emitted in the first sub-pass below) can resolve the outer vars.
self.predeclare_var_declarations(&program.body, &mut ctx);
// 闭包捕获分析(AST 级,emit 前确定)
ctx.own_bindings = self.collect_own_binding_names(&[], &program.body);
ctx.captured_bindings = self.collect_captured_bindings(&program.body, &ctx.own_bindings);
// First sub-pass: emit FunctionDeclarations (hoisting)
// This ensures function objects are available before any code runs.
for stmt in &program.body {
if matches!(stmt, Statement::FunctionDeclaration(_)) {
self.emit_statement(stmt, &mut ctx)?;
}
}
// Second sub-pass: emit all other statements
let mut last_result: Option<u8> = None;
for stmt in &program.body {
if matches!(stmt, Statement::FunctionDeclaration(_)) {
continue; // Already emitted above
}
match self.emit_statement(stmt, &mut ctx)? {
Some(r) => last_result = Some(r),
None => last_result = None,
}
}
if let Some(r) = last_result {
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::None, Operand::Reg(r as u32), Operand::None));
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.inst(Inst::load_const(Operand::None, undef_idx));
}
ctx.inst(Inst::new(OpCode::HALT, Operand::None, Operand::None, Operand::None));
let ir = ctx.assemble_ir(
oxide_ir::ParamLayout {
base: ctx.scopes.builtin_reg_map.len() as u32,
count: 0,
},
None,
);
Ok(ir)
}
}
impl Default for Emitter {
fn default() -> Self {
Self::new()
}
}
@@ -6,9 +6,9 @@
//! Execution-stream fields (insts, registers, pc, …) stay flat on //! Execution-stream fields (insts, registers, pc, …) stay flat on
//! `CompileCtx`. //! `CompileCtx`.
use crate::compiler::LabelScope;
use oxide_ir::operand::LabelId;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::LabelScope;
use oxide_ir::operand::LabelId;
/// Jump-target / labeled-statement resolution state. /// Jump-target / labeled-statement resolution state.
pub(crate) struct LabelCtx { pub(crate) struct LabelCtx {
@@ -1,11 +1,11 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::AssignmentOperator; use oxide_parser::AssignmentOperator;
impl Compiler { impl Emitter {
pub(crate) fn emit_assignment_expression( pub(crate) fn emit_assignment_expression(
&self, assign: &oxide_parser::AssignmentExpression, ctx: &mut CompileCtx, &self, assign: &oxide_parser::AssignmentExpression, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,11 +1,11 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::Expression; use oxide_parser::Expression;
impl Compiler { impl Emitter {
fn emit_call_expression(&self, call: &oxide_parser::CallExpression, ctx: &mut CompileCtx) -> Result<u8, String> { fn emit_call_expression(&self, call: &oxide_parser::CallExpression, ctx: &mut CompileCtx) -> Result<u8, String> {
if matches!(&call.callee, Expression::Super(_)) { if matches!(&call.callee, Expression::Super(_)) {
if !ctx.in_derived_constructor { if !ctx.in_derived_constructor {
@@ -1,9 +1,9 @@
use crate::compiler::{CompileCtx, Compiler, ParamSpec}; use crate::{CompileCtx, Emitter, ParamSpec};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_parser::{Class, Expression, Statement}; use oxide_parser::{Class, Expression, Statement};
impl Compiler { impl Emitter {
fn emit_arrow_function_expression( fn emit_arrow_function_expression(
&self, arrow: &oxide_parser::ArrowFunctionExpression, ctx: &mut CompileCtx, &self, arrow: &oxide_parser::ArrowFunctionExpression, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,9 +1,9 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
impl Compiler { impl Emitter {
pub(crate) fn emit_identifier_expression( pub(crate) fn emit_identifier_expression(
&self, ident: &oxide_parser::IdentifierReference, ctx: &mut CompileCtx, &self, ident: &oxide_parser::IdentifierReference, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,11 +1,11 @@
use crate::compiler::{is_int_literal, CompileCtx, Compiler}; use crate::{is_int_literal, CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::Expression; use oxide_parser::Expression;
impl Compiler { impl Emitter {
pub(crate) fn emit_literal(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> { pub(crate) fn emit_literal(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> {
match expr { match expr {
Expression::NumericLiteral(n) => self.emit_numeric_literal_expression(n, ctx), Expression::NumericLiteral(n) => self.emit_numeric_literal_expression(n, ctx),
@@ -1,11 +1,11 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::Expression; use oxide_parser::Expression;
impl Compiler { impl Emitter {
fn emit_static_member_expression( fn emit_static_member_expression(
&self, member: &oxide_parser::StaticMemberExpression, ctx: &mut CompileCtx, &self, member: &oxide_parser::StaticMemberExpression, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,4 +1,4 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::Expression; use oxide_parser::Expression;
pub mod assignment; pub mod assignment;
@@ -13,7 +13,7 @@ pub mod parenthesized;
pub mod template; pub mod template;
pub mod this; pub mod this;
impl Compiler { impl Emitter {
pub(crate) fn emit_unsupported_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> { pub(crate) fn emit_unsupported_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> {
let _ = ctx; let _ = ctx;
Err(format!("unsupported expression type: {:?}", expr)) Err(format!("unsupported expression type: {:?}", expr))
@@ -1,9 +1,9 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
impl Compiler { impl Emitter {
pub(crate) fn emit_array_expression( pub(crate) fn emit_array_expression(
&self, arr: &oxide_parser::ArrayExpression, ctx: &mut CompileCtx, &self, arr: &oxide_parser::ArrayExpression, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::Expression; use oxide_parser::Expression;
mod array; mod array;
mod object_literal; mod object_literal;
impl Compiler { impl Emitter {
pub(crate) fn emit_object_domain(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> { pub(crate) fn emit_object_domain(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> {
match expr { match expr {
Expression::ObjectExpression(obj) => self.emit_object_expression(obj, ctx), Expression::ObjectExpression(obj) => self.emit_object_expression(obj, ctx),
@@ -1,11 +1,11 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{Expression, ObjectPropertyKind, PropertyKey, PropertyKind}; use oxide_parser::{Expression, ObjectPropertyKind, PropertyKey, PropertyKind};
impl Compiler { impl Emitter {
pub(crate) fn emit_object_expression( pub(crate) fn emit_object_expression(
&self, obj: &oxide_parser::ObjectExpression, ctx: &mut CompileCtx, &self, obj: &oxide_parser::ObjectExpression, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,11 +1,11 @@
use crate::compiler::{is_side_effect_free, BinaryOperator, CompileCtx, Compiler}; use crate::{is_side_effect_free, BinaryOperator, CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{ChainElement, Expression, LogicalOperator, SimpleAssignmentTarget, UnaryOperator, UpdateOperator}; use oxide_parser::{ChainElement, Expression, LogicalOperator, SimpleAssignmentTarget, UnaryOperator, UpdateOperator};
impl Compiler { impl Emitter {
fn emit_private_in_expression( fn emit_private_in_expression(
&self, pin: &oxide_parser::PrivateInExpression, ctx: &mut CompileCtx, &self, pin: &oxide_parser::PrivateInExpression, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,6 +1,6 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
impl Compiler { impl Emitter {
pub(crate) fn emit_parenthesized_expression( pub(crate) fn emit_parenthesized_expression(
&self, p: &oxide_parser::ParenthesizedExpression, ctx: &mut CompileCtx, &self, p: &oxide_parser::ParenthesizedExpression, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::Expression; use oxide_parser::Expression;
mod tagged; mod tagged;
mod template_literal; mod template_literal;
impl Compiler { impl Emitter {
pub(crate) fn emit_template_domain(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> { pub(crate) fn emit_template_domain(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> {
match expr { match expr {
Expression::TemplateLiteral(tl) => self.emit_template_literal_expression(tl, ctx), Expression::TemplateLiteral(tl) => self.emit_template_literal_expression(tl, ctx),
@@ -1,9 +1,9 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
impl Compiler { impl Emitter {
pub(crate) fn emit_tagged_template_expression( pub(crate) fn emit_tagged_template_expression(
&self, tt: &oxide_parser::TaggedTemplateExpression, ctx: &mut CompileCtx, &self, tt: &oxide_parser::TaggedTemplateExpression, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,9 +1,9 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
impl Compiler { impl Emitter {
pub(crate) fn emit_template_literal_expression( pub(crate) fn emit_template_literal_expression(
&self, tl: &oxide_parser::TemplateLiteral, ctx: &mut CompileCtx, &self, tl: &oxide_parser::TemplateLiteral, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,9 +1,9 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
impl Compiler { impl Emitter {
pub(crate) fn emit_this_expression(&self, ctx: &mut CompileCtx) -> Result<u8, String> { pub(crate) fn emit_this_expression(&self, ctx: &mut CompileCtx) -> Result<u8, String> {
let r = ctx.alloc_reg(); let r = ctx.alloc_reg();
let src = ctx.static_block_this_reg.unwrap_or(254); let src = ctx.static_block_this_reg.unwrap_or(254);
+14
View File
@@ -0,0 +1,14 @@
pub mod class;
pub mod closure;
pub mod emit;
pub mod emit_ctx;
pub mod expr;
pub mod prepass;
pub mod shared;
pub mod stmt;
pub mod symbol_table;
pub use emit::{CompileCtx, Emitter, FunctionBodyContext, LabelScope, ParamSpec};
pub use emit::{is_int_literal, is_side_effect_free};
pub use oxide_bytecode::module::Constant;
pub use oxide_parser::{AssignmentOperator, BinaryOperator, UnaryOperator};
@@ -1,7 +1,7 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::{Expression, Statement, VariableDeclarationKind}; use oxide_parser::{Expression, Statement, VariableDeclarationKind};
impl Compiler { impl Emitter {
/// builtin slot allocation ahead of the temporary register pool. /// builtin slot allocation ahead of the temporary register pool.
pub(crate) fn pre_register_builtin_references(&self, stmts: &[Statement], ctx: &mut CompileCtx) { pub(crate) fn pre_register_builtin_references(&self, stmts: &[Statement], ctx: &mut CompileCtx) {
@@ -340,4 +340,3 @@ impl Compiler {
} }
} }
@@ -1,4 +1,4 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
@@ -8,7 +8,7 @@ use oxide_parser::{
BindingPattern, Expression, ObjectAssignmentTarget, ObjectPattern, VariableDeclarationKind, BindingPattern, Expression, ObjectAssignmentTarget, ObjectPattern, VariableDeclarationKind,
}; };
impl Compiler { impl Emitter {
fn emit_default_if_undefined( fn emit_default_if_undefined(
&self, val_reg: u8, default_expr: &Expression, ctx: &mut CompileCtx, &self, val_reg: u8, default_expr: &Expression, ctx: &mut CompileCtx,
) -> Result<u8, String> { ) -> Result<u8, String> {
@@ -1,11 +1,11 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::{LabelId, Operand}; use oxide_ir::operand::{LabelId, Operand};
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{ChainElement, Expression, LogicalOperator, PropertyKey}; use oxide_parser::{ChainElement, Expression, LogicalOperator, PropertyKey};
impl Compiler { impl Emitter {
fn emit_optional_guard(&self, reg: u8, short_label: LabelId, ctx: &mut CompileCtx) -> Result<(), String> { fn emit_optional_guard(&self, reg: u8, short_label: LabelId, ctx: &mut CompileCtx) -> Result<(), String> {
let dup_reg = ctx.alloc_reg(); let dup_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(dup_reg as u32), Operand::Reg(reg as u32), Operand::None)); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(dup_reg as u32), Operand::Reg(reg as u32), Operand::None));
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{Expression, PropertyKey}; use oxide_parser::{Expression, PropertyKey};
impl Compiler { impl Emitter {
pub(crate) fn emit_public_field_init( pub(crate) fn emit_public_field_init(
&self, target: Operand, key: &PropertyKey, computed: bool, value: Option<&Expression>, ctx: &mut CompileCtx, &self, target: Operand, key: &PropertyKey, computed: bool, value: Option<&Expression>, ctx: &mut CompileCtx,
) -> Result<(), String> { ) -> Result<(), String> {
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_parser::PropertyKey; use oxide_parser::PropertyKey;
impl Compiler { impl Emitter {
fn private_name_id(&self, name: &str, ctx: &CompileCtx) -> Result<u32, String> { fn private_name_id(&self, name: &str, ctx: &CompileCtx) -> Result<u32, String> {
ctx.scopes ctx.scopes
.private_name_map .private_name_map
@@ -1,7 +1,7 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
impl Compiler { impl Emitter {
pub(crate) fn emit_break_statement( pub(crate) fn emit_break_statement(
&self, stmt: &oxide_parser::BreakStatement, ctx: &mut CompileCtx, &self, stmt: &oxide_parser::BreakStatement, ctx: &mut CompileCtx,
) -> Result<Option<u8>, String> { ) -> Result<Option<u8>, String> {
@@ -1,7 +1,7 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::Statement; use oxide_parser::Statement;
impl Compiler { impl Emitter {
fn emit_expression_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { fn emit_expression_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::ExpressionStatement(es) = stmt else { let Statement::ExpressionStatement(es) = stmt else {
return Ok(None); return Ok(None);
@@ -1,7 +1,7 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::Statement; use oxide_parser::Statement;
impl Compiler { impl Emitter {
fn is_iteration_statement(stmt: &Statement) -> bool { fn is_iteration_statement(stmt: &Statement) -> bool {
matches!( matches!(
stmt, stmt,
@@ -1,4 +1,4 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::Statement; use oxide_parser::Statement;
mod break_continue; mod break_continue;
@@ -6,7 +6,7 @@ mod expression;
mod labeled; mod labeled;
mod return_stmt; mod return_stmt;
impl Compiler { impl Emitter {
pub(crate) fn emit_basic_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_basic_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
match stmt { match stmt {
Statement::ExpressionStatement(_) => self.emit_basic_expression(stmt, ctx), Statement::ExpressionStatement(_) => self.emit_basic_expression(stmt, ctx),
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::Statement; use oxide_parser::Statement;
impl Compiler { impl Emitter {
fn emit_return_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { fn emit_return_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::ReturnStatement(ret) = stmt else { let Statement::ReturnStatement(ret) = stmt else {
return Ok(None); return Ok(None);
@@ -1,7 +1,7 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::Statement; use oxide_parser::Statement;
impl Compiler { impl Emitter {
fn emit_block_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { fn emit_block_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::BlockStatement(block) = stmt else { let Statement::BlockStatement(block) = stmt else {
return Ok(None); return Ok(None);
@@ -1,11 +1,11 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::Statement; use oxide_parser::Statement;
impl Compiler { impl Emitter {
fn emit_if_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { fn emit_if_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::IfStatement(ifs) = stmt else { let Statement::IfStatement(ifs) = stmt else {
return Ok(None); return Ok(None);
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{Statement, VariableDeclarationKind}; use oxide_parser::{Statement, VariableDeclarationKind};
impl Compiler { impl Emitter {
pub(crate) fn emit_class_declaration_statement( pub(crate) fn emit_class_declaration_statement(
&self, stmt: &Statement, ctx: &mut CompileCtx, &self, stmt: &Statement, ctx: &mut CompileCtx,
) -> Result<Option<u8>, String> { ) -> Result<Option<u8>, String> {
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler, ParamSpec}; use crate::{CompileCtx, Emitter, ParamSpec};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::Statement; use oxide_parser::Statement;
impl Compiler { impl Emitter {
pub(crate) fn emit_function_declaration_statement( pub(crate) fn emit_function_declaration_statement(
&self, stmt: &Statement, ctx: &mut CompileCtx, &self, stmt: &Statement, ctx: &mut CompileCtx,
) -> Result<Option<u8>, String> { ) -> Result<Option<u8>, String> {
@@ -1,11 +1,11 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::Statement; use oxide_parser::Statement;
mod class_decl; mod class_decl;
mod function_decl; mod function_decl;
mod var; mod var;
impl Compiler { impl Emitter {
pub(crate) fn emit_declaration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_declaration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
match stmt { match stmt {
Statement::VariableDeclaration(_) => self.emit_variable_declaration_statement(stmt, ctx), Statement::VariableDeclaration(_) => self.emit_variable_declaration_statement(stmt, ctx),
@@ -1,11 +1,11 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{BindingPattern, Expression, Statement, VariableDeclarationKind}; use oxide_parser::{BindingPattern, Expression, Statement, VariableDeclarationKind};
impl Compiler { impl Emitter {
pub(crate) fn emit_variable_declaration_statement( pub(crate) fn emit_variable_declaration_statement(
&self, stmt: &Statement, ctx: &mut CompileCtx, &self, stmt: &Statement, ctx: &mut CompileCtx,
) -> Result<Option<u8>, String> { ) -> Result<Option<u8>, String> {
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::Statement; use oxide_parser::Statement;
mod throw; mod throw;
mod try_catch; mod try_catch;
impl Compiler { impl Emitter {
pub(crate) fn emit_exception_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_exception_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
match stmt { match stmt {
Statement::ThrowStatement(_) => self.emit_throw_statement(stmt, ctx), Statement::ThrowStatement(_) => self.emit_throw_statement(stmt, ctx),
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::Statement; use oxide_parser::Statement;
impl Compiler { impl Emitter {
pub(crate) fn emit_throw_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_throw_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::ThrowStatement(ts) = stmt else { let Statement::ThrowStatement(ts) = stmt else {
return Ok(None); return Ok(None);
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{Statement, VariableDeclarationKind}; use oxide_parser::{Statement, VariableDeclarationKind};
impl Compiler { impl Emitter {
pub(crate) fn emit_try_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_try_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::TryStatement(ts) = stmt else { let Statement::TryStatement(ts) = stmt else {
return Ok(None); return Ok(None);
@@ -1,8 +1,8 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_parser::Statement; use oxide_parser::Statement;
impl Compiler { impl Emitter {
pub(crate) fn emit_do_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_do_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::DoWhileStatement(dw) = stmt else { let Statement::DoWhileStatement(dw) = stmt else {
return Ok(None); return Ok(None);
@@ -1,11 +1,11 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{BindingPattern, ForStatementInit, Statement, VariableDeclarationKind}; use oxide_parser::{BindingPattern, ForStatementInit, Statement, VariableDeclarationKind};
impl Compiler { impl Emitter {
pub(crate) fn emit_for_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_for_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::ForStatement(fr) = stmt else { let Statement::ForStatement(fr) = stmt else {
return Ok(None); return Ok(None);
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{ForStatementLeft, Statement, VariableDeclarationKind}; use oxide_parser::{ForStatementLeft, Statement, VariableDeclarationKind};
impl Compiler { impl Emitter {
pub(crate) fn emit_for_in_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_for_in_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::ForInStatement(fi) = stmt else { let Statement::ForInStatement(fi) = stmt else {
return Ok(None); return Ok(None);
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::{ForStatementLeft, Statement}; use oxide_parser::{ForStatementLeft, Statement};
impl Compiler { impl Emitter {
pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::ForOfStatement(fo) = stmt else { let Statement::ForOfStatement(fo) = stmt else {
return Ok(None); return Ok(None);
@@ -1,4 +1,4 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_parser::Statement; use oxide_parser::Statement;
mod do_while; mod do_while;
@@ -7,7 +7,7 @@ mod for_in;
mod for_of; mod for_of;
mod while_loop; mod while_loop;
impl Compiler { impl Emitter {
pub(crate) fn emit_iteration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_iteration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
match stmt { match stmt {
Statement::WhileStatement(_) => self.emit_while_statement(stmt, ctx), Statement::WhileStatement(_) => self.emit_while_statement(stmt, ctx),
@@ -1,8 +1,8 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_parser::Statement; use oxide_parser::Statement;
impl Compiler { impl Emitter {
pub(crate) fn emit_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { pub(crate) fn emit_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::WhileStatement(wh) = stmt else { let Statement::WhileStatement(wh) = stmt else {
return Ok(None); return Ok(None);
@@ -1,10 +1,10 @@
use crate::compiler::{CompileCtx, Compiler}; use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_parser::Statement; use oxide_parser::Statement;
impl Compiler { impl Emitter {
fn emit_switch_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> { fn emit_switch_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let Statement::SwitchStatement(sw) = stmt else { let Statement::SwitchStatement(sw) = stmt else {
return Ok(None); return Ok(None);