mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 11:11:11 +08:00
emit/kernel: 可写 builtin 全局标识符写双写全局属性,三常量描述符对齐规范 c:false,受限词法名集补 eval
- 谓词注册表化:targets_readonly_builtin 解析条件抽公共助手,新增 targets_writable_builtin / is_writable_builtin_global(BUILTIN_GLOBALS 减只读三常量派生,不复制名单)+ 只读/可写划分漂移守卫单测 - 12 个赋值族写点(简单/复合/更新/逻辑赋值、解构两臂、for-in/for-of 标识符目标)补双写臂:STORE_VAR 后 0x98 同步全局对象属性(既有可写 属性原地更值、保 e/c 位),裸读镜像与 globalThis 反射不失步 - for-in 头 tier 写点移除 builtin 名例外:可写 builtin 名迭代键照规范 覆写全局属性(三常量仍由拦截臂跳过) - 三常量(undefined/NaN/Infinity)全局描述符 configurable 翻正为 false:delete 拒绝、属性保留,描述符读同步 - 脚本顶层受限词法名集 3→4(补 eval,静态受限臂):let/const/class eval 顶层声明编译期 SyntaxError - 配套钉:7 处既有钉随 c 位翻正 + 2 枚 for-in/eval 形态钉反转,新增 global_builtin_writable_tests(16 钉:双写六形态 + 遮蔽/strict/eval/ 宿主名面) test262 子集:built-ins/global 29/29、三常量族 20/20、11.13.1-4 7/7、 assignmenttargettype plain 8/8 全绿;language/global-code 17 失败文件 双树单跑一致(pre-existing);annexB assignmenttargettype 7 个 parse error 为既有解析器缺口
This commit is contained in:
@@ -134,9 +134,10 @@ const BUILTIN_GLOBALS: &[&str] = &[
|
||||
"$262",
|
||||
];
|
||||
|
||||
/// 不可写全局内置名:全局对象上的数据属性为 {writable:false, configurable:false},
|
||||
/// 只读全局内置名:全局对象上的数据属性为 {writable:false, configurable:false},
|
||||
/// 对它们的 put 永不成功。写路径命中这些名字的全局绑定(非局部遮蔽)时编译期
|
||||
/// 拦截:sloppy 静默丢弃、strict 抛 TypeError;其余内置名属性可写,写路径放行。
|
||||
/// 拦截:sloppy 静默丢弃、strict 抛 TypeError;BUILTIN_GLOBALS 内其余名属性可写
|
||||
/// (writable:true),标识符写须双写全局属性(见 targets_writable_builtin)。
|
||||
const NON_WRITABLE_GLOBAL_BUILTINS: &[&str] = &["undefined", "NaN", "Infinity"];
|
||||
|
||||
pub(crate) struct FieldBuffer {
|
||||
@@ -675,20 +676,37 @@ impl CompileCtx {
|
||||
false
|
||||
}
|
||||
|
||||
/// 写目标是否为不可写全局内置(undefined/NaN/Infinity 的全局绑定)。
|
||||
/// 名字在名单内且解析寄存器是全局内置槽(builtin_reg_map 登记),或名字落到
|
||||
/// 全局作用域绑定(顶层 var 预声明/隐式全局,含继承的全局槽)——局部
|
||||
/// 名字是否解析到全局内置槽:builtin_reg_map 登记(预注册镜像槽),或绑定
|
||||
/// 落全局作用域(scope 0,顶层 var 预声明/隐式全局,含继承的全局槽)。局部
|
||||
/// var/let/参数遮蔽同名时解析到局部寄存器/局部作用域,两条件均不命中。
|
||||
pub(crate) fn targets_readonly_builtin(&self, name: &str, reg: u32) -> bool {
|
||||
if !NON_WRITABLE_GLOBAL_BUILTINS.contains(&name) {
|
||||
return false;
|
||||
}
|
||||
fn resolves_to_global_builtin_slot(&self, name: &str, reg: u32) -> bool {
|
||||
if self.scopes.builtin_reg_map.iter().any(|(n, r)| n == name && *r == reg) {
|
||||
return true;
|
||||
}
|
||||
matches!(self.scopes.symbols.lookup_any_binding(name), Some((_, 0)))
|
||||
}
|
||||
|
||||
/// 写目标是否为只读全局内置(undefined/NaN/Infinity 的全局绑定——全局对象上
|
||||
/// 数据属性 {writable:false, configurable:false},put 永不成功)。名字在只读
|
||||
/// 名单内且解析到全局内置槽;声明臂与赋值臂共用本谓词。
|
||||
pub(crate) fn targets_readonly_builtin(&self, name: &str, reg: u32) -> bool {
|
||||
NON_WRITABLE_GLOBAL_BUILTINS.contains(&name) && self.resolves_to_global_builtin_slot(name, reg)
|
||||
}
|
||||
|
||||
/// 写目标是否为可写全局内置(全局对象上数据属性 {writable:true,
|
||||
/// configurable:true})。标识符写须双写镜像槽 + 全局对象属性,否则裸读
|
||||
/// (镜像)与 globalThis 反射(属性)失步。
|
||||
pub(crate) fn targets_writable_builtin(&self, name: &str, reg: u32) -> bool {
|
||||
Self::is_writable_builtin_global(name) && self.resolves_to_global_builtin_slot(name, reg)
|
||||
}
|
||||
|
||||
/// 规范可写全局名:BUILTIN_GLOBALS 减只读三常量集——11 个 w:true 函数、
|
||||
/// 48 个构造器/命名空间、globalThis 与宿主名,描述符皆
|
||||
/// {writable:true, enumerable:false, configurable:true}。
|
||||
pub(crate) fn is_writable_builtin_global(name: &str) -> bool {
|
||||
BUILTIN_GLOBALS.contains(&name) && !NON_WRITABLE_GLOBAL_BUILTINS.contains(&name)
|
||||
}
|
||||
|
||||
pub(crate) fn is_known_builtin(name: &str) -> bool {
|
||||
BUILTIN_GLOBALS.contains(&name)
|
||||
}
|
||||
@@ -1802,7 +1820,7 @@ impl Default for Emitter {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::BUILTIN_GLOBALS;
|
||||
use super::{BUILTIN_GLOBALS, NON_WRITABLE_GLOBAL_BUILTINS};
|
||||
use crate::prepass::RESTRICTED_GLOBAL_LEXICAL_NAMES;
|
||||
|
||||
/// 漂移守卫:受限全局名集与 put 写拦截名单交叠名恒同步(两名单语义独立、
|
||||
@@ -1813,4 +1831,18 @@ mod tests {
|
||||
assert!(BUILTIN_GLOBALS.contains(name), "受限全局名缺失于 builtin 名单:{name}");
|
||||
}
|
||||
}
|
||||
|
||||
/// 漂移守卫:只读名单是 builtin 母集子集,且只读/可写两谓词对母集构成
|
||||
/// 划分(不重不漏)——防三常量集或母集改名/删名时拦截面与双写面单边漂移。
|
||||
#[test]
|
||||
fn readonly_and_writable_partition_builtin_globals() {
|
||||
for name in NON_WRITABLE_GLOBAL_BUILTINS {
|
||||
assert!(BUILTIN_GLOBALS.contains(name), "只读全局名缺失于 builtin 名单:{name}");
|
||||
}
|
||||
for name in BUILTIN_GLOBALS {
|
||||
let readonly = NON_WRITABLE_GLOBAL_BUILTINS.contains(name);
|
||||
let writable = crate::CompileCtx::is_writable_builtin_global(name);
|
||||
assert!(readonly ^ writable, "builtin 名只读/可写归属漂移:{name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ impl Emitter {
|
||||
return Ok(val_reg);
|
||||
}
|
||||
ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(rhs), Operand::None));
|
||||
// 可写内置名:RMW 新值同步落全局对象属性。
|
||||
if ctx.targets_writable_builtin(name, reg) {
|
||||
self.emit_global_put_write(name, reg, ctx);
|
||||
}
|
||||
return Ok(reg);
|
||||
}
|
||||
}
|
||||
@@ -116,9 +120,11 @@ impl Emitter {
|
||||
if is_tier {
|
||||
// 顶层已声明 var 复合赋值:新值落全局对象属性(A 侧单一真值)。
|
||||
self.emit_tier_global_write(name, var_reg, ctx);
|
||||
}
|
||||
if is_implicit {
|
||||
} else if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
} else if ctx.targets_writable_builtin(name, var_reg) {
|
||||
// 可写内置名:RMW 新值同步落全局对象属性。
|
||||
self.emit_global_put_write(name, var_reg, ctx);
|
||||
}
|
||||
Ok(var_reg)
|
||||
}
|
||||
@@ -452,6 +458,9 @@ impl Emitter {
|
||||
));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
} else if ctx.targets_writable_builtin(name, var_reg) {
|
||||
// 可写内置名:同步落全局对象属性(仅短路通过时到达)。
|
||||
self.emit_global_put_write(name, var_reg, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +159,10 @@ impl Emitter {
|
||||
return;
|
||||
}
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(reg), Operand::Reg(val_reg), Operand::Imm(0)));
|
||||
// 可写内置名:值同步落全局对象属性,裸读(镜像)与反射不失步。
|
||||
if ctx.targets_writable_builtin(name, reg) {
|
||||
self.emit_global_put_write(name, reg, ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -211,6 +215,10 @@ impl Emitter {
|
||||
));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
} else if ctx.targets_writable_builtin(name, var_reg) {
|
||||
// 可写内置名:值同步落全局对象属性(0x98 对既有可写属性仅更值、
|
||||
// 保 e/c 位),否则镜像槽与 globalThis 反射失步。
|
||||
self.emit_global_put_write(name, var_reg, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -548,6 +548,10 @@ impl Emitter {
|
||||
}
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(result_reg), Operand::Reg(result_reg)));
|
||||
// 可写内置名:RMW 新值同步落全局对象属性。
|
||||
if ctx.targets_writable_builtin(name, reg) {
|
||||
self.emit_global_put_write(name, reg, ctx);
|
||||
}
|
||||
return Ok(result_reg);
|
||||
}
|
||||
}
|
||||
@@ -663,9 +667,11 @@ impl Emitter {
|
||||
if is_tier {
|
||||
// 顶层已声明 var 自增/自减:新值落全局对象属性(A 侧单一真值)。
|
||||
self.emit_tier_global_write(name, var_reg, ctx);
|
||||
}
|
||||
if is_implicit {
|
||||
} else if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
} else if ctx.targets_writable_builtin(name, var_reg) {
|
||||
// 可写内置名:RMW 新值同步落全局对象属性。
|
||||
self.emit_global_put_write(name, var_reg, ctx);
|
||||
}
|
||||
Ok(result_reg)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,13 @@ use oxide_parser::{
|
||||
BindingPattern, Declaration, ExportDefaultDeclarationKind, Expression, Statement, VariableDeclarationKind,
|
||||
};
|
||||
|
||||
/// 脚本顶层 lexical 声明禁止使用的受限全局名:规范 HasRestrictedGlobalProperty
|
||||
/// 判定「全局对象不可配置自有属性名」,现规范全局对象该自有属性恰为三常量。
|
||||
/// 名基静态集(编译期不可查运行时描述符);eval 与各 builtin 属性均可配置
|
||||
/// (合法遮蔽),不在此列。与 BUILTIN_GLOBALS 语义不同(后者是 put 写拦截名单),
|
||||
/// 不互相派生,交叠名由漂移守卫单测断言恒同步。
|
||||
pub(crate) const RESTRICTED_GLOBAL_LEXICAL_NAMES: &[&str] = &["undefined", "NaN", "Infinity"];
|
||||
/// 脚本顶层 lexical 声明(let/const/class)禁止使用的受限全局名:4 名静态集。
|
||||
/// 三常量是全局对象上 {configurable:false} 的自有属性名(声明实例化无法建立
|
||||
/// 遮蔽绑定);eval 属静态受限臂(顶层 lexical 声明不得遮蔽,var/function
|
||||
/// 路径不受本集约束)。名基静态集(编译期不可查运行时描述符)。与
|
||||
/// BUILTIN_GLOBALS 语义不同(后者是 put 写拦截/双写名单),不互相派生,交叠
|
||||
/// 名由漂移守卫单测断言恒同步。
|
||||
pub(crate) const RESTRICTED_GLOBAL_LEXICAL_NAMES: &[&str] = &["undefined", "eval", "NaN", "Infinity"];
|
||||
|
||||
/// 脚本顶层 lexical 声明撞受限全局名 → SyntaxError(声明实例化期拒绝,整程序
|
||||
/// 编译失败)。错误消息与既有重复声明错同形;非顶层或名不在受限集 → Ok。
|
||||
|
||||
@@ -161,6 +161,9 @@ impl Emitter {
|
||||
));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
} else if ctx.targets_writable_builtin(name, var_reg) {
|
||||
// 可写内置名:值同步落全局对象属性。
|
||||
self.emit_global_put_write(name, var_reg, ctx);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -369,6 +372,9 @@ impl Emitter {
|
||||
));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
} else if ctx.targets_writable_builtin(name, var_reg) {
|
||||
// 可写内置名:值同步落全局对象属性。
|
||||
self.emit_global_put_write(name, var_reg, ctx);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -459,6 +465,9 @@ impl Emitter {
|
||||
));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
} else if ctx.targets_writable_builtin(name, var_reg) {
|
||||
// 可写内置名:值同步落全局对象属性(其余属性继续赋值)。
|
||||
self.emit_global_put_write(name, var_reg, ctx);
|
||||
}
|
||||
}
|
||||
AssignmentTargetProperty::AssignmentTargetPropertyProperty(prop) => {
|
||||
|
||||
@@ -72,8 +72,9 @@ impl Emitter {
|
||||
Operand::None,
|
||||
));
|
||||
// 顶层 for-in var 头:迭代值落全局对象属性(A 侧单一真值)。
|
||||
// builtin 名(writable 如 Math)保留既有全局属性值,不 clobber。
|
||||
if self.is_global_tier_name(ctx, name) && !CompileCtx::is_known_builtin(name) {
|
||||
// 只读三常量已在上方拦截臂跳过;其余 builtin 名属性可写,
|
||||
// 迭代键照规范覆写既有全局属性。
|
||||
if self.is_global_tier_name(ctx, name) {
|
||||
self.emit_tier_global_write(name, key_reg, ctx);
|
||||
}
|
||||
}
|
||||
@@ -99,9 +100,10 @@ impl Emitter {
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
||||
self.emit_strict_undeclared_write(name, ctx)?;
|
||||
} else if is_tier && !CompileCtx::is_known_builtin(name) {
|
||||
} else if is_tier {
|
||||
// 顶层 for-in 赋值头:迭代值落全局对象属性(A 侧单一真值)。
|
||||
// builtin 名保留既有全局属性值,不 clobber。
|
||||
// 只读三常量已在上方拦截臂跳过;其余 builtin 名属性可写,
|
||||
// 迭代键照规范覆写既有全局属性。
|
||||
self.emit_tier_global_write(name, key_reg, ctx);
|
||||
} else {
|
||||
ctx.inst(Inst::new(
|
||||
@@ -112,6 +114,9 @@ impl Emitter {
|
||||
));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
} else if ctx.targets_writable_builtin(name, var_reg) {
|
||||
// 可写内置名:迭代键同步落全局对象属性。
|
||||
self.emit_global_put_write(name, var_reg, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,9 @@ impl Emitter {
|
||||
));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
} else if ctx.targets_writable_builtin(name, var_reg) {
|
||||
// 可写内置名:迭代值同步落全局对象属性。
|
||||
self.emit_global_put_write(name, var_reg, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user