feat: 函数名登记为作用域内不可写绑定并按帧槽写入函数对象

This commit is contained in:
2026-10-09 21:32:51 +00:00
parent 71904a644f
commit 4f6bd5d947
16 changed files with 271 additions and 42 deletions
+2
View File
@@ -271,6 +271,7 @@ impl Emitter {
&field_value_exprs,
&extra_upvalue_names,
true,
None,
)?
} else {
let mut module = self.compile_function_body_with_field_hooks(
@@ -285,6 +286,7 @@ impl Emitter {
&field_value_exprs,
&extra_upvalue_names,
true,
None,
)?;
if is_derived {
module.insts.clear();
+1
View File
@@ -138,6 +138,7 @@ impl Emitter {
method_value.generator,
method_value.r#async,
true,
None,
)?;
ctx.in_instance_method = saved_instance;
ctx.in_static_method = saved_static;
+11
View File
@@ -208,6 +208,14 @@ pub struct CompileCtx {
/// 运行时与模块 flat_id 组成模板对象缓存键,保证同一编译树同 site 恒返回
/// 同一对象、不同编译树(eval 每次编译)互不共享。
pub(crate) next_template_site: u32,
/// 本函数登记的函数名(非抑制面):`assemble_ir` 回写到 IR 供调试与
/// 帧槽写入判定。抑制面(形参/var 同名)不登记名绑定,此字段仍记名
/// 供 `instantiate_var_bindings` 排除入口 undefined 写。
pub(crate) function_name: Option<String>,
/// 函数名不可写绑定的寄存器(帧槽写入目标):压帧时把 callee 函数对象
/// 写入该槽。非抑制面在名登记时分配;抑制面(var 同名)在
/// `instantiate_var_bindings` 回填为 var 槽寄存器。
pub(crate) function_name_reg: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -304,6 +312,8 @@ impl CompileCtx {
module_self_aliases: HashMap::new(),
module_alias_pairs: Vec::new(),
next_template_site: 0,
function_name: None,
function_name_reg: None,
}
}
@@ -1004,6 +1014,7 @@ impl CompileCtx {
is_strict: self.is_strict,
captured_this_const_idx: 0,
function_name: None,
function_name_reg: self.function_name_reg,
function_length: self.function_length,
has_simple_params: self.has_simple_params,
is_top_level: parent_ctx.is_none(),
+7 -4
View File
@@ -47,6 +47,7 @@ impl Emitter {
is_expr_body,
true,
arrow.has_use_strict_directive(),
None,
)?
} else {
self.compile_function_body(
@@ -56,6 +57,7 @@ impl Emitter {
is_expr_body,
true,
arrow.has_use_strict_directive(),
None,
)?
};
sub_module.is_arrow = true;
@@ -98,14 +100,15 @@ impl Emitter {
let body_stmts: &[Statement] = if let Some(body) = &fe.body { &body.statements } else { &[] };
let own_strict = fe.has_use_strict_directive();
let fn_name = fe.id.as_ref().map(|id| id.name.as_str());
let mut sub_module = if fe.generator && fe.r#async {
self.compile_async_generator_body(&param_names, body_stmts, ctx, own_strict)?
self.compile_async_generator_body(&param_names, body_stmts, ctx, own_strict, fn_name)?
} else if fe.generator {
self.compile_generator_body(&param_names, body_stmts, ctx, own_strict)?
self.compile_generator_body(&param_names, body_stmts, ctx, own_strict, fn_name)?
} else if fe.r#async {
self.compile_async_body(&param_names, body_stmts, ctx, false, false, own_strict)?
self.compile_async_body(&param_names, body_stmts, ctx, false, false, own_strict, fn_name)?
} else {
self.compile_function_body(&param_names, body_stmts, ctx, false, false, own_strict)?
self.compile_function_body(&param_names, body_stmts, ctx, false, false, own_strict, fn_name)?
};
if let Some(id) = &fe.id {
sub_module.function_name = Some(id.name.to_string());
+106 -10
View File
@@ -141,10 +141,12 @@ impl Emitter {
///
/// `is_expression_body` 为 true(箭头表达式体)时返回最后一个表达式的值,
/// 否则返回 undefined。`is_arrow` 控制 super 相关标志的继承:箭头函数词法
/// 继承外层 super,普通函数重置 super 作用域。
/// 继承外层 super,普通函数重置 super 作用域。`name` 为函数名(具名函数
/// 声明/表达式传名,箭头与匿名传 `None`),用于函数名不可写绑定登记。
#[allow(clippy::too_many_arguments)]
pub(crate) fn compile_function_body<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, is_arrow: bool, own_strict: bool,
is_expression_body: bool, is_arrow: bool, own_strict: bool, name: Option<&str>,
) -> Result<IRFunction, String> {
self.compile_function_body_with_flags(
param_specs,
@@ -155,12 +157,14 @@ impl Emitter {
false,
false,
own_strict,
name,
)
}
/// 编译函数体并显式指定生成器标志(`function*` 走此入口)。
pub(crate) fn compile_generator_body<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, own_strict: bool,
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
own_strict: bool, name: Option<&str>,
) -> Result<IRFunction, String> {
self.compile_function_body_with_flags(
param_specs,
@@ -171,6 +175,7 @@ impl Emitter {
true,
false,
own_strict,
name,
)
}
@@ -178,16 +183,20 @@ impl Emitter {
/// 同时标记 `is_generator` 与 `is_async`,VM 调用时按异步生成器协议执行
/// (next 返回 Promise,yield 挂起与 await 挂起共存)。
pub(crate) fn compile_async_generator_body<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, own_strict: bool,
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
own_strict: bool, name: Option<&str>,
) -> Result<IRFunction, String> {
self.compile_function_body_with_flags(param_specs, body_stmts, parent_ctx, false, false, true, true, own_strict)
self.compile_function_body_with_flags(
param_specs, body_stmts, parent_ctx, false, false, true, true, own_strict, name,
)
}
/// 编译异步函数体(`async function` / async 箭头走此入口):`is_async` 使
/// `assemble_ir` 标记模块,VM 调用时按异步函数协议执行。
#[allow(clippy::too_many_arguments)]
pub(crate) fn compile_async_body<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, is_arrow: bool, own_strict: bool,
is_expression_body: bool, is_arrow: bool, own_strict: bool, name: Option<&str>,
) -> Result<IRFunction, String> {
self.compile_function_body_with_flags(
param_specs,
@@ -198,6 +207,7 @@ impl Emitter {
false,
true,
own_strict,
name,
)
}
@@ -205,6 +215,7 @@ impl Emitter {
fn compile_function_body_with_flags<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, is_arrow: bool, is_generator: bool, is_async: bool, own_strict: bool,
name: Option<&str>,
) -> Result<IRFunction, String> {
let body_context = if is_arrow {
FunctionBodyContext::Arrow
@@ -221,6 +232,7 @@ impl Emitter {
is_generator,
is_async,
own_strict,
name,
)
}
@@ -228,7 +240,7 @@ impl Emitter {
pub(crate) fn compile_function_body_with_bindings_gen<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, extra_bindings: &[(&str, u32)], body_context: FunctionBodyContext,
is_generator: bool, is_async: bool, own_strict: bool,
is_generator: bool, is_async: bool, own_strict: bool, name: Option<&str>,
) -> Result<IRFunction, String> {
self.compile_function_body_with_field_hooks_gen(
param_specs,
@@ -244,6 +256,7 @@ impl Emitter {
is_generator,
is_async,
own_strict,
name,
)
}
@@ -259,7 +272,7 @@ impl Emitter {
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
is_expression_body: bool, extra_bindings: &[(&str, u32)], body_context: FunctionBodyContext,
emit_fields: Option<E>, fields_after_super: bool, extra_capture_exprs: &[&'a Expression<'a>],
extra_upvalue_names: &[(&str, u8)], own_strict: bool,
extra_upvalue_names: &[(&str, u8)], own_strict: bool, name: Option<&str>,
) -> Result<IRFunction, String>
where
E: FnMut(&Emitter, &mut CompileCtx) -> Result<(), String>,
@@ -278,6 +291,7 @@ impl Emitter {
false,
false,
own_strict,
name,
)
}
@@ -287,6 +301,7 @@ impl Emitter {
is_expression_body: bool, extra_bindings: &[(&str, u32)], body_context: FunctionBodyContext,
mut emit_fields: Option<E>, fields_after_super: bool, extra_capture_exprs: &[&'a Expression<'a>],
extra_upvalue_names: &[(&str, u8)], is_generator: bool, is_async: bool, own_strict: bool,
name: Option<&str>,
) -> Result<IRFunction, String>
where
E: FnMut(&Emitter, &mut CompileCtx) -> Result<(), String>,
@@ -368,6 +383,7 @@ impl Emitter {
// 父层 cell 索引在子帧无效:子帧经 upvalue 路径访问父捕获
// 绑定,不复制索引(复制会让解析点误发子帧 cell 表读)。
cell_idx: None,
non_writable: binding.non_writable,
},
);
inherited_reg_start = inherited_reg_start.max(binding.reg.saturating_add(1));
@@ -394,6 +410,7 @@ impl Emitter {
lexical: false,
predeclared: false,
cell_idx: None,
non_writable: false,
},
);
inherited_reg_start = inherited_reg_start.max(reg.saturating_add(1));
@@ -411,6 +428,7 @@ impl Emitter {
body_context,
extra_capture_exprs,
extra_upvalue_names,
name,
)?;
// 实例字段 computed key 数组所在 upvalue 下标,供字段初始化 emit 定位。
ctx.field_keys_uv = ctx
@@ -458,6 +476,22 @@ impl Emitter {
// MAKE_CELL(undefined) 实例化,此处只补未捕获名。
self.instantiate_var_bindings(body_stmts, &mut ctx);
// 被捕获的函数名绑定同样建 cell(MAKE_CELL):值源是帧槽写入已置位的
// 名槽寄存器(函数对象),非 undefined。名槽寄存器此时已知(非抑制面
// 在名登记时分配、抑制面在 instantiate_var_bindings 回填)。
if let Some(fn_name) = ctx.function_name.as_deref() {
if let Some(&cell_idx) = ctx.captured_bindings.get(fn_name) {
if let Some(name_reg) = ctx.function_name_reg {
ctx.inst(Inst::new(
OpCode::MAKE_CELL,
Operand::Reg(name_reg),
Operand::Imm(cell_idx as u16),
Operand::None,
));
}
}
}
// 函数体是释放作用域:仅当体含 using 声明时发 mark/pop(无 using 的体
// 不发,防早退 return 跳过 POP 留未配对水位错乱外层对齐)。
let has_using = Self::scope_contains_using(body_stmts);
@@ -596,12 +630,18 @@ impl Emitter {
true
}
/// 参数 prologue:函数作用域 + 参数声明/解构 + 闭包捕获与 upvalue 分析。返回 param_base。
/// 参数 prologue:函数作用域 + 参数声明/解构 + 函数名不可写绑定登记 +
/// 闭包捕获与 upvalue 分析。返回 param_base。
///
/// `name` 为函数名(具名函数声明/表达式传名,箭头与匿名传 `None`):
/// 非抑制面在函数作用域登记不可写绑定并分配名槽寄存器(帧槽写入目标);
/// 抑制面(形参/var 同名、arguments 特例)不建名绑定,var 同名时名槽
/// 回填到 var 槽寄存器(`instantiate_var_bindings` 处)。
#[allow(clippy::too_many_arguments)]
fn emit_params_prologue<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
ctx: &mut CompileCtx, body_context: FunctionBodyContext, extra_capture_exprs: &[&'a Expression<'a>],
extra_upvalue_names: &[(&str, u8)],
extra_upvalue_names: &[(&str, u8)], name: Option<&str>,
) -> Result<u32, String> {
ctx.push_scope_with_kind(ScopeKind::FunctionScope);
let param_base = ctx.next_reg;
@@ -655,6 +695,48 @@ impl Emitter {
arguments_reg = Some(reg);
}
// 函数名不可写绑定(15.2.10.1 步 m):函数名在函数作用域登记为不可写
// 绑定,帧槽写入把它初始化为函数对象。抑制面(形参同名 / 体内容器 var
// 同名 / arguments 对象占位)不建名绑定:前两者名解析到参数/var 绑定,
// 后者 arguments 绑定占位(var 同名时由帧槽写入初始化为函数对象)。
// 名登记须先于 set_captured_bindings:嵌套函数捕获名分析依赖 own_bindings
// 完整(名被捕获时须识别为本函数绑定并建 cell)。
if let Some(fn_name) = name {
let var_names = collect_var_binding_names(body_stmts);
let suppressed = ctx.param_names.contains(fn_name)
|| var_names.contains(fn_name)
|| (fn_name == "arguments" && arguments_reg.is_some());
if !suppressed {
let reg = ctx.alloc_reg();
ctx.scopes
.symbols
.scopes
.last_mut()
.expect("函数作用域已压入")
.bindings
.insert(
fn_name.to_string(),
Binding {
reg,
initialized: true,
is_const: false,
lexical: false,
predeclared: false,
cell_idx: None,
non_writable: true,
},
);
ctx.own_bindings.insert(fn_name.to_string());
ctx.function_name = Some(fn_name.to_string());
ctx.function_name_reg = Some(reg);
} else if var_names.contains(fn_name) {
// 抑制面:var 绑定持名槽,帧槽写入把函数对象写入该 var 槽。
// var 绑定此时未声明(predeclare_var_declarations 在后),名槽
// 寄存器在 instantiate_var_bindings 回填。
ctx.function_name = Some(fn_name.to_string());
}
}
// 字段初始化表达式(值表达式)与参数默认值一并纳入捕获分析。
let mut capture_exprs: Vec<&oxide_parser::Expression> = param_defaults.clone();
capture_exprs.extend_from_slice(extra_capture_exprs);
@@ -844,6 +926,9 @@ impl Emitter {
.into_iter()
.filter(|n| !param_names.contains(&n.as_str()) && n != "arguments")
.filter(|n| ctx.captured_bindings.contains_key(n))
// 函数名(抑制面 var 同名)由专属 MAKE_CELL 处理(值源是帧槽写入的
// 名槽寄存器,非 undefined),此处排除以免误发 undefined 源。
.filter(|n| Some(n.as_str()) != ctx.function_name.as_deref())
.collect();
// HashSet 迭代序带随机种子,排序后入口 MAKE_CELL 发射序跨进程稳定。
var_names.sort();
@@ -926,6 +1011,17 @@ impl Emitter {
}
}
// 抑制面:函数名是 var 绑定,帧槽写入把它初始化为函数对象,入口 undefined
// 写须排除该名(否则覆盖帧槽写入的函数对象),并回填名槽寄存器供帧槽写入。
// 非抑制面名绑定不是 var(不在 name_set 内),remove 返回 false 不回填。
if let Some(fn_name) = ctx.function_name.as_deref() {
if name_set.remove(fn_name) {
if let Some(reg) = ctx.scopes.symbols.lookup_any(fn_name) {
ctx.function_name_reg = Some(reg);
}
}
}
let mut names: Vec<String> = name_set
.into_iter()
.filter(|n| !ctx.param_names.contains(n) && n != "arguments")
@@ -152,13 +152,13 @@ impl Emitter {
let body_stmts: &[Statement] = if let Some(body) = &fd.body { &body.statements } else { &[] };
let own_strict = fd.has_use_strict_directive();
let mut sub_module = if fd.generator && fd.r#async {
self.compile_async_generator_body(&param_names, body_stmts, ctx, own_strict)?
self.compile_async_generator_body(&param_names, body_stmts, ctx, own_strict, Some(name))?
} else if fd.generator {
self.compile_generator_body(&param_names, body_stmts, ctx, own_strict)?
self.compile_generator_body(&param_names, body_stmts, ctx, own_strict, Some(name))?
} else if fd.r#async {
self.compile_async_body(&param_names, body_stmts, ctx, false, false, own_strict)?
self.compile_async_body(&param_names, body_stmts, ctx, false, false, own_strict, Some(name))?
} else {
self.compile_function_body(&param_names, body_stmts, ctx, false, false, own_strict)?
self.compile_function_body(&param_names, body_stmts, ctx, false, false, own_strict, Some(name))?
};
sub_module.function_name = Some(name.to_string());
ctx.nested.push(sub_module);
+22
View File
@@ -34,6 +34,9 @@ pub(crate) struct Binding {
/// 函数级绑定由捕获分析的名字排序分配回填;块级遮蔽绑定在预声明点
/// 追加新索引。解析点按绑定实例取索引,同名多绑定各持各的 cell。
pub(crate) cell_idx: Option<u8>,
/// 函数名不可写绑定标志(与 `is_const` 正交):函数名绑定在 sloppy 模式
/// 写静默失败、strict 抛 TypeError,模式相关故独立于 const 的无条件抛错。
pub(crate) non_writable: bool,
}
/// 作用域符号表:名字 → 寄存器号/初始化状态/const 标志。
@@ -120,6 +123,7 @@ impl SymbolTable {
lexical: !matches!(kind, VariableDeclarationKind::Var),
predeclared: false,
cell_idx: None,
non_writable: false,
},
);
Ok(())
@@ -196,6 +200,7 @@ impl SymbolTable {
lexical: false,
predeclared: false,
cell_idx: None,
non_writable: false,
},
);
reg_for_new
@@ -214,6 +219,19 @@ impl SymbolTable {
false
}
/// 返回已初始化绑定是否为函数名不可写绑定(用于写路径守卫);未初始化/未找到返回 false。
pub fn lookup_non_writable(&self, name: &str) -> bool {
for scope in self.scopes.iter().rev() {
if let Some(b) = scope.bindings.get(name) {
if b.initialized {
return b.non_writable;
}
return false;
}
}
false
}
/// 声明并直接标记为已初始化;绑定已存在时仅补初始化标志(供预声明路径)。
pub fn declare_initialized(
&mut self, name: &str, reg: u32, kind: VariableDeclarationKind, is_const: bool,
@@ -238,6 +256,7 @@ impl SymbolTable {
lexical: !matches!(kind, VariableDeclarationKind::Var),
predeclared: false,
cell_idx: None,
non_writable: false,
},
);
Ok(())
@@ -252,6 +271,7 @@ impl SymbolTable {
lexical: false,
predeclared: false,
cell_idx: None,
non_writable: false,
});
}
@@ -278,6 +298,7 @@ impl SymbolTable {
lexical: !matches!(kind, VariableDeclarationKind::Var),
predeclared: true,
cell_idx: None,
non_writable: false,
},
);
Ok(())
@@ -339,6 +360,7 @@ impl SymbolTable {
lexical: true,
predeclared: false,
cell_idx: None,
non_writable: false,
},
);
self.aliases.insert(local.to_string(), base);