fix: 块级 let/const/class 预声明 + TDZ 读取运行时抛 ReferenceError

This commit is contained in:
爱特每羊队
2026-08-10 21:07:39 +08:00
parent 717e927057
commit 7055ef1fbb
9 changed files with 237 additions and 8 deletions
+1
View File
@@ -9,6 +9,7 @@ impl Emitter {
return Ok(None);
};
ctx.push_scope();
self.predeclare_lexical_declarations(&block.body, ctx);
let mut r = None;
for s in &block.body {
if let Some(rr) = self.emit_statement(s, ctx)? {
@@ -18,8 +18,14 @@ impl Emitter {
.as_ref()
.map(|id| id.name.to_string())
.ok_or_else(|| "ClassDeclaration without name".to_string())?;
let var_reg = ctx.alloc_reg();
ctx.declare(&name, var_reg, VariableDeclarationKind::Let, false)?;
let var_reg = match ctx.scopes.symbols.consume_predeclared_slot(&name) {
Some(reg) => reg,
None => {
let reg = ctx.alloc_reg();
ctx.declare(&name, reg, VariableDeclarationKind::Let, false)?;
reg
}
};
ctx.init_var(&name);
let ctor_reg = self.emit_class(class, ctx)?;
if let Some(&cell_idx) = ctx.captured_bindings.get(&name) {
+10 -1
View File
@@ -47,7 +47,16 @@ impl Emitter {
Err(_) => ctx.lookup(bi.name.as_str()).unwrap_or(var_reg),
}
} else {
ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const)?;
// let/const 无初始化器:复用本块预声明槽位,否则新声明
// (for 头等未预声明路径)。
let var_reg =
if let Some(reg) = ctx.scopes.symbols.consume_predeclared_slot(bi.name.as_str()) {
reg
} else {
let r = ctx.alloc_reg();
ctx.declare(bi.name.as_str(), r, decl.kind, is_const)?;
r
};
var_reg
};
if let Some(&cell_idx) = ctx.captured_bindings.get(bi.name.as_str()) {
@@ -29,11 +29,16 @@ impl Emitter {
ctx.push_open_catch_handler();
}
let mut last_try_result: Option<u32> = None;
// try block 是独立块作用域:lexical 声明(let/const/class)限定于 try 内,
// 与外层同名绑定互不干扰;块级预声明使声明点前读取编译为 TDZ 抛错。
ctx.push_scope();
self.predeclare_lexical_declarations(&ts.block.body, ctx);
for s in &ts.block.body {
if let Some(r) = self.emit_statement(s, ctx)? {
last_try_result = Some(r);
}
}
ctx.pop_scope();
ctx.inst(Inst::new(
OpCode::LOAD_VAR,
Operand::Reg(result_reg),
@@ -58,6 +63,7 @@ impl Emitter {
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(src_reg), Operand::None, Operand::None));
self.emit_binding_pattern(&param.pattern, src_reg, VariableDeclarationKind::Let, false, false, ctx)?;
}
self.predeclare_lexical_declarations(&catch.body.body, ctx);
let mut last_catch_result: Option<u32> = None;
for s in &catch.body.body {
if let Some(r) = self.emit_statement(s, ctx)? {
@@ -75,11 +81,15 @@ impl Emitter {
if has_finally {
ctx.labels.set_label_pos(finally_label, ctx.insts.len());
let mut last_finally_result: Option<u32> = None;
// finally block 同为独立块作用域,lexical 声明互不泄漏。
ctx.push_scope();
self.predeclare_lexical_declarations(&ts.finalizer.as_ref().unwrap().body, ctx);
for s in &ts.finalizer.as_ref().unwrap().body {
if let Some(r) = self.emit_statement(s, ctx)? {
last_finally_result = Some(r);
}
}
ctx.pop_scope();
ctx.inst(Inst::new(
OpCode::LOAD_VAR,
Operand::Reg(result_reg),