fix: 原型链直读改经 get_prototype_of 路由

This commit is contained in:
2026-10-18 12:24:10 +00:00
parent 659bb272c4
commit 89255abd17
11 changed files with 126 additions and 67 deletions
Generated
+21 -21
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1228,7 +1228,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1237,7 +1237,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1258,7 +1258,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1272,7 +1272,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1288,7 +1288,7 @@ dependencies = [
[[package]]
name = "oxide_engine"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1300,7 +1300,7 @@ dependencies = [
[[package]]
name = "oxide_fold"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"num-bigint",
"oxide_bytecode",
@@ -1312,7 +1312,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1321,7 +1321,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"bumpalo",
"dashmap",
@@ -1335,7 +1335,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1347,7 +1347,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"tracing",
"tracing-appender",
@@ -1356,11 +1356,11 @@ dependencies = [
[[package]]
name = "oxide_net"
version = "0.20.13"
version = "0.20.14"
[[package]]
name = "oxide_parser"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1375,7 +1375,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1391,7 +1391,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"num-bigint",
"num-traits",
@@ -1404,7 +1404,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1424,7 +1424,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"bumpalo",
"dashmap",
@@ -1437,7 +1437,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.20.13"
version = "0.20.14"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -25,7 +25,7 @@ members = [
]
[workspace.package]
version = "0.20.13"
version = "0.20.14"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+10 -2
View File
@@ -146,7 +146,12 @@ pub fn function_symbol_has_instance<H: VmHost>(vm: &mut H, args: &[u8]) -> Nativ
let proto_ptr = proto_val.as_js_object_ptr();
// 沿 O 原型链与 C.prototype 比对;深度上限防止原型环死循环。
let mut cur = unsafe { &*o_val.as_js_object_ptr() }.proto();
// 原型链走查经 [[GetPrototypeOf]] 路由:代理经 getPrototypeOf trap,
// 非代理对象零行为变化。
let mut cur = match vm.get_prototype_of(unsafe { &*o_val.as_js_object_ptr() }) {
Ok(v) => v,
Err(e) => return NativeResult::Err(to_string_error_value(vm, &e)),
};
for _ in 0..1024 {
if !cur.is_object() {
return NativeResult::Ok(JsValue::bool(false));
@@ -154,7 +159,10 @@ pub fn function_symbol_has_instance<H: VmHost>(vm: &mut H, args: &[u8]) -> Nativ
if cur.as_js_object_ptr() == proto_ptr {
return NativeResult::Ok(JsValue::bool(true));
}
cur = unsafe { &*cur.as_js_object_ptr() }.proto();
cur = match vm.get_prototype_of(unsafe { &*cur.as_js_object_ptr() }) {
Ok(v) => v,
Err(e) => return NativeResult::Err(to_string_error_value(vm, &e)),
};
}
NativeResult::Ok(JsValue::bool(false))
}
+2 -1
View File
@@ -536,7 +536,8 @@ pub trait VmHost {
fn function_is_strict(&self, obj: &JsObject) -> bool;
/// caller/arguments 访问是否受限:严格函数对象,或生成器 / 异步 /
/// 异步生成器函数对象([[Prototype]] 为三个动态函数原型之一)。
fn function_is_restricted(&self, obj: &JsObject) -> bool;
/// 原型经 [[GetPrototypeOf]] 路由判定(代理函数对象经 getPrototypeOf trap)。
fn function_is_restricted(&mut self, obj: &JsObject) -> Result<bool, String>;
fn step_rng(&mut self);
fn math_rng_value(&self) -> f64;
fn sub_module_function_name(&self, gen: u32, sub_idx: u16) -> String;
+13 -5
View File
@@ -5,7 +5,7 @@ use crate::{vm_debug, vm_trace};
use oxide_builtins::iterator::make_iterator_for_value;
use oxide_builtins::{builtins_debug, builtins_trace};
use oxide_bytecode::opcode;
use oxide_runtime_api::{to_boolean, NativeResult};
use oxide_runtime_api::{to_boolean, NativeResult, VmHost};
use oxide_types::object::{Cell, JsObject, PropAttributes};
use oxide_types::value::JsValue;
@@ -653,7 +653,9 @@ impl Vm {
return Ok(true);
}
let callee_obj = unsafe { &*callee.as_js_object_ptr() };
let super_ctor = callee_obj.proto();
// super 构造器取当前帧 callee 的 [[GetPrototypeOf]](规范 10.2.11 面):
// 代理函数对象经 getPrototypeOf trap 取基。
let super_ctor = self.get_prototype_of(callee_obj)?;
if !super_ctor.is_object() {
self.raise_error_kind("TypeError", "super constructor is not an object")?;
return Ok(true);
@@ -781,7 +783,9 @@ impl Vm {
let home_obj = unsafe { &*home_object.as_js_object_ptr() };
// 规范步序:GetSuperBase 先于 ToPropertyKey;基非对象可强转时抛
// TypeError(RequireObjectCoercible 面)。
let super_base = home_obj.proto();
// GetSuperBase 取 home 对象的 [[GetPrototypeOf]](规范 10.2.11 面):
// 代理 home 对象经 getPrototypeOf trap 取基。
let super_base = self.get_prototype_of(home_obj)?;
if !super_base.is_object() {
self.raise_type_error("Cannot convert null or undefined to object")?;
return Ok(true);
@@ -826,7 +830,9 @@ impl Vm {
let home_obj = unsafe { &*home_object.as_js_object_ptr() };
// 规范步序:GetSuperBase 先于 ToPropertyKey;基非对象可强转时抛
// TypeError(RequireObjectCoercible 面)。
let super_base = home_obj.proto();
// GetSuperBase 取 home 对象的 [[GetPrototypeOf]](规范 10.2.11 面):
// 代理 home 对象经 getPrototypeOf trap 取基。
let super_base = self.get_prototype_of(home_obj)?;
if !super_base.is_object() {
self.raise_type_error("Cannot convert null or undefined to object")?;
return Ok(true);
@@ -1158,7 +1164,9 @@ impl Vm {
return Ok(true);
}
let callee_obj = unsafe { &*callee.as_js_object_ptr() };
let super_ctor = callee_obj.proto();
// super 构造器取当前帧 callee 的 [[GetPrototypeOf]](规范 10.2.11 面):
// 代理函数对象经 getPrototypeOf trap 取基。
let super_ctor = self.get_prototype_of(callee_obj)?;
if !super_ctor.is_object() {
self.raise_error_kind("TypeError", "super constructor is not an object")?;
return Ok(true);
+18 -12
View File
@@ -1,6 +1,7 @@
use crate::{ic_debug, ic_trace, vm_trace};
use oxide_bytecode::opcode::{OpCode, IC_EXT_WORDS};
use oxide_runtime_api as coercion;
use oxide_runtime_api::VmHost;
use oxide_types::object::JsObject;
use oxide_types::private_key::{int_key_value, is_int_key, make_int_key, make_private_name_id};
use oxide_types::value::JsValue;
@@ -123,8 +124,10 @@ impl Vm {
/// 沿实例→proto 链查找私有方法/访问器槽(存于类的 home=proto)。字段槽不跨原型
/// (PrivateFieldFind 只查 own)——链上遇到字段槽(无 hole meta)即拒绝,防
/// `Object.create(instance)` 穿透读取实例字段。
fn find_private_method_slot(&self, obj: &JsObject, private_key: u32) -> Option<*mut JsObject> {
let mut proto = obj.proto();
fn find_private_method_slot(
&mut self, obj: &JsObject, private_key: u32,
) -> Result<Option<*mut JsObject>, String> {
let mut proto = self.get_prototype_of(obj)?;
let mut depth = 0usize;
while proto.is_object() && depth < MAX_PROTO_CHAIN_DEPTH {
depth += 1;
@@ -133,13 +136,13 @@ impl Vm {
// 方法/访问器槽有 hole 标记(私有方法)或 is_accessor 标记(私有 getter/setter);
// 字段槽(存实例)沿链出现说明接收者并非本类实例 → 拒绝穿透。
if proto_obj.prop_meta_at(pos).is_some_and(|m| m.is_hole() || m.is_accessor) {
return Some(proto_obj as *const JsObject as *mut JsObject);
return Ok(Some(proto_obj as *const JsObject as *mut JsObject));
}
return None;
return Ok(None);
}
proto = proto_obj.proto();
proto = self.get_prototype_of(proto_obj)?;
}
None
Ok(None)
}
/// 私有方法/访问器/静态字段访问的 brand 检查:接收者 own 的 brand 槽(构造时写入
@@ -185,7 +188,7 @@ impl Vm {
return Ok(());
}
// 方法路径:own 无槽(私有方法/访问器存于 home=proto)→ 沿链找方法槽 + brand 检查。
let Some(home_ptr) = self.find_private_method_slot(obj, private_key) else {
let Ok(Some(home_ptr)) = self.find_private_method_slot(obj, private_key) else {
return self.raise_type_error("private field brand check failed");
};
if brand_reg != 0 {
@@ -244,7 +247,7 @@ impl Vm {
return Ok(());
}
// 方法路径:own 无槽(私有方法/访问器存于 home=proto)→ 沿链找方法槽 + brand 检查。
let Some(home_ptr) = self.find_private_method_slot(obj, private_key) else {
let Ok(Some(home_ptr)) = self.find_private_method_slot(obj, private_key) else {
return self.raise_type_error("private field brand check failed");
};
if brand_reg != 0 {
@@ -465,8 +468,10 @@ impl Vm {
ic_debug!("IC_GET write-back own shape={} slot={}", obj.shape_id(), pos);
}
} else {
// 沿原型链查找真正拥有该属性的对象。
let mut cursor = obj.proto().as_js_object_ptr();
// 沿原型链查找真正拥有该属性的对象(逐层经 [[GetPrototypeOf]]
// 路由,代理层经 trap 取原型)。
let proto = self.get_prototype_of(obj)?;
let mut cursor = proto.as_js_object_ptr();
let mut depth = 1u8;
while !cursor.is_null() {
let co_ref = unsafe { &*cursor };
@@ -484,10 +489,11 @@ impl Vm {
}
break;
}
if !co_ref.proto().is_object() {
let next_proto = self.get_prototype_of(co_ref)?;
if !next_proto.is_object() {
break;
}
cursor = co_ref.proto().as_js_object_ptr();
cursor = next_proto.as_js_object_ptr();
depth += 1;
}
}
+16
View File
@@ -119,6 +119,10 @@ pub(crate) fn ic_get_hit(obj: &JsObject, shape_id: u32, slot_index: u32, proto_d
return None;
}
let target_ref = unsafe { &*target };
// 链上目标为代理:直读自身槽绕过 get trap,恒 miss,落穿慢路径路由 trap。
if target_ref.is_proxy_obj() {
return None;
}
if target_ref.shape_id() == shape_id && slot_index < target_ref.prop_vec_len() as u32 {
Some(target_ref.get_prop_shape(slot_index))
} else {
@@ -153,6 +157,10 @@ pub(crate) fn ic_get_hit_poly(obj: &JsObject, bytecode: &[Instr], ext_pc: usize)
continue;
}
let target_ref = unsafe { &*target };
// 链上目标为代理:恒 miss,落穿慢路径路由 get trap。
if target_ref.is_proxy_obj() {
continue;
}
if target_ref.shape_id() == shape_id && slot_index < target_ref.prop_vec_len() as u32 {
return Some(target_ref.get_prop_shape(slot_index));
}
@@ -177,6 +185,10 @@ pub(crate) fn ic_set_hit(obj: &mut JsObject, shape_id: u32, slot_index: u32, pro
return false;
}
let target_ref = unsafe { &*target };
// 链上目标为代理:直写自身槽绕过 set trap,恒 miss,落穿慢路径路由 trap。
if target_ref.is_proxy_obj() {
return false;
}
if target_ref.shape_id() == shape_id && slot_index < target_ref.prop_vec_len() as u32 {
unsafe { &mut *(target as *mut JsObject) }.set_prop_shape(slot_index, value);
true
@@ -209,6 +221,10 @@ pub(crate) fn ic_set_hit_poly(obj: &mut JsObject, bytecode: &[Instr], ext_pc: us
continue;
}
let target_ref = unsafe { &*target };
// 链上目标为代理:恒 miss,落穿慢路径路由 set trap。
if target_ref.is_proxy_obj() {
continue;
}
if target_ref.shape_id() == shape_id && slot_index < target_ref.prop_vec_len() as u32 {
unsafe { &mut *(target as *mut JsObject) }.set_prop_shape(slot_index, value);
return true;
+8 -1
View File
@@ -191,6 +191,11 @@ impl Vm {
while proto.is_object() && depth < MAX_PROTO_CHAIN_DEPTH {
depth += 1;
let proto_obj = unsafe { &*proto.as_js_object_ptr() };
// 链上代理层:不可变签名无法同步调 get trap,保守返 None,
// 与顶层代理口径一致(权威路径由可变签名入口承载)。
if proto_obj.is_proxy_obj() {
return None;
}
if let Some(pos) = self
.kernel_core
.shape_forge()
@@ -300,7 +305,9 @@ impl Vm {
break;
}
depth += 1;
let proto = obj.proto();
// 链上续层经 [[GetPrototypeOf]] 路由:代理层经 trap 取原型,
// 取到的代理层在下一轮循环经入口守卫路由 has trap。
let proto = self.get_prototype_of(obj)?;
current = proto.is_object().then(|| unsafe { &*proto.as_js_object_ptr() });
}
Ok(false)
+8 -7
View File
@@ -571,20 +571,21 @@ impl oxide_runtime_api::VmHost for Vm {
// 按严格处理(受限访问器对其抛错)。
self.callee_module(obj).map(|m| m.is_strict).unwrap_or(true)
}
fn function_is_restricted(&self, obj: &JsObject) -> bool {
fn function_is_restricted(&mut self, obj: &JsObject) -> Result<bool, String> {
// 严格函数对象,或 [[Prototype]] 为三个动态函数原型之一(生成器 /
// 异步 / 异步生成器函数):caller/arguments 访问一律受限。
// 异步 / 异步生成器函数):caller/arguments 访问一律受限。原型经
// [[GetPrototypeOf]] 路由取(代理函数对象经 getPrototypeOf trap)。
if self.function_is_strict(obj) {
return true;
return Ok(true);
}
let proto = obj.proto();
let proto = self.get_prototype_of(obj)?;
if !proto.is_object() {
return false;
return Ok(false);
}
let p = proto.as_js_object_ptr() as *const JsObject;
std::ptr::eq(p, self.realm.generator_function_proto.borrow().as_ptr())
Ok(std::ptr::eq(p, self.realm.generator_function_proto.borrow().as_ptr())
|| std::ptr::eq(p, self.realm.async_function_proto.borrow().as_ptr())
|| std::ptr::eq(p, self.realm.async_generator_function_proto.borrow().as_ptr())
|| std::ptr::eq(p, self.realm.async_generator_function_proto.borrow().as_ptr()))
}
fn step_rng(&mut self) {
self.step_rng()
+7 -3
View File
@@ -498,7 +498,10 @@ impl Vm {
}
let ctor_proto_ptr = ctor_proto.as_js_object_ptr();
let mut proto = unsafe { &*lhs_val.as_js_object_ptr() }.proto();
// 原型链走查经 [[GetPrototypeOf]] 路由:代理经 getPrototypeOf trap,
// 非代理对象零行为变化。
let lhs_obj = unsafe { &*lhs_val.as_js_object_ptr() };
let mut proto = self.get_prototype_of(lhs_obj)?;
let mut depth = 0usize;
loop {
if !proto.is_object() {
@@ -515,7 +518,7 @@ impl Vm {
self.regs[rd] = JsValue::bool(true);
break;
}
proto = unsafe { &*proto_ptr }.proto();
proto = self.get_prototype_of(unsafe { &*proto_ptr })?;
}
Ok(())
}
@@ -681,7 +684,8 @@ impl Vm {
// shape 链按叶→根遍历(与插入序相反);在接上原型前把本对象的切片
// 翻转为插入序。
keys_vec[obj_start..].reverse();
current = cur.proto();
// 链上续层经 [[GetPrototypeOf]] 路由:代理层经 trap 取原型。
current = self.get_prototype_of(cur)?;
}
// 模块命名空间 exotic:EnumerateObjectProperties 构建键表时逐键走
+22 -14
View File
@@ -246,7 +246,9 @@ impl Vm {
break;
}
depth += 1;
let proto = obj.proto();
// 原型链走查经 [[GetPrototypeOf]] 路由:代理层调 getPrototypeOf
// trap,非代理对象零行为变化。
let proto = self.get_prototype_of(obj)?;
current = proto.is_object().then(|| unsafe { &*proto.as_js_object_ptr() });
if let Some(proto_obj) = current {
vm_trace!("ordinary_get proto step: depth={} shape={}", depth, proto_obj.shape_id());
@@ -312,16 +314,18 @@ impl Vm {
Ok(true)
}
fn inherited_property_meta(&self, obj: &JsObject, prop_name_si: u32) -> Option<PropMetaEntry> {
fn inherited_property_meta(
&mut self, obj: &JsObject, prop_name_si: u32,
) -> Result<Option<PropMetaEntry>, String> {
vm_trace!("inherited_property_meta: shape={} prop_si={}", obj.shape_id(), prop_name_si);
let mut proto = obj.proto();
let mut proto = self.get_prototype_of(obj)?;
let mut depth = 0usize;
while proto.is_object() && depth < MAX_PROTO_CHAIN_DEPTH {
depth += 1;
let proto_obj = unsafe { &*proto.as_js_object_ptr() };
// 代理层:无 getOwnPropertyDescriptor trap 时 [[GetOwnProperty]]
// 落穿 target 的自身属性,故按 target 的自身槽位判定;链上续层取
// 代理自身的原型([[GetPrototypeOf]] 同样落穿 target)。
// 落穿 target 的自身属性,故按 target 的自身槽位判定;链上续层经
// [[GetPrototypeOf]] 路由取下一层(代理经 trap,非代理零行为变化)。
let effective = if proto_obj.is_proxy_obj() {
let target = oxide_builtins::proxy::proxy_state_ref(proto_obj).expect("状态盒").target;
// SAFETY: target 由构造器校验为非空对象指针。
@@ -330,11 +334,11 @@ impl Vm {
proto_obj
};
if let Some(pos) = self.get_own_property_slot(effective, prop_name_si) {
return effective.prop_meta_at(pos);
return Ok(effective.prop_meta_at(pos));
}
proto = proto_obj.proto();
proto = self.get_prototype_of(proto_obj)?;
}
None
Ok(None)
}
/// 全局 builtin 属性在原始存储侧(全局对象自身属性槽,下文简称 A 侧)写成功后
@@ -705,7 +709,9 @@ impl Vm {
// 即 receiver);原型链走完(或深度到界)时 CreateDataProperty(Receiver,
// P, V)。
if depth < MAX_PROTO_CHAIN_DEPTH {
let parent = obj.proto();
// 经 [[GetPrototypeOf]] 路由取父层:代理父层经 trap 取原型,
// 父层自身 [[Set]] 由递归入口判定。
let parent = self.get_prototype_of(obj)?;
if parent.is_object() {
// SAFETY: parent 为值携带的非空对象指针,对象在本会话内存活;
// 写路径不移动对象。
@@ -1451,8 +1457,9 @@ impl Vm {
}
return Ok(resolved);
}
// 沿原型链查找继承属性。
let mut cursor = obj.proto().as_js_object_ptr();
// 沿原型链查找继承属性(逐层经 [[GetPrototypeOf]] 路由,代理层经 trap)。
let proto = self.get_prototype_of(obj)?;
let mut cursor = proto.as_js_object_ptr();
let mut depth = 1u8;
while !cursor.is_null() {
let co = unsafe { &*cursor };
@@ -1462,10 +1469,11 @@ impl Vm {
}
break;
}
if !co.proto().is_object() {
let next_proto = self.get_prototype_of(co)?;
if !next_proto.is_object() {
break;
}
cursor = co.proto().as_js_object_ptr();
cursor = next_proto.as_js_object_ptr();
depth += 1;
}
Ok(resolved)
@@ -1531,7 +1539,7 @@ impl Vm {
let use_frame_push = contract.use_frame_push;
// 原型链同名 accessor 须触发 setter、只读 data 须报错(与 ordinary_set 一致),
// 命中时不得创建 own 属性(规范 [[Set]] shadow 语义)。
if let Some(meta) = self.inherited_property_meta(obj, prop_name_si) {
if let Ok(Some(meta)) = self.inherited_property_meta(obj, prop_name_si) {
if meta.is_accessor {
if meta.set.is_undefined() {
// 继承无 setter:严格抛错,sloppy 静默 no-op。