mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 03:01:12 +08:00
fix: 顶层 var 读写直连全局对象属性单一真值——GDI 序言 + 声明处同步删除,类原型 session 直分防晋升克隆 identity 分裂,自增减 int 保持
This commit is contained in:
@@ -27,6 +27,26 @@ fn int_sub(a: i32, b: i32) -> JsValue {
|
||||
}
|
||||
}
|
||||
|
||||
/// int 自增:结果落在 i32 范围则保 int,否则升 double(与 int_add 同族语义,
|
||||
/// 保持与 ADD 路径一致的 int 保持行为)。
|
||||
#[inline(always)]
|
||||
fn int_inc(a: i32) -> JsValue {
|
||||
match a.checked_add(1) {
|
||||
Some(v) => JsValue::int(v),
|
||||
None => JsValue::float(a as f64 + 1.0),
|
||||
}
|
||||
}
|
||||
|
||||
/// int 自减:结果落在 i32 范围则保 int,否则升 double(与 int_sub 同族语义,
|
||||
/// 保持与 SUB 路径一致的 int 保持行为)。
|
||||
#[inline(always)]
|
||||
fn int_dec(a: i32) -> JsValue {
|
||||
match a.checked_sub(1) {
|
||||
Some(v) => JsValue::int(v),
|
||||
None => JsValue::float(a as f64 - 1.0),
|
||||
}
|
||||
}
|
||||
|
||||
/// int+int 乘法:结果落在 i32 范围则保 int,否则升 double。
|
||||
#[inline(always)]
|
||||
fn int_mul(a: i32, b: i32) -> JsValue {
|
||||
@@ -538,6 +558,12 @@ impl Vm {
|
||||
self.regs[a] = result;
|
||||
return Ok(());
|
||||
}
|
||||
if self.regs[rd].is_int() {
|
||||
let result = int_inc(self.regs[rd].as_int());
|
||||
self.regs[rd] = result;
|
||||
self.regs[a] = result;
|
||||
return Ok(());
|
||||
}
|
||||
let n = self.coerce_number_bounded(self.regs[rd])?;
|
||||
let result = JsValue::float(n + 1.0);
|
||||
self.regs[rd] = result;
|
||||
@@ -554,6 +580,12 @@ impl Vm {
|
||||
self.regs[rd] = self.new_bigint(v + 1);
|
||||
return Ok(());
|
||||
}
|
||||
if self.regs[rd].is_int() {
|
||||
let old = self.regs[rd];
|
||||
self.regs[a] = old;
|
||||
self.regs[rd] = int_inc(old.as_int());
|
||||
return Ok(());
|
||||
}
|
||||
let n = self.coerce_number_bounded(self.regs[rd])?;
|
||||
self.regs[a] = JsValue::float(n);
|
||||
self.regs[rd] = JsValue::float(n + 1.0);
|
||||
@@ -570,6 +602,12 @@ impl Vm {
|
||||
self.regs[a] = result;
|
||||
return Ok(());
|
||||
}
|
||||
if self.regs[rd].is_int() {
|
||||
let result = int_dec(self.regs[rd].as_int());
|
||||
self.regs[rd] = result;
|
||||
self.regs[a] = result;
|
||||
return Ok(());
|
||||
}
|
||||
let n = self.coerce_number_bounded(self.regs[rd])?;
|
||||
let result = JsValue::float(n - 1.0);
|
||||
self.regs[rd] = result;
|
||||
@@ -586,6 +624,12 @@ impl Vm {
|
||||
self.regs[rd] = self.new_bigint(v - 1);
|
||||
return Ok(());
|
||||
}
|
||||
if self.regs[rd].is_int() {
|
||||
let old = self.regs[rd];
|
||||
self.regs[a] = old;
|
||||
self.regs[rd] = int_dec(old.as_int());
|
||||
return Ok(());
|
||||
}
|
||||
let n = self.coerce_number_bounded(self.regs[rd])?;
|
||||
self.regs[a] = JsValue::float(n);
|
||||
self.regs[rd] = JsValue::float(n - 1.0);
|
||||
|
||||
@@ -238,6 +238,27 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 创建 session 直分的空普通对象([[Prototype]] = Object.prototype):与
|
||||
/// `create_function_object` 的 prototype 子对象同策略——若按 epoch 分配,写入
|
||||
/// 全局等逃逸根时晋升屏障会深克隆进 session,持有者(构造器 prototype 属性)
|
||||
/// 与克隆体指针分裂、严格相等恒 false。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 登记 session 对象表并计入堆账目;回收由 session GC mark/sweep 承担。
|
||||
pub(crate) fn dispatch_new_session_object(&mut self, rd: usize) -> Result<(), String> {
|
||||
vm_trace!("NEW_SESSION_OBJECT rd={}", rd);
|
||||
let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject;
|
||||
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr));
|
||||
obj.set_session_epoch(true);
|
||||
let obj_ptr = self.gc_state.session_epoch.alloc(obj) as *mut JsObject;
|
||||
self.gc_state.session_object_ptrs.push(obj_ptr);
|
||||
// 直 session 分配计入堆账目(与 promote 同式:对象头 + 对象堆数据)。
|
||||
self.gc_state.session_bytes_allocated += std::mem::size_of::<JsObject>()
|
||||
+ crate::session_gc::SessionGc::object_heap_data_bytes(unsafe { &*obj_ptr }) as usize;
|
||||
self.regs[rd] = JsValue::object(obj_ptr as *mut u8);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 创建 arguments 对象:索引属性取当前帧(或 inline 同步调用)的完整实参,
|
||||
/// 附 length / callee 属性。第一版为 unmapped(非严格)语义,索引与形参不同步。
|
||||
///
|
||||
|
||||
@@ -67,6 +67,7 @@ fn ext_word_count(bytecode: &[Instr], pc: usize) -> usize {
|
||||
| OpCode::DEFINE_ACCESSOR_DYNAMIC
|
||||
| OpCode::DEFINE_PROP_ATTRS
|
||||
| OpCode::DEFINE_GLOBAL_PROP_C
|
||||
| OpCode::DEFINE_GLOBAL_PROP_C_IF_ABSENT
|
||||
| OpCode::DELETE_PROP_STATIC
|
||||
| OpCode::REST_OBJECT
|
||||
| OpCode::INIT_PRIVATE => 1,
|
||||
|
||||
@@ -1826,8 +1826,9 @@ mod tests {
|
||||
#[test]
|
||||
fn closure_cell_survives_object_sweep() {
|
||||
let mut vm = vm_with_threshold(1);
|
||||
// counter 函数作用域局部:被 inc 经 cell 捕获(顶层 var 直连全局属性不走 cell)。
|
||||
vm.run(&Arc::new(compile(
|
||||
"var counter = 0; function inc() { return ++counter; } globalThis.inc = inc; 0",
|
||||
"function outer() { var counter = 0; function inc() { return ++counter; } globalThis.inc = inc; return 0; } outer(); 0",
|
||||
)))
|
||||
.expect("run1");
|
||||
assert!(!vm.gc_state.session_cell_ptrs.borrow().is_empty(), "run1 应分配 upvalue cell");
|
||||
@@ -1880,15 +1881,20 @@ mod tests {
|
||||
#[test]
|
||||
fn cells_freed_by_full_reset_and_reallocatable() {
|
||||
let mut vm = Vm::new();
|
||||
vm.run(&Arc::new(compile("var x = 1; function f() { return x; } globalThis.f = f; f()")))
|
||||
.expect("run1");
|
||||
// x/y 函数作用域局部:被 f/g 经 cell 捕获(顶层 var 直连全局属性不走 cell)。
|
||||
vm.run(&Arc::new(compile(
|
||||
"function outer() { var x = 1; function f() { return x; } globalThis.f = f; return f(); } outer()",
|
||||
)))
|
||||
.expect("run1");
|
||||
assert!(!vm.gc_state.session_cell_ptrs.borrow().is_empty());
|
||||
|
||||
vm.full_reset();
|
||||
assert!(vm.gc_state.session_cell_ptrs.borrow().is_empty(), "full_reset 应释放全部 cell");
|
||||
|
||||
let result = vm
|
||||
.run(&Arc::new(compile("var y = 2; function g() { return y; } g()")))
|
||||
.run(&Arc::new(compile(
|
||||
"function outer() { var y = 2; function g() { return y; } return g(); } outer()",
|
||||
)))
|
||||
.expect("run2");
|
||||
assert_eq!(format!("{result}"), "2", "重置后新 cell 应正常分配与读取");
|
||||
}
|
||||
|
||||
@@ -2040,6 +2040,16 @@ impl Vm {
|
||||
self.dispatch_define_global_prop_c(a, key_idx)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_GLOBAL_PROP_IF_ABSENT => {
|
||||
self.dispatch_define_global_prop_if_absent(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_GLOBAL_PROP_C_IF_ABSENT => {
|
||||
let key_idx = self.bytecode[self.pc] as u16;
|
||||
self.pc += 1;
|
||||
self.dispatch_define_global_prop_c_if_absent(a, key_idx)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_PROP_ATTRS => {
|
||||
let attrs = self.bytecode[self.pc] as u8;
|
||||
self.pc += 1;
|
||||
@@ -2078,6 +2088,10 @@ impl Vm {
|
||||
self.dispatch_new_object(rd, instr)?;
|
||||
}
|
||||
|
||||
OpCode::NEW_SESSION_OBJECT => {
|
||||
self.dispatch_new_session_object(rd)?;
|
||||
}
|
||||
|
||||
OpCode::CREATE_ARGUMENTS => {
|
||||
self.dispatch_create_arguments(rd)?;
|
||||
}
|
||||
|
||||
@@ -365,6 +365,70 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
/// 顶层 var 声明的全局属性 define-if-absent:rd=全局对象,a=值,b=键。
|
||||
///
|
||||
/// CreateGlobalVarBinding 对既有属性零动作:属性已存在(数据或 accessor)直接
|
||||
/// 返回——可写/不可写/配置位与值均不更新。缺失新建可写/可枚举/不可配置数据
|
||||
/// 属性;全局对象不可扩展时静默 no-op(GDI 面,基线同形)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - rd 非对象(非对象 this)时按 no-op 处理,不抛错。
|
||||
pub(crate) fn dispatch_define_global_prop_if_absent(
|
||||
&mut self, rd: usize, a: usize, b: usize,
|
||||
) -> Result<(), String> {
|
||||
vm_trace!("DEFINE_GLOBAL_PROP_IF_ABSENT rd={} value={} key={}", rd, a, b);
|
||||
let obj_val = self.regs[rd];
|
||||
if !obj_val.is_object() {
|
||||
return Ok(());
|
||||
}
|
||||
let prop_name_si = self.property_key_si(self.regs[b])?;
|
||||
let value = self.regs[a];
|
||||
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
|
||||
// 既有属性(数据或 accessor):CreateGlobalVarBinding 零动作。
|
||||
if self
|
||||
.kernel_core
|
||||
.shape_forge()
|
||||
.lookup_position(obj.shape_id(), prop_name_si)
|
||||
.is_some()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
// 缺失:新建可写/可枚举/不可配置;不可扩展静默(GDI 面,基线同形)。
|
||||
let _ = self.define_data_property(obj, prop_name_si, value, PropAttributes::new(true, true, false));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// eval 脚本顶层 var 声明的全局属性 define-if-absent:ext 字 = 键常量池下标
|
||||
/// (u16),a 槽 = 值寄存器。全局对象由 session 解析(不依赖 this)。
|
||||
///
|
||||
/// CreateGlobalVarBinding 对既有属性零动作:属性已存在直接返回。缺失新建
|
||||
/// 可写/可枚举/可配置属性;全局对象不可扩展 → TypeError(两模式均抛)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 键常量必须是字符串(emit 侧保证);非字符串按错误返回。
|
||||
pub(crate) fn dispatch_define_global_prop_c_if_absent(&mut self, a: usize, key_idx: u16) -> Result<(), String> {
|
||||
let idx = key_idx as usize;
|
||||
vm_trace!("DEFINE_GLOBAL_PROP_C_IF_ABSENT value={} idx={}", a, idx);
|
||||
let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined());
|
||||
if !key_val.is_string() {
|
||||
return Err(format!("DEFINE_GLOBAL_PROP_C_IF_ABSENT constant index {idx} is not a string key"));
|
||||
}
|
||||
let si = self.property_key_si(key_val)?;
|
||||
let value = self.regs[a];
|
||||
let global_ptr = self.session.global_object().as_ptr() as *mut JsObject;
|
||||
// SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。
|
||||
let obj = unsafe { &mut *global_ptr };
|
||||
// 既有属性:CreateGlobalVarBinding 零动作。
|
||||
if self.kernel_core.shape_forge().lookup_position(obj.shape_id(), si).is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
// 缺失:新建;唯一失败面是全局对象不可扩展(两模式均抛 TypeError)。
|
||||
match self.define_data_property(obj, si, value, PropAttributes::new(true, true, true)) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(msg) => self.raise_error_kind("TypeError", &msg),
|
||||
}
|
||||
}
|
||||
|
||||
/// break 完成:`crossed`(rd 槽)为 emit 词法算出的逃出 finally 域数。
|
||||
/// 逐个穿越 finally 后跳转到目标;crossed 为 0 时直接跳转。
|
||||
/// ext 字携带逃出的迭代器层数:无 finally 穿越时立即关闭后跳转,有 finally
|
||||
|
||||
@@ -1068,9 +1068,10 @@ mod tests {
|
||||
#[test]
|
||||
fn generator_captured_upvalue_survives_sweep() {
|
||||
let mut vm = vm_with_low_threshold();
|
||||
// x 函数作用域局部:被生成器 g 经 cell 捕获(顶层 var 直连全局属性不走 cell)。
|
||||
let _ = run_source(
|
||||
&mut vm,
|
||||
"var x = 0; function* g(){ x++; yield x; x++; yield x; } globalThis.it = g(); globalThis.it.next(); 0",
|
||||
"function outer() { var x = 0; function* g(){ x++; yield x; x++; yield x; } globalThis.it = g(); globalThis.it.next(); return 0; } outer(); 0",
|
||||
);
|
||||
|
||||
vm.maybe_collect_session_gc();
|
||||
@@ -1085,8 +1086,12 @@ mod tests {
|
||||
#[test]
|
||||
fn generator_promoted_clone_owns_independent_state_box() {
|
||||
let mut vm = Vm::new();
|
||||
// `var it = g(); it.next(); it`:it 为 epoch 生成器对象(未逃逸不 promote)。
|
||||
let it = run_source(&mut vm, "function* g(){ yield 1; yield 2; } var it = g(); it.next(); it");
|
||||
// `(function(){ var it = g(); it.next(); return it; })()`:it 为函数局部(非顶层
|
||||
// var,不经全局属性逃逸),保持 epoch 生成器对象(未 promote)。
|
||||
let it = run_source(
|
||||
&mut vm,
|
||||
"function* g(){ yield 1; yield 2; } (function(){ var it = g(); it.next(); return it; })()",
|
||||
);
|
||||
assert!(it.is_object());
|
||||
let epoch_ptr = it.as_js_object_ptr();
|
||||
let epoch_box = unsafe { (*epoch_ptr).native_data() };
|
||||
|
||||
@@ -270,11 +270,12 @@ fn closure_cells_freed_by_full_reset_without_double_free() {
|
||||
fn dead_closure_upvalues_freed_by_sweep_without_double_free() {
|
||||
let mut vm = vm_with_threshold(4096);
|
||||
// run1:两个 3-upvalue 闭包逃逸 global(成 session 对象),同 run 内调用
|
||||
// 存活闭包确认 upvalue 语义完好。
|
||||
// 存活闭包确认 upvalue 语义完好。捕获变量置于函数作用域:顶层 var 按全局
|
||||
// 对象属性单一真值读取,不产生 cell 捕获。
|
||||
let first = compile(
|
||||
"var a = 1; var b = 2; var c = 3; \
|
||||
"(function(){var a = 1; var b = 2; var c = 3; \
|
||||
globalThis.dead = function() { return a + b + c; }; \
|
||||
globalThis.live = function() { return a + b + c; }; globalThis.live()",
|
||||
globalThis.live = function() { return a + b + c; }; return globalThis.live();})()",
|
||||
);
|
||||
let result = vm.run(&Arc::new(first)).expect("run1");
|
||||
assert_eq!(format!("{}", result), "6");
|
||||
@@ -299,8 +300,8 @@ fn dead_closure_upvalues_freed_by_sweep_without_double_free() {
|
||||
|
||||
// 重置后引擎健康:重建新 3-upvalue 闭包,同 run 调用语义正确。
|
||||
let fourth = compile(
|
||||
"var x = 10; var y = 20; var z = 30; \
|
||||
globalThis.f = function() { return x + y + z; }; globalThis.f()",
|
||||
"(function(){var x = 10; var y = 20; var z = 30; \
|
||||
globalThis.f = function() { return x + y + z; }; return globalThis.f();})()",
|
||||
);
|
||||
let result = vm.run(&Arc::new(fourth)).expect("run4");
|
||||
assert_eq!(format!("{}", result), "60");
|
||||
@@ -309,7 +310,8 @@ fn dead_closure_upvalues_freed_by_sweep_without_double_free() {
|
||||
// 各走独立 VM 的「创建 → 撤根 → 完整收集」。两次收集的字节账目差即死
|
||||
// arrow 的 upvalue 列表 Box 字节数——对象本体、length/name 属性区、函数
|
||||
// 名 session 串在 A/B 恒等,差分相消(各 1 个死对象 + 1 条死串)。
|
||||
// 死分支不释放 upvalue 列表(泄漏)时差分为 0,本断言转红。
|
||||
// 捕获变量置于函数作用域(顶层 var 按全局对象属性读取,不产生 cell
|
||||
// 捕获)。死分支不释放 upvalue 列表(泄漏)时差分为 0,本断言转红。
|
||||
let collect_freed = |src: &str| -> u64 {
|
||||
let mut v = vm_with_threshold(4096);
|
||||
v.run(&Arc::new(compile(src))).expect("run create");
|
||||
@@ -320,7 +322,10 @@ fn dead_closure_upvalues_freed_by_sweep_without_double_free() {
|
||||
assert!(stats.last_collection_objects_dead >= 1, "arrow 闭包应经 sweep 死分支");
|
||||
stats.last_collection_bytes_freed
|
||||
};
|
||||
let freed_with_captures = collect_freed("var a = 1; var b = 2; var c = 3; globalThis.arrow = () => a + b + c; 0");
|
||||
let freed_with_captures = collect_freed(
|
||||
"(function(){var a = 1; var b = 2; var c = 3; \
|
||||
globalThis.arrow = () => a + b + c; return 0;})()",
|
||||
);
|
||||
let freed_no_captures = collect_freed("globalThis.arrow = () => 7; 0");
|
||||
let upvalue_box_min = (std::mem::size_of::<Vec<*mut oxide_types::object::Cell>>()
|
||||
+ 3 * std::mem::size_of::<*mut oxide_types::object::Cell>()) as u64;
|
||||
|
||||
@@ -243,3 +243,83 @@ fn map_box_value_survives_reset() {
|
||||
"var fill; for (var i = 0; i < 200; i++) { let t = {junk: i, pad: 'x'.repeat(32)}; fill = t; } globalThis.m.get(globalThis.key).v === 2",
|
||||
));
|
||||
}
|
||||
|
||||
/// 顶层 var 单一真值:裸读、globalThis 别名、GOPD 描述符三者见同一值——
|
||||
/// 声明后写点落全局对象属性,镜像寄存器不再参与读。
|
||||
#[test]
|
||||
fn top_level_var_read_write_single_source() {
|
||||
eval_truthy("var x = 1; x = 2; globalThis.x === 2");
|
||||
eval_truthy("var x = 1; x = 2; x === 2 && globalThis.x === 2");
|
||||
eval_truthy("var x = 1; x = 2; Object.getOwnPropertyDescriptor(globalThis, 'x').value === 2 && x === 2");
|
||||
// 属性被直写后裸读见新值:读走全局对象属性而非镜像槽。
|
||||
eval_truthy("var v = 1; globalThis.v = 2; v === 2");
|
||||
// 复合赋值旧值取属性值:属性被外部更新后 RMW 基址须跟随属性。
|
||||
eval_truthy("var c = 5; globalThis.c = 100; c += 2; c === 102 && globalThis.c === 102");
|
||||
}
|
||||
|
||||
/// 顶层 var 的 typeof 读全局对象属性:声明语句前 GDI 序言已建属性(值
|
||||
/// undefined);声明后见真实值。
|
||||
#[test]
|
||||
fn top_level_var_typeof_reads_global_property() {
|
||||
eval_truthy("(function(){ seen = (typeof t === 'undefined'); })(); var t; seen === true");
|
||||
eval_truthy("var t2 = 's'; typeof t2 === 'string' && globalThis.t2 === 's'");
|
||||
}
|
||||
|
||||
/// 顶层 var 自增/自减、复合/逻辑赋值:新值落全局对象属性。
|
||||
#[test]
|
||||
fn update_and_compound_assign_write_global_property() {
|
||||
eval_truthy("var u = 3; ++u; u === 4 && globalThis.u === 4");
|
||||
eval_truthy("var p = 3; p++; p === 4 && globalThis.p === 4");
|
||||
eval_truthy("var c = 5; c += 2; c === 7 && globalThis.c === 7");
|
||||
eval_truthy("var l = 0; l ||= 9; l === 9 && globalThis.l === 9");
|
||||
eval_truthy("var n = 1; n &&= 0; n === 0 && globalThis.n === 0");
|
||||
}
|
||||
|
||||
/// 顶层 var 作解构赋值目标:值落全局对象属性。
|
||||
#[test]
|
||||
fn destructuring_assign_writes_global_property() {
|
||||
eval_truthy("var d1; ({d1} = {d1: 5}); d1 === 5 && globalThis.d1 === 5");
|
||||
eval_truthy("var d2; [d2] = [7]; d2 === 7 && globalThis.d2 === 7");
|
||||
}
|
||||
|
||||
/// 顶层 for-in 头的迭代值落全局对象属性:声明头与赋值头两形。
|
||||
#[test]
|
||||
fn for_in_head_writes_global_property() {
|
||||
eval_truthy("for (var k in {a: 1}) {} k === 'a' && globalThis.k === 'a'");
|
||||
eval_truthy("var k2; for (k2 in {a: 1, b: 2}) {} k2 === 'b' && globalThis.k2 === 'b'");
|
||||
}
|
||||
|
||||
/// 嵌套函数局部同名遮蔽不穿透顶层绑定:遮蔽侧走局部槽,顶层属性不受影响;
|
||||
/// 无遮蔽时嵌套裸读/裸写直连全局对象属性(不经 cell)。
|
||||
#[test]
|
||||
fn nested_local_shadow_does_not_pierce_tier() {
|
||||
eval_truthy("var s = 1; (function(){ var s = 2; return s; })() === 2 && s === 1 && globalThis.s === 1");
|
||||
eval_truthy(
|
||||
"var s2 = 1; (function(){ var s2 = 9; s2 = 10; return s2; })() === 10 && s2 === 1 && globalThis.s2 === 1",
|
||||
);
|
||||
eval_truthy("var n = 42; (function(){ return n; })() === 42");
|
||||
// 严格嵌套函数 this=undefined:写点不依赖 this,经 session 全局对象落属性。
|
||||
eval_truthy("\"use strict\"; var w = 1; (function(){ \"use strict\"; w = 5; })(); w === 5 && globalThis.w === 5");
|
||||
}
|
||||
|
||||
/// 直接 eval:var 声明在全局对象建可配置属性;eval 内裸写穿透全局对象
|
||||
/// 属性,外层(顶层 var 名)读见同一值。
|
||||
#[test]
|
||||
fn direct_eval_var_and_write_visible_to_outer() {
|
||||
eval_truthy("eval('var y = 5'); y === 5 && globalThis.y === 5");
|
||||
eval_truthy("var q = 1; eval('q = 55'); q === 55 && globalThis.q === 55");
|
||||
eval_truthy("eval('var ev = 1'); Object.getOwnPropertyDescriptor(globalThis, 'ev').configurable === true");
|
||||
// 嵌套函数内直接 eval:写点经 session 解析全局对象(不依赖 this)。
|
||||
eval_truthy("var z = 1; (function(){ \"use strict\"; eval('z = 77'); })(); z === 77 && globalThis.z === 77");
|
||||
}
|
||||
|
||||
/// 既有属性与顶层 var 声明交互:序言 define-if-absent 零动作,声明值写
|
||||
/// 是描述符感知写——既有不可写属性保值不覆盖。既有属性须先于 GDI 存在,
|
||||
/// 分两阶段:阶段一建属性(该程序无 var,GDI 不触碰),阶段二声明。
|
||||
#[test]
|
||||
fn tier_declaration_respects_existing_property() {
|
||||
assert!(eval_two_phases(
|
||||
"Object.defineProperty(globalThis, 'rw', {value: 7, writable: false, configurable: true})",
|
||||
"var rw = 99; rw === 7 && globalThis.rw === 7",
|
||||
));
|
||||
}
|
||||
|
||||
@@ -92,13 +92,15 @@ fn test_full_reset_clears_session_state() {
|
||||
|
||||
/// NEW_OBJECT 字面量必须登记进 epoch 追踪表:`{}` 写入超内联容量的属性会分配
|
||||
/// hash_props 堆 Box,reset 时经追踪表统一释放;不登记则每个字面量泄漏一个 Box。
|
||||
/// 对象变量置于函数作用域:顶层 var 在声明写即逃逸 promote,后续属性写落
|
||||
/// session 克隆(其堆数据不属 epoch 源对象,reset 不释放)。
|
||||
#[test]
|
||||
fn new_object_literal_tracked_and_freed_on_reset() {
|
||||
let mut vm = Vm::new();
|
||||
let result = run_source(
|
||||
&mut vm,
|
||||
"var o = {}; for (var i = 0; i < 40; i++) { o['k' + i] = i; } \
|
||||
delete o.k7; o.k8",
|
||||
"(function(){var o = {}; for (var i = 0; i < 40; i++) { o['k' + i] = i; } \
|
||||
delete o.k7; return o.k8;})()",
|
||||
);
|
||||
assert_eq!(format!("{}", result), "8", "属性写入/删除后读取应正常");
|
||||
assert!(vm.epoch_object_count() > 0, "NEW_OBJECT 应登记 epoch 追踪表");
|
||||
|
||||
Reference in New Issue
Block a user