fix: 通用构造 GpFC 读体传播读与内建原型回落

This commit is contained in:
2026-09-24 02:41:28 +00:00
parent a5663b439f
commit dfa85d3eac
5 changed files with 394 additions and 54 deletions
+56 -31
View File
@@ -73,43 +73,68 @@ fn disposed_reference_error<H: VmHost>(vm: &mut H) -> JsValue {
crate::error::create_reference_error(vm, "DisposableStack has been disposed")
}
/// 资源栈构造器公共实现:校验 NewTarget 原型链后建带空状态盒的栈对象。
/// 接收者原型链是否命中对应栈类型内建原型(上限 16 层,普通调用接收者不命中)。
fn receiver_intrinsic_proto(this_val: JsValue, stack_proto_val: JsValue) -> bool {
if !this_val.is_object() {
return false;
}
let this_ptr = this_val.as_js_object_ptr();
if this_ptr.is_null() {
return false;
}
let stack_proto = stack_proto_val.as_js_object_ptr();
// SAFETY: this_ptr 是 is_object 校验过的当前 session 存活对象指针。
let mut proto = unsafe { &*this_ptr }.proto();
for _ in 0..16 {
if !proto.is_object() {
return false;
}
let proto_ptr = proto.as_js_object_ptr();
if proto_ptr.is_null() {
return false;
}
if std::ptr::eq(proto_ptr, stack_proto) {
return true;
}
// SAFETY: 同上,proto 链指针本 session 存活。
proto = unsafe { &*proto_ptr }.proto();
}
false
}
/// 资源栈构造器公共实现:构造形态校验后经 GpFC 推导原型,建带空状态盒的栈对象。
///
/// # 步骤
/// 1. 校验 NewTarget:`this` 的原型链须命中对应栈的 prototype
/// (`new X()` 直接命中,子类 `super()` 经子类原型链命中;普通调用抛 TypeError)。
/// 2. 建空状态盒对象,proto 与 type_tag 按栈类型传入。
/// 1. 非构造形态 → TypeError。构造形态标记由 NEW/SUPER/construct_with 三入口置位、
/// 普通调用入口清零;标记未置位的直接 native 调用形态以接收者原型链命中
/// 对应内建原型视同构造形态。
/// 2. GetPrototypeFromConstructor:传播式读 new.target 的 "prototype"(访问器
/// 副作用与异常原值传播);非对象结果回落本栈类型内建原型。
/// 3. 建空状态盒对象,proto 与 type_tag 按栈类型传入。
fn stack_constructor_impl<H: VmHost>(vm: &mut H, args: &[u8], proto_val: JsValue, type_tag: u8) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let is_new_call = this_val.is_object() && {
let stack_proto = proto_val.as_js_object_ptr();
let this_ptr = this_val.as_js_object_ptr();
if this_ptr.is_null() {
false
} else {
let mut proto = unsafe { &*this_ptr }.proto();
let mut found = false;
for _ in 0..16 {
if !proto.is_object() {
break;
}
let proto_ptr = proto.as_js_object_ptr();
if proto_ptr.is_null() {
break;
}
if std::ptr::eq(proto_ptr, stack_proto) {
found = true;
break;
}
proto = unsafe { &*proto_ptr }.proto();
}
found
}
};
if !is_new_call {
if !vm.constructing_native() && !receiver_intrinsic_proto(this_val, proto_val) {
return NativeResult::Err(crate::error::create_type_error(vm, "DisposableStack must be called with new"));
}
let stack = alloc_disposable_stack(vm, proto_val, type_tag);
let new_target = vm.reg(255);
let gpf_proto = if new_target.is_object() {
let nt_ptr = new_target.as_js_object_ptr();
// SAFETY: is_object 保证指针非空且对象本 session 存活。
let nt_obj = unsafe { &*nt_ptr };
let proto_si = vm.kernel_core().perm_interner().intern("prototype").0;
let proto = match vm.ordinary_get(nt_obj, proto_si, new_target) {
Ok(v) => v,
Err(err) => return NativeResult::Err(crate::iterator::engine_error(vm, &err)),
};
if proto.is_object() {
proto
} else {
proto_val
}
} else {
proto_val
};
let stack = alloc_disposable_stack(vm, gpf_proto, type_tag);
NativeResult::Ok(JsValue::from_js_object(stack))
}
+28 -3
View File
@@ -985,11 +985,36 @@ impl Vm {
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;
if let Some(proto_val) = self.resolve_property(ctor_obj, proto_si) {
if proto_val.is_object() {
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 = proto_val.as_js_object_ptr();
let proto_obj_ptr = v.as_js_object_ptr();
let _ = new_obj_mut.set_proto(JsValue::from_js_object(proto_obj_ptr));
}
}
+86 -14
View File
@@ -87,7 +87,7 @@ impl Vm {
JsValue::from_js_object(self.session.builtin_world().data_view_proto.as_ptr() as *mut JsObject);
JsValue::from_js_object(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, data_view_proto_val)))
} else {
let this_ptr = self.alloc_ctor_this(nt_obj)?;
let this_ptr = self.alloc_ctor_this(nt_obj, ctor_obj, new_target)?;
JsValue::from_js_object(this_ptr)
};
// newTarget 经 reg(255) 暴露给 native 构造器(快照/恢复:call 窗口
@@ -111,12 +111,47 @@ impl Vm {
self.call_constructor_bytecode_inline(ctor, ctor_obj, nt_obj, new_target, args)
}
/// 分配构造 this:proto = ctor.prototype(缺省 Object.prototype)。
fn alloc_ctor_this(&mut self, ctor_obj: &JsObject) -> Result<*mut JsObject, JsValue> {
/// 分配构造 this:proto = newTarget.prototype(缺省 Object.prototype)。
///
/// 按**构造器**(非 newTarget)种类分读形:native 构造器保持裸存储读
/// (占位 receiver 的原型不可观测,可观测面由各构造体自重读覆盖,native
/// 面的 getter 触发权归构造体的规范步序位);字节码构造器走传播读
/// (Ordinary [[Construct]] 的 GpFC 先于函数体求值且读体是唯一读点,
/// 访问器 getter 的异常须原值上抛,非对象结果回落 Object.prototype)。
fn alloc_ctor_this(
&mut self, nt_obj: &JsObject, ctor_obj: &JsObject, nt_val: JsValue,
) -> Result<*mut JsObject, JsValue> {
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
let proto_val = match self.resolve_property(ctor_obj, proto_si) {
Some(p) if p.is_object() => p,
_ => JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject),
let object_proto = JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
let is_native_ctor = ctor_obj.native_fn().is_some() && ctor_obj.type_tag == JsObject::OBJ_TYPE_CONSTRUCTOR;
let proto_val = if is_native_ctor {
match self.resolve_property(nt_obj, proto_si) {
Some(p) if p.is_object() => p,
_ => object_proto,
}
} else {
let pc_before = self.pc;
match self.ordinary_get(nt_obj, proto_si, nt_val) {
Ok(v) if self.pc == pc_before => {
if v.is_object() {
v
} else {
object_proto
}
}
Ok(_) => {
// depth-0 getter 抛已就地展开跳走(构造入口恒在 native 上下文,
// 结构性不可达):停掉构造流,不续行。
return Err(JsValue::undefined());
}
Err(err) => {
// 重入路径:恢复原始异常值,按 construct_with 契约携带原值。
return Err(self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &err)));
}
}
};
Ok(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)))
}
@@ -144,7 +179,7 @@ impl Vm {
}
// 先拷出寄存器数,释放对代际表的借用后再进入可变借用区。
let callee_reg_count = callee_module.n_registers;
let new_obj_ptr = self.alloc_ctor_this(nt_obj)?;
let new_obj_ptr = self.alloc_ctor_this(nt_obj, ctor_obj, new_target)?;
let new_obj_val = JsValue::from_js_object(new_obj_ptr);
// derived 构造器 super() 前 this 为 undefined,基类 this = 新对象。
let this_value = if ctor_obj.is_derived_constructor() {
@@ -303,15 +338,52 @@ impl Vm {
}
/// `Promise` 构造器:设置状态盒并同步调用 executor(resolve, reject)。
///
/// # 步骤
/// 1. NewTarget 缺失形态(普通调用 / .call 入口)→ TypeError。
/// 2. executor 可读性检查(步序先于 GpFC 原型读)。
/// 3. GetPrototypeFromConstructor:传播式读 new.target 的 "prototype"
/// (访问器副作用与异常原值传播);非对象结果回落 %Promise.prototype%。
/// 4. 物化 receiver(原型重设 + type_tag + 状态盒)后经原型链检放行。
fn promise_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
// `new` 调用时 this 是 proto 链含 %Promise.prototype% 的新对象;裸调用拒绝。
if !vm.has_promise_proto(this_val) {
// NewTarget 缺失 = 非构造形态:构造入口置位、普通调用入口清零,据此拒绝。
if !vm.constructing_native {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise must be called with new"));
}
// SAFETY: `has_promise_proto` 已校验 this 为存活对象且原型链含 %Promise.prototype%;
// 此处写 type_tag 后即新建状态盒,无别名。
let executor = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
if !oxide_builtins::iterator::is_callable(executor) {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise executor is not a function"));
}
// GetPrototypeFromConstructor:异常原值上抛;非对象结果回落内建原型。
let promise_proto_val = JsValue::from_js_object(vm.promise_proto.as_ptr() as *mut JsObject);
let new_target = vm.reg(255);
let proto = if new_target.is_object() {
let nt_ptr = new_target.as_js_object_ptr();
// SAFETY: is_object 保证指针非空且对象本 session 存活。
let nt_obj = unsafe { &*nt_ptr };
let proto_si = vm.kernel_core.perm_interner().intern("prototype").0;
match vm.ordinary_get(nt_obj, proto_si, new_target) {
Ok(v) if v.is_object() => v,
Ok(_) => promise_proto_val,
Err(err) => {
return NativeResult::Err(
vm.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_from_text(vm, &err)),
);
}
}
} else {
promise_proto_val
};
// receiver 原型重设为 GpFC 结果(恒等重设是 no-op,非对象回落重设是语义修正),
// 随后物化状态盒。
// SAFETY: this 为构造帧分配的存活对象;此处写 type_tag 后即新建状态盒,无别名。
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
if let Err(msg) = obj.set_proto(proto) {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, msg));
}
obj.type_tag = JsObject::OBJ_TYPE_PROMISE;
let resolve = vm.make_resolve_reject_fn(this_val, false, false);
let reject = vm.make_resolve_reject_fn(this_val, true, false);
@@ -325,9 +397,9 @@ fn promise_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
promoted_clone: std::ptr::null_mut(),
});
obj.set_native_data(Box::into_raw(state) as *mut u8);
let executor = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
if !oxide_builtins::iterator::is_callable(executor) {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise executor is not a function"));
// 物化后的 receiver 原型链须含 %Promise.prototype%(构造面放行兜底)。
if !vm.has_promise_proto(this_val) {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise must be called with new"));
}
match vm.call_function_sync(executor, JsValue::undefined(), &[resolve, reject]) {
Ok(_) => NativeResult::Ok(this_val),
+55 -6
View File
@@ -69,11 +69,38 @@ impl Vm {
JsValue::from_js_object(proto_ptr),
));
// GpFC 按构造器种类分读形:native 构造器体(native_fn + CONSTRUCTOR
// tag)保持裸存储读(占位 receiver 的原型不可观测,可观测面由构造体
// 自重读覆盖);字节码构造器走传播读(Ordinary [[Construct]] 的 GpFC
// 先于函数体求值,访问器 getter 的异常须原值上抛,非对象结果回落
// Object.prototype)。bound 包装已在上方转发臂返回,不到达此处。
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
if let Some(proto_val) = self.resolve_property(ctor_obj, proto_si) {
if proto_val.is_object() {
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 = proto_val.as_js_object_ptr();
let proto_obj_ptr = v.as_js_object_ptr();
let _ = new_obj_mut.set_proto(JsValue::from_js_object(proto_obj_ptr));
}
}
@@ -224,11 +251,33 @@ impl Vm {
}
// 新对象原型取最内层 target 的 prototype(bound 包装自身无 prototype)。
// 读形与 NEW 读体同构:native target 裸读,字节码 target 传播读
// (getter 异常原值上抛,非对象回落 Object.prototype)。
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
if let Some(proto_val) = self.resolve_property(target_obj, proto_si) {
if proto_val.is_object() {
let proto_val = if target_obj.native_fn().is_some() {
self.resolve_property(target_obj, proto_si)
} else {
let pc_before = self.pc;
match self.ordinary_get(target_obj, proto_si, target_val) {
Ok(v) if self.pc == pc_before => Some(v),
Ok(_) => {
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 = proto_val.as_js_object_ptr();
let proto_obj_ptr = v.as_js_object_ptr();
let _ = new_obj_mut.set_proto(JsValue::from_js_object(proto_obj_ptr));
}
}
+169
View File
@@ -145,3 +145,172 @@ fn native_construct_keeps_instance_semantics() {
let result = eval(&mut vm, "(function(){ var d = new Date(0); return d instanceof Date; })()").unwrap();
assert_bool(result, true);
}
// ── GpFC 通用构造面(读体按构造器种类分支 + 构造体自重读)回归钉 ──
fn throwing_proto_ctor(source_tail: &str) -> String {
format!(
"var bound = (function(){{}}).bind(); \
Object.defineProperty(bound, 'prototype', {{ get: function() {{ \
calls++; throw new Error('boom'); }} }}); \
var calls = 0; {source_tail}"
)
}
#[test]
fn gpfc_new_expression_bytecode_ctor_propagates_getter_throw() {
// 字节码构造器 prototype 为访问器且 getter 抛错:new 表达式须原值上抛(getter 触发)。
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 }",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "boom:1");
}
#[test]
fn gpfc_new_expression_spread_bytecode_ctor_propagates_getter_throw() {
// spread 构造变体同形:getter 抛原值上抛且恰触发一次。
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 }",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "boom:1");
}
#[test]
fn gpfc_reflect_construct_bytecode_ctor_bound_nt_propagates_getter_throw() {
// Reflect.construct(JS 构造器, 实参, bound NT):bound 自身 prototype 访问器
// getter 抛错须原值上抛(读体对字节码构造器为唯一 GpFC 读点)。
let mut vm = Vm::new();
let result = eval(
&mut vm,
&throwing_proto_ctor("try { Reflect.construct(function(){}, [], bound); } catch (e) { e.message }"),
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "boom");
}
#[test]
fn gpfc_promise_executor_check_precedes_proto_read() {
// Promise 体步序:executor 可读性检查先于 GpFC 原型读(非 callable executor
// + 抛 getter 的 NT → TypeError 胜 Test262Error 形)。
let mut vm = Vm::new();
let result = eval(
&mut vm,
&throwing_proto_ctor("try { Reflect.construct(Promise, [], bound); } catch (e) { e.constructor.name }"),
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "TypeError");
}
#[test]
fn gpfc_promise_abrupt_proto_read_propagates() {
// Promise 体 GpFC 自重读:executor callable 时 getter 抛原值上抛。
let mut vm = Vm::new();
let result = eval(
&mut vm,
&throwing_proto_ctor("try { Reflect.construct(Promise, [function(){}], bound); } catch (e) { e.message }"),
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "boom");
}
#[test]
fn promise_plain_call_rejected_by_construct_guard() {
// NewTarget 缺失形态:普通调用与 .call 入口一律 TypeError(含 receiver 为
// 真实 Promise 实例的 .call 面)。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var r = []; \
try { Promise(function(){}); } catch (e) { r.push(e.constructor.name); } \
try { Promise.call(null, function(){}); } catch (e) { r.push(e.constructor.name); } \
var p = new Promise(function(){}); \
try { Promise.call(p, function(){}); } catch (e) { r.push(e.constructor.name); } \
r.join(',')",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "TypeError,TypeError,TypeError");
}
#[test]
fn gpfc_disposable_stack_newtarget_proto_forms() {
// DS 体 GpFC 自重读三形:custom NT → NT.prototype 直用;非对象 NT.prototype
// → 回落 %DisposableStack.prototype%;抛 getter → 原值上抛且恰一次。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var s = Reflect.construct(DisposableStack, [], Object); \
var a = Object.getPrototypeOf(s) === Object.prototype; \
function nt() {} nt.prototype = undefined; \
var s2 = Reflect.construct(DisposableStack, [], nt); \
var b = Object.getPrototypeOf(s2) === DisposableStack.prototype; \
a + ':' + b",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "true:true");
let mut vm = Vm::new();
let result = eval(
&mut vm,
&throwing_proto_ctor(
"try { Reflect.construct(DisposableStack, [], bound); } catch (e) { e.message + ':' + calls }",
),
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "boom:1");
}
#[test]
fn gpfc_async_disposable_stack_newtarget_proto_forms() {
// ADS 同形三钉:custom NT 直用 / 非对象回落内建原型 / 抛 getter 原值一次。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var s = Reflect.construct(AsyncDisposableStack, [], Object); \
var a = Object.getPrototypeOf(s) === Object.prototype; \
function nt() {} nt.prototype = null; \
var s2 = Reflect.construct(AsyncDisposableStack, [], nt); \
var b = Object.getPrototypeOf(s2) === AsyncDisposableStack.prototype; \
a + ':' + b",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "true:true");
let mut vm = Vm::new();
let result = eval(
&mut vm,
&throwing_proto_ctor(
"try { Reflect.construct(AsyncDisposableStack, [], bound); } catch (e) { e.message + ':' + calls }",
),
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "boom:1");
}
#[test]
fn gpfc_native_ctor_raw_read_unchanged() {
// native 构造器臂回归:new Map()/new Set() 实例原型不变;Reflect.construct
// 以数据属性 prototype 的 NT 构造 Map 时原型直用(native 臂保持裸读)。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var a = Object.getPrototypeOf(new Map()) === Map.prototype; \
var b = Object.getPrototypeOf(new Set()) === Set.prototype; \
function nt() {} nt.prototype = Map.prototype; \
var c = Object.getPrototypeOf(Reflect.construct(Map, [], nt)) === Map.prototype; \
a + ':' + b + ':' + c",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "true:true:true");
}