refactor: 错误守卫四函数迁出 emit.rs 独立成 errors 模块

This commit is contained in:
2026-09-15 21:19:29 +00:00
parent dc6d825cae
commit 5efc64dc71
3 changed files with 70 additions and 57 deletions
-57
View File
@@ -199,63 +199,6 @@ impl Emitter {
self
}
/// 生成运行时抛 `kind` 类型错误的指令序列,返回一个未定义 dummy 寄存器
/// 保证 THROW 后不可达控制流的寄存器良定义。
///
/// # 步骤
/// 1. 取全局错误构造器并 LOAD。
/// 2. 加载错误消息常量,`new {kind}(msg)` 构造错误对象。
/// 3. THROW 抛出;尾接 dummy 值保持后续读引用有确定寄存器。
///
/// # 边界与前提
/// - `kind` 必须是已注册的全局构造器名(如 "ReferenceError"/"TypeError")。
pub(crate) fn emit_throw_error(&self, kind: &str, msg: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
let ctor_reg = ctx.lookup_or_builtin(kind)?;
let ctor = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(ctor), Operand::Reg(ctor_reg), Operand::None));
let msg_reg = ctx.alloc_reg();
let msg_idx = ctx.add_constant(Constant::String(msg.to_string()));
ctx.inst(Inst::load_const(Operand::Reg(msg_reg), msg_idx));
let exc_reg = ctx.alloc_reg();
ctx.inst(Inst::new_expression(Operand::Reg(exc_reg), Operand::Reg(ctor), Operand::Reg(msg_reg), 1));
ctx.inst(Inst::new(OpCode::THROW, Operand::Reg(exc_reg), Operand::None, Operand::None));
let dummy = ctx.alloc_reg();
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.inst(Inst::load_const(Operand::Reg(dummy), undef_idx));
Ok(dummy)
}
/// 生成运行时抛 ReferenceError 的指令序列(TDZ 访问专用,语义见 [`emit_throw_error`])。
pub(crate) fn emit_tdz_throw(&self, msg: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
self.emit_throw_error("ReferenceError", msg, ctx)
}
/// 赋值目标 TDZ 检查:未初始化绑定在赋值引用解析时抛 ReferenceError。
/// 须在 RHS 求值之前调用(规范:赋值 LHS 的 ResolveBinding 先于 RHS 副作用)。
///
/// # 副作用
/// - TDZ 命中时发射 THROW 指令序列,其后指令不可达但保持寄存器良定义。
pub(crate) fn emit_identifier_tdz_guard(&self, name: &str, ctx: &mut CompileCtx) -> Result<(), String> {
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
if !binding.initialized {
let _ = self.emit_tdz_throw(&format!("Cannot access '{name}' before initialization"), ctx)?;
}
}
Ok(())
}
/// const 写检查:已初始化的 const 绑定再赋值编译期抛 TypeError(与槽值无关)。
/// 简单赋值在 RHS 求值之后、复合/更新在读旧值之前调用;解构赋值在写目标时调用。
///
/// # 副作用
/// - const 命中时发射 THROW 指令序列,其后写指令不可达但保持寄存器良定义。
pub(crate) fn emit_const_write_guard(&self, name: &str, ctx: &mut CompileCtx) -> Result<(), String> {
if ctx.lookup_const_flag(name) {
let _ = self.emit_throw_error("TypeError", "Assignment to constant variable", ctx)?;
}
Ok(())
}
/// 遍历解构 pattern 收集内嵌默认值表达式(AssignmentPattern.right)与
/// 计算键表达式(`{[k]: a}` 的键,运行时求值)——二者都会在参数绑定期被
/// 内部闭包引用,须一并纳入捕获分析。
+69
View File
@@ -0,0 +1,69 @@
//! 错误构造/传播四守卫:运行时抛错指令序列与 TDZ / const 写检查。
//!
//! 均为 `Emitter` 方法,与各语法域 emit 族共用 `CompileCtx` 编译上下文。
use crate::compile_ctx::CompileCtx;
use crate::Emitter;
use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
impl Emitter {
/// 生成运行时抛 `kind` 类型错误的指令序列,返回一个未定义 dummy 寄存器
/// 保证 THROW 后不可达控制流的寄存器良定义。
///
/// # 步骤
/// 1. 取全局错误构造器并 LOAD。
/// 2. 加载错误消息常量,`new {kind}(msg)` 构造错误对象。
/// 3. THROW 抛出;尾接 dummy 值保持后续读引用有确定寄存器。
///
/// # 边界与前提
/// - `kind` 必须是已注册的全局构造器名(如 "ReferenceError"/"TypeError")。
pub(crate) fn emit_throw_error(&self, kind: &str, msg: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
let ctor_reg = ctx.lookup_or_builtin(kind)?;
let ctor = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(ctor), Operand::Reg(ctor_reg), Operand::None));
let msg_reg = ctx.alloc_reg();
let msg_idx = ctx.add_constant(Constant::String(msg.to_string()));
ctx.inst(Inst::load_const(Operand::Reg(msg_reg), msg_idx));
let exc_reg = ctx.alloc_reg();
ctx.inst(Inst::new_expression(Operand::Reg(exc_reg), Operand::Reg(ctor), Operand::Reg(msg_reg), 1));
ctx.inst(Inst::new(OpCode::THROW, Operand::Reg(exc_reg), Operand::None, Operand::None));
let dummy = ctx.alloc_reg();
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.inst(Inst::load_const(Operand::Reg(dummy), undef_idx));
Ok(dummy)
}
/// 生成运行时抛 ReferenceError 的指令序列(TDZ 访问专用,语义见 [`emit_throw_error`])。
pub(crate) fn emit_tdz_throw(&self, msg: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
self.emit_throw_error("ReferenceError", msg, ctx)
}
/// 赋值目标 TDZ 检查:未初始化绑定在赋值引用解析时抛 ReferenceError。
/// 须在 RHS 求值之前调用(规范:赋值 LHS 的 ResolveBinding 先于 RHS 副作用)。
///
/// # 副作用
/// - TDZ 命中时发射 THROW 指令序列,其后指令不可达但保持寄存器良定义。
pub(crate) fn emit_identifier_tdz_guard(&self, name: &str, ctx: &mut CompileCtx) -> Result<(), String> {
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
if !binding.initialized {
let _ = self.emit_tdz_throw(&format!("Cannot access '{name}' before initialization"), ctx)?;
}
}
Ok(())
}
/// const 写检查:已初始化的 const 绑定再赋值编译期抛 TypeError(与槽值无关)。
/// 简单赋值在 RHS 求值之后、复合/更新在读旧值之前调用;解构赋值在写目标时调用。
///
/// # 副作用
/// - const 命中时发射 THROW 指令序列,其后写指令不可达但保持寄存器良定义。
pub(crate) fn emit_const_write_guard(&self, name: &str, ctx: &mut CompileCtx) -> Result<(), String> {
if ctx.lookup_const_flag(name) {
let _ = self.emit_throw_error("TypeError", "Assignment to constant variable", ctx)?;
}
Ok(())
}
}
+1
View File
@@ -11,6 +11,7 @@ mod compile_ctx;
pub mod emit;
pub mod emit_ctx;
mod emit_log;
mod errors;
pub mod expr;
pub mod module;
pub mod prepass;