mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 嵌套函数删除隐式全局名后外层读取不再取残留
This commit is contained in:
@@ -55,6 +55,11 @@ pub struct CompileCtx {
|
||||
/// 是否为 eval 脚本:脚本顶层 var/function 声明落全局对象时属性
|
||||
/// configurable:true(普通脚本顶层为 false)。仅动态脚本入口设置。
|
||||
pub(crate) is_eval_script: bool,
|
||||
/// 编译树是否源自 eval 脚本:eval 代码自身顶层 var/function 名物化 c:true
|
||||
/// 全局属性、可删,本标志随嵌套函数继承,供 delete 分类判定。不直接继承
|
||||
/// `is_eval_script`——该标志只标识当前顶层程序,且还控制顶层全局写点与块级
|
||||
/// 函数门控,整体传播语义过宽。
|
||||
pub(crate) is_eval_origin: bool,
|
||||
/// 源码是否为 `source_escape` 产物(动态编译入口):正则字面量源文本切片
|
||||
/// 的池键形态据此选择(见 [`Emitter::source_encoded`])。
|
||||
pub(crate) source_encoded: bool,
|
||||
@@ -183,6 +188,7 @@ impl CompileCtx {
|
||||
is_global_scope: false,
|
||||
repl_persist: false,
|
||||
is_eval_script: false,
|
||||
is_eval_origin: false,
|
||||
source_encoded: false,
|
||||
static_block_this_reg: None,
|
||||
field_buffer: None,
|
||||
|
||||
@@ -50,7 +50,10 @@ impl Emitter {
|
||||
// 文档),缺失抛 ReferenceError(unresolvable)。tier 名属性由
|
||||
// GlobalDeclarationInstantiation(脚本顶层声明实例化)序言创建(仅删除后缺失);
|
||||
// 未声明名属性缺失即 unresolvable。已声明 var 不再落引擎侧镜像副本。
|
||||
if self.is_global_tier_name(ctx, name) || ctx.implicit_global_reads.contains(&var_reg) {
|
||||
// 隐式全局槽(读侧登记与写侧登记同属一个槽)同样读全局对象属性:属性是
|
||||
// 隐式全局值的唯一真源,嵌套函数内 delete 真删后外层裸读据此可见,引擎侧
|
||||
// 镜像槽不反映删除。
|
||||
if self.is_global_tier_name(ctx, name) || ctx.is_implicit_global_reg(var_reg) {
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
ctx.inst(Inst::new(OpCode::LOAD_GLOBAL, Operand::Reg(r), Operand::Const(key_idx), Operand::None));
|
||||
} else {
|
||||
|
||||
@@ -215,9 +215,10 @@ impl Emitter {
|
||||
// var 名集 ∪ 顶层函数声明名,物化 c:true 全局属性)
|
||||
// 可删;eval 顶层 let/const 不在名集内(自身 lexical
|
||||
// 环境不可删),与非 eval 脚本自身顶层 var/函数名
|
||||
// (c:false)同保留 false。
|
||||
// (c:false)同保留 false。eval 起源随嵌套函数继承,
|
||||
// 子 ctx 内 delete 同一名同样判可删。
|
||||
ctx.is_implicit_global_reg(binding.reg)
|
||||
|| (ctx.is_eval_script && ctx.global_tier_names.contains(name))
|
||||
|| (ctx.is_eval_origin && ctx.global_tier_names.contains(name))
|
||||
} else {
|
||||
// 函数/块作用域局部绑定:非属性引用,恒 false。
|
||||
false
|
||||
|
||||
@@ -368,6 +368,9 @@ impl Emitter {
|
||||
// 顶层已声明 var 名集随作用域继承:嵌套函数裸读/写顶层 var 直连全局对象
|
||||
// (经继承的 scope-0 绑定 + 作用域索引判定,不被局部同名遮蔽误判)。
|
||||
ctx.global_tier_names = parent_ctx.global_tier_names.clone();
|
||||
// eval 起源随作用域继承:eval 顶层 var/函数名在嵌套函数内同样可删(物化
|
||||
// c:true 全局属性),delete 分类判定读此标志。
|
||||
ctx.is_eval_origin = parent_ctx.is_eval_origin;
|
||||
for (name, reg) in extra_bindings {
|
||||
ctx.scopes.symbols.scopes[0].bindings.insert(
|
||||
(*name).to_string(),
|
||||
|
||||
@@ -265,6 +265,9 @@ impl Emitter {
|
||||
ctx.is_global_scope = true;
|
||||
ctx.repl_persist = repl_persist;
|
||||
ctx.is_eval_script = is_eval_script;
|
||||
// eval 起源随嵌套函数继承,供 delete 分类判定 eval 顶层可删名;与
|
||||
// `is_eval_script` 区分——后者只标识当前顶层程序,传播会误改全局写点。
|
||||
ctx.is_eval_origin = is_eval_script;
|
||||
ctx.source_encoded = self.source_encoded;
|
||||
// 脚本顶层严格模式由源码 "use strict" directive 决定(嵌套函数经父 ctx 继承)。
|
||||
ctx.is_strict = program.has_use_strict_directive();
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
//! 环境,declarative 环境不可删)保留 false 常数。覆盖:direct eval 对调用方
|
||||
//! 脚本 var、eval 自身 var 真删、跨 eval 真删、函数内隐式全局真删、读侧真删
|
||||
//! 可见(删后裸读抛 ReferenceError)、typeof 对删后缺失名不抛、删后重写同步、
|
||||
//! 绿基线(脚本 var/顶层函数名/未声明缺失/builtin 槽优先级/strict 调用方
|
||||
//! indirect eval)、eval 自身顶层 let/const false 且无全局属性副作用。
|
||||
//! 跨嵌套函数 delete 后外层裸读抛 ReferenceError、catch 参数错误类型、eval
|
||||
//! 起源随嵌套函数继承(顶层 var 可删/顶层 let 保留)、strict 未声明写抛错、
|
||||
//! 嵌套局部变量遮蔽隐式全局名、绿基线(脚本 var/顶层函数名/未声明缺失/builtin
|
||||
//! 槽优先级/strict 调用方 indirect eval)、eval 自身顶层 let/const false 且无
|
||||
//! 全局属性副作用。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -126,3 +129,65 @@ fn eval_delete_own_const_returns_false_without_global_property() {
|
||||
fn eval_delete_own_let_shadowing_caller_var_returns_false_and_kept() {
|
||||
eval_truthy("var l = 5; eval('let l = 1; delete l') === false && l === 5 && globalThis.l === 5");
|
||||
}
|
||||
|
||||
// ── 跨嵌套函数 delete:外层函数随后裸读该隐式全局名抛 ReferenceError ──
|
||||
// 写与读同在外层函数,delete 在嵌套函数;删除登记须对外层读可见。
|
||||
#[test]
|
||||
fn nested_delete_then_outer_read_throws_reference_error() {
|
||||
eval_truthy(
|
||||
"(function() { q194a = 2; var d = (function() { return delete q194a; })(); \
|
||||
try { void q194a; return false; } catch (e) { return d === true && e instanceof ReferenceError; } })()",
|
||||
);
|
||||
}
|
||||
|
||||
// ── 跨嵌套 delete 后 catch 参数:错误类型为 ReferenceError 且 name 正确 ──
|
||||
#[test]
|
||||
fn nested_delete_catch_param_is_reference_error_with_name() {
|
||||
eval_truthy(
|
||||
"(function() { q194b = 1; var d = (function() { return delete q194b; })(); \
|
||||
try { void q194b; return false; } catch (e) { \
|
||||
return d === true && e.name === 'ReferenceError' && e instanceof ReferenceError; } })()",
|
||||
);
|
||||
}
|
||||
|
||||
// ── 嵌套函数内 delete 未存在名:探针缺失臂 true ──
|
||||
#[test]
|
||||
fn nested_delete_missing_name_returns_true() {
|
||||
eval_truthy("(function() { return (function() { return delete noexist194; })() === true; })()");
|
||||
}
|
||||
|
||||
// ── eval 起源随嵌套函数继承:eval 顶层 var 在嵌套函数内删可删(c:true) ──
|
||||
#[test]
|
||||
fn eval_origin_delete_own_top_var_from_nested_function_returns_true() {
|
||||
eval_truthy("eval('var v194 = 1; function f194() { return delete v194; }') === undefined && f194() === true");
|
||||
}
|
||||
|
||||
// ── eval 起源守卫:eval 顶层 let 是 lexical 绑定,嵌套函数内 delete 仍 false ──
|
||||
#[test]
|
||||
fn eval_origin_delete_own_top_let_from_nested_function_returns_false() {
|
||||
eval_truthy("eval('let l194 = 1; function g194() { return delete l194; }') === undefined && g194() === false");
|
||||
}
|
||||
|
||||
// ── strict 守卫:未声明写仍抛 ReferenceError,不受读路由改动影响 ──
|
||||
#[test]
|
||||
fn strict_undeclared_write_still_throws_reference_error() {
|
||||
eval_truthy(
|
||||
"(function() { \"use strict\"; try { undeclared194 = 1; return false; } \
|
||||
catch (e) { return e instanceof ReferenceError; } })()",
|
||||
);
|
||||
}
|
||||
|
||||
// ── 跨嵌套 delete 后重写:镜像槽与全局对象属性重新同步 ──
|
||||
#[test]
|
||||
fn reassign_after_nested_delete_resyncs_both_sides() {
|
||||
eval_truthy(
|
||||
"(function() { q194c = 1; (function() { return delete q194c; })(); q194c = 9; \
|
||||
return q194c === 9 && globalThis.q194c === 9; })()",
|
||||
);
|
||||
}
|
||||
|
||||
// ── 嵌套函数内局部同名 var 遮蔽隐式全局名:局部绑定不受全局读路由影响 ──
|
||||
#[test]
|
||||
fn nested_local_var_shadowing_implicit_global_name_unaffected() {
|
||||
eval_truthy("(function() { q194d = 1; return (function() { var q194d = 2; return q194d === 2; })(); })()");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user