mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
fix: 块级函数声明补 web-compat 外层 var 绑定实例化与声明点写回
This commit is contained in:
@@ -216,6 +216,13 @@ pub struct CompileCtx {
|
||||
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
|
||||
/// 本函数作用域声明的绑定名(参数 + 变量/函数声明,AST 收集,emit 前确定)。
|
||||
pub(crate) own_bindings: HashSet<String>,
|
||||
/// 本函数形参名集(含解构形参叶子与 rest,编译入口收集):块级函数名与
|
||||
/// 形参同名时不建外层绑定、不求值写回(规范 paramNames 守卫面)。子函数
|
||||
/// ctx 为新建,不继承。
|
||||
pub(crate) param_names: HashSet<String>,
|
||||
/// 块级函数名 web-compat 外层绑定抑制集(形参 ∪ 函数作用域树内词法声明名):
|
||||
/// 抑制集内名字退化为纯块作用域。预声明期定稿,实例化与声明点写回两侧同查。
|
||||
pub(crate) block_fn_suppressed: HashSet<String>,
|
||||
/// 本函数被嵌套函数捕获的绑定名 → cell_idx(名字排序分配,稳定跨 run)。
|
||||
/// 捕获判断(MAKE_CELL / CELL_GET / CELL_SET)与子函数 upvalue cell_idx 统一查此映射,
|
||||
/// 消除符号表时序依赖与 cell 索引错位。
|
||||
@@ -362,6 +369,8 @@ impl CompileCtx {
|
||||
class_keys_reg: None,
|
||||
current_upvalue_captures: Vec::new(),
|
||||
own_bindings: HashSet::new(),
|
||||
param_names: HashSet::new(),
|
||||
block_fn_suppressed: HashSet::new(),
|
||||
captured_bindings: BTreeMap::new(),
|
||||
global_tier_names: HashSet::new(),
|
||||
upvalue_const_flags: HashSet::new(),
|
||||
@@ -1147,6 +1156,16 @@ impl Emitter {
|
||||
// 类元素方法/构造器恒 strict(ClassBody 是严格模式代码)。
|
||||
ctx.is_strict = own_strict || matches!(body_context, FunctionBodyContext::ClassElement) || parent_ctx.is_strict;
|
||||
|
||||
// 形参名集(含解构叶子与 rest):块级函数名同名的 web-compat 守卫查此集。
|
||||
let mut param_names = HashSet::new();
|
||||
for spec in param_specs {
|
||||
param_names.insert(spec.register_name().to_string());
|
||||
if let ParamSpec::Pattern { pattern, .. } = spec {
|
||||
self.collect_binding_pattern_names(pattern, &mut param_names);
|
||||
}
|
||||
}
|
||||
ctx.param_names = param_names;
|
||||
|
||||
// length = 第一个带默认值形参之前的形参数(解构默认与标识符默认同规则);
|
||||
// rest 参数不计入 length(以 0 结尾即止)。
|
||||
ctx.function_length = param_specs
|
||||
@@ -1253,6 +1272,19 @@ impl Emitter {
|
||||
// 局部绑定,不做受限全局名检查;重复声明错在 emit 期报。
|
||||
let _ = self.predeclare_lexical_declarations(body_stmts, &mut ctx, false);
|
||||
|
||||
// 块级函数名 web-compat 外层 var 绑定(sloppy):块内函数声明名在实例化
|
||||
// 期于函数作用域建外层 var 绑定——无同名绑定则新建 var 槽;名在抑制集
|
||||
// (形参/词法声明同名,该形退化为纯块作用域)则不建。求值期声明点把
|
||||
// 函数对象写回此绑定(见函数声明 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 生成器:body 起点标记——调用时参数初始化(emit_params_prologue)结束后挂起于此,
|
||||
// 参数副作用/异常在 `g()` 调用时刻生效,首次 next() 从这继续执行 body。
|
||||
if is_generator {
|
||||
@@ -1801,6 +1833,20 @@ impl Emitter {
|
||||
let global_lexical = !ctx.is_eval_script;
|
||||
self.predeclare_lexical_declarations(&program.body, &mut ctx, global_lexical)?;
|
||||
|
||||
// 顶层块级函数名 web-compat 外层绑定(sloppy、非 eval):实例化 var 绑定
|
||||
// (新建 var 槽;顶层 builtin 名不可声明不建)、并入顶层 var 名集(裸读/
|
||||
// 写路由全局对象属性)、GDI 序言建属性(define-if-absent,既有属性零动作)。
|
||||
let mut block_fn_names: Vec<String> = Vec::new();
|
||||
if !ctx.is_strict && !ctx.is_eval_script {
|
||||
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);
|
||||
for name in &block_fn_names {
|
||||
if !ctx.block_fn_suppressed.contains(name) && !CompileCtx::is_known_builtin(name) {
|
||||
self.predeclare_var_name(name, &mut ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 闭包捕获分析(AST 级,emit 前确定)
|
||||
ctx.own_bindings = self.collect_own_binding_names(&[], &program.body);
|
||||
// 顶层已声明名(A 侧单一真值):裸读走全局对象属性、裸写走描述符感知
|
||||
@@ -1811,6 +1857,12 @@ impl Emitter {
|
||||
let var_names = self.collect_var_binding_names(&program.body);
|
||||
let mut tier_names = var_names.clone();
|
||||
tier_names.extend(self.collect_top_level_function_names(&program.body));
|
||||
// 块级函数泄漏名并入:求值期写回与头写同走全局对象属性(A 侧单一真值)。
|
||||
for name in &block_fn_names {
|
||||
if !ctx.block_fn_suppressed.contains(name) && !CompileCtx::is_known_builtin(name) {
|
||||
tier_names.insert(name.clone());
|
||||
}
|
||||
}
|
||||
ctx.global_tier_names = tier_names;
|
||||
ctx.captured_bindings = self.collect_captured_bindings(&program.body, &[], &ctx.own_bindings);
|
||||
ctx.captured_bindings.retain(|n, _| !ctx.global_tier_names.contains(n));
|
||||
@@ -1846,8 +1898,15 @@ impl Emitter {
|
||||
// 新建,可写/不可写/可配置既有属性(含值)一律保留。builtin 名的全局属性
|
||||
// 运行期预存(session 绑定):缺失分支写入值取 builtin 镜像槽(run 起点
|
||||
// 预载全局属性值),既有属性零动作,值幂等保留。
|
||||
// 序言名集 var-only,不随 tier 集扩宽。
|
||||
let gdi_var_names = self.collect_var_binding_names(&program.body);
|
||||
// 序言名集 = 顶层 var 名 ∪ 顶层块级函数泄漏名(web-compat 外层绑定同样
|
||||
// 在求值前实例化,同走 define-if-absent)。
|
||||
let mut gdi_var_names = self.collect_var_binding_names(&program.body);
|
||||
gdi_var_names.extend(
|
||||
block_fn_names
|
||||
.iter()
|
||||
.filter(|n| !ctx.block_fn_suppressed.contains(*n) && !CompileCtx::is_known_builtin(n))
|
||||
.cloned(),
|
||||
);
|
||||
if !gdi_var_names.is_empty() {
|
||||
let undef_reg = self.emit_undefined(&mut ctx);
|
||||
for name in &gdi_var_names {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
//! (builtin_reg_map),避免与临时寄存器池冲突;同时预声明函数/var 声明,
|
||||
//! 支持提升语义。
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::{
|
||||
BindingPattern, Declaration, ExportDefaultDeclarationKind, Expression, Statement, VariableDeclarationKind,
|
||||
@@ -393,7 +395,7 @@ impl Emitter {
|
||||
/// (run 起点预载全局属性值),使 GDI 序言与声明点同步都用入口原值而非
|
||||
/// fresh undefined 槽,现存值(NaN 等)不被抹;函数体内 builtin 名仍是局部
|
||||
/// var 遮蔽(fresh 槽,不命中只读内置拦截);其余名 fresh var 槽。
|
||||
fn predeclare_var_name(&self, name: &str, ctx: &mut CompileCtx) {
|
||||
pub(crate) fn predeclare_var_name(&self, name: &str, ctx: &mut CompileCtx) {
|
||||
if ctx.is_global_scope && CompileCtx::is_known_builtin(name) {
|
||||
let _ = ctx.lookup_or_builtin(name);
|
||||
} else {
|
||||
@@ -749,3 +751,195 @@ impl Emitter {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Emitter {
|
||||
/// 递归收集语句树内块级函数声明名(源序去重),供 web-compat 外层 var 绑定
|
||||
/// 实例化。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 调用点直接子级(函数体/程序顶层直接子句)是提升 var 声明,非块级
|
||||
/// 函数,不收集;进入嵌套语句(块/if/循环/switch case/try 各区)后的
|
||||
/// 函数声明才是块级。
|
||||
/// - 不递归进嵌套函数体:那是独立编译单元,其内部块函数名由该单元自身收集。
|
||||
/// - switch 不推作用域(case 内函数声明随外层块),收集须进入 case 才能到达
|
||||
/// case 内的 for/块等块级形。
|
||||
pub(crate) fn collect_block_function_names(&self, statements: &[Statement]) -> Vec<String> {
|
||||
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<String>, block_level: bool) {
|
||||
for statement in statements {
|
||||
match statement {
|
||||
Statement::FunctionDeclaration(f) => {
|
||||
if block_level {
|
||||
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<String>) {
|
||||
for statement in statements {
|
||||
match statement {
|
||||
Statement::VariableDeclaration(vd) => {
|
||||
if !matches!(vd.kind, VariableDeclarationKind::Var) {
|
||||
for d in &vd.declarations {
|
||||
self.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 {
|
||||
self.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 {
|
||||
self.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 {
|
||||
self.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 {
|
||||
self.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 {
|
||||
self.collect_binding_pattern_names(&d.id, names);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 块级函数名 web-compat 外层绑定抑制集:形参名(含解构叶/rest)∪ 语句树内
|
||||
/// 词法声明名。命中抑制的名字不建外层 var 绑定、不求值写回。
|
||||
pub(crate) fn collect_block_fn_suppressed_names(
|
||||
&self, statements: &[Statement], param_names: &HashSet<String>,
|
||||
) -> HashSet<String> {
|
||||
let mut names = param_names.clone();
|
||||
self.collect_lexical_name_suppressions(statements, &mut names);
|
||||
names
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,35 @@ impl Emitter {
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(var_reg), Operand::Reg(var_reg), Operand::None));
|
||||
}
|
||||
// 块内函数声明求值写回外层 var 绑定(sloppy web-compat):预声明期为该
|
||||
// 名实例化的外层 var 绑定(函数作用域)在此写入块槽位里的函数对象,
|
||||
// 求值一次写回一次(循环体内每迭代覆写头绑定)。形参/词法声明同名
|
||||
// (抑制集)与顶层 builtin 名不写回;顶层 A 侧补全局对象属性写(与
|
||||
// for 头写同形)。
|
||||
if !ctx.is_strict
|
||||
&& ctx.scopes.symbols.scopes.len() > 1
|
||||
&& !ctx.block_fn_suppressed.contains(&name)
|
||||
&& !(ctx.is_global_scope && CompileCtx::is_known_builtin(&name))
|
||||
{
|
||||
let var_target = ctx.scopes.symbols.find_var_target_scope();
|
||||
if let Some(outer) = ctx.scopes.symbols.scopes[var_target].bindings.get(&name) {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(outer.reg),
|
||||
Operand::Reg(var_reg),
|
||||
Operand::None,
|
||||
));
|
||||
// 顶层 A 侧同步写以外层绑定是否落全局作用域为准:此处块内
|
||||
// Let 绑定已占内层作用域,按名解析(is_global_tier_name)会
|
||||
// 命中块绑定误判为非顶层;嵌套函数作用域同名局部 var 不在
|
||||
// 全局 ctx 上,经 is_global_scope 门禁不走全局对象写。
|
||||
if ctx.is_global_scope && var_target == 0 && ctx.global_tier_names.contains(&name) {
|
||||
self.emit_tier_global_write(&name, var_reg, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 脚本顶层(非块内)函数声明:同步写全局对象,使 globalThis 可反射函数名。
|
||||
// 块内函数声明是块级绑定,不得落全局对象(作用域隔离,块外不可见)。
|
||||
// 块内声明不走本臂(其全局可见性只经上方外层 var 写回面建立)。
|
||||
// 严格 eval 代码函数声明绑定 eval 自身 lexical 环境、不落全局对象:抑制 A
|
||||
// 侧写(局部绑定读面未暴露),同时避免不可写全局内置在 0x98 上误抛 strict。
|
||||
if ctx.is_global_scope && ctx.scopes.symbols.scopes.len() == 1 && !(ctx.is_eval_script && ctx.is_strict) {
|
||||
|
||||
@@ -70,7 +70,7 @@ impl SymbolTable {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_var_target_scope(&self) -> usize {
|
||||
pub(crate) fn find_var_target_scope(&self) -> usize {
|
||||
for (i, scope) in self.scopes.iter().enumerate().rev() {
|
||||
if scope.kind == ScopeKind::FunctionScope {
|
||||
return i;
|
||||
|
||||
Reference in New Issue
Block a user