mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 默认参数自引用与后位引用读抛 TDZ ReferenceError
This commit is contained in:
Generated
+19
-19
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1226,7 +1226,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1235,7 +1235,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1255,7 +1255,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1269,7 +1269,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1284,7 +1284,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_engine"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1296,7 +1296,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1305,7 +1305,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1319,7 +1319,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1331,7 +1331,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1340,7 +1340,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1355,7 +1355,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1371,7 +1371,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1384,7 +1384,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1402,7 +1402,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1415,7 +1415,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.13.22"
|
||||
version = "0.13.23"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -80,6 +80,10 @@ pub struct CompileCtx {
|
||||
/// 形参同名时不建外层绑定、不求值写回(规范 paramNames 守卫面)。子函数
|
||||
/// ctx 为新建,不继承。
|
||||
pub(crate) param_names: HashSet<String>,
|
||||
/// 参数序言默认值发射期的形参名 TDZ 集:当前形参自身名(含解构叶)加其后
|
||||
/// 全部形参名。发射期临时置位,每形参处理完即清空;重名形参不建集(规范
|
||||
/// 变量环境面无 TDZ)。标识符静态读入口命中此集时发射运行时 ReferenceError。
|
||||
pub(crate) param_tdz_names: HashSet<String>,
|
||||
/// 块级函数名按 sloppy 模式下与浏览器/web 实现惯例兼容的行为建外层绑定的抑制集
|
||||
/// (形参 ∪ 函数作用域树内词法声明名):抑制集内名字退化为纯块作用域;预声明期定稿,实例化与声明点写回两侧同查。
|
||||
pub(crate) block_fn_suppressed: HashSet<String>,
|
||||
@@ -281,6 +285,7 @@ impl CompileCtx {
|
||||
current_upvalue_captures: Vec::new(),
|
||||
own_bindings: HashSet::new(),
|
||||
param_names: HashSet::new(),
|
||||
param_tdz_names: HashSet::new(),
|
||||
block_fn_suppressed: HashSet::new(),
|
||||
block_fn_entry_mats: Vec::new(),
|
||||
captured_bindings: BTreeMap::new(),
|
||||
|
||||
@@ -16,6 +16,14 @@ impl Emitter {
|
||||
/// - 活读映射仅模块顶层 ctx 填充:嵌套函数内读 import 名仍走链接期快照
|
||||
/// (`current_upvalue_captures` 不在此列,闭包内不继承映射)。
|
||||
fn emit_static_identifier_read(&self, name: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
// 形参名 TDZ:默认值表达式内引用当前形参与其后形参是形参环境未初始化
|
||||
// 绑定,运行期抛 ReferenceError(重名形参无 TDZ,编译期集为空)。判定置于
|
||||
// 读入口最前:被捕获参数在默认值发射期经 visible_cell 回退命中 CELL_GET,
|
||||
// 判定后置即漏判。
|
||||
if ctx.param_tdz_names.contains(name) {
|
||||
return self.emit_tdz_throw(&format!("Cannot access '{name}' before initialization"), ctx);
|
||||
}
|
||||
|
||||
// 可重赋依赖的命名/默认导入:读点直接查依赖命名空间当前值,跟随源模块重赋。
|
||||
if let Some((dep_ns, exported)) = ctx.module_live_imports.get(name).cloned() {
|
||||
let name_reg = self.load_string_const(&exported, ctx);
|
||||
@@ -107,9 +115,12 @@ impl Emitter {
|
||||
ctx.inst(Inst::jmp(end_label));
|
||||
|
||||
ctx.labels.set_label_pos(fallback_label, ctx.insts.len());
|
||||
// 回退:upvalue / 被捕获 cell / 静态绑定 / 未定义四选一。
|
||||
// 对象无该属性时才走此处,语义与静态解析一致。
|
||||
if let Some((uv_idx, _)) = ctx.current_upvalue_captures.iter().enumerate().find(|(_, u)| u.name == name) {
|
||||
// 回退:形参名 TDZ / upvalue / 被捕获 cell / 静态绑定 / 未定义五选一。
|
||||
// 对象无该属性时才走此处,语义与静态解析一致。TDZ 判定置于回退入口最前:
|
||||
// 被捕获参数经 visible_cell 回退命中 CELL_GET,判定后置即漏判。
|
||||
if ctx.param_tdz_names.contains(name) {
|
||||
let _ = self.emit_tdz_throw(&format!("Cannot access '{name}' before initialization"), ctx);
|
||||
} else if let Some((uv_idx, _)) = ctx.current_upvalue_captures.iter().enumerate().find(|(_, u)| u.name == name) {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_UPVALUE,
|
||||
Operand::Reg(result_reg),
|
||||
|
||||
@@ -860,7 +860,41 @@ impl Emitter {
|
||||
ctx.inst(Inst::create_arguments(Operand::Reg(reg)));
|
||||
}
|
||||
|
||||
for spec in param_specs {
|
||||
// 形参名 TDZ 集:按形参序收集每形参绑定名(含解构叶),重名面不建集
|
||||
// (规范变量环境面无 TDZ)。默认值发射期读路径命中此集抛 ReferenceError。
|
||||
let param_bound_names: Vec<Vec<String>> = param_specs
|
||||
.iter()
|
||||
.map(|spec| match spec {
|
||||
ParamSpec::Identifier { name, .. } | ParamSpec::Rest { name } => vec![name.clone()],
|
||||
ParamSpec::Pattern { pattern, .. } => {
|
||||
let mut names = HashSet::new();
|
||||
collect_binding_pattern_names(pattern, &mut names);
|
||||
names.into_iter().collect()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let has_param_duplicates = {
|
||||
let total: usize = param_bound_names.iter().map(|v| v.len()).sum();
|
||||
let mut seen = HashSet::new();
|
||||
for names in ¶m_bound_names {
|
||||
for n in names {
|
||||
seen.insert(n.clone());
|
||||
}
|
||||
}
|
||||
seen.len() < total
|
||||
};
|
||||
|
||||
for (idx, spec) in param_specs.iter().enumerate() {
|
||||
// 循环首置 TDZ 集:当前形参自身名(含解构叶)加其后全部形参名;
|
||||
// 重名面保持空集。
|
||||
if !has_param_duplicates {
|
||||
ctx.param_tdz_names.clear();
|
||||
ctx.param_tdz_names.extend(param_bound_names[idx].iter().cloned());
|
||||
for subsequent in ¶m_bound_names[idx + 1..] {
|
||||
ctx.param_tdz_names.extend(subsequent.iter().cloned());
|
||||
}
|
||||
}
|
||||
|
||||
match spec {
|
||||
ParamSpec::Pattern {
|
||||
synthetic_name,
|
||||
@@ -890,6 +924,9 @@ impl Emitter {
|
||||
ctx.inst(Inst::create_rest_array(Operand::Reg(reg), fixed_count));
|
||||
}
|
||||
}
|
||||
|
||||
// 每形参处理完即清空,防 TDZ 集泄漏到 body 读取。
|
||||
ctx.param_tdz_names.clear();
|
||||
}
|
||||
|
||||
// 被捕获的参数也必须建 cell(MAKE_CELL):否则子函数经 lazy upvalue 路径读
|
||||
|
||||
@@ -285,3 +285,83 @@ fn fn_name_recursive_call() {
|
||||
let result = eval(&mut vm, "function fact(n) { return n <= 1 ? 1 : n * fact(n - 1); } fact(5)").unwrap();
|
||||
assert_num(result, 120.0);
|
||||
}
|
||||
|
||||
// --- 默认参数自引用 TDZ 守卫 ---
|
||||
|
||||
/// 断言运行期抛出未捕获 ReferenceError(默认参数 TDZ 守卫的期望形态)。
|
||||
fn assert_dflt_tdz_reference_error(source: &str, msg: &str) {
|
||||
let err = eval(&mut Vm::new(), source).unwrap_err();
|
||||
assert!(
|
||||
err.contains("ReferenceError"),
|
||||
"{msg}: expected an uncaught ReferenceError, got: {err}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dflt_param_self_ref_throws() {
|
||||
// 默认值自引用当前形参:形参环境未初始化绑定,运行期抛 ReferenceError。
|
||||
assert_dflt_tdz_reference_error("function f(x = x) {} f()", "self-ref");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dflt_param_ref_later_throws() {
|
||||
// 默认值引用后位形参:后位形参在默认值求值期尚未初始化,抛 ReferenceError。
|
||||
assert_dflt_tdz_reference_error("function f(x = y, y) {} f()", "ref-later");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dflt_param_ref_prior_legal() {
|
||||
// 默认值引用前位形参:前位形参已初始化,读取合法,y 得 x 的值。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "function f(x, y = x) { return y; } f(3)").unwrap();
|
||||
assert_eq!(result.as_int(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dflt_param_arg_defined_no_eval() {
|
||||
// 实参非 undefined:默认值不求值,自引用不抛。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "function f(x = x) { return x; } f(1)").unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dflt_param_object_dstr_self_ref_throws() {
|
||||
// 对象解构自引用抛 ReferenceError。
|
||||
assert_dflt_tdz_reference_error("function f({x = x}) {} f({})", "object-dstr-self");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dflt_param_arguments_not_in_tdz() {
|
||||
// arguments 不在 TDZ 集:默认值引用 arguments 合法。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "function f(x = arguments[1]) { return x; } f(undefined, 2)").unwrap();
|
||||
assert_eq!(result.as_int(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dflt_param_nested_fn_no_throw() {
|
||||
// 默认值为嵌套函数:嵌套函数体在子 ctx 编译,经 upvalue 读初始化后的值,不抛。
|
||||
// 闭包调用后读回 x 为默认值函数对象本身(非 undefined)。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "function f(x = function() { return x; }) { return x; } f()()").unwrap();
|
||||
assert!(is_function_value(result), "expected the default function, got: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dflt_param_typeof_self_ref_throws() {
|
||||
// typeof 对 TDZ 绑定抛 ReferenceError(经 emit_expression 同形)。
|
||||
assert_dflt_tdz_reference_error("function f(x = typeof x) {} f()", "typeof-self");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dflt_param_generator_self_ref_throws() {
|
||||
// 生成器默认值自引用抛 ReferenceError(参数初始化在调用时刻)。
|
||||
assert_dflt_tdz_reference_error("function* g(x = x) {} g()", "generator-self");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dflt_param_strict_self_ref_throws() {
|
||||
// 严格模式默认值自引用同样抛 ReferenceError(TDZ 与模式无关)。
|
||||
assert_dflt_tdz_reference_error("'use strict'; function f(x = x) {} f()", "strict-self");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user