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
Generated
+19 -19
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1226,7 +1226,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1235,7 +1235,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1255,7 +1255,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1269,7 +1269,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1284,7 +1284,7 @@ dependencies = [
[[package]]
name = "oxide_engine"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1296,7 +1296,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1305,7 +1305,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"bumpalo",
"dashmap",
@@ -1319,7 +1319,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1331,7 +1331,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"tracing",
"tracing-appender",
@@ -1340,7 +1340,7 @@ dependencies = [
[[package]]
name = "oxide_parser"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1355,7 +1355,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1371,7 +1371,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"num-bigint",
"num-traits",
@@ -1384,7 +1384,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1402,7 +1402,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"bumpalo",
"dashmap",
@@ -1415,7 +1415,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.13.19"
version = "0.13.20"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -23,7 +23,7 @@ members = [
]
[workspace.package]
version = "0.13.19"
version = "0.13.20"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+5
View File
@@ -66,6 +66,9 @@ pub struct CompiledModule {
pub captured_this_const_idx: u16,
/// 由赋值上下文推断的函数名,在变量声明 / 对象属性赋值点设置。
pub function_name: Option<String>,
/// 函数名不可写绑定的寄存器(帧槽写入目标):压帧时把 callee 函数对象写入该槽。
/// `None` 表示无名绑定(匿名函数 / 形参或 var 同名抑制 / arguments 特例)。
pub function_name_reg: Option<u32>,
/// 函数 `length` 属性值:第一个带默认值/解构默认的形参之前的形参数(rest 不计)。
pub function_length: u32,
/// 形参列表是否 simple(全部无初始值标识符且无 rest):arguments 对象
@@ -110,6 +113,7 @@ impl CompiledModule {
is_strict: false,
captured_this_const_idx: 0,
function_name: None,
function_name_reg: None,
function_length: 0,
has_simple_params: false,
is_class_constructor: false,
@@ -146,6 +150,7 @@ impl Clone for CompiledModule {
is_strict: self.is_strict,
captured_this_const_idx: self.captured_this_const_idx,
function_name: self.function_name.clone(),
function_name_reg: self.function_name_reg,
function_length: self.function_length,
has_simple_params: self.has_simple_params,
is_class_constructor: self.is_class_constructor,
+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);
+4
View File
@@ -62,6 +62,9 @@ pub struct IRFunction {
pub is_strict: bool,
pub captured_this_const_idx: u16,
pub function_name: Option<String>,
/// 函数名不可写绑定的寄存器(帧槽写入目标):压帧时把 callee 函数对象写入该槽。
/// `None` 表示无名绑定(匿名函数 / 形参或 var 同名抑制 / arguments 特例)。
pub function_name_reg: Option<u32>,
/// 函数 `length` 属性值:第一个带默认值/解构默认的形参之前的形参数(rest 不计)。
pub function_length: u32,
/// 形参列表是否 simple(全部无初始值标识符且无 rest):arguments 对象
@@ -99,6 +102,7 @@ impl IRFunction {
is_strict: false,
captured_this_const_idx: 0,
function_name: None,
function_name_reg: None,
function_length: 0,
has_simple_params: false,
is_top_level: false,
+1
View File
@@ -89,6 +89,7 @@ pub fn lower(f: &IRFunction) -> Result<CompiledModule, String> {
is_strict: f.is_strict,
captured_this_const_idx: f.captured_this_const_idx,
function_name: f.function_name.clone(),
function_name_reg: f.function_name_reg,
function_length: f.function_length,
has_simple_params: f.has_simple_params,
is_class_constructor: f.is_class_constructor,
+9
View File
@@ -58,6 +58,15 @@ pub(super) fn run(f: &mut IRFunction, map: &AllocMap) {
// 直接槽引用(class field 计算键等)的 escaped vreg 已由预着色恒等保留。
// 注:cell 捕获变量的父 vreg 可被 RegAlloc 移动(MAKE_CELL 后值入 cell,与寄存器无关),
// 此处不做恒等断言。
// 函数名不可写绑定寄存器回写:vreg → 物理槽(与 builtin_reg_map 同口径)。
// 映射缺失(名槽无字节码引用,帧槽写入为死写)时置 None,VM 侧跳过写入。
if let Some(vreg) = f.function_name_reg {
f.function_name_reg = match map.map.get(&vreg) {
Some(Alloc::Phys(p)) => Some(*p),
_ => None,
};
}
}
#[cfg(test)]
+7
View File
@@ -293,6 +293,13 @@ impl Vm {
};
self.regs[sub_param_base + i] = v;
}
// 函数名不可写绑定帧槽写入:把 callee 函数对象写入名槽寄存器(压帧时置位,
// 函数体入口的 MAKE_CELL / 读取据此取函数对象)。无名绑定(None)不写。
// 须在 this 绑定之前:名槽寄存器与 this 槽(254)互不重叠,但集中在此
// 保持帧初始化顺序清晰。
if let Some(name_reg) = sub.function_name_reg {
self.regs[name_reg as usize] = callee;
}
// this 绑定:箭头函数恒用词法捕获;sloppy 普通函数 this 为 null/undefined 时
// 替换为全局对象,其余原始值经 ToObject 盒装(ECMA-262 10.4.3);严格模式
// this 原样保留。
+67
View File
@@ -17,6 +17,15 @@ fn assert_num(result: JsValue, expected: f64) {
assert!((actual - expected).abs() < 0.0001, "expected {expected}, got {actual}");
}
/// 判断值是否为函数对象:经对象指针解出 header 函数标志,非对象返回假。
fn is_function_value(v: JsValue) -> bool {
let ptr = v.as_js_object_ptr();
if ptr.is_null() {
return false;
}
unsafe { (*ptr).is_function() }
}
// --- Function Declaration Basics ---
#[test]
@@ -218,3 +227,61 @@ fn function_default_prototype_accepts_member_assignment() {
let rendered = vm.lookup_str(result).unwrap_or_default();
assert_eq!(rendered, "ok");
}
// ── 函数名不可写局部绑定(函数作用域内读回函数对象)──
#[test]
fn fn_name_binding_readable() {
let mut vm = Vm::new();
// 函数名登记进函数作用域,体内裸读 f 解析到名绑定,读回函数对象本身。
let result = eval(&mut vm, "function f() { return f; } f()").unwrap();
assert!(is_function_value(result), "expected the function itself, got: {:?}", result);
}
#[test]
fn fn_name_binding_shadows_outer_var() {
let mut vm = Vm::new();
// 外层 var f 持 'x';函数表达式名绑定 f 在函数作用域内优先,体内读回函数对象而非 'x'。
let result = eval(&mut vm, "var f = 'x'; var g = function f() { return typeof f; }; g()").unwrap();
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "function");
}
#[test]
fn fn_name_var_suppression() {
let mut vm = Vm::new();
// 体内容器 var f 同名:不建不可写绑定,var 绑定由帧槽写入初始化为函数对象。
let result = eval(&mut vm, "function f() { var f; return f; } f()").unwrap();
assert!(is_function_value(result), "expected the function, got: {:?}", result);
}
#[test]
fn fn_name_param_priority() {
let mut vm = Vm::new();
// 形参同名:形参绑定持有实参,名绑定被抑制,读回实参值。
let result = eval(&mut vm, "function f(f) { return f; } f(1)").unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn fn_name_capture_side() {
let mut vm = Vm::new();
// 嵌套函数捕获外层名绑定(upvalue),内层读回外层函数对象。
let result = eval(&mut vm, "function f() { return function() { return f; }; } f()()").unwrap();
assert!(is_function_value(result), "expected the outer function, got: {:?}", result);
}
#[test]
fn fn_name_arguments_special_case() {
let mut vm = Vm::new();
// 名等于 arguments 且 arguments 对象已建:抑制名绑定,arguments 解析到 arguments 对象。
let result = eval(&mut vm, "function arguments() { return arguments; } arguments()").unwrap();
assert!(result.is_object(), "expected the arguments object, got: {:?}", result);
}
#[test]
fn fn_name_recursive_call() {
let mut vm = Vm::new();
// 递归调用依赖名绑定读回函数对象再调用,覆盖寄存器写回回归面。
let result = eval(&mut vm, "function fact(n) { return n <= 1 ? 1 : n * fact(n - 1); } fact(5)").unwrap();
assert_num(result, 120.0);
}
@@ -390,8 +390,9 @@ fn cross_module_aliased_imports_read_live_value() {
assert_eq!(result, "1|1|2|2", "一源两别名未同步读到重赋后的活值");
}
/// 默认导出具名函数体的自引用重赋:函数体内 `fn = 2` 写模块级绑定,
/// 导入方再次读 default 得新值(含捕获分析下探 export 声明与共享 cell)。
/// 默认导出具名函数体的自引用重赋:函数名 `fn` 在函数作用域登记为不可写
/// 绑定(15.2.10.1 步 m),遮蔽模块级同名绑定;体内容器 `fn = 2` 落在函数
/// 作用域名绑定,模块级绑定不被改写,导入方读 default 仍得函数。
#[test]
fn cross_module_default_export_reads_live_value() {
let cwd = std::env::current_dir().expect("cwd");
@@ -402,11 +403,11 @@ fn cross_module_default_export_reads_live_value() {
dir.join("main.mjs"),
"import val from './dep.mjs';\n \
const ret = val();\n \
globalThis.__ns = [ret, val].join('|');",
globalThis.__ns = [ret, typeof val].join('|');",
)
.expect("write main");
let _cleanup = Cleanup(dir.clone());
let result = run_namespace_module(&dir);
assert_eq!(result, "1|2", "default 导出的具名函数自引用重赋未活读");
assert_eq!(result, "1|function", "函数名不可写绑定遮蔽模块级绑定,自引用重赋静默失败");
}