mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
fix: DataView 原型访问器与 ToNumber 强转序位收口
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::bind_constructor;
|
||||
use crate::bindings::{apply_binding_table, configure_native_constructor};
|
||||
use crate::bindings::{apply_binding_table, bind_accessor_getter, configure_native_constructor};
|
||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
|
||||
/// 把 DataView 构造器与原型方法绑定到 global。
|
||||
pub fn bind_data_view(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
@@ -15,7 +15,7 @@ pub fn bind_data_view(core: &Arc<KernelCore>, session: &KernelSession, global: &
|
||||
configure_native_constructor(
|
||||
ctor,
|
||||
oxide_builtins::data_view::data_view_constructor::<crate::vm::Vm> as *const (),
|
||||
3,
|
||||
1,
|
||||
);
|
||||
|
||||
apply_binding_table(
|
||||
@@ -133,5 +133,36 @@ pub fn bind_data_view(core: &Arc<KernelCore>, session: &KernelSession, global: &
|
||||
],
|
||||
);
|
||||
|
||||
bind_constructor!(core, global, "DataView", ctor_ptr, oxide_builtins::data_view::data_view_constructor::<crate::vm::Vm>, 3, hash: true);
|
||||
bind_constructor!(core, global, "DataView", ctor_ptr, oxide_builtins::data_view::data_view_constructor::<crate::vm::Vm>, 1, hash: true);
|
||||
// length 属性描述符补 { writable:false, enumerable:false, configurable:true }
|
||||
// (bind_constructor! 宏的通用缺省 meta 不可写回,此处理仅覆盖 DataView)。
|
||||
{
|
||||
let length_si = core.perm_interner().intern("length").0;
|
||||
if let Some(pos) = core.shape_forge().lookup_position(ctor.shape_id(), length_si) {
|
||||
ctor.set_data_meta(pos, PropAttributes::new(false, false, true));
|
||||
}
|
||||
}
|
||||
|
||||
// buffer/byteOffset/byteLength 只读访问器(set 恒 undefined,getter 读视图状态盒)。
|
||||
bind_accessor_getter(
|
||||
core,
|
||||
session,
|
||||
proto,
|
||||
"buffer",
|
||||
oxide_builtins::data_view::data_view_buffer_getter::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
bind_accessor_getter(
|
||||
core,
|
||||
session,
|
||||
proto,
|
||||
"byteOffset",
|
||||
oxide_builtins::data_view::data_view_byte_offset_getter::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
bind_accessor_getter(
|
||||
core,
|
||||
session,
|
||||
proto,
|
||||
"byteLength",
|
||||
oxide_builtins::data_view::data_view_byte_length_getter::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -972,8 +972,13 @@ impl Vm {
|
||||
let new_obj_val = JsValue::object(new_obj as *mut u8);
|
||||
|
||||
if ctor_obj.native_fn().is_some() {
|
||||
// native 构造器:receiver 为新对象,值传递调用。
|
||||
match self.call_function_sync(constructor, new_obj_val, &args) {
|
||||
// native 构造器:receiver 为新对象,值传递调用。newTarget 快照后置入
|
||||
// reg(255) 暴露给 native 构造器(调用后恢复原值)。
|
||||
let saved_new_target = self.regs[255];
|
||||
self.regs[255] = constructor;
|
||||
let result = self.call_function_sync(constructor, new_obj_val, &args);
|
||||
self.regs[255] = saved_new_target;
|
||||
match result {
|
||||
Ok(v) => {
|
||||
self.regs[rd] = if v.is_object() { v } else { new_obj_val };
|
||||
Ok(false)
|
||||
|
||||
@@ -74,9 +74,28 @@ impl Vm {
|
||||
let nt_obj = unsafe { &*new_target.as_js_object_ptr() };
|
||||
// native 构造器:receiver 为新对象(this 取 newTarget.prototype),值传递调用。
|
||||
if ctor_obj.native_fn().is_some() {
|
||||
let this_ptr = self.alloc_ctor_this(nt_obj)?;
|
||||
let this_val = JsValue::from_js_object(this_ptr);
|
||||
return match self.call_function_sync(ctor, this_val, args) {
|
||||
// DataView 构造器不预分配 this:其体内先做全部校验、后读 newTarget.prototype
|
||||
// (抛错的 prototype getter 不得先于越界 RangeError 触发),占位 receiver
|
||||
// 不可观测(构造器恒返回自建对象);原型取 %DataView.prototype% 使构造器
|
||||
// 的 receiver 原型链判定通过。
|
||||
let is_data_view = std::ptr::eq(
|
||||
ctor.as_js_object_ptr(),
|
||||
self.session.builtin_world().data_view_constructor.as_ptr() as *mut JsObject,
|
||||
);
|
||||
let this_val = if is_data_view {
|
||||
let data_view_proto_val =
|
||||
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)?;
|
||||
JsValue::from_js_object(this_ptr)
|
||||
};
|
||||
// newTarget 经 reg(255) 暴露给 native 构造器(快照/恢复:call 窗口不触及 255)。
|
||||
let saved_nt = self.regs[255];
|
||||
self.regs[255] = new_target;
|
||||
let result = self.call_function_sync(ctor, this_val, args);
|
||||
self.regs[255] = saved_nt;
|
||||
return match result {
|
||||
Ok(ret) if ret.is_object() => Ok(ret),
|
||||
Ok(_) => Ok(this_val),
|
||||
Err(e) => Err(self
|
||||
|
||||
@@ -121,7 +121,8 @@ enum AggregateKind {
|
||||
}
|
||||
|
||||
/// IsConstructor 近似判定:可调用、非 arrow,且 native 函数须带构造器 tag
|
||||
/// (与 `construct_ctor` 的校验一致)。
|
||||
/// (与 `construct_ctor` 的校验一致);bound 包装恒接受(target 可构造性在
|
||||
/// `dispatch_new_bound` 入口复核)。
|
||||
fn is_constructor_value(c: JsValue) -> bool {
|
||||
if !c.is_object() {
|
||||
return false;
|
||||
@@ -130,7 +131,9 @@ fn is_constructor_value(c: JsValue) -> bool {
|
||||
let c_obj = unsafe { &*c.as_js_object_ptr() };
|
||||
c_obj.is_function()
|
||||
&& !c_obj.is_arrow()
|
||||
&& !(c_obj.native_fn().is_some() && c_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR)
|
||||
&& !(c_obj.native_fn().is_some()
|
||||
&& c_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR
|
||||
&& c_obj.type_tag != JsObject::OBJ_TYPE_BOUND)
|
||||
}
|
||||
|
||||
// ── session GC 支撑:状态盒中的 JsValues 作为 Promise 对象边追踪 ──
|
||||
|
||||
@@ -325,6 +325,20 @@ fn map_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
val
|
||||
}
|
||||
|
||||
/// 分配一个原型指向 DataView.prototype 的占位对象并写入寄存器,作为构造器调用的 `this`。
|
||||
fn data_view_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
let proto = vm.session.builtin_world().data_view_proto.as_ptr() as *mut JsObject;
|
||||
let obj = vm.epoch.alloc(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto),
|
||||
));
|
||||
// 测试辅助函数绕过 alloc_object,需手动置位 EPOCH_BIT。
|
||||
unsafe { (*obj).set_is_epoch(true) };
|
||||
let val = JsValue::from_js_object(obj);
|
||||
vm.regs[reg as usize] = val;
|
||||
val
|
||||
}
|
||||
|
||||
/// 分配一个原型指向 Set.prototype 的占位对象并写入寄存器,作为构造器调用的 `this`。
|
||||
fn set_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
let proto = vm.session.builtin_world().set_proto.as_ptr() as *mut JsObject;
|
||||
@@ -735,6 +749,7 @@ fn session_gc_keeps_shared_array_buffer_alive_through_view_native_edges() {
|
||||
let typed = native_ok(typed_array::int32array_constructor(&mut vm, &[0, 1]));
|
||||
|
||||
vm.regs[1] = buffer;
|
||||
data_view_this(&mut vm, 0);
|
||||
let view = native_ok(data_view::data_view_constructor(&mut vm, &[0, 1]));
|
||||
|
||||
vm.regs[0] = view;
|
||||
@@ -790,6 +805,7 @@ fn session_gc_rewrites_buffer_retained_only_by_data_view_native_edge() {
|
||||
vm.regs[1] = JsValue::int(8);
|
||||
let buffer = native_ok(array_buffer::array_buffer_constructor(&mut vm, &[0, 1]));
|
||||
vm.regs[1] = buffer;
|
||||
data_view_this(&mut vm, 0);
|
||||
let view = native_ok(data_view::data_view_constructor(&mut vm, &[0, 1]));
|
||||
|
||||
vm.regs[0] = view;
|
||||
|
||||
@@ -147,14 +147,20 @@ impl Vm {
|
||||
/// ToNumber 有界版:先按 number hint 做 ToPrimitive,再转 `f64`。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// 原始值为 Symbol 时按规范(ECMA-262 §7.1.4)抛 `TypeError`,不落入
|
||||
/// [`coercion::to_number`] 的宽松快路径(后者对该输入返回 `NaN`)。
|
||||
/// 原始值为 Symbol 或 BigInt 时抛 `TypeError`,不落入
|
||||
/// [`coercion::to_number`] 的宽松快路径(后者对 Symbol 返回 `NaN`、
|
||||
/// 对 BigInt 做 lossy 转换)。BigInt 入口语义:显式 lossy 站点
|
||||
/// (`Number` 构造器)先解盒自行转换,不经本入口。
|
||||
pub(crate) fn coerce_number_bounded(&mut self, value: JsValue) -> Result<f64, String> {
|
||||
let primitive = self.coerce_primitive_bounded(value, false)?;
|
||||
if primitive.is_symbol() {
|
||||
self.conversion_error("Cannot convert a Symbol value to a number")?;
|
||||
return Ok(f64::NAN);
|
||||
}
|
||||
if primitive.is_bigint() {
|
||||
self.conversion_error("Cannot convert a BigInt value to a number")?;
|
||||
return Ok(f64::NAN);
|
||||
}
|
||||
Ok(coercion::to_number(primitive))
|
||||
}
|
||||
|
||||
|
||||
@@ -87,8 +87,13 @@ impl Vm {
|
||||
// native 构造器:收口到 call_function_sync(与 spread / bound 变体一致)。
|
||||
// 该入口统一保存/恢复调用窗口与 253/254 槽且不触碰 255,调用方
|
||||
// new.target 与 this 跨 native 构造不被污染;receiver 经 arg0 打包,
|
||||
// native 侧经 reg(args[0]) 读取,构造语义不变。
|
||||
match self.call_function_sync(constructor, new_obj_val, &args) {
|
||||
// native 侧经 reg(args[0]) 读取,构造语义不变。newTarget 快照后置入
|
||||
// reg(255) 暴露给 native 构造器(调用后恢复原值)。
|
||||
let saved_new_target = self.regs[255];
|
||||
self.regs[255] = constructor;
|
||||
let result = self.call_function_sync(constructor, new_obj_val, &args);
|
||||
self.regs[255] = saved_new_target;
|
||||
match result {
|
||||
Ok(v) => {
|
||||
self.regs[rd] = if v.is_object() { v } else { new_obj_val };
|
||||
Ok(false)
|
||||
@@ -224,7 +229,13 @@ impl Vm {
|
||||
|
||||
if target_obj.native_fn().is_some() {
|
||||
// native 构造器:receiver = 新对象,值传递调用(错误原值恢复后展开)。
|
||||
match self.call_function_sync(target_val, new_obj_val, &call_args) {
|
||||
// new.target 按 bound [[Construct]] 语义取最外层 bound 包装(wrapper_val),
|
||||
// 快照后置入 reg(255) 暴露给 native 构造器(调用后恢复原值)。
|
||||
let saved_new_target = self.regs[255];
|
||||
self.regs[255] = wrapper_val;
|
||||
let result = self.call_function_sync(target_val, new_obj_val, &call_args);
|
||||
self.regs[255] = saved_new_target;
|
||||
match result {
|
||||
Ok(v) => {
|
||||
self.regs[rd] = if v.is_object() { v } else { new_obj_val };
|
||||
Ok(false)
|
||||
|
||||
Reference in New Issue
Block a user