mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
272 lines
16 KiB
Rust
272 lines
16 KiB
Rust
//! `VmHost` trait:builtins 依赖的 `Vm` 能力集合,trait 面向泛型而非对象安全。
|
||
|
||
use std::sync::Arc;
|
||
|
||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||
use oxide_types::mem::Epoch;
|
||
use oxide_types::object::{Cell, JsObject, PropAttributes};
|
||
use oxide_types::value::JsValue;
|
||
|
||
/// builtins 依赖的 `Vm` 能力集合。
|
||
///
|
||
/// 方法签名是 `Vm` 上同名固有方法的逐字节拷贝;`impl VmHost for Vm` 委托给
|
||
/// 它们。trait 刻意保持扁平且非对象安全——builtins 始终接受 `&mut impl VmHost`。
|
||
pub trait VmHost {
|
||
// 寄存器访问
|
||
fn reg(&self, idx: u8) -> JsValue;
|
||
fn set_reg(&mut self, idx: u8, val: JsValue);
|
||
|
||
/// 当前 native 调用 spill 溢出区的实参个数(寄存器窗口 253 之外的部分)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 仅在 native 实现内部、实参仍有效时读取;无溢出时为 0。
|
||
fn native_overflow_count(&self) -> usize;
|
||
/// 读 spill 溢出区第 `i` 个实参(0 基,相对溢出区起点)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - `i` 必须小于 [`Self::native_overflow_count`]。
|
||
fn native_overflow_at(&self, i: usize) -> JsValue;
|
||
/// 当前 native 调用的完整实参个数(寄存器窗口 + spill 溢出区,不含 receiver)。
|
||
///
|
||
/// 供支持大实参集的 builtin(如 `String.fromCodePoint`)遍历全部实参;
|
||
/// 尚未改用本接口的 builtin 仍按 `args` 索引读寄存器。
|
||
fn native_arg_count(&self, args: &[u8]) -> usize {
|
||
args.len().saturating_sub(1) + self.native_overflow_count()
|
||
}
|
||
/// 第 `idx` 个实参(0 基,不含 receiver):窗口内读寄存器,窗口外读 spill 溢出区。
|
||
///
|
||
/// # 边界与前提
|
||
/// - `idx` 必须小于 [`Self::native_arg_count`]。
|
||
fn native_arg_at(&self, args: &[u8], idx: usize) -> JsValue {
|
||
let window = args.len().saturating_sub(1);
|
||
if idx < window {
|
||
self.reg(args[idx + 1])
|
||
} else {
|
||
self.native_overflow_at(idx - window)
|
||
}
|
||
}
|
||
/// 当前 native 调用是否以构造形态发起(NEW/SUPER native 臂与 `construct_with`
|
||
/// native 臂置 true,普通调用入口置 false,调用结束即恢复)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 仅在 native 实现内部读取;构造器 builtin 据此判定向 receiver 物化,
|
||
/// 不得用寄存器推断(native 调用与调用方共享寄存器文件,new.target
|
||
/// 槽在类构造器帧内残留类构造器对象)。
|
||
fn constructing_native(&self) -> bool;
|
||
|
||
// 对象分配 / 字符串创建
|
||
fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject;
|
||
fn new_string(&mut self, s: &str) -> JsValue;
|
||
/// move 接收 `String` 创建会话字符串,避免一次整串克隆。
|
||
fn new_string_owned(&mut self, s: String) -> JsValue;
|
||
/// 取 ASCII 单字符的永久字符串值:命中返回共享 perm 串(零分配、可指针
|
||
/// 短路比较),非 ASCII 返回 `None` 由调用方回落普通字符串创建。
|
||
///
|
||
/// # 注意事项
|
||
/// `&self` 可借用期调用;`None` 回落 `new_string` 是 `&mut` 路径,须先结束
|
||
/// 本次 `&self` 借用(返回值即时消费即可)。
|
||
fn single_char(&self, ch: char) -> Option<JsValue> {
|
||
if ch.is_ascii() {
|
||
Some(JsValue::string(oxide_kernel::string_forge::single_char_ptr(ch as u8)))
|
||
} else {
|
||
None
|
||
}
|
||
}
|
||
/// 借出字符串值的 UTF-16 单元序列(Flat 惰性编码 / FlatU16 直接借用 /
|
||
/// Cons 扁平化缓存),生命周期绑定到 `&self` 借用。调用方须保证
|
||
/// `val` 为字符串值。
|
||
fn string_units(&self, val: JsValue) -> std::borrow::Cow<'_, [u16]>;
|
||
/// 取单单元的永久字符串值:ASCII 单元命中共享 perm 串(零分配、可指针
|
||
/// 短路),非 ASCII 返回 `None` 由调用方回落单元创建。
|
||
fn single_unit(&self, u: u16) -> Option<JsValue> {
|
||
if u < 0x80 {
|
||
Some(JsValue::string(oxide_kernel::string_forge::single_char_ptr(u as u8)))
|
||
} else {
|
||
None
|
||
}
|
||
}
|
||
/// 以单元序列创建会话字符串(智能路由:含孤立 surrogate 落 FlatU16)。
|
||
fn new_string_units(&mut self, units: &[u16]) -> JsValue;
|
||
/// 同 `new_string_units`,以 owned 单元序列接收,避免一次克隆。
|
||
fn new_string_units_owned(&mut self, units: Vec<u16>) -> JsValue;
|
||
/// 分配 BigInt 值(num_bigint::BigInt box 登记到 VM,返回携带指针的 `JsValue`)。
|
||
fn new_bigint(&mut self, v: num_bigint::BigInt) -> JsValue;
|
||
/// 读取 BigInt 值;调用方须保证 `val.is_bigint()`。
|
||
fn bigint_value(&mut self, val: JsValue) -> &num_bigint::BigInt;
|
||
|
||
// 内核访问器
|
||
fn kernel_core(&self) -> &Arc<KernelCore>;
|
||
fn session(&self) -> &KernelSession;
|
||
fn epoch(&self) -> &Epoch;
|
||
|
||
// 属性解析
|
||
fn property_key_si(&mut self, val: JsValue) -> u32;
|
||
/// ToPropertyKey 完整路径:键值 → 内部属性键 si。对象经 ToPrimitive(string
|
||
/// hint),转换异常以 `Err` 返回(调用方须取 `take_uncaught_value` 传播原异常)。
|
||
/// 与 `property_key_si` 的区别:后者对转换失败退化为空键,不传播异常。
|
||
fn to_property_key_si(&mut self, val: JsValue) -> Result<u32, String>;
|
||
/// 字符串→键规范化:规范数字串(`"5"`)映射整数键,其余文本以 `encode_key`
|
||
/// 形态入键空间(无 FFFD 的良形文本恒等,含 FFFD 与运行时单元路径同形态,
|
||
/// 见 `string_key_units`)。
|
||
/// 供建键入口(fromEntries/json/rest excluded)与 `property_key_si` 的字符串分支统一口径。
|
||
fn string_key_si(&mut self, s: &str) -> u32;
|
||
fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue>;
|
||
/// 存在性判定(规范 HasProperty):自身与原型链任一层 P 为自有属性即存在;
|
||
/// 数组元素区 hole 视同缺失,TypedArray 整数索引按视图长度判在界。
|
||
fn has_property(&self, obj: &JsObject, prop_name_si: u32) -> bool;
|
||
fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32>;
|
||
|
||
// 属性访问
|
||
fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result<JsValue, String>;
|
||
/// `strict` 为写方严格模式:写失败时严格抛错,sloppy 静默 no-op。内置调用方
|
||
/// 一律传 true(内置写语义恒抛错,与调用上下文模式无关)。
|
||
fn ordinary_set(
|
||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, strict: bool,
|
||
) -> Result<(), String>;
|
||
|
||
// 属性定义
|
||
fn define_data_property(
|
||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, attributes: PropAttributes,
|
||
) -> Result<(), String>;
|
||
fn define_accessor_property(
|
||
&mut self, obj: &mut JsObject, prop_name_si: u32, get: JsValue, set: JsValue, attributes: PropAttributes,
|
||
) -> Result<(), String>;
|
||
fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue);
|
||
/// 全局 builtin 属性 A 侧写/删成功后,把当前帧对应镜像槽反向同步为 `val`
|
||
///(维护"槽 = A 侧原始存储"不变式)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - `obj` 非会话全局对象时立即 no-op(接收者指针判等,热路径零额外成本)。
|
||
/// - 键未登记在活动模块 builtin 镜像名集时无槽可写,no-op。
|
||
/// # 副作用
|
||
/// - 写当前帧寄存器文件的镜像槽。
|
||
fn sync_global_builtin_mirror(&mut self, obj: &JsObject, key_si: u32, val: JsValue);
|
||
|
||
// 查找 / 强制转换
|
||
fn lookup_str(&self, val: JsValue) -> Option<String>;
|
||
fn coerce_primitive_bounded(&mut self, value: JsValue, prefer_string: bool) -> Result<JsValue, String>;
|
||
fn coerce_number_bounded(&mut self, value: JsValue) -> Result<f64, String>;
|
||
|
||
// 调用基础设施
|
||
fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result<JsValue, String>;
|
||
/// 构造调用(Construct(C, args)):native 构造器以新对象为 receiver 值传递调用,
|
||
/// bytecode 构造器压构造帧执行(含 derived 构造器 super() 语义与 new.target
|
||
/// 传播),返回值非对象时回退到新对象。
|
||
///
|
||
/// # 边界与前提
|
||
/// - `ctor` 必须为可构造值(箭头 / 非构造 native / 普通值在入口处拒绝,
|
||
/// 返回 `Err`);`args` 为完整实参列表。
|
||
///
|
||
/// # 副作用
|
||
/// - bytecode 构造器压帧内嵌 dispatch:调用方执行状态在帧边界保存/恢复
|
||
/// (寄存器窗口 / 表代际 / spill)。
|
||
/// - 失败时已消费 `last_uncaught_value`,`Err` 直接携带原始抛出值
|
||
/// (保原值身份,不做文本降级);调用方不得再取槽。
|
||
fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result<JsValue, JsValue>;
|
||
/// 带 newTarget 覆写的构造调用(Construct(C, args, newTarget)):this 与
|
||
/// new.target 均按 `new_target` 推导;不覆写时调用方传构造器本身(与
|
||
/// `construct_ctor` 逐位等价)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - `ctor` 必须为可构造值(入口拒绝,返回 `Err`);`new_target` 的可构造性
|
||
/// 由调用方入口校验;`args` 为完整实参列表。
|
||
///
|
||
/// # 副作用
|
||
/// - 同 `construct_ctor`:bytecode 构造器压帧内嵌 dispatch,失败时消费
|
||
/// `last_uncaught_value`,`Err` 直接携带原始抛出值(保原值身份,不做
|
||
/// 文本降级);调用方不得再取槽。
|
||
fn construct_ctor_nt(&mut self, ctor: JsValue, new_target: JsValue, args: &[JsValue]) -> Result<JsValue, JsValue>;
|
||
/// 取回在 String 展平调用边界上保留下来的原始抛出 JsValue,
|
||
/// 使迭代器包装器能重新抛出原错误而非二次包装。
|
||
fn take_uncaught_value(&mut self) -> Option<JsValue>;
|
||
/// 恢复被忽略调用暂存的原始抛出值(与 [`Self::take_uncaught_value`] 配对)。
|
||
///
|
||
/// # 注意事项
|
||
/// 忽略调用(如 IteratorClose 的 `return()`)抛错时不得让自身值覆盖槽——
|
||
/// 调用前暂存、调用后恢复,保证在途异常值跨忽略调用存活。
|
||
fn restore_uncaught_value(&mut self, value: Option<JsValue>);
|
||
/// 取回数组 length define 强转期用户代码抛出的原始异常。
|
||
///
|
||
/// # 注意事项
|
||
/// - 与 [`Self::take_uncaught_value`] 分离:后者可能残留其它操作忽略调用时
|
||
/// 写入的值,本槽仅由 length 强转失败写入,入口据此区分「强转异常」与
|
||
/// 「描述符收敛失败」。
|
||
fn take_pending_length_exception(&mut self) -> Option<JsValue>;
|
||
/// 清空 uncaught 槽。define 通道强转入口先清后转:强转失败后槽内值必为
|
||
/// 本次强转产生的原值,先前操作的残留不得被误转存为本次原值(与 length
|
||
/// 路径同纪律)。
|
||
fn clear_uncaught_value(&mut self);
|
||
/// 把 uncaught 槽移入 define 专用槽(强转期用户回调抛出的原值):
|
||
/// Object/Reflect define 入口失败时先取专用槽原值重抛,不投影为 false
|
||
/// 或改写成引擎错误。
|
||
fn move_uncaught_to_pending_length(&mut self);
|
||
|
||
// 错误处理
|
||
fn checked_object_ptr(&mut self, val: JsValue, error_msg: &str) -> Result<Option<*mut JsObject>, String>;
|
||
/// 读当前分派指令位置(pc)。供有界强转调用方在强转前后采样:深度 0
|
||
/// 用户回调抛错已就地展开时 pc 必已变化,调用方须立即停止执行后续步骤
|
||
/// (强转结果为残值,继续即假值写或二次抛错)。
|
||
fn pc(&self) -> usize;
|
||
fn raise_type_error(&mut self, msg: &str) -> Result<(), String>;
|
||
/// 恢复已捕获的原始异常值(强转失败载荷)并走异常展开,保原值 kind:
|
||
/// 深度 0 置原值入异常通道就地展开到外围 catch;深度 >0 返回 kind 前缀
|
||
/// 文本,由原生调用边界恢复为异常对象。
|
||
///
|
||
/// # 边界与前提
|
||
/// - `exc` 须为调用方已提取的原始异常值(如经 `take_uncaught_value`);
|
||
/// 本入口不重取 uncaught 槽。
|
||
///
|
||
/// # 副作用
|
||
/// - 深度 0 写 `exception_value`/`pending_error_kind`,pc 经展开改写。
|
||
fn raise_captured(&mut self, exc: JsValue) -> Result<(), String>;
|
||
fn error_message_text(&self, kind: &str, msg: &str) -> String;
|
||
fn call_stack_function_names(&self) -> Vec<String>;
|
||
/// 函数对象是否为严格模式函数(经子模块 `is_strict` 判定);native 函数
|
||
/// (无子模块)视为严格。供 caller/arguments 受限访问器按严格性分流抛错。
|
||
fn function_is_strict(&self, obj: &JsObject) -> bool;
|
||
/// caller/arguments 访问是否受限:严格函数对象,或生成器 / 异步 /
|
||
/// 异步生成器函数对象([[Prototype]] 为三个动态函数原型之一)。
|
||
fn function_is_restricted(&self, obj: &JsObject) -> bool;
|
||
fn step_rng(&mut self);
|
||
fn math_rng_value(&self) -> f64;
|
||
fn sub_module_function_name(&self, gen: u32, sub_idx: u16) -> String;
|
||
/// 取当前字节码帧 `cell_stack.last()[cell_idx]` 的共享 cell 指针。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 仅在同一 VM 世代的字节码帧执行期读取:native 调用不压 `cell_stack`,故
|
||
/// builtin 内读到的是调用方模块帧;`cell_idx` 越界或槽为空返回 `None`。
|
||
fn module_frame_cell(&self, cell_idx: u32) -> Option<*mut Cell>;
|
||
/// 动态编译一个函数体(`Function` / `GeneratorFunction` / `AsyncFunction` /
|
||
/// `AsyncGeneratorFunction` 构造器用):把参数列表与函数体编译为可调用
|
||
/// 函数对象。`is_generator` / `is_async` 决定 wrap 源码的函数形态
|
||
/// (`function` / `function*` / `async function` / `async function*`),
|
||
/// 函数对象原型与 `prototype` 属性面按编译标志自动分流。
|
||
/// 编译或解析失败返回 `Err`,由调用方转为 `SyntaxError`。
|
||
fn create_dynamic_function(
|
||
&mut self, params: &[String], body: &str, is_generator: bool, is_async: bool,
|
||
) -> Result<JsValue, String>;
|
||
/// 动态编译脚本(`eval` 字符串模式):按脚本模式编译,var/函数声明落全局对象。
|
||
/// 编译或解析失败返回 `Err`,由调用方转为 `SyntaxError`。
|
||
fn create_dynamic_script(&mut self, code: &str) -> Result<JsValue, String>;
|
||
/// `None` 表示无描述(`Symbol()`/`Symbol(undefined)`),`Some(desc)` 为字符串描述。
|
||
fn symbol_intern(&mut self, desc: Option<String>) -> u32;
|
||
fn symbol_description(&self, idx: u32) -> Option<&str>;
|
||
fn symbol_lookup_global(&self, key: &str) -> Option<u32>;
|
||
fn symbol_register_global(&mut self, key: String, idx: u32);
|
||
fn symbol_key_for_id(&self, idx: u32) -> Option<String>;
|
||
|
||
// Atomics waiter 面(waitAsync 登记 / notify 唤醒)
|
||
/// 新建 pending Promise,供 `Atomics.waitAsync` 异步臂的 value 槽;结算不
|
||
/// 经能力闭包,由 notify 唤醒直调引擎结算核。
|
||
fn atomics_new_waiter_promise(&mut self) -> JsValue;
|
||
/// 登记 waitAsync waiter:键 = (缓冲对象指针, 元素字节偏移),同键 FIFO 追加。
|
||
///
|
||
/// # 注意事项
|
||
/// `buffer` 须为登记时刻视图的 `buffer` 现指针(与 notify 侧同一读径),
|
||
/// 同 run 无 GC 时两侧指针恒匹配。
|
||
fn atomics_register_waiter(&mut self, buffer: *mut JsObject, offset: usize, promise: JsValue);
|
||
/// 唤醒 (缓冲, 偏移) 处登记的前 `count` 个 waiter(`<= 0` 不唤醒、`+Inf`
|
||
/// 全唤醒),逐个以 "ok" 结算(反应入队随 run 收尾 drain);返回唤醒数。
|
||
fn atomics_wake_waiters(&mut self, buffer: *mut JsObject, offset: usize, count: f64) -> usize;
|
||
}
|