mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
fix: 被捕获词法绑定入口建 TDZ 占位 cell 并跳过陈旧静态守卫
This commit is contained in:
@@ -16,6 +16,7 @@ mod upvalues;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub(crate) use captured::collect_captured_bindings;
|
||||
pub(crate) use names::collect_direct_lexical_names;
|
||||
pub(crate) use names::collect_own_binding_names;
|
||||
pub(crate) use names::collect_top_level_function_names;
|
||||
pub(crate) use names::collect_top_level_function_names_ordered;
|
||||
|
||||
@@ -124,6 +124,63 @@ pub(crate) fn collect_top_level_function_names_ordered(stmts: &[Statement]) -> V
|
||||
names
|
||||
}
|
||||
|
||||
/// 收集语句列表**直接子级**词法声明名(let/const,含解构 pattern 叶):
|
||||
/// 入口 TDZ 占位 cell 名集(被捕获的词法绑定在入口未初始化)。
|
||||
///
|
||||
/// 遍历面与 `predeclare_lexical_declarations` 一致——只走直接子级,不递归块/
|
||||
/// if/for/switch case/try 区(块级名不提升到函数/程序作用域,不建入口
|
||||
/// cell);for 头声明是循环作用域绑定,不纳;var 声明不纳(var 入口实例化
|
||||
/// 独立覆盖);import 绑定由模块导入初始化的发射点建 cell,不纳。
|
||||
pub(crate) fn collect_direct_lexical_names(stmts: &[Statement]) -> HashSet<String> {
|
||||
let mut names = HashSet::new();
|
||||
for stmt in stmts {
|
||||
match stmt {
|
||||
Statement::VariableDeclaration(decl) => {
|
||||
if matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||
continue;
|
||||
}
|
||||
for d in &decl.declarations {
|
||||
collect_binding_pattern_names(&d.id, &mut names);
|
||||
}
|
||||
}
|
||||
Statement::ClassDeclaration(cd) => {
|
||||
if let Some(id) = &cd.id {
|
||||
names.insert(id.name.to_string());
|
||||
}
|
||||
}
|
||||
Statement::ExportNamedDeclaration(exp) => {
|
||||
if let Some(decl) = &exp.declaration {
|
||||
match decl {
|
||||
oxide_parser::Declaration::VariableDeclaration(vd) => {
|
||||
if matches!(vd.kind, VariableDeclarationKind::Var) {
|
||||
continue;
|
||||
}
|
||||
for d in &vd.declarations {
|
||||
collect_binding_pattern_names(&d.id, &mut names);
|
||||
}
|
||||
}
|
||||
oxide_parser::Declaration::ClassDeclaration(cd) => {
|
||||
if let Some(id) = &cd.id {
|
||||
names.insert(id.name.to_string());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::ExportDefaultDeclaration(exp) => {
|
||||
if let oxide_parser::ExportDefaultDeclarationKind::ClassDeclaration(cd) = &exp.declaration {
|
||||
if let Some(id) = &cd.id {
|
||||
names.insert(id.name.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
names
|
||||
}
|
||||
|
||||
/// 收集当前函数作用域声明的绑定名(参数 + 变量/函数声明,含嵌套 block,不含嵌套函数体)。
|
||||
pub(crate) fn collect_own_binding_names(param_names: &[&str], stmts: &[Statement]) -> HashSet<String> {
|
||||
let mut names = HashSet::new();
|
||||
|
||||
@@ -47,9 +47,20 @@ impl Emitter {
|
||||
/// 赋值目标 TDZ 检查:未初始化绑定在赋值引用解析时抛 ReferenceError。
|
||||
/// 须在 RHS 求值之前调用(规范:赋值 LHS 的 ResolveBinding 先于 RHS 副作用)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 被捕获名(upvalue)跳过:其 TDZ 由运行时 cell 初始化标志判定,编译期
|
||||
/// 快照对声明序晚于函数编译的绑定是陈旧的。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - TDZ 命中时发射 THROW 指令序列,其后指令不可达但保持寄存器良定义。
|
||||
pub(crate) fn emit_identifier_tdz_guard(&self, name: &str, ctx: &mut CompileCtx) -> Result<(), String> {
|
||||
// 被捕获绑定经运行时 cell 的初始化标志统一判定(cell 读写与 upvalue
|
||||
// 读写同面):嵌套函数 ctx 继承的父作用域 initialized 快照是陈旧的
|
||||
// (函数声明编译早于 let/const 声明的初始化点),静态 throw 会误报,
|
||||
// 命中捕获名即跳过,真 TDZ 由运行时抛。
|
||||
if ctx.current_upvalue_captures.iter().any(|u| u.name == name) {
|
||||
return Ok(());
|
||||
}
|
||||
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)?;
|
||||
|
||||
@@ -14,8 +14,8 @@ use oxide_ir::IRFunction;
|
||||
use oxide_parser::{Expression, Statement, VariableDeclarationKind};
|
||||
|
||||
use crate::capture::{
|
||||
collect_binding_pattern_names, collect_captured_bindings, collect_own_binding_names, collect_upvalue_names,
|
||||
collect_var_binding_names,
|
||||
collect_binding_pattern_names, collect_captured_bindings, collect_direct_lexical_names, collect_own_binding_names,
|
||||
collect_upvalue_names, collect_var_binding_names,
|
||||
};
|
||||
use crate::compile_ctx::{CompileCtx, FieldBuffer};
|
||||
use crate::symbol_table::{Binding, ScopeKind};
|
||||
@@ -783,6 +783,33 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
// 被捕获函数作用域词法(let/const/class)入口 TDZ 占位 cell:函数声明
|
||||
// hoisting 先于声明语句执行时,闭包指向此未初始化 cell,声明前读写经
|
||||
// 运行时抛真 TDZ;声明语句的 MAKE_CELL 按占位更新语义原位翻转。名集只取
|
||||
// 直接子级(块级名不提升到函数作用域,与块内重执行 cell 族零交集),
|
||||
// 排序保证发射序跨进程稳定。
|
||||
let mut lex_names: Vec<String> = collect_direct_lexical_names(body_stmts)
|
||||
.into_iter()
|
||||
.filter(|n| !param_names.contains(&n.as_str()) && n != "arguments")
|
||||
.filter(|n| ctx.captured_bindings.contains_key(n))
|
||||
.collect();
|
||||
lex_names.sort();
|
||||
if !lex_names.is_empty() {
|
||||
let undef_reg = self.emit_undefined(ctx);
|
||||
for name in lex_names {
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(&name) {
|
||||
// 未初始化标志折入 16 位立即数高字节(0x0100),dispatch 侧
|
||||
// 按字节拆回两字段。
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(undef_reg),
|
||||
Operand::Imm(cell_idx as u16 | 0x0100),
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(param_base)
|
||||
}
|
||||
|
||||
|
||||
@@ -725,6 +725,31 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 被捕获模块词法(let/const/class)入口 TDZ 占位 cell:模块词法导出
|
||||
// 既有预注册保持未初始化 TDZ(命名空间对象面,按导出名),入口 cell
|
||||
// 只覆盖被嵌套函数捕获的名(按捕获名),两面名集独立。声明语句的
|
||||
// MAKE_CELL 按占位更新语义原位翻转;直接子级 only,块级名不提升。
|
||||
let mut lex_names: Vec<String> = crate::capture::collect_direct_lexical_names(body)
|
||||
.into_iter()
|
||||
.filter(|n| !ctx.param_names.contains(n.as_str()))
|
||||
.filter(|n| ctx.captured_bindings.contains_key(n))
|
||||
.collect();
|
||||
lex_names.sort();
|
||||
if !lex_names.is_empty() {
|
||||
let undef_reg = self.emit_undefined(ctx);
|
||||
for name in lex_names {
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(&name) {
|
||||
// 未初始化标志折入 16 位立即数高字节(0x0100),dispatch 侧
|
||||
// 按字节拆回两字段。
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(undef_reg),
|
||||
Operand::Imm(cell_idx as u16 | 0x0100),
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// —— re-export(export { x } from 'mod')链接检查:实例化期解析,须先于
|
||||
|
||||
@@ -17,8 +17,8 @@ use oxide_ir::IRFunction;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
use crate::capture::{
|
||||
collect_captured_bindings, collect_own_binding_names, collect_top_level_function_names,
|
||||
collect_top_level_function_names_ordered, collect_var_binding_names,
|
||||
collect_captured_bindings, collect_direct_lexical_names, collect_own_binding_names,
|
||||
collect_top_level_function_names, collect_top_level_function_names_ordered, collect_var_binding_names,
|
||||
};
|
||||
use crate::compile_ctx::CompileCtx;
|
||||
use crate::Emitter;
|
||||
@@ -377,6 +377,31 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
// 被捕获顶层词法(let/const/class)入口 TDZ 占位 cell:声明语句执行前
|
||||
// 创建的闭包指向此未初始化 cell,声明前读写经运行时抛真 TDZ;声明语句
|
||||
// 的 MAKE_CELL 按占位更新语义原位翻转为已初始化。名集取捕获集交集
|
||||
// (直接子级词法名,块级名不提升到顶层),排序保证发射序跨进程稳定。
|
||||
let mut lex_names: Vec<String> = collect_direct_lexical_names(&program.body)
|
||||
.into_iter()
|
||||
.filter(|n| ctx.captured_bindings.contains_key(n))
|
||||
.collect();
|
||||
lex_names.sort();
|
||||
if !lex_names.is_empty() {
|
||||
let undef_reg = self.emit_undefined(&mut ctx);
|
||||
for name in lex_names {
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(&name) {
|
||||
// 未初始化标志折入 16 位立即数高字节(0x0100),dispatch 侧
|
||||
// 按字节拆回两字段。
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(undef_reg),
|
||||
Operand::Imm(cell_idx as u16 | 0x0100),
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 全局声明实例化序言:脚本求值前为顶层 var 名创建全局对象属性(值 undefined),
|
||||
// 使声明语句执行前的读取(typeof、反射、自引用)可经全局对象见绑定。
|
||||
// 顶层函数声明名不进序言:函数值是编译闭包,编译期不可得,其全局对象属性值
|
||||
|
||||
Reference in New Issue
Block a user