mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 11:11:11 +08:00
fix: 补齐代理读取组与完整性级别语义
This commit is contained in:
@@ -1273,9 +1273,10 @@ fn sab_new_target_proto_and_fallback() {
|
||||
var d = Reflect.construct(SharedArrayBuffer, [4], D); \
|
||||
if (Object.getPrototypeOf(d) !== SharedArrayBuffer.prototype) { return false; } \
|
||||
var E = function () {}; \
|
||||
Object.defineProperty(E, 'prototype', { get: function () { throw new RangeError('x'); } }); \
|
||||
var e_proxy = new Proxy(E, { get: function (t, k) { \
|
||||
if (k === 'prototype') { throw new RangeError('x'); } return t[k]; } }); \
|
||||
var threw = false; \
|
||||
try { Reflect.construct(SharedArrayBuffer, [1], E); } catch (e) { threw = e instanceof RangeError; } \
|
||||
try { Reflect.construct(SharedArrayBuffer, [1], e_proxy); } catch (e) { threw = e instanceof RangeError; } \
|
||||
return threw; })()",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -483,9 +483,9 @@ fn function_prototype_new_throws_catchable_type_error() {
|
||||
|
||||
#[test]
|
||||
fn dynamic_function_prototype_descriptor_not_configurable() {
|
||||
// 动态函数(Function 构造器产物)的 prototype 属性为
|
||||
// {writable:true, enumerable:false, configurable:false},delete 失败且值不变;
|
||||
// 普通函数的 prototype 属性保持可配置,不回归。
|
||||
// 动态函数(Function 构造器产物)与普通函数的 prototype 属性均为
|
||||
// {writable:true, enumerable:false, configurable:false}(规范 10.2.1 口径),
|
||||
// delete 失败且值不变。
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
let result = eval(
|
||||
@@ -505,7 +505,14 @@ fn dynamic_function_prototype_descriptor_not_configurable() {
|
||||
"function g() {} var d = Object.getOwnPropertyDescriptor(g, 'prototype'); d.writable + ',' + d.enumerable + ',' + d.configurable",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "true,false,true");
|
||||
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "true,false,false");
|
||||
// 普通函数同口径:delete 失败且值不变。
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"function h() {} var p = h.prototype; delete h.prototype; h.prototype === p",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -267,11 +267,21 @@ fn regexp_exec_negative_last_index_clamped_to_zero() {
|
||||
|
||||
#[test]
|
||||
fn regexp_exec_last_index_set_once_on_success() {
|
||||
// 成功路径 lastIndex 恰好 Set 一次(写回在核内单点):计数 setter 只被观察一次。
|
||||
let source = "(() => { var re = /a/g; var n = 0; Object.defineProperty(re, 'lastIndex', { configurable: true, set() { n += 1; }, get() { return 0; } }); re.exec('a'); return String(n); })()";
|
||||
// 成功路径 lastIndex 写回匹配末尾(核内单点写回的可观测口径);
|
||||
// 失败路径置 0(lastIndex 规范不可配置,无法以访问器计数观察)。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, source).unwrap();
|
||||
assert_eq!(to_str(&vm, result), "1");
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(() => { var re = /a/g; var m = re.exec('a'); return m[0] + ':' + re.lastIndex; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(to_str(&vm, result), "a:1");
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(() => { var re = /a/g; var r = re.exec('b'); return r === null && re.lastIndex === 0; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// --- test() lastIndex 规范语义(sticky 锚定 / 越界短路 / 不追踪不写回)---
|
||||
@@ -747,10 +757,10 @@ fn symbol_search_lastindex_init_and_restore() {
|
||||
.unwrap();
|
||||
assert_eq!(to_str(&vm, result), "0/3");
|
||||
// 严格 SameValue 口径:-0 与 +0 不同值,初始化 Set 0 与恢复 Set -0
|
||||
// 各触发一次写。
|
||||
// 各触发一次写(lastIndex 规范不可配置,计数经代理 set trap 观察)。
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(() => { var r = /b/; var sets = 0; var store = -0; Object.defineProperty(r, 'lastIndex', { configurable: true, set(v) { sets += 1; store = v; }, get() { return store; } }); r.exec = function() { return null; }; r[Symbol.search](''); return sets; })()",
|
||||
"(() => { var r = /b/; var sets = 0; var store = -0; var p = new Proxy(r, { set(t, k, v) { if (k === 'lastIndex') { sets += 1; store = v; } t[k] = v; return true; }, get(t, k) { if (k === 'lastIndex') { return store; } return t[k]; } }); p.exec = function() { return null; }; p[Symbol.search](''); return sets; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_int() == 2);
|
||||
|
||||
@@ -2303,14 +2303,14 @@ fn typed_array_ctor_plain_call_all_forms_throw() {
|
||||
|
||||
#[test]
|
||||
fn typed_array_ctor_gpf_getter_throws_propagates() {
|
||||
// newTarget.prototype 访问器抛值原值传播(buffer 形 + length 形):
|
||||
// 构造器体内 GpFC 自重读,不依赖通用构造机制的原型读。
|
||||
// newTarget.prototype 读取抛值原值传播(buffer 形 + length 形):
|
||||
// 构造器体内 GpFC 自重读,不依赖通用构造机制的原型读。普通函数 prototype
|
||||
// 规范不可配置,抛错形态经代理 get trap 表达。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(function () { var nt = function () {}; \
|
||||
Object.defineProperty(nt, 'prototype', { \
|
||||
get: function () { throw new RangeError('gpf'); } }); \
|
||||
"(function () { var nt = new Proxy(function () {}, { \
|
||||
get: function (t, k) { if (k === 'prototype') { throw new RangeError('gpf'); } return t[k]; } }); \
|
||||
var ab = new ArrayBuffer(4); var t1, t2; \
|
||||
try { Reflect.construct(Int8Array, [ab], nt); } \
|
||||
catch (e) { t1 = e instanceof RangeError && e.message === 'gpf'; } \
|
||||
|
||||
@@ -159,14 +159,15 @@ fn throwing_proto_ctor(source_tail: &str) -> String {
|
||||
|
||||
#[test]
|
||||
fn gpfc_new_expression_bytecode_ctor_propagates_getter_throw() {
|
||||
// 字节码构造器 prototype 为访问器且 getter 抛错:new 表达式须原值上抛(getter 触发)。
|
||||
// newTarget 为代理且 get trap 读 prototype 抛错:new 表达式须原值上抛
|
||||
// (getter 触发;普通函数 prototype 规范不可配置,抛错形态经代理表达)。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var F = function(){}; var calls = 0; \
|
||||
Object.defineProperty(F, 'prototype', { configurable: true, \
|
||||
get: function() { calls++; throw new Error('boom'); } }); \
|
||||
try { new F(); } catch (e) { e.message + ':' + calls }",
|
||||
var p = new Proxy(F, { get: function(t, k) { \
|
||||
if (k === 'prototype') { calls++; throw new Error('boom'); } return t[k]; } }); \
|
||||
try { new p(); } catch (e) { e.message + ':' + calls }",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(vm.lookup_str(result).unwrap(), "boom:1");
|
||||
@@ -179,9 +180,9 @@ fn gpfc_new_expression_spread_bytecode_ctor_propagates_getter_throw() {
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var F = function(){}; var calls = 0; \
|
||||
Object.defineProperty(F, 'prototype', { configurable: true, \
|
||||
get: function() { calls++; throw new Error('boom'); } }); \
|
||||
try { new F(...[]); } catch (e) { e.message + ':' + calls }",
|
||||
var p = new Proxy(F, { get: function(t, k) { \
|
||||
if (k === 'prototype') { calls++; throw new Error('boom'); } return t[k]; } }); \
|
||||
try { new p(...[]); } catch (e) { e.message + ':' + calls }",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(vm.lookup_str(result).unwrap(), "boom:1");
|
||||
|
||||
Reference in New Issue
Block a user