Files
OxideJS-New/crates/oxide_vm/tests/global_prop_desc_tests.rs
pjh123 c564ec539b 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 为既有解析器缺口
2026-09-13 10:46:10 +00:00

126 lines
4.4 KiB
Rust

//! 内置全局属性描述符测试:规范要求全局构造器/命名空间对象属性
//! enumerable=false(不泄漏进 Object.keys(globalThis) / for-in)且可写可配置,
//! 全局 NaN/undefined/Infinity 三常量不可写不可枚举不可配置。
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 global_constructors_are_non_enumerable() {
for name in [
"Array", "Object", "Math", "JSON", "Reflect", "Number", "String", "Date", "Map", "Set", "Error", "Promise",
"BigInt",
] {
let src = format!("Object.getOwnPropertyDescriptor(globalThis, '{name}').enumerable === false");
eval_truthy(&src);
}
}
#[test]
fn global_constructors_descriptor_full_attributes() {
eval_truthy(
"var d = Object.getOwnPropertyDescriptor(globalThis, 'Array'); \
d.writable === true && d.enumerable === false && d.configurable === true",
);
eval_truthy(
"var d = Object.getOwnPropertyDescriptor(globalThis, 'Math'); \
d.writable === true && d.enumerable === false && d.configurable === true",
);
}
#[test]
fn global_this_is_non_enumerable_data_property() {
eval_truthy(
"var d = Object.getOwnPropertyDescriptor(globalThis, 'globalThis'); \
d.writable === true && d.enumerable === false && d.configurable === true",
);
}
#[test]
fn keys_of_global_this_exclude_builtins() {
eval_truthy("Object.keys(globalThis).indexOf('Math') === -1");
eval_truthy("Object.keys(globalThis).indexOf('Array') === -1");
eval_truthy("Object.keys(globalThis).indexOf('NaN') === -1");
// 用户属性仍可枚举:内置属性收口不影响用户写入。
eval_truthy("var x = 1; Object.keys(globalThis).indexOf('x') >= 0");
}
#[test]
fn for_in_global_this_excludes_builtins() {
eval_truthy("var found = false; for (var k in globalThis) { if (k === 'Math') found = true; } found === false");
}
#[test]
fn nan_undefined_infinity_are_non_writable_non_configurable() {
for name in ["NaN", "undefined", "Infinity"] {
let src = format!(
"var d = Object.getOwnPropertyDescriptor(globalThis, '{name}'); \
d.writable === false && d.enumerable === false && d.configurable === false"
);
eval_truthy(&src);
}
eval_truthy("isNaN(globalThis.NaN) === true");
eval_truthy("globalThis.undefined === undefined");
eval_truthy("globalThis.Infinity === Infinity");
}
#[test]
fn math_constants_non_enumerable() {
eval_truthy("Object.getOwnPropertyDescriptor(Math, 'PI').enumerable === false");
eval_truthy("Object.getOwnPropertyDescriptor(Math, 'E').writable === false");
eval_truthy("Object.getOwnPropertyDescriptor(Math, 'PI').configurable === false");
eval_truthy("Object.keys(Math).length === 0");
eval_truthy("Math.PI === 3.141592653589793");
}
#[test]
fn math_namespace_object_is_non_enumerable_on_global() {
eval_truthy("Object.keys(globalThis).indexOf('Math') === -1");
}
#[test]
fn global_functions_non_enumerable() {
for name in [
"parseInt",
"parseFloat",
"isNaN",
"isFinite",
"encodeURI",
"decodeURI",
"escape",
"unescape",
] {
let src = format!("Object.getOwnPropertyDescriptor(globalThis, '{name}').enumerable === false");
eval_truthy(&src);
}
eval_truthy("Object.keys(globalThis).indexOf('parseInt') === -1");
}
#[test]
fn user_global_properties_remain_enumerable() {
eval_truthy("var x = 1; Object.getOwnPropertyDescriptor(globalThis, 'x').enumerable === true");
eval_truthy("globalThis.y = 2; Object.getOwnPropertyDescriptor(globalThis, 'y').enumerable === true");
}
#[test]
fn builtin_globals_remain_readable() {
eval_truthy("Array.isArray([]) === true");
eval_truthy("JSON.stringify({a:1}) === '{\"a\":1}'");
eval_truthy("Math.abs(-3) === 3");
eval_truthy("typeof parseInt === 'function'");
eval_truthy("Reflect.has({a:1}, 'a') === true");
eval_truthy("Number('42') === 42");
}