fix: var 捕获绑定函数与程序入口统一初始化为 undefined 消除 TDZ 误报
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, HashSet};
|
||||||
|
|
||||||
use oxide_bytecode::module::UpvalueCapture;
|
use oxide_bytecode::module::UpvalueCapture;
|
||||||
use oxide_parser::{ClassBody, ClassElement, Expression, Statement};
|
use oxide_parser::{ClassBody, ClassElement, Expression, Statement, VariableDeclarationKind};
|
||||||
|
|
||||||
use crate::Emitter;
|
use crate::Emitter;
|
||||||
|
|
||||||
@@ -63,6 +63,85 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 收集语句树中全部 `var` 声明名(var 提升到函数/程序作用域,递归进嵌套块)。
|
||||||
|
/// 供函数/程序入口批量 MAKE_CELL(undefined) 使用(var 入口实例化语义)。
|
||||||
|
pub(crate) fn collect_var_binding_names(&self, stmts: &[Statement]) -> HashSet<String> {
|
||||||
|
let mut names = HashSet::new();
|
||||||
|
self.collect_var_names_stmt(stmts, &mut names);
|
||||||
|
names
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_var_names_stmt(&self, stmts: &[Statement], out: &mut HashSet<String>) {
|
||||||
|
for stmt in stmts {
|
||||||
|
match stmt {
|
||||||
|
Statement::VariableDeclaration(vd) => {
|
||||||
|
if matches!(vd.kind, VariableDeclarationKind::Var) {
|
||||||
|
for d in &vd.declarations {
|
||||||
|
self.collect_binding_pattern_names(&d.id, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::BlockStatement(b) => self.collect_var_names_stmt(&b.body, out),
|
||||||
|
Statement::IfStatement(is) => {
|
||||||
|
self.collect_var_names_stmt(std::slice::from_ref(&is.consequent), out);
|
||||||
|
if let Some(alt) = &is.alternate {
|
||||||
|
self.collect_var_names_stmt(std::slice::from_ref(alt), out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::WhileStatement(w) => self.collect_var_names_stmt(std::slice::from_ref(&w.body), out),
|
||||||
|
Statement::DoWhileStatement(d) => self.collect_var_names_stmt(std::slice::from_ref(&d.body), out),
|
||||||
|
Statement::ForStatement(f) => {
|
||||||
|
if let Some(oxide_parser::ForStatementInit::VariableDeclaration(vd)) = &f.init {
|
||||||
|
if matches!(vd.kind, VariableDeclarationKind::Var) {
|
||||||
|
for d in &vd.declarations {
|
||||||
|
self.collect_binding_pattern_names(&d.id, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.collect_var_names_stmt(std::slice::from_ref(&f.body), out);
|
||||||
|
}
|
||||||
|
Statement::ForInStatement(fi) => {
|
||||||
|
if let oxide_parser::ForStatementLeft::VariableDeclaration(_) = &fi.left {
|
||||||
|
self.collect_for_left_decl_names(&fi.left, out);
|
||||||
|
}
|
||||||
|
self.collect_var_names_stmt(std::slice::from_ref(&fi.body), out);
|
||||||
|
}
|
||||||
|
Statement::ForOfStatement(fo) => {
|
||||||
|
if let oxide_parser::ForStatementLeft::VariableDeclaration(_) = &fo.left {
|
||||||
|
self.collect_for_left_decl_names(&fo.left, out);
|
||||||
|
}
|
||||||
|
self.collect_var_names_stmt(std::slice::from_ref(&fo.body), out);
|
||||||
|
}
|
||||||
|
Statement::SwitchStatement(sw) => {
|
||||||
|
for case in &sw.cases {
|
||||||
|
self.collect_var_names_stmt(&case.consequent, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::TryStatement(ts) => {
|
||||||
|
self.collect_var_names_stmt(&ts.block.body, out);
|
||||||
|
if let Some(h) = &ts.handler {
|
||||||
|
self.collect_var_names_stmt(&h.body.body, out);
|
||||||
|
}
|
||||||
|
if let Some(f) = &ts.finalizer {
|
||||||
|
self.collect_var_names_stmt(&f.body, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::LabeledStatement(ls) => self.collect_var_names_stmt(std::slice::from_ref(&ls.body), out),
|
||||||
|
Statement::WithStatement(ws) => self.collect_var_names_stmt(std::slice::from_ref(&ws.body), out),
|
||||||
|
Statement::ExportNamedDeclaration(exp) => {
|
||||||
|
if let Some(oxide_parser::Declaration::VariableDeclaration(vd)) = &exp.declaration {
|
||||||
|
if matches!(vd.kind, VariableDeclarationKind::Var) {
|
||||||
|
for d in &vd.declarations {
|
||||||
|
self.collect_binding_pattern_names(&d.id, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 收集当前函数作用域声明的绑定名(参数 + 变量/函数声明,含嵌套 block,不含嵌套函数体)。
|
/// 收集当前函数作用域声明的绑定名(参数 + 变量/函数声明,含嵌套 block,不含嵌套函数体)。
|
||||||
pub(crate) fn collect_own_binding_names(&self, param_names: &[&str], stmts: &[Statement]) -> HashSet<String> {
|
pub(crate) fn collect_own_binding_names(&self, param_names: &[&str], stmts: &[Statement]) -> HashSet<String> {
|
||||||
let mut names = HashSet::new();
|
let mut names = HashSet::new();
|
||||||
|
|||||||
@@ -1329,6 +1329,31 @@ impl Emitter {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// var 绑定函数入口实例化:被捕获的 var 名统一 MAKE_CELL(undefined)。
|
||||||
|
// 规范上 var 在函数入口即初始化为 undefined(HoistDeclaration),声明语句
|
||||||
|
// 只是赋值;否则声明语句前创建的闭包读取占位 cell → TDZ 误报。参数与
|
||||||
|
// arguments 已在上方初始化(跳过以免覆盖参数值);let/const/class 保持
|
||||||
|
// TDZ 语义不动。声明语句的 MAKE_CELL 按占位更新语义覆盖此初值。
|
||||||
|
let var_names: Vec<String> = self
|
||||||
|
.collect_var_binding_names(body_stmts)
|
||||||
|
.into_iter()
|
||||||
|
.filter(|n| !param_names.contains(&n.as_str()) && n != "arguments")
|
||||||
|
.filter(|n| ctx.captured_bindings.contains_key(n))
|
||||||
|
.collect();
|
||||||
|
if !var_names.is_empty() {
|
||||||
|
let undef_reg = self.emit_undefined(ctx);
|
||||||
|
for name in var_names {
|
||||||
|
if let Some(&cell_idx) = ctx.captured_bindings.get(&name) {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::MAKE_CELL,
|
||||||
|
Operand::Reg(undef_reg),
|
||||||
|
Operand::Imm(cell_idx as u16),
|
||||||
|
Operand::None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(param_base)
|
Ok(param_base)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1459,6 +1484,28 @@ impl Emitter {
|
|||||||
ctx.own_bindings = self.collect_own_binding_names(&[], &program.body);
|
ctx.own_bindings = self.collect_own_binding_names(&[], &program.body);
|
||||||
ctx.captured_bindings = self.collect_captured_bindings(&program.body, &[], &ctx.own_bindings);
|
ctx.captured_bindings = self.collect_captured_bindings(&program.body, &[], &ctx.own_bindings);
|
||||||
|
|
||||||
|
// 顶层 var 入口实例化:被捕获的 var 名统一 MAKE_CELL(undefined),使 var
|
||||||
|
// 声明语句执行前创建的闭包读取到 undefined(脚本 GlobalDeclarationInstantiation
|
||||||
|
// 语义),而非占位 cell 的 TDZ 误报。声明语句的 MAKE_CELL 覆盖此初值。
|
||||||
|
let var_names: Vec<String> = self
|
||||||
|
.collect_var_binding_names(&program.body)
|
||||||
|
.into_iter()
|
||||||
|
.filter(|n| ctx.captured_bindings.contains_key(n))
|
||||||
|
.collect();
|
||||||
|
if !var_names.is_empty() {
|
||||||
|
let undef_reg = self.emit_undefined(&mut ctx);
|
||||||
|
for name in var_names {
|
||||||
|
if let Some(&cell_idx) = ctx.captured_bindings.get(&name) {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::MAKE_CELL,
|
||||||
|
Operand::Reg(undef_reg),
|
||||||
|
Operand::Imm(cell_idx as u16),
|
||||||
|
Operand::None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 首个 sub-pass:发函数声明(hoisting),保证任何代码运行前函数对象已就绪。
|
// 首个 sub-pass:发函数声明(hoisting),保证任何代码运行前函数对象已就绪。
|
||||||
for stmt in &program.body {
|
for stmt in &program.body {
|
||||||
if matches!(stmt, Statement::FunctionDeclaration(_)) {
|
if matches!(stmt, Statement::FunctionDeclaration(_)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user