diff --git a/Cargo.lock b/Cargo.lock index e5cab49f..60e893d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.16.5" +version = "0.16.6" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.16.5" +version = "0.16.6" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.16.5" +version = "0.16.6" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.16.5" +version = "0.16.6" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.16.5" +version = "0.16.6" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.16.5" +version = "0.16.6" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1257,7 +1257,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.16.5" +version = "0.16.6" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.16.5" +version = "0.16.6" dependencies = [ "num-bigint", "oxc_allocator", @@ -1286,7 +1286,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.16.5" +version = "0.16.6" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1298,7 +1298,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.16.5" +version = "0.16.6" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1307,7 +1307,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.16.5" +version = "0.16.6" dependencies = [ "bumpalo", "dashmap", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.16.5" +version = "0.16.6" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.16.5" +version = "0.16.6" dependencies = [ "tracing", "tracing-appender", @@ -1342,11 +1342,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.16.5" +version = "0.16.6" [[package]] name = "oxide_parser" -version = "0.16.5" +version = "0.16.6" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1361,7 +1361,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.16.5" +version = "0.16.6" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1377,7 +1377,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.16.5" +version = "0.16.6" dependencies = [ "num-bigint", "num-traits", @@ -1390,7 +1390,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.16.5" +version = "0.16.6" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1410,7 +1410,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.16.5" +version = "0.16.6" dependencies = [ "bumpalo", "dashmap", @@ -1423,7 +1423,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.16.5" +version = "0.16.6" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 603da1dc..ef875976 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ members = [ ] [workspace.package] -version = "0.16.5" +version = "0.16.6" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_emit/src/capture/mod.rs b/crates/oxide_emit/src/capture/mod.rs index b2d3684f..595d337b 100644 --- a/crates/oxide_emit/src/capture/mod.rs +++ b/crates/oxide_emit/src/capture/mod.rs @@ -18,13 +18,7 @@ use std::collections::HashSet; pub(crate) use captured::collect_captured_bindings; pub(crate) use guard::has_nested_function; -pub(crate) use names::collect_block_level_decl_names; -pub(crate) use names::collect_direct_lexical_names; pub(crate) use names::collect_fn_scope_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; -pub(crate) use names::collect_var_binding_names; pub(crate) use names::FnScopeNames; pub(crate) use upvalues::collect_upvalue_names; diff --git a/crates/oxide_emit/src/capture/names.rs b/crates/oxide_emit/src/capture/names.rs index b62a6bbb..53ec022b 100644 --- a/crates/oxide_emit/src/capture/names.rs +++ b/crates/oxide_emit/src/capture/names.rs @@ -1,6 +1,8 @@ -//! 闭包捕获分析,own/var/顶层声明名收集:本函数 own_bindings 名集 -//! (参数名 + 变量/函数/import/export 声明,含嵌套块)、var 提升入口 -//! 名集、顶层函数声明名集(无序集 + 声明序列表)。 +//! 闭包捕获分析,函数作用域名收集:单趟收集八个名字面(var 提升名 / own 绑定名 / +//! 块级声明名 / 直接子词法名 / 顶层函数名集 / 顶层函数声明序 / 块级函数名 / +//! 块函数抑制集),供各入口(程序/模块/函数体)的 GDI 检查、builtin 排除、 +//! 入口实例化与块函数 web-compat 各调用点共用;另保留捕获扫描器内部依赖的 +//! own 绑定名集收集(`collect_own_binding_names`)。 use std::collections::HashSet; @@ -8,289 +10,6 @@ use oxide_parser::{Statement, VariableDeclarationKind}; use super::{collect_binding_pattern_names, collect_for_left_decl_names}; -/// 收集语句树中全部 `var` 声明名(var 提升到函数/程序作用域,递归进嵌套块)。 -/// 供函数/程序入口批量 MAKE_CELL(undefined) 使用(var 入口实例化语义)。 -pub(crate) fn collect_var_binding_names(stmts: &[Statement]) -> HashSet { - let mut names = HashSet::new(); - collect_var_names_stmt(stmts, &mut names); - names -} - -/// 递归遍历语句树收集 `var` 声明名(函数/程序级提升目标),含嵌套块与控制流各分支。 -pub(crate) fn collect_var_names_stmt(stmts: &[Statement], out: &mut HashSet) { - for stmt in stmts { - match stmt { - Statement::VariableDeclaration(vd) => { - if matches!(vd.kind, VariableDeclarationKind::Var) { - for d in &vd.declarations { - collect_binding_pattern_names(&d.id, out); - } - } - } - Statement::BlockStatement(b) => collect_var_names_stmt(&b.body, out), - Statement::IfStatement(is) => { - collect_var_names_stmt(std::slice::from_ref(&is.consequent), out); - if let Some(alt) = &is.alternate { - collect_var_names_stmt(std::slice::from_ref(alt), out); - } - } - Statement::WhileStatement(w) => collect_var_names_stmt(std::slice::from_ref(&w.body), out), - Statement::DoWhileStatement(d) => 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 { - collect_binding_pattern_names(&d.id, out); - } - } - } - collect_var_names_stmt(std::slice::from_ref(&f.body), out); - } - Statement::ForInStatement(fi) => { - if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left { - // 仅 var 头名提升到函数作用域,let/const 头名是迭代级绑定。 - if matches!(vd.kind, VariableDeclarationKind::Var) { - collect_for_left_decl_names(&fi.left, out); - } - } - collect_var_names_stmt(std::slice::from_ref(&fi.body), out); - } - Statement::ForOfStatement(fo) => { - if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left { - // 仅 var 头名提升到函数作用域,let/const 头名是迭代级绑定。 - if matches!(vd.kind, VariableDeclarationKind::Var) { - collect_for_left_decl_names(&fo.left, out); - } - } - collect_var_names_stmt(std::slice::from_ref(&fo.body), out); - } - Statement::SwitchStatement(sw) => { - for case in &sw.cases { - collect_var_names_stmt(&case.consequent, out); - } - } - Statement::TryStatement(ts) => { - collect_var_names_stmt(&ts.block.body, out); - if let Some(h) = &ts.handler { - collect_var_names_stmt(&h.body.body, out); - } - if let Some(f) = &ts.finalizer { - collect_var_names_stmt(&f.body, out); - } - } - Statement::LabeledStatement(ls) => collect_var_names_stmt(std::slice::from_ref(&ls.body), out), - Statement::WithStatement(ws) => 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 { - collect_binding_pattern_names(&d.id, out); - } - } - } - } - _ => {} - } - } -} - -/// 收集语句列表**直接子级**函数声明的绑定名(顶层函数声明是全局 var 绑定, -/// 裸读/裸写与 var 名同路由全局对象属性)。不递归进块:块内函数声明是块 -/// 作用域,不参与全局对象同步;`export default function` 是 lexical 绑定,不纳。 -pub(crate) fn collect_top_level_function_names(stmts: &[Statement]) -> HashSet { - let mut names = HashSet::new(); - for stmt in stmts { - if let Statement::FunctionDeclaration(fd) = stmt { - if let Some(id) = &fd.id { - names.insert(id.name.to_string()); - } - } - } - names -} - -/// 同 [`collect_top_level_function_names`] 的收集面,但按**声明序**返回 Vec -/// (供 GlobalDeclarationInstantiation(脚本顶层声明实例化)检查阶段逆序遍历逐名 -/// 去重,规范"逆序首见 = 源序最后声明";重名只查一次,取源序最后一次声明)。 -pub(crate) fn collect_top_level_function_names_ordered(stmts: &[Statement]) -> Vec { - let mut names = Vec::new(); - for stmt in stmts { - if let Statement::FunctionDeclaration(fd) = stmt { - if let Some(id) = &fd.id { - names.push(id.name.to_string()); - } - } - } - 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 { - 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 -} - -/// 收集块级声明名(块内 let/const/class、catch 参数、for 头 let/const): -/// 这些名字是块作用域绑定,遮蔽同名顶层 tier 名时,tier 名须保留在捕获集, -/// 块级绑定才能追加新索引供闭包捕获。顶层直接子级 let/const/class 是顶层 -/// 词法绑定,不纳;var 声明不纳(var 是函数/全局作用域)。 -pub(crate) fn collect_block_level_decl_names(stmts: &[Statement]) -> HashSet { - let mut names = HashSet::new(); - collect_block_level_names_stmt(stmts, &mut names, true); - names -} - -/// 递归遍历语句树收集块级声明名。`top` 标记当前层是否为顶层(顶层直接子级 -/// let/const/class 是顶层词法绑定,非块级)。 -fn collect_block_level_names_stmt(stmts: &[Statement], out: &mut HashSet, top: bool) { - for stmt in stmts { - match stmt { - Statement::VariableDeclaration(vd) => { - if matches!(vd.kind, VariableDeclarationKind::Var) || top { - continue; - } - for d in &vd.declarations { - collect_binding_pattern_names(&d.id, out); - } - } - Statement::ClassDeclaration(cd) => { - if !top { - if let Some(id) = &cd.id { - out.insert(id.name.to_string()); - } - } - } - Statement::BlockStatement(b) => collect_block_level_names_stmt(&b.body, out, false), - Statement::IfStatement(is) => { - collect_block_level_names_stmt(std::slice::from_ref(&is.consequent), out, false); - if let Some(alt) = &is.alternate { - collect_block_level_names_stmt(std::slice::from_ref(alt), out, false); - } - } - Statement::WhileStatement(w) => collect_block_level_names_stmt(std::slice::from_ref(&w.body), out, false), - Statement::DoWhileStatement(d) => collect_block_level_names_stmt(std::slice::from_ref(&d.body), out, false), - Statement::ForStatement(f) => { - if let Some(oxide_parser::ForStatementInit::VariableDeclaration(vd)) = &f.init { - if !matches!(vd.kind, VariableDeclarationKind::Var) { - for d in &vd.declarations { - collect_binding_pattern_names(&d.id, out); - } - } - } - collect_block_level_names_stmt(std::slice::from_ref(&f.body), out, false); - } - Statement::ForInStatement(fi) => { - if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left { - if !matches!(vd.kind, VariableDeclarationKind::Var) { - collect_for_left_decl_names(&fi.left, out); - } - } - collect_block_level_names_stmt(std::slice::from_ref(&fi.body), out, false); - } - Statement::ForOfStatement(fo) => { - if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left { - if !matches!(vd.kind, VariableDeclarationKind::Var) { - collect_for_left_decl_names(&fo.left, out); - } - } - collect_block_level_names_stmt(std::slice::from_ref(&fo.body), out, false); - } - Statement::SwitchStatement(sw) => { - for case in &sw.cases { - collect_block_level_names_stmt(&case.consequent, out, false); - } - } - Statement::TryStatement(ts) => { - collect_block_level_names_stmt(&ts.block.body, out, false); - if let Some(h) = &ts.handler { - if let Some(param) = &h.param { - collect_binding_pattern_names(¶m.pattern, out); - } - collect_block_level_names_stmt(&h.body.body, out, false); - } - if let Some(f) = &ts.finalizer { - collect_block_level_names_stmt(&f.body, out, false); - } - } - Statement::LabeledStatement(ls) => { - collect_block_level_names_stmt(std::slice::from_ref(&ls.body), out, false) - } - Statement::WithStatement(ws) => collect_block_level_names_stmt(std::slice::from_ref(&ws.body), out, false), - Statement::ExportNamedDeclaration(exp) => { - if let Some(decl) = &exp.declaration { - match decl { - oxide_parser::Declaration::VariableDeclaration(vd) => { - if !matches!(vd.kind, VariableDeclarationKind::Var) && !top { - for d in &vd.declarations { - collect_binding_pattern_names(&d.id, out); - } - } - } - oxide_parser::Declaration::ClassDeclaration(cd) if !top => { - if let Some(id) = &cd.id { - out.insert(id.name.to_string()); - } - } - _ => {} - } - } - } - _ => {} - } - } -} - /// 收集当前函数作用域声明的绑定名(参数 + 变量/函数声明,含嵌套 block,不含嵌套函数体)。 pub(crate) fn collect_own_binding_names(param_names: &[&str], stmts: &[Statement]) -> HashSet { let mut names = HashSet::new(); @@ -301,10 +20,10 @@ pub(crate) fn collect_own_binding_names(param_names: &[&str], stmts: &[Statement names } -/// 函数作用域名收集结果:单趟遍历同时填六个输出,与旧的六个独立收集函数逐位一致。 +/// 函数作用域名收集结果:单趟遍历同时填八个输出,与各旧独立收集函数逐位一致。 /// -/// 字段含义与旧函数一一对应(var 提升名 / own 绑定名 / 块级声明名 / 直接子词法名 / -/// 顶层函数名集 / 顶层函数声明序)。调用点改经本结构取各面,旧收集器保留待删。 +/// 字段含义(var 提升名 / own 绑定名 / 块级声明名 / 直接子词法名 / 顶层函数名集 / +/// 顶层函数声明序 / 块级函数名 / 块函数抑制集)。调用点经本结构取各面。 pub(crate) struct FnScopeNames { /// var 声明名(函数/程序作用域提升目标,递归进嵌套块)。 pub(crate) var_binding_names: HashSet, @@ -318,19 +37,26 @@ pub(crate) struct FnScopeNames { pub(crate) top_level_function_names: HashSet, /// 直接子级函数声明名(声明序,不排序不去重)。 pub(crate) top_level_function_names_ordered: Vec, + /// 块级普通函数声明名(源序去重;直接子级是提升 var 声明不纳,async/生成器 + /// 是块级词法绑定不纳,不递归嵌套函数体)。 + pub(crate) block_function_names: Vec, + /// 块级函数名的外层 var 绑定抑制集(形参名 ∪ 树内词法声明名;命中抑制的名字 + /// 不建外层 var 绑定、不求值写回)。 + pub(crate) block_fn_suppressed: HashSet, } -/// 单趟收集函数作用域六个名字输出。 +/// 单趟收集函数作用域八个名字输出。 /// /// 步骤: -/// 1. 形参名注入 `own_binding_names`(须在遍历前,own 面是遮蔽集,形参须先入)。 -/// 2. 以 `top=true` 递归遍历顶层语句列表,直接子级填直接子词法/顶层函数名输出, -/// 各递归层填 var/own/块级输出。 +/// 1. 形参名注入 `own_binding_names` 与 `block_fn_suppressed`(须在遍历前, +/// own 面是遮蔽集、抑制集以形参名为基底,形参须先入)。 +/// 2. 以 `top=true` 递归遍历顶层语句列表,直接子级填直接子词法/顶层函数名/ +/// 块函数名输出,各递归层填 var/own/块级/抑制输出。 /// /// 边界:`param_names` 含解构叶名与 rest 名(规范 `paramNames` 口径);顶层语句列表 /// 不递归进嵌套函数体(嵌套函数是独立编译单元)。 /// 副作用:无(纯收集)。 -/// 注意事项:六个字段与旧六函数逐位一致,调用点切换后发射产物不变。 +/// 注意事项:八个字段与旧八收集函数逐位一致,调用点切换后发射产物不变。 pub(crate) fn collect_fn_scope_names(param_names: &[&str], stmts: &[Statement]) -> FnScopeNames { let mut out = FnScopeNames { var_binding_names: HashSet::new(), @@ -339,15 +65,19 @@ pub(crate) fn collect_fn_scope_names(param_names: &[&str], stmts: &[Statement]) direct_lexical_names: HashSet::new(), top_level_function_names: HashSet::new(), top_level_function_names_ordered: Vec::new(), + block_function_names: Vec::new(), + block_fn_suppressed: HashSet::new(), }; for p in param_names { - out.own_binding_names.insert(p.to_string()); + let name = p.to_string(); + out.own_binding_names.insert(name.clone()); + out.block_fn_suppressed.insert(name); } collect_fn_scope_names_impl(stmts, &mut out, true); out } -/// 递归遍历语句树单趟填六个输出。`top` 标记当前层是否为顶层(直接子级):顶层填 +/// 递归遍历语句树单趟填八个输出。`top` 标记当前层是否为顶层(直接子级):顶层填 /// 直接子词法与顶层函数名输出,且顶层 let/const/class 是顶层词法绑定非块级; /// 各递归层(块/if/循环/switch case/try/标签/with)以 `top=false` 进入。 fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: bool) { @@ -377,6 +107,12 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: collect_binding_pattern_names(&d.id, &mut out.direct_lexical_names); } } + // 抑制面:词法名与块函数名同名时退化为纯块作用域(各层均收集)。 + if !is_var { + for d in &vd.declarations { + collect_binding_pattern_names(&d.id, &mut out.block_fn_suppressed); + } + } } Statement::FunctionDeclaration(fd) => { if let Some(id) = &fd.id { @@ -386,6 +122,13 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: // 顶层函数名集与声明序列表(顶层函数声明是全局 var 绑定)。 out.top_level_function_names.insert(id.name.to_string()); out.top_level_function_names_ordered.push(id.name.to_string()); + } else if !fd.generator && !fd.r#async { + // 块级函数面:仅普通函数(async/生成器是块级词法绑定), + // 源序去重,重名只记一次。 + let name = id.name.to_string(); + if !out.block_function_names.contains(&name) { + out.block_function_names.push(name); + } } } } @@ -394,9 +137,11 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: let name = id.name.to_string(); // own 面:类名是本作用域 const 绑定。 out.own_binding_names.insert(name.clone()); + // 抑制面:类名是词法声明,与块函数名同名时退化纯块作用域。 + out.block_fn_suppressed.insert(name.clone()); if top { // 直接子词法面:顶层类是顶层词法绑定。 - out.direct_lexical_names.insert(name); + out.direct_lexical_names.insert(name.clone()); } else { // 块级面:块内类是块作用域绑定。 out.block_level_decl_names.insert(name); @@ -444,6 +189,12 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: collect_binding_pattern_names(&d.id, &mut out.direct_lexical_names); } } + // 抑制面:export 包装的词法名与块函数名同名时退化纯块作用域。 + if !is_var { + for d in &vd.declarations { + collect_binding_pattern_names(&d.id, &mut out.block_fn_suppressed); + } + } } oxide_parser::Declaration::FunctionDeclaration(fd) => { if let Some(id) = &fd.id { @@ -514,6 +265,12 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: collect_binding_pattern_names(&d.id, &mut out.block_level_decl_names); } } + // 抑制面:for 头词法名与块函数名同名时退化纯块作用域。 + if !is_var { + for d in &vd.declarations { + collect_binding_pattern_names(&d.id, &mut out.block_fn_suppressed); + } + } } collect_fn_scope_names_impl(std::slice::from_ref(&f.body), out, false); } @@ -527,6 +284,8 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: collect_for_left_decl_names(&fi.left, &mut out.own_binding_names); if !is_var { collect_for_left_decl_names(&fi.left, &mut out.block_level_decl_names); + // 抑制面:for 头词法名与块函数名同名时退化纯块作用域。 + collect_for_left_decl_names(&fi.left, &mut out.block_fn_suppressed); } } collect_fn_scope_names_impl(std::slice::from_ref(&fi.body), out, false); @@ -540,6 +299,8 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: collect_for_left_decl_names(&fo.left, &mut out.own_binding_names); if !is_var { collect_for_left_decl_names(&fo.left, &mut out.block_level_decl_names); + // 抑制面:for 头词法名与块函数名同名时退化纯块作用域。 + collect_for_left_decl_names(&fo.left, &mut out.block_fn_suppressed); } } collect_fn_scope_names_impl(std::slice::from_ref(&fo.body), out, false); @@ -550,15 +311,18 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: } } Statement::TryStatement(ts) => { - // catch 参数是本作用域的词法绑定:own 与块级两面均计入。 + // 先块后 handler 再 finalizer:块级函数名列表按源序去重,块在前 + // 与旧收集器逐位一致(各集合面不受访问序影响)。 + collect_fn_scope_names_impl(&ts.block.body, out, false); if let Some(h) = &ts.handler { + // catch 参数是本作用域的词法绑定:own、块级与抑制三面均计入。 if let Some(param) = &h.param { collect_binding_pattern_names(¶m.pattern, &mut out.own_binding_names); collect_binding_pattern_names(¶m.pattern, &mut out.block_level_decl_names); + collect_binding_pattern_names(¶m.pattern, &mut out.block_fn_suppressed); } collect_fn_scope_names_impl(&h.body.body, out, false); } - collect_fn_scope_names_impl(&ts.block.body, out, false); if let Some(f) = &ts.finalizer { collect_fn_scope_names_impl(&f.body, out, false); } @@ -709,118 +473,660 @@ pub(crate) fn collect_decl_names_stmt(stmts: &[Statement], out: &mut HashSet, want: &[&str], face: &str| { + let want_set: HashSet = want.iter().map(|s| s.to_string()).collect(); + assert_eq!(got, &want_set, "{face} 面漂移:{src:?}"); + }; + eq(&names.var_binding_names, var, "var"); + eq(&names.own_binding_names, own, "own"); + eq(&names.block_level_decl_names, blk, "块级"); + eq(&names.direct_lexical_names, lex, "直接子词法"); + eq(&names.top_level_function_names, top, "顶层函数"); assert_eq!( - names.direct_lexical_names, - collect_direct_lexical_names(body), - "直接子词法面漂移:{src:?}" - ); - assert_eq!( - names.top_level_function_names, - collect_top_level_function_names(body), - "顶层函数名面漂移:{src:?}" - ); - assert_eq!( - names.top_level_function_names_ordered, - collect_top_level_function_names_ordered(body), + &names.top_level_function_names_ordered, + &topo.iter().map(|s| s.to_string()).collect::>(), "顶层函数声明序漂移:{src:?}" ); + assert_eq!( + &names.block_function_names, + &bfn.iter().map(|s| s.to_string()).collect::>(), + "块函数面漂移:{src:?}" + ); + eq(&names.block_fn_suppressed, sup, "抑制"); } /// 顶层声明面:var/let/const/函数/类声明与解构 pattern 叶名。 #[test] - fn fn_scope_names_bitwise_parity_declarations() { - assert_bitwise("var a; let b = 1; const c = 2; function f() {} class K {}", &[]); - assert_bitwise("var a = {x: 1, y: [z]}; let [p, ...q] = arr; let {m, n: o} = obj;", &[]); - assert_bitwise("function f() { var inner; } function g() {} var f2;", &[]); - assert_bitwise("var a; var a; function f() {} function f() {}", &[]); + fn fn_scope_names_declarations() { + assert_scope_names( + "var a; let b = 1; const c = 2; function f() {} class K {}", + &[], + &["a"], + &["K", "a", "b", "c", "f"], + &[], + &["K", "b", "c"], + &["f"], + &["f"], + &[], + &["K", "b", "c"], + ); + assert_scope_names( + "var a = {x: 1, y: [z]}; let [p, ...q] = arr; let {m, n: o} = obj;", + &[], + &["a"], + &["a", "m", "o", "p", "q"], + &[], + &["m", "o", "p", "q"], + &[], + &[], + &[], + &["m", "o", "p", "q"], + ); + assert_scope_names( + "function f() { var inner; } function g() {} var f2;", + &[], + &["f2"], + &["f", "f2", "g"], + &[], + &[], + &["f", "g"], + &["f", "g"], + &[], + &[], + ); + // 重名顶层函数:名集去重、声明序保留两份。 + assert_scope_names( + "var a; var a; function f() {} function f() {}", + &[], + &["a"], + &["a", "f"], + &[], + &[], + &["f"], + &["f", "f"], + &[], + &[], + ); } /// 控制流面:if/while/do-while/for/for-in/for-of/switch/try/labeled/with 各分支。 #[test] - fn fn_scope_names_bitwise_parity_control_flow() { - assert_bitwise("if (x) { var a; let b; } else { var c; let d; }", &[]); - assert_bitwise("while (x) { var a; let b; } do { var c; } while (x);", &[]); - assert_bitwise("for (var i = 0; i < n; i++) { var a; let b; }", &[]); - assert_bitwise("for (let j of it) { var a; } for (var k in obj) { let b; }", &[]); - assert_bitwise("switch (x) { case 1: { var a; let b; } default: { var c; } }", &[]); - assert_bitwise("try { var a; } catch (e) { let b; } finally { var c; }", &[]); - assert_bitwise("try { var a; } catch { let b; }", &[]); - assert_bitwise("L: { var a; let b; }", &[]); - assert_bitwise("with (obj) { var a; let b; }", &[]); + fn fn_scope_names_control_flow() { + assert_scope_names( + "if (x) { var a; let b; } else { var c; let d; }", + &[], + &["a", "c"], + &["a", "b", "c", "d"], + &["b", "d"], + &[], + &[], + &[], + &[], + &["b", "d"], + ); + assert_scope_names( + "while (x) { var a; let b; } do { var c; } while (x);", + &[], + &["a", "c"], + &["a", "b", "c"], + &["b"], + &[], + &[], + &[], + &[], + &["b"], + ); + assert_scope_names( + "for (var i = 0; i < n; i++) { var a; let b; }", + &[], + &["a", "i"], + &["a", "b", "i"], + &["b"], + &[], + &[], + &[], + &[], + &["b"], + ); + assert_scope_names( + "for (let j of it) { var a; } for (var k in obj) { let b; }", + &[], + &["a", "k"], + &["a", "b", "j", "k"], + &["b", "j"], + &[], + &[], + &[], + &[], + &["b", "j"], + ); + assert_scope_names( + "switch (x) { case 1: { var a; let b; } default: { var c; } }", + &[], + &["a", "c"], + &["a", "b", "c"], + &["b"], + &[], + &[], + &[], + &[], + &["b"], + ); + assert_scope_names( + "try { var a; } catch (e) { let b; } finally { var c; }", + &[], + &["a", "c"], + &["a", "b", "c", "e"], + &["b", "e"], + &[], + &[], + &[], + &[], + &["b", "e"], + ); + assert_scope_names("try { var a; } catch { let b; }", &[], &["a"], &["a", "b"], &["b"], &[], &[], &[], &[], &["b"],); + assert_scope_names("L: { var a; let b; }", &[], &["a"], &["a", "b"], &["b"], &[], &[], &[], &[], &["b"],); + assert_scope_names("with (obj) { var a; let b; }", &[], &["a"], &["a", "b"], &["b"], &[], &[], &[], &[], &["b"],); } /// 嵌套块面:块内 let/const/class 是块级绑定,顶层直接子不纳。 #[test] - fn fn_scope_names_bitwise_parity_nested_blocks() { - assert_bitwise("{ { var a; let b; class K {} } }", &[]); - assert_bitwise("if (x) { { let a; const b = 1; } }", &[]); - assert_bitwise("var a; { let b; } function f() { var c; }", &[]); - assert_bitwise("if (x) while (y) { let a; } for (var i;;) { const b = 1; }", &[]); + fn fn_scope_names_nested_blocks() { + assert_scope_names( + "{ { var a; let b; class K {} } }", + &[], + &["a"], + &["K", "a", "b"], + &["K", "b"], + &[], + &[], + &[], + &[], + &["K", "b"], + ); + assert_scope_names( + "if (x) { { let a; const b = 1; } }", + &[], + &[], + &["a", "b"], + &["a", "b"], + &[], + &[], + &[], + &[], + &["a", "b"], + ); + assert_scope_names( + "var a; { let b; } function f() { var c; }", + &[], + &["a"], + &["a", "b", "f"], + &["b"], + &[], + &["f"], + &["f"], + &[], + &["b"], + ); + assert_scope_names( + "if (x) while (y) { let a; } for (var i;;) { const b = 1; }", + &[], + &["i"], + &["a", "b", "i"], + &["a", "b"], + &[], + &[], + &[], + &[], + &["a", "b"], + ); } - /// 形参注入面:param_names 进 own 面,须与旧 collect_own_binding_names 同口径。 + /// 形参注入面:param_names 进 own 面与抑制面。 #[test] - fn fn_scope_names_bitwise_parity_params_injection() { - assert_bitwise("var p; let q; function f() {}", &["p", "q", "r"]); - assert_bitwise("var x = 1;", &["x", "y"]); - assert_bitwise("function f() { var p; }", &["p"]); + fn fn_scope_names_params_injection() { + assert_scope_names( + "var p; let q; function f() {}", + &["p", "q", "r"], + &["p"], + &["f", "p", "q", "r"], + &[], + &["q"], + &["f"], + &["f"], + &[], + &["p", "q", "r"], + ); + assert_scope_names("var x = 1;", &["x", "y"], &["x"], &["x", "y"], &[], &[], &[], &[], &[], &["x", "y"],); + // 内层 var 在嵌套函数体内,不提升收集;形参名注入 own 面与抑制面。 + assert_scope_names( + "function f() { var p; }", + &["p"], + &[], + &["f", "p"], + &[], + &[], + &["f"], + &["f"], + &[], + &["p"], + ); + } + + /// 块函数面:源序去重、各控制流下钻、async/生成器排除、直接子排除、嵌套函数不递归。 + #[test] + fn fn_scope_names_block_fn_names() { + assert_scope_names( + "{ function bf() {} } if (x) { function bf2() {} } while (y) { function bf3() {} }", + &[], + &[], + &["bf", "bf2", "bf3"], + &[], + &[], + &[], + &[], + &["bf", "bf2", "bf3"], + &[], + ); + assert_scope_names( + "for (var i;;) { function bf4() {} } switch (z) { case 1: { function bf5() {} } }", + &[], + &["i"], + &["bf4", "bf5", "i"], + &[], + &[], + &[], + &[], + &["bf4", "bf5"], + &[], + ); + assert_scope_names( + "try { function bf6() {} } catch (e) { function bf7() {} } finally { function bf8() {} }", + &[], + &[], + &["bf6", "bf7", "bf8", "e"], + &["e"], + &[], + &[], + &[], + &["bf6", "bf7", "bf8"], + &["e"], + ); + assert_scope_names( + "L2: { function bf9() {} } with (o) { function bf10() {} }", + &[], + &[], + &["bf10", "bf9"], + &[], + &[], + &[], + &[], + &["bf9", "bf10"], + &[], + ); + // async/生成器是块级词法绑定,不入块函数名集。 + assert_scope_names( + "async function af() {} { async function af2() {} } { function* g() {} }", + &[], + &[], + &["af", "af2", "g"], + &[], + &[], + &["af"], + &["af"], + &[], + &[], + ); + // 直接子级函数是提升 var 声明,非块级。 + assert_scope_names( + "function direct() {} { function inner() {} }", + &[], + &[], + &["direct", "inner"], + &[], + &[], + &["direct"], + &["direct"], + &["inner"], + &[], + ); + // 重名块函数源序去重,只记一次。 + assert_scope_names( + "{ function a() {} } { function a() {} }", + &[], + &[], + &["a"], + &[], + &[], + &[], + &[], + &["a"], + &[], + ); + // 嵌套函数体是独立编译单元,不递归收集。 + assert_scope_names( + "function outer() { { function inner() {} } }", + &[], + &[], + &["outer"], + &[], + &[], + &["outer"], + &["outer"], + &[], + &[], + ); + } + + /// 抑制面:词法声明(let/class/for 头/catch 参数/export 词法)与块函数名同名时退化纯块作用域。 + #[test] + fn fn_scope_names_block_fn_suppressed() { + assert_scope_names( + "let shadow; { function shadow() {} }", + &[], + &[], + &["shadow"], + &[], + &["shadow"], + &[], + &[], + &["shadow"], + &["shadow"], + ); + assert_scope_names( + "class C {} { function C() {} }", + &[], + &[], + &["C"], + &[], + &["C"], + &[], + &[], + &["C"], + &["C"], + ); + assert_scope_names( + "for (let s;;) { function s() {} }", + &[], + &[], + &["s"], + &["s"], + &[], + &[], + &[], + &["s"], + &["s"], + ); + // catch 参数与块函数名同名:catch 体外的 if 臂块函数被抑制。 + assert_scope_names( + "try {} catch (e) {} if (x) { function e() {} }", + &[], + &[], + &["e"], + &["e"], + &[], + &[], + &[], + &["e"], + &["e"], + ); + // 形参名进抑制面:块函数名撞形参名退化纯块作用域(形参名同时注入 own 面)。 + assert_scope_names( + "function f(a) { { function a() {} } }", + &["a"], + &[], + &["a", "f"], + &[], + &[], + &["f"], + &["f"], + &[], + &["a"], + ); + } + + /// 闭包捕获面:var/词法入口 cell 名集与块函数泄漏名混合。 + #[test] + fn fn_scope_names_capture_faces() { + assert_scope_names( + "var v = 1; let l = 2; function f() { return function() { return v + l; }; }", + &[], + &["v"], + &["f", "l", "v"], + &[], + &["l"], + &["f"], + &["f"], + &[], + &["l"], + ); + assert_scope_names( + "{ function g() { return function() { return v; }; } var v = 3; }", + &[], + &["v"], + &["g", "v"], + &[], + &[], + &[], + &[], + &["g"], + &[], + ); } /// 模块声明面:import/export 包装的声明计入各面(模块口径 parse)。 #[test] - fn fn_scope_names_bitwise_parity_module_decls() { - assert_bitwise_module("import a from 'm'; import * as ns from 'm'; import { b, c as d } from 'm';", &[]); - assert_bitwise_module("export var e = 1; export let f = 2; export const g = 3;", &[]); - assert_bitwise_module("export function h() {} export class J {}", &[]); - assert_bitwise_module("export default function named() {}", &[]); - assert_bitwise_module("export default class K {}", &[]); - assert_bitwise_module("import a from 'm'; export var b = 1; function c() {}", &[]); - assert_bitwise_module("export { x } from 'n';", &[]); + fn fn_scope_names_module_decls() { + assert_scope_names_module( + "import a from 'm'; import * as ns from 'm'; import { b, c as d } from 'm';", + &[], + &[], + &["a", "b", "d", "ns"], + &[], + &[], + &[], + &[], + &[], + &[], + ); + assert_scope_names_module( + "export var e = 1; export let f = 2; export const g = 3;", + &[], + &["e"], + &["e", "f", "g"], + &[], + &["f", "g"], + &[], + &[], + &[], + &["f", "g"], + ); + // export class 不入抑制面(旧收集器只收 export 变量声明)。 + assert_scope_names_module( + "export function h() {} export class J {}", + &[], + &[], + &["J", "h"], + &[], + &["J"], + &[], + &[], + &[], + &[], + ); + assert_scope_names_module( + "export default function named() {}", + &[], + &[], + &["named"], + &[], + &[], + &[], + &[], + &[], + &[], + ); + assert_scope_names_module( + "export default class K {}", + &[], + &[], + &["K"], + &[], + &["K"], + &[], + &[], + &[], + &[], + ); + assert_scope_names_module( + "import a from 'm'; export var b = 1; function c() {}", + &[], + &["b"], + &["a", "b", "c"], + &[], + &[], + &["c"], + &["c"], + &[], + &[], + ); + assert_scope_names_module("export { x } from 'n';", &[], &[], &[], &[], &[], &[], &[], &[], &[]); } - /// for 头面:var/let/const 头名分面归属(var 提升、let/const 块级)。 + /// for 头面:var/let/const 头名分面归属(var 提升、let/const 块级与抑制)。 #[test] - fn fn_scope_names_bitwise_parity_for_heads() { - assert_bitwise("for (var [a, b] of it) {} for (let {x, ...y} in obj) {}", &[]); - assert_bitwise("for (var i;;) {} for (let j;;) {} for (const k = 0;;) {}", &[]); - assert_bitwise("for (var i = 0;;) { for (let j;;) { var a; } }", &[]); + fn fn_scope_names_for_heads() { + assert_scope_names( + "for (var [a, b] of it) {} for (let {x, ...y} in obj) {}", + &[], + &["a", "b"], + &["a", "b", "x", "y"], + &["x", "y"], + &[], + &[], + &[], + &[], + &["x", "y"], + ); + assert_scope_names( + "for (var i;;) {} for (let j;;) {} for (const k = 0;;) {}", + &[], + &["i"], + &["i", "j", "k"], + &["j", "k"], + &[], + &[], + &[], + &[], + &["j", "k"], + ); + assert_scope_names( + "for (var i = 0;;) { for (let j;;) { var a; } }", + &[], + &["a", "i"], + &["a", "i", "j"], + &["j"], + &[], + &[], + &[], + &[], + &["j"], + ); + } + + /// 模块块函数面:export 词法声明抑制块函数外层 var 绑定。 + #[test] + fn fn_scope_names_module_block_fn() { + assert_scope_names_module( + "var mv = 1; let ml = 2; function mf() { return function() { return mv + ml; }; }", + &[], + &["mv"], + &["mf", "ml", "mv"], + &[], + &["ml"], + &["mf"], + &["mf"], + &[], + &["ml"], + ); + assert_scope_names_module( + "export let ex; { function ex() {} }", + &[], + &[], + &["ex"], + &[], + &["ex"], + &[], + &[], + &["ex"], + &["ex"], + ); + assert_scope_names_module( + "export let fl = 1; { function fl() {} }", + &[], + &[], + &["fl"], + &[], + &["fl"], + &[], + &[], + &["fl"], + &["fl"], + ); } } diff --git a/crates/oxide_emit/src/function_body.rs b/crates/oxide_emit/src/function_body.rs index 6dd65140..db14f974 100644 --- a/crates/oxide_emit/src/function_body.rs +++ b/crates/oxide_emit/src/function_body.rs @@ -434,10 +434,18 @@ impl Emitter { // 让 next_reg 与 builtin 槽位对齐,参数在 builtin 槽之后分配。 ctx.reset_regs(); - // 函数作用域六个名字面单趟收集(形参名先注入 own 面):本函数体编译链 - // 各调用点(参数 prologue / builtin 排除 / 入口实例化)共用此结果。 + // 函数作用域八个名字面单趟收集(形参名先注入 own 面与抑制面):本函数体 + // 编译链各调用点(参数 prologue / builtin 排除 / 块函数 / 入口实例化) + // 共用此结果。 let param_name_refs: Vec<&str> = param_specs.iter().map(|s| s.register_name()).collect(); - let scope_names = collect_fn_scope_names(¶m_name_refs, body_stmts); + let mut scope_names = collect_fn_scope_names(¶m_name_refs, body_stmts); + // 抑制集口径是规范 paramNames(含解构叶名),合并趟形参注入按登记名, + // 叶名在此补入(登记名已入集,重复插入无副作用)。 + for name in ctx.param_names.iter() { + if !param_name_refs.contains(&name.as_str()) { + scope_names.block_fn_suppressed.insert(name.clone()); + } + } let param_base = self.emit_params_prologue( param_specs, @@ -478,10 +486,10 @@ impl Emitter { // (形参/词法声明同名,该形退化为纯块作用域)则不建。求值期声明点把 // 函数对象写回此绑定(见函数声明 emit)。 if !ctx.is_strict { - ctx.block_fn_suppressed = self.collect_block_fn_suppressed_names(body_stmts, &ctx.param_names); - for name in self.collect_block_function_names(body_stmts) { - if !ctx.block_fn_suppressed.contains(&name) { - self.predeclare_var_name(&name, &mut ctx); + ctx.block_fn_suppressed = scope_names.block_fn_suppressed.clone(); + for name in &scope_names.block_function_names { + if !ctx.block_fn_suppressed.contains(name) { + self.predeclare_var_name(name, &mut ctx); } } } @@ -1013,13 +1021,12 @@ impl Emitter { // 语义覆盖此初值。 let mut captured_entry_names = scope_names.var_binding_names.clone(); // 块级函数外层 var 名与 instantiate_var_bindings 同源并入;抑制名(形参/ - // 词法声明同名)无外层绑定,不并入。抑制集在此独立计算:本循环早于函数体 - // 的 block_fn_suppressed 构建。 + // 词法声明同名)无外层绑定,不并入。 if !ctx.is_strict { - let suppressed = self.collect_block_fn_suppressed_names(body_stmts, &ctx.param_names); - for name in self.collect_block_function_names(body_stmts) { - if !suppressed.contains(&name) { - captured_entry_names.insert(name); + let suppressed = &scope_names.block_fn_suppressed; + for name in &scope_names.block_function_names { + if !suppressed.contains(name) { + captured_entry_names.insert(name.clone()); } } } @@ -1107,9 +1114,9 @@ impl Emitter { .filter_map(|fd| fd.id.as_ref()) .map(|id| id.name.as_str()) .collect(); - for name in self.collect_block_function_names(body_stmts) { - if !ctx.block_fn_suppressed.contains(&name) && !labeled_hoisted.contains(name.as_str()) { - name_set.insert(name); + for name in &scope_names.block_function_names { + if !ctx.block_fn_suppressed.contains(name) && !labeled_hoisted.contains(name.as_str()) { + name_set.insert(name.clone()); } } } diff --git a/crates/oxide_emit/src/prepass/block_fn.rs b/crates/oxide_emit/src/prepass/block_fn.rs deleted file mode 100644 index 7ecc9077..00000000 --- a/crates/oxide_emit/src/prepass/block_fn.rs +++ /dev/null @@ -1,205 +0,0 @@ -//! emit 前置 pass,块级函数名收集:web-compat 外层 var 绑定实例化支撑—— -//! 语句树内块级普通函数声明名源序去重收集、词法声明名抑制集(块函数名与词法名 -//! 同名时退化为纯块作用域)、形参名 ∪ 词法名抑制集构建。 - -use std::collections::HashSet; - -use crate::capture::collect_binding_pattern_names; -use crate::Emitter; -use oxide_parser::{Declaration, Statement, VariableDeclarationKind}; - -impl Emitter { - /// 递归收集语句树内块级普通函数声明名(源序去重),供 sloppy 模式浏览器兼容 - /// 行为(Annex B.3.3 为块级普通函数声明建外层 var 绑定)下的外层 var 绑定实例化。 - /// - /// # 边界与前提 - /// - 只收普通 `FunctionDeclaration`:Annex B.3.3 的 web-compat 仅改写普通函数 - /// 声明,async 函数、生成器与 async 生成器是各自独立的块级词法绑定,块外 - /// 不可见,不入名集。 - /// - 调用点直接子级(函数体/程序顶层直接子句)是提升 var 声明,非块级 - /// 函数,不收集;进入嵌套语句(块/if/循环/switch case/try 各区)后的 - /// 函数声明才是块级。 - /// - 不递归进嵌套函数体:那是独立编译单元,其内部块函数名由该单元自身收集。 - /// - switch 不推作用域(case 内函数声明随外层块),收集须进入 case 才能到达 - /// case 内的 for/块等块级形。 - pub(crate) fn collect_block_function_names(&self, statements: &[Statement]) -> Vec { - let mut names = Vec::new(); - self.collect_block_function_names_into(statements, &mut names, false); - names - } - - fn collect_block_function_names_into(&self, statements: &[Statement], names: &mut Vec, block_level: bool) { - for statement in statements { - match statement { - Statement::FunctionDeclaration(f) => { - if block_level && !f.generator && !f.r#async { - if let Some(id) = &f.id { - let name = id.name.to_string(); - if !names.contains(&name) { - names.push(name); - } - } - } - } - Statement::BlockStatement(bs) => self.collect_block_function_names_into(&bs.body, names, true), - Statement::IfStatement(is) => { - self.collect_block_function_names_into(std::slice::from_ref(&is.consequent), names, true); - if let Some(alt) = &is.alternate { - self.collect_block_function_names_into(std::slice::from_ref(alt), names, true); - } - } - Statement::WhileStatement(wh) => { - self.collect_block_function_names_into(std::slice::from_ref(&wh.body), names, true) - } - Statement::DoWhileStatement(dw) => { - self.collect_block_function_names_into(std::slice::from_ref(&dw.body), names, true) - } - Statement::ForStatement(fs) => { - self.collect_block_function_names_into(std::slice::from_ref(&fs.body), names, true) - } - Statement::ForInStatement(fi) => { - self.collect_block_function_names_into(std::slice::from_ref(&fi.body), names, true) - } - Statement::ForOfStatement(fo) => { - self.collect_block_function_names_into(std::slice::from_ref(&fo.body), names, true) - } - Statement::SwitchStatement(sw) => { - for case in &sw.cases { - self.collect_block_function_names_into(&case.consequent, names, true); - } - } - Statement::TryStatement(ts) => { - self.collect_block_function_names_into(&ts.block.body, names, true); - if let Some(handler) = &ts.handler { - self.collect_block_function_names_into(&handler.body.body, names, true); - } - if let Some(finalizer) = &ts.finalizer { - self.collect_block_function_names_into(&finalizer.body, names, true); - } - } - Statement::LabeledStatement(ls) => { - self.collect_block_function_names_into(std::slice::from_ref(&ls.body), names, true) - } - Statement::WithStatement(ws) => { - self.collect_block_function_names_into(std::slice::from_ref(&ws.body), names, true) - } - _ => {} - } - } - } - - /// 收集语句树内的词法声明名(let/const/class 含解构叶、for 头词法名、catch - /// 参数):块级函数名与树内任意词法声明同名时退化为纯块作用域(无外层 - /// 绑定、无求值写回)。 - /// - /// # 边界与前提 - /// - 不递归进嵌套函数体/class 元素体:独立编译单元,其词法名不撞本单元的 - /// 块函数名。 - /// - var 声明是 var 绑定,不撞词法环境,不入集。 - fn collect_lexical_name_suppressions(&self, statements: &[Statement], names: &mut HashSet) { - for statement in statements { - match statement { - Statement::VariableDeclaration(vd) => { - if !matches!(vd.kind, VariableDeclarationKind::Var) { - for d in &vd.declarations { - collect_binding_pattern_names(&d.id, names); - } - } - } - Statement::ClassDeclaration(cd) => { - if let Some(id) = &cd.id { - names.insert(id.name.to_string()); - } - } - Statement::ForStatement(fs) => { - if let Some(oxide_parser::ForStatementInit::VariableDeclaration(decl)) = &fs.init { - if !matches!(decl.kind, VariableDeclarationKind::Var) { - for d in &decl.declarations { - collect_binding_pattern_names(&d.id, names); - } - } - } - self.collect_lexical_name_suppressions(std::slice::from_ref(&fs.body), names); - } - Statement::ForInStatement(fi) => { - if let oxide_parser::ForStatementLeft::VariableDeclaration(decl) = &fi.left { - if !matches!(decl.kind, VariableDeclarationKind::Var) { - for d in &decl.declarations { - collect_binding_pattern_names(&d.id, names); - } - } - } - self.collect_lexical_name_suppressions(std::slice::from_ref(&fi.body), names); - } - Statement::ForOfStatement(fo) => { - if let oxide_parser::ForStatementLeft::VariableDeclaration(decl) = &fo.left { - if !matches!(decl.kind, VariableDeclarationKind::Var) { - for d in &decl.declarations { - collect_binding_pattern_names(&d.id, names); - } - } - } - self.collect_lexical_name_suppressions(std::slice::from_ref(&fo.body), names); - } - Statement::TryStatement(ts) => { - // catch 参数是 handler 环境的词法绑定。 - if let Some(handler) = &ts.handler { - if let Some(param) = &handler.param { - collect_binding_pattern_names(¶m.pattern, names); - } - self.collect_lexical_name_suppressions(&handler.body.body, names); - } - self.collect_lexical_name_suppressions(&ts.block.body, names); - if let Some(finalizer) = &ts.finalizer { - self.collect_lexical_name_suppressions(&finalizer.body, names); - } - } - Statement::BlockStatement(bs) => self.collect_lexical_name_suppressions(&bs.body, names), - Statement::IfStatement(is) => { - self.collect_lexical_name_suppressions(std::slice::from_ref(&is.consequent), names); - if let Some(alt) = &is.alternate { - self.collect_lexical_name_suppressions(std::slice::from_ref(alt), names); - } - } - Statement::WhileStatement(wh) => { - self.collect_lexical_name_suppressions(std::slice::from_ref(&wh.body), names) - } - Statement::DoWhileStatement(dw) => { - self.collect_lexical_name_suppressions(std::slice::from_ref(&dw.body), names) - } - Statement::SwitchStatement(sw) => { - for case in &sw.cases { - self.collect_lexical_name_suppressions(&case.consequent, names); - } - } - Statement::LabeledStatement(ls) => { - self.collect_lexical_name_suppressions(std::slice::from_ref(&ls.body), names) - } - Statement::WithStatement(ws) => { - self.collect_lexical_name_suppressions(std::slice::from_ref(&ws.body), names) - } - Statement::ExportNamedDeclaration(exp) => { - if let Some(Declaration::VariableDeclaration(vd)) = &exp.declaration { - if !matches!(vd.kind, VariableDeclarationKind::Var) { - for d in &vd.declarations { - collect_binding_pattern_names(&d.id, names); - } - } - } - } - _ => {} - } - } - } - - /// 块级函数名的外层 var 绑定抑制集(sloppy 模式浏览器兼容行为):形参名 - /// (含解构叶/rest)∪ 语句树内词法声明名。命中抑制的名字不建外层 var 绑定、 - /// 不求值写回。 - pub(crate) fn collect_block_fn_suppressed_names( - &self, statements: &[Statement], param_names: &HashSet, - ) -> HashSet { - let mut names = param_names.clone(); - self.collect_lexical_name_suppressions(statements, &mut names); - names - } -} diff --git a/crates/oxide_emit/src/prepass/mod.rs b/crates/oxide_emit/src/prepass/mod.rs index ea1ae52c..73a67ff1 100644 --- a/crates/oxide_emit/src/prepass/mod.rs +++ b/crates/oxide_emit/src/prepass/mod.rs @@ -4,6 +4,5 @@ //! (builtin_reg_map),避免与临时寄存器池冲突;同时预声明函数/var 声明, //! 支持提升语义。 -mod block_fn; mod builtin_scan; mod declare; diff --git a/crates/oxide_emit/src/program.rs b/crates/oxide_emit/src/program.rs index 60935236..665a97e0 100644 --- a/crates/oxide_emit/src/program.rs +++ b/crates/oxide_emit/src/program.rs @@ -16,10 +16,7 @@ use oxide_ir::operand::Operand; use oxide_ir::IRFunction; use oxide_parser::Statement; -use crate::capture::{ - collect_block_level_decl_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::capture::{collect_captured_bindings, collect_fn_scope_names}; use crate::compile_ctx::CompileCtx; use crate::Emitter; @@ -231,10 +228,10 @@ impl Emitter { /// # 边界与前提 /// - 仅普通脚本调用(`is_eval_script` 面由调用点按条件判定排除);eval 动态 /// 路径与 eval 三常量编译期条件开关零触碰。 - /// - 仅遍历语句列表直接子级具名函数声明(生成器/异步声明同节点类型,天然 - /// 覆盖);块内函数声明与 `export default function` 不入全局检查面。 - pub(crate) fn emit_gdi_func_decl_checks(&self, stmts: &[Statement], ctx: &mut CompileCtx) { - let names = collect_top_level_function_names_ordered(stmts); + /// - `names` 为顶层函数声明名列表(声明序,由调用点经合并单趟收集):仅 + /// 直接子级具名函数声明(生成器/异步声明同节点类型,天然覆盖);块内函数 + /// 声明与 `export default function` 不入全局检查面。 + pub(crate) fn emit_gdi_func_decl_checks(&self, names: &[String], ctx: &mut CompileCtx) { let mut seen = HashSet::new(); for name in names.iter().rev() { // 逆序首见即源序最后声明者(规范去重口径),重名只查一次。 @@ -272,6 +269,11 @@ impl Emitter { // 脚本顶层严格模式由源码 "use strict" directive 决定(嵌套函数经父 ctx 继承)。 ctx.is_strict = program.has_use_strict_directive(); + // 程序作用域八个名字面单趟收集(程序无形参,param_names 传空): + // GDI 检查 / eval 门禁 / builtin 排除 / 块函数 / own 绑定 / tier 名集 / + // 入口实例化各调用点共用此结果。 + let scope_names = collect_fn_scope_names(&[], &program.body); + // eval 代码顶层函数声明撞不可写全局内置(三常量的全局绑定在任何符合规范的 // 实现中均不可配置,声明实例化无法建立全局绑定):规范在建立全局 var 绑定 // 之前抛 TypeError(step 8 的 abrupt 先于绑定实例化),故 throw 发为程序首 @@ -279,7 +281,7 @@ impl Emitter { // 严格 eval 代码函数声明绑定 eval 自身 lexical 环境、不触全局、不抛,门禁 // 随 !is_strict 关闭,其写点抑制在声明发射处。 if ctx.is_eval_script && !ctx.is_strict { - for name in collect_top_level_function_names_ordered(&program.body) { + for name in &scope_names.top_level_function_names_ordered { if NON_WRITABLE_GLOBAL_BUILTINS.contains(&name.as_str()) { let _ = self.emit_throw_error( "TypeError", @@ -295,7 +297,7 @@ impl Emitter { // 顶层函数声明撞既有不可配置全局属性抛 TypeError,sloppy/strict 同形。 // eval 面零触碰:静态三常量面由上方 eval 三常量编译期门禁拦,动态臂另归口。 if !ctx.is_eval_script { - self.emit_gdi_func_decl_checks(&program.body, &mut ctx); + self.emit_gdi_func_decl_checks(&scope_names.top_level_function_names_ordered, &mut ctx); } self.predeclare_function_declarations(&program.body, &mut ctx); @@ -303,7 +305,7 @@ impl Emitter { // 预注册 builtin 引用(先于任何临时寄存器),builtin 槽不进入临时寄存器池。 // 顶层词法声明名(let/const/class)排除登记:词法绑定遮蔽同名全局属性, // 镜像占位先建会与词法预声明撞 scope 0 致合法遮蔽形被编译期误拒。 - let lexical_excluded = collect_direct_lexical_names(&program.body); + let lexical_excluded = scope_names.direct_lexical_names.clone(); self.pre_register_builtin_references(&program.body, &lexical_excluded, &mut ctx); // 预声明顶层 `var` 名,使首个 sub-pass 中提升的函数声明能解析外层 var。 @@ -320,8 +322,8 @@ impl Emitter { // (define-if-absent,既有属性不改动值)。strict 代码不建。 let mut block_fn_names: Vec = Vec::new(); if !ctx.is_strict { - ctx.block_fn_suppressed = self.collect_block_fn_suppressed_names(&program.body, &ctx.param_names); - block_fn_names = self.collect_block_function_names(&program.body); + ctx.block_fn_suppressed = scope_names.block_fn_suppressed.clone(); + block_fn_names = scope_names.block_function_names.clone(); for name in &block_fn_names { if !ctx.block_fn_suppressed.contains(name) && !CompileCtx::is_non_writable_global_builtin(name) { self.predeclare_var_name(name, &mut ctx); @@ -330,7 +332,7 @@ impl Emitter { } // 闭包捕获分析(AST 级,emit 前确定) - ctx.own_bindings = collect_own_binding_names(&[], &program.body); + ctx.own_bindings = scope_names.own_binding_names.clone(); // 顶层已声明名(全局对象属性为唯一存储):裸读走全局对象属性、裸写走感知 // 全局对象属性描述符的写,不落引擎侧镜像副本——从捕获集剔除,使嵌套函数经 // 继承 scope 0 直连全局。 @@ -338,9 +340,9 @@ impl Emitter { // 其全局对象属性值由首个 sub-pass(`emit_program` 顶层的内部阶段划分:先 // 声明实例化、后用户代码发射)以真闭包建立,先于任何用户代码。 // 仅在此顶层调用点过滤:嵌套函数的局部同名遮蔽是独立绑定,其调用点不过滤。 - let var_names = collect_var_binding_names(&program.body); + let var_names = scope_names.var_binding_names.clone(); let mut tier_names = var_names.clone(); - tier_names.extend(collect_top_level_function_names(&program.body)); + tier_names.extend(scope_names.top_level_function_names.iter().cloned()); // 块级函数泄漏名并入:求值期写回与块入口头写同走全局对象属性(全局对象 // 属性为唯一存储)。 for name in &block_fn_names { @@ -353,7 +355,7 @@ impl Emitter { // 顶层 tier 名(顶层 var/函数)的全局对象属性是唯一存储,从捕获集剔除; // 被同名块级绑定(catch 参数 / 块级 let/const)遮蔽时保留,使块级绑定能 // 追加新索引供闭包捕获(顶层 var 本身不建 cell,回填与解析点均跳过)。 - let block_level_names = collect_block_level_decl_names(&program.body); + let block_level_names = scope_names.block_level_decl_names.clone(); ctx.captured_bindings .retain(|n, _| !ctx.global_tier_names.contains(n) || block_level_names.contains(n)); // 顶层函数级绑定 cell 索引回填:预声明已完成,绑定已齐。 @@ -388,9 +390,11 @@ impl Emitter { // 创建的闭包指向此未初始化 cell,声明前读写经运行时抛真 TDZ;声明语句 // 的 MAKE_CELL 按占位更新语义原位翻转为已初始化。名集取捕获集交集 // (直接子级词法名,块级名不提升到顶层),排序保证发射序跨进程稳定。 - let mut lex_names: Vec = collect_direct_lexical_names(&program.body) - .into_iter() - .filter(|n| ctx.captured_bindings.contains_key(n)) + let mut lex_names: Vec = scope_names + .direct_lexical_names + .iter() + .filter(|n| ctx.captured_bindings.contains_key(*n)) + .cloned() .collect(); lex_names.sort(); if !lex_names.is_empty() { @@ -420,7 +424,7 @@ impl Emitter { // 把全局属性值预载进固定寄存器槽),既有属性不改动值,值幂等保留。 // 序言名集 = 顶层 var 名 ∪ 顶层块级函数泄漏名(sloppy 模式浏览器兼容行为的 // 外层绑定同样在求值前实例化,同走 define-if-absent)。 - let mut gdi_var_names: Vec = collect_var_binding_names(&program.body).into_iter().collect(); + let mut gdi_var_names: Vec = scope_names.var_binding_names.iter().cloned().collect(); gdi_var_names.extend( block_fn_names .iter() diff --git a/crates/oxide_emit/tests/emit_ir_tests.rs b/crates/oxide_emit/tests/emit_ir_tests.rs index 8236cebc..45479b45 100644 --- a/crates/oxide_emit/tests/emit_ir_tests.rs +++ b/crates/oxide_emit/tests/emit_ir_tests.rs @@ -375,3 +375,27 @@ fn source_encoded_regex_pool_key() { assert!(!enc.contains("\\ud800")); assert!(pool_strings(&static_ir).contains("\\ud800"), "静态源池键为明文转义文本"); } + +/// 面 12:块函数抑制集含解构形参叶名——块函数名撞叶名时不建外层 var 绑定、 +/// 声明点不写回(STORE_VAR 少一条写回);非抑制名块函数有入口 undefined 写 +/// 与声明点写回。 +#[test] +fn block_fn_suppressed_by_destructured_param_leaf() { + // 撞解构叶名:叶名绑定(1)+ 块函数物化自写(1)= 2,无外层 var 写回。 + let suppressed = emit_ir("function f({x}) { { function x() { return 1; } } return 2; }"); + let f = &suppressed.nested[0]; + assert_eq!( + count_op(&f.insts, OpCode::STORE_VAR), + 2, + "撞解构叶名的块函数被抑制:无外层 var 写回" + ); + + // 非抑制对照:入口 undefined 写(1)+ 块函数物化自写(1)+ 外层 var 写回(1)= 3。 + let control = emit_ir("function f() { { function y() { return 1; } } return 2; }"); + let fc = &control.nested[0]; + assert_eq!( + count_op(&fc.insts, OpCode::STORE_VAR), + 3, + "非抑制块函数应有入口 undefined 写与外层 var 写回" + ); +}