mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 10:41:11 +08:00
emit/vm: delete 全局可写内置标识符真删属性并清镜像槽——新增 DELETE_GLOBAL_PROP_C,可删名集自可写划分单源派生(除 globalThis 与宿主名),test262 delete JSON 用例由红转绿
This commit is contained in:
@@ -660,4 +660,43 @@ impl Vm {
|
||||
self.regs[rd] = JsValue::bool(deleted);
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// 删除全局内置数据属性(delete 标识符):ext 字 = 键常量池下标(u16),
|
||||
/// `rd` = 布尔结果槽,`slot` = 内置镜像槽寄存器(0 = 无槽)。全局对象由
|
||||
/// session 解析——函数体内 delete 不依赖 this 寄存器。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 键常量 → intern si → 对全局对象走共享删除逻辑(不可配置 → false 且属性
|
||||
/// 保留;缺失 → true;可配置 → 真删并返回 true)
|
||||
/// 2. 删除成功且 a 槽为有效镜像槽时槽写 undefined
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 删除成功时修改全局对象 shape/属性表/generation
|
||||
/// - 删除成功时写镜像槽寄存器(该槽随后裸读回 undefined)
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 键常量必须是字符串(emit 侧保证);非字符串按错误返回
|
||||
/// - 镜像槽仅在 run/帧入口重载,不清槽则删除后裸读回入口预载的旧值——
|
||||
/// 清槽与真删同生共死:删除失败(c:false)属性保留,槽值不动
|
||||
pub(crate) fn dispatch_delete_global_prop_c(&mut self, rd: usize, slot: usize, key_idx: u16) -> Result<(), String> {
|
||||
vm_trace!("DELETE_GLOBAL_PROP_C slot={} idx={}", slot, key_idx);
|
||||
let idx = key_idx as usize;
|
||||
let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined());
|
||||
if !key_val.is_string() {
|
||||
return Err(format!("DELETE_GLOBAL_PROP_C constant index {idx} is not a string key"));
|
||||
}
|
||||
let si = self.property_key_si(key_val)?;
|
||||
let global_ptr = self.session.global_object().as_ptr() as *mut JsObject;
|
||||
// SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。
|
||||
let obj = unsafe { &mut *global_ptr };
|
||||
// 统一走共享删除逻辑(与 Reflect.deleteProperty 同源);不可配置返回
|
||||
// false 且属性保留。
|
||||
let deleted = oxide_builtins::object::delete_own_property(self, obj, si);
|
||||
// 仅删除成功才清镜像槽(无槽 0 不写,reg 0 是返回槽不可误写)。
|
||||
if deleted && slot > 0 {
|
||||
self.regs[slot] = JsValue::undefined();
|
||||
}
|
||||
self.regs[rd] = JsValue::bool(deleted);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ fn ext_word_count(bytecode: &[Instr], pc: usize) -> usize {
|
||||
| OpCode::DEFINE_PROP_ATTRS
|
||||
| OpCode::DEFINE_GLOBAL_PROP_C
|
||||
| OpCode::DEFINE_GLOBAL_PROP_C_IF_ABSENT
|
||||
| OpCode::DELETE_GLOBAL_PROP_C
|
||||
| OpCode::DELETE_PROP_STATIC
|
||||
| OpCode::REST_OBJECT
|
||||
| OpCode::INIT_PRIVATE => 1,
|
||||
|
||||
@@ -2129,6 +2129,12 @@ impl Vm {
|
||||
self.dispatch_define_global_prop_c_if_absent(a, key_idx)?;
|
||||
}
|
||||
|
||||
OpCode::DELETE_GLOBAL_PROP_C => {
|
||||
let key_idx = self.bytecode[self.pc] as u16;
|
||||
self.pc += 1;
|
||||
self.dispatch_delete_global_prop_c(rd, a, key_idx)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_PROP_ATTRS => {
|
||||
let attrs = self.bytecode[self.pc] as u8;
|
||||
self.pc += 1;
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
//! delete 全局内置标识符(DELETE_GLOBAL_PROP_C)回归测试。
|
||||
//!
|
||||
//! 可删全局内置(可写全局名除 globalThis 与宿主名,描述符
|
||||
//! {writable:true, configurable:true})的 delete 标识符运行期真删全局对象属性
|
||||
//! 并返 true,删除成功时清镜像槽(裸读与 globalThis 反射不失步)。覆盖:删除返
|
||||
//! 值、属性真删、镜像槽清、删后重写、重复删、三常量 c:false 拒绝、globalThis 拒
|
||||
//! 绝、成员形不受影响、参数遮蔽不命中。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_parser::Allocator;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval_truthy(source: &str) {
|
||||
let allocator = Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, source).expect("parse");
|
||||
let module = Compiler::new().compile(&program).expect("compile");
|
||||
let mut vm = Vm::new();
|
||||
let result = vm.run(&Arc::new(module)).expect("run");
|
||||
assert!(result.is_bool() && result.as_bool(), "expected true, got: {result:?}\nsource: {source}");
|
||||
}
|
||||
|
||||
// ── 删除返值与属性真删 ──
|
||||
#[test]
|
||||
fn delete_math_returns_true_and_property_removed() {
|
||||
eval_truthy("var m = Math; var d = delete Math; d === true && globalThis.Math === undefined");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_writable_builtin_names() {
|
||||
for name in ["Object", "JSON", "Symbol", "Promise", "parseInt"] {
|
||||
let src = format!("var m = {name}; var d = delete {name}; d === true && globalThis.{name} === undefined");
|
||||
eval_truthy(&src);
|
||||
}
|
||||
}
|
||||
|
||||
// ── 镜像槽清:删除后裸读回 undefined ──
|
||||
#[test]
|
||||
fn delete_math_then_bare_read_via_mirror() {
|
||||
eval_truthy("var m = Math; delete Math; Math === undefined");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_body_delete_clears_frame_mirror() {
|
||||
eval_truthy(
|
||||
"function f() { var m = Math; delete Math; \
|
||||
return Math === undefined && globalThis.Math === undefined; } f() === true",
|
||||
);
|
||||
}
|
||||
|
||||
// ── 删后重写:两侧再同步 ──
|
||||
#[test]
|
||||
fn delete_then_reassign_both_sides() {
|
||||
eval_truthy("var m = Math; delete Math; Math = 5; Math === 5 && globalThis.Math === 5");
|
||||
}
|
||||
|
||||
// ── 重复删:属性已缺失仍返 true ──
|
||||
#[test]
|
||||
fn delete_twice_second_returns_true() {
|
||||
eval_truthy("var m = Math; delete Math; delete Math === true && globalThis.Math === undefined");
|
||||
}
|
||||
|
||||
// ── 三常量 c:false:拒绝且属性保留 ──
|
||||
#[test]
|
||||
fn delete_three_constants_refused_property_kept() {
|
||||
eval_truthy(
|
||||
"delete undefined === false && delete NaN === false && delete Infinity === false \
|
||||
&& Object.getOwnPropertyDescriptor(globalThis, 'undefined') !== undefined \
|
||||
&& Object.getOwnPropertyDescriptor(globalThis, 'NaN') !== undefined \
|
||||
&& Object.getOwnPropertyDescriptor(globalThis, 'Infinity') !== undefined",
|
||||
);
|
||||
}
|
||||
|
||||
// ── globalThis c:false:拒绝且属性保留 ──
|
||||
#[test]
|
||||
fn delete_global_this_refused_property_kept() {
|
||||
eval_truthy(
|
||||
"delete globalThis === false \
|
||||
&& Object.getOwnPropertyDescriptor(globalThis, 'globalThis') !== undefined",
|
||||
);
|
||||
}
|
||||
|
||||
// ── 成员形不受影响(既有 DELETE_PROP_STATIC 路径) ──
|
||||
#[test]
|
||||
fn member_delete_unchanged() {
|
||||
eval_truthy("delete Math.LN2 === false");
|
||||
}
|
||||
|
||||
// ── 参数遮蔽:不注册镜像槽,delete 落非严格 false ──
|
||||
#[test]
|
||||
fn parameter_shadow_delete_false() {
|
||||
eval_truthy("function f(Math) { return delete Math; } f(1) === false");
|
||||
}
|
||||
Reference in New Issue
Block a user