mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user