mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 11:11:11 +08:00
fix: 补齐代理读取组与完整性级别语义
This commit is contained in:
@@ -789,10 +789,18 @@ impl Vm {
|
||||
let key_val = self.regs[b];
|
||||
let prop_name_si = self.property_key_si(key_val)?;
|
||||
let super_obj = unsafe { &*super_base.as_js_object_ptr() };
|
||||
let pc_before = self.pc;
|
||||
let val = self.ordinary_get_with_target(super_obj, prop_name_si, self.regs[a], rd as u8)?;
|
||||
if self.accessor_frame_target_reg.take().is_none() {
|
||||
self.regs[rd] = val;
|
||||
if self.accessor_frame_target_reg.take().is_some() {
|
||||
// getter 帧已入栈:结果由帧续行交付,此处不写结果寄存器。
|
||||
return Ok(true);
|
||||
}
|
||||
if self.pc != pc_before {
|
||||
// 读路径在途抛错已展开到 catch/finally 入口:pc 由处理器接管,
|
||||
// 结果寄存器不得再写(regs[0] 持在途异常)。
|
||||
return Ok(true);
|
||||
}
|
||||
self.regs[rd] = val;
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
|
||||
@@ -370,11 +370,21 @@ impl Vm {
|
||||
// 命中会绕过 get trap 直读代理自身属性槽(恒 miss 守卫的读入口侧)。
|
||||
if obj.is_proxy_obj() {
|
||||
let prop_name_si = self.property_key_si(self.regs[b])?;
|
||||
// 先消费扩展字:代理分支不写回 IC(槽恒零),getter 帧的返回地址
|
||||
// 与展开后的 catch 入口都以 pc 接管,调用后不得再推进。
|
||||
let _ = ic_helper::read_ic_slot0(&self.bytecode, &mut self.pc);
|
||||
let pc_before = self.pc;
|
||||
let val = self.ordinary_get_with_target(obj, prop_name_si, val, a as u8)?;
|
||||
if self.accessor_frame_target_reg.take().is_none() {
|
||||
self.regs[a] = val;
|
||||
if self.accessor_frame_target_reg.take().is_some() {
|
||||
// getter 帧已入栈:结果由帧续行交付,此处不写结果寄存器。
|
||||
return Ok(());
|
||||
}
|
||||
self.pc += IC_EXT_WORDS;
|
||||
if self.pc != pc_before {
|
||||
// 读路径在途抛错已展开到 catch/finally 入口:pc 由处理器接管,
|
||||
// 结果寄存器不得再写(regs[0] 持在途异常)。
|
||||
return Ok(());
|
||||
}
|
||||
self.regs[a] = val;
|
||||
return Ok(());
|
||||
}
|
||||
// 入口快判:键寄存器恒为 perm 串,指针相等即键为 "length";
|
||||
@@ -390,10 +400,18 @@ impl Vm {
|
||||
if obj.has_prop_meta() {
|
||||
// meta 对象整对象走慢路径,此时才解析键。
|
||||
let prop_name_si = self.property_key_si(self.regs[b])?;
|
||||
let pc_before = self.pc;
|
||||
let val = self.ordinary_get_with_target(obj, prop_name_si, val, a as u8)?;
|
||||
if self.accessor_frame_target_reg.take().is_none() {
|
||||
self.regs[a] = val;
|
||||
if self.accessor_frame_target_reg.take().is_some() {
|
||||
// getter 帧已入栈:结果由帧续行交付,此处不写结果寄存器。
|
||||
return Ok(());
|
||||
}
|
||||
if self.pc != pc_before {
|
||||
// 读路径在途抛错已展开到 catch/finally 入口:pc 由处理器接管,
|
||||
// 结果寄存器不得再写(regs[0] 持在途异常)。
|
||||
return Ok(());
|
||||
}
|
||||
self.regs[a] = val;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -570,10 +588,18 @@ impl Vm {
|
||||
return Ok(());
|
||||
};
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
let pc_before = self.pc;
|
||||
let val = self.ordinary_get_with_target(obj, prop_name_si, self.regs[rd], a as u8)?;
|
||||
if self.accessor_frame_target_reg.take().is_none() {
|
||||
self.regs[a] = val;
|
||||
if self.accessor_frame_target_reg.take().is_some() {
|
||||
// getter 帧已入栈:结果由帧续行交付,此处不写结果寄存器。
|
||||
return Ok(());
|
||||
}
|
||||
if self.pc != pc_before {
|
||||
// 读路径在途抛错已展开到 catch/finally 入口:pc 由处理器接管,
|
||||
// 结果寄存器不得再写(regs[0] 持在途异常)。
|
||||
return Ok(());
|
||||
}
|
||||
self.regs[a] = val;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -645,10 +671,18 @@ impl Vm {
|
||||
return Ok(());
|
||||
};
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
let pc_before = self.pc;
|
||||
let val = self.ordinary_get_with_target(obj, prop_name_si, receiver, b as u8)?;
|
||||
if self.accessor_frame_target_reg.take().is_none() {
|
||||
self.regs[b] = val;
|
||||
if self.accessor_frame_target_reg.take().is_some() {
|
||||
// getter 帧已入栈:结果由帧续行交付,此处不写结果寄存器。
|
||||
return Ok(());
|
||||
}
|
||||
if self.pc != pc_before {
|
||||
// 读路径在途抛错已展开到 catch/finally 入口:pc 由处理器接管,
|
||||
// 结果寄存器不得再写(regs[0] 持在途异常)。
|
||||
return Ok(());
|
||||
}
|
||||
self.regs[b] = val;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -98,9 +98,7 @@ impl Vm {
|
||||
table.immutables.resize(table.modules.len(), OnceLock::new());
|
||||
table.si_tables.resize(table.modules.len(), OnceLock::new());
|
||||
|
||||
// 动态函数对象置 DYNAMIC 旗:`prototype` 属性按规范不可配置
|
||||
// (区别于普通函数的可配置面)。
|
||||
let func_val = self.create_function_object(base, self.current_gen, FunctionFlags(FunctionFlags::DYNAMIC));
|
||||
let func_val = self.create_function_object(base, self.current_gen, FunctionFlags::none());
|
||||
let func_obj = unsafe { &mut *func_val.as_js_object_ptr() };
|
||||
let length_si = self.kernel_core.perm_interner().intern("length").0;
|
||||
let name_si = self.kernel_core.perm_interner().intern("name").0;
|
||||
|
||||
@@ -565,6 +565,36 @@ impl Vm {
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
let mut current = obj_val;
|
||||
|
||||
// 代理 for-in:[[OwnPropertyKeys]] 经 ownKeys trap 路由(缺失落穿
|
||||
// target),[[GetOwnProperty]] 可枚举过滤经存储对象(代理本体无原型
|
||||
// 链可走,规范枚举面即自身键)。
|
||||
if current.is_object() {
|
||||
let arr = unsafe { &*current.as_js_object_ptr() };
|
||||
if arr.is_proxy_obj() {
|
||||
let (keys, storage) = match oxide_builtins::object::own_keys_proxy_aware(self, arr) {
|
||||
Ok(k) => k,
|
||||
Err(exc) => return self.raise_captured(exc),
|
||||
};
|
||||
for (si, store) in keys {
|
||||
if is_symbol_key(si) {
|
||||
continue;
|
||||
}
|
||||
let enumerable = storage
|
||||
.prop_meta_at(store)
|
||||
.map(|m| m.attributes.enumerable())
|
||||
.unwrap_or(PropAttributes::DEFAULT_DATA.enumerable());
|
||||
if !enumerable {
|
||||
continue;
|
||||
}
|
||||
let key_val = oxide_builtins::object::key_si_to_js_value(self, si);
|
||||
keys_vec.push((key_val, si));
|
||||
}
|
||||
let iter = Box::into_raw(Box::new(ForInIter { keys: keys_vec, index: 0 }));
|
||||
self.iters.push_for_in(iter);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// 数组把整型下标元素存在 prop_vec,而 prop_vec 不属于 shape 链。
|
||||
// 单独枚举它们(ES:数组下标是可枚举字符串键,按升序排在其它自有键之前)。
|
||||
// hole(删除标记)跳过——for-in 不枚举数组稀疏空洞。
|
||||
|
||||
+106
-39
@@ -108,34 +108,45 @@ impl Vm {
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue, target_reg: Option<u8>,
|
||||
) -> Result<JsValue, String> {
|
||||
vm_trace!("ordinary_get_inner: shape={} prop_si={}", obj.shape_id(), prop_name_si);
|
||||
// Proxy [[Get]]:get trap 权威(target、P、Receiver),trap 定义时其返回值
|
||||
// 即读结果;trap 缺失落穿 target 的 [[Get]](receiver 不变)。target 本身
|
||||
// 可为代理(嵌套代理),逐层递归应用同一语义直至非代理对象,再落普通读
|
||||
// 路径从该对象继续。
|
||||
let obj = if obj.is_proxy_obj() {
|
||||
let mut current = obj;
|
||||
loop {
|
||||
let target = oxide_builtins::proxy::proxy_state_ref(current).expect("状态盒").target;
|
||||
let key = oxide_builtins::object::key_si_to_js_value(self, prop_name_si);
|
||||
match crate::proxy::call_trap(self, current, self.perm_intern("get"), &[target, key, receiver])? {
|
||||
Some(v) => return Ok(v),
|
||||
None => {
|
||||
// SAFETY: target 由构造器校验为非空对象指针。
|
||||
let target_obj = unsafe { &*target.as_js_object_ptr() };
|
||||
if !target_obj.is_proxy_obj() {
|
||||
break target_obj;
|
||||
}
|
||||
current = target_obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
obj
|
||||
};
|
||||
let length_si = self.length_si;
|
||||
let mut current = Some(obj);
|
||||
let mut depth = 0usize;
|
||||
while let Some(obj) = current {
|
||||
// Proxy [[Get]]:链上任意层(入口对象或原型链)为代理时,get trap
|
||||
// 权威(target、P、Receiver),trap 定义时其返回值即读结果;trap 缺失
|
||||
// 落穿 target 的 [[Get]](receiver 不变)。target 本身可为代理(嵌套
|
||||
// 代理),逐层递归应用同一语义直至非代理对象,再落普通读路径从该对象
|
||||
// 继续。
|
||||
if obj.is_proxy_obj() {
|
||||
let mut chain = obj;
|
||||
loop {
|
||||
let target = oxide_builtins::proxy::proxy_state_ref(chain).expect("状态盒").target;
|
||||
let key = oxide_builtins::object::key_si_to_js_key(self, prop_name_si);
|
||||
match crate::proxy::call_trap(self, chain, self.perm_intern("get"), &[target, key, receiver]) {
|
||||
Ok(Some(v)) => {
|
||||
// 不变量检查:target 自身属性不可配置时,不可写数据属性
|
||||
// 的 trap 结果须与 target 值 SameValue,无 getter 的访问器
|
||||
// 属性要求 trap 结果为 undefined。违反抛 TypeError:字节码
|
||||
// 上下文就地展开到外围 catch(pc 已交处理器、regs[0] 持在途
|
||||
// 异常),builtin 上下文返回格式化 Err 由调用边界恢复。
|
||||
// 检查失败须立即返回,调用方不得再写结果寄存器或推进 pc。
|
||||
self.proxy_get_invariant_check(target, prop_name_si, v, self.native_call_depth != 0)?;
|
||||
return Ok(v);
|
||||
}
|
||||
Ok(None) => {
|
||||
// SAFETY: target 由构造器校验为非空对象指针。
|
||||
let target_obj = unsafe { &*target.as_js_object_ptr() };
|
||||
if !target_obj.is_proxy_obj() {
|
||||
current = Some(target_obj);
|
||||
break;
|
||||
}
|
||||
chain = target_obj;
|
||||
}
|
||||
Err(e) => return self.proxy_trap_call_failure(e).map(|_| JsValue::undefined()),
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 模块命名空间 exotic [[Get]]:条目表是权威状态,预注册但未初始化的
|
||||
// 导出读抛 ReferenceError;非 live ns(无表)落普通属性路径零行为变化。
|
||||
if obj.is_module_namespace() {
|
||||
@@ -875,6 +886,9 @@ impl Vm {
|
||||
) -> Option<(JsValue, bool, PropAttributes, JsValue, JsValue)> {
|
||||
// SAFETY: target 由构造器校验为非空对象指针。
|
||||
let target_obj = unsafe { &*target.as_js_object_ptr() };
|
||||
// target 自身为代理时其存储恒空:沿代理链落到终极 target 读自身
|
||||
// 描述符(不变量检查与属性补全均按终极 target 口径)。
|
||||
let target_obj = Self::ultimate_proxy_target(target_obj);
|
||||
let pos = self.get_own_property_slot(target_obj, prop_name_si)?;
|
||||
let meta = target_obj.prop_meta_at(pos)?;
|
||||
Some((target_obj.get_prop_at(pos), meta.is_accessor, meta.attributes, meta.get, meta.set))
|
||||
@@ -909,6 +923,35 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Proxy [[Get]] trap 返回值的不变量检查(规范 [[Get]] 步 9 形态):target
|
||||
/// 自身属性不可配置时,不可写数据属性要求 trap 结果与 target 值
|
||||
/// SameValue,无 getter 的访问器属性要求 trap 结果为 undefined;违反抛
|
||||
/// TypeError(builtin 上下文物化原值入 uncaught 侧通道,调用边界原值
|
||||
/// 重抛,字节码上下文就地抛可捕获异常并展开)。
|
||||
fn proxy_get_invariant_check(
|
||||
&mut self, target: JsValue, prop_name_si: u32, trap_result: JsValue, builtin: bool,
|
||||
) -> Result<(), String> {
|
||||
let Some((target_val, is_accessor, attrs, get, _)) = self.proxy_target_desc(target, prop_name_si) else {
|
||||
return Ok(());
|
||||
};
|
||||
if !attrs.configurable() {
|
||||
if is_accessor {
|
||||
if get.is_undefined() && !trap_result.is_undefined() {
|
||||
return self.proxy_invariant_failure(
|
||||
builtin,
|
||||
"Proxy get trap returned a value for a non-configurable target accessor without getter",
|
||||
);
|
||||
}
|
||||
} else if !attrs.writable() && !oxide_runtime_api::same_value(trap_result, target_val) {
|
||||
return self.proxy_invariant_failure(
|
||||
builtin,
|
||||
"Proxy get trap returned a different value for a non-writable target property",
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Proxy trap 不变量检查违反的两态出口:builtin 上下文物化 TypeError
|
||||
/// 写入 uncaught 侧通道(调用边界 `take_uncaught_value` 原值重抛),
|
||||
/// 字节码上下文就地抛可捕获异常并展开。
|
||||
@@ -1119,12 +1162,13 @@ impl Vm {
|
||||
}
|
||||
|
||||
/// Proxy receiver 的 `[[DefineOwnProperty]]`(defineProperty trap):trap
|
||||
/// 以(target、P、部分描述符 {value: V})调用;报 true 时读 target 自身
|
||||
/// 描述符补全部分描述符(数据形态取 target 的 writable/enumerable/
|
||||
/// configurable)、做不变量检查后写 target;报 false 或 target 不可扩展
|
||||
/// 的新属性定义返 `Ok(false)`(`[[Set]]` 投影为 false)。
|
||||
/// 以(target、P、部分描述符 {value: V})调用;报 true 时按
|
||||
/// `receiver_configurable` 补全属性(可配置按完整描述符全真、不可配置
|
||||
/// 保留 target 既有属性)、做不变量检查后写 target;trap 缺失落穿 target
|
||||
/// 的普通定义,同口径补全;报 false 或 target 不可扩展的新属性定义返
|
||||
/// `Ok(false)`(`[[Set]]` 投影为 false)。
|
||||
fn proxy_define_on_receiver(
|
||||
&mut self, receiver_proxy: &JsObject, prop_name_si: u32, val: JsValue,
|
||||
&mut self, receiver_proxy: &JsObject, prop_name_si: u32, val: JsValue, receiver_configurable: bool,
|
||||
) -> Result<bool, String> {
|
||||
let target = oxide_builtins::proxy::proxy_state_ref(receiver_proxy).expect("状态盒").target;
|
||||
let key = oxide_builtins::object::key_si_to_js_key(self, prop_name_si);
|
||||
@@ -1140,12 +1184,7 @@ impl Vm {
|
||||
};
|
||||
if let Some(r) = trap_result {
|
||||
if crate::proxy::trap_bool_result(r, "defineProperty")? {
|
||||
// 补全部分描述符:数据 targetDesc 取其 writable/enumerable/
|
||||
// configurable;targetDesc 缺失按新建数据属性(全真)。
|
||||
let completed = self
|
||||
.proxy_target_desc(target, prop_name_si)
|
||||
.map(|(_, _, attrs, _, _)| attrs)
|
||||
.unwrap_or(oxide_types::object::PropAttributes::DEFAULT_DATA);
|
||||
let completed = self.receiver_set_completed_attrs(target, prop_name_si, receiver_configurable);
|
||||
self.proxy_define_invariant_check(
|
||||
target,
|
||||
prop_name_si,
|
||||
@@ -1157,9 +1196,9 @@ impl Vm {
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
// trap 缺失:落穿 target 的普通定义(完整数据描述符)。
|
||||
let full = oxide_types::object::PropAttributes::DEFAULT_DATA;
|
||||
self.define_on_target_bool(target, prop_name_si, val, full)
|
||||
// trap 缺失:落穿 target 的普通定义(同口径补全)。
|
||||
let completed = self.receiver_set_completed_attrs(target, prop_name_si, receiver_configurable);
|
||||
self.define_on_target_bool(target, prop_name_si, val, completed)
|
||||
}
|
||||
|
||||
/// CreateDataProperty(Receiver, P, V)(Receiver 为代理):defineProperty
|
||||
@@ -1224,11 +1263,39 @@ impl Vm {
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
self.proxy_define_on_receiver(receiver_proxy, prop_name_si, val)
|
||||
// receiverDesc 为可写数据属性(规范步 7):可配置按完整描述符
|
||||
// 重定义,不可配置按部分描述符 {value: V} 保留既有属性。
|
||||
let receiver_configurable = self.read_desc_configurable(desc);
|
||||
self.proxy_define_on_receiver(receiver_proxy, prop_name_si, val, receiver_configurable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 读描述符对象的 configurable 字段(缺省视为 false:缺字段时无法确认
|
||||
/// 可配置,按不可配置分支保留既有属性,与部分描述符语义一致)。
|
||||
fn read_desc_configurable(&self, desc: JsValue) -> bool {
|
||||
// SAFETY: desc 为值携带的非空对象指针。
|
||||
let desc_obj = unsafe { &*desc.as_js_object_ptr() };
|
||||
self.resolve_property(desc_obj, self.perm_intern("configurable"))
|
||||
.map(oxide_runtime_api::to_boolean)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// `[[Set]]` receiver 定义补全属性:可配置 receiverDesc 按完整描述符
|
||||
/// 重定义(全真,规范步 7.b);不可配置 receiverDesc 保留 target 既有
|
||||
/// 属性(规范步 7.c 部分描述符 {value: V}),target 缺该属性时按新建
|
||||
/// 数据属性(全真)。
|
||||
fn receiver_set_completed_attrs(
|
||||
&self, target: JsValue, prop_name_si: u32, receiver_configurable: bool,
|
||||
) -> PropAttributes {
|
||||
if receiver_configurable {
|
||||
return oxide_types::object::PropAttributes::DEFAULT_DATA;
|
||||
}
|
||||
self.proxy_target_desc(target, prop_name_si)
|
||||
.map(|(_, _, attrs, _, _)| attrs)
|
||||
.unwrap_or(oxide_types::object::PropAttributes::DEFAULT_DATA)
|
||||
}
|
||||
|
||||
/// TypedArray 界内规范键在 `receiver` ≠ TA 时的 [[Set]] 语义:
|
||||
///
|
||||
/// # 步骤
|
||||
|
||||
@@ -40,9 +40,6 @@ impl FunctionFlags {
|
||||
pub(crate) const CLASS_CONSTRUCTOR: u8 = 0b010;
|
||||
/// 派生构造器标志位(bit2)。
|
||||
pub(crate) const DERIVED_CONSTRUCTOR: u8 = 0b100;
|
||||
/// 动态函数标志位(bit3):经 Function 构造器创建的函数对象,其
|
||||
/// `prototype` 属性不可配置。
|
||||
pub(crate) const DYNAMIC: u8 = 0b1000;
|
||||
|
||||
/// 无标志(普通函数)。
|
||||
pub(crate) const fn none() -> Self {
|
||||
@@ -78,11 +75,6 @@ impl FunctionFlags {
|
||||
pub(crate) const fn is_derived_constructor(self) -> bool {
|
||||
self.0 & Self::DERIVED_CONSTRUCTOR != 0
|
||||
}
|
||||
|
||||
/// 是否动态函数(经 Function 构造器创建)。
|
||||
pub(crate) const fn is_dynamic(self) -> bool {
|
||||
self.0 & Self::DYNAMIC != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// 模块级装载缓存条目三件套:已转换常量池、BigInt 自持存储、si 侧表。
|
||||
@@ -903,21 +895,16 @@ impl Vm {
|
||||
prototype.bump_generation();
|
||||
}
|
||||
|
||||
// 函数自身 `prototype` 属性:生成器与动态函数(Function 构造器产物)
|
||||
// writable:true / enumerable:false / configurable:false;普通函数与
|
||||
// 构造器 writable:true / enumerable:false / configurable:true。显式
|
||||
// 落元数据——缺元数据按默认描述符(可枚举)判定,`prototype` 会泄漏
|
||||
// 进 keys / for-in / 聚合枚举。
|
||||
// 函数自身 `prototype` 属性:writable:true / enumerable:false /
|
||||
// configurable:false(规范 10.2.1 口径,生成器与动态函数同口径)。
|
||||
// 显式落元数据——缺元数据按默认描述符(可枚举)判定,`prototype`
|
||||
// 会泄漏进 keys / for-in / 聚合枚举。
|
||||
let prototype_si = self.kernel_core.perm_interner().intern("prototype").0;
|
||||
let func = unsafe { &mut *obj_ptr };
|
||||
let prototype_shape = self.kernel_core.shape_forge().make_shape(func.shape_id(), prototype_si);
|
||||
func.set_shape_id(prototype_shape);
|
||||
let prototype_pos = func.push_prop(prototype_val);
|
||||
if is_generator || is_async_generator || flags.is_dynamic() {
|
||||
func.set_data_meta(prototype_pos, PropAttributes::new(true, false, false));
|
||||
} else {
|
||||
func.set_data_meta(prototype_pos, PropAttributes::new(true, false, true));
|
||||
}
|
||||
func.set_data_meta(prototype_pos, PropAttributes::new(true, false, false));
|
||||
func.bump_generation();
|
||||
}
|
||||
|
||||
|
||||
@@ -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