mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
refactor: CompileCtx 拆分为 LabelCtx/ScopeCtx,字面量域改用注册表入口
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::emit_ctx::{LabelCtx, ScopeCtx};
|
||||
use crate::symbol_table::{Binding, SymbolTable};
|
||||
use oxide_bytecode::module::CompiledModule;
|
||||
use oxide_bytecode::opcode::{self, OpCode};
|
||||
@@ -145,18 +146,9 @@ pub(crate) struct CompileCtx {
|
||||
next_reg: u8,
|
||||
pub(crate) max_regs: u8,
|
||||
reserved_reg_start: u8,
|
||||
symbols: SymbolTable,
|
||||
pub(crate) label_map: HashMap<Label, usize>,
|
||||
pub(crate) loop_stack: Vec<(Label, Label)>,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) switch_stack: Vec<Label>,
|
||||
/// Active labeled-statement scopes (resolves `break label` / `continue label`).
|
||||
pub(crate) label_scopes: Vec<LabelScope>,
|
||||
/// Label names awaiting binding to the next emitted loop's continue target.
|
||||
pub(crate) pending_loop_labels: Vec<String>,
|
||||
pub(crate) label_counter: u32,
|
||||
pub(crate) labels: LabelCtx,
|
||||
pub(crate) scopes: ScopeCtx,
|
||||
pub(crate) projected_pc: usize,
|
||||
pub(crate) builtin_reg_map: Vec<(String, u8)>,
|
||||
pub(crate) sub_modules: Vec<CompiledModule>,
|
||||
/// Register holding `this` in the enclosing function context.
|
||||
/// Used by arrow functions to capture lexical `this`.
|
||||
@@ -166,8 +158,6 @@ pub(crate) struct CompileCtx {
|
||||
pub(crate) in_instance_method: bool,
|
||||
pub(crate) in_static_method: bool,
|
||||
pub(crate) static_block_this_reg: Option<u8>,
|
||||
pub(crate) private_name_map: Vec<(String, u32)>,
|
||||
pub(crate) next_private_name_id: u32,
|
||||
pub(crate) after_super_insert: Option<Vec<opcode::Instr>>,
|
||||
pub(crate) after_super_inserted: bool,
|
||||
/// Set when alloc_reg() overflows into the reserved this/new.target range (≥254).
|
||||
@@ -220,23 +210,27 @@ impl CompileCtx {
|
||||
next_reg: 1,
|
||||
max_regs: 1,
|
||||
reserved_reg_start: 1,
|
||||
symbols: SymbolTable::new(),
|
||||
label_map: HashMap::new(),
|
||||
loop_stack: Vec::new(),
|
||||
switch_stack: Vec::new(),
|
||||
label_scopes: Vec::new(),
|
||||
pending_loop_labels: Vec::new(),
|
||||
label_counter: 0,
|
||||
labels: LabelCtx {
|
||||
label_map: HashMap::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,
|
||||
},
|
||||
projected_pc: 0,
|
||||
builtin_reg_map: Vec::new(),
|
||||
sub_modules: 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,
|
||||
private_name_map: Vec::new(),
|
||||
next_private_name_id: 1,
|
||||
after_super_insert: None,
|
||||
after_super_inserted: false,
|
||||
reg_overflow: false,
|
||||
@@ -339,7 +333,7 @@ impl CompileCtx {
|
||||
pub(crate) fn reset_regs(&mut self) {
|
||||
self.next_reg = self.builtin_reg_floor().max(self.reserved_reg_start);
|
||||
self.projected_pc = 0;
|
||||
self.label_counter = 0;
|
||||
self.labels.label_counter = 0;
|
||||
}
|
||||
|
||||
pub(crate) fn reg_checkpoint(&self) -> u8 {
|
||||
@@ -395,48 +389,49 @@ impl CompileCtx {
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_label(&self, label: Label) -> Result<usize, String> {
|
||||
self.label_map
|
||||
self.labels
|
||||
.label_map
|
||||
.get(&label)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("Label {:?} not found in bytecode map", label))
|
||||
}
|
||||
|
||||
pub(crate) fn push_scope(&mut self) {
|
||||
self.symbols.push_scope();
|
||||
self.scopes.symbols.push_scope();
|
||||
}
|
||||
|
||||
pub(crate) fn pop_scope(&mut self) {
|
||||
self.symbols.pop_scope();
|
||||
self.scopes.symbols.pop_scope();
|
||||
}
|
||||
|
||||
pub(crate) fn declare(
|
||||
&mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
|
||||
) -> Result<(), String> {
|
||||
self.symbols.declare(name, reg, kind, is_const)
|
||||
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.symbols.declare_initialized(name, reg, kind, is_const)
|
||||
self.scopes.symbols.declare_initialized(name, reg, kind, is_const)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn push_scope_with_kind(&mut self, kind: ScopeKind) {
|
||||
self.symbols.push_scope_with_kind(kind);
|
||||
self.scopes.symbols.push_scope_with_kind(kind);
|
||||
}
|
||||
|
||||
pub(crate) fn lookup(&self, name: &str) -> Result<u8, String> {
|
||||
self.symbols.lookup(name)
|
||||
self.scopes.symbols.lookup(name)
|
||||
}
|
||||
|
||||
pub(crate) fn lookup_or_builtin(&mut self, name: &str) -> Result<u8, String> {
|
||||
match self.symbols.lookup(name) {
|
||||
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.symbols.pre_register_global(name, reg);
|
||||
self.builtin_reg_map.push((name.to_string(), 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),
|
||||
@@ -444,58 +439,58 @@ impl CompileCtx {
|
||||
}
|
||||
|
||||
pub(crate) fn lookup_or_global(&mut self, name: &str) -> u8 {
|
||||
if let Some(reg) = self.symbols.lookup_any(name) {
|
||||
if let Some(reg) = self.scopes.symbols.lookup_any(name) {
|
||||
return reg;
|
||||
}
|
||||
let reg = self.alloc_reg();
|
||||
self.symbols.lookup_or_global(name, reg)
|
||||
self.scopes.symbols.lookup_or_global(name, reg)
|
||||
}
|
||||
|
||||
pub(crate) fn lookup_const_flag(&self, name: &str) -> bool {
|
||||
self.symbols.lookup_is_const(name)
|
||||
self.scopes.symbols.lookup_is_const(name)
|
||||
}
|
||||
|
||||
pub(crate) fn init_var(&mut self, name: &str) {
|
||||
self.symbols.init_var(name);
|
||||
self.scopes.symbols.init_var(name);
|
||||
}
|
||||
|
||||
pub(crate) fn next_label_id(&mut self) -> u32 {
|
||||
let id = self.label_counter;
|
||||
self.label_counter += 1;
|
||||
let id = self.labels.label_counter;
|
||||
self.labels.label_counter += 1;
|
||||
id
|
||||
}
|
||||
|
||||
pub(crate) fn push_loop(&mut self, break_label: Label, continue_label: Label) {
|
||||
self.loop_stack.push((break_label, continue_label));
|
||||
self.labels.loop_stack.push((break_label, continue_label));
|
||||
}
|
||||
|
||||
pub(crate) fn pop_loop(&mut self) {
|
||||
self.loop_stack.pop();
|
||||
self.labels.loop_stack.pop();
|
||||
}
|
||||
|
||||
pub(crate) fn current_loop(&self) -> Option<&(Label, Label)> {
|
||||
self.loop_stack.last()
|
||||
self.labels.loop_stack.last()
|
||||
}
|
||||
|
||||
pub(crate) fn push_switch(&mut self, break_label: Label) {
|
||||
self.switch_stack.push(break_label);
|
||||
self.labels.switch_stack.push(break_label);
|
||||
}
|
||||
|
||||
pub(crate) fn pop_switch(&mut self) {
|
||||
self.switch_stack.pop();
|
||||
self.labels.switch_stack.pop();
|
||||
}
|
||||
|
||||
pub(crate) fn current_switch(&self) -> Option<&Label> {
|
||||
self.switch_stack.last()
|
||||
self.labels.switch_stack.last()
|
||||
}
|
||||
|
||||
pub(crate) fn push_label_scope(
|
||||
&mut self, name: &str, break_label: Label, continue_label: Option<Label>,
|
||||
) -> Result<(), String> {
|
||||
if self.label_scopes.iter().any(|s| s.name == name) {
|
||||
if self.labels.label_scopes.iter().any(|s| s.name == name) {
|
||||
return Err(format!("SyntaxError: Label '{name}' has already been declared"));
|
||||
}
|
||||
self.label_scopes.push(LabelScope {
|
||||
self.labels.label_scopes.push(LabelScope {
|
||||
name: name.to_string(),
|
||||
break_label,
|
||||
continue_label,
|
||||
@@ -504,31 +499,33 @@ impl CompileCtx {
|
||||
}
|
||||
|
||||
pub(crate) fn pop_label_scope(&mut self) {
|
||||
self.label_scopes.pop();
|
||||
self.labels.label_scopes.pop();
|
||||
}
|
||||
|
||||
pub(crate) fn find_label(&self, name: &str) -> Option<&LabelScope> {
|
||||
self.label_scopes.iter().rev().find(|s| s.name == name)
|
||||
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.label_scopes.iter().any(|s| s.name == name) || self.pending_loop_labels.iter().any(|n| n == name) {
|
||||
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.pending_loop_labels.push(name.to_string());
|
||||
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: Label, continue_label: Label) -> usize {
|
||||
let names = std::mem::take(&mut self.pending_loop_labels);
|
||||
let names = std::mem::take(&mut self.labels.pending_loop_labels);
|
||||
let count = names.len();
|
||||
for name in names {
|
||||
self.label_scopes.push(LabelScope {
|
||||
self.labels.label_scopes.push(LabelScope {
|
||||
name,
|
||||
break_label,
|
||||
continue_label: Some(continue_label),
|
||||
@@ -539,12 +536,12 @@ impl CompileCtx {
|
||||
|
||||
pub(crate) fn pop_label_scopes(&mut self, n: usize) {
|
||||
for _ in 0..n {
|
||||
self.label_scopes.pop();
|
||||
self.labels.label_scopes.pop();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_builtin(&self, name: &str) -> bool {
|
||||
self.builtin_reg_map.iter().any(|(n, _)| n == name)
|
||||
self.scopes.builtin_reg_map.iter().any(|(n, _)| n == name)
|
||||
}
|
||||
|
||||
pub(crate) fn is_known_builtin(name: &str) -> bool {
|
||||
@@ -552,7 +549,8 @@ impl CompileCtx {
|
||||
}
|
||||
|
||||
fn builtin_reg_floor(&self) -> u8 {
|
||||
self.builtin_reg_map
|
||||
self.scopes
|
||||
.builtin_reg_map
|
||||
.iter()
|
||||
.map(|(_, reg)| reg.saturating_add(1))
|
||||
.max()
|
||||
@@ -662,9 +660,9 @@ impl Compiler {
|
||||
|
||||
// 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.builtin_reg_map = parent_ctx.builtin_reg_map.clone();
|
||||
ctx.private_name_map = parent_ctx.private_name_map.clone();
|
||||
ctx.next_private_name_id = parent_ctx.next_private_name_id;
|
||||
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;
|
||||
@@ -683,8 +681,8 @@ impl Compiler {
|
||||
// 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.symbols.scopes[0].bindings {
|
||||
ctx.symbols.scopes[0].bindings.insert(
|
||||
for (name, binding) in &parent_ctx.scopes.symbols.scopes[0].bindings {
|
||||
ctx.scopes.symbols.scopes[0].bindings.insert(
|
||||
name.clone(),
|
||||
Binding {
|
||||
reg: binding.reg,
|
||||
@@ -695,7 +693,7 @@ impl Compiler {
|
||||
inherited_reg_start = inherited_reg_start.max(binding.reg.saturating_add(1));
|
||||
}
|
||||
for (name, reg) in extra_bindings {
|
||||
ctx.symbols.scopes[0].bindings.insert(
|
||||
ctx.scopes.symbols.scopes[0].bindings.insert(
|
||||
(*name).to_string(),
|
||||
Binding {
|
||||
reg: *reg,
|
||||
@@ -820,7 +818,7 @@ impl Compiler {
|
||||
n_registers: ctx.max_regs,
|
||||
n_args: param_specs.len() as u8,
|
||||
param_base,
|
||||
builtin_reg_map: ctx.builtin_reg_map,
|
||||
builtin_reg_map: ctx.scopes.builtin_reg_map,
|
||||
sub_modules: ctx.sub_modules,
|
||||
is_arrow: false,
|
||||
captured_this_const_idx: 0,
|
||||
@@ -890,8 +888,8 @@ impl Compiler {
|
||||
constants: ctx.constants,
|
||||
n_registers: ctx.max_regs,
|
||||
n_args: 0,
|
||||
param_base: ctx.builtin_reg_map.len() as u8,
|
||||
builtin_reg_map: ctx.builtin_reg_map,
|
||||
param_base: ctx.scopes.builtin_reg_map.len() as u8,
|
||||
builtin_reg_map: ctx.scopes.builtin_reg_map,
|
||||
sub_modules: ctx.sub_modules,
|
||||
is_arrow: false,
|
||||
captured_this_const_idx: 0,
|
||||
|
||||
@@ -72,7 +72,7 @@ impl Compiler {
|
||||
self.count_expression(right, ctx);
|
||||
ctx.count_ic_set_with_ext(); // IC_SET_PROP + 3 ext
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- rhs
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
}
|
||||
|
||||
fn count_computed_member_logical_assignment(
|
||||
@@ -88,7 +88,7 @@ impl Compiler {
|
||||
self.count_expression(right, ctx);
|
||||
ctx.projected_pc += 1; // SET_PROP_DYNAMIC
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- rhs
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
}
|
||||
|
||||
fn count_identifier_logical_assignment(
|
||||
@@ -101,6 +101,6 @@ impl Compiler {
|
||||
self.count_expression(right, ctx);
|
||||
ctx.projected_pc += 1; // STORE_VAR
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- rhs
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ impl Compiler {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- chain value
|
||||
ctx.projected_pc += 1; // JMP over short-circuit writer
|
||||
ctx.label_map.insert(Label::TernaryElse(id), ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(Label::TernaryElse(id), ctx.projected_pc);
|
||||
ctx.projected_pc += 1; // LOAD_CONST undefined
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ impl Compiler {
|
||||
ctx.alloc_reg(); // result register
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- consequent
|
||||
ctx.projected_pc += 1; // JMP to end
|
||||
ctx.label_map.insert(else_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(else_label, ctx.projected_pc);
|
||||
self.count_expression(&cond.alternate, ctx);
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- alternate
|
||||
ctx.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(end_label, ctx.projected_pc);
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_sequence_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
@@ -49,7 +49,7 @@ impl Compiler {
|
||||
ctx.projected_pc += 1; // JMP_IF_FALSE, JMP_IF_TRUE, or JMP_IF_NULLISH
|
||||
if matches!(log.operator, LogicalOperator::Coalesce) {
|
||||
ctx.projected_pc += 1; // JMP over RHS on non-nullish
|
||||
ctx.label_map.insert(Label::TernaryElse(id), ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(Label::TernaryElse(id), ctx.projected_pc);
|
||||
}
|
||||
self.count_expression(&log.right, ctx);
|
||||
ctx.projected_pc += 1; // LOAD_VAR (overwrite)
|
||||
@@ -58,7 +58,7 @@ impl Compiler {
|
||||
LogicalOperator::Or => Label::TernaryElse(id),
|
||||
LogicalOperator::Coalesce => Label::TernaryEnd(id),
|
||||
};
|
||||
ctx.label_map.insert(skip_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(skip_label, ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
/// Domain entry point: count any literal expression. The central
|
||||
/// `count_expression` match delegates all literal variants here in one arm.
|
||||
/// Numeric/String/Boolean/Null share the default single-`LOAD_CONST` shape;
|
||||
/// RegExp emits three instructions.
|
||||
pub(in crate::counter) fn count_literal(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
match expr {
|
||||
Expression::RegExpLiteral(_) => self.count_regexp_literal(ctx),
|
||||
_ => self.count_default_expression(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_this_expression(&self, ctx: &mut CompileCtx) {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR from reg 254
|
||||
|
||||
@@ -38,7 +38,11 @@ impl Compiler {
|
||||
Expression::ThisExpression(_) => self.count_this_expression(ctx),
|
||||
Expression::Identifier(_) => self.count_identifier_expression(expr, ctx),
|
||||
Expression::UpdateExpression(_) => self.count_update_expression(expr, ctx),
|
||||
Expression::RegExpLiteral(_) => self.count_regexp_literal(ctx),
|
||||
Expression::NumericLiteral(_)
|
||||
| Expression::StringLiteral(_)
|
||||
| Expression::BooleanLiteral(_)
|
||||
| Expression::NullLiteral(_)
|
||||
| Expression::RegExpLiteral(_) => self.count_literal(expr, ctx),
|
||||
_ => self.count_default_expression(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ impl Compiler {
|
||||
LogicalOperator::Coalesce => {
|
||||
ctx.projected_pc += 1; // JMP_IF_NULLISH to store body
|
||||
ctx.projected_pc += 1; // JMP to end on non-nullish
|
||||
ctx.label_map.insert(Label::TernaryElse(id), ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(Label::TernaryElse(id), ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ impl Compiler {
|
||||
// LabeledEnd target right after the body for `break label`.
|
||||
let id = ctx.next_label_id();
|
||||
self.count_statement(&stmt.body, ctx);
|
||||
ctx.label_map.insert(Label::LabeledEnd(id), ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(Label::LabeledEnd(id), ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@ impl Compiler {
|
||||
if stmt.alternate.is_some() {
|
||||
ctx.projected_pc += 1; // JMP (skip else)
|
||||
}
|
||||
ctx.label_map.insert(else_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(else_label, ctx.projected_pc);
|
||||
if let Some(alt_stmt) = &stmt.alternate {
|
||||
self.count_statement(alt_stmt, ctx);
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- alternate
|
||||
}
|
||||
ctx.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(end_label, ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl Compiler {
|
||||
ctx.projected_pc += 1; // JMP
|
||||
}
|
||||
|
||||
ctx.label_map.insert(catch_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(catch_label, ctx.projected_pc);
|
||||
if let Some(catch) = &stmt.handler {
|
||||
ctx.push_scope();
|
||||
if let Some(_param) = &catch.param {
|
||||
@@ -55,7 +55,7 @@ impl Compiler {
|
||||
|
||||
if let Some(finally) = &stmt.finalizer {
|
||||
let finally_label = Label::FinallyBody(id);
|
||||
ctx.label_map.insert(finally_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(finally_label, ctx.projected_pc);
|
||||
for fs in &finally.body {
|
||||
self.count_statement(fs, ctx);
|
||||
}
|
||||
@@ -63,6 +63,6 @@ impl Compiler {
|
||||
ctx.projected_pc += 1; // TRY_FINALLY_END
|
||||
}
|
||||
|
||||
ctx.label_map.insert(try_end_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(try_end_label, ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,12 @@ impl Compiler {
|
||||
let start_label = Label::WhileStart(id);
|
||||
let end_label = Label::WhileEnd(id);
|
||||
|
||||
ctx.label_map.insert(start_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(start_label, ctx.projected_pc);
|
||||
self.count_expression(&stmt.test, ctx);
|
||||
ctx.projected_pc += 1; // JMP_IF_FALSE
|
||||
self.count_statement(&stmt.body, ctx);
|
||||
ctx.projected_pc += 1; // JMP (backward)
|
||||
ctx.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(end_label, ctx.projected_pc);
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_do_while_statement(
|
||||
@@ -23,11 +23,11 @@ impl Compiler {
|
||||
let start_label = Label::DoWhileStart(id);
|
||||
let end_label = Label::DoWhileEnd(id);
|
||||
|
||||
ctx.label_map.insert(start_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(start_label, ctx.projected_pc);
|
||||
self.count_statement(&stmt.body, ctx);
|
||||
self.count_expression(&stmt.test, ctx);
|
||||
ctx.projected_pc += 1; // JMP_IF_TRUE (backward)
|
||||
ctx.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(end_label, ctx.projected_pc);
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_for_statement(&self, stmt: &oxide_parser::ForStatement<'_>, ctx: &mut CompileCtx) {
|
||||
@@ -51,18 +51,18 @@ impl Compiler {
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.label_map.insert(start_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(start_label, ctx.projected_pc);
|
||||
if let Some(test) = &stmt.test {
|
||||
self.count_expression(test, ctx);
|
||||
ctx.projected_pc += 1; // JMP_IF_FALSE
|
||||
}
|
||||
self.count_statement(&stmt.body, ctx);
|
||||
ctx.label_map.insert(update_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(update_label, ctx.projected_pc);
|
||||
if let Some(update) = &stmt.update {
|
||||
self.count_expression(update, ctx);
|
||||
}
|
||||
ctx.projected_pc += 1; // JMP (backward)
|
||||
ctx.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(end_label, ctx.projected_pc);
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_for_in_statement(
|
||||
@@ -75,7 +75,7 @@ impl Compiler {
|
||||
self.count_expression(&stmt.right, ctx);
|
||||
ctx.count_instr(); // FOR_IN_INIT
|
||||
|
||||
ctx.label_map.insert(start_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(start_label, ctx.projected_pc);
|
||||
ctx.count_instr(); // FOR_IN_DONE
|
||||
ctx.count_jump(); // JMP_IF_FALSE
|
||||
ctx.count_jump(); // JMP cleanup
|
||||
@@ -98,7 +98,7 @@ impl Compiler {
|
||||
self.count_statement(&stmt.body, ctx);
|
||||
ctx.count_jump(); // JMP back to start
|
||||
|
||||
ctx.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.count_instr(); // FOR_IN_CLEANUP
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ impl Compiler {
|
||||
|
||||
self.count_expression(&stmt.right, ctx);
|
||||
ctx.count_instr(); // FOR_OF_INIT
|
||||
ctx.label_map.insert(start_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(start_label, ctx.projected_pc);
|
||||
ctx.alloc_reg(); // has_reg
|
||||
ctx.count_instr(); // FOR_OF_DONE
|
||||
ctx.count_jump(); // JMP_IF_FALSE
|
||||
@@ -137,7 +137,7 @@ impl Compiler {
|
||||
}
|
||||
self.count_statement(&stmt.body, ctx);
|
||||
ctx.count_jump(); // JMP back
|
||||
ctx.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.count_instr(); // FOR_OF_CLOSE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +29,13 @@ impl Compiler {
|
||||
|
||||
for (case_idx, case) in cases.iter().enumerate() {
|
||||
let case_label = Label::SwitchCase(id, case_idx as u32);
|
||||
ctx.label_map.insert(case_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(case_label, ctx.projected_pc);
|
||||
for s in &case.consequent {
|
||||
self.count_statement(s, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.labels.label_map.insert(end_label, ctx.projected_pc);
|
||||
ctx.pop_switch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//! Grouped `CompileCtx` sub-contexts.
|
||||
//!
|
||||
//! Splits the compiler's central `CompileCtx` so syntax-domain workers borrow
|
||||
//! only the slice they need: `LabelCtx` for jump-target resolution, `ScopeCtx`
|
||||
//! for identifier binding. `PatternCtx` is reserved for destructuring work.
|
||||
//! Execution-stream fields (bytecode, registers, pc, …) stay flat on
|
||||
//! `CompileCtx`.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::compiler::{Label, LabelScope};
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
/// Jump-target / labeled-statement resolution state.
|
||||
pub(crate) struct LabelCtx {
|
||||
pub(crate) label_map: HashMap<Label, usize>,
|
||||
pub(crate) loop_stack: Vec<(Label, Label)>,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) switch_stack: Vec<Label>,
|
||||
/// Active labeled-statement scopes (resolves `break label` / `continue label`).
|
||||
pub(crate) label_scopes: Vec<LabelScope>,
|
||||
/// Label names awaiting binding to the next emitted loop's continue target.
|
||||
pub(crate) pending_loop_labels: Vec<String>,
|
||||
pub(crate) label_counter: u32,
|
||||
}
|
||||
|
||||
/// Identifier-binding state: symbols, builtin registers, private names.
|
||||
pub(crate) struct ScopeCtx {
|
||||
pub(crate) symbols: SymbolTable,
|
||||
pub(crate) builtin_reg_map: Vec<(String, u8)>,
|
||||
pub(crate) private_name_map: Vec<(String, u32)>,
|
||||
pub(crate) next_private_name_id: u32,
|
||||
}
|
||||
|
||||
/// Reserved for destructuring-pattern state (Phase 15). Intentionally empty
|
||||
/// today; named so future pattern work has a home without touching `ScopeCtx`.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct PatternCtx;
|
||||
@@ -1,6 +1,20 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
/// Domain entry point: emit any literal expression. The central
|
||||
/// `emit_expression` match delegates all literal variants here in one arm;
|
||||
/// adding a new literal kind only touches this file plus that one arm.
|
||||
pub(in crate::emitter) fn emit_literal(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> {
|
||||
match expr {
|
||||
Expression::NumericLiteral(n) => self.emit_numeric_literal_expression(n, ctx),
|
||||
Expression::StringLiteral(s) => self.emit_string_literal_expression(s, ctx),
|
||||
Expression::BooleanLiteral(b) => self.emit_boolean_literal_expression(b, ctx),
|
||||
Expression::NullLiteral(_) => self.emit_null_literal_expression(ctx),
|
||||
Expression::RegExpLiteral(lit) => self.emit_reg_exp_literal_expression(lit, ctx),
|
||||
_ => self.emit_unsupported_expression(expr, ctx),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::emitter) fn emit_numeric_literal_expression(
|
||||
&self, n: &oxide_parser::NumericLiteral, ctx: &mut CompileCtx,
|
||||
) -> Result<u8, String> {
|
||||
|
||||
@@ -217,7 +217,8 @@ impl Compiler {
|
||||
}
|
||||
|
||||
fn private_name_id(&self, name: &str, ctx: &CompileCtx) -> Result<u32, String> {
|
||||
ctx.private_name_map
|
||||
ctx.scopes
|
||||
.private_name_map
|
||||
.iter()
|
||||
.find_map(|(n, id)| (n == name).then_some(*id))
|
||||
.ok_or_else(|| format!("private name #{name} is not defined"))
|
||||
@@ -640,8 +641,8 @@ impl Compiler {
|
||||
if private_names.iter().any(|(existing, _)| existing == &name) {
|
||||
return Err(format!("duplicate private name #{name}"));
|
||||
}
|
||||
let id = ctx.next_private_name_id;
|
||||
ctx.next_private_name_id = ctx.next_private_name_id.saturating_add(1);
|
||||
let id = ctx.scopes.next_private_name_id;
|
||||
ctx.scopes.next_private_name_id = ctx.scopes.next_private_name_id.saturating_add(1);
|
||||
private_names.push((name, id));
|
||||
}
|
||||
if method.kind == MethodDefinitionKind::Constructor {
|
||||
@@ -658,8 +659,8 @@ impl Compiler {
|
||||
if private_names.iter().any(|(existing, _)| existing == &name) {
|
||||
return Err(format!("duplicate private name #{name}"));
|
||||
}
|
||||
let id = ctx.next_private_name_id;
|
||||
ctx.next_private_name_id = ctx.next_private_name_id.saturating_add(1);
|
||||
let id = ctx.scopes.next_private_name_id;
|
||||
ctx.scopes.next_private_name_id = ctx.scopes.next_private_name_id.saturating_add(1);
|
||||
private_names.push((name, id));
|
||||
}
|
||||
if !prop.r#static {
|
||||
@@ -682,9 +683,9 @@ impl Compiler {
|
||||
};
|
||||
|
||||
let saved_derived = ctx.in_derived_constructor;
|
||||
let saved_private_names = ctx.private_name_map.clone();
|
||||
let saved_private_names = ctx.scopes.private_name_map.clone();
|
||||
ctx.in_derived_constructor = is_derived;
|
||||
ctx.private_name_map = private_names.clone();
|
||||
ctx.scopes.private_name_map = private_names.clone();
|
||||
|
||||
let mut ctor_module = if let Some(method) = constructor_method {
|
||||
let (param_names, body_stmts) = self.extract_function_parts(method.value.as_ref())?;
|
||||
@@ -784,7 +785,7 @@ impl Compiler {
|
||||
module.bytecode.push(opcode::encode(OpCode::SUPER_CALL, 0, 0, 0));
|
||||
module.bytecode.push(0);
|
||||
let mut field_ctx = CompileCtx::new();
|
||||
field_ctx.private_name_map = private_names.clone();
|
||||
field_ctx.scopes.private_name_map = private_names.clone();
|
||||
for field in &instance_fields {
|
||||
if let PropertyKey::PrivateIdentifier(private) = &field.key {
|
||||
self.emit_private_field_init(254, private.name.as_str(), field.value.as_ref(), &mut field_ctx)?;
|
||||
@@ -912,7 +913,7 @@ impl Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.private_name_map = saved_private_names;
|
||||
ctx.scopes.private_name_map = saved_private_names;
|
||||
Ok(ctor_reg)
|
||||
}
|
||||
|
||||
@@ -978,10 +979,11 @@ impl Compiler {
|
||||
}
|
||||
pub(crate) fn emit_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> {
|
||||
match expr {
|
||||
Expression::NumericLiteral(n) => self.emit_numeric_literal_expression(n, ctx),
|
||||
Expression::StringLiteral(s) => self.emit_string_literal_expression(s, ctx),
|
||||
Expression::BooleanLiteral(b) => self.emit_boolean_literal_expression(b, ctx),
|
||||
Expression::NullLiteral(_) => self.emit_null_literal_expression(ctx),
|
||||
Expression::NumericLiteral(_)
|
||||
| Expression::StringLiteral(_)
|
||||
| Expression::BooleanLiteral(_)
|
||||
| Expression::NullLiteral(_)
|
||||
| Expression::RegExpLiteral(_) => self.emit_literal(expr, ctx),
|
||||
Expression::PrivateInExpression(pin) => self.emit_private_in_expression(pin, ctx),
|
||||
Expression::BinaryExpression(bin) => self.emit_binary_expression(bin, ctx),
|
||||
Expression::UnaryExpression(un) => self.emit_unary_expression(un, ctx),
|
||||
@@ -1005,7 +1007,6 @@ impl Compiler {
|
||||
Expression::ParenthesizedExpression(p) => self.emit_parenthesized_expression(p, ctx),
|
||||
Expression::CallExpression(call) => self.emit_call_expression(call, ctx),
|
||||
Expression::ThisExpression(_) => self.emit_this_expression(ctx),
|
||||
Expression::RegExpLiteral(lit) => self.emit_reg_exp_literal_expression(lit, ctx),
|
||||
Expression::SequenceExpression(seq) => self.emit_sequence_expression(seq, ctx),
|
||||
_ => self.emit_unsupported_expression(expr, ctx),
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ impl Compiler {
|
||||
};
|
||||
|
||||
let catch_label_pc = ctx.bytecode.len();
|
||||
ctx.label_map.insert(catch_label, catch_label_pc);
|
||||
ctx.labels.label_map.insert(catch_label, catch_label_pc);
|
||||
|
||||
if let Some(try_begin_pc) = try_begin_pos {
|
||||
let offset = catch_label_pc as isize - (try_begin_pc as isize);
|
||||
@@ -90,7 +90,7 @@ impl Compiler {
|
||||
if has_finally {
|
||||
let finally_label = Label::FinallyBody(id);
|
||||
let finally_label_pc = ctx.bytecode.len();
|
||||
ctx.label_map.insert(finally_label, finally_label_pc);
|
||||
ctx.labels.label_map.insert(finally_label, finally_label_pc);
|
||||
if let Some(fb_pos) = try_finally_begin_pos {
|
||||
let offset = finally_label_pc as isize - (fb_pos as isize);
|
||||
let offset = ctx.checked_jump_offset(offset);
|
||||
@@ -118,7 +118,7 @@ impl Compiler {
|
||||
}
|
||||
|
||||
let try_end_pc = ctx.bytecode.len();
|
||||
ctx.label_map.insert(try_end_label, try_end_pc);
|
||||
ctx.labels.label_map.insert(try_end_label, try_end_pc);
|
||||
|
||||
Ok(Some(result_reg))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
pub mod compiler;
|
||||
pub mod counter;
|
||||
pub mod emit_ctx;
|
||||
pub mod emitter;
|
||||
pub mod hash;
|
||||
pub mod symbol_table;
|
||||
|
||||
Reference in New Issue
Block a user