feat: 函数代理 apply 与 construct trap 路由落地

This commit is contained in:
2026-10-17 07:53:31 +00:00
parent addaab394d
commit e5db10581e
12 changed files with 437 additions and 152 deletions
Generated
+21 -21
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1228,7 +1228,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1237,7 +1237,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1258,7 +1258,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1272,7 +1272,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1288,7 +1288,7 @@ dependencies = [
[[package]]
name = "oxide_engine"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1300,7 +1300,7 @@ dependencies = [
[[package]]
name = "oxide_fold"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"num-bigint",
"oxide_bytecode",
@@ -1312,7 +1312,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1321,7 +1321,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"bumpalo",
"dashmap",
@@ -1335,7 +1335,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1347,7 +1347,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"tracing",
"tracing-appender",
@@ -1356,11 +1356,11 @@ dependencies = [
[[package]]
name = "oxide_net"
version = "0.20.6"
version = "0.20.7"
[[package]]
name = "oxide_parser"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1375,7 +1375,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1391,7 +1391,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"num-bigint",
"num-traits",
@@ -1404,7 +1404,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1424,7 +1424,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"bumpalo",
"dashmap",
@@ -1437,7 +1437,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.20.6"
version = "0.20.7"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -25,7 +25,7 @@ members = [
]
[workspace.package]
version = "0.20.6"
version = "0.20.7"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+20 -2
View File
@@ -17,6 +17,12 @@ use super::common::{
pub fn array_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let proto = vm.builtin_proto(ProtoKind::ArrayProto);
let proto_val = JsValue::from_js_object(proto);
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
// 构造形态(newTarget 在场):产物须为 Array exotic 对象(Array.isArray 认)
// 且原型取 newTarget.prototype(派生类 / 代理 newTarget 由此拿到子类实例),
// 原型已由构造入口预写入 receiver,直接读取复用;普通调用形态自建
// Array.prototype 原型的新数组。
let in_construct = vm.constructing_native() && this_val.is_object();
if args.len() == 2 {
let val = vm.reg(args[1]);
@@ -25,7 +31,13 @@ pub fn array_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
Ok(n) => n,
Err(err) => return NativeResult::Err(err),
};
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n));
let arr_proto = if in_construct {
// SAFETY: receiver 是构造帧分配的 this(本 session 存活),只读原型。
unsafe { &*this_val.as_js_object_ptr() }.proto()
} else {
proto_val
};
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, arr_proto, n));
// 数值长度分支建 n 个空洞而非 present-undefined:`in`、for-in、
// Object.keys 按缺失处理,元素 Get 落原型链。物理区以稠密上限
// 钳位(越界索引无物理槽,逻辑长度已覆盖)。
@@ -39,7 +51,13 @@ pub fn array_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
}
let n_elems = if args.len() > 1 { args.len() - 1 } else { 0 };
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n_elems));
let arr_proto = if in_construct {
// SAFETY: receiver 是构造帧分配的 this(本 session 存活),只读原型。
unsafe { &*this_val.as_js_object_ptr() }.proto()
} else {
proto_val
};
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, arr_proto, n_elems));
for i in 0..n_elems {
unsafe {
(*arr).set_prop_at(i, vm.reg(args[1 + i]));
+13 -5
View File
@@ -70,15 +70,23 @@ fn check_target_and_handler(target: JsValue, handler: JsValue) -> Result<(), &'s
Ok(())
}
/// 不变量检查(规范 10.2.4 步骤 4-5):handler 的 isExtensible 与 setPrototypeOf
/// trap 若存在,其布尔值须与 target 实际状态一致,否则抛 TypeError。
/// 不变量检查(规范 10.2.4 步骤 1、4-5):target 可调用时须可扩展,handler
/// 的 isExtensible 与 setPrototypeOf trap 若存在,其布尔值须与 target 实际
/// 状态一致,否则抛 TypeError。
///
/// # 边界与前提
/// - 仅当 handler 自有对应 trap 属性时检查(继承属性不触发)。
/// - trap 读为传播读,getter 抛错原值上抛。
fn proxy_create_invariants<H: VmHost>(
vm: &mut H, target_obj: &JsObject, handler_obj: &JsObject, handler: JsValue,
vm: &mut H, target: JsValue, target_obj: &JsObject, handler_obj: &JsObject, handler: JsValue,
) -> Result<(), JsValue> {
// target 可调用且不可扩展:函数代理的不变量无法满足,抛 TypeError。
if crate::iterator::is_callable(target) && !target_obj.is_extensible() {
return Err(crate::error::create_type_error(
vm,
"Proxy target must be extensible if it is callable",
));
}
// isExtensible trap:handler 报 true 时 target 须可扩展,报 false 时须不可扩展。
let si_is_extensible = vm.perm_intern("isExtensible");
if vm.get_own_property_slot(handler_obj, si_is_extensible).is_some() {
@@ -160,7 +168,7 @@ pub fn proxy_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
// SAFETY: check_target_and_handler 已校验非空对象指针。
let target_obj = unsafe { &*target.as_js_object_ptr() };
let handler_obj = unsafe { &*handler.as_js_object_ptr() };
if let Err(exc) = proxy_create_invariants(vm, target_obj, handler_obj, handler) {
if let Err(exc) = proxy_create_invariants(vm, target, target_obj, handler_obj, handler) {
return NativeResult::Err(exc);
}
NativeResult::Ok(create_proxy_object(vm, target, handler))
@@ -187,7 +195,7 @@ pub fn proxy_revocable<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
// SAFETY: check_target_and_handler 已校验非空对象指针。
let target_obj = unsafe { &*target.as_js_object_ptr() };
let handler_obj = unsafe { &*handler.as_js_object_ptr() };
if let Err(exc) = proxy_create_invariants(vm, target_obj, handler_obj, handler) {
if let Err(exc) = proxy_create_invariants(vm, target, target_obj, handler_obj, handler) {
return NativeResult::Err(exc);
}
let proxy_val = create_proxy_object(vm, target, handler);
+7 -100
View File
@@ -108,12 +108,13 @@ impl Vm {
}
// 无本体函数对象(Function.prototype):[[Call]] 接受任意实参
// 返回 undefined,不执行函数体。
if obj.native_fn().is_none() && obj.sub_module_index() == 0 {
if obj.native_fn().is_none() && obj.sub_module_index() == 0 && !obj.is_proxy_obj() {
self.regs[0] = JsValue::undefined();
return Ok(());
}
if obj.native_fn().is_some() {
// 尾调用转发为普通调用形态。
if obj.is_proxy_obj() || obj.native_fn().is_some() {
// 尾调用转发为普通调用形态(代理经 call_function_sync 的
// apply trap 路由,缺失落穿 target)。
self.constructing_native = false;
match self.call_function_sync(callee, this, &args) {
Ok(val) => {
@@ -1094,103 +1095,9 @@ impl Vm {
return self.dispatch_new_bound(rd, constructor, new_obj, args, 0);
}
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
let new_obj = self.alloc_object(JsObject::new_empty(
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
JsValue::from_js_object(proto_ptr),
));
// GpFC 按构造器种类分读形:native 构造器保持裸存储读(占位 receiver
// 不可观测,构造体自重读覆盖可观测面);字节码构造器走传播读(getter
// 异常原值上抛,非对象结果回落 Object.prototype)。
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
let proto_val = if ctor_obj.native_fn().is_some() {
self.resolve_property(ctor_obj, proto_si)
} else {
let pc_before = self.pc;
match self.ordinary_get(ctor_obj, proto_si, constructor) {
Ok(v) if self.pc == pc_before => Some(v),
Ok(_) => {
// getter 抛且已有 catch/finally 接手:展开已跳到落点,停构造流。
return Ok(true);
}
Err(err) => {
// 无处理器(或重入在途异常):恢复原值走异常通道。
let exc = self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &err));
self.exception_value = Some(exc);
self.pending_error_kind = Some(self.thrown_error_kind(exc));
return self.unwind().map(|_| true);
}
}
};
if let Some(v) = proto_val {
if v.is_object() {
let new_obj_mut = unsafe { &mut *new_obj };
let proto_obj_ptr = v.as_js_object_ptr();
let _ = new_obj_mut.set_proto(JsValue::from_js_object(proto_obj_ptr));
}
}
let new_obj_val = JsValue::object(new_obj as *mut u8);
if ctor_obj.native_fn().is_some() {
// native 构造器:receiver 为新对象,值传递调用。newTarget 快照后置入
// reg(255) 暴露给 native 构造器(调用后恢复原值);构造形态标记同窗
// 夹持(调用返回即恢复,先于结果分派,异常结局不残留)。
let saved_new_target = self.regs[255];
let saved_constructing = self.constructing_native;
self.regs[255] = constructor;
self.constructing_native = true;
let result = self.call_function_sync(constructor, new_obj_val, &args);
self.constructing_native = saved_constructing;
self.regs[255] = saved_new_target;
match result {
Ok(v) => {
self.regs[rd] = if v.is_object() { v } else { new_obj_val };
Ok(false)
}
Err(_) => {
let exc = self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_error(self, "constructor call failed"));
self.exception_value = Some(exc);
self.pending_error_kind = Some(self.thrown_error_kind(exc));
self.unwind().map(|_| true)
}
}
} else if ctor_obj.sub_module_index() > 0 {
// 按构造器自身记录的表代际解析。
let sub = self.callee_module(ctor_obj);
// 生成器函数不是构造器:`new g()` 抛 TypeError。
if matches!(sub, Some(m) if m.is_generator) {
return self.raise_type_error("g is not a constructor").map(|_| true);
}
// 异步函数不是构造器:`new f()` 抛 TypeError。
if matches!(sub, Some(m) if m.is_async) {
return self.raise_type_error("g is not a constructor").map(|_| true);
}
let this_value = if ctor_obj.is_derived_constructor() {
JsValue::undefined()
} else {
new_obj_val
};
self.push_bytecode_frame(
constructor,
this_value,
FrameArgs::Slice(&args),
ConstructArgs::new(rd as u8, new_obj_val, constructor),
FrameContinuation::None,
0,
)?;
Ok(true)
} else {
// 无函数体的函数对象(如 Function.prototype):IsConstructor 为假,
// 按规范抛 TypeError(与非 spread 的 NEW_EXPRESSION 臂同形)。
self.raise_type_error("NEW_EXPRESSION: constructor is not a constructor")
.map(|_| true)
}
// Proxy [[Construct]] 路由、新对象分配、原型读与 native / 字节码分派
// 收敛到统一入口(spread 路径无调用点存活上界,窗口取 0)。
self.dispatch_new_target(ctor_obj, constructor, constructor, rd, &args, 0)
}
/// SUPER_CALL_SPREAD:物化实参后按父构造器目标分派(native 走值传递,字节码走帧)。
+10
View File
@@ -366,6 +366,16 @@ impl Vm {
}
let obj = unsafe { &*obj_ptr };
// 代理对象恒走慢路径:IC 键不含 type_tag,shape_id 与同形普通对象碰撞时
// 命中会绕过 get trap 直读代理自身属性槽(恒 miss 守卫的读入口侧)。
if obj.is_proxy_obj() {
let prop_name_si = self.property_key_si(self.regs[b])?;
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;
}
return Ok(());
}
// 入口快判:键寄存器恒为 perm 串,指针相等即键为 "length";
// 数组接收者直返逻辑长度,免 IC 读与键解析(与虚拟长度槽口径一致)。
let key_val = self.regs[b];
@@ -75,6 +75,23 @@ impl Vm {
// SAFETY: 调用方入口已对 new_target 做过同谓词 IsConstructor 校验,
// 指针非空且指向存活对象。
let nt_obj = unsafe { &*new_target.as_js_object_ptr() };
// Proxy [[Construct]]:construct trap 权威(target, argumentsList,
// newTarget);trap 缺失落穿 target 的普通 [[Construct]](newTarget 不变)。
if ctor_obj.is_proxy_obj() {
match crate::proxy::proxy_construct_trap(self, ctor_obj, args, new_target) {
Ok(Some(v)) => return Ok(v),
Ok(None) => {
let target = oxide_builtins::proxy::proxy_state_ref(ctor_obj).expect("状态盒").target;
return self.construct_with(target, new_target, args);
}
Err(e) => {
return Err(self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)));
}
}
}
// bound 包装:解包链(绑定实参前置、newTarget 逐层指针相等替换),
// 最内层非构造器判定后递归进入既有臂。
if ctor_obj.type_tag == JsObject::OBJ_TYPE_BOUND {
+239 -2
View File
@@ -55,7 +55,7 @@ pub(crate) fn drop_proxy_native(obj: &JsObject) -> u64 {
/// 1. 取状态盒并检 revoked 位:位真抛 TypeError("Proxy is revoked")。
/// 2. 经全 get 路径从 handler 读 trap 值(receiver 为 handler——handler 自身
/// 是代理时其 get trap 依规范触发)。
/// 3. trap 值 undefined 返 None(落穿普通行为)。
/// 3. trap 值 undefined 或 null 返 None(GetMethod 形态:落穿普通行为)。
/// 4. 经 `call_function_sync` 同步调用 trap 值(this 为 handler)。
///
/// # 边界与前提
@@ -79,9 +79,23 @@ pub(crate) fn call_trap(
// SAFETY: handler 由构造器校验为非空对象指针。
let handler_obj = unsafe { &*handler.as_js_object_ptr() };
let trap_val = vm.ordinary_get(handler_obj, trap_name_si, handler)?;
if trap_val.is_undefined() {
if trap_val.is_undefined() || trap_val.is_null() {
return Ok(None);
}
// trap 值须可调用(规范 Call 语义):非可调用抛 TypeError 而非落入
// call_function_sync 的通用「accessor is not callable」文本(kind 丢失)。
if !trap_val.is_object() {
return Err(oxide_runtime_api::format_error_message(
"TypeError",
"Proxy trap is not callable",
));
}
if !unsafe { &*trap_val.as_js_object_ptr() }.is_function() {
return Err(oxide_runtime_api::format_error_message(
"TypeError",
"Proxy trap is not callable",
));
}
let result = vm.call_function_sync(trap_val, handler, args)?;
Ok(Some(result))
}
@@ -100,6 +114,77 @@ pub(crate) fn trap_bool_result(result: JsValue, trap_name: &str) -> Result<bool,
))
}
/// 把实参切片物化为 JS 数组(CreateArrayFromList 形态:Array.prototype
/// 原型链,元素区逐值写入)。
pub(crate) fn materialize_args_array(vm: &mut Vm, args: &[JsValue]) -> JsValue {
let array_proto = vm.builtin_proto(oxide_runtime_api::ProtoKind::ArrayProto);
let arr = vm.alloc_object(JsObject::new_array(
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
JsValue::from_js_object(array_proto),
args.len().min(oxide_types::object::MAX_DENSE_PROPS),
));
for (i, item) in args.iter().enumerate() {
// SAFETY: arr 是刚经统一入口分配的数组对象,元素区已就绪。
unsafe { (*arr).set_prop_at(i, *item) };
}
// SAFETY: 与 new_array 同 epoch,写入后按实际元素数设 prop_count。
unsafe { (*arr).set_prop_count(args.len().min(oxide_types::object::MAX_DENSE_PROPS)) };
JsValue::from_js_object(arr)
}
/// apply trap 调用([[Call]] 路由):trap 权威,缺失落穿 target。
///
/// # 步骤
/// 1. 取状态盒(revoked 检查在 `call_trap` 内)。
/// 2. 从 handler 读 apply trap 值(GetMethod 形态:undefined 与 null 均视为缺失)。
/// 3. trap 缺失:落穿 target 的 [[Call]](同 this 同实参的同步调用)。
/// 4. trap 存在:以 (target, thisArgument, argumentsList) 调用,返回值原样透传。
///
/// # 边界与前提
/// - `proxy` 须为带状态盒的 Proxy 对象(守卫以 `is_proxy_obj` 保证入口)。
/// - 函数代理的 target 可调用(构造期置 is_function 位的前提),落穿
/// `call_function_sync` 依赖该前提。
pub(crate) fn proxy_apply_call(
vm: &mut Vm, proxy: &JsObject, this_value: JsValue, args: &[JsValue],
) -> Result<JsValue, String> {
let state = oxide_builtins::proxy::proxy_state_ref(proxy).expect("状态盒");
let target = state.target;
let args_array = materialize_args_array(vm, args);
match call_trap(vm, proxy, vm.perm_intern("apply"), &[target, this_value, args_array])? {
Some(result) => Ok(result),
None => vm.call_function_sync(target, this_value, args),
}
}
/// construct trap 调用([[Construct]] 路由):trap 权威,缺失落穿 target。
///
/// # 步骤
/// 1. 取状态盒(revoked 检查在 `call_trap` 内)。
/// 2. 从 handler 读 construct trap 值(GetMethod 形态)。
/// 3. trap 缺失:返 None,调用方落穿 target 的普通 [[Construct]]。
/// 4. trap 存在:以 (target, argumentsList, newTarget) 调用;返回值须为对象,
/// 否则抛 TypeError。
///
/// # 边界与前提
/// - `proxy` 须为带状态盒的 Proxy 对象(守卫以 `is_proxy_obj` 保证入口)。
/// - `new_target` 为调用点 newTarget(代理自身),落穿时原样透传给 target。
pub(crate) fn proxy_construct_trap(
vm: &mut Vm, proxy: &JsObject, args: &[JsValue], new_target: JsValue,
) -> Result<Option<JsValue>, String> {
let state = oxide_builtins::proxy::proxy_state_ref(proxy).expect("状态盒");
let target = state.target;
let args_array = materialize_args_array(vm, args);
let result = call_trap(vm, proxy, vm.perm_intern("construct"), &[target, args_array, new_target])?;
match result {
Some(v) if v.is_object() => Ok(Some(v)),
Some(_) => Err(oxide_runtime_api::format_error_message(
"TypeError",
"a [[Construct]] trap returned a non-object",
)),
None => Ok(None),
}
}
/// 构造 defineProperty trap 的属性描述符对象(value 加三标志位)。
///
/// 描述符属性均为不可写、不可枚举、不可配置(与内建对象属性口径一致);
@@ -243,6 +328,158 @@ mod tests {
assert!(err.contains("Proxy is revoked"), "错误文本须带撤销标记:{err}");
}
/// 函数代理不变量:target 可调用且不可扩展时构造器抛 TypeError。
#[test]
fn proxy_constructor_rejects_callable_non_extensible_target() {
let mut vm = Vm::new();
let target = native_fn_obj(&mut vm, gc_forcing_get_trap);
unsafe { (*target).set_extensible(false) };
let handler = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
// native 调用协议:253/254 为 receiver/callee 槽,实参经寄存器下标传递。
vm.regs[1] = JsValue::from_js_object(target);
vm.regs[2] = JsValue::from_js_object(handler);
vm.constructing_native = true;
let result = oxide_builtins::proxy::proxy_constructor(&mut vm, &[253, 254, 1, 2]);
let exc = match result {
NativeResult::Err(exc) => exc,
_ => panic!("可调用且不可扩展的 target 须抛 TypeError"),
};
let exc_ref = unsafe { &*exc.as_js_object_ptr() };
let name_val = vm.resolve_property(exc_ref, vm.perm_intern("name")).unwrap();
// SAFETY: name_val 为字符串值(异常对象 name 属性)。
let name = unsafe { (*name_val.as_string_ptr()).as_lossy_str() };
assert_eq!(name, "TypeError", "须抛 TypeError");
}
/// apply trap 体:把三实参(target、thisArg、argumentsList)身份记入 handler,
/// 返回固定值。
fn apply_trap_capture(vm: &mut Vm, args: &[u8]) -> NativeResult {
let handler = vm.reg(253);
let handler_ref = unsafe { &mut *handler.as_js_object_ptr() };
vm.set_or_create_prop_value(handler_ref, vm.perm_intern("target"), vm.reg(args[1]));
vm.set_or_create_prop_value(handler_ref, vm.perm_intern("this"), vm.reg(args[2]));
vm.set_or_create_prop_value(handler_ref, vm.perm_intern("list"), vm.reg(args[3]));
NativeResult::Ok(JsValue::int(99))
}
/// apply trap 路由:trap 以 (target, thisArg, argumentsList) 调用,返回值原样透传。
#[test]
fn proxy_apply_call_routes_apply_trap_with_args() {
let mut vm = Vm::new();
let target = native_fn_obj(&mut vm, counting_trap);
let handler = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
let proxy = proxy_with_state(&mut vm, JsValue::from_js_object(target), JsValue::from_js_object(handler));
let trap_fn = native_fn_obj(&mut vm, apply_trap_capture);
let this_value = plain_obj_value();
vm.set_or_create_prop_value(
unsafe { &mut *handler },
vm.perm_intern("apply"),
JsValue::from_js_object(trap_fn),
);
let result = proxy_apply_call(&mut vm, unsafe { &*proxy }, this_value, &[JsValue::int(1), JsValue::int(2)])
.expect("apply 应成功");
assert_eq!(result, JsValue::int(99), "trap 返回值须原样透传");
let handler_ref = unsafe { &*handler };
assert_eq!(
vm.resolve_property(handler_ref, vm.perm_intern("target")).unwrap(),
JsValue::from_js_object(target),
"第一实参须为 target"
);
assert_eq!(
vm.resolve_property(handler_ref, vm.perm_intern("this")).unwrap(),
this_value,
"第二实参须为 thisArg"
);
let list = vm.resolve_property(handler_ref, vm.perm_intern("list")).unwrap();
assert!(list.is_object(), "第三实参须为数组对象");
let list_ref = unsafe { &*list.as_js_object_ptr() };
assert!(list_ref.is_array(), "argumentsList 须为数组");
}
/// apply trap 缺失:落穿 target 的普通调用路径。
#[test]
fn proxy_apply_call_falls_through_to_target() {
let mut vm = Vm::new();
// target 不读 receiver(落穿时 this 为 undefined),仅返回固定值。
fn target_return_7(_vm: &mut Vm, _args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::int(7))
}
let target = native_fn_obj(&mut vm, target_return_7);
let handler = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
let proxy = proxy_with_state(&mut vm, JsValue::from_js_object(target), JsValue::from_js_object(handler));
let result =
proxy_apply_call(&mut vm, unsafe { &*proxy }, JsValue::undefined(), &[JsValue::int(1)])
.expect("落穿 target 应成功");
assert_eq!(result, JsValue::int(7), "target 调用结果须原样返回");
}
/// construct trap 返回值须为对象,非对象返回抛 TypeError。
#[test]
fn proxy_construct_trap_non_object_result_throws() {
let mut vm = Vm::new();
let target = native_fn_obj(&mut vm, counting_trap);
let handler = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
let proxy = proxy_with_state(&mut vm, JsValue::from_js_object(target), JsValue::from_js_object(handler));
// construct trap 体:返回非对象值。
fn construct_trap_non_object(_vm: &mut Vm, _args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::int(42))
}
let trap_fn = native_fn_obj(&mut vm, construct_trap_non_object);
vm.set_or_create_prop_value(
unsafe { &mut *handler },
vm.perm_intern("construct"),
JsValue::from_js_object(trap_fn),
);
let err = proxy_construct_trap(&mut vm, unsafe { &*proxy }, &[], JsValue::from_js_object(proxy))
.expect_err("非对象返回值须抛 TypeError");
assert!(err.contains("TypeError"), "须抛 TypeError:{err}");
assert!(err.contains("non-object"), "错误文本须带非对象标记:{err}");
}
/// construct trap 路由:trap 以 (target, argumentsList, newTarget) 调用,
/// 对象返回值原样透传。
#[test]
fn proxy_construct_trap_routes_with_new_target() {
let mut vm = Vm::new();
let target = native_fn_obj(&mut vm, counting_trap);
let handler = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
let proxy = proxy_with_state(&mut vm, JsValue::from_js_object(target), JsValue::from_js_object(handler));
// construct trap 体:记录三实参身份,返回固定对象。
fn construct_trap_capture(vm: &mut Vm, args: &[u8]) -> NativeResult {
let handler = vm.reg(253);
let handler_ref = unsafe { &mut *handler.as_js_object_ptr() };
vm.set_or_create_prop_value(handler_ref, vm.perm_intern("target"), vm.reg(args[1]));
vm.set_or_create_prop_value(handler_ref, vm.perm_intern("list"), vm.reg(args[2]));
vm.set_or_create_prop_value(handler_ref, vm.perm_intern("newtarget"), vm.reg(args[3]));
let result = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
NativeResult::Ok(JsValue::from_js_object(result))
}
let trap_fn = native_fn_obj(&mut vm, construct_trap_capture);
vm.set_or_create_prop_value(
unsafe { &mut *handler },
vm.perm_intern("construct"),
JsValue::from_js_object(trap_fn),
);
let new_target = plain_obj_value();
let result = proxy_construct_trap(&mut vm, unsafe { &*proxy }, &[JsValue::int(3)], new_target)
.expect("construct 应成功")
.expect("trap 存在须有返回值");
assert!(result.is_object(), "trap 对象返回值须原样透传");
let handler_ref = unsafe { &*handler };
assert_eq!(
vm.resolve_property(handler_ref, vm.perm_intern("target")).unwrap(),
JsValue::from_js_object(target),
"第一实参须为 target"
);
let list = vm.resolve_property(handler_ref, vm.perm_intern("list")).unwrap();
assert!(list.is_object() && unsafe { &*list.as_js_object_ptr() }.is_array(), "第二实参须为数组");
assert_eq!(
vm.resolve_property(handler_ref, vm.perm_intern("newtarget")).unwrap(),
new_target,
"第三实参须为 newTarget"
);
}
/// 守卫路由:get / set / has trap 对代理命中,resolve_property 对代理保守返 None。
#[test]
fn guard_routing_for_proxy_get_set_has_and_resolve() {
+5
View File
@@ -125,6 +125,11 @@ impl Vm {
if !callee_obj.is_function() {
return Err(self.error_message_text("TypeError", "accessor is not callable"));
}
// Proxy [[Call]]:apply trap 权威(target, thisArgument, argumentsList);
// trap 缺失落穿 target 的普通调用路径(嵌套代理经递归自然消解)。
if callee_obj.is_proxy_obj() {
return crate::proxy::proxy_apply_call(self, callee_obj, receiver, args);
}
if let Some(native_fn) = callee_obj.native_fn() {
if self.native_call_depth >= self.kernel_core.config.max_call_depth {
+20
View File
@@ -41,6 +41,26 @@ impl Vm {
let call_window = (ext >> 8) as u8;
crate::vm_debug!("CALL rd={} this={} args={} depth={}", rd, this_reg, arg_count, self.frames.len());
// Proxy [[Call]]:apply trap 权威(target, thisArgument,
// argumentsList);trap 缺失落穿 target 的普通调用路径。
if obj.is_proxy_obj() {
let this_value = self.regs[this_reg as usize];
let mut args = Vec::with_capacity(arg_count);
for i in 0..arg_count.min(256) {
args.push(self.regs[first_arg_reg.wrapping_add(i as u8) as usize]);
}
match crate::proxy::proxy_apply_call(self, obj, this_value, &args) {
Ok(v) => {
self.regs[0] = v;
return Ok(false);
}
Err(msg) => {
self.raise_call_error(&msg)?;
return Ok(true);
}
}
}
// 无本体函数对象(Function.prototype):[[Call]] 接受任意实参
// 返回 undefined,不执行函数体。
if obj.native_fn().is_none() && obj.sub_module_index() == 0 {
+62 -14
View File
@@ -87,6 +87,59 @@ impl Vm {
return self.dispatch_new_bound(rd, constructor, new_obj, args, call_window);
}
let mut args = Vec::with_capacity(arg_count);
for i in 0..arg_count.min(256) {
args.push(self.regs[first_arg_reg.wrapping_add(i as u8) as usize]);
}
self.dispatch_new_target(ctor_obj, constructor, constructor, rd, &args, call_window)
}
/// 构造器普通 [[Construct]]:Proxy construct trap 路由、新对象分配、原型读
/// (newTarget)、native / 字节码分派。
///
/// # 步骤
/// 1. Proxy [[Construct]]:construct trap 权威(target、argumentsList、
/// newTarget);trap 缺失落穿 target 的普通 [[Construct]](newTarget 不变,
/// 嵌套代理经递归自然消解)。
/// 2. 分配占位 this(原型默认 Object.prototype)。
/// 3. GpFC 自 newTarget 读原型(native 形裸读、字节码形传播读)。
/// 4. native 构造器:值传递调用(receiver = 新对象,new.target 槽 = newTarget);
/// 字节码构造器:压构造帧(this = 新对象,派生构造器为 undefined)。
///
/// # 边界与前提
/// - `ctor_obj` 为待分派的构造器(native / 字节码臂);`new_target` 为
/// newTarget(原型读源与 new.target 槽),非代理路径两者恒同一值。
pub(crate) fn dispatch_new_target(
&mut self,
ctor_obj: &JsObject,
constructor: JsValue,
new_target: JsValue,
rd: usize,
args: &[JsValue],
call_window: u8,
) -> Result<bool, String> {
// Proxy [[Construct]]:construct trap 权威(target, argumentsList,
// newTarget);trap 缺失落穿 target 的普通 [[Construct]] 路径。
if ctor_obj.is_proxy_obj() {
match crate::proxy::proxy_construct_trap(self, ctor_obj, args, new_target) {
Ok(Some(v)) => {
self.regs[rd] = v;
return Ok(false);
}
Ok(None) => {
let target = oxide_builtins::proxy::proxy_state_ref(ctor_obj).expect("状态盒").target;
let target_obj = unsafe { &*target.as_js_object_ptr() };
return self.dispatch_new_target(target_obj, target, new_target, rd, args, call_window);
}
Err(msg) => {
// trap 抛错(含 revoked / 非可调用 / 非对象返回)须恢复为可捕获
// JS 异常:原值经 last_uncaught_value 保留,深度 0 就地展开。
self.raise_call_error(&msg)?;
return Ok(true);
}
}
}
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
let new_obj = self.alloc_object(JsObject::new_empty(
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
@@ -99,11 +152,13 @@ impl Vm {
// 先于函数体求值,访问器 getter 的异常须原值上抛,非对象结果回落
// Object.prototype)。bound 包装已在上方转发臂返回,不到达此处。
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
// SAFETY: new_target 为构造器值(非空对象指针)。
let new_target_obj = unsafe { &*new_target.as_js_object_ptr() };
let proto_val = if ctor_obj.native_fn().is_some() {
self.resolve_property(ctor_obj, proto_si)
self.resolve_property(new_target_obj, proto_si)
} else {
let pc_before = self.pc;
match self.ordinary_get(ctor_obj, proto_si, constructor) {
match self.ordinary_get(new_target_obj, proto_si, new_target) {
Ok(v) if self.pc == pc_before => Some(v),
Ok(_) => {
// getter 抛且已有 catch/finally 接手:展开已跳到落点,停构造流。
@@ -131,10 +186,6 @@ impl Vm {
if ctor_obj.native_fn().is_some() {
let new_obj_val = JsValue::object(new_obj as *mut u8);
let mut args = Vec::with_capacity(arg_count);
for i in 0..arg_count.min(256) {
args.push(self.regs[first_arg_reg.wrapping_add(i as u8) as usize]);
}
// native 构造器:收口到 call_function_sync(与 spread / bound 变体一致)。
// 该入口统一保存/恢复调用窗口与 253/254 槽且不触碰 255,调用方
// new.target 与 this 跨 native 构造不被污染;receiver 经 arg0 打包,
@@ -143,9 +194,9 @@ impl Vm {
// 夹持(调用后恢复)。
let saved_new_target = self.regs[255];
let saved_constructing = self.constructing_native;
self.regs[255] = constructor;
self.regs[255] = new_target;
self.constructing_native = true;
let result = self.call_function_sync(constructor, new_obj_val, &args);
let result = self.call_function_sync(constructor, new_obj_val, args);
self.constructing_native = saved_constructing;
self.regs[255] = saved_new_target;
match result {
@@ -187,7 +238,7 @@ impl Vm {
let new_obj_val = JsValue::object(new_obj as *mut u8);
// 收敛到统一压帧入口:derived 构造器在 super() 前 this 为 undefined,
// 基类构造器 this = 新对象;new.target = 构造器本身。
// 基类构造器 this = 新对象;new.target = newTarget。
let this_value = if ctor_obj.is_derived_constructor() {
JsValue::undefined()
} else {
@@ -196,11 +247,8 @@ impl Vm {
self.push_bytecode_frame(
constructor,
this_value,
FrameArgs::RegRange {
first: first_arg_reg,
count: arg_count,
},
ConstructArgs::new(rd as u8, new_obj_val, constructor),
FrameArgs::Slice(args),
ConstructArgs::new(rd as u8, new_obj_val, new_target),
FrameContinuation::None,
call_window,
)?;
+20 -5
View File
@@ -109,14 +109,29 @@ impl Vm {
) -> 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 未定义落穿普通属性路径。
if obj.is_proxy_obj() {
let target = oxide_builtins::proxy::proxy_state_ref(obj).expect("状态盒").target;
// 即读结果;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);
if let Some(v) = crate::proxy::call_trap(self, obj, self.perm_intern("get"), &[target, key, receiver])? {
return Ok(v);
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;