mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 运行时 cell TDZ 消息带上被访问绑定名
This commit is contained in:
Generated
+18
-18
@@ -1095,7 +1095,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_builtins"
|
name = "oxide_builtins"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"fast-float",
|
"fast-float",
|
||||||
@@ -1120,7 +1120,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_bytecode"
|
name = "oxide_bytecode"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1128,7 +1128,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cfg"
|
name = "oxide_cfg"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_emit",
|
"oxide_emit",
|
||||||
@@ -1139,7 +1139,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cli"
|
name = "oxide_cli"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
@@ -1159,7 +1159,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_code_cache"
|
name = "oxide_code_cache"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lru",
|
"lru",
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
@@ -1168,7 +1168,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_compiler"
|
name = "oxide_compiler"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1187,7 +1187,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_dce"
|
name = "oxide_dce"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1201,7 +1201,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_emit"
|
name = "oxide_emit"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
@@ -1216,7 +1216,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_ir"
|
name = "oxide_ir"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1225,7 +1225,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_kernel"
|
name = "oxide_kernel"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1240,7 +1240,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_liveness"
|
name = "oxide_liveness"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1252,7 +1252,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_log"
|
name = "oxide_log"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
@@ -1261,7 +1261,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_parser"
|
name = "oxide_parser"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
"oxc_ast",
|
"oxc_ast",
|
||||||
@@ -1276,7 +1276,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_regalloc"
|
name = "oxide_regalloc"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1292,7 +1292,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_runtime_api"
|
name = "oxide_runtime_api"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -1304,7 +1304,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_test262"
|
name = "oxide_test262"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_compiler",
|
"oxide_compiler",
|
||||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_types"
|
name = "oxide_types"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown 0.14.5",
|
||||||
@@ -1333,7 +1333,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_vm"
|
name = "oxide_vm"
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.8.16"
|
version = "0.8.17"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ pub struct CompiledModule {
|
|||||||
pub is_async: bool,
|
pub is_async: bool,
|
||||||
pub upvalue_captures: Vec<UpvalueCapture>,
|
pub upvalue_captures: Vec<UpvalueCapture>,
|
||||||
pub cells_needed: u8,
|
pub cells_needed: u8,
|
||||||
|
/// own cell 绑定名表:下标 = cell 下标,值 = 绑定名;未覆盖下标(合成
|
||||||
|
/// cell、块级预声明追加)留空串。与 `cells_needed` 同容量口径,供运行时
|
||||||
|
/// TDZ 消息取名。
|
||||||
|
pub cell_names: Vec<String>,
|
||||||
/// 全局扁平模块 id:编译末端 flatten 阶段分配(顶层 0,子模块 DFS 递增)。
|
/// 全局扁平模块 id:编译末端 flatten 阶段分配(顶层 0,子模块 DFS 递增)。
|
||||||
/// `CREATE_CLOSURE` 的 imm16 在 flatten 后即此 id,运行时以它为平表下标。
|
/// `CREATE_CLOSURE` 的 imm16 在 flatten 后即此 id,运行时以它为平表下标。
|
||||||
pub flat_id: u32,
|
pub flat_id: u32,
|
||||||
@@ -115,6 +119,7 @@ impl CompiledModule {
|
|||||||
is_async: false,
|
is_async: false,
|
||||||
upvalue_captures: Vec::new(),
|
upvalue_captures: Vec::new(),
|
||||||
cells_needed: 0,
|
cells_needed: 0,
|
||||||
|
cell_names: Vec::new(),
|
||||||
flat_id: 0,
|
flat_id: 0,
|
||||||
is_es_module: false,
|
is_es_module: false,
|
||||||
}
|
}
|
||||||
@@ -150,6 +155,7 @@ impl Clone for CompiledModule {
|
|||||||
is_async: self.is_async,
|
is_async: self.is_async,
|
||||||
upvalue_captures: self.upvalue_captures.clone(),
|
upvalue_captures: self.upvalue_captures.clone(),
|
||||||
cells_needed: self.cells_needed,
|
cells_needed: self.cells_needed,
|
||||||
|
cell_names: self.cell_names.clone(),
|
||||||
flat_id: self.flat_id,
|
flat_id: self.flat_id,
|
||||||
is_es_module: self.is_es_module,
|
is_es_module: self.is_es_module,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -931,6 +931,22 @@ impl CompileCtx {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
// 容量覆盖全部已分配 cell 索引:函数级名字排序索引(映射值 0..n-1)
|
||||||
|
// 与追加索引(计数器,块级预声明 / 合成 cell)取较大者加一;别名
|
||||||
|
// 捕获合并让多个名字共享同一 cell,下标可重复。
|
||||||
|
let cells_needed = self
|
||||||
|
.captured_bindings
|
||||||
|
.values()
|
||||||
|
.copied()
|
||||||
|
.max()
|
||||||
|
.map_or(0, |m| m.saturating_add(1))
|
||||||
|
.max(self.next_cell_idx);
|
||||||
|
// own cell 绑定名表:下标 = cell 下标,值 = 绑定名;合成 cell 与块级
|
||||||
|
// 预声明追加无源名,留空串。
|
||||||
|
let mut cell_names = vec![String::new(); cells_needed as usize];
|
||||||
|
for (name, idx) in &self.captured_bindings {
|
||||||
|
cell_names[*idx as usize] = name.clone();
|
||||||
|
}
|
||||||
IRFunction {
|
IRFunction {
|
||||||
insts: std::mem::take(&mut self.insts),
|
insts: std::mem::take(&mut self.insts),
|
||||||
label_pos: std::mem::take(&mut self.labels.label_pos),
|
label_pos: std::mem::take(&mut self.labels.label_pos),
|
||||||
@@ -939,16 +955,8 @@ impl CompileCtx {
|
|||||||
param_layout,
|
param_layout,
|
||||||
builtin_reg_map: std::mem::take(&mut self.scopes.builtin_reg_map),
|
builtin_reg_map: std::mem::take(&mut self.scopes.builtin_reg_map),
|
||||||
upvalue_captures,
|
upvalue_captures,
|
||||||
// 容量覆盖全部已分配 cell 索引:函数级名字排序索引(映射值 0..n-1)
|
cells_needed,
|
||||||
// 与追加索引(计数器,块级预声明 / 合成 cell)取较大者加一;别名
|
cell_names,
|
||||||
// 捕获合并让多个名字共享同一 cell,下标可重复。
|
|
||||||
cells_needed: self
|
|
||||||
.captured_bindings
|
|
||||||
.values()
|
|
||||||
.copied()
|
|
||||||
.max()
|
|
||||||
.map_or(0, |m| m.saturating_add(1))
|
|
||||||
.max(self.next_cell_idx),
|
|
||||||
n_registers: self.max_regs,
|
n_registers: self.max_regs,
|
||||||
is_arrow: false,
|
is_arrow: false,
|
||||||
is_class_constructor: false,
|
is_class_constructor: false,
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ pub struct IRFunction {
|
|||||||
// closures 域
|
// closures 域
|
||||||
pub upvalue_captures: Vec<UpvalueCapture>,
|
pub upvalue_captures: Vec<UpvalueCapture>,
|
||||||
pub cells_needed: u8,
|
pub cells_needed: u8,
|
||||||
|
/// own cell 绑定名表:下标 = cell 下标,值 = 绑定名;未覆盖下标留空串。
|
||||||
|
/// 与 `cells_needed` 同容量口径,供运行时 TDZ 消息取名。
|
||||||
|
pub cell_names: Vec<String>,
|
||||||
// meta 域
|
// meta 域
|
||||||
pub n_registers: u32,
|
pub n_registers: u32,
|
||||||
pub is_arrow: bool,
|
pub is_arrow: bool,
|
||||||
@@ -85,6 +88,7 @@ impl IRFunction {
|
|||||||
builtin_reg_map: Vec::new(),
|
builtin_reg_map: Vec::new(),
|
||||||
upvalue_captures: Vec::new(),
|
upvalue_captures: Vec::new(),
|
||||||
cells_needed: 0,
|
cells_needed: 0,
|
||||||
|
cell_names: Vec::new(),
|
||||||
n_registers: 0,
|
n_registers: 0,
|
||||||
is_arrow: false,
|
is_arrow: false,
|
||||||
is_class_constructor: false,
|
is_class_constructor: false,
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ pub fn lower(f: &IRFunction) -> Result<CompiledModule, String> {
|
|||||||
is_async: f.is_async,
|
is_async: f.is_async,
|
||||||
upvalue_captures: f.upvalue_captures.clone(),
|
upvalue_captures: f.upvalue_captures.clone(),
|
||||||
cells_needed: f.cells_needed,
|
cells_needed: f.cells_needed,
|
||||||
|
cell_names: f.cell_names.clone(),
|
||||||
flat_id: 0,
|
flat_id: 0,
|
||||||
is_es_module: false,
|
is_es_module: false,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
let c = unsafe { &*current[cell_idx] };
|
let c = unsafe { &*current[cell_idx] };
|
||||||
if !c.is_initialized() {
|
if !c.is_initialized() {
|
||||||
return self.raise_error_kind("ReferenceError", "Cannot access variable before initialization");
|
return self.raise_error_kind("ReferenceError", &Self::tdz_message(self.tdz_binding_name(cell_idx, false)));
|
||||||
}
|
}
|
||||||
self.regs[rd] = c.value;
|
self.regs[rd] = c.value;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -376,7 +376,8 @@ impl Vm {
|
|||||||
let cell = &mut *cell_ptr;
|
let cell = &mut *cell_ptr;
|
||||||
// TDZ 写检查:cell 未初始化(捕获绑定声明点前)写抛 ReferenceError。
|
// TDZ 写检查:cell 未初始化(捕获绑定声明点前)写抛 ReferenceError。
|
||||||
if !cell.is_initialized() {
|
if !cell.is_initialized() {
|
||||||
return self.raise_error_kind("ReferenceError", "Cannot access variable before initialization");
|
return self
|
||||||
|
.raise_error_kind("ReferenceError", &Self::tdz_message(self.tdz_binding_name(cell_idx, false)));
|
||||||
}
|
}
|
||||||
cell.value = src_val;
|
cell.value = src_val;
|
||||||
cell.set_initialized(true);
|
cell.set_initialized(true);
|
||||||
@@ -390,6 +391,36 @@ impl Vm {
|
|||||||
self.frames.last().map(|f| f.callee).or(self.inline_callee)
|
self.frames.last().map(|f| f.callee).or(self.inline_callee)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TDZ 消息的绑定名:解析当前函数对象的编译模块,按站点类型取名
|
||||||
|
/// (upvalue 站点查 upvalue_captures,own cell 站点查 cell_names)。
|
||||||
|
///
|
||||||
|
/// # 边界
|
||||||
|
/// - 无当前函数对象(顶层脚本直读)、非对象 callee、模块解析缺失或下标
|
||||||
|
/// 越界时返回 None,调用方回退通用文本。
|
||||||
|
fn tdz_binding_name(&self, idx: usize, is_upvalue: bool) -> Option<&str> {
|
||||||
|
let callee = self.current_callee()?;
|
||||||
|
if !callee.is_object() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// callee 是 GC 根,对象在取名期间稳定。
|
||||||
|
let obj = unsafe { &*callee.as_js_object_ptr() };
|
||||||
|
let module = self.callee_module(obj)?;
|
||||||
|
if is_upvalue {
|
||||||
|
module.upvalue_captures.get(idx).map(|c| c.name.as_str())
|
||||||
|
} else {
|
||||||
|
module.cell_names.get(idx).filter(|n| !n.is_empty()).map(|n| n.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TDZ 消息:取到绑定名时用 `Cannot access 'x' before initialization` 形态,
|
||||||
|
/// 取不到时回退通用文本。
|
||||||
|
fn tdz_message(name: Option<&str>) -> String {
|
||||||
|
match name {
|
||||||
|
Some(n) => format!("Cannot access '{n}' before initialization"),
|
||||||
|
None => String::from("Cannot access variable before initialization"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 读取当前闭包的 upvalue:命中 cell 判初始化后取值,未命中委托惰性建 cell。
|
/// 读取当前闭包的 upvalue:命中 cell 判初始化后取值,未命中委托惰性建 cell。
|
||||||
///
|
///
|
||||||
/// # 步骤
|
/// # 步骤
|
||||||
@@ -413,7 +444,8 @@ impl Vm {
|
|||||||
if !cell.is_null() {
|
if !cell.is_null() {
|
||||||
let c = unsafe { &*cell };
|
let c = unsafe { &*cell };
|
||||||
if !c.is_initialized() {
|
if !c.is_initialized() {
|
||||||
return self.raise_error_kind("ReferenceError", "Cannot access variable before initialization");
|
return self
|
||||||
|
.raise_error_kind("ReferenceError", &Self::tdz_message(self.tdz_binding_name(uv_idx, true)));
|
||||||
}
|
}
|
||||||
self.regs[rd] = c.value;
|
self.regs[rd] = c.value;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -483,7 +515,10 @@ impl Vm {
|
|||||||
let cell = &mut *upvals[uv_idx];
|
let cell = &mut *upvals[uv_idx];
|
||||||
// TDZ 写检查:cell 未初始化(捕获绑定声明点前)写抛 ReferenceError。
|
// TDZ 写检查:cell 未初始化(捕获绑定声明点前)写抛 ReferenceError。
|
||||||
if !cell.is_initialized() {
|
if !cell.is_initialized() {
|
||||||
self.raise_error_kind("ReferenceError", "Cannot access variable before initialization")?;
|
self.raise_error_kind(
|
||||||
|
"ReferenceError",
|
||||||
|
&Self::tdz_message(self.tdz_binding_name(uv_idx, true)),
|
||||||
|
)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
// const guard:TDZ 检查后 cell 必已初始化(存在性判定,与值无关),
|
// const guard:TDZ 检查后 cell 必已初始化(存在性判定,与值无关),
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
//! TDZ 错误消息绑定名钉:运行时 cell 的四个 TDZ 抛点(own cell 读/写、upvalue
|
||||||
|
//! 读/写)消息须携带被访问绑定名(`Cannot access 'x' before initialization`,
|
||||||
|
//! 与 node 口径一致);取不到名时回退通用文本。每站点各钉一例:闭包读未初始化
|
||||||
|
//! 捕获 let(LOAD_UPVALUE)、闭包写未初始化捕获 let(STORE_UPVALUE)、同函数块级
|
||||||
|
//! let 声明前读(CELL_GET)、同函数块级 let 声明前写(CELL_SET)。
|
||||||
|
//!
|
||||||
|
//! 块级 let 用例的被捕获绑定须被嵌套闭包引用(捕获名跳过静态 TDZ 守卫、留运行时
|
||||||
|
//! 判定),故源例均含一个引用该绑定的嵌套闭包。顶层直读(无函数对象)取不到名,
|
||||||
|
//! 回退通用文本,属已知残面,单独钉住。
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use oxide_compiler::compiler::Compiler;
|
||||||
|
use oxide_vm::vm::Vm;
|
||||||
|
|
||||||
|
fn eval(source: &str) -> Result<oxide_types::value::JsValue, String> {
|
||||||
|
let allocator = oxide_parser::Allocator::default();
|
||||||
|
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("parse error: {}", e[0].message))?;
|
||||||
|
let module = Compiler::new().compile(&program).map_err(|e| format!("compile error: {e}"))?;
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
vm.run(&Arc::new(module))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 断言程序抛错且未捕获错误消息含带名 TDZ 形态。
|
||||||
|
fn assert_tdz_message(source: &str, binding: &str) {
|
||||||
|
let err = match eval(source) {
|
||||||
|
Ok(v) => panic!("expected a TDZ throw, got completion value {v:?}\nsource: {source}"),
|
||||||
|
Err(e) => e,
|
||||||
|
};
|
||||||
|
let expected = format!("Cannot access '{binding}' before initialization");
|
||||||
|
assert!(err.contains(&expected), "expected {expected} in: {err}\nsource: {source}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tdz_upvalue_read_message_carries_binding_name() {
|
||||||
|
// 闭包读未初始化捕获 let(LOAD_UPVALUE 站点):消息带绑定名。
|
||||||
|
assert_tdz_message("function f() { return x; } f(); let x = 1", "x");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tdz_upvalue_write_message_carries_binding_name() {
|
||||||
|
// 闭包写未初始化捕获 let(STORE_UPVALUE 站点):消息带绑定名。
|
||||||
|
assert_tdz_message("function f() { x = 1; } f(); let x = 1", "x");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tdz_cell_read_message_carries_binding_name() {
|
||||||
|
// 同函数块级 let 声明前读(CELL_GET 站点):绑定被块内闭包捕获,静态守卫
|
||||||
|
// 跳过,声明前读经运行时 cell 抛带名 TDZ。
|
||||||
|
assert_tdz_message("function f() { const g = () => x; return x; let x; } f()", "x");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tdz_cell_write_message_carries_binding_name() {
|
||||||
|
// 同函数块级 let 声明前写(CELL_SET 站点):同读例的捕获形态。
|
||||||
|
assert_tdz_message("function f() { const g = () => x; x = 1; let x; } f()", "x");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tdz_top_level_read_falls_back_to_generic_message() {
|
||||||
|
// 顶层直读(无函数对象)取不到绑定名:回退通用文本(已知残面钉)。
|
||||||
|
let err = match eval("function f() { return x; } x; let x = 1") {
|
||||||
|
Ok(v) => panic!("expected a TDZ throw, got completion value {v:?}"),
|
||||||
|
Err(e) => e,
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
err.contains("Cannot access variable before initialization"),
|
||||||
|
"expected generic TDZ message in: {err}"
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user