//! `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 { 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 { 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) -> 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; 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; /// 字符串→键规范化:规范数字串(`"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; /// 存在性判定(规范 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; // 属性访问 fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result; /// `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; fn coerce_primitive_bounded(&mut self, value: JsValue, prefer_string: bool) -> Result; fn coerce_number_bounded(&mut self, value: JsValue) -> Result; // 调用基础设施 fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result; /// 构造调用(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; /// 带 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; /// 取回在 String 展平调用边界上保留下来的原始抛出 JsValue, /// 使迭代器包装器能重新抛出原错误而非二次包装。 fn take_uncaught_value(&mut self) -> Option; /// 恢复被忽略调用暂存的原始抛出值(与 [`Self::take_uncaught_value`] 配对)。 /// /// # 注意事项 /// 忽略调用(如 IteratorClose 的 `return()`)抛错时不得让自身值覆盖槽—— /// 调用前暂存、调用后恢复,保证在途异常值跨忽略调用存活。 fn restore_uncaught_value(&mut self, value: Option); /// 取回数组 length define 强转期用户代码抛出的原始异常。 /// /// # 注意事项 /// - 与 [`Self::take_uncaught_value`] 分离:后者可能残留其它操作忽略调用时 /// 写入的值,本槽仅由 length 强转失败写入,入口据此区分「强转异常」与 /// 「描述符收敛失败」。 fn take_pending_length_exception(&mut self) -> Option; /// 清空 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, 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; /// 函数对象是否为严格模式函数(经子模块 `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; /// 动态编译脚本(`eval` 字符串模式):按脚本模式编译,var/函数声明落全局对象。 /// 编译或解析失败返回 `Err`,由调用方转为 `SyntaxError`。 fn create_dynamic_script(&mut self, code: &str) -> Result; /// `None` 表示无描述(`Symbol()`/`Symbol(undefined)`),`Some(desc)` 为字符串描述。 fn symbol_intern(&mut self, desc: Option) -> u32; fn symbol_description(&self, idx: u32) -> Option<&str>; fn symbol_lookup_global(&self, key: &str) -> Option; fn symbol_register_global(&mut self, key: String, idx: u32); fn symbol_key_for_id(&self, idx: u32) -> Option; // 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; }