mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
feat: 脚本顶层词法声明三常量名合法化并登记语料跳过条目
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
//! 脚本顶层 lexical 声明撞受限全局名的编译期拒绝:let/const 声明名撞
|
||||
//! 受限集(undefined/NaN/Infinity——三常量,全局对象 {configurable:false}
|
||||
//! 自有属性名)→ SyntaxError(整程序编译失败)。局部作用域(函数体/块/try)与
|
||||
//! eval 代码为合法遮蔽不拒;三常数值使用不受影响。
|
||||
//! 脚本顶层 lexical 声明合法遮蔽三常量全局名(undefined/NaN/Infinity):
|
||||
//! let/const/class 声明名撞三常量合法(对齐 V8/node 现行行为;ES2025 规范
|
||||
//! GDI lexNames 臂仍保留受限全局属性检查,属有意偏离,语料负面 oracle 已
|
||||
//! 登记跳过集)。裸读读绑定槽、裸写/delete 不落全局对象,全局属性描述符
|
||||
//! 不变;局部作用域(函数体/块/try)与 eval 代码为合法遮蔽不拒;三常数值
|
||||
//! 使用不受影响。
|
||||
//!
|
||||
//! class 名撞 eval 不在本集(解析器 early-error 族独立拒绝,类体恒严格模式,
|
||||
//! 与受限集检查无关,见 top_level_class_eval_name_rejected)。
|
||||
//! class 名撞 undefined/eval 不在本集(解析器 early-error 族独立拒绝,类体
|
||||
//! 恒严格模式,与本集机制无关,见 top_level_class_undefined_rejected /
|
||||
//! top_level_class_eval_name_rejected)。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -20,74 +23,86 @@ fn eval(source: &str) -> Result<JsValue, String> {
|
||||
vm.run(&Arc::new(module))
|
||||
}
|
||||
|
||||
/// 断言整程序编译失败且错消息为声明实例化期的重复声明错形(SyntaxError 族,
|
||||
/// 消息实现自定,只断结构)。
|
||||
fn assert_compile_err(source: &str) {
|
||||
let err = eval(source).unwrap_err();
|
||||
assert!(err.contains("already been declared"), "got: {err}");
|
||||
}
|
||||
|
||||
/// 断言编译 + 运行均成功。
|
||||
/// 断言编译 + 运行均成功,返回完成值。
|
||||
fn assert_ok(source: &str) -> JsValue {
|
||||
eval(source).unwrap_or_else(|e| panic!("expected ok, got: {e}\nsource: {source}"))
|
||||
}
|
||||
|
||||
// ── 脚本顶层 let/const/class 撞三常量:编译期拒绝 ──
|
||||
/// 断言编译 + 运行均成功且完成值为字符串,返回字符串内容。
|
||||
fn assert_ok_str(source: &str) -> String {
|
||||
let value = assert_ok(source);
|
||||
if value.is_string() {
|
||||
// SAFETY: 值已确认字符串类别,指针指向 VM 存活期内的 JsString。
|
||||
unsafe { &*value.as_string_ptr() }.as_str().to_string()
|
||||
} else {
|
||||
panic!("expected string, got {value:?}\nsource: {source}")
|
||||
}
|
||||
}
|
||||
|
||||
// ── 脚本顶层 let/const/class 撞三常量:合法遮蔽 ──
|
||||
|
||||
#[test]
|
||||
fn top_level_let_undefined_rejected() {
|
||||
// 独立形(脚本内无裸 undefined 引用,无镜像槽):规范 SyntaxError。
|
||||
assert_compile_err("let undefined;");
|
||||
fn top_level_let_undefined_allowed() {
|
||||
// 独立形(脚本内无裸 undefined 引用,无镜像槽):合法,完成值 undefined。
|
||||
let r = assert_ok("let undefined;");
|
||||
assert!(r.is_undefined(), "let undefined 完成值应为 undefined,实际 {r:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_let_nan_rejected() {
|
||||
assert_compile_err("let NaN;");
|
||||
fn top_level_let_nan_allowed() {
|
||||
let r = assert_ok("let NaN;");
|
||||
assert!(r.is_undefined(), "let NaN 完成值应为 undefined,实际 {r:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_let_infinity_rejected() {
|
||||
assert_compile_err("let Infinity;");
|
||||
fn top_level_let_infinity_allowed() {
|
||||
let r = assert_ok("let Infinity;");
|
||||
assert!(r.is_undefined(), "let Infinity 完成值应为 undefined,实际 {r:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_const_nan_rejected() {
|
||||
assert_compile_err("const NaN = 1;");
|
||||
fn top_level_const_nan_allowed() {
|
||||
let r = assert_ok("const NaN = 1; NaN");
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_const_undefined_rejected() {
|
||||
assert_compile_err("const undefined = 1;");
|
||||
fn top_level_const_undefined_allowed() {
|
||||
let r = assert_ok("const undefined = 1; undefined");
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_class_undefined_rejected() {
|
||||
// class 名同为 lexical 绑定(受限检查同形);undefined 类名由解析器更早拒绝
|
||||
// (SyntaxError 族,整程序拒绝可观察行为同形),断言接受任一拒绝相位。
|
||||
// class 名撞 undefined 由解析器 early-error 族更早拒绝(SyntaxError 族,
|
||||
// 整程序拒绝可观察行为同形),与本集机制无关,断言接受任一拒绝相位。
|
||||
assert!(eval("class undefined {}").is_err(), "class undefined 类名应被拒绝");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_class_infinity_rejected() {
|
||||
assert_compile_err("class Infinity {}");
|
||||
fn top_level_class_infinity_allowed() {
|
||||
// class 名同为 lexical 绑定:合法遮蔽,typeof 读绑定槽(类函数)。
|
||||
assert_eq!(assert_ok_str("class Infinity {} typeof Infinity"), "function");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_let_undefined_with_bare_use_rejected() {
|
||||
// 镜像形(脚本另有裸 undefined 引用):拒绝行为不变,错误相位提前到
|
||||
// 声明预登记期(消息同形)。
|
||||
assert_compile_err("let undefined; undefined");
|
||||
fn top_level_let_undefined_with_bare_use_allowed() {
|
||||
// 镜像形(脚本另有裸 undefined 引用):声明点之后的裸读是词法绑定读,
|
||||
// 合法,完成值为绑定值 undefined。
|
||||
let r = assert_ok("let undefined; undefined");
|
||||
assert!(r.is_undefined(), "镜像形完成值应为 undefined,实际 {r:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_let_destructured_nan_rejected() {
|
||||
// 解构叶子同属声明名,受限检查覆盖。
|
||||
assert_compile_err("let [NaN] = [1];");
|
||||
fn top_level_let_destructured_nan_allowed() {
|
||||
// 解构叶子同属声明名,合法遮蔽。
|
||||
let r = assert_ok("let [NaN] = [1]; NaN");
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_switch_case_let_nan_allowed() {
|
||||
// switch case 有独立 CaseBlock 作用域:case 内 lexical 是块局部绑定,不受限检查。
|
||||
// switch case 有独立 CaseBlock 作用域:case 内 lexical 是块局部绑定。
|
||||
let r = assert_ok("switch (0) { case 0: let NaN; } 1");
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
@@ -96,12 +111,68 @@ fn top_level_switch_case_let_nan_allowed() {
|
||||
|
||||
#[test]
|
||||
fn eval_script_let_nan_allowed() {
|
||||
// eval 代码在独立 lexical 环境实例化,不受限检查约束;外层裸读仍是三常量值
|
||||
// eval 代码在独立 lexical 环境实例化;外层裸读仍是三常量值
|
||||
//(NaN 自反性用 isNaN 断言,=== 对 NaN 恒 false)。
|
||||
let r = assert_ok("eval('let NaN;'); isNaN(NaN)");
|
||||
assert!(r.is_bool() && r.as_bool(), "eval 内 let NaN 应为合法遮蔽,实际 {r:?}");
|
||||
}
|
||||
|
||||
// ── 值隔离与全局对象不变面:写/delete 落绑定槽,全局属性原值与描述符保留 ──
|
||||
|
||||
#[test]
|
||||
fn top_level_let_undefined_value_isolated() {
|
||||
// 声明点后的裸读读绑定槽:得声明值,非全局属性值。
|
||||
let r = assert_ok("let undefined=5; undefined");
|
||||
assert_eq!(r.as_int(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_let_undefined_global_this_reflection() {
|
||||
// 全局对象属性原值保留:globalThis.undefined 仍是真 undefined(typeof 串形)。
|
||||
assert_eq!(assert_ok_str("let undefined=5; typeof globalThis.undefined"), "undefined");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_let_undefined_delete_not_global() {
|
||||
// 词法绑定 delete 返 false(DeleteBinding 非属性引用),全局属性原值保留。
|
||||
let r = assert_ok("let undefined=5; delete undefined");
|
||||
assert!(r.is_bool() && !r.as_bool(), "实际 {r:?}");
|
||||
let r = assert_ok("let undefined=5; delete undefined; undefined");
|
||||
assert_eq!(r.as_int(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_let_undefined_typeof() {
|
||||
// typeof 读绑定槽:声明值 5 的 typeof 为 number。
|
||||
assert_eq!(assert_ok_str("let undefined=5; typeof undefined"), "number");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_let_undefined_nested_capture() {
|
||||
// 嵌套函数读顶层词法绑定经 upvalue 捕获:得声明值。
|
||||
let r = assert_ok("let undefined=1; function f(){return undefined} f()");
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_let_undefined_tdz_read_throws() {
|
||||
// 声明点前裸读是 TDZ 运行期 ReferenceError(错消息含 before initialization)。
|
||||
let err = eval("undefined; let undefined;").unwrap_err();
|
||||
assert!(err.contains("before initialization"), "got: {err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_let_undefined_descriptor_unchanged() {
|
||||
// 全局对象属性描述符不变:writable/configurable/enumerable 恒 false。
|
||||
let r = assert_ok(
|
||||
"let undefined=5; Object.getOwnPropertyDescriptor(globalThis,'undefined')
|
||||
.writable === false && Object.getOwnPropertyDescriptor(globalThis,'undefined')
|
||||
.configurable === false && Object.getOwnPropertyDescriptor(globalThis,'undefined')
|
||||
.enumerable === false",
|
||||
);
|
||||
assert!(r.is_bool() && r.as_bool(), "描述符应为只读不可配置不可枚举,实际 {r:?}");
|
||||
}
|
||||
|
||||
// ── 假阳性守卫:合法面不得误拒 ──
|
||||
|
||||
#[test]
|
||||
@@ -113,8 +184,7 @@ fn top_level_let_uses_nan_value_allowed() {
|
||||
|
||||
#[test]
|
||||
fn top_level_let_eval_name_allowed() {
|
||||
// eval 是全局对象可配置自有属性(受限判定走自有属性臂,eval 不命中):
|
||||
// 合法遮蔽,不在受限集。
|
||||
// eval 是全局对象可配置自有属性:合法遮蔽,不在受限集。
|
||||
let r = assert_ok("let eval; 1");
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
@@ -129,8 +199,8 @@ fn top_level_const_eval_name_allowed() {
|
||||
fn top_level_class_eval_name_rejected() {
|
||||
// eval 不在受限集(let/const 合法遮蔽),但 class 名撞 eval 由解析器
|
||||
// early-error 族拒绝:类体恒严格模式,类名绑定检查落在类作用域 strict
|
||||
// 判定内(V8 同形:sloppy `class eval {}` 亦 SyntaxError)。与本受限集
|
||||
// 检查无关,整程序拒绝可观察行为同形,断言任意拒绝相位。
|
||||
// 判定内(V8 同形:sloppy `class eval {}` 亦 SyntaxError)。与本集
|
||||
// 机制无关,整程序拒绝可观察行为同形,断言任意拒绝相位。
|
||||
assert!(eval("class eval {}").is_err(), "class eval 类名应被拒绝");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
//! 不为其登记,声明合法建立遮蔽绑定——裸读读绑定槽、裸写/delete 不落全局
|
||||
//! 对象(全局属性原值保留),声明点前裸读按 TDZ 运行期抛 ReferenceError。
|
||||
//!
|
||||
//! 回归约束:受限三常量(87 面)声明仍编译期拒绝;let+var / function+let
|
||||
//! 撞 builtin 名的重复声明仍编译期拒绝(var 懒登记镜像与函数先登记撞点保留);
|
||||
//! 未遮蔽 builtin 读、var 内置名写全局、函数内遮蔽读不回退。
|
||||
//! 回归约束:let+var / function+let 撞 builtin 名的重复声明仍编译期拒绝
|
||||
//! (var 懒登记镜像与函数先登记撞点保留);未遮蔽 builtin 读、var 内置名
|
||||
//! 写全局、函数内遮蔽读不回退。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -153,13 +153,15 @@ fn top_level_let_shadow_tdz_second_read_throws() {
|
||||
assert!(err.contains("before initialization"), "got: {err}");
|
||||
}
|
||||
|
||||
// ── 回归约束:受限名与重复声明拒面不翻 ──
|
||||
// ── 回归约束:受限名已合法化(对齐 V8),重复声明拒面不翻 ──
|
||||
|
||||
#[test]
|
||||
fn top_level_let_restricted_names_still_rejected() {
|
||||
// 受限三常量(87 面):非受限名修面不动此面,声明仍编译期拒绝。
|
||||
assert_compile_err("let undefined;");
|
||||
assert_compile_err("let NaN=1; NaN");
|
||||
fn top_level_let_restricted_names_legal_shadow() {
|
||||
// 三常量名(undefined/NaN/Infinity)声明合法化:裸读读绑定槽。
|
||||
let r = assert_ok("let undefined;");
|
||||
assert!(r.is_undefined(), "let undefined 完成值应为 undefined,实际 {r:?}");
|
||||
let r = assert_ok("let NaN=1; NaN");
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user