mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: with 内 delete 回退臂解析外层绑定按可删除性定值
This commit is contained in:
Generated
+18
-18
@@ -1095,7 +1095,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1120,7 +1120,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1128,7 +1128,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1139,7 +1139,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1159,7 +1159,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1168,7 +1168,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1187,7 +1187,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1201,7 +1201,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1216,7 +1216,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1225,7 +1225,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1240,7 +1240,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1252,7 +1252,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1261,7 +1261,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1276,7 +1276,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1292,7 +1292,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1304,7 +1304,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"hashbrown 0.14.5",
|
||||
@@ -1333,7 +1333,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -138,7 +138,7 @@ impl Emitter {
|
||||
Expression::Identifier(ident) => {
|
||||
let name = ident.name.as_str();
|
||||
// with 体内自由标识符:对象有该属性则删除对象属性(返回删除结果),
|
||||
// 否则非严格语义返回 true。
|
||||
// 对象无该属性则回退外层绑定,按其可删除性定值。
|
||||
if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) {
|
||||
let obj_reg = ctx.innermost_with_obj().expect("with stack non-empty");
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
@@ -173,77 +173,19 @@ impl Emitter {
|
||||
));
|
||||
ctx.inst(Inst::jmp(end_label));
|
||||
ctx.labels.set_label_pos(fallback_label, ctx.insts.len());
|
||||
let true_idx = ctx.add_constant(Constant::Boolean(true));
|
||||
ctx.inst(Inst::load_const(Operand::Reg(result_reg), true_idx));
|
||||
// with 对象无该属性:引用解析到外层绑定,按外层绑定的可删除性
|
||||
// 定值(声明式绑定 → false;全局属性 → 运行期探针)。
|
||||
let binding_result = self.emit_delete_binding_result(name, ctx)?;
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(binding_result),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||
return Ok(result_reg);
|
||||
}
|
||||
// 可删全局内置(可写全局名除宿主名 $262):运行期真删
|
||||
// 全局对象属性(c:true 数据描述符)并返 true;删除成功时清镜像
|
||||
// 槽,裸读与 globalThis 反射不失步。
|
||||
if let Some(slot_reg) = ctx.global_builtin_delete_slot(name) {
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
let reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::delete_global_prop_c(Operand::Reg(reg), Operand::Reg(slot_reg), key_idx));
|
||||
return Ok(reg);
|
||||
}
|
||||
// DeleteBinding 三分类(13.5.1.2 步5 绑定引用走 base.DeleteBinding):
|
||||
// - 局部绑定(捕获 cell/upvalue/lookup 命中函数或块作用域槽)、
|
||||
// 非 eval 脚本自身顶层已声明名(脚本 var/函数名 c:false)、eval
|
||||
// 程序自身顶层 let/const(落 eval 自身 lexical 环境,declarative
|
||||
// 环境记录 DeleteBinding 恒 false 且不物化全局属性)→ false 常数;
|
||||
// - 其余——未声明名(引用不可解析)、隐式全局槽、eval 程序自身顶层
|
||||
// var/函数名(物化 c:true 全局属性)——发全局对象运行期探针(与
|
||||
// Reflect.deleteProperty 同源 DeleteBinding 语义:缺失 → true;
|
||||
// 不可配置 → false 且保留;可配置 → 真删且 true)。三类全局属性的
|
||||
// c 位已由各自写点物化,探针取值即规范值;独立编译的 eval 程序见
|
||||
// 不到调用方域变量,静态"当前程序内是否声明"粗于规范动态判定,故
|
||||
// 不可解析面一律运行期定值。严格模式的 delete 标识符由语义分析
|
||||
// 提前拦截为早期错误,本臂在 strict 代码不可达。
|
||||
let local = ctx.captured_bindings.contains_key(name)
|
||||
|| ctx.current_upvalue_captures.iter().any(|u| u.name == name);
|
||||
let probe = if local {
|
||||
false
|
||||
} else {
|
||||
match ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
// 未声明名(引用不可解析):运行期按全局属性定值。
|
||||
None => true,
|
||||
Some((binding, scope_idx)) => {
|
||||
if scope_idx == 0 {
|
||||
// 全局作用域:隐式全局槽(未声明名读写登记,属性
|
||||
// c:true)与 eval 程序自身顶层 var/函数名(顶层
|
||||
// var 名集 ∪ 顶层函数声明名,物化 c:true 全局属性)
|
||||
// 可删;eval 顶层 let/const 不在名集内(自身 lexical
|
||||
// 环境不可删),与非 eval 脚本自身顶层 var/函数名
|
||||
// (c:false)同保留 false。eval 起源随嵌套函数继承,
|
||||
// 子 ctx 内 delete 同一名同样判可删。
|
||||
ctx.is_implicit_global_reg(binding.reg)
|
||||
|| (ctx.is_eval_origin && ctx.global_tier_names.contains(name))
|
||||
} else {
|
||||
// 函数/块作用域局部绑定:非属性引用,恒 false。
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if probe {
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
let reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::delete_global_prop_c(Operand::Reg(reg), Operand::None, key_idx));
|
||||
// 隐式全局槽被真删后,同程序后续裸读该名须走全局对象属性(顶层 var
|
||||
// 的唯一存储,引擎侧不保留镜像副本):缺失属性读抛 ReferenceError,
|
||||
// delete 的真删效应在读侧可见。
|
||||
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
if ctx.is_implicit_global_reg(binding.reg) {
|
||||
ctx.implicit_global_reads.insert(binding.reg);
|
||||
}
|
||||
}
|
||||
return Ok(reg);
|
||||
}
|
||||
let idx = ctx.add_constant(Constant::Boolean(false));
|
||||
let reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(reg), idx));
|
||||
Ok(reg)
|
||||
self.emit_delete_binding_result(name, ctx)
|
||||
}
|
||||
Expression::StaticMemberExpression(member) if matches!(&member.object, Expression::Super(_)) => {
|
||||
// super 属性引用不可删除:在求值引用之前直接抛 ReferenceError。
|
||||
@@ -337,6 +279,96 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// delete 标识符回退外层绑定的定值:可删全局内置镜像槽先行(真删加清槽),
|
||||
/// 其余按 DeleteBinding 三分类发运行期探针(未声明名/隐式全局槽/eval 自身
|
||||
/// 顶层 var/函数名)或 false 常数(局部绑定/脚本自身顶层 var/函数名/eval
|
||||
/// 自身顶层 let/const)。返回结果寄存器。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 可删全局内置镜像槽命中:发 DELETE_GLOBAL_PROP_C(真删加清槽)返结果寄存器
|
||||
/// 2. DeleteBinding 三分类:局部绑定与脚本自身顶层已声明名发 false 常数;
|
||||
/// 未声明名/隐式全局槽/eval 自身顶层 var/函数名发全局对象运行期探针
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 严格模式的 delete 标识符由语义分析提前拦截为早期错误,本臂在 strict 代码不可达
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 探针臂真删隐式全局槽后登记 `implicit_global_reads`(同程序后续裸读该名
|
||||
/// 走全局对象属性,缺失抛 ReferenceError)
|
||||
fn emit_delete_binding_result(&self, name: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
// 可删全局内置(可写全局名除宿主名 $262):运行期真删全局对象属性
|
||||
// (c:true 数据描述符)并返 true;删除成功时清镜像槽,裸读与
|
||||
// globalThis 反射不失步。
|
||||
if let Some(slot_reg) = ctx.global_builtin_delete_slot(name) {
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
let reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::delete_global_prop_c(Operand::Reg(reg), Operand::Reg(slot_reg), key_idx));
|
||||
return Ok(reg);
|
||||
}
|
||||
|
||||
// DeleteBinding 三分类(13.5.1.2 步5 绑定引用走 base.DeleteBinding):
|
||||
// - 局部绑定(捕获 cell/upvalue/lookup 命中函数或块作用域槽)、
|
||||
// 非 eval 脚本自身顶层已声明名(脚本 var/函数名 c:false)、eval
|
||||
// 程序自身顶层 let/const(落 eval 自身 lexical 环境,declarative
|
||||
// 环境记录 DeleteBinding 恒 false 且不物化全局属性)→ false 常数;
|
||||
// - 其余——未声明名(引用不可解析)、隐式全局槽、eval 程序自身顶层
|
||||
// var/函数名(物化 c:true 全局属性)——发全局对象运行期探针(与
|
||||
// Reflect.deleteProperty 同源 DeleteBinding 语义:缺失 → true;
|
||||
// 不可配置 → false 且保留;可配置 → 真删且 true)。三类全局属性的
|
||||
// c 位已由各自写点物化,探针取值即规范值;独立编译的 eval 程序见
|
||||
// 不到调用方域变量,静态"当前程序内是否声明"粗于规范动态判定,故
|
||||
// 不可解析面一律运行期定值。
|
||||
let local =
|
||||
ctx.captured_bindings.contains_key(name) || ctx.current_upvalue_captures.iter().any(|u| u.name == name);
|
||||
let probe = if local {
|
||||
false
|
||||
} else {
|
||||
match ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
// 未声明名(引用不可解析):运行期按全局属性定值。
|
||||
None => true,
|
||||
Some((binding, scope_idx)) => {
|
||||
if scope_idx == 0 {
|
||||
// 全局作用域:隐式全局槽(未声明名读写登记,属性
|
||||
// c:true)与 eval 程序自身顶层 var/函数名(顶层
|
||||
// var 名集 ∪ 顶层函数声明名,物化 c:true 全局属性)
|
||||
// 可删;eval 顶层 let/const 不在名集内(自身 lexical
|
||||
// 环境不可删),与非 eval 脚本自身顶层 var/函数名
|
||||
// (c:false)同保留 false。eval 起源随嵌套函数继承,
|
||||
// 子 ctx 内 delete 同一名同样判可删。
|
||||
ctx.is_implicit_global_reg(binding.reg)
|
||||
|| (ctx.is_eval_origin && ctx.global_tier_names.contains(name))
|
||||
} else {
|
||||
// 函数/块作用域局部绑定:非属性引用,恒 false。
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 探针臂:全局对象运行期定值(缺失 → true;不可配置 → false 且保留;
|
||||
// 可配置 → 真删且 true)。
|
||||
if probe {
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
let reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::delete_global_prop_c(Operand::Reg(reg), Operand::None, key_idx));
|
||||
// 隐式全局槽被真删后,同程序后续裸读该名须走全局对象属性(顶层 var
|
||||
// 的唯一存储,引擎侧不保留镜像副本):缺失属性读抛 ReferenceError,
|
||||
// delete 的真删效应在读侧可见。
|
||||
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
if ctx.is_implicit_global_reg(binding.reg) {
|
||||
ctx.implicit_global_reads.insert(binding.reg);
|
||||
}
|
||||
}
|
||||
return Ok(reg);
|
||||
}
|
||||
|
||||
// 声明式绑定与脚本自身顶层已声明名:恒 false。
|
||||
let idx = ctx.add_constant(Constant::Boolean(false));
|
||||
let reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(reg), idx));
|
||||
Ok(reg)
|
||||
}
|
||||
|
||||
/// typeof 操作数求值:未声明标识符特判为 "undefined"。
|
||||
///
|
||||
/// # 步骤
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
//! with 内 delete 标识符结果值回归测试。
|
||||
//!
|
||||
//! `with` 对象有该属性时删对象属性(返回删除结果);对象无该属性时引用
|
||||
//! 解析到外层绑定,按 DeleteBinding 定值(var/let/const 绑定恒 false;
|
||||
//! 未声明名/隐式全局槽/可删全局内置走运行期探针真删)。覆盖:var 回退
|
||||
//! false、let 回退 false、属性删除分支保真、未声明名探针、可删全局内置
|
||||
//! 真删加清槽、with 内声明分支。
|
||||
|
||||
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}");
|
||||
}
|
||||
|
||||
// ── var 回退:with 对象无该属性,外层 var c:false 恒 false ──
|
||||
#[test]
|
||||
fn with_delete_outer_var_returns_false() {
|
||||
eval_truthy("var o = {}; o.x = 1; var d; with(o) { d = delete o; } d === false && o.x === 1");
|
||||
}
|
||||
|
||||
// ── let 回退:外层 lexical 绑定 DeleteBinding 恒 false ──
|
||||
#[test]
|
||||
fn with_delete_outer_let_returns_false() {
|
||||
eval_truthy("let l = 1; var o = {}; var d; with(o) { d = delete l; } d === false && l === 1");
|
||||
}
|
||||
|
||||
// ── 属性删除分支保真:with 对象有该属性,真删返 true ──
|
||||
#[test]
|
||||
fn with_delete_object_property_returns_true() {
|
||||
eval_truthy("var o = {p: 1}; var d; with(o) { d = delete p; } d === true && !('p' in o)");
|
||||
}
|
||||
|
||||
// ── 未声明名回退:探针缺失臂返 true ──
|
||||
#[test]
|
||||
fn with_delete_undeclared_name_returns_true() {
|
||||
eval_truthy("var o = {}; var d; with(o) { d = delete nomatch_w1; } d === true");
|
||||
}
|
||||
|
||||
// ── 可删全局内置回退:真删全局属性并清镜像槽 ──
|
||||
#[test]
|
||||
fn with_delete_deletable_builtin_returns_true_and_removed() {
|
||||
eval_truthy(
|
||||
"var m = Math; var o = {}; var d; with(o) { d = delete Math; } \
|
||||
d === true && globalThis.Math === undefined",
|
||||
);
|
||||
}
|
||||
|
||||
// ── with 内声明分支:with 内 let 优先于对象属性,落局部绑定 false ──
|
||||
#[test]
|
||||
fn with_delete_internal_let_returns_false() {
|
||||
eval_truthy("var o = {}; var d; with(o) { let p = 1; d = delete p; } d === false");
|
||||
}
|
||||
Reference in New Issue
Block a user