From 10f0f74b7665421ad30029394624c5f19af16869 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 17 Sep 2026 12:35:05 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=A1=A5=E9=BD=90=E6=95=B0=E7=BB=84?= =?UTF-8?q?=E4=B8=8E=E5=AF=B9=E8=B1=A1=E5=B7=A5=E5=85=B7=E6=97=8F=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E5=B9=B6=E6=B8=85=E9=99=A4=E4=B9=B1=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/array/common.rs | 26 +++++++++++++------ crates/oxide_builtins/src/array/immutable.rs | 23 +++++++++++++--- .../oxide_builtins/src/array/sort_iterator.rs | 16 +++++++++--- crates/oxide_builtins/src/disposable_stack.rs | 10 +++++-- crates/oxide_builtins/src/object.rs | 15 +++++++---- crates/oxide_builtins/src/typed_array.rs | 9 +++++++ 6 files changed, 76 insertions(+), 23 deletions(-) diff --git a/crates/oxide_builtins/src/array/common.rs b/crates/oxide_builtins/src/array/common.rs index d0eec36f..76d16377 100644 --- a/crates/oxide_builtins/src/array/common.rs +++ b/crates/oxide_builtins/src/array/common.rs @@ -72,8 +72,13 @@ pub(crate) fn get_this_array_ref(vm: &mut H, val: JsValue) -> Result< Ok(ptr) } -/// 接受 array 或 ArrayLike (object with length property) — for read-only methods. -/// 返回 (object_ptr, length, is_real_array) +/// 取 this 为真数组或类数组对象(带 length 属性的普通对象),按规范读取长度, +/// 返回 (对象指针, 长度, 是否为真数组),供只读方法族(values/entries/keys/iterator 等)共用。 +/// +/// # 步骤 +/// 1. this 非对象(null/undefined/原始值)→ TypeError。 +/// 2. 按规范 Get 语义读 length(getter 抛错向上传播,不走 prop_count 直读)。 +/// 3. ToLength:ToIntegerOrInfinity 后负数与 NaN 归 0,上限 2^53-1。 #[inline(always)] pub(crate) fn get_this_arraylike(vm: &mut H, val: JsValue) -> Result<(*mut JsObject, usize, bool), JsValue> { if !val.is_object() { @@ -101,7 +106,7 @@ pub(crate) fn get_this_arraylike(vm: &mut H, val: JsValue) -> Result< Ok(v) => v, Err(msg) => return Err(from_engine_error(vm, &msg)), }; - // ToLength:min(max(0, ToIntegerOrInfinity(len)), 2**53-1) + // ToLength:len 先按 ToIntegerOrInfinity 取整,负数与 NaN 归 0,上限 2^53-1。 let len = if len_num.is_nan() || len_num <= 0.0 { 0 } else { @@ -113,10 +118,10 @@ pub(crate) fn get_this_arraylike(vm: &mut H, val: JsValue) -> Result< /// 从 arraylike 读取 index 位置元素(按规范 Get:访问器/原型链/异常完整传播)。 #[inline(always)] pub(crate) fn arraylike_get(vm: &mut H, ptr: *mut JsObject, i: usize) -> Result { - // 真数组密集直读快路径:无元素 meta(无 hole / accessor,O(1) 空指针判定)且 - // 索引在元素区内时,直接读数据槽,免每元素构造数字串 + 属性键转换。 - // 语义与 ordinary_get 一致:`array_elements_meta_vec().is_none()` 保证该索引 - // 恒为数据属性(见 vm_props.rs ordinary_get_inner 数组分支)。 + // 真数组密集直读快路径:无元素元信息表(记录 hole/accessor 的可选侧表, + // 空指针判定为 O(1))且索引在元素区内时,直接读数据槽,免每元素构造数字串 + // + 属性键转换。语义与 ordinary_get 的数组分支一致:无元素元信息表保证该 + // 索引恒为数据属性,直读数据槽与规范 Get 结果相同。 let obj = unsafe { &*ptr }; if obj.is_array() && obj.array_elements_meta_vec().is_none() && i < obj.array_prop_count as usize { return Ok(obj.get_prop_at(i)); @@ -188,6 +193,7 @@ pub(crate) fn is_constructor_value(c: JsValue) -> bool { && !c_obj.is_arrow() && !(c_obj.native_fn().is_some() && c_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR) } +/// 校验回调参数为可调用函数对象(判空与 is_function 均须通过),不是则抛 TypeError。 pub(crate) fn require_callback(vm: &mut H, callback_val: JsValue) -> Result { if !callback_val.is_object() { return Err(array_type_error(vm, "callback is not a function")); @@ -209,7 +215,8 @@ pub(crate) fn create_new_array(vm: &mut H, n: usize) -> *mut JsObject )) } -/// ArrayCreate ?????length > 2**32 - 1 ? RangeError??????/??????? +/// ArrayCreate 前置检查:创建长度超过 2^32-1 时抛 RangeError +/// (密集数组长度按 u32 存储,超之不可容纳)。 pub(crate) fn check_array_create_len(vm: &mut H, n: usize) -> Result<(), JsValue> { if n > u32::MAX as usize { return Err(crate::error::create_range_error(vm, "Invalid array length")); @@ -225,6 +232,9 @@ pub(crate) fn array_length_arg(vm: &mut H, value: JsValue) -> Result< Ok(n as usize) } +/// native 回调通用调用入口:先校验回调可调用(非对象、空指针、非函数均转 +/// TypeError),再以 this_val 为接收者、cb_args 为实参同步调用用户回调;调用 +/// 失败时优先取回未捕获的原始异常值,无原始值时按错误文本重建异常。 pub(crate) fn invoke_native_callback( vm: &mut H, callback_val: JsValue, this_val: JsValue, cb_args: &[JsValue], ) -> NativeResult { diff --git a/crates/oxide_builtins/src/array/immutable.rs b/crates/oxide_builtins/src/array/immutable.rs index c3b092b9..c6ee22fe 100644 --- a/crates/oxide_builtins/src/array/immutable.rs +++ b/crates/oxide_builtins/src/array/immutable.rs @@ -24,7 +24,8 @@ pub fn array_to_reversed(vm: &mut H, args: &[u8]) -> NativeResult { if let Err(err) = check_array_create_len(vm, n) { return NativeResult::Err(err); } - // ????????????????getter ??????????? + // 按倒序逐个读取原数组元素(规范 Get,访问器 getter 触发),写入新数组对应槽位: + // 新数组即原数组元素的逆序。 for i in (0..n).rev() { let elem = arraylike_get_or_err!(vm, arr_ptr, i); unsafe { @@ -68,9 +69,15 @@ pub fn array_with(vm: &mut H, args: &[u8]) -> NativeResult { } NativeResult::Ok(JsValue::from_js_object(new_arr)) } +/// 返回按比较语义排序的新数组,原数组不变(`Array.prototype.toSorted`)。 +/// +/// # 边界与前提 +/// - 先校验 compareFn 可调用(非 undefined 且不可调用则抛 TypeError),再读取 +/// this/length; +/// - 未提供比较回调时按 ToString 结果的字符串字典序排序;底层 `sort_by` 为稳定 +/// 排序,比较结果相等的元素保持原有相对次序。 pub fn array_to_sorted(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.toSorted called with {} args", args.len()); - // 规范先校验 compareFn 可调用,再读取 this/length。 let comparator = if args.len() > 1 { match parse_sort_comparator(vm, vm.reg(args[1])) { Ok(c) => c, @@ -105,6 +112,14 @@ pub fn array_to_sorted(vm: &mut H, args: &[u8]) -> NativeResult { } NativeResult::Ok(JsValue::from_js_object(new_arr)) } +/// 按 start/deleteCount/插入值三元 splice 语义返回新数组,原数组不变 +///(`Array.prototype.toSpliced`)。 +/// +/// # 边界与前提 +/// - 全缺省(无 start 与 deleteCount)按拷贝处理;仅缺 deleteCount 时从 start +/// 删到末尾; +/// - start 为负时按 len + start 折算并夹到 `[0, len]`;deleteCount 为负或 NaN +/// 视为 0,上限为 len - start。 pub fn array_to_spliced(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.toSpliced called with {} args", args.len()); let this_val = vm.reg(args[0]); @@ -140,8 +155,8 @@ pub fn array_to_spliced(vm: &mut H, args: &[u8]) -> NativeResult { dc.min(n - actual_start) }; let insert_count = if args.len() > 3 { args.len() - 3 } else { 0 }; - // ???newLen = len + insertCount - actualDeleteCount?> 2**53-1 ? TypeError? - // ArrayCreate(newLen) ? 2**32-1 ? RangeError??????/??????? + // 新长度 newLen = len + insertCount - actualDeleteCount 超过 2^53-1 抛 TypeError; + // 再经 check_array_create_len 检查,超过 2^32-1 抛 RangeError。 let new_len = n.saturating_add(insert_count).saturating_sub(delete_count); if new_len > 9_007_199_254_740_991 { return NativeResult::Err(crate::error::create_type_error(vm, "Invalid array length")); diff --git a/crates/oxide_builtins/src/array/sort_iterator.rs b/crates/oxide_builtins/src/array/sort_iterator.rs index d7168463..d9a99b40 100644 --- a/crates/oxide_builtins/src/array/sort_iterator.rs +++ b/crates/oxide_builtins/src/array/sort_iterator.rs @@ -14,10 +14,10 @@ use super::common::{ invoke_native_callback, require_callback, }; -/// `Array.prototype.sort(compareFn)`:原地排序。默认按字符串字典序; -/// 提供比较函数时按其返回值(<0/=0/>0)排序,回调抛错则中止。 -/// 对元素向量执行 Array.prototype.sort 的比较语义(原地)。 -/// 比较回调抛错时中止并把异常返回;默认按字符串字典序。 +/// 对元素向量执行 `Array.prototype.sort` 的比较语义(原地排序)。 +/// +/// 默认按 ToString 结果的字符串字典序比较;提供比较回调时按其返回值 +///(<0/=0/>0)决定次序,回调抛错则中止并把异常原样返回。 pub(crate) fn sort_values_inner( vm: &mut H, vals: &mut [JsValue], comparator: Option, ) -> Result<(), JsValue> { @@ -70,6 +70,14 @@ pub(crate) fn parse_sort_comparator(vm: &mut H, candidate: JsValue) - } } +/// 原地排序数组元素并返回原数组(`Array.prototype.sort`)。 +/// +/// 未提供比较回调时按 ToString 结果的字符串字典序排序;底层 `sort_by` 为稳定 +/// 排序,比较结果相等(含 NaN)的元素保持原有相对次序。 +/// +/// # 边界与前提 +/// - 接收者不是数组时抛 TypeError; +/// - 比较回调抛错时中止排序并把异常原样返回。 pub fn array_sort(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.sort called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); diff --git a/crates/oxide_builtins/src/disposable_stack.rs b/crates/oxide_builtins/src/disposable_stack.rs index 029565bf..1871e9cd 100644 --- a/crates/oxide_builtins/src/disposable_stack.rs +++ b/crates/oxide_builtins/src/disposable_stack.rs @@ -4,8 +4,9 @@ //! 记录栈状态,`entries` 按入栈序存待释放资源。同步栈 dispose 只用 //! Pending/Disposed 两态;Disposing 为异步资源栈 disposeAsync 执行期标记 //! (当前状态机只置 Disposed,变体语义预留)。两栈共用本模块:方法按 -//! `type_tag`(24 同步 / 25 异步)区分接收者,`hint` 记录条目释放语义 -//! (0=sync-dispose,1=async-dispose),`wrap_sync` 标记 async 栈 use 落回 +//! `type_tag` 为 `JsObject::OBJ_TYPE_DISPOSABLE_STACK`(同步)或 +//! `OBJ_TYPE_ASYNC_DISPOSABLE_STACK`(异步)时区分接收者,`hint` 以 0/1 两值区分 +//! 条目释放语义(定义见 `DisposeEntry.hint`),`wrap_sync` 标记 async 栈 use 落回 //! `@@dispose` 的条目(返回值丢弃、异常异步化)。GC 四函数 //! (edges/rewrite/clone/drop)供 session 层跨 epoch 追踪,签名与 Map/Promise //! 状态盒同构。 @@ -529,6 +530,11 @@ pub struct DisposeCapability { pub entries: Vec, } +/// 取对象 `native_data` 中存放的状态盒指针。 +/// +/// # 注意事项 +/// - 仅在对象存活期内调用(GC 边收集与绑定层);指针由分配入口 +/// `alloc_disposable_stack` 保证有效。 pub fn get_capability_ptr(obj: &JsObject) -> *mut DisposeCapability { obj.native_data() as *mut DisposeCapability } diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index 56dd5017..5a2ed51d 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -11,6 +11,7 @@ use oxide_runtime_api::{NativeResult, VmHost}; use crate::builtins_debug; +/// 判断字符串是否为规范整数索引:非空、无前导零、全数字且值 < 2^32-1。 fn is_integer_index(key: &str) -> bool { if key.is_empty() || key.len() > 1 && key.as_bytes()[0] == b'0' { return false; @@ -19,8 +20,9 @@ fn is_integer_index(key: &str) -> bool { } /// 收集对象全部自身属性(数组元素区 + shape 链),按规范顺序排列:整数索引在前 -/// 升序,其余保持插入序。返回 `(属性键 si, 绝对存储索引)`:数组对象元素区索引即 -/// 绝对下标,命名属性 = `array_prop_count + shape 槽位`;普通对象即 shape 槽位。 +/// 升序,其余保持插入序。si 指 interned 字符串键编号(PermInterner 分配的 u32 id)。 +/// 返回 `(属性键 si, 绝对存储索引)`:数组对象元素区索引即绝对下标,命名属性 = +/// `array_prop_count + shape 槽位`;普通对象即 shape 槽位。 pub fn walk_own_keys(vm: &H, obj: &JsObject) -> Vec<(u32, u32)> { let mut keys: Vec<(u32, u32)> = Vec::new(); // 数组元素区:整数下标是可枚举自身属性(hole 视为不存在),排在命名属性之前。 @@ -78,7 +80,8 @@ pub fn walk_own_keys(vm: &H, obj: &JsObject) -> Vec<(u32, u32)> { keys } -/// 键 si 的数组下标值:整数键直接反解,字符串键查 interner 后判是否规范数字串。 +/// 取键 si(interned 字符串键编号)对应的数组下标值:整数键直接反解,字符串键查 +/// interner 后判是否规范数字串。 fn int_or_string_index(vm: &H, si: u32) -> Option { if is_int_key(si) { return Some(int_key_value(si)); @@ -87,9 +90,9 @@ fn int_or_string_index(vm: &H, si: u32) -> Option { is_integer_index(key).then(|| key.parse::().unwrap()) } -/// 键 si 物化为 JS 可见字符串值(Object.keys / ownKeys / entries 族): +/// 键 si 构造为 JS 可见字符串值(Object.keys / ownKeys / entries 族): /// 整数键反解数字串,字符串键经 `decode_key` 还原单元序列(孤立 surrogate -/// 物化为 FlatU16,良形键与旧行为逐位一致)。 +/// 构造为 FlatU16;良形键的输出为良形 UTF-8 字符串,与直接 `to_string` 结果一致)。 pub fn key_si_to_js_value(vm: &mut H, si: u32) -> JsValue { if is_int_key(si) { return vm.new_string(&int_key_value(si).to_string()); @@ -792,6 +795,8 @@ fn push_desc_prop(obj: &mut JsObject, shape_forge: &ShapeForge, prop_si: u32, va obj.push_prop(val); } +/// 校验并取出 Object 类方法的目标对象:`args[0]` 为接收者,`args[1]` 为待转换值, +/// 缺参或值经 ToObject 失败(null/undefined)时抛 TypeError。 fn require_obj_arg(vm: &mut H, args: &[u8], fn_name: &str) -> Result<*mut JsObject, JsValue> { if args.len() < 2 { return Err(crate::error::create_type_error(vm, &format!("Object.{fn_name} called on non-object"))); diff --git a/crates/oxide_builtins/src/typed_array.rs b/crates/oxide_builtins/src/typed_array.rs index 0a4fa06c..d0e1ec90 100644 --- a/crates/oxide_builtins/src/typed_array.rs +++ b/crates/oxide_builtins/src/typed_array.rs @@ -183,6 +183,15 @@ pub fn drop_typed_array_native(obj: &mut JsObject) -> u64 { bytes } +/// 取接收者的 TypedArrayData 视图(按值返回,非借出)。 +/// +/// # 边界与前提 +/// - 接收者非对象、对象指针为空或不是 TypedArray 对象时抛 TypeError(接收者不兼容); +/// - `native_fn` 槽为空值或存空指针时抛 TypeError(内部状态无效)。 +/// +/// # 注意事项 +/// - 返回的是解引用后的拷贝(`Ok(*data_ptr)`),底层 buffer 仍由对象持有, +/// 调用方不得据此延长其生命周期。 pub(crate) fn get_typed_array_data(vm: &mut H, this_val: JsValue) -> Result { if !this_val.is_object() { return Err(type_error(vm, "TypedArray method called on incompatible receiver"));