diff --git a/crates/oxide_builtins/src/array.rs b/crates/oxide_builtins/src/array.rs index 3730875..72ac524 100644 --- a/crates/oxide_builtins/src/array.rs +++ b/crates/oxide_builtins/src/array.rs @@ -181,6 +181,7 @@ fn unexpected_tail_call_error(vm: &mut H) -> NativeResult { NativeResult::Err(crate::error::create_type_error(vm, "unexpected tail call in array callback")) } +/// JS `Array()` 构造逻辑:单个数字参数创建指定长度空数组,其余情况把参数作为元素。 pub fn array_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject; let proto_val = JsValue::from_js_object(proto); @@ -210,6 +211,7 @@ pub fn array_constructor(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(arr)) } +/// `Array.isArray(value)`:参数是否为真正的 Array 对象。 pub fn array_is_array(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); @@ -225,6 +227,7 @@ pub fn array_is_array(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(unsafe { &*ptr }.is_array())) } +/// `Array.prototype.push(...items)`:追加元素到尾部,返回新长度。 pub fn array_push(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.push called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); @@ -236,6 +239,7 @@ pub fn array_push(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(len as i32)) } +/// `Array.prototype.pop()`:移除并返回末位元素;空数组返回 undefined。 pub fn array_pop(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.pop called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); @@ -249,6 +253,7 @@ pub fn array_pop(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(last) } +/// `Array.prototype.slice(start, end)`:复制区间元素返回新数组(支持负索引)。 pub fn array_slice(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.slice called with {} args", args.len()); let this_val = vm.reg(args[0]); @@ -284,6 +289,8 @@ pub fn array_slice(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } +/// `Array.prototype.splice(start, deleteCount, ...items)`:删除并/或插入元素, +/// 返回被删除元素组成的新数组。 pub fn array_splice(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.splice called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); @@ -347,6 +354,7 @@ pub fn array_splice(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(removed_arr)) } +/// `Array.prototype.concat(...items)`:连接 this 与参数(数组参数展开)返回新数组。 pub fn array_concat(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.concat called with {} args", args.len()); let this_val = vm.reg(args[0]); @@ -386,6 +394,7 @@ pub fn array_concat(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } +/// `Array.prototype.join(separator)`:用分隔符连接元素字符串(null/undefined 视为空串)。 pub fn array_join(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.join called with {} args", args.len()); let this_val = vm.reg(args[0]); @@ -412,12 +421,14 @@ pub fn array_join(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&joined)) } +/// `Array.prototype.toString`:委托给 join,默认用 `,` 分隔。 pub fn array_to_string(vm: &mut H, args: &[u8]) -> NativeResult { // Array.prototype.toString() delegates to join with the default "," separator, // ignoring its own arguments per spec. array_join(vm, &[args[0]]) } +/// `Array.prototype.indexOf(searchElement, fromIndex)`:用严格相等查找首个匹配索引,找不到返回 -1。 pub fn array_index_of(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.indexOf called with {} args", args.len()); let this_val = vm.reg(args[0]); @@ -456,6 +467,8 @@ pub fn array_index_of(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(-1)) } +/// `Array.prototype.includes(searchElement, fromIndex)`:用 SameValueZero 判断是否包含 +/// (NaN 视为存在、+0/-0 视为相同)。 pub fn array_includes(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.includes called with {} args", args.len()); let this_val = vm.reg(args[0]); @@ -510,6 +523,7 @@ fn same_value_zero(a: JsValue, b: JsValue) -> bool { oxide_runtime_api::strict_equality(a, b) } +/// `Array.prototype.reverse()`:原地反转元素顺序,返回 this。 pub fn array_reverse(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.reverse called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); @@ -527,6 +541,7 @@ pub fn array_reverse(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.reg(args[0])) } +/// `Array.prototype.flat(depth)`:按深度递归展开嵌套数组(含循环引用保护)返回新数组。 pub fn array_flat(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.flat called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); @@ -584,6 +599,7 @@ pub fn array_flat(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } +/// `Array.prototype.forEach(callback, thisArg)`:对每个元素调用 callback,返回 undefined。 pub fn array_for_each(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.forEach called with {} args", args.len()); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); @@ -606,6 +622,7 @@ pub fn array_for_each(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } +/// `Array.prototype.map(callback, thisArg)`:对每个元素调用 callback 生成新数组。 pub fn array_map(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.map called with {} args", args.len()); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); @@ -634,6 +651,7 @@ pub fn array_map(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } +/// `Array.prototype.filter(callback, thisArg)`:保留 callback 返回真值的元素形成新数组。 pub fn array_filter(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.filter called with {} args", args.len()); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); @@ -672,6 +690,8 @@ pub fn array_filter(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } +/// `Array.prototype.reduce(callback, initialValue)`:从左到右累计归约; +/// 空数组且无初始值抛 TypeError。 pub fn array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.reduce called with {} args", args.len()); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); @@ -723,6 +743,7 @@ pub fn array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(accumulator) } +/// `Array.prototype.find(callback, thisArg)`:返回首个 callback 为真的元素,否则 undefined。 pub fn array_find(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.find called with {} args", args.len()); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); @@ -759,6 +780,7 @@ pub fn array_find(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } +/// `Array.prototype.some(callback, thisArg)`:任一元素满足 callback 返回 true。 pub fn array_some(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.some called with {} args", args.len()); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); @@ -795,6 +817,7 @@ pub fn array_some(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(false)) } +/// `Array.prototype.every(callback, thisArg)`:所有元素满足 callback 才返回 true。 pub fn array_every(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.every called with {} args", args.len()); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); @@ -831,6 +854,7 @@ pub fn array_every(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(true)) } +/// `Array.prototype.flatMap(callback, thisArg)`:map 后把返回的数组展开一层。 pub fn array_flat_map(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.flatMap called with {} args", args.len()); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); @@ -886,6 +910,7 @@ pub fn array_flat_map(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } +/// `Array.prototype.shift()`:移除并返回首元素,其余元素前移;空数组返回 undefined。 pub fn array_shift(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.shift called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); @@ -903,6 +928,7 @@ pub fn array_shift(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(first) } +/// `Array.prototype.unshift(...items)`:插入元素到头部,返回新长度。 pub fn array_unshift(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.unshift called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); @@ -921,6 +947,7 @@ pub fn array_unshift(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(new_len as i32)) } +/// `Array.prototype.fill(value, start, end)`:用给定值填充区间,返回 this。 pub fn array_fill(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.fill called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); @@ -945,6 +972,7 @@ pub fn array_fill(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.reg(args[0])) } +/// `Array.prototype.copyWithin(target, start, end)`:在数组内部复制元素区间,返回 this。 pub fn array_copy_within(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.copyWithin called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); @@ -982,6 +1010,7 @@ pub fn array_copy_within(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.reg(args[0])) } +/// `Array.prototype.at(index)`:按索引取元素,支持负索引;越界返回 undefined。 pub fn array_at(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.at called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); @@ -1001,6 +1030,7 @@ pub fn array_at(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(arr.get_prop_at(index)) } +/// `Array.prototype.lastIndexOf(searchElement, fromIndex)`:从后往前查找首个匹配索引。 pub fn array_last_index_of(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.lastIndexOf called with {} args", args.len()); let this_val = vm.reg(args[0]); @@ -1040,6 +1070,7 @@ pub fn array_last_index_of(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(-1)) } +/// `Array.prototype.findIndex(callback, thisArg)`:返回首个 callback 为真的索引,否则 -1。 pub fn array_find_index(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.findIndex called with {} args", args.len()); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); @@ -1076,6 +1107,7 @@ pub fn array_find_index(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(-1)) } +/// `Array.prototype.findLast(callback, thisArg)`:从后往前返回首个 callback 为真的元素。 pub fn array_find_last(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.findLast called with {} args", args.len()); let (arr_ptr, n) = { @@ -1115,6 +1147,7 @@ pub fn array_find_last(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } +/// `Array.prototype.reduceRight(callback, initialValue)`:从右到左累计归约。 pub fn array_reduce_right(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.reduceRight called with {} args", args.len()); let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); @@ -1160,6 +1193,8 @@ pub fn array_reduce_right(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(acc) } +/// `Array.prototype.sort(compareFn)`:原地排序。默认按字符串字典序; +/// 提供比较函数时按其返回值(<0/=0/>0)排序,回调抛错则中止。 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); @@ -1224,6 +1259,7 @@ pub fn array_sort(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.reg(args[0])) } +/// `Array.prototype.values()`:返回迭代数组元素的迭代器。 pub fn array_values(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.values called with {} args", args.len()); let this_val = vm.reg(args[0]); diff --git a/crates/oxide_builtins/src/array_buffer.rs b/crates/oxide_builtins/src/array_buffer.rs index 9edf406..39c13e3 100644 --- a/crates/oxide_builtins/src/array_buffer.rs +++ b/crates/oxide_builtins/src/array_buffer.rs @@ -74,6 +74,7 @@ fn array_buffer_vec_ptr(obj: &JsObject) -> Option<*mut Vec> { obj.native_fn().map(|ptr| ptr.as_ptr() as *mut Vec) } +/// 克隆 ArrayBuffer 的字节数据到新对象(跨 epoch 克隆流程用)。 pub fn clone_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObject) { let Some(ptr) = array_buffer_vec_ptr(old_obj) else { return; @@ -86,6 +87,7 @@ pub fn clone_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObject) { new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned_ptr as *const ()) })); } +/// 释放 ArrayBuffer 的字节缓冲(native_fn 槽中的 `Box>`),返回字节数。 pub fn drop_array_buffer_native(obj: &mut JsObject) -> u64 { let Some(data_ptr) = array_buffer_vec_ptr(obj) else { return 0; @@ -122,6 +124,7 @@ pub(crate) fn array_buffer_data_ptr(vm: &mut H, this_val: JsValue) -> Ok(data_ptr) } +/// `ArrayBuffer(length)` 构造逻辑:分配 length 字节(0 填充)的新缓冲区。 pub fn array_buffer_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let length = if args.len() > 1 { match to_index(vm, vm.reg(args[1])) { @@ -134,12 +137,14 @@ pub fn array_buffer_constructor(vm: &mut H, args: &[u8]) -> NativeRes NativeResult::Ok(JsValue::from_js_object(new_array_buffer(vm, vec![0; length]))) } +/// `ArrayBuffer.prototype.byteLength` getter:返回缓冲区字节数。 pub fn array_buffer_byte_length(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let data_ptr = native_try!(array_buffer_data_ptr(vm, this_val)); NativeResult::Ok(JsValue::int(unsafe { (*data_ptr).len() } as i32)) } +/// `ArrayBuffer.prototype.slice(start, end)`:复制字节区间生成新 ArrayBuffer。 pub fn array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let data_ptr = native_try!(array_buffer_data_ptr(vm, this_val)); @@ -151,6 +156,7 @@ pub fn array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_array_buffer(vm, data[start..end].to_vec()))) } +/// `ArrayBuffer.isView(value)`:参数是 DataView 或 TypedArray 才返回 true。 pub fn array_buffer_is_view(vm: &mut H, args: &[u8]) -> NativeResult { let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; if !value.is_object() { @@ -164,6 +170,7 @@ pub fn array_buffer_is_view(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::bool(obj.is_data_view_obj() || obj.is_typed_array_obj())) } +/// `ArrayBuffer.prototype.toString`:校验 receiver 后返回 `[object ArrayBuffer]`。 pub fn array_buffer_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if array_buffer_data_ptr(vm, this_val).is_err() { diff --git a/crates/oxide_builtins/src/boolean.rs b/crates/oxide_builtins/src/boolean.rs index c2e4162..d4d98a5 100644 --- a/crates/oxide_builtins/src/boolean.rs +++ b/crates/oxide_builtins/src/boolean.rs @@ -3,6 +3,8 @@ use oxide_types::value::JsValue; use oxide_runtime_api::{NativeResult, VmHost}; +/// JS `Boolean()` 构造逻辑:把参数按 ToBoolean 语义转换。 +/// 以普通函数调用时返回原始 bool;以 new 语义调用时返回 `[[BooleanData]]` 为结果的包装对象。 pub fn boolean_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let bool_val = if args.len() > 1 { @@ -50,6 +52,8 @@ pub fn boolean_constructor(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(this_val) } +/// `Boolean.prototype.valueOf`:返回包装对象的原始 bool 值。 +/// this 为原始 bool 时直接返回;否则必须是 Boolean 包装对象,否则抛 TypeError。 pub fn boolean_prototype_value_of(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if this_val.is_bool() { @@ -72,6 +76,8 @@ pub fn boolean_prototype_value_of(vm: &mut H, args: &[u8]) -> NativeR NativeResult::Ok(val) } +/// `Boolean.prototype.toString`:返回 `"true"` 或 `"false"`。 +/// 支持原始 bool 与 Boolean 包装对象;其它 this 抛 TypeError。 pub fn boolean_prototype_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if this_val.is_bool() { diff --git a/crates/oxide_builtins/src/builtins_log.rs b/crates/oxide_builtins/src/builtins_log.rs index f19c853..4bce8a5 100644 --- a/crates/oxide_builtins/src/builtins_log.rs +++ b/crates/oxide_builtins/src/builtins_log.rs @@ -1,3 +1,4 @@ +/// 输出 ERROR 级别日志,target 固定为 `oxide::builtins`。 #[macro_export] macro_rules! builtins_error { ($($arg:tt)*) => { @@ -5,6 +6,7 @@ macro_rules! builtins_error { }; } +/// 输出 WARN 级别日志,target 固定为 `oxide::builtins`。 #[macro_export] macro_rules! builtins_warn { ($($arg:tt)*) => { @@ -12,6 +14,7 @@ macro_rules! builtins_warn { }; } +/// 输出 INFO 级别日志,target 固定为 `oxide::builtins`。 #[macro_export] macro_rules! builtins_info { ($($arg:tt)*) => { @@ -19,6 +22,7 @@ macro_rules! builtins_info { }; } +/// 输出 DEBUG 级别日志,target 固定为 `oxide::builtins`。 #[macro_export] macro_rules! builtins_debug { ($($arg:tt)*) => { @@ -26,6 +30,7 @@ macro_rules! builtins_debug { }; } +/// 输出 TRACE 级别日志,target 固定为 `oxide::builtins`。 #[macro_export] macro_rules! builtins_trace { ($($arg:tt)*) => { diff --git a/crates/oxide_builtins/src/data_view.rs b/crates/oxide_builtins/src/data_view.rs index 5dbf63f..1843d6a 100644 --- a/crates/oxide_builtins/src/data_view.rs +++ b/crates/oxide_builtins/src/data_view.rs @@ -121,6 +121,7 @@ fn write_bytes(vm: &mut H, args: &[u8], bytes: [u8; N Ok(()) } +/// `DataView(buffer, byteOffset, byteLength)` 构造逻辑:在 ArrayBuffer 上建立定点字节视图。 pub fn data_view_constructor(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "DataView requires an ArrayBuffer")); @@ -182,6 +183,7 @@ fn data_view_data_ptr(obj: &JsObject) -> Option<*mut DataViewData> { obj.native_fn().map(|ptr| ptr.as_ptr() as *mut DataViewData) } +/// 收集 DataView 引用的底层 buffer(GC 根边)。 pub fn data_view_native_edges(obj: &JsObject) -> Vec { let Some(ptr) = data_view_data_ptr(obj) else { return Vec::new(); @@ -197,6 +199,7 @@ pub fn data_view_native_edges(obj: &JsObject) -> Vec { } } +/// 克隆 DataView 视图数据到新对象,用 `rewrite` 改写 buffer 引用。 pub fn clone_data_view_native_with_rewrite(old_obj: &JsObject, new_obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, @@ -213,6 +216,7 @@ where new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned as *const ()) })); } +/// 原地重写 DataView 的 buffer 引用。 pub fn rewrite_data_view_native(obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, @@ -228,6 +232,7 @@ where } } +/// 释放 DataView 视图数据(`Box`),返回释放字节数。 pub fn drop_data_view_native(obj: &mut JsObject) -> u64 { let Some(ptr) = data_view_data_ptr(obj) else { return 0; @@ -240,16 +245,19 @@ pub fn drop_data_view_native(obj: &mut JsObject) -> u64 { std::mem::size_of::() as u64 } +/// `DataView.prototype.getInt8(byteOffset)`:读取 1 字节有符号整数。 pub fn data_view_get_int8(vm: &mut H, args: &[u8]) -> NativeResult { let bytes = native_try!(read_bytes::<1, H>(vm, args)); NativeResult::Ok(JsValue::int(i8::from_ne_bytes(bytes) as i32)) } +/// `DataView.prototype.getUint8(byteOffset)`:读取 1 字节无符号整数。 pub fn data_view_get_uint8(vm: &mut H, args: &[u8]) -> NativeResult { let bytes = native_try!(read_bytes::<1, H>(vm, args)); NativeResult::Ok(JsValue::int(bytes[0] as i32)) } +/// `DataView.prototype.getInt16(byteOffset, littleEndian)`:读取 2 字节有符号整数。 pub fn data_view_get_int16(vm: &mut H, args: &[u8]) -> NativeResult { let bytes = native_try!(read_bytes::<2, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { @@ -260,6 +268,7 @@ pub fn data_view_get_int16(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(n as i32)) } +/// `DataView.prototype.getUint16(byteOffset, littleEndian)`:读取 2 字节无符号整数。 pub fn data_view_get_uint16(vm: &mut H, args: &[u8]) -> NativeResult { let bytes = native_try!(read_bytes::<2, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { @@ -270,6 +279,7 @@ pub fn data_view_get_uint16(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::int(n as i32)) } +/// `DataView.prototype.getInt32(byteOffset, littleEndian)`:读取 4 字节有符号整数。 pub fn data_view_get_int32(vm: &mut H, args: &[u8]) -> NativeResult { let bytes = native_try!(read_bytes::<4, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { @@ -280,6 +290,7 @@ pub fn data_view_get_int32(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(n)) } +/// `DataView.prototype.getUint32(byteOffset, littleEndian)`:读取 4 字节无符号整数。 pub fn data_view_get_uint32(vm: &mut H, args: &[u8]) -> NativeResult { let bytes = native_try!(read_bytes::<4, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { @@ -290,6 +301,7 @@ pub fn data_view_get_uint32(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::float(n as f64)) } +/// `DataView.prototype.getFloat32(byteOffset, littleEndian)`:读取 4 字节单精度浮点。 pub fn data_view_get_float32(vm: &mut H, args: &[u8]) -> NativeResult { let bytes = native_try!(read_bytes::<4, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { @@ -300,6 +312,7 @@ pub fn data_view_get_float32(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::float(n as f64)) } +/// `DataView.prototype.getFloat64(byteOffset, littleEndian)`:读取 8 字节双精度浮点。 pub fn data_view_get_float64(vm: &mut H, args: &[u8]) -> NativeResult { let bytes = native_try!(read_bytes::<8, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { @@ -310,6 +323,7 @@ pub fn data_view_get_float64(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::float(n)) } +/// `DataView.prototype.getBigInt64(byteOffset, littleEndian)`:读取 8 字节有符号 64 位整数。 pub fn data_view_get_big_int64(vm: &mut H, args: &[u8]) -> NativeResult { let bytes = native_try!(read_bytes::<8, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { @@ -320,6 +334,7 @@ pub fn data_view_get_big_int64(vm: &mut H, args: &[u8]) -> NativeResu NativeResult::Ok(JsValue::float(n as f64)) } +/// `DataView.prototype.getBigUint64(byteOffset, littleEndian)`:读取 8 字节无符号 64 位整数。 pub fn data_view_get_big_uint64(vm: &mut H, args: &[u8]) -> NativeResult { let bytes = native_try!(read_bytes::<8, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { @@ -330,18 +345,21 @@ pub fn data_view_get_big_uint64(vm: &mut H, args: &[u8]) -> NativeRes NativeResult::Ok(JsValue::float(n as f64)) } +/// `DataView.prototype.setInt8(byteOffset, value)`:写入 1 字节有符号整数。 pub fn data_view_set_int8(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i32 as u8 as i8; native_try!(write_bytes(vm, args, value.to_ne_bytes())); NativeResult::Ok(JsValue::undefined()) } +/// `DataView.prototype.setUint8(byteOffset, value)`:写入 1 字节无符号整数。 pub fn data_view_set_uint8(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i32 as u8; native_try!(write_bytes(vm, args, [value])); NativeResult::Ok(JsValue::undefined()) } +/// `DataView.prototype.setInt16(byteOffset, value, littleEndian)`:写入 2 字节有符号整数。 pub fn data_view_set_int16(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i32 as u16 as i16; let bytes = if is_little_endian(vm, args, 3) { @@ -353,6 +371,7 @@ pub fn data_view_set_int16(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } +/// `DataView.prototype.setUint16(byteOffset, value, littleEndian)`:写入 2 字节无符号整数。 pub fn data_view_set_uint16(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i32 as u16; let bytes = if is_little_endian(vm, args, 3) { @@ -364,6 +383,7 @@ pub fn data_view_set_uint16(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::undefined()) } +/// `DataView.prototype.setInt32(byteOffset, value, littleEndian)`:写入 4 字节有符号整数。 pub fn data_view_set_int32(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i32; let bytes = if is_little_endian(vm, args, 3) { @@ -375,6 +395,7 @@ pub fn data_view_set_int32(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } +/// `DataView.prototype.setUint32(byteOffset, value, littleEndian)`:写入 4 字节无符号整数。 pub fn data_view_set_uint32(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as u32; let bytes = if is_little_endian(vm, args, 3) { @@ -386,6 +407,7 @@ pub fn data_view_set_uint32(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::undefined()) } +/// `DataView.prototype.setFloat32(byteOffset, value, littleEndian)`:写入单精度浮点。 pub fn data_view_set_float32(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as f32; let bytes = if is_little_endian(vm, args, 3) { @@ -397,6 +419,7 @@ pub fn data_view_set_float32(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::undefined()) } +/// `DataView.prototype.setFloat64(byteOffset, value, littleEndian)`:写入双精度浮点。 pub fn data_view_set_float64(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2); let bytes = if is_little_endian(vm, args, 3) { @@ -408,6 +431,7 @@ pub fn data_view_set_float64(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::undefined()) } +/// `DataView.prototype.setBigInt64(byteOffset, value, littleEndian)`:写入 8 字节有符号 64 位整数。 pub fn data_view_set_big_int64(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i64; let bytes = if is_little_endian(vm, args, 3) { @@ -419,6 +443,7 @@ pub fn data_view_set_big_int64(vm: &mut H, args: &[u8]) -> NativeResu NativeResult::Ok(JsValue::undefined()) } +/// `DataView.prototype.setBigUint64(byteOffset, value, littleEndian)`:写入 8 字节无符号 64 位整数。 pub fn data_view_set_big_uint64(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as u64; let bytes = if is_little_endian(vm, args, 3) { @@ -430,6 +455,7 @@ pub fn data_view_set_big_uint64(vm: &mut H, args: &[u8]) -> NativeRes NativeResult::Ok(JsValue::undefined()) } +/// `DataView.prototype.toString`:校验 receiver 后返回 `[object DataView]`。 pub fn data_view_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); native_try!(get_data_view_data(vm, this_val)); diff --git a/crates/oxide_builtins/src/date.rs b/crates/oxide_builtins/src/date.rs index ab9329a..0f496ab 100644 --- a/crates/oxide_builtins/src/date.rs +++ b/crates/oxide_builtins/src/date.rs @@ -84,6 +84,8 @@ fn naive_from_ms(ms: f64) -> Option { dt_from_ms(ms).map(|dt| dt.naive_utc()) } +/// JS `Date()` 构造逻辑:无参取当前时间;单参支持时间戳/字符串/Date 对象; +/// 多参按本地时间字段(年/月/日/时/分/秒/毫秒)组合。非构造调用返回日期字符串。 pub fn date_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let is_ctor_call = this_val.is_object() && { @@ -214,10 +216,13 @@ pub fn date_constructor(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(ptr)) } +/// `Date.now()`:返回当前时间戳(毫秒)。 pub fn date_now(_vm: &mut H, _args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(Utc::now().timestamp_millis() as f64)) } +/// `Date.parse(string)`:解析日期字符串(RFC 2822、ISO 8601 及常用格式)为时间戳; +/// 无法解析返回 NaN。 pub fn date_parse(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NAN)); @@ -263,6 +268,7 @@ pub fn date_parse(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(ts)) } +/// `Date.UTC(y, m, d, h, min, s, ms)`:按 UTC 各字段组合成时间戳(m 为 0-based 月)。 pub fn date_utc(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return NativeResult::Ok(JsValue::float(f64::NAN)); @@ -309,6 +315,7 @@ pub fn date_utc(vm: &mut H, args: &[u8]) -> NativeResult { macro_rules! make_getter { ($name:ident, $f:expr, $df:expr) => { + /// UTC 视图 getter(如 `getTime`):返回 UTC 时间的指定分量;Invalid Date 返回 NaN。 pub fn $name(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); @@ -325,6 +332,7 @@ macro_rules! make_getter { macro_rules! make_utc_getter { ($name:ident, $f:expr, $df:expr) => { + /// UTC getter(如 `getUTCFullYear`):返回 UTC 时间的指定分量;Invalid Date 返回 NaN。 pub fn $name(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); @@ -341,6 +349,7 @@ macro_rules! make_utc_getter { macro_rules! make_local_getter { ($name:ident, $f:expr, $df:expr) => { + /// 本地时区 getter(如 `getFullYear`):返回本地时间的指定分量;Invalid Date 返回 NaN。 pub fn $name(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); @@ -382,6 +391,7 @@ make_utc_getter!( f64::NAN ); +/// `Date.prototype.getTimezoneOffset()`:返回本地时区相对 UTC 的分钟偏移。 pub fn date_get_timezone_offset(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); @@ -408,6 +418,7 @@ fn get_opt_arg(vm: &H, args: &[u8], idx: usize, default: u32) -> u32 } } +/// `Date.prototype.setTime(ms)`:直接设置时间戳,返回新时间戳。 pub fn date_set_time(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let val = if args.len() > 1 { @@ -419,6 +430,7 @@ pub fn date_set_time(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(val)) } +/// `Date.prototype.setFullYear(y, m, d)`:设置本地年份(可选月/日),返回新时间戳。 pub fn date_set_full_year(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let v = vm.coerce_number_bounded(val).unwrap_or(f64::NAN); @@ -442,6 +454,7 @@ pub fn date_set_full_year(vm: &mut H, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setMonth(m, d)`:设置本地月份(可选日),返回新时间戳。 pub fn date_set_month(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let v = vm.coerce_number_bounded(val).unwrap_or(f64::NAN); @@ -463,6 +476,7 @@ pub fn date_set_month(vm: &mut H, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setDate(d)`:设置本地日,返回新时间戳。 pub fn date_set_date(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let v = vm.coerce_number_bounded(val).unwrap_or(f64::NAN); @@ -480,6 +494,7 @@ pub fn date_set_date(vm: &mut H, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setHours(h, min, s, ms)`:设置本地小时(可选分/秒/毫秒),返回新时间戳。 pub fn date_set_hours(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let v = vm.coerce_number_bounded(val).unwrap_or(f64::NAN); @@ -505,6 +520,7 @@ pub fn date_set_hours(vm: &mut H, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setMinutes(min, s, ms)`:设置本地分钟(可选秒/毫秒),返回新时间戳。 pub fn date_set_minutes(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let v = vm.coerce_number_bounded(val).unwrap_or(f64::NAN); @@ -528,6 +544,7 @@ pub fn date_set_minutes(vm: &mut H, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setSeconds(s, ms)`:设置本地秒(可选毫秒),返回新时间戳。 pub fn date_set_seconds(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let v = vm.coerce_number_bounded(val).unwrap_or(f64::NAN); @@ -549,6 +566,7 @@ pub fn date_set_seconds(vm: &mut H, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setMilliseconds(ms)`:设置本地毫秒,返回新时间戳。 pub fn date_set_milliseconds(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let v = vm.coerce_number_bounded(val).unwrap_or(f64::NAN); @@ -567,6 +585,7 @@ pub fn date_set_milliseconds(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setUTCFullYear(y, m, d)`:设置 UTC 年份(可选月/日),返回新时间戳。 pub fn date_set_utc_full_year(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); @@ -596,6 +615,7 @@ pub fn date_set_utc_full_year(vm: &mut H, args: &[u8]) -> NativeResul NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setUTCMonth(m, d)`:设置 UTC 月份(可选日),返回新时间戳。 pub fn date_set_utc_month(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); @@ -624,6 +644,7 @@ pub fn date_set_utc_month(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setUTCDate(d)`:设置 UTC 日,返回新时间戳。 pub fn date_set_utc_date(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); @@ -651,6 +672,7 @@ pub fn date_set_utc_date(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setUTCHours(h, min, s, ms)`:设置 UTC 小时(可选分/秒/毫秒),返回新时间戳。 pub fn date_set_utc_hours(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); @@ -682,6 +704,7 @@ pub fn date_set_utc_hours(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setUTCMinutes(min, s, ms)`:设置 UTC 分钟(可选秒/毫秒),返回新时间戳。 pub fn date_set_utc_minutes(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); @@ -711,6 +734,7 @@ pub fn date_set_utc_minutes(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setUTCSeconds(s, ms)`:设置 UTC 秒(可选毫秒),返回新时间戳。 pub fn date_set_utc_seconds(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); @@ -738,6 +762,7 @@ pub fn date_set_utc_seconds(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.setUTCMilliseconds(ms)`:设置 UTC 毫秒,返回新时间戳。 pub fn date_set_utc_milliseconds(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); @@ -761,6 +786,7 @@ pub fn date_set_utc_milliseconds(vm: &mut H, args: &[u8]) -> NativeRe NativeResult::Ok(JsValue::float(ts)) } +/// Annex B `Date.prototype.getYear()`:返回本地年减 1900。 pub fn date_get_year(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); @@ -773,6 +799,8 @@ pub fn date_get_year(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// Annex B `Date.prototype.setYear(y)`:设置年份,0..99 自动加 1900; +/// 缺参/NaN 置为 Invalid Date。 pub fn date_set_year(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); @@ -799,18 +827,22 @@ pub fn date_set_year(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(ts)) } +/// `Date.prototype.toGMTString()`:别名 `toUTCString`(GMT 格式)。 pub fn date_to_gmt_string(vm: &mut H, args: &[u8]) -> NativeResult { date_to_utc_string(vm, args) } +/// `Date.prototype.toLocaleDateString()`:本地化日期字符串(当前等同于 `toDateString`)。 pub fn date_to_locale_date_string(vm: &mut H, args: &[u8]) -> NativeResult { date_to_date_string(vm, args) } +/// `Date.prototype.toLocaleString()`:本地化日期时间字符串(当前等同于 `toString`)。 pub fn date_to_locale_string(vm: &mut H, args: &[u8]) -> NativeResult { date_to_string(vm, args) } +/// `Date.prototype.toLocaleTimeString()`:本地化时间字符串(当前等同于 `toTimeString`)。 pub fn date_to_locale_time_string(vm: &mut H, args: &[u8]) -> NativeResult { date_to_time_string(vm, args) } @@ -828,10 +860,12 @@ fn date_to_string_inner(vm: &mut H, args: &[u8], format_str: &str, in NativeResult::Ok(vm.new_string(&s)) } +/// `Date.prototype.toISOString()`:输出 ISO 8601 格式(`YYYY-MM-DDTHH:MM:SS.mmmZ`)。 pub fn date_to_iso_string(vm: &mut H, args: &[u8]) -> NativeResult { date_to_string_inner(vm, args, "%Y-%m-%dT%H:%M:%S%.3fZ", "Invalid Date") } +/// `Date.prototype.toJSON()`:输出 ISO 8601 字符串;Invalid Date 返回 null。 pub fn date_to_json(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); @@ -845,22 +879,27 @@ pub fn date_to_json(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&s)) } +/// `Date.prototype.toString()`:本地时间完整字符串(如 `Wed Aug 05 2026 12:00:00 GMT+0000`)。 pub fn date_to_string(vm: &mut H, args: &[u8]) -> NativeResult { date_to_string_inner(vm, args, "%a %b %d %Y %H:%M:%S %Z %z", "Invalid Date") } +/// `Date.prototype.toDateString()`:日期部分字符串(如 `Wed Aug 05 2026`)。 pub fn date_to_date_string(vm: &mut H, args: &[u8]) -> NativeResult { date_to_string_inner(vm, args, "%a %b %d %Y", "Invalid Date") } +/// `Date.prototype.toTimeString()`:时间部分字符串(如 `12:00:00 GMT+0000`)。 pub fn date_to_time_string(vm: &mut H, args: &[u8]) -> NativeResult { date_to_string_inner(vm, args, "%H:%M:%S %Z", "Invalid Date") } +/// `Date.prototype.toUTCString()`:UTC 完整字符串(如 `Wed, 05 Aug 2026 12:00:00 GMT`)。 pub fn date_to_utc_string(vm: &mut H, args: &[u8]) -> NativeResult { date_to_string_inner(vm, args, "%a, %d %b %Y %H:%M:%S GMT", "Invalid Date") } +/// `Date.prototype.valueOf()`:返回时间戳(毫秒)。 pub fn date_value_of(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); diff --git a/crates/oxide_builtins/src/error.rs b/crates/oxide_builtins/src/error.rs index dd04eb2..5eddb7e 100644 --- a/crates/oxide_builtins/src/error.rs +++ b/crates/oxide_builtins/src/error.rs @@ -26,6 +26,8 @@ fn set_own_message(host: &mut H, this: *mut JsObject, args: &[u8]) { } } +/// 按错误类型名创建对应 Error 对象(message 非空时写为自身属性)。 +/// 这是引擎内部构造错误的统一入口,供 VM/native 层抛错使用。 pub fn create_kind_error(host: &mut H, kind: &str, msg: &str) -> JsValue { let proto_ptr = match kind { "TypeError" => P::as_ptr(&host.session().builtin_world().type_error_proto) as *mut JsObject, @@ -53,32 +55,40 @@ pub fn create_kind_error(host: &mut H, kind: &str, msg: &str) -> JsVa JsValue::from_js_object(obj) } +/// 创建一个带指定 message 的 TypeError 对象。 pub fn create_type_error(host: &mut H, msg: &str) -> JsValue { create_kind_error(host, "TypeError", msg) } +/// 创建一个带指定 message 的普通 Error 对象。 pub fn create_error(host: &mut H, msg: &str) -> JsValue { create_kind_error(host, "Error", msg) } +/// 创建一个带指定 message 的 ReferenceError 对象。 pub fn create_reference_error(host: &mut H, msg: &str) -> JsValue { create_kind_error(host, "ReferenceError", msg) } +/// 创建一个带指定 message 的 RangeError 对象。 pub fn create_range_error(host: &mut H, msg: &str) -> JsValue { create_kind_error(host, "RangeError", msg) } +/// 创建一个带指定 message 的 SyntaxError 对象。 pub fn create_syntax_error(host: &mut H, msg: &str) -> JsValue { create_kind_error(host, "SyntaxError", msg) } +/// 创建一个带指定 message 的 URIError 对象。 pub fn create_uri_error(host: &mut H, msg: &str) -> JsValue { create_kind_error(host, "URIError", msg) } macro_rules! error_ctor { ($name:ident, $proto_field:ident) => { + /// 对应 Error 子类(如 `TypeError`)的构造函数:接收第一个实参作为 message, + /// 返回原型链指向对应 prototype 的 Error 对象。 pub fn $name(host: &mut H, args: &[u8]) -> NativeResult { let this_val = host.reg(255); let this = if this_val.is_object() { @@ -102,6 +112,8 @@ error_ctor!(syntax_error_constructor, syntax_error_proto); error_ctor!(uri_error_constructor, uri_error_proto); error_ctor!(eval_error_constructor, eval_error_proto); +/// `Error.prototype.toString`:按 `name: message` 拼接字符串; +/// 缺少 name/message 时按规范回退到 `"Error"` 或空串。 pub fn error_to_string(host: &mut H, args: &[u8]) -> NativeResult { let this_val = host.reg(args[0]); if !this_val.is_object() { @@ -145,6 +157,7 @@ pub fn error_to_string(host: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(host.new_string(&result)) } +/// `Error.prototype.stack` getter:输出 `name: message` 头后附调用栈函数名列表。 pub fn error_stack_getter(host: &mut H, args: &[u8]) -> NativeResult { let this_val = host.reg(args[0]); let (name_str, msg_str) = if this_val.is_object() { diff --git a/crates/oxide_builtins/src/function.rs b/crates/oxide_builtins/src/function.rs index ee52454..7a10f70 100644 --- a/crates/oxide_builtins/src/function.rs +++ b/crates/oxide_builtins/src/function.rs @@ -30,6 +30,8 @@ fn bind_dispatcher(vm: &mut H, args: &[u8]) -> NativeResult { invoke_target(vm, bound_target, bound_this, &arg_regs) } +/// `Function.prototype.call(thisArg, ...args)`:以指定 this 调用目标函数。 +/// 返回 TailCall 让 VM 继续执行目标函数体。 pub fn function_call(vm: &mut H, args: &[u8]) -> NativeResult { if args.is_empty() { return NativeResult::Err(JsValue::undefined()); @@ -40,6 +42,8 @@ pub fn function_call(vm: &mut H, args: &[u8]) -> NativeResult { invoke_target(vm, target_val, this_val, &arg_regs) } +/// `Function.prototype.apply(thisArg, argsArray)`:以指定 this 和参数数组调用目标函数。 +/// 数组元素拷入寄存器(上限受寄存器空间限制,最多 55 个参数)。 pub fn function_apply(vm: &mut H, args: &[u8]) -> NativeResult { if args.is_empty() { return NativeResult::Err(JsValue::undefined()); @@ -80,6 +84,8 @@ pub fn function_apply(vm: &mut H, args: &[u8]) -> NativeResult { invoke_target(vm, target_val, this_val, &arg_regs) } +/// `Function.prototype.bind(thisArg, ...args)`:返回绑定 this 的新包装函数, +/// 调用时通过 `bind_dispatcher` 转发到原目标。非函数目标抛 TypeError。 pub fn function_bind(vm: &mut H, args: &[u8]) -> NativeResult { if args.is_empty() { return NativeResult::Err(crate::error::create_type_error( @@ -111,6 +117,8 @@ pub fn function_bind(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(wrapper)) } +/// `Function.prototype.toString`:返回 `function name() { [native code] }` +/// 或 `[bytecode]` 形式;函数名取自 `name` 属性或字节码子模块名。 pub fn function_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if !this_val.is_object() { diff --git a/crates/oxide_builtins/src/global.rs b/crates/oxide_builtins/src/global.rs index 4c24a76..5f3c71a 100644 --- a/crates/oxide_builtins/src/global.rs +++ b/crates/oxide_builtins/src/global.rs @@ -2,7 +2,10 @@ use oxide_runtime_api::VmHost; mod annex_b; mod uri; +/// 全局函数模块:聚合 Annex B 的 `escape`/`unescape` 与 URI 处理函数 +/// (`encodeURI`/`decodeURI`/`encodeURIComponent`/`decodeURIComponent`)。 pub use annex_b::{js_escape, js_unescape}; +/// URI 编解码全局函数(`encodeURI`/`decodeURI` 等)。 pub use uri::{decode_uri, decode_uri_component, encode_uri, encode_uri_component}; fn string_arg(vm: &mut H, args: &[u8]) -> String { diff --git a/crates/oxide_builtins/src/global/annex_b.rs b/crates/oxide_builtins/src/global/annex_b.rs index 9cbc9d5..6295531 100644 --- a/crates/oxide_builtins/src/global/annex_b.rs +++ b/crates/oxide_builtins/src/global/annex_b.rs @@ -84,11 +84,15 @@ fn unescape_string(input: &str) -> String { out } +/// Annex B 的全局 `escape(string)`:除 ASCII 字母数字与 `@*_+-./` 外全部编码。 +/// <=0xFF 字符用 `%XX`,其它按 UTF-16 code unit 用 `%uXXXX` 转义。 pub fn js_escape(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); NativeResult::Ok(vm.new_string(&escape_string(&input))) } +/// Annex B 的全局 `unescape(string)`:解码 `escape` 生成的 `%XX`/`%uXXXX` 序列, +/// 支持 surrogate pair 合并;无法解析的序列原样保留。 pub fn js_unescape(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); NativeResult::Ok(vm.new_string(&unescape_string(&input))) diff --git a/crates/oxide_builtins/src/global/uri.rs b/crates/oxide_builtins/src/global/uri.rs index 77240b2..a4c6c5d 100644 --- a/crates/oxide_builtins/src/global/uri.rs +++ b/crates/oxide_builtins/src/global/uri.rs @@ -85,16 +85,22 @@ fn uri_error(vm: &mut H) -> NativeResult { NativeResult::Err(crate::error::create_uri_error(vm, URI_ERROR_MESSAGE)) } +/// `encodeURI`:编码输入为 URI,保留未转义字符与保留字符 `;/?:@&=+$,#`, +/// 其余按 UTF-8 字节转成 `%XX`。 pub fn encode_uri(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); NativeResult::Ok(vm.new_string(&encode_uri_string(&input, URI_SAFE))) } +/// `encodeURIComponent`:编码输入为 URI component,仅保留未转义字符, +/// 保留字符(如 `=&`)也会被转成 `%XX`。 pub fn encode_uri_component(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); NativeResult::Ok(vm.new_string(&encode_uri_string(&input, URI_UNESCAPED))) } +/// `decodeURI`:解码 `%XX` 序列,但保留字符的转义形式原样保留(不还原), +/// 保证 decode(encode(uri)) 不变。非法序列抛 URIError。 pub fn decode_uri(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); match decode_uri_string(&input, true) { @@ -103,6 +109,8 @@ pub fn decode_uri(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `decodeURIComponent`:解码所有 `%XX` 序列(含保留字符), +/// 非法或截断的 UTF-8 序列抛 URIError。 pub fn decode_uri_component(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); match decode_uri_string(&input, false) { diff --git a/crates/oxide_builtins/src/iterator.rs b/crates/oxide_builtins/src/iterator.rs index ee8457a..8815a94 100644 --- a/crates/oxide_builtins/src/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -7,10 +7,13 @@ use oxide_runtime_api::{NativeResult, VmHost}; const INNER_PROP: &str = "__inner__"; const INDEX_PROP: &str = "__index__"; +/// 占位构造函数:`Iterator` 不是构造函数,任何调用都抛 TypeError。 pub fn iterator_constructor(vm: &mut H, _args: &[u8]) -> NativeResult { NativeResult::Err(crate::error::create_type_error(vm, "Iterator is not a constructor")) } +/// `Iterator.from(iterable)`:为任意可迭代值包装一个迭代器对象。 +/// 包装器带 `next` 与 `return`(用于 for-of 提前退出时的 IteratorClose 清理)。 pub fn iterator_from(vm: &mut H, args: &[u8]) -> NativeResult { let iterable = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; match make_iterator_for_value(vm, iterable) { @@ -19,6 +22,8 @@ pub fn iterator_from(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// 为任意值创建统一迭代器包装对象:String/Array/Map/Set 直接支持索引遍历, +/// 其它对象则要求提供可调用的 `next`。不可迭代时返回 TypeError。 pub fn make_iterator_for_value(vm: &mut H, value: JsValue) -> Result { let inner = get_iterator(vm, value)?; let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject; @@ -70,6 +75,8 @@ fn iterator_wrapper_return(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// 迭代器包装器的 `next` 方法:对 Array/String/Map/Set 直接按索引取值, +/// 其它对象委托其自身 `next`;底层抛出异常时透传原始值(不做二次包装)。 pub fn iterator_wrapper_next(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if !this_val.is_object() { diff --git a/crates/oxide_builtins/src/json.rs b/crates/oxide_builtins/src/json.rs index 15cf6c1..f3475c5 100644 --- a/crates/oxide_builtins/src/json.rs +++ b/crates/oxide_builtins/src/json.rs @@ -9,6 +9,8 @@ use crate::object::walk_own_keys; use oxide_runtime_api::{NativeResult, VmHost}; +/// `JSON.parse(text, reviver)`:解析 JSON 文本为 JS 值(经 serde_json)。 +/// 提供 reviver 时以后序遍历逐属性调用 reviver 重建值。 pub fn json_parse(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_syntax_error(vm, "JSON.parse requires 1 argument")); @@ -205,6 +207,8 @@ fn call_to_json(vm: &mut H, obj_val: JsValue, key: &str) -> Result(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::undefined()); diff --git a/crates/oxide_builtins/src/lib.rs b/crates/oxide_builtins/src/lib.rs index b23061c..2c68706 100644 --- a/crates/oxide_builtins/src/lib.rs +++ b/crates/oxide_builtins/src/lib.rs @@ -1,22 +1,53 @@ +//! OxideJS 内置对象层(builtins)。 +//! +//! 本 crate 实现 ECMAScript 内置对象与全局函数的 native 实现,包括 Array、String、 +//! Date、Object、Map、Set、JSON、Math、Number、RegExp、Symbol、TypedArray、 +//! ArrayBuffer、DataView、Reflect、Function、Error 与全局函数(URI/escape 等)。 +//! 每个内置方法以 `fn xxx(vm, args: &[u8]) -> NativeResult` 形式暴露, +//! 由上层(oxide_vm / oxide_api)注册为 JS 全局对象上的 native function。 +//! 尚未支持的特性(Proxy、BigInt、WeakMap 等)在 `stubs` 中以占位实现占位并抛 TypeError。 + +/// Array 内置对象实现(constructor 与全部 Array.prototype 方法)。 pub mod array; +/// ArrayBuffer 内置对象实现(字节缓冲区与 byteLength/slice/isView)。 pub mod array_buffer; +/// Boolean 内置对象实现(constructor 与 prototype 的 valueOf/toString)。 pub mod boolean; +/// builtins 层日志宏(`builtins_error`/`builtins_info` 等),target 为 `oxide::builtins`。 pub mod builtins_log; +/// DataView 内置对象实现(对 ArrayBuffer 的定点读写视图)。 pub mod data_view; +/// Date 内置对象实现(时间戳存取、get/set 系列与 to*String 系列)。 pub mod date; +/// Error 内置对象实现(各类 Error 构造函数与 stack/toString)。 pub mod error; +/// Function 内置对象实现(call/apply/bind/toString)。 pub mod function; +/// 全局函数模块(URI 编解码与 Annex B 的 escape/unescape)。 pub mod global; +/// 迭代器相关实现(Iterator 包装、for-of 底层迭代逻辑)。 pub mod iterator; +/// JSON 内置对象实现(parse/stringify)。 pub mod json; +/// Map 内置对象实现(键值集合与 entries/keys/values 迭代)。 pub mod map; +/// Math 内置对象实现(数学函数)。 pub mod math; +/// Number 内置对象实现(constructor 与 toFixed/isInteger 等)。 pub mod number; +/// Object 内置对象实现(keys/create/assign/defineProperty 等静态方法)。 pub mod object; +/// Reflect 内置对象实现(属性操作镜像)。 pub mod reflect; +/// RegExp 内置对象实现(constructor/test/exec/toString)。 pub mod regexp; +/// Set 内置对象实现(值集合与 entries/values/keys 迭代)。 pub mod set; +/// String 内置对象实现(constructor 与全部 String.prototype 方法)。 pub mod string; +/// 未实现特性的占位 native 实现(统一抛 TypeError)。 pub mod stubs; +/// Symbol 内置对象实现(constructor/for/keyFor/toString)。 pub mod symbol; +/// TypedArray 内置对象实现(元素访问、fill/slice/subarray/set 等)。 pub mod typed_array; diff --git a/crates/oxide_builtins/src/map.rs b/crates/oxide_builtins/src/map.rs index 8057cf7..ebcae42 100644 --- a/crates/oxide_builtins/src/map.rs +++ b/crates/oxide_builtins/src/map.rs @@ -68,6 +68,7 @@ fn alloc_map(vm: &mut H) -> *mut JsObject { vm.alloc_object(obj) } +/// 收集 Map 中作为对象引用的键和值(GC 根边),供跨 epoch 遍历/重写时追踪。 pub fn map_native_edges(obj: &JsObject) -> Vec { if !obj.is_map() { return Vec::new(); @@ -85,6 +86,8 @@ pub fn map_native_edges(obj: &JsObject) -> Vec { } } +/// 克隆 Map 的 native 数据到新对象,用 `rewrite` 改写其中的对象引用 +/// (供跨 epoch 的对象重写/克隆流程使用)。 pub fn clone_map_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, @@ -108,6 +111,7 @@ where dst.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8); } +/// 原地重写 Map 的 native 数据,用 `rewrite` 改写其中的对象引用。 pub fn rewrite_map_native(obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, @@ -130,6 +134,7 @@ where } } +/// 释放 Map 的 native 数据(IndexMap),返回释放的字节数供泄漏统计。 pub fn drop_map_native(obj: &mut JsObject) -> u64 { if !obj.is_map() { return 0; @@ -147,11 +152,13 @@ pub fn drop_map_native(obj: &mut JsObject) -> u64 { } } +/// `Map` 构造函数:创建带空 IndexMap native 数据的 Map 对象。 pub fn map_constructor(vm: &mut H, _args: &[u8]) -> NativeResult { let map_obj = alloc_map(vm); NativeResult::Ok(JsValue::from_js_object(map_obj)) } +/// `Map.prototype.set(key, value)`:插入/更新键值对,返回 this。 pub fn map_set(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); @@ -163,6 +170,7 @@ pub fn map_set(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(this_val) } +/// `Map.prototype.get(key)`:返回 key 对应的值;不存在返回 undefined。 pub fn map_get(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); @@ -171,6 +179,7 @@ pub fn map_get(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(found.unwrap_or(JsValue::undefined())) } +/// `Map.prototype.has(key)`:key 是否存在。 pub fn map_has(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); @@ -179,6 +188,7 @@ pub fn map_has(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(found)) } +/// `Map.prototype.delete(key)`:删除键并返回是否删除成功。 pub fn map_delete(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); @@ -187,6 +197,7 @@ pub fn map_delete(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(removed.is_some())) } +/// `Map.prototype.clear()`:清空全部键值对,返回 undefined。 pub fn map_clear(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); @@ -196,12 +207,14 @@ pub fn map_clear(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } +/// `Map.prototype.size` getter:返回键值对数量。 pub fn map_size(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); NativeResult::Ok(JsValue::float(unsafe { (*inner).len() } as f64)) } +/// `Map.prototype.entries()`:返回按插入序迭代 `[key, value]` 对的迭代器。 pub fn map_entries(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_map_inner(vm, this_val)); @@ -212,6 +225,7 @@ pub fn map_entries(vm: &mut H, args: &[u8]) -> NativeResult { )) } +/// `Map.prototype.values()`:返回按插入序迭代值的迭代器。 pub fn map_values(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_map_inner(vm, this_val)); @@ -222,6 +236,7 @@ pub fn map_values(vm: &mut H, args: &[u8]) -> NativeResult { )) } +/// `Map.prototype.keys()`:返回按插入序迭代键的迭代器。 pub fn map_keys(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_map_inner(vm, this_val)); diff --git a/crates/oxide_builtins/src/math.rs b/crates/oxide_builtins/src/math.rs index c48be7e..074e202 100644 --- a/crates/oxide_builtins/src/math.rs +++ b/crates/oxide_builtins/src/math.rs @@ -21,6 +21,7 @@ fn arg2(vm: &mut H, args: &[u8]) -> (f64, f64) { (a, b) } +/// `Math.abs`:返回参数的绝对值。int 参数走 int 运算,其余按 double 返回。 pub fn math_abs(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NAN)); @@ -35,6 +36,8 @@ pub fn math_abs(vm: &mut H, args: &[u8]) -> NativeResult { macro_rules! math_unary { ($name:ident, $op:ident) => { + /// 一元数学函数(`Math.acos` 等):对首个参数做 Rust 同名浮点运算, + /// 返回 double 结果;缺参或不可转数字时按 NaN 处理。 pub fn $name(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(arg1(vm, args).$op())) } @@ -65,17 +68,20 @@ math_unary!(math_tan, tan); math_unary!(math_tanh, tanh); math_unary!(math_trunc, trunc); +/// `Math.atan2(y, x)`:返回 y/x 的反正切角(弧度)。 pub fn math_atan2(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::float(a.atan2(b))) } +/// `Math.round`:四舍五入到最接近的整数,.5 时向正无穷取整(JS 语义)。 pub fn math_round(vm: &mut H, args: &[u8]) -> NativeResult { let x = arg1(vm, args); let r = if x < 0.0 { (x - 0.5).ceil() } else { (x + 0.5).floor() }; NativeResult::Ok(JsValue::float(r)) } +/// `Math.sign`:返回 1 / -1 / 0 / -0 / NaN。 pub fn math_sign(vm: &mut H, args: &[u8]) -> NativeResult { let x = arg1(vm, args); if x.is_nan() { @@ -89,30 +95,36 @@ pub fn math_sign(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `Math.clz32`:返回 32 位无符号整数表示的前导零个数。 pub fn math_clz32(vm: &mut H, args: &[u8]) -> NativeResult { let n = arg1(vm, args) as u32; NativeResult::Ok(JsValue::int(n.leading_zeros() as i32)) } +/// `Math.fround`:把 double 舍入到 float32 精度再还原为 double。 pub fn math_fround(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(arg1(vm, args) as f32 as f64)) } +/// `Math.hypot`:返回 sqrt(a²+b²)(当前仅支持两个参数)。 pub fn math_hypot(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::float(a.hypot(b))) } +/// `Math.imul`:按 32 位整数做 wrap-around 乘法。 pub fn math_imul(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::int((a as i32).wrapping_mul(b as i32))) } +/// `Math.pow(base, exp)`:返回幂运算结果。 pub fn math_pow(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::float(a.powf(b))) } +/// `Math.max`:返回参数中的最大值;任一无参时返回 -Infinity,任一 NaN 时返回 NaN。 pub fn math_max(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NEG_INFINITY)); @@ -130,6 +142,7 @@ pub fn math_max(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(m)) } +/// `Math.min`:返回参数中的最小值;无参时返回 Infinity,任一 NaN 时返回 NaN。 pub fn math_min(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::INFINITY)); @@ -147,6 +160,7 @@ pub fn math_min(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(m)) } +/// `Math.random`:推进 RNG 并返回 [0, 1) 的伪随机浮点数。 pub fn math_random(vm: &mut H, _args: &[u8]) -> NativeResult { vm.step_rng(); NativeResult::Ok(JsValue::float(vm.math_rng_value())) diff --git a/crates/oxide_builtins/src/number.rs b/crates/oxide_builtins/src/number.rs index ce4232e..d696426 100644 --- a/crates/oxide_builtins/src/number.rs +++ b/crates/oxide_builtins/src/number.rs @@ -2,6 +2,8 @@ use oxide_types::value::JsValue; use oxide_runtime_api::{NativeResult, VmHost}; +/// JS `Number()` 构造逻辑:把参数按 ToNumber 语义转换。 +/// 普通调用返回原始 number(整数走 int 表示);new 语义返回 `[[NumberData]]` 包装对象。 pub fn number_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let n = if args.len() > 1 { vm.coerce_number_bounded(vm.reg(args[1])).unwrap_or(f64::NAN) @@ -46,6 +48,7 @@ pub fn number_constructor(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `Number.isNaN`:参数严格等于 NaN 才返回 true(不做隐式类型转换)。 pub fn number_is_nan(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); @@ -54,6 +57,7 @@ pub fn number_is_nan(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(n.is_nan())) } +/// `Number.isFinite`:参数为有限数才返回 true(不做隐式类型转换)。 pub fn number_is_finite(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); @@ -62,6 +66,8 @@ pub fn number_is_finite(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(n.is_finite())) } +/// `parseInt(string, radix)`:按指定进制解析整数前缀;支持 `0x` 前缀, +/// 空串或非法前缀返回 NaN。radix 为 0 或缺省时按 10 进制(`0x` 前缀除外)。 pub fn number_parse_int(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NAN)); @@ -103,6 +109,7 @@ pub fn number_parse_int(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(f64::NAN)) } +/// `parseFloat(string)`:解析尽可能长的十进制浮点前缀;无法解析返回 NaN。 pub fn number_parse_float(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NAN)); @@ -120,6 +127,8 @@ pub fn number_parse_float(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `Number.prototype.toString(radix)`:按指定进制(2..36)转字符串。 +/// 十进制走 ryu 快速格式化,NaN/Infinity 有专名输出。 pub fn number_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let n = vm.coerce_number_bounded(vm.reg(args[0])).unwrap_or(f64::NAN); let radix = if args.len() > 1 { @@ -171,6 +180,8 @@ pub fn number_to_string(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `Number.prototype.toFixed(digits)`:固定小数位数(0..100)输出字符串, +/// 超出范围抛 RangeError;NaN/Infinity 输出专名。 pub fn number_to_fixed(vm: &mut H, args: &[u8]) -> NativeResult { let n = vm.coerce_number_bounded(vm.reg(args[0])).unwrap_or(f64::NAN); if n.is_nan() { @@ -196,6 +207,7 @@ pub fn number_to_fixed(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&formatted)) } +/// `Number.isInteger`:参数是有限且无小数部分的数值才返回 true。 pub fn number_is_integer(vm: &mut H, args: &[u8]) -> NativeResult { let val = vm.reg(if args.len() > 1 { args[1] } else { args[0] }); if !val.is_int() && !val.is_double() { @@ -205,6 +217,7 @@ pub fn number_is_integer(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(n.trunc() == n && n.is_finite())) } +/// `Number.isSafeInteger`:参数是安全整数范围(±2^53-1)内的整数才返回 true。 pub fn number_is_safe_integer(vm: &mut H, args: &[u8]) -> NativeResult { let val = vm.reg(if args.len() > 1 { args[1] } else { args[0] }); if !val.is_int() && !val.is_double() { @@ -215,6 +228,8 @@ pub fn number_is_safe_integer(vm: &mut H, args: &[u8]) -> NativeResul NativeResult::Ok(JsValue::bool(safe)) } +/// `Number.prototype.toPrecision(precision)`:按有效数字位数(1..100)输出, +/// 科学计数法与定点表示按指数自动切换,超范围抛 RangeError。 pub fn number_to_precision(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() <= 1 { return NativeResult::Err(crate::error::create_range_error( @@ -253,6 +268,8 @@ pub fn number_to_precision(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `Number.prototype.toExponential(digits)`:按科学计数法输出; +/// digits 缺省时自动决定小数位数,超范围抛 RangeError。 pub fn number_to_exponential(vm: &mut H, args: &[u8]) -> NativeResult { let n = vm.coerce_number_bounded(vm.reg(args[0])).unwrap_or(f64::NAN); if n.is_nan() { @@ -291,6 +308,8 @@ pub fn number_to_exponential(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(vm.new_string(&format!("{}{}", sign_prefix, formatted))) } +/// `Number.prototype.valueOf`:返回包装对象的原始 number; +/// 原始 number 直接返回,非 Number 对象抛 TypeError。 pub fn number_value_of(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(args[0]); if this_val.is_int() || this_val.is_double() { diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index 3e6a694..6cdb501 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -13,6 +13,7 @@ fn is_integer_index(key: &str) -> bool { key.bytes().all(|b| b.is_ascii_digit()) && key.parse::().unwrap_or(u64::MAX) < (1u64 << 32) - 1 } +/// 收集对象全部自身属性(shape 链),按规范顺序排列:整数索引在前升序,其余保持插入序。 pub(crate) fn walk_own_keys(vm: &H, obj: &JsObject) -> Vec<(u32, u32)> { let mut keys: Vec<(u32, u32)> = Vec::new(); let shape_id = obj.shape_id(); @@ -53,11 +54,13 @@ pub(crate) fn walk_own_keys(vm: &H, obj: &JsObject) -> Vec<(u32, u32) keys } +/// JS `Object()` 构造逻辑:创建空对象(prototype 为 null,由 VM 补装内置原型)。 pub fn object_constructor(vm: &mut H, _args: &[u8]) -> NativeResult { let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); NativeResult::Ok(JsValue::from_js_object(obj)) } +/// `Object.keys(obj)`:返回可枚举自身属性的字符串名数组。 pub fn object_keys(vm: &mut H, args: &[u8]) -> NativeResult { let obj_ptr = match require_obj_arg(vm, args, "keys") { Ok(ptr) => ptr, @@ -102,6 +105,7 @@ pub fn object_keys(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(arr)) } +/// `Object.create(proto, properties)`:以指定 prototype 创建新对象。 pub fn object_create(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "Object.create: at least 1 argument required")); @@ -121,6 +125,7 @@ pub fn object_create(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(obj)) } +/// `Object.assign(target, ...sources)`:拷贝各源对象的可枚举自身属性到目标,返回目标。 pub fn object_assign(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "Object.assign requires a target")); @@ -164,6 +169,7 @@ pub fn object_assign(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(target_val) } +/// `Object.is(a, b)`:按 SameValue 语义比较(NaN 相等、+0/-0 不等)。 pub fn object_is(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return NativeResult::Err(crate::error::create_type_error(vm, "Object.is called with insufficient arguments")); @@ -173,6 +179,8 @@ pub fn object_is(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(oxide_runtime_api::same_value(lhs, rhs))) } +/// `Object.defineProperty(obj, key, descriptor)`:按 descriptor 定义/修改属性, +/// 支持数据与访问器描述符,兼容已有属性的默认回填。 pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 4 { return NativeResult::Err(crate::error::create_type_error( @@ -318,6 +326,8 @@ pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResul NativeResult::Ok(obj_val) } +/// `Object.getOwnPropertyDescriptor(obj, key)`:返回自身属性的描述符对象; +/// 不存在返回 undefined。 pub fn object_get_own_property_descriptor(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return NativeResult::Err(crate::error::create_type_error( @@ -430,6 +440,7 @@ macro_rules! native_try { }; } +/// `Object.freeze(obj)`:冻结对象(不可扩展 + 全部属性不可配置/不可写),返回原对象。 pub fn object_freeze(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "Object.freeze called on non-object")); @@ -446,6 +457,7 @@ pub fn object_freeze(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(val) } +/// `Object.seal(obj)`:密封对象(不可扩展 + 全部属性不可配置),返回原对象。 pub fn object_seal(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "Object.seal called on non-object")); @@ -462,6 +474,7 @@ pub fn object_seal(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(val) } +/// `Object.preventExtensions(obj)`:禁止添加新属性,返回原对象。 pub fn object_prevent_extensions(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "Object.preventExtensions called on non-object")); @@ -477,6 +490,7 @@ pub fn object_prevent_extensions(vm: &mut H, args: &[u8]) -> NativeRe NativeResult::Ok(val) } +/// `Object.isFrozen(obj)`:对象是否冻结(检查 extensible 及全部属性 writable/configurable)。 pub fn object_is_frozen(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "Object.isFrozen called on non-object")); @@ -512,6 +526,7 @@ pub fn object_is_frozen(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(true)) } +/// `Object.isSealed(obj)`:对象是否密封(检查 extensible 及全部属性 configurable)。 pub fn object_is_sealed(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "Object.isSealed called on non-object")); @@ -544,6 +559,7 @@ pub fn object_is_sealed(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(true)) } +/// `Object.isExtensible(obj)`:对象是否可扩展。 pub fn object_is_extensible(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "Object.isExtensible called on non-object")); @@ -560,6 +576,7 @@ pub fn object_is_extensible(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::bool(obj.is_extensible())) } +/// `Object.getOwnPropertyNames(obj)`:返回全部自身属性名(含不可枚举)。 pub fn object_get_own_property_names(vm: &mut H, args: &[u8]) -> NativeResult { let obj_ptr = native_try!(require_obj_arg(vm, args, "getOwnPropertyNames")); @@ -591,6 +608,7 @@ pub fn object_get_own_property_names(vm: &mut H, args: &[u8]) -> Nati NativeResult::Ok(JsValue::from_js_object(arr)) } +/// `Object.defineProperties(obj, descriptors)`:批量定义数据属性,返回目标对象。 pub fn object_define_properties(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return NativeResult::Err(crate::error::create_type_error( @@ -645,6 +663,7 @@ pub fn object_define_properties(vm: &mut H, args: &[u8]) -> NativeRes NativeResult::Ok(target_val) } +/// `Object.fromEntries(entries)`:由 `[key, value]` 对数组构建对象。 pub fn object_from_entries(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "Object.fromEntries: expected 1 argument")); @@ -683,11 +702,13 @@ pub fn object_from_entries(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(target_val) } +/// `Object.getPrototypeOf(obj)`:返回对象的 prototype。 pub fn object_get_prototype_of(vm: &mut H, args: &[u8]) -> NativeResult { let obj_ptr = native_try!(require_obj_arg(vm, args, "getPrototypeOf")); NativeResult::Ok(unsafe { (*obj_ptr).proto() }) } +/// `Object.hasOwn(obj, key)`:对象是否有指定自身属性。 pub fn object_has_own(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return NativeResult::Ok(JsValue::bool(false)); @@ -698,18 +719,21 @@ pub fn object_has_own(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(vm.get_own_property_slot(obj, key_si).is_some())) } +/// `Object.prototype.valueOf`:返回 this 本身(配合 OrdinaryToPrimitive 的兜底)。 pub fn object_proto_value_of(vm: &mut H, args: &[u8]) -> NativeResult { // Object.prototype.valueOf returns the `this` object unchanged; OrdinaryToPrimitive // then falls through to toString since the result is not primitive. NativeResult::Ok(vm.reg(args[0])) } +/// `Object.prototype.toString`:返回 `[object Object]`(暂不区分具体类型标签)。 pub fn object_proto_to_string(vm: &mut H, _args: &[u8]) -> NativeResult { // ponytail: minimal [[Class]] string — always "[object Object]". Type-specific // tags ("[object Array]" etc.) and Symbol.toStringTag are a later refinement. NativeResult::Ok(vm.new_string("[object Object]")) } +/// `Object.prototype.hasOwnProperty(key)`:this 是否有指定自身属性。 pub fn object_proto_has_own_property(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); @@ -726,6 +750,7 @@ pub fn object_proto_has_own_property(vm: &mut H, args: &[u8]) -> Nati NativeResult::Ok(JsValue::bool(vm.get_own_property_slot(obj, key_si).is_some())) } +/// `Object.prototype.propertyIsEnumerable(key)`:指定自身属性是否可枚举。 pub fn object_proto_property_is_enumerable(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); @@ -749,6 +774,7 @@ pub fn object_proto_property_is_enumerable(vm: &mut H, args: &[u8]) - NativeResult::Ok(JsValue::bool(enumerable)) } +/// `Object.entries(obj)`:返回可枚举自身属性的 `[key, value]` 对数组。 pub fn object_entries(vm: &mut H, args: &[u8]) -> NativeResult { let obj_ptr = native_try!(require_obj_arg(vm, args, "entries")); let obj = unsafe { &*obj_ptr }; @@ -792,6 +818,7 @@ pub fn object_entries(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(arr)) } +/// `Object.values(obj)`:返回可枚举自身属性的值数组。 pub fn object_values(vm: &mut H, args: &[u8]) -> NativeResult { let obj_ptr = native_try!(require_obj_arg(vm, args, "values")); let obj = unsafe { &*obj_ptr }; diff --git a/crates/oxide_builtins/src/reflect.rs b/crates/oxide_builtins/src/reflect.rs index f887fdb..cc59aca 100644 --- a/crates/oxide_builtins/src/reflect.rs +++ b/crates/oxide_builtins/src/reflect.rs @@ -7,6 +7,7 @@ use crate::object::walk_own_keys; use oxide_runtime_api::{NativeResult, VmHost}; +/// `Reflect.apply(target, thisArgument, argumentsList)`:以指定 this 与参数数组调用函数。 pub fn reflect_apply(vm: &mut H, args: &[u8]) -> NativeResult { let target = arg(vm, args, 1); let this_arg = arg(vm, args, 2); @@ -23,6 +24,8 @@ pub fn reflect_apply(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `Reflect.construct(target, argumentsList, newTarget)`:以 newTarget 的 prototype +/// 分配 this 后调用 target;返回值非对象时回退到新建的 this。 pub fn reflect_construct(vm: &mut H, args: &[u8]) -> NativeResult { let target = arg(vm, args, 1); let arg_list = arg(vm, args, 2); @@ -59,6 +62,8 @@ pub fn reflect_construct(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `Reflect.defineProperty(target, key, descriptor)`:按 descriptor 定义属性, +/// 数据/访问器属性混用或非法 getter/setter 返回 false。 pub fn reflect_define_property(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { @@ -111,6 +116,8 @@ pub fn reflect_define_property(vm: &mut H, args: &[u8]) -> NativeResu NativeResult::Ok(JsValue::bool(result.is_ok())) } +/// `Reflect.deleteProperty(target, key)`:删除自身属性; +/// 属性不可配置时返回 false,否则重构属性表并返回 true。 pub fn reflect_delete_property(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { @@ -121,6 +128,7 @@ pub fn reflect_delete_property(vm: &mut H, args: &[u8]) -> NativeResu NativeResult::Ok(JsValue::bool(delete_own_property(vm, target, key_si))) } +/// `Reflect.get(target, key, receiver)`:读取属性(含原型链与 accessor)。 pub fn reflect_get(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { @@ -134,6 +142,7 @@ pub fn reflect_get(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `Reflect.getOwnPropertyDescriptor(target, key)`:复用 Object 同名实现。 pub fn reflect_get_own_property_descriptor(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(_) = object_ptr(target_val) else { @@ -142,6 +151,7 @@ pub fn reflect_get_own_property_descriptor(vm: &mut H, args: &[u8]) - crate::object::object_get_own_property_descriptor(vm, args) } +/// `Reflect.getPrototypeOf(target)`:返回对象的 prototype。 pub fn reflect_get_prototype_of(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { @@ -150,6 +160,7 @@ pub fn reflect_get_prototype_of(vm: &mut H, args: &[u8]) -> NativeRes NativeResult::Ok(unsafe { &*target_ptr }.proto()) } +/// `Reflect.has(target, key)`:属性是否存在(含原型链)。 pub fn reflect_has(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { @@ -159,6 +170,7 @@ pub fn reflect_has(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(vm.resolve_property(unsafe { &*target_ptr }, key_si).is_some())) } +/// `Reflect.isExtensible(target)`:对象是否可扩展。 pub fn reflect_is_extensible(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { @@ -167,6 +179,7 @@ pub fn reflect_is_extensible(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::bool(unsafe { &*target_ptr }.is_extensible())) } +/// `Reflect.ownKeys(target)`:返回对象全部自身属性名(字符串数组)。 pub fn reflect_own_keys(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { @@ -180,6 +193,7 @@ pub fn reflect_own_keys(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(make_string_array(vm, &key_names)) } +/// `Reflect.preventExtensions(target)`:阻止扩展,返回 true。 pub fn reflect_prevent_extensions(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { @@ -189,6 +203,8 @@ pub fn reflect_prevent_extensions(vm: &mut H, args: &[u8]) -> NativeR NativeResult::Ok(JsValue::bool(true)) } +/// `Reflect.set(target, key, value, receiver)`:写入属性; +/// 不可扩展且属性不存在时返回 false。 pub fn reflect_set(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { @@ -204,6 +220,7 @@ pub fn reflect_set(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(vm.ordinary_set(target, key_si, value, receiver).is_ok())) } +/// `Reflect.setPrototypeOf(target, proto)`:设置 prototype(对象或 null)。 pub fn reflect_set_prototype_of(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { diff --git a/crates/oxide_builtins/src/regexp.rs b/crates/oxide_builtins/src/regexp.rs index 6010927..7109332 100644 --- a/crates/oxide_builtins/src/regexp.rs +++ b/crates/oxide_builtins/src/regexp.rs @@ -59,6 +59,8 @@ fn set_prop_at(obj: *mut JsObject, idx: usize, val: JsValue) { } } +/// `RegExp(pattern, flags)` 构造逻辑:用 regex crate 编译模式, +/// 支持 g/i/m 标志;非法模式抛 SyntaxError。编译结果存于对象的 native_fn 槽。 pub fn regexp_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let (pattern, flags) = if args.len() < 2 { (String::new(), String::new()) @@ -115,6 +117,7 @@ pub fn regexp_constructor(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(obj_ptr)) } +/// 释放 RegExp 对象 native_fn 槽中编译的 `regex::Regex`,返回释放字节数。 pub fn drop_regexp_native(obj: &mut JsObject) -> u64 { if !obj.is_regexp_obj() { return 0; @@ -131,6 +134,7 @@ pub fn drop_regexp_native(obj: &mut JsObject) -> u64 { std::mem::size_of::() as u64 } +/// `RegExp.prototype.test(string)`:判断是否匹配。global 模式下从 lastIndex 开始匹配。 pub fn regexp_test(vm: &mut H, args: &[u8]) -> NativeResult { let re_ptr = match get_regexp_ptr(vm, args) { Ok(ptr) => ptr, @@ -159,6 +163,8 @@ pub fn regexp_test(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `RegExp.prototype.exec(string)`:执行匹配并返回数组(含捕获组、index、input)。 +/// 无匹配返回 null;global 模式推进/重置 lastIndex。 pub fn regexp_exec(vm: &mut H, args: &[u8]) -> NativeResult { let re_ptr = match get_regexp_ptr(vm, args) { Ok(ptr) => ptr, @@ -233,6 +239,7 @@ pub fn regexp_exec(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `RegExp.prototype.toString`:按 `/source/flags` 形式返回。 pub fn regexp_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let re_ptr = match get_regexp_ptr(vm, args) { Ok(ptr) => ptr, diff --git a/crates/oxide_builtins/src/set.rs b/crates/oxide_builtins/src/set.rs index 4439210..8f68faf 100644 --- a/crates/oxide_builtins/src/set.rs +++ b/crates/oxide_builtins/src/set.rs @@ -15,6 +15,8 @@ macro_rules! native_try { }; } +/// Set/Map 的键包装:用 SameValue 语义比较(NaN 视为相同、±0 视为相同), +/// 非 double 值按原始位比较。 #[derive(Clone, Copy)] pub struct SetKey(pub JsValue); @@ -104,6 +106,7 @@ fn alloc_set(vm: &mut H) -> *mut JsObject { vm.alloc_object(obj) } +/// 收集 Set 中作为对象引用的元素(GC 根边),供跨 epoch 遍历/重写时追踪。 pub fn set_native_edges(obj: &JsObject) -> Vec { if !obj.is_set() { return Vec::new(); @@ -121,6 +124,7 @@ pub fn set_native_edges(obj: &JsObject) -> Vec { } } +/// 克隆 Set 的 native 数据到新对象,用 `rewrite` 改写其中的对象引用。 pub fn clone_set_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, @@ -143,6 +147,7 @@ where dst.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8); } +/// 原地重写 Set 的 native 数据,用 `rewrite` 改写其中的对象引用。 pub fn rewrite_set_native(obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, @@ -164,6 +169,7 @@ where } } +/// 释放 Set 的 native 数据(IndexSet),返回释放的字节数供泄漏统计。 pub fn drop_set_native(obj: &mut JsObject) -> u64 { if !obj.is_set() { return 0; @@ -181,11 +187,13 @@ pub fn drop_set_native(obj: &mut JsObject) -> u64 { } } +/// `Set` 构造函数:创建带空 IndexSet native 数据的 Set 对象。 pub fn set_constructor(vm: &mut H, _args: &[u8]) -> NativeResult { let set_obj = alloc_set(vm); NativeResult::Ok(JsValue::from_js_object(set_obj)) } +/// `Set.prototype.add(value)`:插入元素,返回 this。 pub fn set_add(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_set_inner(vm, this_val)); @@ -196,6 +204,7 @@ pub fn set_add(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(this_val) } +/// `Set.prototype.has(value)`:元素是否存在。 pub fn set_has(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_set_inner(vm, this_val)); @@ -204,6 +213,7 @@ pub fn set_has(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(found)) } +/// `Set.prototype.delete(value)`:删除元素并返回是否删除成功。 pub fn set_delete(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_set_inner(vm, this_val)); @@ -212,6 +222,7 @@ pub fn set_delete(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(removed)) } +/// `Set.prototype.clear()`:清空全部元素,返回 undefined。 pub fn set_clear(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_set_inner(vm, this_val)); @@ -221,12 +232,14 @@ pub fn set_clear(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } +/// `Set.prototype.size` getter:返回元素数量。 pub fn set_size(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_set_inner(vm, this_val)); NativeResult::Ok(JsValue::float(unsafe { (*inner).len() } as f64)) } +/// `Set.prototype.entries()`:返回按插入序迭代 `[value, value]` 对的迭代器。 pub fn set_entries(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_set_inner(vm, this_val)); @@ -237,6 +250,7 @@ pub fn set_entries(vm: &mut H, args: &[u8]) -> NativeResult { )) } +/// `Set.prototype.values()`:返回按插入序迭代值的迭代器。 pub fn set_values(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_set_inner(vm, this_val)); @@ -247,6 +261,7 @@ pub fn set_values(vm: &mut H, args: &[u8]) -> NativeResult { )) } +/// `Set.prototype.keys()`:别名 `values()`(Set 无独立键),返回同样的迭代器。 pub fn set_keys(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_set_inner(vm, this_val)); diff --git a/crates/oxide_builtins/src/string.rs b/crates/oxide_builtins/src/string.rs index cbc58c9..b86e64c 100644 --- a/crates/oxide_builtins/src/string.rs +++ b/crates/oxide_builtins/src/string.rs @@ -17,6 +17,7 @@ fn this_string(vm: &H, args: &[u8]) -> String { } } +/// `String.fromCharCode(...codes)`:把各参数按低 16 位转成字符拼接为字符串。 pub fn string_from_char_code(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.fromCharCode called with {} args", args.len()); let mut out = String::new(); @@ -31,6 +32,7 @@ pub fn string_from_char_code(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(vm.new_string(&out)) } +/// `String.prototype.valueOf`:返回包装对象的原始字符串;其它 this 抛 TypeError。 pub fn string_value_of(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.valueOf called with {} args", args.len()); let this_val = vm.reg(args[0]); @@ -53,6 +55,7 @@ pub fn string_value_of(vm: &mut H, args: &[u8]) -> NativeResult { )) } +/// JS `String()` 构造逻辑:把参数转成字符串;new 语义返回 `[[StringData]]` 包装对象。 pub fn string_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let s = if args.len() > 1 { oxide_runtime_api::to_string(vm.reg(args[1])) @@ -85,6 +88,7 @@ pub fn string_constructor(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(this_val) } +/// `String.prototype.toString`:返回包装对象的原始字符串;其它 this 抛 TypeError。 pub fn string_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(args[0]); if this_val.is_string() { @@ -167,6 +171,7 @@ fn is_regexp_obj(val: JsValue, vm: &H) -> bool { std::ptr::eq(proto_ptr, rp) } +/// `String.prototype.indexOf(searchString, position)`:按字符索引查找首次出现位置。 pub fn string_index_of(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.indexOf called with {} args", args.len()); let s = this_string(vm, args); @@ -199,6 +204,7 @@ pub fn string_index_of(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(-1)) } +/// `String.prototype.includes(searchString, position)`:是否包含子串。 pub fn string_includes(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.includes called with {} args", args.len()); let s = this_string(vm, args); @@ -225,6 +231,7 @@ pub fn string_includes(vm: &mut H, args: &[u8]) -> NativeResult { } } +/// `String.prototype.charAt(index)`:返回指定位置的单字符;越界返回空串。 pub fn string_char_at(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.charAt called with {} args", args.len()); let s = this_string(vm, args); @@ -242,6 +249,7 @@ pub fn string_char_at(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&ch)) } +/// `String.prototype.charCodeAt(index)`:返回指定位置字符的 UTF-16 code unit;越界返回 NaN。 pub fn string_char_code_at(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.charCodeAt called with {} args", args.len()); let s = this_string(vm, args); @@ -258,6 +266,7 @@ pub fn string_char_code_at(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(s.chars().nth(idx as usize).unwrap() as i32)) } +/// `String.prototype.concat(...strings)`:拼接 this 与各参数返回新字符串。 pub fn string_concat(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.concat called with {} args", args.len()); let mut result = this_string(vm, args); @@ -267,6 +276,7 @@ pub fn string_concat(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&result)) } +/// `String.prototype.slice(start, end)`:按字符区间(支持负索引)取子串。 pub fn string_slice(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.slice called with {} args", args.len()); let s = this_string(vm, args); @@ -297,6 +307,7 @@ pub fn string_slice(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(result)) } +/// `String.prototype.substring(start, end)`:取子串,start/end 自动对调且取非负。 pub fn string_substring(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.substring called with {} args", args.len()); let s = this_string(vm, args); @@ -328,6 +339,7 @@ pub fn string_substring(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(result)) } +/// `String.prototype.substr(start, length)`:从 start 取 length 个字符(Annex B,支持负 start)。 pub fn string_substr(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.substr called with {} args", args.len()); let s = this_string(vm, args); @@ -351,6 +363,7 @@ pub fn string_substr(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&result)) } +/// `String.prototype.at(index)`:按字符索引取字符(支持负索引);越界返回 undefined。 pub fn string_at(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.at called with {} args", args.len()); let s = this_string(vm, args); @@ -368,6 +381,7 @@ pub fn string_at(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&ch)) } +/// `String.prototype.lastIndexOf(searchString, position)`:从后往前查找首次出现位置。 pub fn string_last_index_of(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.lastIndexOf called with {} args", args.len()); let s = this_string(vm, args); @@ -406,24 +420,28 @@ pub fn string_last_index_of(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::int(-1)) } +/// `String.prototype.toUpperCase`:全大写转换。 pub fn string_to_upper_case(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.toUpperCase called with {} args", args.len()); let s = this_string(vm, args); NativeResult::Ok(vm.new_string(&s.to_uppercase())) } +/// `String.prototype.toLowerCase`:全小写转换。 pub fn string_to_lower_case(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.toLowerCase called with {} args", args.len()); let s = this_string(vm, args); NativeResult::Ok(vm.new_string(&s.to_lowercase())) } +/// `String.prototype.trim`:去除两端空白。 pub fn string_trim(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.trim called with {} args", args.len()); let s = this_string(vm, args); NativeResult::Ok(vm.new_string(s.trim())) } +/// `String.prototype.repeat(count)`:重复字符串 count 次(当前上限 10000 防滥用)。 pub fn string_repeat(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.repeat called with {} args", args.len()); let s = this_string(vm, args); @@ -435,6 +453,7 @@ pub fn string_repeat(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&s.repeat(n))) } +/// `String.prototype.padStart(targetLength, padString)`:在头部补足 padString 到目标长度。 pub fn string_pad_start(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.padStart called with {} args", args.len()); let s = this_string(vm, args); @@ -460,6 +479,7 @@ pub fn string_pad_start(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&out)) } +/// `String.prototype.padEnd(targetLength, padString)`:在尾部补足 padString 到目标长度。 pub fn string_pad_end(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.padEnd called with {} args", args.len()); let s = this_string(vm, args); @@ -485,6 +505,7 @@ pub fn string_pad_end(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&out)) } +/// `String.prototype.startsWith(searchString, position)`:是否以指定子串开头。 pub fn string_starts_with(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.startsWith called with {} args", args.len()); let s = this_string(vm, args); @@ -501,6 +522,7 @@ pub fn string_starts_with(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(s[byte_index_at_char(&s, pos)..].starts_with(&search))) } +/// `String.prototype.endsWith(searchString, endPosition)`:是否以指定子串结尾。 pub fn string_ends_with(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.endsWith called with {} args", args.len()); let s = this_string(vm, args); @@ -517,6 +539,8 @@ pub fn string_ends_with(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(s[..byte_index_at_char(&s, end_pos)].ends_with(&search))) } +/// `String.prototype.split(separator, limit)`:按分隔符拆分为字符串数组; +/// 分隔符可为 RegExp(含捕获组)或字符串。 pub fn string_split(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.split called with {} args", args.len()); let s = this_string(vm, args); @@ -579,6 +603,8 @@ pub fn string_split(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(make_string_array(vm, &parts)) } +/// `String.prototype.replace(pattern, replacement)`:替换首个匹配; +/// 支持 RegExp(global 全替换)、字符串以及函数替换器。 pub fn string_replace(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.replace called with {} args", args.len()); let s = this_string(vm, args); @@ -644,6 +670,8 @@ pub fn string_replace(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&result)) } +/// `String.prototype.match(pattern)`:按 RegExp 匹配;global 返回全部匹配数组, +/// 否则返回首个匹配及捕获组,无匹配返回 null。 pub fn string_match_fn(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.match called with {} args", args.len()); let s = this_string(vm, args); @@ -683,6 +711,7 @@ pub fn string_match_fn(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::null()) } +/// `String.prototype.search(pattern)`:返回首个匹配位置,无匹配返回 -1。 pub fn string_search(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.search called with {} args", args.len()); let s = this_string(vm, args); @@ -713,18 +742,22 @@ pub fn string_search(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(-1)) } +/// `String.prototype.trimStart`:去除头部空白。 pub fn string_trim_start(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.trimStart called with {} args", args.len()); let s = this_string(vm, args); NativeResult::Ok(vm.new_string(s.trim_start())) } +/// `String.prototype.trimEnd`:去除尾部空白。 pub fn string_trim_end(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.trimEnd called with {} args", args.len()); let s = this_string(vm, args); NativeResult::Ok(vm.new_string(s.trim_end())) } +/// `String.prototype.codePointAt(pos)`:返回 code point(surrogate pair 会合并), +/// 越界返回 undefined。 pub fn string_code_point_at(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.codePointAt called with {} args", args.len()); let s = this_string(vm, args); @@ -748,6 +781,7 @@ pub fn string_code_point_at(vm: &mut H, args: &[u8]) -> NativeResult NativeResult::Ok(JsValue::int(c as i32)) } +/// `String.prototype.normalize(form)`:按 NFC/NFD/NFKC/NFKD 规范化为 Unicode 规范形式。 pub fn string_normalize(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.normalize called with {} args", args.len()); use unicode_normalization::UnicodeNormalization; @@ -766,6 +800,8 @@ const MALL_INPUT: &str = "__mal_input__"; const MALL_INDEX: &str = "__mal_index__"; const MALL_RE: &str = "__mal_re__"; +/// `String.prototype.matchAll(pattern)`:返回带 `next` 的迭代器,逐步产出全部匹配 +/// (要求 RegExp 带 global 标志;普通字符串会被转义成等效正则)。 pub fn string_match_all(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.matchAll called with {} args", args.len()); let s = this_string(vm, args); @@ -846,6 +882,7 @@ fn make_public_native_fn(vm: &mut H, name: &str, native_fn: *const () JsValue::from_js_object(func) } +/// `matchAll` 迭代器的 `next`:返回 `{value: 匹配数组, done}`,耗尽后 done 为 true。 pub fn string_match_all_next(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if !this_val.is_object() { @@ -908,6 +945,8 @@ fn make_match_done_result(vm: &mut H, value: JsValue) -> NativeResult NativeResult::Ok(JsValue::from_js_object(obj)) } +/// `String.prototype.replaceAll(pattern, replacement)`:替换全部匹配 +/// (RegExp 或字符串模式)。 pub fn string_replace_all(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.replaceAll called with {} args", args.len()); let s = this_string(vm, args); diff --git a/crates/oxide_builtins/src/stubs.rs b/crates/oxide_builtins/src/stubs.rs index ccc267d..6bd1659 100644 --- a/crates/oxide_builtins/src/stubs.rs +++ b/crates/oxide_builtins/src/stubs.rs @@ -5,27 +5,35 @@ fn stub_error(vm: &mut H, name: &str) -> NativeResult { } // Architecturally deferred — these features are excluded from the supported language subset by design. +/// Stub for the `Proxy` global. Always throws a TypeError; Proxy is deferred by design. pub fn proxy_stub(vm: &mut H, _args: &[u8]) -> NativeResult { stub_error(vm, "Proxy") } +/// Stub for the `BigInt` global. Always throws a TypeError; BigInt is deferred by design. pub fn bigint_stub(vm: &mut H, _args: &[u8]) -> NativeResult { stub_error(vm, "BigInt") } +/// Stub for the `WeakMap` global. Always throws a TypeError; WeakMap is deferred by design. pub fn weakmap_stub(vm: &mut H, _args: &[u8]) -> NativeResult { stub_error(vm, "WeakMap") } +/// Stub for the `WeakSet` global. Always throws a TypeError; WeakSet is deferred by design. pub fn weakset_stub(vm: &mut H, _args: &[u8]) -> NativeResult { stub_error(vm, "WeakSet") } +/// Stub for the `WeakRef` global. Always throws a TypeError; WeakRef is deferred by design. pub fn weakref_stub(vm: &mut H, _args: &[u8]) -> NativeResult { stub_error(vm, "WeakRef") } +/// Stub for the `FinalizationRegistry` global. Always throws a TypeError; deferred by design. pub fn finalization_registry_stub(vm: &mut H, _args: &[u8]) -> NativeResult { stub_error(vm, "FinalizationRegistry") } +/// Stub for the `SharedArrayBuffer` global. Always throws a TypeError; deferred by design. pub fn shared_array_buffer_stub(vm: &mut H, _args: &[u8]) -> NativeResult { stub_error(vm, "SharedArrayBuffer") } +/// Stub for the `Atomics` global. Always throws a TypeError; deferred by design. pub fn atomics_stub(vm: &mut H, _args: &[u8]) -> NativeResult { stub_error(vm, "Atomics") } diff --git a/crates/oxide_builtins/src/symbol.rs b/crates/oxide_builtins/src/symbol.rs index 7501423..d346cc0 100644 --- a/crates/oxide_builtins/src/symbol.rs +++ b/crates/oxide_builtins/src/symbol.rs @@ -3,6 +3,8 @@ use oxide_types::value::JsValue; use oxide_runtime_api::{NativeResult, VmHost}; +/// JS `Symbol()` 构造逻辑:以可选 description 创建一个新的唯一 Symbol。 +/// 当以 new 语义调用(this 原型链指向 Symbol.prototype)时抛 TypeError,与规范一致。 pub fn symbol_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if this_val.is_object() { @@ -32,6 +34,8 @@ pub fn symbol_constructor(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::symbol(idx)) } +/// `Symbol.prototype.toString`:返回 `Symbol(description)` 形式字符串。 +/// this 必须是 Symbol,否则抛 TypeError。 pub fn symbol_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(args[0]); if !this_val.is_symbol() { @@ -44,6 +48,8 @@ pub fn symbol_to_string(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&result)) } +/// `Symbol.for(key)`:在全局 symbol 注册表中查找并返回同 key 的 Symbol; +/// 不存在则新建并登记。同 key 的 Symbol 在全局唯一。 pub fn symbol_for(vm: &mut H, args: &[u8]) -> NativeResult { let key = if args.len() > 1 { oxide_runtime_api::to_string(vm.reg(args[1])) @@ -60,6 +66,8 @@ pub fn symbol_for(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::symbol(idx)) } +/// `Symbol.keyFor(sym)`:返回全局注册表中该 Symbol 的 key;未登记返回 undefined。 +/// 参数非 Symbol 抛 TypeError。 pub fn symbol_key_for(vm: &mut H, args: &[u8]) -> NativeResult { let sym = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; if !sym.is_symbol() { diff --git a/crates/oxide_builtins/src/typed_array.rs b/crates/oxide_builtins/src/typed_array.rs index 49d03a1..3cb794b 100644 --- a/crates/oxide_builtins/src/typed_array.rs +++ b/crates/oxide_builtins/src/typed_array.rs @@ -126,6 +126,7 @@ fn typed_array_data_ptr(obj: &JsObject) -> Option<*mut TypedArrayData> { obj.native_fn().map(|ptr| ptr.as_ptr() as *mut TypedArrayData) } +/// 收集 TypedArray 引用的底层 buffer(GC 根边)。 pub fn typed_array_native_edges(obj: &JsObject) -> Vec { let Some(ptr) = typed_array_data_ptr(obj) else { return Vec::new(); @@ -141,6 +142,7 @@ pub fn typed_array_native_edges(obj: &JsObject) -> Vec { } } +/// 克隆 TypedArray 的视图数据到新对象,用 `rewrite` 改写 buffer 引用。 pub fn clone_typed_array_native_with_rewrite(old_obj: &JsObject, new_obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, @@ -157,6 +159,7 @@ where new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned as *const ()) })); } +/// 原地重写 TypedArray 的 buffer 引用。 pub fn rewrite_typed_array_native(obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, @@ -172,6 +175,7 @@ where } } +/// 释放 TypedArray 的视图数据(`Box`),返回释放字节数。 pub fn drop_typed_array_native(obj: &mut JsObject) -> u64 { let Some(ptr) = typed_array_data_ptr(obj) else { return 0; @@ -341,6 +345,8 @@ fn typed_array_new(vm: &mut H, args: &[u8], kind: TypedArrayKind) -> macro_rules! typed_array_ctor { ($name:ident, $kind:ident) => { + /// 对应类型(如 `Int8Array`)的构造函数:支持数字长度、ArrayBuffer+offset+length + /// 或 array-like 数据三种调用形式。 pub fn $name(vm: &mut H, args: &[u8]) -> NativeResult { typed_array_new(vm, args, TypedArrayKind::$kind) } @@ -359,6 +365,7 @@ typed_array_ctor!(float64array_constructor, Float64); typed_array_ctor!(bigint64array_constructor, BigInt64); typed_array_ctor!(biguint64array_constructor, BigUint64); +/// `TypedArray.prototype.at(index)`:按索引取元素,支持负索引;越界返回 undefined。 pub fn typed_array_at(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); @@ -376,6 +383,7 @@ pub fn typed_array_at(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(read_element(view.kind, buffer, absolute_byte_offset(view, idx as usize))) } +/// `TypedArray.prototype.fill(value, start, end)`:用给定值填充区间,返回 this。 pub fn typed_array_fill(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); @@ -399,6 +407,7 @@ pub fn typed_array_fill(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(this_val) } +/// `TypedArray.prototype.slice(start, end)`:复制区间元素生成新同类型 TypedArray。 pub fn typed_array_slice(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); @@ -424,6 +433,7 @@ pub fn typed_array_slice(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(create_typed_array(vm, view.kind, buffer, 0, count))) } +/// `TypedArray.prototype.subarray(start, end)`:共享底层 buffer 创建区间子视图。 pub fn typed_array_subarray(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); @@ -448,6 +458,8 @@ pub fn typed_array_subarray(vm: &mut H, args: &[u8]) -> NativeResult ))) } +/// `TypedArray.prototype.set(source, offset)`:从 array-like/另一个 TypedArray 拷贝元素; +/// 越界抛 RangeError。 pub fn typed_array_set(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); @@ -473,6 +485,7 @@ pub fn typed_array_set(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } +/// `TypedArray.prototype.toString`:校验 receiver 后返回 `[object TypedArray]`。 pub fn typed_array_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); native_try!(get_typed_array_data(vm, this_val)); diff --git a/crates/oxide_bytecode/src/lib.rs b/crates/oxide_bytecode/src/lib.rs index 6214124..9780515 100644 --- a/crates/oxide_bytecode/src/lib.rs +++ b/crates/oxide_bytecode/src/lib.rs @@ -1,7 +1,11 @@ #![doc = "OxideJS - Shared bytecode protocol and compiled module ABI"] +/// 编译产物与常量池 ABI([`CompiledModule`] / [`Constant`])。 pub mod module; +/// 操作码表与指令编解码([`OpCode`] / [`Instr`])。 pub mod opcode; +/// 重导出 [`module`] 的编译产物与常量池类型。 pub use module::{CompiledModule, Constant}; +/// 重导出 [`opcode`] 的操作码与指令类型。 pub use opcode::{Instr, OpCode}; diff --git a/crates/oxide_bytecode/src/module.rs b/crates/oxide_bytecode/src/module.rs index 2eaaf07..3f07eca 100644 --- a/crates/oxide_bytecode/src/module.rs +++ b/crates/oxide_bytecode/src/module.rs @@ -1,7 +1,16 @@ +//! 编译产物(compiled module)与常量池 ABI。 +//! +//! [`CompiledModule`] 是编译器输出的字节码函数单元:指令序列、常量池、 +//! 寄存器布局元信息、子函数(嵌套函数)与 upvalue 捕获描述;随 VM 解释执行 +//! 或由其它模块克隆复制。`Display` 输出可读的反汇编文本,供调试用。 + use std::fmt; use crate::opcode::{self, OpCode}; +/// 常量池条目:编译期折叠的不可变值。 +/// +/// 变体覆盖 ECMAScript 顶层字面量类型;`Display` 未实现,调试输出走 `Debug`。 #[derive(Debug, Clone, PartialEq)] pub enum Constant { Number(f64), @@ -12,6 +21,10 @@ pub enum Constant { Undefined, } +/// 闭包对上层作用域一个变量的捕获描述。 +/// +/// `enclosing_reg` 是外层函数中该变量的寄存器位;若外层变量本身就是 upvalue +/// (多级闭包),`cell_idx` 指向链式捕获的 cell。 #[derive(Debug, Clone)] pub struct UpvalueCapture { pub name: String, @@ -19,6 +32,16 @@ pub struct UpvalueCapture { pub cell_idx: u8, } +/// 一个函数单元(或顶层脚本)的编译产物。 +/// +/// 字段说明: +/// - `bytecode` / `constants` — 指令序列与常量池; +/// - `n_registers` / `n_args` / `param_base` — 寄存器窗口布局; +/// - `builtin_reg_map` — 内置对象到寄存器的预绑定; +/// - `sub_modules` — 嵌套函数(闭包体)的编译产物; +/// - `is_arrow` / `captured_this_const_idx` — 箭头函数词法 `this`; +/// - `is_class_constructor` / `is_derived_constructor` / `needs_home_object` — 类相关; +/// - `upvalue_captures` / `cells_needed` — 闭包捕获描述。 pub struct CompiledModule { pub bytecode: Vec, pub constants: Vec, @@ -49,6 +72,7 @@ pub struct CompiledModule { } impl CompiledModule { + /// 构造空模块:空字节码、空常量池、零寄存器与全部标志默认关闭。 pub fn new() -> Self { Self { bytecode: Vec::new(), diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index 73b58b2..4f49905 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -1,3 +1,9 @@ +//! 字节码操作码表与指令编解码。 +//! +//! [`OpCode`] 由 `define_opcodes!` 宏从单张表生成;[`Instr`] 是 4 字节指令 +//! (opcode + rd + a + b,imm16/offset16 复用 a、b 两字节)。本模块提供 +//! 编译器发射(`encode*`)与 VM 解码(`opcode` / `rd` / `a` / `b` 等)全套函数。 + use std::fmt; /// Generate the `OpCode` enum plus its `TryFrom` and `Display` impls from a @@ -196,6 +202,7 @@ define_opcodes! { } impl OpCode { + /// 是否需要 IC 扩展字(inline cache 元数据跟随指令之后)。 pub fn has_ic_ext_words(&self) -> bool { matches!( self, @@ -221,64 +228,77 @@ impl OpCode { /// - `b` — second source register, or imm16 high byte pub type Instr = u32; +/// 把操作码与三个操作数字节编码为一条 [`Instr`]。 pub fn encode(op: OpCode, rd: u8, a: u8, b: u8) -> Instr { ((b as Instr) << 24) | ((a as Instr) << 16) | ((rd as Instr) << 8) | (op as Instr) } +/// 解码指令的低 8 位操作码;未知字节返回 [`OpCode::NOP`]。 pub fn opcode(instr: Instr) -> OpCode { OpCode::try_from((instr & 0xFF) as u8).unwrap_or(OpCode::NOP) } +/// 解码目标寄存器(bits 8-15)。 pub fn rd(instr: Instr) -> u8 { ((instr >> 8) & 0xFF) as u8 } +/// 解码第一个操作数(bits 16-23)。 pub fn a(instr: Instr) -> u8 { ((instr >> 16) & 0xFF) as u8 } +/// 解码第二个操作数(bits 24-31)。 pub fn b(instr: Instr) -> u8 { ((instr >> 24) & 0xFF) as u8 } +/// 解码 16 位立即数(bits 16-31,由 a、b 两字节拼成)。 pub fn imm16(instr: Instr) -> u16 { ((instr >> 16) & 0xFFFF) as u16 } +/// 解码 16 位有符号跳转偏移(bits 16-31)。 pub fn offset16(instr: Instr) -> i16 { ((instr >> 16) & 0xFFFF) as i16 } +/// 发射无条件跳转指令(`JMP`,偏移以 16 位补码编码)。 pub fn encode_jmp(offset: i16) -> Instr { let lo = (offset as u16 & 0xFF) as u8; let hi = ((offset as u16 >> 8) & 0xFF) as u8; encode(OpCode::JMP, 0, lo, hi) } +/// 发射 `JMP_IF_FALSE`:当 `rd` 寄存器为 falsy 时跳转。 pub fn encode_jmp_if_false(rd: u8, offset: i16) -> Instr { let lo = (offset as u16 & 0xFF) as u8; let hi = ((offset as u16 >> 8) & 0xFF) as u8; encode(OpCode::JMP_IF_FALSE, rd, lo, hi) } +/// 发射 `JMP_IF_TRUE`:当 `rd` 寄存器为 truthy 时跳转。 pub fn encode_jmp_if_true(rd: u8, offset: i16) -> Instr { let lo = (offset as u16 & 0xFF) as u8; let hi = ((offset as u16 >> 8) & 0xFF) as u8; encode(OpCode::JMP_IF_TRUE, rd, lo, hi) } +/// 发射 `JMP_IF_NULLISH`:当 `rd` 为 `null` / `undefined` 时跳转(`??` 短路)。 pub fn encode_jmp_if_nullish(rd: u8, offset: i16) -> Instr { let lo = (offset as u16 & 0xFF) as u8; let hi = ((offset as u16 >> 8) & 0xFF) as u8; encode(OpCode::JMP_IF_NULLISH, rd, lo, hi) } +/// 发射 `TRY_BEGIN`:标记 try 块开始,偏移指向 catch 处理器。 pub fn encode_try_begin(offset: i16) -> Instr { let lo = (offset as u16 & 0xFF) as u8; let hi = ((offset as u16 >> 8) & 0xFF) as u8; encode(OpCode::TRY_BEGIN, 0, lo, hi) } +/// 发射 `TRY_FINALLY_BEGIN`:标记 try/finally 块开始,偏移指向 finally 处理器。 pub fn encode_try_finally_begin(offset: i16) -> Instr { let lo = (offset as u16 & 0xFF) as u8; let hi = ((offset as u16 >> 8) & 0xFF) as u8; diff --git a/crates/oxide_cli/src/bench/baseline.rs b/crates/oxide_cli/src/bench/baseline.rs index 110e469..5abad7d 100644 --- a/crates/oxide_cli/src/bench/baseline.rs +++ b/crates/oxide_cli/src/bench/baseline.rs @@ -1,15 +1,18 @@ use crate::bench::metrics::MetricCollection; +/// 基准结果集合,持久化到 `benchmark_baseline.json` 用于后续回归对比。 pub struct Baseline { pub entries: Vec, } impl Baseline { + /// 构造空的基线。 pub fn empty() -> Self { Self { entries: Vec::new() } } } +/// 一次性能回退:当前指标相对基线的比值超过容差即视为回归。 pub struct Regression { pub metric: String, pub test_name: String, @@ -19,6 +22,7 @@ pub struct Regression { pub tolerance: f64, } +/// 从 `benchmark_baseline.json` 加载基线;文件不存在时返回空基线。 pub fn load_baseline() -> Result { let json_path = "benchmark_baseline.json"; let data = match std::fs::read_to_string(json_path) { @@ -33,6 +37,7 @@ pub fn load_baseline() -> Result { Ok(Baseline { entries }) } +/// 保存当前结果到 `benchmark_baseline.json` 并生成 Markdown 表格。 pub fn save_baseline(results: &[MetricCollection]) -> Result<(), String> { let json = serde_json::to_string_pretty(results).map_err(|e| format!("Failed to serialize baseline: {}", e))?; std::fs::write("benchmark_baseline.json", &json) @@ -42,6 +47,7 @@ pub fn save_baseline(results: &[MetricCollection]) -> Result<(), String> { Ok(()) } +/// 对比当前结果与基线,返回所有超过各自容差(wall_time 10%、GC 类 50%、其它 20%)的回归项。 pub fn compare_baseline(current: &[MetricCollection]) -> Vec { let baseline = match load_baseline() { Ok(b) => b, diff --git a/crates/oxide_cli/src/bench/js_stress.rs b/crates/oxide_cli/src/bench/js_stress.rs index 0616be8..22227c3 100644 --- a/crates/oxide_cli/src/bench/js_stress.rs +++ b/crates/oxide_cli/src/bench/js_stress.rs @@ -13,6 +13,8 @@ use crate::bench::metrics::MetricCollection; use crate::bench::output::{format_json, format_text_table}; use crate::bench::BenchConfig; +/// 运行 `tests/stress/` 下全部(或 filter 匹配的)JS 压力测试: +/// 预热后迭代执行并采集指标,输出 JSON + 表格;必要时保存/对比基线。 pub fn run_js_stress_bench(config: &BenchConfig, kernel: &Arc, pool: &Arc) -> ExitCode { let mut results: Vec = Vec::new(); let compiler = Compiler::new(); diff --git a/crates/oxide_cli/src/bench/leak_detect.rs b/crates/oxide_cli/src/bench/leak_detect.rs index d829ef9..bd5876c 100644 --- a/crates/oxide_cli/src/bench/leak_detect.rs +++ b/crates/oxide_cli/src/bench/leak_detect.rs @@ -12,12 +12,15 @@ use crate::bench::metrics::MetricCollection; use crate::bench::output::format_json; use crate::bench::BenchConfig; +/// 滑动窗口采样器:对 `(iteration, value)` 序列做线性回归, +/// 当斜率明显为正且拟合度 R² 高时判定为内存泄漏。 pub struct LeakSampler { window: VecDeque<(usize, f64)>, window_size: usize, } impl LeakSampler { + /// 以给定窗口大小创建采样器。 pub fn new(window_size: usize) -> Self { Self { window: VecDeque::new(), @@ -25,6 +28,7 @@ impl LeakSampler { } } + /// 加入一次采样;样本足够且回归显著时返回泄漏判定。 pub fn add_sample(&mut self, iteration: usize, value: f64) -> Option { self.window.push_back((iteration, value)); if self.window.len() >= self.window_size { @@ -56,11 +60,14 @@ impl LeakSampler { } } +/// 泄漏判定的回归结果:线性斜率和拟合优度 R²。 pub struct LeakVerdict { pub slope: f64, pub r2: f64, } +/// 反复运行固定 JS 脚本,跟踪 session/code-forge/symbol 等内存指标, +/// 用 `LeakSampler` 回归检测随迭代次数增长的内存占用。 pub fn run_leak_detect(config: &BenchConfig, kernel: &Arc, pool: &Arc) -> ExitCode { let js = "var ITERATIONS = 1000; var obj = {}; for (var i = 0; i < ITERATIONS; i++) { obj['key' + i] = i; } obj['key0']"; diff --git a/crates/oxide_cli/src/bench/metrics.rs b/crates/oxide_cli/src/bench/metrics.rs index 65e6be4..f9d811b 100644 --- a/crates/oxide_cli/src/bench/metrics.rs +++ b/crates/oxide_cli/src/bench/metrics.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; +/// 单次 benchmark 测试收集的完整指标集(可序列化为 JSON 用于基线持久化)。 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MetricCollection { pub test_name: String, @@ -21,6 +22,7 @@ pub struct MetricCollection { } impl MetricCollection { + /// 导出为 `(指标名, 值)` 列表,供回归对比与表格输出遍历。 pub fn iter_metrics(&self) -> Vec<(&'static str, f64)> { vec![ ("wall_time_us", self.wall_time_us as f64), diff --git a/crates/oxide_cli/src/bench/mod.rs b/crates/oxide_cli/src/bench/mod.rs index 44b0f22..5725f5f 100644 --- a/crates/oxide_cli/src/bench/mod.rs +++ b/crates/oxide_cli/src/bench/mod.rs @@ -1,8 +1,17 @@ +//! Benchmark 子系统:支持 JS 压力测试(`js`)、Rust 原生 bench(`rust`) +//! 与内存泄漏检测(`leak`),并附带基线保存/回归对比功能。 + +/// 基准持久化与回归对比。 pub mod baseline; +/// JS 压力测试执行。 pub mod js_stress; +/// 内存泄漏检测。 pub mod leak_detect; +/// 指标采集结构。 pub mod metrics; +/// 结果格式化输出。 pub mod output; +/// Rust 原生 benchmark 委托。 pub mod rust_bench; use std::process::ExitCode; @@ -11,6 +20,7 @@ use std::sync::Arc; use oxide_kernel::kernel::KernelCore; use oxide_vm::vm_pool::VmPool; +/// 一次 bench 运行的全部配置(来自 CLI `bench` 子命令参数)。 pub struct BenchConfig { pub mode: String, pub filter: Option, @@ -21,6 +31,7 @@ pub struct BenchConfig { pub leak_check_interval: usize, } +/// 按 mode 分派到具体 benchmark 实现(js/rust/leak)。 pub fn run_benchmarks(config: BenchConfig, kernel: Arc, pool: Arc) -> ExitCode { match config.mode.as_str() { "js" => js_stress::run_js_stress_bench(&config, &kernel, &pool), diff --git a/crates/oxide_cli/src/bench/output.rs b/crates/oxide_cli/src/bench/output.rs index 8a881fa..a46fe04 100644 --- a/crates/oxide_cli/src/bench/output.rs +++ b/crates/oxide_cli/src/bench/output.rs @@ -1,9 +1,11 @@ use crate::bench::metrics::MetricCollection; +/// 把结果序列化为美观 JSON 字符串。 pub fn format_json(results: &[MetricCollection]) -> String { serde_json::to_string_pretty(results).unwrap_or_else(|e| format!("{{\"error\": \"{}\"}}", e)) } +/// 把结果格式化为对齐的文本表格(供终端打印)。 pub fn format_text_table(results: &[MetricCollection]) -> String { let mut out = String::new(); out.push_str(&format!( diff --git a/crates/oxide_cli/src/bench/rust_bench.rs b/crates/oxide_cli/src/bench/rust_bench.rs index 5de7a7a..9565976 100644 --- a/crates/oxide_cli/src/bench/rust_bench.rs +++ b/crates/oxide_cli/src/bench/rust_bench.rs @@ -1,3 +1,5 @@ +/// 委托 `cargo bench -p oxide_vm` 运行 Rust 原生 benchmark; +/// 可选 filter 指定具体 bench 目标。 pub fn run_rust_bench(filter: Option<&str>) -> std::process::ExitCode { let mut cmd = std::process::Command::new("cargo"); cmd.args(["bench", "-p", "oxide_vm"]); diff --git a/crates/oxide_code_cache/src/code_cache_log.rs b/crates/oxide_code_cache/src/code_cache_log.rs index ed7c657..4d6187d 100644 --- a/crates/oxide_code_cache/src/code_cache_log.rs +++ b/crates/oxide_code_cache/src/code_cache_log.rs @@ -1,3 +1,5 @@ +//! 缓存日志宏:`code_cache_debug!`,经由 oxide_log 上报。 + #[macro_export] macro_rules! code_cache_debug { ($($arg:tt)*) => { diff --git a/crates/oxide_code_cache/src/lib.rs b/crates/oxide_code_cache/src/lib.rs index 28ff47c..dfdd913 100644 --- a/crates/oxide_code_cache/src/lib.rs +++ b/crates/oxide_code_cache/src/lib.rs @@ -1,4 +1,8 @@ -#![doc = "OxideJS - Compiler-independent compiled module cache"] +//! oxide_code_cache:编译结果缓存(LRU,与编译器解耦)。 +//! +//! `CodeForge` 以调用方提供的模块哈希为键缓存 `CompiledModule`(`Arc` 共享)。 +//! 缓存自身不解析/编译 JS;键的计算与编译回调均由编译器侧提供。 +//! LRU 上限约束内存,供 eval 循环/REPL 等重复编译场景复用产物。 use std::num::NonZeroUsize; use std::sync::{Arc, Mutex}; @@ -21,12 +25,14 @@ pub struct CodeForge { } impl CodeForge { + /// 构造容量为 `capacity` 的缓存(容量为 0 会 panic)。 pub fn new(capacity: NonZeroUsize) -> Self { Self { map: Mutex::new(LruCache::new(capacity)), } } + /// 按哈希取缓存模块;未命中返回 None(命中会提升 LRU 位置)。 pub fn get(&self, hash: u64) -> Option> { let result = self.map.lock().unwrap().get(&hash).map(Arc::clone); if result.is_some() { @@ -35,6 +41,7 @@ impl CodeForge { result } + /// 插入模块并返回共享 `Arc`;超出容量时逐出最久未用项。 pub fn insert(&self, hash: u64, module: CompiledModule) -> Arc { let module = Arc::new(module); let mut cache = self.map.lock().unwrap(); @@ -52,6 +59,8 @@ impl CodeForge { module } + /// 命中返回缓存模块,未命中则调用 `compile` 编译后缓存并返回。 + /// debug 构建下命中时会重编译校验 bytecode,检测结构哈希碰撞。 pub fn get_or_insert_with(&self, hash: u64, compile: F) -> Result, String> where F: Fn() -> Result, @@ -79,10 +88,12 @@ impl CodeForge { Ok(module) } + /// 当前缓存条目数。 pub fn len(&self) -> usize { self.map.lock().unwrap().len() } + /// 缓存是否为空。 pub fn is_empty(&self) -> bool { self.map.lock().unwrap().is_empty() } diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index 4ae70c4..0a9ac45 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -1,16 +1,22 @@ +//! 编译入口:`Compiler::compile` 串联 emit 与 lowering。 + use oxide_bytecode::module::CompiledModule; use oxide_emit::Emitter; use oxide_ir::lower::lower; +/// 结构哈希 / 编译模块哈希 re-export(供编译缓存作键)。 pub use crate::hash::{compiled_module_hash, structural_hash}; +/// 编译入口(marker 类型):无状态,编译状态在 emit/lower 内部。 pub struct Compiler; impl Compiler { + /// 构造 `Compiler`(无状态,恒返回空实例)。 pub fn new() -> Self { Self } + /// 编译整个 program:AST → IR(`Emitter::emit_program`)→ bytecode(`lower`)。 pub fn compile(&self, program: &oxide_parser::Program) -> Result { crate::compiler_debug!("compile: starting..."); let ir = Emitter::new().emit_program(program)?; diff --git a/crates/oxide_compiler/src/compiler_log.rs b/crates/oxide_compiler/src/compiler_log.rs index 1280f9e..53e088b 100644 --- a/crates/oxide_compiler/src/compiler_log.rs +++ b/crates/oxide_compiler/src/compiler_log.rs @@ -1,3 +1,5 @@ +//! 编译日志宏:`compiler_error!/warn!/info!/debug!/trace!`,经由 oxide_log 上报。 + #[macro_export] macro_rules! compiler_error { ($($arg:tt)*) => { diff --git a/crates/oxide_compiler/src/hash/class.rs b/crates/oxide_compiler/src/hash/class.rs index f57cf94..8cec5fb 100644 --- a/crates/oxide_compiler/src/hash/class.rs +++ b/crates/oxide_compiler/src/hash/class.rs @@ -1,3 +1,5 @@ +//! 类相关 AST 节点的结构哈希(ClassElement/ClassExpression 等)。 + use super::*; pub(super) fn hash_class_element(element: &ClassElement, h: &mut rustc_hash::FxHasher, include_binding_names: bool) { diff --git a/crates/oxide_compiler/src/hash/expression.rs b/crates/oxide_compiler/src/hash/expression.rs index b2524e6..927fc3b 100644 --- a/crates/oxide_compiler/src/hash/expression.rs +++ b/crates/oxide_compiler/src/hash/expression.rs @@ -1,3 +1,5 @@ +//! 表达式 AST 节点的结构哈希。 + use super::*; pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, include_binding_names: bool) { diff --git a/crates/oxide_compiler/src/hash/mod.rs b/crates/oxide_compiler/src/hash/mod.rs index fe9029b..116626b 100644 --- a/crates/oxide_compiler/src/hash/mod.rs +++ b/crates/oxide_compiler/src/hash/mod.rs @@ -1,3 +1,9 @@ +//! AST 结构哈希:按语法域(statement/expression/class/...)递归计算 +//! `Program` 的稳定哈希,用作编译缓存键(code cache)。 +//! +//! `structural_hash` 忽略绑定名(结构相等即可命中), +//! `compiled_module_hash` 纳入绑定名(精确匹配避免错误复用)。 + use oxide_parser::{ BindingPattern, ChainElement, ClassElement, Expression, ForStatementInit, ObjectPropertyKind, PropertyKey, SimpleAssignmentTarget, Statement, @@ -30,10 +36,12 @@ mod property; mod statement; mod target; +/// 结构哈希:忽略绑定名的 `Program` 哈希(用于粗粒度缓存命中判断)。 pub fn structural_hash(program: &oxide_parser::Program) -> u64 { hash_program(program, false) } +/// 编译模块哈希:纳入绑定名的 `Program` 哈希(用于精确缓存命中判断)。 pub fn compiled_module_hash(program: &oxide_parser::Program) -> u64 { hash_program(program, true) } diff --git a/crates/oxide_compiler/src/hash/property.rs b/crates/oxide_compiler/src/hash/property.rs index 0aed603..9e3ffd6 100644 --- a/crates/oxide_compiler/src/hash/property.rs +++ b/crates/oxide_compiler/src/hash/property.rs @@ -1,3 +1,5 @@ +//! 对象属性键/属性类型(PropertyKey/ObjectPropertyKind)的结构哈希。 + use super::*; pub(super) fn hash_property_key(key: &PropertyKey, h: &mut rustc_hash::FxHasher, include_binding_names: bool) { diff --git a/crates/oxide_compiler/src/hash/statement.rs b/crates/oxide_compiler/src/hash/statement.rs index 35643fa..f129b22 100644 --- a/crates/oxide_compiler/src/hash/statement.rs +++ b/crates/oxide_compiler/src/hash/statement.rs @@ -1,3 +1,5 @@ +//! 语句 AST 节点的结构哈希。 + use super::*; pub(super) fn hash_statement(stmt: &Statement, h: &mut rustc_hash::FxHasher, include_binding_names: bool) { diff --git a/crates/oxide_compiler/src/hash/target.rs b/crates/oxide_compiler/src/hash/target.rs index 4f2f89c..8e2dcca 100644 --- a/crates/oxide_compiler/src/hash/target.rs +++ b/crates/oxide_compiler/src/hash/target.rs @@ -1,3 +1,5 @@ +//! 赋值目标(SimpleAssignmentTarget)的结构哈希。 + use super::*; pub(super) fn hash_simple_assignment_target( diff --git a/crates/oxide_compiler/src/lib.rs b/crates/oxide_compiler/src/lib.rs index a1c2a1a..2ff885d 100644 --- a/crates/oxide_compiler/src/lib.rs +++ b/crates/oxide_compiler/src/lib.rs @@ -1,4 +1,8 @@ -#![doc = "OxideJS - AST to bytecode compiler"] +//! oxide_compiler:AST → bytecode 编译入口(编译管线收尾)。 +//! +//! `Compiler::compile` 串联 `oxide_emit`(AST → IR)与 `oxide_ir::lower` +//! (IR → bytecode),产出 `CompiledModule`。另有 AST 结构哈希 +//! (`structural_hash` / `compiled_module_hash`)用于编译缓存键。 pub mod compiler; pub mod compiler_log; diff --git a/crates/oxide_emit/src/class/core.rs b/crates/oxide_emit/src/class/core.rs index 8b13789..2d45e67 100644 --- a/crates/oxide_emit/src/class/core.rs +++ b/crates/oxide_emit/src/class/core.rs @@ -1 +1,2 @@ +//! 类编译核心预留模块(当前为空,为后续类相关公共状态预留位置)。 diff --git a/crates/oxide_emit/src/class/emit.rs b/crates/oxide_emit/src/class/emit.rs index faafdff..d00f023 100644 --- a/crates/oxide_emit/src/class/emit.rs +++ b/crates/oxide_emit/src/class/emit.rs @@ -1,3 +1,7 @@ +//! class 声明整体 emit:驱动头、原型、方法、字段各阶段,组装最终类对象。 +//! +//! 函数:`emit_class`。 + use crate::{CompileCtx, Emitter, FunctionBodyContext}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/class/field.rs b/crates/oxide_emit/src/class/field.rs index 273cb4d..18b6d1d 100644 --- a/crates/oxide_emit/src/class/field.rs +++ b/crates/oxide_emit/src/class/field.rs @@ -1,3 +1,5 @@ +//! 类静态字段/元素 emit:`emit_class_static_elements`。 + use crate::{CompileCtx, Emitter}; use oxide_ir::operand::Operand; use oxide_parser::{ClassElement, PropertyKey}; diff --git a/crates/oxide_emit/src/class/header.rs b/crates/oxide_emit/src/class/header.rs index d73bf49..956a82e 100644 --- a/crates/oxide_emit/src/class/header.rs +++ b/crates/oxide_emit/src/class/header.rs @@ -1,3 +1,5 @@ +//! 类头 emit:类名绑定、继承链(`extends`)设置,见 `emit_class_header`。 + use crate::{CompileCtx, Emitter}; impl Emitter { diff --git a/crates/oxide_emit/src/class/method.rs b/crates/oxide_emit/src/class/method.rs index 6fb5c12..88c95a9 100644 --- a/crates/oxide_emit/src/class/method.rs +++ b/crates/oxide_emit/src/class/method.rs @@ -1,3 +1,6 @@ +//! 类方法 emit:`emit_class_methods` 逐方法构造,`emit_class_method_function` +//! 负责单个方法函数体(含 `super` 与 home object)。 + use crate::{CompileCtx, Emitter, FunctionBodyContext}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/class/mod.rs b/crates/oxide_emit/src/class/mod.rs index 51027ce..42c317b 100644 --- a/crates/oxide_emit/src/class/mod.rs +++ b/crates/oxide_emit/src/class/mod.rs @@ -1,3 +1,5 @@ +//! 类编译域:class 声明的整体 emit 拆分为头/原型/方法/字段等子模块。 + pub mod core; pub mod emit; pub mod field; diff --git a/crates/oxide_emit/src/class/prototype.rs b/crates/oxide_emit/src/class/prototype.rs index c8fc0a2..86d6acb 100644 --- a/crates/oxide_emit/src/class/prototype.rs +++ b/crates/oxide_emit/src/class/prototype.rs @@ -1,3 +1,5 @@ +//! 类原型对象构造:`emit_class_prototype` 建立 `Class.prototype` 与属性初始化。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/closure.rs b/crates/oxide_emit/src/closure.rs index a3e5d12..b0c50fd 100644 --- a/crates/oxide_emit/src/closure.rs +++ b/crates/oxide_emit/src/closure.rs @@ -1,3 +1,9 @@ +//! 闭包捕获分析(AST 级,emit 前完成,时序无关)。 +//! +//! 收集本函数绑定的名字(own_bindings)与被嵌套函数捕获的名字 +//! (captured_bindings → cell_idx,按名排序稳定跨 run),为 MAKE_CELL / +//! CELL_GET / CELL_SET 与子函数 upvalue cell_idx 提供统一依据。 + use std::collections::{BTreeMap, HashSet}; use oxide_bytecode::module::UpvalueCapture; diff --git a/crates/oxide_emit/src/emit.rs b/crates/oxide_emit/src/emit.rs index 2860eb3..7c45010 100644 --- a/crates/oxide_emit/src/emit.rs +++ b/crates/oxide_emit/src/emit.rs @@ -1,3 +1,10 @@ +//! emit:AST → IR 代码生成(parse → IR → bytecode 中段)。 +//! +//! `Emitter` 提供各语法域的 emit_* 方法;核心状态集中在 `CompileCtx`: +//! 执行流字段(insts/registers/constants/labels)平铺其上,标识符绑定与 +//! 闭包捕获分别下沉到 `SymbolTable` / `captured_bindings`。产出分域组合的 +//! `IRFunction`,由 `oxide_ir::lower` 降为 bytecode。 + use std::collections::{BTreeMap, HashMap, HashSet}; use oxide_bytecode::module::UpvalueCapture; @@ -9,16 +16,23 @@ use oxide_ir::IRFunction; use crate::emit_ctx::{LabelCtx, ScopeCtx}; use crate::symbol_table::{Binding, ScopeKind, SymbolTable}; +/// 常量池项(bytecode module 类型)re-export,供调用方构造常量。 pub use oxide_bytecode::module::Constant; +/// 变量声明种类(var/let/const),re-export 自 parser。 pub use oxide_parser::VariableDeclarationKind; +/// AST 语法树节点与运算符类型,re-export 自 parser。 pub use oxide_parser::{AssignmentOperator, BinaryOperator, Expression, Statement, UnaryOperator}; +/// 编译入口(marker 类型)。方法按语法域组织在 `impl Emitter` 中。 pub struct Emitter; +/// 判断 f64 是否为整数值且在 i32 范围内(整数常量编码用)。 pub fn is_int_literal(value: f64) -> bool { value.fract() == 0.0 && value >= i32::MIN as f64 && value <= i32::MAX as f64 } +/// 判断表达式是否无副作用(字面量/标识符/纯二元运算等)。 +/// 用于可丢弃值的优化路径。 pub fn is_side_effect_free(expr: &Expression) -> bool { let mut stack = vec![expr]; while let Some(expr) = stack.pop() { @@ -120,6 +134,8 @@ pub struct LabelScope { pub(crate) continue_label: Option, } +/// 单函数编译上下文:执行流 + 作用域 + 闭包捕获的聚合状态。 +/// 指令、常量池、寄存器分配平铺于此,作用域/绑定见 `ScopeCtx`,跳转见 `LabelCtx`。 pub struct CompileCtx { pub(crate) insts: Vec, pub(crate) constants: Vec, @@ -152,13 +168,18 @@ pub struct CompileCtx { pub(crate) const_overflow: bool, } +/// 函数体编译上下文:决定 `this`/`super` 绑定与参数前导(prologue)形态。 #[derive(Clone, Copy)] pub enum FunctionBodyContext { + /// 普通函数:自身 `this`、独立作用域。 Ordinary, + /// 箭头函数:词法捕获外层 `this`,不生成参数前导。 Arrow, + /// 类元素方法:按类语义处理 `super` 与 home object。 ClassElement, } +/// 参数规格:普通形参为标识符,解构形参用合成名 + 原始 pattern。 pub enum ParamSpec<'a> { Identifier(String), Pattern { @@ -513,6 +534,7 @@ impl ConstantKey { } impl Emitter { + /// 构造空 `Emitter`(无内部状态,所有状态在 `CompileCtx` 中)。 pub fn new() -> Self { Self } diff --git a/crates/oxide_emit/src/expr/assignment.rs b/crates/oxide_emit/src/expr/assignment.rs index 449b059..6d155b0 100644 --- a/crates/oxide_emit/src/expr/assignment.rs +++ b/crates/oxide_emit/src/expr/assignment.rs @@ -1,3 +1,6 @@ +//! 赋值表达式 emit:简单 `=`、复合赋值与解构赋值(数组/对象 pattern)。 +//! 函数:`emit_assignment_expression`。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/call.rs b/crates/oxide_emit/src/expr/call.rs index 8180719..c7ade3f 100644 --- a/crates/oxide_emit/src/expr/call.rs +++ b/crates/oxide_emit/src/expr/call.rs @@ -1,3 +1,6 @@ +//! 调用表达式 emit:普通/原生/`new`/`super` 调用与调用域分发。 +//! 函数:`emit_call_expression`、`emit_call_domain`。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/function/mod.rs b/crates/oxide_emit/src/expr/function/mod.rs index 0b1370c..6fb184f 100644 --- a/crates/oxide_emit/src/expr/function/mod.rs +++ b/crates/oxide_emit/src/expr/function/mod.rs @@ -1,3 +1,7 @@ +//! 函数表达式域:箭头/普通函数/类表达式/`new` 表达式的 emit 与分发。 +//! 函数:`emit_arrow_function_expression`、`emit_function_expression`、 +//! `emit_class_expression`、`emit_new_expression`、`emit_function_domain`。 + use crate::{CompileCtx, Emitter, ParamSpec}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/identifier.rs b/crates/oxide_emit/src/expr/identifier.rs index a052a95..27a7322 100644 --- a/crates/oxide_emit/src/expr/identifier.rs +++ b/crates/oxide_emit/src/expr/identifier.rs @@ -1,3 +1,5 @@ +//! 标识符表达式 emit:`emit_identifier_expression` 按绑定/builtin/全局解析寄存器。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/literal.rs b/crates/oxide_emit/src/expr/literal.rs index 5b6aa75..36a0c06 100644 --- a/crates/oxide_emit/src/expr/literal.rs +++ b/crates/oxide_emit/src/expr/literal.rs @@ -1,3 +1,6 @@ +//! 字面量表达式 emit:数字/字符串/布尔/null/正则 → 常量池或立即数。 +//! 整数走立即数编码(`is_int_literal`),其余入常量池。 + use crate::{is_int_literal, CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/member/mod.rs b/crates/oxide_emit/src/expr/member/mod.rs index 2f15b32..0214d17 100644 --- a/crates/oxide_emit/src/expr/member/mod.rs +++ b/crates/oxide_emit/src/expr/member/mod.rs @@ -1,3 +1,7 @@ +//! 成员访问域:静态/计算/私有字段成员与可选链表达式 emit 及分发。 +//! 函数:`emit_static_member_expression`、`emit_computed_member_expression`、 +//! `emit_private_field_expression`、`emit_chain_expression`、`emit_member_domain`。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/mod.rs b/crates/oxide_emit/src/expr/mod.rs index 82660cf..47acb74 100644 --- a/crates/oxide_emit/src/expr/mod.rs +++ b/crates/oxide_emit/src/expr/mod.rs @@ -1,3 +1,8 @@ +//! 表达式编译域:表达式分发 + sequence/unsupported。 +//! +//! `emit_expression` 按 `Expression` 变体分发到各子模块;本文件还处理 +//! 序列表达式(逐项求值取末值)与不支持表达式的报错。 + use crate::{CompileCtx, Emitter}; use oxide_parser::Expression; diff --git a/crates/oxide_emit/src/expr/object/array.rs b/crates/oxide_emit/src/expr/object/array.rs index 17792af..2c62609 100644 --- a/crates/oxide_emit/src/expr/object/array.rs +++ b/crates/oxide_emit/src/expr/object/array.rs @@ -1,3 +1,5 @@ +//! 数组字面量 emit:`emit_array_expression` 逐元素求值并建数组(含 holes)。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/object/mod.rs b/crates/oxide_emit/src/expr/object/mod.rs index 8a26daf..f873af8 100644 --- a/crates/oxide_emit/src/expr/object/mod.rs +++ b/crates/oxide_emit/src/expr/object/mod.rs @@ -1,3 +1,5 @@ +//! 对象表达式域:对象/数组字面量子模块与域分发 `emit_object_domain`。 + use crate::{CompileCtx, Emitter}; use oxide_parser::Expression; diff --git a/crates/oxide_emit/src/expr/object/object_literal.rs b/crates/oxide_emit/src/expr/object/object_literal.rs index 3cc8b4b..11c0149 100644 --- a/crates/oxide_emit/src/expr/object/object_literal.rs +++ b/crates/oxide_emit/src/expr/object/object_literal.rs @@ -1,3 +1,5 @@ +//! 对象字面量 emit:`emit_object_expression` 逐属性定义(含 getter/setter/展开)。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/operator/mod.rs b/crates/oxide_emit/src/expr/operator/mod.rs index 0525cf5..34f8a7a 100644 --- a/crates/oxide_emit/src/expr/operator/mod.rs +++ b/crates/oxide_emit/src/expr/operator/mod.rs @@ -1,3 +1,7 @@ +//! 运算符域:二元/一元/条件/逻辑/更新/`in` 表达式 emit 与分发。 +//! 逻辑与 `??` 用短路跳转,复合求值利用 `is_side_effect_free` 优化。 +//! 函数:`emit_operator` 及各类 `emit_*_expression`。 + use crate::{is_side_effect_free, BinaryOperator, CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/parenthesized.rs b/crates/oxide_emit/src/expr/parenthesized.rs index a85ce88..afbd2ab 100644 --- a/crates/oxide_emit/src/expr/parenthesized.rs +++ b/crates/oxide_emit/src/expr/parenthesized.rs @@ -1,3 +1,5 @@ +//! 括号表达式 emit:`emit_parenthesized_expression` 透传内层表达式。 + use crate::{CompileCtx, Emitter}; impl Emitter { diff --git a/crates/oxide_emit/src/expr/template/mod.rs b/crates/oxide_emit/src/expr/template/mod.rs index b8d01fe..25c2344 100644 --- a/crates/oxide_emit/src/expr/template/mod.rs +++ b/crates/oxide_emit/src/expr/template/mod.rs @@ -1,3 +1,5 @@ +//! 模板字符串域:普通/标签模板子模块与域分发 `emit_template_domain`。 + use crate::{CompileCtx, Emitter}; use oxide_parser::Expression; diff --git a/crates/oxide_emit/src/expr/template/tagged.rs b/crates/oxide_emit/src/expr/template/tagged.rs index 8fa557b..fc7b552 100644 --- a/crates/oxide_emit/src/expr/template/tagged.rs +++ b/crates/oxide_emit/src/expr/template/tagged.rs @@ -1,3 +1,5 @@ +//! 标签模板表达式 emit:`emit_tagged_template_expression` 构造 template object 并调用标签函数。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/template/template_literal.rs b/crates/oxide_emit/src/expr/template/template_literal.rs index 96c24d7..bb4bec9 100644 --- a/crates/oxide_emit/src/expr/template/template_literal.rs +++ b/crates/oxide_emit/src/expr/template/template_literal.rs @@ -1,3 +1,6 @@ +//! 模板字面量 emit:`emit_template_literal_expression` 生成 TEMPLATE_STR 指令 +//! 拼接字面量段与内插表达式。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/expr/this.rs b/crates/oxide_emit/src/expr/this.rs index 1cc184e..d42e571 100644 --- a/crates/oxide_emit/src/expr/this.rs +++ b/crates/oxide_emit/src/expr/this.rs @@ -1,3 +1,5 @@ +//! `this` 表达式 emit:`emit_this_expression` 解析当前函数的 this 寄存器。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/lib.rs b/crates/oxide_emit/src/lib.rs index 68f361b..9a5145f 100644 --- a/crates/oxide_emit/src/lib.rs +++ b/crates/oxide_emit/src/lib.rs @@ -1,3 +1,10 @@ +//! oxide_emit:AST → IR 代码生成层(编译管线中段)。 +//! +//! 入口 `Emitter::emit_program` 把 parser 产出的 `Program` 编译为分域组合的 +//! `IRFunction`(code/data/bindings/builtins/closures/meta/nested),再由 +//! `oxide_ir::lower` 降为 bytecode。语法按域拆分:`expr/`、`stmt/`、`class/`、 +//! `shared/`;作用域与闭包捕获分析在 emit 前完成,消除符号表时序依赖。 + pub mod class; pub mod closure; pub mod emit; diff --git a/crates/oxide_emit/src/prepass.rs b/crates/oxide_emit/src/prepass.rs index b7ca86f..0dd387b 100644 --- a/crates/oxide_emit/src/prepass.rs +++ b/crates/oxide_emit/src/prepass.rs @@ -1,3 +1,9 @@ +//! emit 前置 pass:builtin 引用预扫描 + 声明预登记。 +//! +//! 在生成临时寄存器前遍历 AST,把内置全局标识符预先登记到固定寄存器槽 +//! (builtin_reg_map),避免与临时寄存器池冲突;同时预声明函数/var 声明, +//! 支持提升语义。 + use crate::{CompileCtx, Emitter}; use oxide_parser::{Expression, Statement, VariableDeclarationKind}; diff --git a/crates/oxide_emit/src/shared/binding.rs b/crates/oxide_emit/src/shared/binding.rs index c10835e..291d098 100644 --- a/crates/oxide_emit/src/shared/binding.rs +++ b/crates/oxide_emit/src/shared/binding.rs @@ -1,3 +1,7 @@ +//! 解构绑定/赋值 emit:数组与对象 pattern 的绑定初始化、rest 收集与解构赋值。 +//! 函数:`emit_bind_target`、`emit_binding_pattern`、`emit_array_binding`、 +//! `emit_object_binding`、`emit_assign_target` 等。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/shared/chain.rs b/crates/oxide_emit/src/shared/chain.rs index 3c8652c..7bb0676 100644 --- a/crates/oxide_emit/src/shared/chain.rs +++ b/crates/oxide_emit/src/shared/chain.rs @@ -1,3 +1,6 @@ +//! 可选链(`?.`)emit:短路守卫、链式成员访问/调用与逻辑赋值短路测试。 +//! 函数:`emit_chainable_expression`、`emit_optional_guard`、`emit_chain_call` 等。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::{LabelId, Operand}; diff --git a/crates/oxide_emit/src/shared/class_field.rs b/crates/oxide_emit/src/shared/class_field.rs index 6667d60..6343608 100644 --- a/crates/oxide_emit/src/shared/class_field.rs +++ b/crates/oxide_emit/src/shared/class_field.rs @@ -1,3 +1,5 @@ +//! 类字段初始化 emit:公有/私有实例字段与私有方法的实例化初始化。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/shared/class_key.rs b/crates/oxide_emit/src/shared/class_key.rs index 1fe27f8..c8ec33b 100644 --- a/crates/oxide_emit/src/shared/class_key.rs +++ b/crates/oxide_emit/src/shared/class_key.rs @@ -1,3 +1,6 @@ +//! 类属性键/私有名 emit:私有名 id 分配、类字段键寄存器与 undefined 常量。 +//! 函数:`private_name_id`、`emit_private_id_reg`、`emit_class_key_reg` 等。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/shared/mod.rs b/crates/oxide_emit/src/shared/mod.rs index 1d3d86b..1f2a12f 100644 --- a/crates/oxide_emit/src/shared/mod.rs +++ b/crates/oxide_emit/src/shared/mod.rs @@ -1,3 +1,5 @@ +//! 跨语法域共享的 emit 辅助:解构绑定、可选链、类字段初始化、类属性键。 + pub mod binding; pub mod chain; pub mod class_field; diff --git a/crates/oxide_emit/src/stmt/basic/break_continue.rs b/crates/oxide_emit/src/stmt/basic/break_continue.rs index f292b39..e58c073 100644 --- a/crates/oxide_emit/src/stmt/basic/break_continue.rs +++ b/crates/oxide_emit/src/stmt/basic/break_continue.rs @@ -1,3 +1,5 @@ +//! break/continue 语句 emit:解析标签与最近循环的跳转目标。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; diff --git a/crates/oxide_emit/src/stmt/basic/expression.rs b/crates/oxide_emit/src/stmt/basic/expression.rs index 43271ae..dbf18fd 100644 --- a/crates/oxide_emit/src/stmt/basic/expression.rs +++ b/crates/oxide_emit/src/stmt/basic/expression.rs @@ -1,3 +1,5 @@ +//! 表达式语句 emit:`emit_expression_statement` 求值并(按需)丢弃结果。 + use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; diff --git a/crates/oxide_emit/src/stmt/basic/labeled.rs b/crates/oxide_emit/src/stmt/basic/labeled.rs index 1d15ba7..7f6739e 100644 --- a/crates/oxide_emit/src/stmt/basic/labeled.rs +++ b/crates/oxide_emit/src/stmt/basic/labeled.rs @@ -1,3 +1,5 @@ +//! 标签语句 emit:`emit_labeled_statement` 登记 break/continue 标签作用域。 + use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; diff --git a/crates/oxide_emit/src/stmt/basic/mod.rs b/crates/oxide_emit/src/stmt/basic/mod.rs index 546bf3f..4542339 100644 --- a/crates/oxide_emit/src/stmt/basic/mod.rs +++ b/crates/oxide_emit/src/stmt/basic/mod.rs @@ -1,3 +1,5 @@ +//! 基本语句域:表达式语句/return/空语句的分发与子模块聚合。 + use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; diff --git a/crates/oxide_emit/src/stmt/basic/return_stmt.rs b/crates/oxide_emit/src/stmt/basic/return_stmt.rs index 3b84d16..3f1407a 100644 --- a/crates/oxide_emit/src/stmt/basic/return_stmt.rs +++ b/crates/oxide_emit/src/stmt/basic/return_stmt.rs @@ -1,3 +1,5 @@ +//! return 语句 emit:`emit_return_statement` 求值返回值并生成返回指令。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/stmt/block.rs b/crates/oxide_emit/src/stmt/block.rs index 96f91f7..e228975 100644 --- a/crates/oxide_emit/src/stmt/block.rs +++ b/crates/oxide_emit/src/stmt/block.rs @@ -1,3 +1,5 @@ +//! 块语句 emit:`emit_block_statement` 压/弹作用域并顺序编译子语句(含域分发)。 + use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; diff --git a/crates/oxide_emit/src/stmt/control.rs b/crates/oxide_emit/src/stmt/control.rs index 0fe0566..0a6221b 100644 --- a/crates/oxide_emit/src/stmt/control.rs +++ b/crates/oxide_emit/src/stmt/control.rs @@ -1,3 +1,5 @@ +//! 控制流语句 emit:`if` 条件分支(含 else)与域分发 `emit_control_domain`。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/stmt/declaration/class_decl.rs b/crates/oxide_emit/src/stmt/declaration/class_decl.rs index 85f76a8..47a7918 100644 --- a/crates/oxide_emit/src/stmt/declaration/class_decl.rs +++ b/crates/oxide_emit/src/stmt/declaration/class_decl.rs @@ -1,3 +1,5 @@ +//! 类声明语句 emit:`emit_class_declaration_statement` 声明类名并初始化类对象。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/stmt/declaration/function_decl.rs b/crates/oxide_emit/src/stmt/declaration/function_decl.rs index 346aa5a..e37b651 100644 --- a/crates/oxide_emit/src/stmt/declaration/function_decl.rs +++ b/crates/oxide_emit/src/stmt/declaration/function_decl.rs @@ -1,3 +1,5 @@ +//! 函数声明语句 emit:`emit_function_declaration_statement` 创建闭包并绑定函数名。 + use crate::{CompileCtx, Emitter, ParamSpec}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/stmt/declaration/mod.rs b/crates/oxide_emit/src/stmt/declaration/mod.rs index fdf3459..dbe8afc 100644 --- a/crates/oxide_emit/src/stmt/declaration/mod.rs +++ b/crates/oxide_emit/src/stmt/declaration/mod.rs @@ -1,3 +1,5 @@ +//! 声明语句域:变量/函数/类声明的分发与子模块聚合。 + use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; diff --git a/crates/oxide_emit/src/stmt/declaration/var.rs b/crates/oxide_emit/src/stmt/declaration/var.rs index d8393e8..71b5de9 100644 --- a/crates/oxide_emit/src/stmt/declaration/var.rs +++ b/crates/oxide_emit/src/stmt/declaration/var.rs @@ -1,3 +1,5 @@ +//! var/let/const 声明语句 emit:声明 + 初始化表达式,处理提升与解构 pattern。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/stmt/exception/mod.rs b/crates/oxide_emit/src/stmt/exception/mod.rs index c5a7d08..fde4d62 100644 --- a/crates/oxide_emit/src/stmt/exception/mod.rs +++ b/crates/oxide_emit/src/stmt/exception/mod.rs @@ -1,3 +1,5 @@ +//! 异常语句域:throw/try 的分发与子模块聚合。 + use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; diff --git a/crates/oxide_emit/src/stmt/exception/throw.rs b/crates/oxide_emit/src/stmt/exception/throw.rs index 0642874..c28b756 100644 --- a/crates/oxide_emit/src/stmt/exception/throw.rs +++ b/crates/oxide_emit/src/stmt/exception/throw.rs @@ -1,3 +1,5 @@ +//! throw 语句 emit:`emit_throw_statement` 求值异常值并生成 THROW。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/stmt/exception/try_catch.rs b/crates/oxide_emit/src/stmt/exception/try_catch.rs index 24f1756..1250095 100644 --- a/crates/oxide_emit/src/stmt/exception/try_catch.rs +++ b/crates/oxide_emit/src/stmt/exception/try_catch.rs @@ -1,3 +1,5 @@ +//! try/catch/finally 语句 emit:`emit_try_statement` 生成 TRY_BEGIN 与处理入口。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/stmt/iteration/do_while.rs b/crates/oxide_emit/src/stmt/iteration/do_while.rs index c26dd71..2ce2739 100644 --- a/crates/oxide_emit/src/stmt/iteration/do_while.rs +++ b/crates/oxide_emit/src/stmt/iteration/do_while.rs @@ -1,3 +1,5 @@ +//! do-while 语句 emit:`emit_do_while_statement` 先执行体后测条件。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_parser::Statement; diff --git a/crates/oxide_emit/src/stmt/iteration/for_.rs b/crates/oxide_emit/src/stmt/iteration/for_.rs index ffe8016..fbc655c 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_.rs @@ -1,3 +1,5 @@ +//! for 语句 emit:`emit_for_statement` 含 init/test/update 三段与循环跳转。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/stmt/iteration/for_in.rs b/crates/oxide_emit/src/stmt/iteration/for_in.rs index 67b8f23..4e58803 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_in.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_in.rs @@ -1,3 +1,5 @@ +//! for-in 语句 emit:`emit_for_in_statement` 遍历可枚举属性名。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/stmt/iteration/for_of.rs b/crates/oxide_emit/src/stmt/iteration/for_of.rs index 74176e6..52f71eb 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_of.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_of.rs @@ -1,3 +1,5 @@ +//! for-of 语句 emit:`emit_for_of_statement` 迭代器协议遍历。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/stmt/iteration/mod.rs b/crates/oxide_emit/src/stmt/iteration/mod.rs index 46007d5..260f912 100644 --- a/crates/oxide_emit/src/stmt/iteration/mod.rs +++ b/crates/oxide_emit/src/stmt/iteration/mod.rs @@ -1,3 +1,5 @@ +//! 迭代语句域:while/do-while/for/for-in/for-of 的分发与子模块聚合。 + use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; diff --git a/crates/oxide_emit/src/stmt/iteration/while_loop.rs b/crates/oxide_emit/src/stmt/iteration/while_loop.rs index 19f00c9..8d096dd 100644 --- a/crates/oxide_emit/src/stmt/iteration/while_loop.rs +++ b/crates/oxide_emit/src/stmt/iteration/while_loop.rs @@ -1,3 +1,5 @@ +//! while 语句 emit:`emit_while_statement` 先测条件后执行体。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_parser::Statement; diff --git a/crates/oxide_emit/src/stmt/mod.rs b/crates/oxide_emit/src/stmt/mod.rs index d5e36fc..34cf24a 100644 --- a/crates/oxide_emit/src/stmt/mod.rs +++ b/crates/oxide_emit/src/stmt/mod.rs @@ -1,3 +1,5 @@ +//! 语句编译域:`emit_statement` 分发到块/控制流/声明/异常/迭代等子模块。 + pub mod basic; pub mod block; pub mod control; diff --git a/crates/oxide_emit/src/stmt/switch.rs b/crates/oxide_emit/src/stmt/switch.rs index 6e35732..1c4c5d0 100644 --- a/crates/oxide_emit/src/stmt/switch.rs +++ b/crates/oxide_emit/src/stmt/switch.rs @@ -1,3 +1,5 @@ +//! switch 语句 emit:case 匹配链 + 穿落(fallthrough)与 default,见 `emit_switch_statement`。 + use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; diff --git a/crates/oxide_emit/src/symbol_table.rs b/crates/oxide_emit/src/symbol_table.rs index 2fe319c..2010014 100644 --- a/crates/oxide_emit/src/symbol_table.rs +++ b/crates/oxide_emit/src/symbol_table.rs @@ -1,3 +1,9 @@ +//! 标识符合法化 / 作用域符号表。 +//! +//! 层叠作用域栈:`var` 声明提升到最近函数作用域,`let`/`const` 留在当前块。 +//! 绑定记录寄存器号 + 初始化标志(TDZ 检查)+ const 标志。声明即写入, +//! `lookup` 在未初始化时报 TDZ 错误。 + use std::collections::HashMap; use oxide_parser::VariableDeclarationKind; @@ -19,6 +25,8 @@ pub(crate) struct Binding { pub(crate) is_const: bool, } +/// 作用域符号表:名字 → 寄存器号/初始化状态/const 标志。 +/// 首层为全局函数作用域,后续 push 的为块作用域。 pub struct SymbolTable { pub(crate) scopes: Vec, } @@ -30,6 +38,7 @@ impl Default for SymbolTable { } impl SymbolTable { + /// 构造符号表:预置一个全局函数作用域。 pub fn new() -> Self { Self { scopes: vec![Scope { @@ -39,6 +48,7 @@ impl SymbolTable { } } + /// 压入新的块作用域(`let`/`const` 限定于此)。 pub fn push_scope(&mut self) { self.scopes.push(Scope { bindings: HashMap::new(), @@ -50,6 +60,7 @@ impl SymbolTable { self.scopes.push(Scope { bindings: HashMap::new(), kind }); } + /// 弹出最内层作用域;全局作用域不可弹出。 pub fn pop_scope(&mut self) { if self.scopes.len() > 1 { self.scopes.pop(); @@ -65,6 +76,8 @@ impl SymbolTable { 0 } + /// 声明绑定:`var` 提升到函数作用域,`let`/`const` 落在当前块。 + /// 初始为未初始化态(TDZ);重复声明报错。 pub fn declare( &mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool, ) -> Result<(), String> { @@ -89,6 +102,7 @@ impl SymbolTable { Ok(()) } + /// 从内到外查找已初始化绑定;命中未初始化绑定报 TDZ 错误,未找到报未定义。 pub fn lookup(&self, name: &str) -> Result { for scope in self.scopes.iter().rev() { if let Some(b) = scope.bindings.get(name) { @@ -117,6 +131,7 @@ impl SymbolTable { None } + /// 查找或视为全局:未命中时以 `reg_for_new` 在全局作用域登记并返回(隐式全局)。 pub fn lookup_or_global(&mut self, name: &str, reg_for_new: u8) -> u8 { for scope in self.scopes.iter().rev() { if let Some(b) = scope.bindings.get(name) { @@ -134,6 +149,7 @@ impl SymbolTable { reg_for_new } + /// 返回已初始化绑定是否为 const(用于 const 赋值检查);未初始化/未找到返回 false。 pub fn lookup_is_const(&self, name: &str) -> bool { for scope in self.scopes.iter().rev() { if let Some(b) = scope.bindings.get(name) { @@ -146,6 +162,7 @@ impl SymbolTable { false } + /// 声明并直接标记为已初始化;绑定已存在时仅补初始化标志(供预声明路径)。 pub fn declare_initialized( &mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool, ) -> Result<(), String> { @@ -171,6 +188,7 @@ impl SymbolTable { Ok(()) } + /// 在全局作用域预登记绑定(builtin 全局等),不覆盖已存在的同名绑定。 pub fn pre_register_global(&mut self, name: &str, reg: u8) { self.scopes[0].bindings.entry(name.to_string()).or_insert(Binding { reg, @@ -179,6 +197,7 @@ impl SymbolTable { }); } + /// 从内到外将同名绑定标记为已初始化(var 提升后初始化阶段使用)。 pub fn init_var(&mut self, name: &str) { for scope in self.scopes.iter_mut().rev() { if let Some(b) = scope.bindings.get_mut(name) { diff --git a/crates/oxide_ir/src/inst.rs b/crates/oxide_ir/src/inst.rs index a3cd23f..ed43c1c 100644 --- a/crates/oxide_ir/src/inst.rs +++ b/crates/oxide_ir/src/inst.rs @@ -8,6 +8,8 @@ use smallvec::SmallVec; use crate::operand::{LabelId, Operand}; +/// IR 指令:opcode + 三个操作数槽(rd/a/b)+ 扩展字 `ext`。 +/// `ext` 内部是裸 u32,其数量与语义值编码由下方 `inst_*` 构造 API 保证。 #[derive(Debug, Clone, PartialEq, Eq)] pub struct Inst { pub op: OpCode, @@ -41,74 +43,91 @@ impl Inst { // ── IC 系:ext = [0, 0, 0](shape/slot/proto 占位字,VM 运行时 patch)── + /// 内联缓存读属性:结果写入 `dst`,属性键为 `key`。 pub fn ic_get(dst: Operand, key: Operand) -> Self { Self::with_ext(OpCode::IC_GET_PROP, Operand::None, dst, key, &[0, 0, 0]) } + /// 内联缓存写属性:`obj[key] = value`。 pub fn ic_set(obj: Operand, value: Operand, key: Operand) -> Self { Self::with_ext(OpCode::IC_SET_PROP, obj, value, key, &[0, 0, 0]) } + /// 成员自增:`obj[key]++`,val 为当前值寄存器。 pub fn member_inc(obj: Operand, val: Operand, key: Operand) -> Self { Self::with_ext(OpCode::MEMBER_INC, obj, val, key, &[0, 0, 0]) } + /// 成员自减:`obj[key]--`,val 为当前值寄存器。 pub fn member_dec(obj: Operand, val: Operand, key: Operand) -> Self { Self::with_ext(OpCode::MEMBER_DEC, obj, val, key, &[0, 0, 0]) } + /// 成员复合赋值加法:`obj[key] += val`。 pub fn compound_member_add(obj: Operand, val: Operand, key: Operand) -> Self { Self::with_ext(OpCode::COMPOUND_MEMBER_ADD, obj, val, key, &[0, 0, 0]) } + /// 成员复合赋值减法:`obj[key] -= val`。 pub fn compound_member_sub(obj: Operand, val: Operand, key: Operand) -> Self { Self::with_ext(OpCode::COMPOUND_MEMBER_SUB, obj, val, key, &[0, 0, 0]) } + /// 成员复合赋值乘法:`obj[key] *= val`。 pub fn compound_member_mul(obj: Operand, val: Operand, key: Operand) -> Self { Self::with_ext(OpCode::COMPOUND_MEMBER_MUL, obj, val, key, &[0, 0, 0]) } + /// 成员复合赋值除法:`obj[key] /= val`。 pub fn compound_member_div(obj: Operand, val: Operand, key: Operand) -> Self { Self::with_ext(OpCode::COMPOUND_MEMBER_DIV, obj, val, key, &[0, 0, 0]) } + /// 成员复合赋值取模:`obj[key] %= val`。 pub fn compound_member_mod(obj: Operand, val: Operand, key: Operand) -> Self { Self::with_ext(OpCode::COMPOUND_MEMBER_MOD, obj, val, key, &[0, 0, 0]) } + /// 成员复合赋值指数:`obj[key] **= val`。 pub fn compound_member_exp(obj: Operand, val: Operand, key: Operand) -> Self { Self::with_ext(OpCode::COMPOUND_MEMBER_EXP, obj, val, key, &[0, 0, 0]) } // ── Call 系:ext = [nargs] ── + /// 普通函数调用:rd=callee,a=this,b=首参,ext=\[nargs\]。参数从 `first_arg` 起连续占 nargs 个寄存器。 pub fn call(callee: Operand, this: Operand, first_arg: Operand, nargs: u8) -> Self { Self::with_ext(OpCode::CALL, callee, this, first_arg, &[nargs as u32]) } + /// 原生函数调用(内置),不经 JS 调用协议。 pub fn call_native(callee: Operand, this: Operand, first_arg: Operand, nargs: u8) -> Self { Self::with_ext(OpCode::CALL_NATIVE, callee, this, first_arg, &[nargs as u32]) } + /// `new` 表达式:结果写入 `result`,构造函数为 `constructor`。 pub fn new_expression(result: Operand, constructor: Operand, first_arg: Operand, nargs: u8) -> Self { Self::with_ext(OpCode::NEW_EXPRESSION, result, constructor, first_arg, &[nargs as u32]) } + /// 派生类构造中的 `super(...)`:结果写入 `result`。 pub fn super_call(result: Operand, first_arg: Operand, nargs: u8) -> Self { Self::with_ext(OpCode::SUPER_CALL, result, first_arg, Operand::None, &[nargs as u32]) } // ── 其他带 ext 字 ── + /// 定义访问器属性:home 为宿主对象,get/set 为访问器函数寄存器,key_idx 为属性名常量下标。 pub fn define_accessor(home: Operand, get: Operand, set: Operand, key_idx: u32) -> Self { Self::with_ext(OpCode::DEFINE_ACCESSOR, home, get, set, &[key_idx]) } + /// 静态删除属性:obj 同时放 rd/a 槽,const_idx 为属性名常量下标。 pub fn delete_prop_static(obj: Operand, const_idx: u32) -> Self { Self::with_ext(OpCode::DELETE_PROP_STATIC, obj, obj, Operand::None, &[const_idx]) } + /// 对象 rest 展开:`{...src, 排除 excluded_idx 常量列出的键}` 存入 `rest`。 pub fn rest_object(rest: Operand, src: Operand, excluded_idx: u32) -> Self { Self::with_ext(OpCode::REST_OBJECT, rest, src, Operand::None, &[excluded_idx]) } @@ -130,20 +149,24 @@ impl Inst { // ── 无 ext:立即数/索引指令(拆字是 lowering 职责)── + /// 加载常量池常量:`dst = constants[idx]`。a 槽 Const 下标由 lowering 拆字。 pub fn load_const(dst: Operand, idx: u16) -> Self { Self::new(OpCode::LOAD_CONST, dst, Operand::Const(idx), Operand::None) } + /// 创建闭包:`dst = nested[sub_idx]` 实例化。a 槽 Imm 子函数下标由 lowering 拆字。 pub fn create_closure(dst: Operand, sub_idx: u16) -> Self { Self::new(OpCode::CREATE_CLOSURE, dst, Operand::Imm(sub_idx), Operand::None) } // ── 跳转族:label 放 b 槽,offset 计算是 lowering 职责 ── + /// 无条件跳转。label 放 b 槽,offset 由 lowering 回填。 pub fn jmp(label: LabelId) -> Self { Self::new(OpCode::JMP, Operand::None, Operand::None, Operand::Label(label)) } + /// 条件寄存器为 false 时跳转。 pub fn jmp_if_false(cond_reg: u8, label: LabelId) -> Self { Self::new( OpCode::JMP_IF_FALSE, @@ -153,6 +176,7 @@ impl Inst { ) } + /// 条件寄存器为 true 时跳转。 pub fn jmp_if_true(cond_reg: u8, label: LabelId) -> Self { Self::new( OpCode::JMP_IF_TRUE, @@ -162,6 +186,7 @@ impl Inst { ) } + /// 条件寄存器为 null/undefined 时跳转(`??` / 可选链短路)。 pub fn jmp_if_nullish(cond_reg: u8, label: LabelId) -> Self { Self::new( OpCode::JMP_IF_NULLISH, @@ -171,10 +196,12 @@ impl Inst { ) } + /// try 块起始,label 指向对应的 catch/finally 处理入口。 pub fn try_begin(label: LabelId) -> Self { Self::new(OpCode::TRY_BEGIN, Operand::None, Operand::None, Operand::Label(label)) } + /// try-finally 块起始,label 指向 finally 入口。 pub fn try_finally_begin(label: LabelId) -> Self { Self::new(OpCode::TRY_FINALLY_BEGIN, Operand::None, Operand::None, Operand::Label(label)) } diff --git a/crates/oxide_ir/src/lib.rs b/crates/oxide_ir/src/lib.rs index f90f1a9..5d225c2 100644 --- a/crates/oxide_ir/src/lib.rs +++ b/crates/oxide_ir/src/lib.rs @@ -59,6 +59,7 @@ pub struct IRFunction { } impl IRFunction { + /// 构造空 IRFunction:全域置空/置零,等价于 Default。 pub fn new() -> Self { Self { insts: Vec::new(), diff --git a/crates/oxide_ir/src/operand.rs b/crates/oxide_ir/src/operand.rs index cbd0c3b..3ca9c98 100644 --- a/crates/oxide_ir/src/operand.rs +++ b/crates/oxide_ir/src/operand.rs @@ -10,6 +10,7 @@ /// 统一跳转目标标识(取代语义化 `Label` 枚举)。 pub type LabelId = u32; +/// 类型安全操作数。IR 中不出现裸寄存器下标,语义见文件头 `//!`。 #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Operand { Reg(u32), diff --git a/crates/oxide_kernel/src/builtin.rs b/crates/oxide_kernel/src/builtin.rs index 7cd3736..cba6cc8 100644 --- a/crates/oxide_kernel/src/builtin.rs +++ b/crates/oxide_kernel/src/builtin.rs @@ -40,6 +40,8 @@ macro_rules! bind_methods_static { }; } +/// Object 静态方法与原型方法的 native 函数指针集合,由 builtin 绑定层填充后交给 +/// [`BuiltinWorld::bind_object_methods`] 安装到对象/原型上。 pub struct ObjectMethods { pub keys: *const (), pub create: *const (), @@ -64,6 +66,7 @@ pub struct ObjectMethods { pub property_is_enumerable: *const (), } +/// Array 静态方法与原型方法的 native 函数指针集合,由 [`BuiltinWorld::bind_array_methods`] 安装。 pub struct ArrayMethods { pub is_array: *const (), pub push: *const (), @@ -97,6 +100,7 @@ pub struct ArrayMethods { pub values: *const (), } +/// Error 家族(含各子类型)构造器与原型方法的 native 函数指针集合,由 [`BuiltinWorld::bind_error_methods`] 安装。 pub struct ErrorMethods { pub error: *const (), pub type_error: *const (), @@ -109,6 +113,7 @@ pub struct ErrorMethods { pub stack: *const (), } +/// String 静态方法与原型方法的 native 函数指针集合,由 [`BuiltinWorld::bind_string_methods`] 安装。 pub struct StringMethods { pub from_char_code: *const (), pub index_of: *const (), @@ -142,12 +147,14 @@ pub struct StringMethods { pub last_index_of: *const (), } +/// RegExp 原型方法的 native 函数指针集合。 pub struct RegExpMethods { pub exec: *const (), pub test: *const (), pub to_string: *const (), } +/// Function 原型方法的 native 函数指针集合,由 [`BuiltinWorld::bind_function_methods`] 安装。 pub struct FunctionMethods { pub call: *const (), pub apply: *const (), @@ -155,6 +162,10 @@ pub struct FunctionMethods { pub to_string: *const (), } +/// 全部内置对象(原型、构造器、全局单例 Math/JSON、well-known symbol 与 stub 对象)的持有者。 +/// +/// 每个 session 独立持有自己的 `BuiltinWorld`,保证 session 间内置对象隔离; +/// 由 [`BuiltinWorld::new`] 全量构造,或 [`BuiltinWorld::rebuild_with_dirty`] 按脏标记部分重建。 pub struct BuiltinWorld { pub object_proto: P, pub array_proto: P, @@ -485,6 +496,7 @@ impl BuiltinWorld { JsValue::from_js_object(self.function_proto.as_ptr() as *mut JsObject) } + /// 按 [`BuiltinId`] 取对应内置对象的指针引用。 pub fn get_by_id(&self, id: BuiltinId) -> &P { match id { BuiltinId::ObjectProto => &self.object_proto, @@ -556,6 +568,8 @@ impl BuiltinWorld { } } + /// 全量构造一个全新的 builtin world:创建所有原型/构造器对、Error 子类型、 + /// TypedArray 家族与 well-known symbol 对象,并建立原型链链接。 pub fn new(string_forge: &PermInterner, shape_forge: &ShapeForge) -> Self { let labels = builtin_labels(string_forge); @@ -666,6 +680,7 @@ impl BuiltinWorld { world } + /// 按脏标记选择性重建 builtin world:仅重建被污染的对象家族,未污染的保留原指针。 pub fn rebuild_with_dirty( current: &BuiltinWorld, string_forge: &PermInterner, shape_forge: &ShapeForge, dirty: &BuiltinDirtySet, ) -> BuiltinWorld { @@ -900,6 +915,7 @@ impl BuiltinWorld { world } + /// 把 Object 家族方法安装到 Object 构造器与原型上(含 `hasOwnProperty` 等非枚举元数据修正)。 pub fn bind_object_methods(&self, methods: &ObjectMethods, string_forge: &PermInterner, shape_forge: &ShapeForge) { let ctor_ptr = P::as_ptr(&self.object_constructor) as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; @@ -947,6 +963,7 @@ impl BuiltinWorld { } } + /// 把 Array 家族方法安装到 Array 构造器与原型上。 pub fn bind_array_methods(&self, methods: &ArrayMethods, string_forge: &PermInterner, shape_forge: &ShapeForge) { let ctor_ptr = P::as_ptr(&self.array_constructor) as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; @@ -999,6 +1016,7 @@ impl BuiltinWorld { debug_assert!(shape_forge.lookup_position(proto.shape_id(), si).is_some()); } + /// 把 Error 家族方法安装到 Error 及各子类型构造器与原型上。 pub fn bind_error_methods(&self, methods: &ErrorMethods, string_forge: &PermInterner, shape_forge: &ShapeForge) { let ctor_ptr = P::as_ptr(&self.error_constructor) as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; @@ -1061,6 +1079,7 @@ impl BuiltinWorld { proto.ensure_hash_props().push(name_val); } + /// 把 String 家族方法安装到 String 构造器与原型上。 pub fn bind_string_methods(&self, methods: &StringMethods, string_forge: &PermInterner, shape_forge: &ShapeForge) { let ctor_ptr = P::as_ptr(&self.string_constructor) as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; @@ -1105,6 +1124,7 @@ impl BuiltinWorld { ); } + /// 把 Function 原型方法安装到 Function.prototype 上。 pub fn bind_function_methods( &self, methods: &FunctionMethods, string_forge: &PermInterner, shape_forge: &ShapeForge, ) { @@ -1123,6 +1143,7 @@ impl BuiltinWorld { ); } + /// 在指定原型上安装一个 native 方法,wrapper 函数以本 world 的 Function 原型为原型。 pub fn bind_method( &self, proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &PermInterner, method_name: &str, native_fn_ptr: NativeFnPtr, arg_count: u8, @@ -1138,6 +1159,9 @@ impl BuiltinWorld { ) } + /// 构造并安装一个 native 方法 wrapper 函数对象(设置 `length`/`name` 属性与参数元数据)。 + /// + /// 无状态版本,不依赖 `BuiltinWorld` 实例,供静态绑定宏在初始化阶段直接调用。 pub fn bind_method_static( proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &PermInterner, method_name: &str, native_fn_ptr: NativeFnPtr, arg_count: u8, wrapper_proto: JsValue, diff --git a/crates/oxide_kernel/src/code_forge.rs b/crates/oxide_kernel/src/code_forge.rs index 3ab24e7..aa43de4 100644 --- a/crates/oxide_kernel/src/code_forge.rs +++ b/crates/oxide_kernel/src/code_forge.rs @@ -1 +1,2 @@ +/// compiled bytecode cache:按编译后的 module 缓存字节码,避免重复编译。 pub use oxide_code_cache::CodeForge; diff --git a/crates/oxide_kernel/src/kernel.rs b/crates/oxide_kernel/src/kernel.rs index 98fde46..2c6c6fa 100644 --- a/crates/oxide_kernel/src/kernel.rs +++ b/crates/oxide_kernel/src/kernel.rs @@ -16,6 +16,9 @@ use crate::string_forge::PermInterner; use oxide_log; use oxide_log::{Level, SUBSYSTEM_COUNT}; +/// kernel 运行配置:VM 池规模、步数/调用深度上限、session GC 阈值、 +/// 日志级别与内置对象预热开关。由三个预设构造器([`KernelConfig::minimal`] / +/// [`KernelConfig::standard`] / [`KernelConfig::full`])或默认值创建。 #[derive(Clone)] pub struct KernelConfig { pub min_pool_size: usize, @@ -32,6 +35,7 @@ pub struct KernelConfig { } impl KernelConfig { + /// 最小配置:小 VM 池、关闭 code/IC 预热,适合嵌入式或单次执行场景。 pub fn minimal() -> Self { Self { min_pool_size: 4, @@ -48,6 +52,7 @@ impl KernelConfig { } } + /// 标准配置:默认 VM 池大小,开启内置对象 code 预热。 pub fn standard() -> Self { Self { min_pool_size: 8, @@ -64,6 +69,7 @@ impl KernelConfig { } } + /// 全量配置:无上限 VM 池,开启 shapes/code/IC 全量预热,为性能场景服务。 pub fn full() -> Self { Self { min_pool_size: 16, @@ -80,18 +86,22 @@ impl KernelConfig { } } + /// 读取 session GC 阈值(字节数)。 pub fn session_gc_threshold(&self) -> usize { self.session_gc_threshold } + /// 设置 session GC 阈值(字节数),超过后触发一次 session 级 GC。 pub fn set_session_gc_threshold(&mut self, bytes: usize) { self.session_gc_threshold = bytes; } + /// 读取 code cache 的 module 数量上限。 pub fn max_cached_modules(&self) -> usize { self.max_cached_modules } + /// 设置 code cache 的 module 数量上限。 pub fn set_max_cached_modules(&mut self, cap: usize) { self.max_cached_modules = cap; } @@ -114,6 +124,7 @@ pub struct KernelCore { } impl KernelCore { + /// 按配置创建共享核心:初始化日志系统与四个共享 forge(interner/shape/code/prop)。 pub fn new(config: KernelConfig) -> Arc { oxide_log::init(&oxide_log::LogConfig { output: oxide_log::Output::Stderr, @@ -138,42 +149,55 @@ impl KernelCore { core } + /// 只读访问永久字符串 intern 表。 pub fn perm_interner(&self) -> &Arc { &self.perm_interner } + /// 只读访问共享 hidden class(shape)存储。 pub fn shape_forge(&self) -> &Arc { &self.shape_forge } + /// 只读访问共享 bytecode cache。 pub fn code_forge(&self) -> &Arc { &self.code_forge } + /// 只读访问共享属性模板缓存。 pub fn prop_forge(&self) -> &Arc { &self.prop_forge } + /// 只读访问构建时固定的配置。 pub fn config(&self) -> &KernelConfig { &self.config } + /// 读取 session GC 阈值(字节数)。 pub fn session_gc_threshold(&self) -> usize { self.config.session_gc_threshold } + /// 设置 session GC 阈值(字节数)。 pub fn set_session_gc_threshold(&mut self, bytes: usize) { self.config.session_gc_threshold = bytes; } + /// 读取 code cache 的 module 数量上限。 pub fn max_cached_modules(&self) -> usize { self.config.max_cached_modules } + /// 设置 code cache 的 module 数量上限。 pub fn set_max_cached_modules(&mut self, cap: usize) { self.config.max_cached_modules = cap; } + /// 在每次 runner(test262 等)边界清理瞬时 shape/prop 缓存,防止跨测试累积膨胀。 + /// + /// 字符串 intern 表是 append-only,无需清理;仅当 shape 或 prop 表超过阈值时才执行 + /// [`ShapeForge::clear_transient`] 与 [`PropForge::clear`]。 pub fn sweep_runner_forges(&self) { // The key interner is append-only (no per-run sweep); only the transient // shape/prop tables need bounding at the test262 per-test boundary. @@ -203,6 +227,10 @@ pub struct KernelSession { /// correct builtin family. pub const NUM_BUILTINS: usize = 66; +/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。 +/// +/// 覆盖各构造器/原型、Error 家族、集合类型、TypedArray 家族与 well-known symbols; +/// `repr(u8)` 使其可直接作为数组下标(`u8` 值即下标)。 #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum BuiltinId { @@ -275,6 +303,7 @@ pub enum BuiltinId { } impl BuiltinId { + /// 全部 66 个内置对象的 id 常量表,供快照/脏检查按序遍历。 pub const ALL: [BuiltinId; NUM_BUILTINS] = [ BuiltinId::ObjectProto, BuiltinId::ArrayProto, @@ -345,6 +374,9 @@ impl BuiltinId { ]; } +/// 内置对象世代(generation)快照:记录构造时各对象及其 stub 的世代号与数量。 +/// +/// 供 [`KernelSession::dirty_since_snapshot`] 对比,判断哪些 builtin 家族在运行期被污染。 #[derive(Clone, Debug)] pub struct BuiltinSnapshot { pub generations: [u32; NUM_BUILTINS], @@ -358,6 +390,7 @@ impl BuiltinSnapshot { obj.generation() } + /// 对给定 builtin world 与 global object 采集一份世代快照。 pub fn new(world: &BuiltinWorld, global_object: &P) -> Self { let mut generations = [0u32; NUM_BUILTINS]; for (i, id) in BuiltinId::ALL.iter().enumerate() { @@ -373,6 +406,9 @@ impl BuiltinSnapshot { } } +/// 按 builtin 家族划分的脏标记位集合:运行期哪些内置对象被用户代码修改过。 +/// +/// `any_builtin_dirty` 只关注 builtin world 内部对象,`any` 额外包含 global object。 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct BuiltinDirtySet { pub object: bool, @@ -397,6 +433,7 @@ pub struct BuiltinDirtySet { } impl BuiltinDirtySet { + /// 是否存在任何 builtin world 内部对象被污染(不含 global object)。 pub fn any_builtin_dirty(&self) -> bool { self.object || self.array @@ -418,6 +455,7 @@ impl BuiltinDirtySet { || self.stubs } + /// 是否存在任何污染(builtin world 或 global object)。 pub fn any(&self) -> bool { self.any_builtin_dirty() || self.global } @@ -460,18 +498,22 @@ impl KernelSession { } } + /// 只读访问当前 session 的 builtin world。 pub fn builtin_world(&self) -> &Arc { &self.builtin_world } + /// 只读访问当前 session 的 global object。 pub fn global_object(&self) -> &P { &self.global_object } + /// 重新采集内置对象世代快照,作为下一次脏检查的基准。 pub fn record_snapshot(&mut self) { self.builtin_snapshot = BuiltinSnapshot::new(&self.builtin_world, &self.global_object); } + /// 对比当前世代与最近快照,返回各 builtin 家族是否被污染。 pub fn dirty_since_snapshot(&self) -> BuiltinDirtySet { let world = self.builtin_world.as_ref(); let snapshot = &self.builtin_snapshot; @@ -555,10 +597,14 @@ impl KernelSession { } } + /// 是否自上次快照以来存在任何污染。 pub fn is_dirty_since_snapshot(&self) -> bool { self.dirty_since_snapshot().any() } + /// 选择性重置:只重建被污染的对象(global 或相应 builtin 家族),并返回脏集合。 + /// + /// 相比全量重建,可保留未污染的内置对象指针与世代,减少隔离成本。 pub fn selective_reset(&mut self, core: &Arc) -> BuiltinDirtySet { let dirty = self.dirty_since_snapshot(); if dirty.global { diff --git a/crates/oxide_kernel/src/lib.rs b/crates/oxide_kernel/src/lib.rs index 2fcd1f3..01ea799 100644 --- a/crates/oxide_kernel/src/lib.rs +++ b/crates/oxide_kernel/src/lib.rs @@ -1,11 +1,24 @@ #![doc = "OxideJS - Shared kernel (CodeForge, ShapeForge, PropForge, StringForge, BuiltinWorld)"] +//! +//! 运行时共享内核层:所有 VM 实例共享的只读缓存(code cache、hidden class / +//! shape store、属性模板、字符串 intern 表)以及内置对象(builtin world)的 +//! 构造与重建逻辑。位于依赖链 `oxide_types ← oxide_kernel ← ...` 中,被 +//! `oxide_vm` 与 `oxide_runtime_api` 共同依赖。 +/// 内置运行时对象(Object/Array/... 及各自原型)的构造与重建。 pub mod builtin; +/// compiled bytecode cache 的再导出(来自 `oxide_code_cache`)。 pub mod code_forge; +/// kernel 核心:配置、共享缓存入口([`KernelCore`])与 per-session 状态。 pub mod kernel; +/// kernel 层日志宏。 pub mod kernel_log; +/// 属性模板缓存:按 shape 缓存属性槽位布局,加速属性查找。 pub mod prop_forge; +/// hidden class(shape)共享存储:把对象结构映射为整数 id。 pub mod shape_forge; +/// 永久字符串 intern 表:属性名/方法名稳定 id 化。 pub mod string_forge; +/// kernel 对外的三个核心类型:配置、共享核心、会话。 pub use kernel::{KernelConfig, KernelCore, KernelSession}; diff --git a/crates/oxide_kernel/src/prop_forge.rs b/crates/oxide_kernel/src/prop_forge.rs index 95192cb..221e643 100644 --- a/crates/oxide_kernel/src/prop_forge.rs +++ b/crates/oxide_kernel/src/prop_forge.rs @@ -4,6 +4,9 @@ use dashmap::DashMap; use crate::shape_forge::ShapeId; +/// 属性布局模板:记录某个 shape 下属性 `prop_name` 应落在对象存储中的 `position`。 +/// +/// `generation` 用于并发更新时的版本裁决(新代覆盖旧代)。 #[derive(Debug, Clone)] pub struct PropTemplate { pub shape_id: ShapeId, @@ -12,23 +15,30 @@ pub struct PropTemplate { pub generation: u32, } +/// 属性模板缓存:`shape_id → 属性布局` 的共享映射。 +/// +/// 供 IC(inline cache)与属性写入路径查询,避免每次写属性都回溯 shape 父链算槽位。 pub struct PropForge { templates: DashMap>, } impl PropForge { + /// 创建空缓存。 pub fn new() -> Self { Self { templates: DashMap::new() } } + /// 取指定 shape 的属性模板;未缓存时返回 `None`。 pub fn get_template(&self, shape_id: ShapeId) -> Option> { self.templates.get(&shape_id).map(|r| Arc::clone(&*r)) } + /// 无条件写入(覆盖)该 shape 的模板。 pub fn upsert(&self, shape_id: ShapeId, template: PropTemplate) { self.templates.insert(shape_id, Arc::new(template)); } + /// 仅当新模板 `generation` 更高时覆盖已有模板;空缺则直接写入。 pub fn upsert_if_better(&self, shape_id: ShapeId, template: PropTemplate) { use dashmap::mapref::entry::Entry; @@ -44,14 +54,17 @@ impl PropForge { } } + /// 清空全部模板缓存。 pub fn clear(&self) { self.templates.clear(); } + /// 缓存的模板数量。 pub fn len(&self) -> usize { self.templates.len() } + /// 缓存是否为空。 pub fn is_empty(&self) -> bool { self.templates.is_empty() } diff --git a/crates/oxide_kernel/src/shape_forge.rs b/crates/oxide_kernel/src/shape_forge.rs index 4e5b6d7..fed3a1e 100644 --- a/crates/oxide_kernel/src/shape_forge.rs +++ b/crates/oxide_kernel/src/shape_forge.rs @@ -6,12 +6,19 @@ use dashmap::DashMap; use crate::{kernel_debug, kernel_trace}; +/// 隐藏类(shape)的整数标识符。 pub type ShapeId = u32; +/// 属性名在字符串 intern 表([`crate::string_forge::PermInterner`])中的 id。 pub type StringIndex = u32; +/// 空 shape 的保留 id:表示"无属性"的根节点,构造对象时以此为初始 shape。 pub const EMPTY_SHAPE_ID: ShapeId = 1; const EMPTY_SENTINEL: StringIndex = u32::MAX; +/// 单个隐藏类节点:记录新增的属性名、父节点与根到自身的深度。 +/// +/// shape 构成一棵树,从根([`EMPTY_SHAPE_ID`])出发每添加一个属性产生一个子节点; +/// `depth` 即该节点的属性总数。 #[derive(Debug, Clone)] pub struct Shape { pub id: ShapeId, @@ -39,6 +46,7 @@ pub struct ShapeForge { } impl ShapeForge { + /// 创建空 forge,并预置 [`EMPTY_SHAPE_ID`] 根节点。 pub fn new() -> Self { let forge = Self { shapes: RwLock::new(Vec::with_capacity(256)), @@ -62,10 +70,14 @@ impl ShapeForge { forge } + /// 把 `(parent_shape, prop_name)` 打包为单个 u64 key,用作 transition / position 缓存的键。 pub fn pack_key(parent_id: ShapeId, prop_name: StringIndex) -> u64 { ((parent_id as u64) << 32) | (prop_name as u64) } + /// 取(或创建)`parent_id` 加属性 `prop_name` 对应的 shape id,实现哈希一致性。 + /// + /// 相同 `(parent, prop_name)` 组合返回同一 id;超过 24 位上限后转入 overflow map 兜底。 pub fn make_shape(&self, parent_id: ShapeId, prop_name: StringIndex) -> ShapeId { let key = Self::pack_key(parent_id, prop_name); @@ -132,19 +144,25 @@ impl ShapeForge { id } + /// 按 id 取出 shape 节点的 `Arc` 副本;id 不存在时返回 `None`。 pub fn get_shape(&self, id: ShapeId) -> Option> { let shapes = self.shapes.read().unwrap(); shapes.get((id - 1) as usize).and_then(|s| s.clone()) } + /// 当前登记在册的 shape 节点数(含根节点)。 pub fn len(&self) -> usize { self.shapes.read().unwrap().len() } + /// 是否尚无任何 shape(正常情况下始终为 false,因根节点恒存在)。 pub fn is_empty(&self) -> bool { self.len() == 0 } + /// 清空所有非根 shape、transition 与 position 缓存,id 计数器复位。 + /// + /// 用于 session 重建:丢弃一次性对象产生的形状,只保留跨 session 共享的根节点。 pub fn clear_transient(&self) { let mut shapes = self.shapes.write().unwrap(); if shapes.len() > 1 { @@ -155,6 +173,7 @@ impl ShapeForge { self.next_id.store(2, Ordering::Relaxed); } + /// 查询属性在给定 shape 对应对象存储中的槽位下标;沿父链上溯查找并缓存结果。 pub fn lookup_position(&self, shape_id: ShapeId, prop_name: StringIndex) -> Option { let cache_key = Self::pack_key(shape_id, prop_name); if let Some(pos) = self.positions.get(&cache_key) { @@ -186,6 +205,7 @@ impl ShapeForge { None } + /// 判断 shape 或其祖先链上是否存在指定属性。 pub fn has_property(&self, shape_id: ShapeId, prop_name: StringIndex) -> bool { let shapes = self.shapes.read().unwrap(); let mut cursor = Some(shape_id); @@ -203,6 +223,7 @@ impl ShapeForge { false } + /// 返回 shape 的属性数量(即根到该节点的深度);未知 id 返回 0。 pub fn shape_prop_count(&self, shape_id: ShapeId) -> u32 { let shapes = self.shapes.read().unwrap(); shapes diff --git a/crates/oxide_kernel/src/string_forge.rs b/crates/oxide_kernel/src/string_forge.rs index dd15afe..6ee2411 100644 --- a/crates/oxide_kernel/src/string_forge.rs +++ b/crates/oxide_kernel/src/string_forge.rs @@ -46,6 +46,7 @@ pub struct PermInterner { } impl PermInterner { + /// 创建空 intern 表。 pub fn new() -> Self { Self { hash_map: DashMap::new(), @@ -110,6 +111,7 @@ impl PermInterner { self.entries.read().unwrap().len() as u32 } + /// 是否尚无任何 intern 过的 key。 pub fn is_empty(&self) -> bool { self.entry_count() == 0 } diff --git a/crates/oxide_log/src/filter.rs b/crates/oxide_log/src/filter.rs index 154a03d..0178b3a 100644 --- a/crates/oxide_log/src/filter.rs +++ b/crates/oxide_log/src/filter.rs @@ -1,3 +1,9 @@ +//! 按子系统过滤日志事件的 `tracing` 层过滤器。 +//! +//! [`SubsystemFilter`] 为每个子系统保存一个独立的原子级别槽位,并实现 +//! [`Filter`](tracing_subscriber::layer::Filter)。`tracing` 事件的 target +//! (形如 `oxide::vm::...`)先被映射到子系统,再按该子系统当前级别决定是否放行。 + use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::Arc; @@ -7,22 +13,29 @@ use tracing_subscriber::layer::Filter; use crate::level::Level; use crate::subsystem::{SubsystemId, SUBSYSTEM_COUNT}; +/// 按子系统维护日志级别的过滤层。 +/// +/// `Clone` 为浅拷贝(内部 `Arc`),多个 subscriber 层可共享同一份级别状态, +/// 因此 [`set_level`](SubsystemFilter::set_level) 对已初始化的 subscriber 即时生效。 #[derive(Clone)] pub struct SubsystemFilter { levels: Arc<[AtomicU8; SUBSYSTEM_COUNT]>, } impl SubsystemFilter { + /// 创建全部子系统级别为 `Off` 的过滤器。 pub fn new() -> Self { Self { levels: Arc::new([AtomicU8::new(0), AtomicU8::new(0), AtomicU8::new(0), AtomicU8::new(0)]), } } + /// 设置子系统的日志级别(原子写,`Relaxed` 序足够)。 pub fn set_level(&self, id: SubsystemId, level: Level) { self.levels[id as usize].store(level as u8, Ordering::Relaxed); } + /// 读取子系统的当前日志级别。 pub fn get_level(&self, id: SubsystemId) -> Level { let raw = self.levels[id as usize].load(Ordering::Relaxed); match raw { diff --git a/crates/oxide_log/src/init.rs b/crates/oxide_log/src/init.rs index dd125f0..636956d 100644 --- a/crates/oxide_log/src/init.rs +++ b/crates/oxide_log/src/init.rs @@ -1,3 +1,9 @@ +//! 日志初始化。 +//! +//! `init` 一次性初始化全局 `tracing` subscriber,输出可定向到 stderr / stdout / +//! 按日滚动的文件;[`set_level`] 与 `get_subsystem` 提供运行期级别控制。 +//! 重复调用 `init` 幂等(内部 `Once`),后续调用不生效。 + use std::io; use std::path::PathBuf; use std::sync::{Once, OnceLock}; @@ -11,13 +17,21 @@ use crate::filter::SubsystemFilter; use crate::level::Level; use crate::subsystem::{self, SubsystemId, SUBSYSTEM_COUNT}; +/// 日志输出目标。 #[derive(Debug, Clone)] pub enum Output { + /// 写到标准错误(默认)。 Stderr, + /// 写到标准输出。 Stdout, + /// 按日滚动写入 `dir` 目录下的 `oxide.log`。 File(PathBuf), } +/// 日志初始化配置。 +/// +/// `levels` 按 [`SubsystemId`] 的下标顺序给出各子系统初始级别; +/// 默认值为输出到 stderr 且全部子系统关闭。 pub struct LogConfig { pub output: Output, pub levels: [Level; SUBSYSTEM_COUNT], @@ -36,14 +50,26 @@ static INIT: Once = Once::new(); static SUBSYSTEM: OnceLock = OnceLock::new(); static FILE_GUARD: OnceLock = OnceLock::new(); +/// 获取全局 [`SubsystemFilter`],供日志宏在检查级别时使用。 +/// +/// # Panics +/// +/// 若在调用 [`init`] 之前调用会 panic——日志宏必须发生在初始化之后。 pub fn get_subsystem() -> &'static SubsystemFilter { SUBSYSTEM.get().expect("oxide_log::init() must be called before logging") } +/// 运行期调整指定子系统的日志级别。 +/// +/// 直接写全局过滤器,无需重新初始化 subscriber。 pub fn set_level(id: SubsystemId, level: Level) { get_subsystem().set_level(id, level); } +/// 初始化全局日志 subscriber。 +/// +/// 按 [`LogConfig::levels`] 设置各子系统初始级别,再读取 `OXIDE_LOG` 环境变量 +/// 覆盖,最后按 `output` 组装 subscriber。内部以 `Once` 保证只执行一次。 pub fn init(config: &LogConfig) { INIT.call_once(|| { let filter = SubsystemFilter::new(); diff --git a/crates/oxide_log/src/level.rs b/crates/oxide_log/src/level.rs index 5cc8f06..c85bde1 100644 --- a/crates/oxide_log/src/level.rs +++ b/crates/oxide_log/src/level.rs @@ -1,3 +1,13 @@ +//! 日志级别定义。 +//! +//! [`Level`] 是引擎内部使用的日志级别枚举,与 `tracing` 的级别一一对应, +//! 通过 [`Level::as_tracing_level`] 转换。值以 `u8` 存储(`repr(u8)`), +//! 可直接写入 `SubsystemFilter` 的原子级别槽位。 + +/// 日志级别。 +/// +/// 由低到高:`Off` < `Error` < `Warn` < `Info` < `Debug` < `Trace`。 +/// `Off` 表示完全关闭,仅由 `SubsystemFilter` 判读,不映射到 `tracing` 级别。 #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Level { @@ -10,6 +20,11 @@ pub enum Level { } impl Level { + /// 转换为 `tracing` 的 [`tracing::Level`]。 + /// + /// `Off` 与 `Error` 都映射到 `ERROR`——关闭语义由 + /// [`SubsystemFilter`](crate::SubsystemFilter) 的 `Off` 分支单独处理, + /// 这里仅保证其余级别 1:1 对齐。 pub const fn as_tracing_level(self) -> tracing::Level { match self { Level::Off | Level::Error => tracing::Level::ERROR, diff --git a/crates/oxide_log/src/lib.rs b/crates/oxide_log/src/lib.rs index fe6573a..081c99a 100644 --- a/crates/oxide_log/src/lib.rs +++ b/crates/oxide_log/src/lib.rs @@ -1,12 +1,30 @@ +//! OxideJS 统一日志基础设施。 +//! +//! 基于 `tracing` / `tracing-subscriber` 实现,日志按子系统(VM / IC / Kernel / +//! Builtins)独立配置级别。入口为 `init`:传入 [`LogConfig`] 后一次性初始化 +//! 全局 subscriber;运行期可用 [`set_level`] 动态调整各子系统级别。 +//! +//! 环境变量 `OXIDE_LOG` 可在进程启动时覆盖配置,格式与 `RUST_LOG` 类似, +//! 见 `subsystem::apply_env_levels`。 + +/// 按子系统过滤 `tracing` 事件的订阅者过滤器。 pub mod filter; +/// 日志初始化与运行期级别控制入口。 pub mod init; +/// 日志级别定义。 pub mod level; +/// 内部日志转发宏。 pub mod macros; +/// 子系统标识与 `OXIDE_LOG` 环境变量解析。 pub mod subsystem; +/// 重导出 [`filter::SubsystemFilter`]。 pub use filter::SubsystemFilter; +/// 重导出 `init` 模块的初始化入口与配置类型。 pub use init::{init, set_level, LogConfig, Output}; +/// 重导出 [`level::Level`]。 pub use level::Level; +/// 重导出 [`subsystem`] 模块的子系统标识。 pub use subsystem::{SubsystemId, SUBSYSTEM_COUNT}; #[doc(hidden)] diff --git a/crates/oxide_log/src/macros.rs b/crates/oxide_log/src/macros.rs index 6b7453d..4616df5 100644 --- a/crates/oxide_log/src/macros.rs +++ b/crates/oxide_log/src/macros.rs @@ -1,3 +1,8 @@ +//! 内部日志转发宏。 +//! +//! [`__log_event`] 是 `$crate::tracing::event!` 的薄封装,供外部 crate 统一 +//! 转发日志事件,避免各处直接依赖 `tracing` 的路径。 + #[macro_export] macro_rules! __log_event { ($target:expr, $level:expr, $($arg:tt)*) => { diff --git a/crates/oxide_log/src/subsystem.rs b/crates/oxide_log/src/subsystem.rs index a15f69b..959ce2a 100644 --- a/crates/oxide_log/src/subsystem.rs +++ b/crates/oxide_log/src/subsystem.rs @@ -1,16 +1,35 @@ +//! 子系统标识与日志级别解析。 +//! +//! 引擎日志按子系统划分(VM / IC / Kernel / Builtins),各子系统可独立设置 +//! 级别。`parse_level` 把字符串解析为 [`Level`];`apply_env_levels` 读取 +//! `OXIDE_LOG` 环境变量并按 `oxide=` 形式的指令批量覆盖级别。 + use crate::level::Level; +/// 引擎子系统标识。 +/// +/// `repr(usize)` 的值同时用作 [`SubsystemFilter`](crate::SubsystemFilter) +/// 内部原子级别数组的下标,故枚举顺序不可随意调整。 #[repr(usize)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SubsystemId { + /// 虚拟机执行循环(字节码解释器)。 Vm = 0, + /// 内联缓存(inline cache)相关路径。 Ic = 1, + /// 内核:对象模型、GC、运行时服务。 Kernel = 2, + /// 内置对象与内置函数实现。 Builtins = 3, } +/// 子系统总数,即 [`SubsystemId`] 变体个数,也是 `SubsystemFilter` 级别数组长度。 pub const SUBSYSTEM_COUNT: usize = 4; +/// 把级别字符串解析为 [`Level`],大小写不敏感。 +/// +/// 接受 `off` / `error` / `warn` / `info` / `debug` / `trace`,未知字符串返回 +/// `None`。 pub fn parse_level(s: &str) -> Option { if s.eq_ignore_ascii_case("off") { Some(Level::Off) @@ -29,6 +48,11 @@ pub fn parse_level(s: &str) -> Option { } } +/// 从环境变量 `OXIDE_LOG` 应用启动时级别覆盖。 +/// +/// 指令格式为逗号分隔的 `target=level` 列表,如 `oxide::vm=debug,oxide::ic=info`; +/// `oxide=` 为所有子系统的快捷写法。无效指令被静默跳过, +/// 若环境变量缺失则直接返回。 pub fn apply_env_levels() { let Ok(spec) = std::env::var("OXIDE_LOG") else { return; diff --git a/crates/oxide_parser/src/error.rs b/crates/oxide_parser/src/error.rs index 14d71e9..9ce0fc4 100644 --- a/crates/oxide_parser/src/error.rs +++ b/crates/oxide_parser/src/error.rs @@ -1,5 +1,11 @@ +//! 解析错误类型。 +//! +//! [`OxideError`] 把 oxc 的 `OxcDiagnostic` 归一化为引擎可消费的 +//! 消息 + 字节区间,供 `parse` 返回错误列表。 + use std::fmt; +/// 一条解析错误:消息与出错源码区间(`(start, end)` 字节偏移)。 #[derive(Debug, Clone)] pub struct OxideError { pub message: String, diff --git a/crates/oxide_parser/src/lib.rs b/crates/oxide_parser/src/lib.rs index 89f347c..4682fe6 100644 --- a/crates/oxide_parser/src/lib.rs +++ b/crates/oxide_parser/src/lib.rs @@ -2,11 +2,20 @@ mod error; +/// 解析错误:消息 + 源文件字节区间(起始,结束)。 pub use error::OxideError; +/// 重导出 `oxc_allocator::Allocator`:AST 节点的 bump 分配器,解析产物生命周期由其决定。 pub use oxc_allocator::Allocator; +/// 重导出 `oxc_ast::ast` 的全部 AST 节点类型(`Program`、`Statement`、`Expression` 等)。 pub use oxc_ast::ast::*; +/// 重导出 `oxc_span::Span`:源码中的位置区间。 pub use oxc_span::Span; +/// 把 `source` 解析为 AST 根节点 `Program`,AST 分配在 `allocator` 中。 +/// +/// 使用 oxc 默认 `SourceType`(严格模式由语法自身决定)。返回 `Ok(program)` +/// 或 `Err`(收集全部解析错误,按源码顺序排列);若 oxc 内部 panic(不可恢复 +/// 语法错误)则返回单个通用错误。 pub fn parse<'a>(allocator: &'a Allocator, source: &'a str) -> Result, Vec> { use oxc_parser::Parser; use oxc_span::SourceType; diff --git a/crates/oxide_runtime_api/src/lib.rs b/crates/oxide_runtime_api/src/lib.rs index 113b421..efe648b 100644 --- a/crates/oxide_runtime_api/src/lib.rs +++ b/crates/oxide_runtime_api/src/lib.rs @@ -27,14 +27,17 @@ pub enum NativeResult { } impl NativeResult { + /// 构造成功结果,携带一个 `JsValue` 返回值。 pub fn ok(val: JsValue) -> Self { Self::Ok(val) } + /// 构造失败结果,携带被抛出的异常值。 pub fn err(val: JsValue) -> Self { Self::Err(val) } + /// 取出成功值;若为 `Err` 或 `TailCall` 则 panic。仅用于已知必然成功的场景。 pub fn unwrap(self) -> JsValue { match self { Self::Ok(val) => val, @@ -43,6 +46,8 @@ impl NativeResult { } } + /// 把 `Err` 分支的错误值映射为自定义错误类型并转为 `Result`; + /// `TailCall` 不能转换,遇到时 panic。 pub fn map_err(self, op: F) -> Result where F: FnOnce(JsValue) -> E, @@ -121,6 +126,7 @@ pub trait VmHost { fn symbol_key_for_id(&self, idx: u32) -> Option; } +/// 拼接 JS 错误消息,格式为 `"{name}: {msg}"`;任一段为空时只取非空段。 pub fn format_error_message(name: &str, msg: &str) -> String { if name.is_empty() { msg.to_string() @@ -140,6 +146,9 @@ pub unsafe fn string_data(val: JsValue) -> &'static str { (*val.as_string_ptr()).as_str() } +/// 按内容比较两个字符串 `JsValue` 是否相等。 +/// +/// 先做指针级短路(同一 interned 字符串必等),否则逐字节比较 `JsString` 内容。 #[inline] pub fn string_value_eq(a: JsValue, b: JsValue) -> bool { if a.as_string_ptr() == b.as_string_ptr() { @@ -150,6 +159,10 @@ pub fn string_value_eq(a: JsValue, b: JsValue) -> bool { sa.data == sb.data } +/// 对原始值执行 ECMAScript ToNumber 的快速路径(不触发对象 coercion)。 +/// +/// Number/String/Boolean/null 按规范转换;undefined 与不可解析字符串为 `NaN`; +/// Object 与 Symbol 不在此处理,返回 `NaN`(对象需走 [`to_number_full`])。 pub fn to_number(val: JsValue) -> f64 { if val.is_int() { return val.as_int() as f64; @@ -176,6 +189,7 @@ pub fn to_number(val: JsValue) -> f64 { f64::NAN } +/// ToUint32(ECMA-262 §7.1.6):对数值取模 2^32。NaN/±0/Infinity 归零。 pub fn to_uint32(val: JsValue) -> u32 { let n = to_number(val); if n == 0.0 || !n.is_finite() { @@ -184,6 +198,7 @@ pub fn to_uint32(val: JsValue) -> u32 { n.trunc().rem_euclid(4_294_967_296.0) as u32 } +/// ToInt32(ECMA-262 §7.1.5):对数值取模 2^32 后按有符号 32 位解释。 pub fn to_int32(val: JsValue) -> i32 { let int = to_uint32(val); if int > i32::MAX as u32 { @@ -241,6 +256,10 @@ pub fn push_to_string(val: JsValue, buf: &mut String) { } } +/// 把原始值转成字符串(ToString 的原始值路径)。 +/// +/// 数值使用与 V8 一致的格式化(整数直接打印、有限数用 ryu 最短表示); +/// Object 在此返回占位符 `[object]`,完整路径见 [`to_string_full`]。 pub fn to_string(val: JsValue) -> String { if val.is_int() { return val.as_int().to_string(); @@ -281,6 +300,7 @@ pub fn to_string(val: JsValue) -> String { String::new() } +/// ToBoolean(ECMA-262 §7.1.2):falsy 值仅限 undefined/null/false/±0/NaN/空串,其余为 true。 pub fn to_boolean(val: JsValue) -> bool { if val.is_undefined() || val.is_null() { return false; @@ -380,6 +400,9 @@ pub fn abstract_eq(lhs: JsValue, rhs: JsValue, host: &mut H) -> Resul Ok(false) } +/// Strict Equality Comparison(`===`,ECMA-262 §7.2.14)。 +/// +/// 类型不同直接为 false;同类型下 Object 按指针、其余按值比较。 pub fn strict_eq(lhs: JsValue, rhs: JsValue) -> bool { if lhs.is_int() && rhs.is_int() { return lhs.as_int() == rhs.as_int(); @@ -415,6 +438,9 @@ fn strict_double_eq(a: f64, b: f64) -> bool { a == b } +/// Relational Comparison(`<`,ECMA-262 §7.2.13)的原始值版本。 +/// +/// 双字符串按字典序;否则转数值比较,任一侧为 NaN 时返回 `None`(表示比较未定义,调用方据此处理 `<`/`>`)。 pub fn relational_compare(lhs: JsValue, rhs: JsValue) -> Option { if lhs.is_string() && rhs.is_string() { let ls = unsafe { string_data(lhs) }; @@ -429,6 +455,7 @@ pub fn relational_compare(lhs: JsValue, rhs: JsValue) -> Option { l.partial_cmp(&r).map(|o| o.is_lt()) } +/// 拼接两个字符串,按已知总长度预分配容量以避免重复扩容。 pub fn string_concat(lhs: &str, rhs: &str) -> String { let mut s = String::with_capacity(lhs.len() + rhs.len()); s.push_str(lhs); @@ -446,6 +473,7 @@ fn to_f64(val: JsValue) -> f64 { } } +/// SameValue(x, y)(ECMA-262 §7.2.9):与 `===` 的区别在于 NaN 视为相等、+0/-0 视为不同。 pub fn same_value(lhs: JsValue, rhs: JsValue) -> bool { if lhs.is_double() && rhs.is_double() { let a = lhs.as_double(); @@ -527,6 +555,7 @@ pub fn same_value_zero(lhs: JsValue, rhs: JsValue) -> bool { same_value(lhs, rhs) } +/// 与 [`strict_eq`] 等价的规范层实现:双浮点走 NaN 安全比较,其余复用 [`same_value`]。 pub fn strict_equality(lhs: JsValue, rhs: JsValue) -> bool { if lhs.is_double() && rhs.is_double() { return strict_double_eq(lhs.as_double(), rhs.as_double()); @@ -534,6 +563,9 @@ pub fn strict_equality(lhs: JsValue, rhs: JsValue) -> bool { same_value(lhs, rhs) } +/// ToObject(ECMA-262 §7.1.13):null/undefined 抛 TypeError,其余原始值包装为对应包装对象。 +/// +/// 包装对象按类型选择原型(String/Number/Boolean 原型或默认 Object 原型),原始值存入 hash props 槽。 pub fn to_object(val: JsValue, host: &mut H) -> Result { if val.is_object() { return Ok(val); diff --git a/crates/oxide_test262/src/main.rs b/crates/oxide_test262/src/main.rs index 7630c03..c2a2572 100644 --- a/crates/oxide_test262/src/main.rs +++ b/crates/oxide_test262/src/main.rs @@ -17,10 +17,13 @@ use oxide_log::{Level, LogConfig, Output, SUBSYSTEM_COUNT}; // Thread-local that records the path currently being executed. // Written before every test; read by the panic hook to identify the crash file. +/// 记录当前正在执行的测试路径(thread-local);每个测试执行前写入, +/// panic hook 据此定位崩溃所在的测试文件。 std::thread_local! { static CURRENT_TEST_PATH: std::cell::RefCell = const { std::cell::RefCell::new(String::new()) }; } +/// 测试头部 YAML 元数据中的 `negative` 段:声明期望的失败阶段与错误类型。 #[derive(Debug, Deserialize)] #[allow(dead_code)] struct Negative { @@ -29,6 +32,8 @@ struct Negative { error_type: String, } +/// test262 测试文件头部 `/*--- ... ---*/` 段解析出的元数据 +/// (description / flags / includes / features / negative 等)。 #[derive(Debug, Deserialize)] #[allow(dead_code)] struct TestMeta { @@ -50,6 +55,7 @@ struct TestMeta { esid: String, } +/// 单个测试的判定结果:通过 / 失败 / 跳过(各带说明消息)。 #[derive(Debug)] #[allow(dead_code)] enum TestOutcome { @@ -58,6 +64,7 @@ enum TestOutcome { Skip(String), } +/// 单个测试的运行结果:路径、判定与耗时(毫秒)。 #[derive(Debug)] #[allow(dead_code)] struct TestResult { @@ -67,6 +74,7 @@ struct TestResult { } impl TestResult { + /// 构造一个通过结果。 fn pass(path: PathBuf, dur: u64, msg: impl Into) -> Self { Self { path, @@ -75,6 +83,7 @@ impl TestResult { } } + /// 构造一个失败结果。 fn fail(path: PathBuf, dur: u64, msg: impl Into) -> Self { Self { path, @@ -83,6 +92,7 @@ impl TestResult { } } + /// 构造一个跳过结果(不计耗时)。 fn skip(path: PathBuf, msg: String) -> Self { Self { path, @@ -92,6 +102,7 @@ impl TestResult { } } +/// 从测试源码头部解析 `/*--- YAML ---*/` 元数据;无该头部返回 None。 fn parse_meta(source: &str) -> Option { let header_start = source.find("/*---")?; let header = &source[header_start..]; @@ -102,6 +113,7 @@ fn parse_meta(source: &str) -> Option { serde_yaml::from_str::(yaml_body).ok() } +/// 剥离测试源码头部的 YAML 元数据段,返回纯 JS 代码。 fn strip_meta(source: &str) -> &str { if let Some(pos) = source.find("---*/") { return source[pos + 5..].trim_start(); @@ -109,6 +121,7 @@ fn strip_meta(source: &str) -> &str { source } +/// 全部已运行测试的累计统计:通过/失败/跳过计数、总耗时与失败原因分类。 #[derive(Default)] struct RunStats { pass: usize, @@ -146,6 +159,7 @@ impl RunStats { } } +/// 运行配置:test262 根目录、路径过滤器及各选项开关。 #[derive(Debug, Default)] struct RunConfig { test262_root: Option, @@ -156,13 +170,16 @@ struct RunConfig { leak_check_interval: usize, } +/// 内嵌的 test262 harness 辅助脚本注册表(编译期 include_str! 打包)。 struct HarnessSources { sources: HashMap<&'static str, &'static str>, } +/// harness 前缀缓存键:由测试 `includes` 列表唯一确定。 type HarnessPrefixCache = HashMap, String>; impl HarnessSources { + /// 构建 harness 源注册表(键为文件名,值为编译期内嵌源码)。 fn new() -> Self { let mut sources = HashMap::new(); sources.insert("sta.js", include_str!("../../../tests/test262/harness/sta.js")); @@ -196,6 +213,7 @@ impl HarnessSources { Self { sources } } + /// 按文件名取 harness 源码。 fn get(&self, name: &str) -> Option<&'static str> { self.sources.get(name).copied() } @@ -203,6 +221,7 @@ impl HarnessSources { static HARNESS: OnceLock = OnceLock::new(); +/// 判断 harness 文件是否在支持范围之外(依赖 Proxy/Intl/async 等未实现特性)。 fn is_blacklisted_harness(name: &str) -> bool { matches!( name, @@ -224,6 +243,7 @@ fn is_blacklisted_harness(name: &str) -> bool { ) } +/// 生成 `Test262Error` 的 JS prelude(供测试脚本 `assert` 失败时抛出)。 fn test262_error_prelude() -> &'static str { r#" function Test262Error(message) { @@ -235,6 +255,7 @@ Test262Error.prototype.constructor = Test262Error; "# } +/// 向拼接源码追加一段带注释标记的 harness 代码块。 fn append_source_chunk(out: &mut String, name: &str, source: &str) { out.push_str("\n// ---- test262 harness: "); out.push_str(name); @@ -243,10 +264,12 @@ fn append_source_chunk(out: &mut String, name: &str, source: &str) { out.push('\n'); } +/// 取测试元数据的 harness 缓存键(即其 `includes` 列表)。 fn harness_key(meta: &TestMeta) -> Vec { meta.includes.clone() } +/// 按测试元数据拼接完整的 harness 前缀源码(prelude + sta/assert + 各 include)。 fn build_harness_source(meta: &TestMeta, harness: &HarnessSources) -> Result { let mut source = String::new(); append_source_chunk(&mut source, "Test262Error prelude", test262_error_prelude()); @@ -272,6 +295,7 @@ fn build_harness_source(meta: &TestMeta, harness: &HarnessSources) -> Result>, ) -> Result { @@ -289,6 +313,7 @@ fn get_harness_prefix( } impl RunConfig { + /// 默认运行配置。 fn new() -> Self { Self { test262_root: None, @@ -300,6 +325,7 @@ impl RunConfig { } } + /// 解析命令行参数为运行配置;未知选项或参数过多返回错误。 fn parse(args: &[String]) -> Result { let mut config = Self::new(); let mut positional = Vec::new(); @@ -332,6 +358,7 @@ impl RunConfig { Ok(config) } + /// 打印用法说明。 fn usage() -> String { "usage: test262-runner [--no-skip] [--supervise] [--leak-check] [--leak-check-interval=N] [test262-root] [path-filter]\n\ \n\ @@ -350,6 +377,7 @@ impl RunConfig { } } +/// 按测试元数据的 flags/features 判断是否应跳过,返回跳过原因(None 表示不跳过)。 fn is_skipped(meta: &TestMeta) -> Option { for flag in &meta.flags { match flag.as_str() { @@ -394,6 +422,7 @@ fn is_skipped(meta: &TestMeta) -> Option { None } +/// 在 catch_unwind 保护下运行单个测试,把引擎 panic 记为失败。 fn run_test( path: &Path, source: &str, meta: &TestMeta, kernel: &Arc, harness: &HarnessSources, harness_cache: &Arc>, no_skip: bool, @@ -413,6 +442,8 @@ fn run_test( } } +/// 单测执行主流程:拼 harness 前缀 → parse → compile → run; +/// 依据 `negative` 元数据校验期望错误,未实现特性按 no_skip 选择跳过或失败。 fn run_test_inner( path: &Path, source: &str, meta: &TestMeta, kernel: &Arc, harness: &HarnessSources, harness_cache: &Arc>, no_skip: bool, @@ -551,6 +582,7 @@ fn run_test_inner( } } +/// 递归发现 test262 根目录下全部 `.js` 测试文件(排序后返回)。 fn discover_tests(test262_root: &Path) -> Vec { let mut paths: Vec = WalkDir::new(test262_root) .into_iter() @@ -562,6 +594,7 @@ fn discover_tests(test262_root: &Path) -> Vec { paths } +/// 从子进程 stdout 中解析形如 `label : N` 的汇总行。 fn parse_summary_count(stdout: &str, label: &str) -> Option { stdout.lines().find_map(|line| { let trimmed = line.trim_start(); @@ -571,6 +604,7 @@ fn parse_summary_count(stdout: &str, label: &str) -> Option { }) } +/// 分块模式:按 chunk_size 把测试区间切块,逐块以子进程执行并汇总结果。 fn run_chunked(args: &[String], skip_until: usize, end_index: usize, chunk_size: usize) -> bool { let exe = match std::env::current_exe() { Ok(path) => path, @@ -1006,6 +1040,7 @@ fn build_runner_kernel() -> Arc { KernelCore::new(kernel_config) } +/// 把失败消息归类为可聚合的失败类别(compile/vm/parse/harness 等前缀)。 fn categorize_fail(msg: &str) -> String { if msg.contains("compile error:") { let reason = msg.trim_start_matches("compile error: ").trim(); @@ -1058,6 +1093,7 @@ fn categorize_fail(msg: &str) -> String { } } +/// 程序入口:安装带当前测试路径的 panic hook,并在大栈线程上运行测试。 fn main() { // Install a panic hook that prints which test was running when the panic occurred. // This covers Rust panics; OS-level crashes (ACCESS_VIOLATION) are caught by the @@ -1086,6 +1122,8 @@ fn main() { } } +/// 测试主流程:解析配置 → 发现测试 → 按 supervise/chunked/并行 三种模式执行 → +/// 汇总并打印统计;任何失败使返回值为 false(进程退出码 1)。 fn run_tests() -> bool { let args: Vec = std::env::args().collect(); let config = match RunConfig::parse(&args) { diff --git a/crates/oxide_test262/src/test262_log.rs b/crates/oxide_test262/src/test262_log.rs index a3f8874..9e91f00 100644 --- a/crates/oxide_test262/src/test262_log.rs +++ b/crates/oxide_test262/src/test262_log.rs @@ -1,3 +1,4 @@ +/// 输出 ERROR 级别日志,target 固定为 `oxide::test262`。 #[macro_export] macro_rules! test262_error { ($($arg:tt)*) => { @@ -5,6 +6,7 @@ macro_rules! test262_error { }; } +/// 输出 INFO 级别日志,target 固定为 `oxide::test262`。 #[macro_export] macro_rules! test262_info { ($($arg:tt)*) => { @@ -12,6 +14,7 @@ macro_rules! test262_info { }; } +/// 输出 DEBUG 级别日志,target 固定为 `oxide::test262`。 #[macro_export] macro_rules! test262_debug { ($($arg:tt)*) => { diff --git a/crates/oxide_types/src/error.rs b/crates/oxide_types/src/error.rs index f26f21e..fdbf2b4 100644 --- a/crates/oxide_types/src/error.rs +++ b/crates/oxide_types/src/error.rs @@ -1,5 +1,14 @@ +//! 运行时错误类型。 +//! +//! [`JsErrorKind`] 对应 ECMAScript 内置错误类型;[`JsError`] 携带 kind 与消息, +//! 在引擎内作为错误值在栈间传播,可通过 `From for String` 转成消息文本。 + use std::fmt; +/// ECMAScript 内置错误类型。 +/// +/// 对应全局构造函数 `TypeError`、`RangeError`、`ReferenceError` 等, +/// `Display` 输出即为错误名字符串。 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum JsErrorKind { TypeError, @@ -26,6 +35,9 @@ impl fmt::Display for JsErrorKind { } } +/// 一个引擎运行时错误:错误类型 [`kind`](JsError::kind) + 消息文本。 +/// +/// `Display` 输出为 `"{kind}: {message}"` 形式,与 JS `Error#toString()` 一致。 #[derive(Debug, Clone, PartialEq, Eq)] pub struct JsError { pub kind: JsErrorKind, @@ -33,26 +45,32 @@ pub struct JsError { } impl JsError { + /// 构造 [`JsErrorKind::TypeError`]。 pub fn type_error(msg: impl Into) -> Self { Self::new(JsErrorKind::TypeError, msg) } + /// 构造 [`JsErrorKind::RangeError`]。 pub fn range_error(msg: impl Into) -> Self { Self::new(JsErrorKind::RangeError, msg) } + /// 构造 [`JsErrorKind::ReferenceError`]。 pub fn reference_error(msg: impl Into) -> Self { Self::new(JsErrorKind::ReferenceError, msg) } + /// 构造 [`JsErrorKind::SyntaxError`]。 pub fn syntax_error(msg: impl Into) -> Self { Self::new(JsErrorKind::SyntaxError, msg) } + /// 构造无特定类型的 [`JsErrorKind::Error`]。 pub fn generic(msg: impl Into) -> Self { Self::new(JsErrorKind::Error, msg) } + /// 按指定 kind 与消息构造错误。 pub fn new(kind: JsErrorKind, msg: impl Into) -> Self { Self { kind, message: msg.into() } } diff --git a/crates/oxide_types/src/lib.rs b/crates/oxide_types/src/lib.rs index d836122..4a4debc 100644 --- a/crates/oxide_types/src/lib.rs +++ b/crates/oxide_types/src/lib.rs @@ -1,10 +1,17 @@ #![doc = "OxideJS - Shared core types (JsValue, JsObject, Shape, P, Epoch)"] +/// 运行时错误类型([`JsError`] / [`JsErrorKind`])。 pub mod error; +/// 内存抽象:持久指针、arena 分配(epoch)与持久堆。 pub mod mem; +/// 对象模型:字符串、对象头、属性元数据与闭包 cell。 pub mod object; +/// 私有名(`#x`)键编码。 pub mod private_key; +/// 形状(hidden class)存储。 pub mod shape; +/// ECMAScript 值(NaN-boxing 的 `JsValue`)。 pub mod value; +/// 重导出 [`error`] 模块的运行时错误类型。 pub use error::{JsError, JsErrorKind}; diff --git a/crates/oxide_types/src/mem.rs b/crates/oxide_types/src/mem.rs index 0f46a5f..f605dc5 100644 --- a/crates/oxide_types/src/mem.rs +++ b/crates/oxide_types/src/mem.rs @@ -1,3 +1,10 @@ +//! 内存管理抽象:跨 epoch 持久存储与每次调用(agent call)内的 arena 分配。 +//! +//! `P` 是基于 `Arc` 的持久指针,持有者跨 `Epoch::reset()` 存活; +//! `PersistentHeap` 负责把对象"提升"到持久堆;`Epoch` 则是对 +//! `bumpalo::Bump` 的封装,用于每次调用内的高频分配与 O(1) 整体回收, +//! 并通过 epoch ID 辅助悬挂指针检测。 + use std::fmt; use std::ops::Deref; use std::sync::Arc; @@ -11,14 +18,17 @@ use std::sync::Arc; pub struct P(Arc); impl P { + /// 在堆上分配并包装 `value`。 pub fn new(value: T) -> Self { Self(Arc::new(value)) } + /// 返回内部 `Arc` 的底层裸指针。 pub fn as_ptr(&self) -> *const T { Arc::as_ptr(&self.0) } + /// 返回可变裸指针(用于外部 GC / 遍历改写)。 #[inline(always)] pub fn as_mut_ptr(&self) -> *mut T { self.as_ptr() as *mut T @@ -59,6 +69,7 @@ impl fmt::Display for P { pub struct PersistentHeap; impl PersistentHeap { + /// 创建空持久堆(无内部状态,仅提供 `promote`)。 pub fn new() -> Self { Self } @@ -86,6 +97,7 @@ pub struct Epoch { } impl Epoch { + /// 创建从 epoch 0 开始、空 arena 的调用级分配器。 pub fn new() -> Self { Self { bump: bumpalo::Bump::new(), @@ -104,6 +116,9 @@ impl Epoch { self.bump.alloc(value) } + /// 直接访问底层 `bumpalo::Bump`。 + /// + /// 供需要依赖 bumpalo API(如 `alloc_slice`)的调用方使用。 pub fn bump(&self) -> &bumpalo::Bump { &self.bump } diff --git a/crates/oxide_types/src/object.rs b/crates/oxide_types/src/object.rs index e2845b6..1a6bf85 100644 --- a/crates/oxide_types/src/object.rs +++ b/crates/oxide_types/src/object.rs @@ -1,3 +1,11 @@ +//! 对象模型:字符串、对象、属性元数据、闭包 cell 与原生函数指针。 +//! +//! `JsObject` 为定长 `repr(C)` 结构(112 字节 + 对齐),核心字段内联在 +//! header 位域中;dense 属性向量、属性元数据与 upvalue cell 列表通过裸指针 +//! 挂在堆上,由 VM / GC 负责生命周期。本模块同时定义属性标志 +//! (`PropAttributes`)、访问器/数据属性元数据 (`PropMetaEntry`) 与 +//! 闭包共享 cell (`Cell`)。 + use crate::value::JsValue; /// Heap-allocated JS string value. @@ -10,18 +18,22 @@ pub struct JsString { } impl JsString { + /// 用 UTF-8 数据构造字符串。 pub fn new(data: String) -> Self { Self { data } } + /// 字符串的字节长度(非字符数)。 pub fn len(&self) -> usize { self.data.len() } + /// 是否为空字符串。 pub fn is_empty(&self) -> bool { self.data.is_empty() } + /// 底层 UTF-8 切片。 pub fn as_str(&self) -> &str { self.data.as_str() } @@ -66,9 +78,13 @@ impl NativeFnPtr { unsafe impl Send for NativeFnPtr {} unsafe impl Sync for NativeFnPtr {} +/// 形状标识符(对象 header 低位 24 位)。 pub type ShapeId = u32; + +/// dense 属性向量长度的硬上限,防止索引失控导致内存膨胀。 pub const MAX_DENSE_PROPS: usize = 1_000_000; +/// TypedArray 的元素类型,决定 `bytes_per_element` 与内存视图的字节序解读。 #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TypedArrayKind { Int8, @@ -85,6 +101,7 @@ pub enum TypedArrayKind { } impl TypedArrayKind { + /// 每个元素的字节数(1/2/4/8)。 pub const fn bytes_per_element(self) -> usize { match self { Self::Int8 | Self::Uint8 | Self::Uint8Clamped => 1, @@ -95,15 +112,23 @@ impl TypedArrayKind { } } +/// 属性描述符标志位集合,压缩在单个 `u8` 中。 +/// +/// 位定义:bit0 = writable,bit1 = enumerable,bit2 = configurable。 #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct PropAttributes(pub u8); impl PropAttributes { + /// writable 标志位(`0b001`)。 pub const WRITABLE: u8 = 0b001; + /// enumerable 标志位(`0b010`)。 pub const ENUMERABLE: u8 = 0b010; + /// configurable 标志位(`0b100`)。 pub const CONFIGURABLE: u8 = 0b100; + /// 数据属性默认描述符:三标志全开。 pub const DEFAULT_DATA: Self = Self(Self::WRITABLE | Self::ENUMERABLE | Self::CONFIGURABLE); + /// 由三个布尔标志构造描述符。 pub const fn new(writable: bool, enumerable: bool, configurable: bool) -> Self { let mut bits = 0; if writable { @@ -118,19 +143,26 @@ impl PropAttributes { Self(bits) } + /// 是否 writable。 pub const fn writable(self) -> bool { self.0 & Self::WRITABLE != 0 } + /// 是否 enumerable。 pub const fn enumerable(self) -> bool { self.0 & Self::ENUMERABLE != 0 } + /// 是否 configurable。 pub const fn configurable(self) -> bool { self.0 & Self::CONFIGURABLE != 0 } } +/// 单个属性的元数据条目。 +/// +/// 数据属性仅用 `attributes`;访问器属性额外携带 getter / setter +/// 的 [`JsValue`] 与 `is_accessor = true` 标记。 #[derive(Clone, Copy, Debug, PartialEq)] pub struct PropMetaEntry { pub attributes: PropAttributes, @@ -140,6 +172,7 @@ pub struct PropMetaEntry { } impl PropMetaEntry { + /// 构造数据属性条目。 pub fn data(attributes: PropAttributes) -> Self { Self { attributes, @@ -149,6 +182,7 @@ impl PropMetaEntry { } } + /// 构造访问器属性条目(getter/setter 可为 `undefined`)。 pub fn accessor(get: JsValue, set: JsValue, attributes: PropAttributes) -> Self { Self { attributes, @@ -159,6 +193,10 @@ impl PropMetaEntry { } } +/// 可转换为 dense 属性下标的值。 +/// +/// 为 `u8` / `u16` / `u32` / `usize` / 非负 `i32` 实现,统一属性向量 +/// 下标参数的类型。 pub trait PropIndex { fn to_u32(self) -> u32; } @@ -196,30 +234,30 @@ impl PropIndex for i32 { /// Layout: /// header: u32 bits -/// [0:23] shape_id -/// [24] is_set -/// [25] is_map -/// [26] is_derived_constructor -/// [27] is_class_constructor -/// [28] is_arrow -/// [29] is_array -/// [30] is_extensible -/// [31] is_function +/// \[0:23\] shape_id +/// \[24\] is_set +/// \[25\] is_map +/// \[26\] is_derived_constructor +/// \[27\] is_class_constructor +/// \[28\] is_arrow +/// \[29\] is_array +/// \[30\] is_extensible +/// \[31\] is_function /// native_arg_count: u8 (1 byte) /// type_tag: u8 — OBJ_TYPE_* constant identifying wrapper/exotic object kind (1 byte) /// is_session_epoch: u8 (1 byte) /// _pad: u8 -/// hash_props: *mut u8 (8 bytes, points to Box>) -/// prop_meta: *mut u8 (8 bytes, points to Box>>) +/// hash_props: *mut u8 (8 bytes, points to Box\\>) +/// prop_meta: *mut u8 (8 bytes, points to Box\\>\>) /// native_data: *mut u8 (8 bytes, opaque VM-owned native/exotic payload) /// proto: JsValue (8 bytes) /// generation: u32 (4 bytes + 4 pad) -/// native_fn: Option (16 bytes — Option optimization NOT available for +/// native_fn: Option\ (16 bytes — Option\ optimization NOT available for /// raw *const (); stored as Option wrapping an 8-byte pointer, with 8 bytes of /// discriminant padding due to repr(Rust) layout rules) /// sub_module_index: u32 (4 bytes + 4 pad, index into CompiledModule.sub_modules) /// captured_this: JsValue (8 bytes, lexical this for arrow functions) -/// home_object: JsValue (8 bytes, [[HomeObject]] for super lookup) +/// home_object: JsValue (8 bytes, \[\[HomeObject\]\] for super lookup) /// upvalues: *mut u8 (8 bytes, points to Box> for closures) /// /// Total: 112 bytes @@ -232,9 +270,12 @@ pub struct Cell { } impl Cell { + /// 已初始化标志(TDZ / 未赋值检测)。 pub const INITIALIZED: u8 = 0x01; + /// GC 标记位。 pub const GC_MARK: u8 = 0x02; + /// 构造 cell,`initialized` 决定是否立即置 [`INITIALIZED`](Cell::INITIALIZED) 位。 pub fn new(value: JsValue, initialized: bool) -> Self { Cell { value, @@ -243,10 +284,12 @@ impl Cell { } } + /// 是否已初始化。 pub fn is_initialized(&self) -> bool { self.flags & Self::INITIALIZED != 0 } + /// 设置 / 清除已初始化标志。 pub fn set_initialized(&mut self, val: bool) { if val { self.flags |= Self::INITIALIZED; @@ -255,10 +298,12 @@ impl Cell { } } + /// 是否被 GC 标记。 pub fn is_gc_marked(&self) -> bool { self.flags & Self::GC_MARK != 0 } + /// 设置 / 清除 GC 标记。 pub fn set_gc_mark(&mut self, marked: bool) { if marked { self.flags |= Self::GC_MARK; @@ -268,6 +313,13 @@ impl Cell { } } +/// 定长对象头 + 堆外数据指针的 JS 普通/外来对象。 +/// +/// 内联字段:`header`(shape_id + 一组标志位)、`type_tag`(外来对象种类)、 +/// `proto`、`generation` 等;dense 属性向量、属性元数据、native payload 与 +/// upvalue cell 列表以裸指针挂在堆上,由 VM / GC 维护。对象可分配在 +/// session arena(`Epoch`)或持久堆(`PersistentHeap`),通过 +/// `is_session_epoch` 位区分。 pub struct JsObject { header: u32, native_arg_count: u8, @@ -289,51 +341,71 @@ pub struct JsObject { } impl JsObject { + /// 普通对象类型标签。 pub const OBJ_TYPE_PLAIN: u8 = 0; + /// Date 对象类型标签。 pub const OBJ_TYPE_DATE: u8 = 1; + /// RegExp 对象类型标签。 pub const OBJ_TYPE_REGEXP: u8 = 2; + /// 装箱 Boolean 对象类型标签。 pub const OBJ_TYPE_BOOLEAN_OBJ: u8 = 3; + /// 装箱 Number 对象类型标签。 pub const OBJ_TYPE_NUMBER_OBJ: u8 = 4; + /// 装箱 String 对象类型标签。 pub const OBJ_TYPE_STRING_OBJ: u8 = 5; + /// ArrayBuffer 对象类型标签。 pub const OBJ_TYPE_ARRAY_BUFFER: u8 = 6; + /// DataView 对象类型标签。 pub const OBJ_TYPE_DATA_VIEW: u8 = 7; + /// TypedArray 对象类型标签。 pub const OBJ_TYPE_TYPED_ARRAY: u8 = 8; + /// `is_session_epoch` 字段中的 session 标记位。 pub const SESSION_EPOCH_BIT: u8 = 0x01; + /// `is_session_epoch` 字段中的 GC 标记位。 pub const GC_MARK_BIT: u8 = 0x02; + /// 是否 Date 外来对象。 #[inline] pub fn is_date_obj(&self) -> bool { self.type_tag == Self::OBJ_TYPE_DATE } + /// 是否 RegExp 外来对象。 #[inline] pub fn is_regexp_obj(&self) -> bool { self.type_tag == Self::OBJ_TYPE_REGEXP } + /// 是否装箱 Boolean 对象。 #[inline] pub fn is_boolean_obj(&self) -> bool { self.type_tag == Self::OBJ_TYPE_BOOLEAN_OBJ } + /// 是否装箱 Number 对象。 #[inline] pub fn is_number_obj(&self) -> bool { self.type_tag == Self::OBJ_TYPE_NUMBER_OBJ } + /// 是否装箱 String 对象。 #[inline] pub fn is_string_obj(&self) -> bool { self.type_tag == Self::OBJ_TYPE_STRING_OBJ } + /// 是否 ArrayBuffer 对象。 #[inline] pub fn is_array_buffer_obj(&self) -> bool { self.type_tag == Self::OBJ_TYPE_ARRAY_BUFFER } + /// 是否 DataView 对象。 #[inline] pub fn is_data_view_obj(&self) -> bool { self.type_tag == Self::OBJ_TYPE_DATA_VIEW } + /// 是否 TypedArray 对象。 #[inline] pub fn is_typed_array_obj(&self) -> bool { self.type_tag == Self::OBJ_TYPE_TYPED_ARRAY } + /// 构造无属性、可扩展的空对象(`new Object()` 的基础对象)。 pub fn new_empty(shape_id: ShapeId, proto: JsValue) -> Self { Self { header: (shape_id & 0x00FF_FFFF) | (1 << 30), @@ -356,6 +428,7 @@ impl JsObject { } } + /// 构造数组对象:预分配 `n_elements` 个 `undefined` 的 dense 向量并置 array 标志。 pub fn new_array(shape_id: ShapeId, proto: JsValue, n_elements: usize, _bump: &bumpalo::Bump) -> Self { let mut obj = Self { header: (shape_id & 0x00FF_FFFF) | (1 << 30) | (1 << 29), @@ -381,11 +454,13 @@ impl JsObject { obj } + /// 是否 session-epoch 分配(调用期内分配,调用结束即失效)。 #[inline] pub fn is_session_epoch(&self) -> bool { self.is_session_epoch & Self::SESSION_EPOCH_BIT != 0 } + /// 设置 / 清除 session-epoch 标记。 #[inline] pub fn set_session_epoch(&mut self, value: bool) { if value { @@ -395,11 +470,13 @@ impl JsObject { } } + /// 是否被 GC 标记。 #[inline] pub fn is_gc_marked(&self) -> bool { self.is_session_epoch & Self::GC_MARK_BIT != 0 } + /// 设置 / 清除 GC 标记。 #[inline] pub fn set_gc_mark(&mut self, marked: bool) { if marked { @@ -409,6 +486,9 @@ impl JsObject { } } + /// 深拷贝本对象到 session epoch:复制属性向量与元数据,标记为新 session 对象。 + /// + /// 用于把持久对象快照进当前调用上下文,修改不反向传播到源对象。 pub fn clone_for_session_epoch(&self) -> Self { let hash_props = self .hash_props_vec() @@ -440,22 +520,27 @@ impl JsObject { } } + /// dense 属性向量底层指针(未分配时为空指针)。 pub fn hash_props_raw(&self) -> *mut u8 { self.hash_props } + /// 属性元数据向量底层指针(未分配时为空指针)。 pub fn prop_meta_raw(&self) -> *mut u8 { self.prop_meta } + /// 原生 / 外来对象 payload 指针。 pub fn native_data(&self) -> *mut u8 { self.native_data } + /// 设置原生 / 外来对象 payload 指针。 pub fn set_native_data(&mut self, ptr: *mut u8) { self.native_data = ptr; } + /// 闭包 upvalue cell 列表切片(未设置时为空切片)。 pub fn upvalues_slice(&self) -> &[*mut Cell] { if self.upvalues.is_null() { &[] @@ -464,6 +549,7 @@ impl JsObject { } } + /// 可变 upvalue cell 列表切片(未设置时为空切片)。 pub fn upvalues_slice_mut(&mut self) -> &mut [*mut Cell] { if self.upvalues.is_null() { &mut [] @@ -472,6 +558,7 @@ impl JsObject { } } + /// 替换 upvalue cell 列表(释放旧列表)。 pub fn set_upvalues(&mut self, v: Box>) { if !self.upvalues.is_null() { unsafe { @@ -481,6 +568,11 @@ impl JsObject { self.upvalues = Box::into_raw(v) as *mut u8; } + /// 用 `rewrite` 改写对象内引用的所有对象值。 + /// + /// 用于 GC 移动 / 世代晋升:遍历 dense 属性、访问器 getter/setter、 + /// `proto`、`captured_this`、`home_object` 与 upvalue cell 中的对象值, + /// 原地替换为新地址。非对象值保持不变。 pub fn rewrite_object_values(&mut self, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, @@ -522,10 +614,12 @@ impl JsObject { } } + /// 当前形状 ID(header 低位 24 位)。 pub fn shape_id(&self) -> ShapeId { self.header & 0x00FF_FFFF } + /// 设置形状 ID(低位 24 位,保留其它标志位)。 pub fn set_shape_id(&mut self, id: ShapeId) { self.header = (self.header & !0x00FF_FFFF) | (id & 0x00FF_FFFF); } @@ -567,6 +661,7 @@ impl JsObject { } } + /// 是否已分配属性元数据向量。 pub fn has_prop_meta(&self) -> bool { !self.prop_meta.is_null() } @@ -598,6 +693,9 @@ impl JsObject { } } + /// 确保属性元数据向量已分配并返回可变引用。 + /// + /// 初始长度与当前 dense 属性数对齐,全部初始化为 `None`(无元数据)。 pub fn ensure_prop_meta(&mut self) -> &mut Vec> { if self.prop_meta.is_null() { let len = self.prop_vec_len(); @@ -609,6 +707,7 @@ impl JsObject { unsafe { &mut *(self.prop_meta as *mut Vec>) } } + /// 只读访问属性元数据向量(未分配时返回 `None`)。 pub fn prop_meta_vec(&self) -> Option<&Vec>> { if self.prop_meta.is_null() { None @@ -629,21 +728,25 @@ impl JsObject { } } + /// 读取指定下标属性的元数据;无元数据或越界返回 `None`。 pub fn prop_meta_at(&self, position: impl PropIndex) -> Option { let pos = position.to_u32() as usize; self.prop_meta_vec().and_then(|vec| vec.get(pos).copied().flatten()) } + /// 设置指定下标属性的数据属性描述符。 pub fn set_data_meta(&mut self, position: impl PropIndex, attributes: PropAttributes) { self.set_meta_at(position, PropMetaEntry::data(attributes)); } + /// 设置指定下标属性的访问器描述符(getter/setter)。 pub fn set_accessor_meta( &mut self, position: impl PropIndex, get: JsValue, set: JsValue, attributes: PropAttributes, ) { self.set_meta_at(position, PropMetaEntry::accessor(get, set, attributes)); } + /// 判断指定下标的属性是否为访问器属性。 pub fn is_accessor_meta(&self, position: impl PropIndex) -> bool { self.prop_meta_at(position).is_some_and(|entry| entry.is_accessor) } @@ -661,14 +764,17 @@ impl JsObject { meta[pos] = Some(entry); } + /// 是否数组(header bit 29)。 pub fn is_array(&self) -> bool { (self.header >> 29) & 1 != 0 } + /// 是否可扩展(header bit 30,与 `Object.preventExtensions` 对应)。 pub fn is_extensible(&self) -> bool { (self.header >> 30) & 1 != 0 } + /// 设置可扩展标志。 pub fn set_extensible(&mut self, ext: bool) { if ext { self.header |= 1 << 30; @@ -677,10 +783,12 @@ impl JsObject { } } + /// 是否冻结(`Object.freeze`,存在访问器/描述符时仍由属性级 configurable 约束)。 pub fn is_frozen(&self) -> bool { (self._pad) & 1 != 0 } + /// 设置冻结标志。 pub fn set_frozen(&mut self, frozen: bool) { if frozen { self._pad |= 1 << 0; @@ -689,10 +797,12 @@ impl JsObject { } } + /// 是否密封(`Object.seal`)。 pub fn is_sealed(&self) -> bool { (self._pad >> 1) & 1 != 0 } + /// 设置密封标志。 pub fn set_sealed(&mut self, sealed: bool) { if sealed { self._pad |= 1 << 1; @@ -701,10 +811,12 @@ impl JsObject { } } + /// 是否 Set 实例(header bit 24)。 pub fn is_set(&self) -> bool { (self.header >> 24) & 1 != 0 } + /// 设置 Set 实例标志。 pub fn set_set(&mut self, s: bool) { if s { self.header |= 1 << 24; @@ -713,10 +825,12 @@ impl JsObject { } } + /// 是否 Map 实例(header bit 25)。 pub fn is_map(&self) -> bool { (self.header >> 25) & 1 != 0 } + /// 设置 Map 实例标志。 pub fn set_map(&mut self, m: bool) { if m { self.header |= 1 << 25; @@ -725,10 +839,12 @@ impl JsObject { } } + /// 是否函数对象(header bit 31)。 pub fn is_function(&self) -> bool { (self.header >> 31) & 1 != 0 } + /// 设置函数对象标志。 pub fn set_function(&mut self, f: bool) { if f { self.header |= 1 << 31; @@ -822,10 +938,15 @@ impl JsObject { } } + /// 当前 `[[Prototype]]` 值。 pub fn proto(&self) -> JsValue { self.proto } + /// 设置 `[[Prototype]]`,成功时递增 generation。 + /// + /// 仅接受 `null` 或对象;沿新原型链检查是否构成环,成环返回 + /// `Err("cyclic __proto__ value")`。 pub fn set_proto(&mut self, proto: JsValue) -> Result<(), &'static str> { if proto.is_null() { self.proto = proto; @@ -852,34 +973,42 @@ impl JsObject { Ok(()) } + /// 当前 generation(对象结构变更计数器,IC 失效依据之一)。 pub fn generation(&self) -> u32 { self.generation } + /// 递增 generation(`wrapping_add`)。 pub fn bump_generation(&mut self) { self.generation = self.generation.wrapping_add(1); } + /// 关联的原生函数指针(若为内建/原生函数对象)。 pub fn native_fn(&self) -> Option { self.native_fn } + /// 设置 / 清除原生函数指针。 pub fn set_native_fn(&mut self, ptr: Option) { self.native_fn = ptr; } + /// 原生函数声明的参数个数。 pub fn native_arg_count(&self) -> u8 { self.native_arg_count } + /// 设置原生函数参数个数。 pub fn set_native_arg_count(&mut self, n: u8) { self.native_arg_count = n; } + /// 子模块下标(函数对象对应的 `CompiledModule` 索引)。 pub fn sub_module_index(&self) -> u32 { self.sub_module_index } + /// 设置子模块下标。 pub fn set_sub_module_index(&mut self, idx: u32) { self.sub_module_index = idx; } @@ -890,6 +1019,7 @@ impl JsObject { (self.header >> 28) & 1 != 0 } + /// 设置箭头函数标志(header bit 28)。 pub fn set_arrow(&mut self, v: bool) { if v { self.header |= 1 << 28; @@ -904,6 +1034,7 @@ impl JsObject { self.captured_this } + /// 设置箭头函数捕获的词法 `this`。 pub fn set_captured_this(&mut self, v: JsValue) { self.captured_this = v; } @@ -914,6 +1045,7 @@ impl JsObject { (self.header >> 27) & 1 != 0 } + /// 设置类构造函数标志(header bit 27)。 pub fn set_class_constructor(&mut self, v: bool) { if v { self.header |= 1 << 27; @@ -922,10 +1054,12 @@ impl JsObject { } } + /// 是否派生类构造函数(有 `extends` 子句,header bit 26)。 pub fn is_derived_constructor(&self) -> bool { (self.header >> 26) & 1 != 0 } + /// 设置派生类构造函数标志(header bit 26)。 pub fn set_derived_constructor(&mut self, v: bool) { if v { self.header |= 1 << 26; @@ -934,10 +1068,12 @@ impl JsObject { } } + /// 当前 `[[HomeObject]]`(供 `super` 属性访问解析)。 pub fn home_object(&self) -> JsValue { self.home_object } + /// 设置 `[[HomeObject]]`。 pub fn set_home_object(&mut self, v: JsValue) { self.home_object = v; } diff --git a/crates/oxide_types/src/private_key.rs b/crates/oxide_types/src/private_key.rs index 7103630..94e3b91 100644 --- a/crates/oxide_types/src/private_key.rs +++ b/crates/oxide_types/src/private_key.rs @@ -1,10 +1,24 @@ +//! 私有名(private name)键编码。 +//! +//! ECMAScript 私有字段(`#x`)与 `Symbol` 使用全局注册符号以外的独立命名空间。 +//! 本模块用 `u32` 键的高半区(`>= 0x8000_0000`)表示私有名,与普通属性名区分, +//! 避免与用户可见的字符串键冲突。 + +/// 私有名键区间的起始值。 +/// +/// 键值 `>= PRIVATE_NAME_BASE` 一律视为私有名。 pub const PRIVATE_NAME_BASE: u32 = 0x8000_0000; +/// 判断键是否为私有名键(即位于高半区)。 #[inline] pub const fn is_private_name_key(key: u32) -> bool { key >= PRIVATE_NAME_BASE } +/// 把类内的局部私有名序号编码为全局唯一键。 +/// +/// 将 `local_id` 置入高半区(按位或 `PRIVATE_NAME_BASE`), +/// 保留低 31 位原始值。 #[inline] pub const fn make_private_name_id(local_id: u32) -> u32 { PRIVATE_NAME_BASE | (local_id & !PRIVATE_NAME_BASE) diff --git a/crates/oxide_types/src/shape.rs b/crates/oxide_types/src/shape.rs index 4f0697f..0ab4318 100644 --- a/crates/oxide_types/src/shape.rs +++ b/crates/oxide_types/src/shape.rs @@ -1,13 +1,29 @@ +//! 隐藏类(hidden class / shape)存储。 +//! +//! 形状表是对象属性布局的可共享描述:每次往对象添加一个命名属性就沿链增长一个 +//! shape。形状按 `(parent_id, prop_name)` 哈希一致化(hash-cons),使结构相同的 +//! 对象共享同一 shape,从而支持属性位置缓存(inline cache)。全局单例 +//! `SHAPE_STORE` 以互斥锁保护,见 `get_shape` 等自由函数。 + use std::sync::{Arc, Mutex, OnceLock}; use hashbrown::HashMap; +/// 形状标识符,作为对象 header 中 24 位属性存储。 pub type ShapeId = u32; +/// 属性名字符串索引(进入字符串表的下标)。 pub type StringIndex = u32; +/// 空形状(无任何属性)的固定 ID。 +/// +/// 所有对象至少从空形状出发,`ShapeStore` 构造时即预置。 pub const EMPTY_SHAPE_ID: ShapeId = 1; const EMPTY_SENTINEL: StringIndex = u32::MAX; +/// 单个形状:一次属性添加对应的节点。 +/// +/// `property_name` 为本节点新增的属性名(空形状为 `EMPTY_SENTINEL`), +/// `parent` 指向前一个形状,形成从当前形状到空形状的属性链。 #[derive(Debug, Clone)] pub struct Shape { pub id: ShapeId, @@ -143,22 +159,31 @@ fn store() -> &'static Mutex { SHAPE_STORE.get_or_init(|| Mutex::new(ShapeStore::new())) } +/// 按 ID 获取形状,不存在则返回 `None`。 pub fn get_shape(id: ShapeId) -> Option> { store().lock().unwrap().get_shape(id) } +/// 获取或创建 `(parent_id, prop_name)` 对应的形状 ID(hash-cons)。 +/// +/// 同一 `(parent, prop_name)` 键返回同一 ID;首次出现时分配新 ID 并沿链挂接。 pub fn make_shape(parent_id: ShapeId, prop_name: StringIndex) -> ShapeId { store().lock().unwrap().make_shape(parent_id, prop_name) } +/// 在形状的属性链中查找 `prop_name` 的位置(从父向子的深度编号)。 +/// +/// 位置即该属性在 dense 属性向量中的下标:深度 0 表示链中最老的属性。 pub fn lookup_position(shape_id: ShapeId, prop_name: StringIndex) -> Option { store().lock().unwrap().lookup_position(shape_id, prop_name) } +/// 判断形状链中是否包含 `prop_name`。 pub fn has_property(shape_id: ShapeId, prop_name: StringIndex) -> bool { store().lock().unwrap().has_property(shape_id, prop_name) } +/// 返回形状链的属性数量(空形状计 0)。 pub fn shape_prop_count(shape_id: ShapeId) -> u32 { store().lock().unwrap().shape_prop_count(shape_id) } diff --git a/crates/oxide_types/src/value.rs b/crates/oxide_types/src/value.rs index 9655ae8..aca605e 100644 --- a/crates/oxide_types/src/value.rs +++ b/crates/oxide_types/src/value.rs @@ -1,3 +1,10 @@ +//! ECMAScript 值表示:NaN-boxing 的 64 位 `JsValue`。 +//! +//! 普通 double 直接以 IEEE-754 位模式存放;其余类型用静默 NaN 前缀 +//! `0xFFF8_0000_0000_0000` 加上 3 位 tag(bits 50-48)区分 int / bool / +//! null / undefined / object / string / symbol。对象与字符串存 48 位指针, +//! 使一个 `JsValue` 可塞进寄存器且类型判断为常数时间。 + use std::fmt; use crate::object::{JsObject, JsString}; @@ -36,6 +43,11 @@ fn get_tag(bits: u64) -> u64 { (bits & TAG_MASK) >> TAG_SHIFT } +/// 统一 ECMAScript 值:一个 NaN-boxed 的 64 位字。 +/// +/// `repr(transparent)` 包裹单个 `u64`。double 原样编码;非 double 类型 +/// 使用 NaN 前缀 + tag + payload。`Copy`,寄存器宽度,是引擎栈与对象 +/// 属性向量的基本元素。字符串/对象按指针恒等比较,内容比较交给上层。 #[repr(transparent)] #[derive(Copy, Clone)] pub struct JsValue(u64); @@ -87,10 +99,15 @@ impl JsValue { self.0 } + /// 构造 32 位整数(tag = int,payload 为 `i32` 位模式)。 pub fn int(v: i32) -> Self { Self(make_tag(TAG_INT) | (v as u32 as u64)) } + /// 构造双精度浮点。 + /// + /// NaN 会被规范化为引擎内唯一的安静 NaN 编码,保证 + /// `float(x).as_double()` 幂等且 `PartialEq` 对 NaN 恒为 false。 pub fn float(v: f64) -> Self { let bits = v.to_bits(); if is_nan_bits(bits) { @@ -100,24 +117,33 @@ impl JsValue { } } + /// 构造布尔值。 pub fn bool(v: bool) -> Self { Self(make_tag(TAG_BOOL) | (v as u64)) } + /// 构造 `null`。 pub fn null() -> Self { Self(make_tag(TAG_NULL)) } + /// 构造 `undefined`。 pub fn undefined() -> Self { Self(make_tag(TAG_UNDEFINED)) } + /// 构造对象引用(48 位指针 + object tag)。 + /// + /// # Panics (debug) + /// + /// debug 构建下若指针超出 48 位地址空间会 panic。 pub fn object(ptr: *const u8) -> Self { let addr = ptr as u64; debug_assert!(addr <= PTR_MASK, "object pointer must fit in 48 bits"); Self(make_tag(TAG_OBJECT) | addr) } + /// 构造字符串引用(指向 [`JsString`] 的 48 位指针 + string tag)。 pub fn string(ptr: *const JsString) -> Self { let addr = ptr as u64; debug_assert!(addr <= PTR_MASK, "string pointer must fit in 48 bits"); @@ -131,38 +157,49 @@ impl JsValue { Self::string(ptr) } + /// 是否为普通双精度浮点(非 NaN-box 编码)。 pub fn is_double(&self) -> bool { !is_nan_boxed(self.0) } + /// 是否为 32 位整数。 pub fn is_int(&self) -> bool { is_nan_boxed(self.0) && get_tag(self.0) == TAG_INT } + /// 是否为布尔值。 pub fn is_bool(&self) -> bool { is_nan_boxed(self.0) && get_tag(self.0) == TAG_BOOL } + /// 是否为 `null`。 pub fn is_null(&self) -> bool { is_nan_boxed(self.0) && get_tag(self.0) == TAG_NULL } + /// 是否为 `undefined`。 pub fn is_undefined(&self) -> bool { is_nan_boxed(self.0) && get_tag(self.0) == TAG_UNDEFINED } + /// 是否为 `null` 或 `undefined`(`??` 与可选链的判据)。 pub fn is_nullish(&self) -> bool { self.is_null() || self.is_undefined() } + /// 是否为对象引用。 pub fn is_object(&self) -> bool { is_nan_boxed(self.0) && get_tag(self.0) == TAG_OBJECT } + /// 是否为字符串引用。 pub fn is_string(&self) -> bool { is_nan_boxed(self.0) && get_tag(self.0) == TAG_STRING } + /// 解出双精度值。 + /// + /// debug 构建下断言必须是 double;release 下非法时返回 `NaN`。 pub fn as_double(&self) -> f64 { debug_assert!(self.is_double(), "JsValue is not a double"); #[cfg(not(debug_assertions))] @@ -172,6 +209,9 @@ impl JsValue { f64::from_bits(self.0) } + /// 解出 32 位整数。 + /// + /// debug 构建下断言必须是 int;release 下非法时返回 0。 pub fn as_int(&self) -> i32 { debug_assert!(self.is_int(), "JsValue is not an int"); #[cfg(not(debug_assertions))] @@ -181,6 +221,9 @@ impl JsValue { (self.0 & INT_MASK) as i32 } + /// 解出布尔值。 + /// + /// debug 构建下断言必须是 bool;release 下非法时返回 false。 pub fn as_bool(&self) -> bool { debug_assert!(self.is_bool(), "JsValue is not a bool"); #[cfg(not(debug_assertions))] @@ -190,6 +233,7 @@ impl JsValue { (self.0 & 1) != 0 } + /// 解出对象底层指针;非对象时返回空指针。 pub fn as_ptr(&self) -> *const u8 { if !self.is_object() { return std::ptr::null(); @@ -197,6 +241,7 @@ impl JsValue { (self.0 & PTR_MASK) as *const u8 } + /// 解出可变对象底层指针;非对象时返回空指针。 pub fn as_object_ptr(&self) -> *mut u8 { if !self.is_object() { return std::ptr::null_mut(); @@ -204,6 +249,7 @@ impl JsValue { (self.0 & PTR_MASK) as *mut u8 } + /// 解出 `JsObject` 指针;非对象时返回空指针。 pub fn as_js_object_ptr(&self) -> *mut JsObject { if !self.is_object() { return std::ptr::null_mut(); @@ -211,30 +257,36 @@ impl JsValue { (self.0 & PTR_MASK) as *mut JsObject } + /// 从 `JsObject` 指针构造对象值(`object` 的类型化别名)。 pub fn from_js_object(ptr: *mut JsObject) -> Self { let addr = ptr as u64; debug_assert!(addr <= PTR_MASK, "object pointer must fit in 48 bits"); Self(make_tag(TAG_OBJECT) | addr) } + /// 解出字符串指针;调用方须先保证 [`is_string`](JsValue::is_string)。 pub fn as_string_ptr(&self) -> *const JsString { debug_assert!(self.is_string(), "JsValue is not a string"); (self.0 & PTR_MASK) as *const JsString } + /// 解出可变字符串指针;调用方须先保证 [`is_string`](JsValue::is_string)。 pub fn as_string_ptr_mut(&self) -> *mut JsString { debug_assert!(self.is_string(), "JsValue is not a string"); (self.0 & PTR_MASK) as *mut JsString } + /// 构造 symbol 值(payload 为符号表下标)。 pub fn symbol(index: u32) -> Self { Self(make_tag(TAG_SYMBOL) | (index as u64)) } + /// 是否为 symbol。 pub fn is_symbol(&self) -> bool { is_nan_boxed(self.0) && get_tag(self.0) == TAG_SYMBOL } + /// 解出 symbol 下标;调用方须先保证 [`is_symbol`](JsValue::is_symbol)。 pub fn as_symbol_index(&self) -> u32 { debug_assert!(self.is_symbol()); (self.0 & INT_MASK) as u32 diff --git a/crates/oxide_vm/src/bindings/bind_array.rs b/crates/oxide_vm/src/bindings/bind_array.rs index 1564a55..1a58cb9 100644 --- a/crates/oxide_vm/src/bindings/bind_array.rs +++ b/crates/oxide_vm/src/bindings/bind_array.rs @@ -7,6 +7,7 @@ use oxide_types::object::{JsObject, PropAttributes}; use crate::bind_constructor; use crate::bindings::apply_binding_table; +/// 把 Array 构造器与原型方法绑定到 global。 pub fn bind_array(core: &Arc, session: &KernelSession, global: &mut JsObject) { let _array_methods = ArrayMethods { is_array: oxide_builtins::array::array_is_array:: as *const (), diff --git a/crates/oxide_vm/src/bindings/bind_array_buffer.rs b/crates/oxide_vm/src/bindings/bind_array_buffer.rs index eb52e47..28e3680 100644 --- a/crates/oxide_vm/src/bindings/bind_array_buffer.rs +++ b/crates/oxide_vm/src/bindings/bind_array_buffer.rs @@ -5,6 +5,7 @@ use crate::bindings::{apply_binding_table, configure_native_constructor}; use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::JsObject; +/// 把 ArrayBuffer 构造器与原型方法绑定到 global。 pub fn bind_array_buffer(core: &Arc, session: &KernelSession, global: &mut JsObject) { let ctor_ptr = session.builtin_world().array_buffer_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_boolean.rs b/crates/oxide_vm/src/bindings/bind_boolean.rs index f2e0218..bc7275a 100644 --- a/crates/oxide_vm/src/bindings/bind_boolean.rs +++ b/crates/oxide_vm/src/bindings/bind_boolean.rs @@ -5,6 +5,7 @@ use crate::bindings::{apply_binding_table, configure_native_constructor}; use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::JsObject; +/// 把 Boolean 构造器与原型方法绑定到 global。 pub fn bind_boolean(core: &Arc, session: &KernelSession, global: &mut JsObject) { let ctor_ptr = session.builtin_world().boolean_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_data_view.rs b/crates/oxide_vm/src/bindings/bind_data_view.rs index 0710d07..621a7f3 100644 --- a/crates/oxide_vm/src/bindings/bind_data_view.rs +++ b/crates/oxide_vm/src/bindings/bind_data_view.rs @@ -5,6 +5,7 @@ use crate::bindings::{apply_binding_table, configure_native_constructor}; use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::JsObject; +/// 把 DataView 构造器与原型方法绑定到 global。 pub fn bind_data_view(core: &Arc, session: &KernelSession, global: &mut JsObject) { let ctor_ptr = session.builtin_world().data_view_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_date.rs b/crates/oxide_vm/src/bindings/bind_date.rs index ac5be31..1e83eae 100644 --- a/crates/oxide_vm/src/bindings/bind_date.rs +++ b/crates/oxide_vm/src/bindings/bind_date.rs @@ -5,6 +5,7 @@ use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::JsObject; use oxide_types::value::JsValue; +/// 把 Date 构造器与原型方法绑定到 global。 pub fn bind_date(core: &Arc, session: &KernelSession, global: &mut JsObject) { let ctor_ptr = session.builtin_world().date_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_error.rs b/crates/oxide_vm/src/bindings/bind_error.rs index d3af2fa..8d5269f 100644 --- a/crates/oxide_vm/src/bindings/bind_error.rs +++ b/crates/oxide_vm/src/bindings/bind_error.rs @@ -43,6 +43,7 @@ fn bind_error_subtype_constructor( global.bump_generation(); } +/// 把 Error 及各子类型(TypeError/ReferenceError/...)构造器与原型方法绑定到 global。 pub fn bind_error(core: &Arc, session: &KernelSession, global: &mut JsObject) { let error_methods = ErrorMethods { error: oxide_builtins::error::error_constructor:: as *const (), diff --git a/crates/oxide_vm/src/bindings/bind_function.rs b/crates/oxide_vm/src/bindings/bind_function.rs index e077774..22a8562 100644 --- a/crates/oxide_vm/src/bindings/bind_function.rs +++ b/crates/oxide_vm/src/bindings/bind_function.rs @@ -7,6 +7,7 @@ use oxide_types::value::JsValue; use super::bind_global_value; +/// 把 Function 构造器与原型方法绑定到 global。 pub fn bind_function(core: &Arc, session: &KernelSession, global: &mut JsObject) { let function_methods = FunctionMethods { call: oxide_builtins::function::function_call:: as *const (), diff --git a/crates/oxide_vm/src/bindings/bind_global.rs b/crates/oxide_vm/src/bindings/bind_global.rs index 2322625..72fe5ec 100644 --- a/crates/oxide_vm/src/bindings/bind_global.rs +++ b/crates/oxide_vm/src/bindings/bind_global.rs @@ -5,6 +5,7 @@ use oxide_types::object::JsObject; use crate::bindings::apply_binding_table; +/// 把普通全局函数(`isNaN`、`parseInt`、`decodeURIComponent` 等)绑定到 global。 pub fn bind_global(core: &Arc, session: &KernelSession, global: &mut JsObject) { apply_binding_table( session.builtin_world(), diff --git a/crates/oxide_vm/src/bindings/bind_iterator.rs b/crates/oxide_vm/src/bindings/bind_iterator.rs index 51d270f..c069710 100644 --- a/crates/oxide_vm/src/bindings/bind_iterator.rs +++ b/crates/oxide_vm/src/bindings/bind_iterator.rs @@ -7,6 +7,7 @@ use oxide_types::value::JsValue; use crate::bindings::{apply_binding_table, bind_global_value, configure_native_constructor}; +/// 绑定迭代器基础设施:`%IteratorPrototype%` 与 `%ArrayIteratorPrototype%` 等。 pub fn bind_iterator(core: &Arc, session: &KernelSession, global: &mut JsObject) { let function_proto = session.builtin_world().function_proto.as_ptr() as *mut JsObject; let mut iterator = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto))); diff --git a/crates/oxide_vm/src/bindings/bind_json.rs b/crates/oxide_vm/src/bindings/bind_json.rs index 2c8140e..69c4ce7 100644 --- a/crates/oxide_vm/src/bindings/bind_json.rs +++ b/crates/oxide_vm/src/bindings/bind_json.rs @@ -5,6 +5,7 @@ use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::JsObject; use oxide_types::value::JsValue; +/// 把 JSON 单例对象及其方法(parse/stringify 等)绑定到 global。 pub fn bind_json(core: &Arc, session: &KernelSession, global: &mut JsObject) { let json_ptr = session.builtin_world().json_object.as_ptr() as *mut JsObject; let json = unsafe { &mut *json_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_map.rs b/crates/oxide_vm/src/bindings/bind_map.rs index 9f4b3c6..bd4acff 100644 --- a/crates/oxide_vm/src/bindings/bind_map.rs +++ b/crates/oxide_vm/src/bindings/bind_map.rs @@ -5,6 +5,7 @@ use crate::bindings::{apply_binding_table, configure_native_constructor}; use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::JsObject; +/// 把 Map 构造器与原型方法绑定到 global。 pub fn bind_map(core: &Arc, session: &KernelSession, global: &mut JsObject) { let ctor_ptr = session.builtin_world().map_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_math.rs b/crates/oxide_vm/src/bindings/bind_math.rs index d82e040..1ffc8c9 100644 --- a/crates/oxide_vm/src/bindings/bind_math.rs +++ b/crates/oxide_vm/src/bindings/bind_math.rs @@ -5,6 +5,7 @@ use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::JsObject; use oxide_types::value::JsValue; +/// 把 Math 单例对象及其方法(abs/floor/max/random 等)绑定到 global。 pub fn bind_math(core: &Arc, session: &KernelSession, global: &mut JsObject) { let math_ptr = session.builtin_world().math_object.as_ptr() as *mut JsObject; let math = unsafe { &mut *math_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_number.rs b/crates/oxide_vm/src/bindings/bind_number.rs index 8597063..d52a7d9 100644 --- a/crates/oxide_vm/src/bindings/bind_number.rs +++ b/crates/oxide_vm/src/bindings/bind_number.rs @@ -6,6 +6,7 @@ use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::{JsObject, PropAttributes}; use oxide_types::value::JsValue; +/// 把 Number 构造器与原型方法绑定到 global(含 NaN/POSITIVE_INFINITY 等常量)。 pub fn bind_number(core: &Arc, session: &KernelSession, global: &mut JsObject) { let ctor_ptr = session.builtin_world().number_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_object.rs b/crates/oxide_vm/src/bindings/bind_object.rs index 712ec39..725c78f 100644 --- a/crates/oxide_vm/src/bindings/bind_object.rs +++ b/crates/oxide_vm/src/bindings/bind_object.rs @@ -7,6 +7,7 @@ use oxide_types::object::{JsObject, PropAttributes}; use crate::bind_constructor; use crate::bindings::apply_binding_table; +/// 把 Object 构造器与原型方法绑定到 global。 pub fn bind_object(core: &Arc, session: &KernelSession, global: &mut JsObject) { let methods = ObjectMethods { keys: oxide_builtins::object::object_keys:: as *const (), diff --git a/crates/oxide_vm/src/bindings/bind_reflect.rs b/crates/oxide_vm/src/bindings/bind_reflect.rs index d4283d4..caca73f 100644 --- a/crates/oxide_vm/src/bindings/bind_reflect.rs +++ b/crates/oxide_vm/src/bindings/bind_reflect.rs @@ -6,6 +6,7 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::JsObject; use oxide_types::value::JsValue; +/// 把 Reflect 单例对象及其方法(get/set/construct/defineProperty 等)绑定到 global。 pub fn bind_reflect(core: &Arc, session: &KernelSession, global: &mut JsObject) { let mut reflect = Box::new(JsObject::new_empty( EMPTY_SHAPE_ID, diff --git a/crates/oxide_vm/src/bindings/bind_regexp.rs b/crates/oxide_vm/src/bindings/bind_regexp.rs index 8674978..92dc971 100644 --- a/crates/oxide_vm/src/bindings/bind_regexp.rs +++ b/crates/oxide_vm/src/bindings/bind_regexp.rs @@ -6,6 +6,7 @@ use oxide_types::object::JsObject; use crate::bind_constructor; +/// 把 RegExp 构造器与原型方法绑定到 global。 pub fn bind_regexp(core: &Arc, session: &KernelSession, global: &mut JsObject) { let ctor_ptr = session.builtin_world().regexp_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_set.rs b/crates/oxide_vm/src/bindings/bind_set.rs index 4e1c1fb..ebe7402 100644 --- a/crates/oxide_vm/src/bindings/bind_set.rs +++ b/crates/oxide_vm/src/bindings/bind_set.rs @@ -5,6 +5,7 @@ use crate::bindings::{apply_binding_table, configure_native_constructor}; use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::JsObject; +/// 把 Set 构造器与原型方法绑定到 global。 pub fn bind_set(core: &Arc, session: &KernelSession, global: &mut JsObject) { let ctor_ptr = session.builtin_world().set_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_string.rs b/crates/oxide_vm/src/bindings/bind_string.rs index 9a3f9f9..5ca3f2c 100644 --- a/crates/oxide_vm/src/bindings/bind_string.rs +++ b/crates/oxide_vm/src/bindings/bind_string.rs @@ -6,6 +6,7 @@ use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::JsObject; use oxide_types::value::JsValue; +/// 把 String 构造器与原型方法绑定到 global。 pub fn bind_string(core: &Arc, session: &KernelSession, global: &mut JsObject) { let string_methods = StringMethods { from_char_code: oxide_builtins::string::string_from_char_code:: as *const (), diff --git a/crates/oxide_vm/src/bindings/bind_stubs.rs b/crates/oxide_vm/src/bindings/bind_stubs.rs index 14625c4..c3e7daa 100644 --- a/crates/oxide_vm/src/bindings/bind_stubs.rs +++ b/crates/oxide_vm/src/bindings/bind_stubs.rs @@ -27,6 +27,8 @@ const STUBS: [(&str, *const (), u8); 8] = [ ("Atomics", oxide_builtins::stubs::atomics_stub:: as *const (), 0), ]; +/// 把未实现内置(Proxy/BigInt/WeakMap/WeakSet/WeakRef/FinalizationRegistry/SharedArrayBuffer/Atomics) +/// 的 stub 构造器绑定到 global,并登记到 `stub_objects` 供快照跟踪。 pub fn bind_stubs(core: &Arc, session: &mut KernelSession, global: &mut JsObject) { let builtin_world = Arc::get_mut(&mut session.builtin_world) .expect("BuiltinWorld must be uniquely owned during init_kernel_builtins"); diff --git a/crates/oxide_vm/src/bindings/bind_symbol.rs b/crates/oxide_vm/src/bindings/bind_symbol.rs index f56c837..2bd9aa8 100644 --- a/crates/oxide_vm/src/bindings/bind_symbol.rs +++ b/crates/oxide_vm/src/bindings/bind_symbol.rs @@ -7,6 +7,7 @@ use oxide_types::value::JsValue; use crate::bind_constructor; +/// 把 Symbol 构造器与原型方法绑定到 global(含 `Symbol.iterator` 等 well-known symbols)。 pub fn bind_symbol(core: &Arc, session: &KernelSession, global: &mut JsObject) { let ctor_ptr = session.builtin_world().symbol_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; diff --git a/crates/oxide_vm/src/bindings/bind_typed_array.rs b/crates/oxide_vm/src/bindings/bind_typed_array.rs index 0d93c80..f09f909 100644 --- a/crates/oxide_vm/src/bindings/bind_typed_array.rs +++ b/crates/oxide_vm/src/bindings/bind_typed_array.rs @@ -13,6 +13,7 @@ macro_rules! bind_typed_array_constructor { }}; } +/// 把所有 TypedArray 家族构造器(Int8Array/Uint8Array/.../BigUint64Array)与其共享原型绑定到 global。 pub fn bind_typed_array(core: &Arc, session: &KernelSession, global: &mut JsObject) { let shared_proto_ptr = session.builtin_world().typed_array_proto.as_ptr() as *mut JsObject; let shared_proto = unsafe { &mut *shared_proto_ptr }; diff --git a/crates/oxide_vm/src/bindings/mod.rs b/crates/oxide_vm/src/bindings/mod.rs index 2fc09ea..ddffccf 100644 --- a/crates/oxide_vm/src/bindings/mod.rs +++ b/crates/oxide_vm/src/bindings/mod.rs @@ -1,23 +1,48 @@ +//! 内置对象绑定:把 `oxide_builtins` 中的 native 实现安装到每个 session 的 +//! builtin 构造器/原型及 global object 上。绑定在 session 创建(`init_kernel_builtins`) +//! 与 dirty reset(`rebind_dirty_builtins`)两个时机执行。 + +/// Array 构造器与原型的 native 方法绑定。 pub mod bind_array; +/// ArrayBuffer 构造器与原型的 native 方法绑定。 pub mod bind_array_buffer; +/// Boolean 构造器与原型的 native 方法绑定。 pub mod bind_boolean; +/// DataView 构造器与原型的 native 方法绑定。 pub mod bind_data_view; +/// Date 构造器与原型的 native 方法绑定。 pub mod bind_date; +/// Error 家族构造器与原型的 native 方法绑定(含各子类型构造器创建)。 pub mod bind_error; +/// Function 构造器与原型的 native 方法绑定。 pub mod bind_function; +/// global 对象上的普通全局函数(parseInt、isNaN 等)绑定。 pub mod bind_global; +/// Iterator 相关全局辅助对象(%IteratorPrototype% 等)绑定。 pub mod bind_iterator; +/// JSON 单例对象及其 native 方法绑定。 pub mod bind_json; +/// Map 构造器与原型的 native 方法绑定。 pub mod bind_map; +/// Math 单例对象及其 native 方法绑定。 pub mod bind_math; +/// Number 构造器与原型的 native 方法绑定(含常量属性)。 pub mod bind_number; +/// Object 构造器与原型的 native 方法绑定。 pub mod bind_object; +/// Reflect 单例对象及其 native 方法绑定。 pub mod bind_reflect; +/// RegExp 构造器与原型的 native 方法绑定。 pub mod bind_regexp; +/// Set 构造器与原型的 native 方法绑定。 pub mod bind_set; +/// String 构造器与原型的 native 方法绑定。 pub mod bind_string; +/// 未实现内置(Proxy/BigInt/WeakMap 等)的 stub 构造器绑定。 pub mod bind_stubs; +/// Symbol 构造器与原型的 native 方法绑定。 pub mod bind_symbol; +/// 各 TypedArray 构造器与共享原型的 native 方法绑定。 pub mod bind_typed_array; use std::sync::Arc; @@ -256,6 +281,10 @@ fn bind_stub_globals(core: &Arc, session: &KernelSession, global: &m } } +/// 把已有 builtin 构造器的 native 实现与 global 槽位一次性装配完整。 +/// +/// 在 global 对象重建(dirty reset)后调用:重新配置构造器 native 函数并重绑 +/// `Object`/`Array`/`Math` 等全局名、TypedArray 家族、stub 与 `globalThis`。 pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession, global: &mut JsObject) { let world = session.builtin_world(); @@ -464,6 +493,7 @@ pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession /// Maintenance: when adding a `BuiltinDirtySet` group, update this rebind map, /// `BuiltinSnapshot`, and `BuiltinWorld::rebuild_with_dirty()` together. +/// 按脏标记重绑被污染的内置对象;`dirty` 为 `None` 时绑定全部(初始化路径)。 pub fn rebind_dirty_builtins(core: &Arc, session: &mut KernelSession, dirty: Option<&BuiltinDirtySet>) { let global_ptr = session.global_object().as_ptr() as *mut JsObject; let global = unsafe { &mut *global_ptr }; @@ -524,6 +554,7 @@ pub fn rebind_dirty_builtins(core: &Arc, session: &mut KernelSession } } +/// 完整初始化一个 session 的内置对象(全量绑定 + `globalThis` + 快照记录)。 pub fn init_kernel_builtins(core: &Arc, session: &mut KernelSession) { rebind_dirty_builtins(core, session, None); let global_ptr = session.global_object().as_ptr() as *mut oxide_types::object::JsObject; diff --git a/crates/oxide_vm/src/lib.rs b/crates/oxide_vm/src/lib.rs index 79f63ae..7268c9f 100644 --- a/crates/oxide_vm/src/lib.rs +++ b/crates/oxide_vm/src/lib.rs @@ -1,18 +1,32 @@ #![doc = "OxideJS - Register-based VM with epoch arena memory"] +//! +//! 字节码执行与运行时内核层:基于寄存器的 VM 负责执行 `CompiledModule` 的 +//! bytecode,采用 epoch arena 管理 session 内对象内存,并提供 session 级 +//! mark-sweep GC、VM 池、native 函数绑定与内置对象初始化。 +//! +//! 核心入口为 [`vm::Vm`];`VmPool` 负责 VM 实例的复用;`session_gc` 提供 +//! session 内存回收;`bindings` 把内置对象绑定到每个 session 的 global 上。 +/// 内置对象绑定模块:向 session 的 global 对象安装 Object/Array/... 及各原型。 pub mod bindings; mod dispatch; mod ic_helper; +/// native 函数签名类型([`native::NativeFn`]),builtin 绑定与 VM 调用约定依赖它。 pub mod native; mod session_arena; +/// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。 pub mod session_gc; +/// 寄存器 VM 主类型([`vm::Vm`])与其执行状态定义。 pub mod vm; mod vm_dispatch_ctrl; mod vm_dispatch_misc; +/// VM 层日志宏。 pub mod vm_log; +/// VM 池:多个 VM 实例的创建、复用与归还([`vm_pool::VmPool`] / [`vm_pool::VmGuard`])。 pub mod vm_pool; mod vm_props; mod vm_runtime; mod vm_state; mod vm_support; +/// JS 值类型再导出(来自 `oxide_types`),作为本 crate 公共 API 的一部分。 pub use oxide_types::value::JsValue; diff --git a/crates/oxide_vm/src/native.rs b/crates/oxide_vm/src/native.rs index 0a8ad19..895a5aa 100644 --- a/crates/oxide_vm/src/native.rs +++ b/crates/oxide_vm/src/native.rs @@ -1,3 +1,7 @@ use oxide_runtime_api::NativeResult; +/// native 内置函数的函数指针类型。 +/// +/// `args` 是寄存器下标列表(参数值取自 `Vm` 的寄存器);返回 [`NativeResult`] +/// 表示成功/抛错/尾调用三种结局。绑定层通过 `fn` 项指针构造,保证零开销调用。 pub type NativeFn = fn(&mut crate::vm::Vm, args: &[u8]) -> NativeResult; diff --git a/crates/oxide_vm/src/session_gc.rs b/crates/oxide_vm/src/session_gc.rs index 4c6315c..0be8023 100644 --- a/crates/oxide_vm/src/session_gc.rs +++ b/crates/oxide_vm/src/session_gc.rs @@ -10,6 +10,12 @@ use rustc_hash::FxBuildHasher; use crate::vm::Vm; use oxide_builtins::{array_buffer, data_view, map, regexp, set, typed_array}; +/// session 级 mark-sweep GC 的状态与统计。 +/// +/// 回收 session arena 中不再可达的对象与 `Vm::new_string` 分配的 session 字符串: +/// `mark` 从 VM roots 标记存活对象,`sweep` 将存活对象复制进新 arena(移动式)、 +/// 更新所有引用,并释放死对象;`sweep_session_strings` 按存活标记回收字符串。 +/// 所有统计字段供外部观测 GC 行为。 pub struct SessionGc { pub total_collections: u64, pub total_bytes_freed: u64, @@ -28,6 +34,7 @@ pub struct SessionGc { } impl SessionGc { + /// 创建全零统计、空标记栈的空 GC 实例。 pub fn new() -> Self { Self::default() } diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 6aea229..c6d96d4 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -6,6 +6,7 @@ use oxide_bytecode::module::{CompiledModule, Constant}; use oxide_bytecode::opcode::{self, OpCode}; use smallvec::SmallVec; +/// 初始化一个 session 的内置对象(global 槽位、各构造器与原型、IC 预热)。 pub use crate::bindings::init_kernel_builtins; use crate::native::NativeFn; use crate::session_gc::SessionGc; @@ -89,6 +90,10 @@ macro_rules! binary_arith { }} } +/// 调用帧被挂起后,恢复时需要继续的执行方式。 +/// +/// `AccessorGet` 表示 accessor getter 返回后需把结果写回 `target_reg`; +/// `AccessorSet` 表示 setter 调用完成;`None` 为普通调用返回。 #[derive(Debug, Clone, Copy)] pub enum FrameContinuation { None, @@ -96,6 +101,10 @@ pub enum FrameContinuation { AccessorSet, } +/// 一次函数调用的调用帧:记录返回地址、调用方寄存器窗口与 `this`/`new.target`。 +/// +/// 调用方寄存器窗口在 `save_stack` 中按 `saved_reg_offset` 保存,返回时由 +/// `restore_frame` 恢复;`continuation` 描述 getter/setter 场景的恢复方式。 pub struct CallFrame { pub return_addr: usize, pub function_name: u32, @@ -110,6 +119,9 @@ pub struct CallFrame { pub continuation: FrameContinuation, } +/// 一次 `for-in` 迭代的游标:已收集的 key 列表与当前下标。 +/// +/// 每个 key 与它的 intern id 配对保存,使整型下标 key 无需重新 intern 即可排到字符串 key 之前。 pub struct ForInIter<'bump> { /// Each key paired with its string-intern id, so for-in can sort /// integer-index keys ahead of string keys without re-interning. @@ -117,6 +129,7 @@ pub struct ForInIter<'bump> { pub index: usize, } +/// try/catch/finally 处理记录,异常展开时用于定位跳转目标与清理范围。 pub struct TryHandler { pub catch_pc: Option, pub finally_pc: Option, @@ -149,6 +162,11 @@ pub(crate) struct InlineSyncState { pub(crate) cell_stack: Vec>, } +/// 基于寄存器的 JS 虚拟机:持有执行状态、寄存器文件、调用栈与 session 内存。 +/// +/// 执行入口为 [`Vm::run`](见 `vm_runtime` 模块);内存模型为 epoch arena + +/// session 对象(可被 `SessionGc` 移动式回收)+ session 字符串。多数内部字段为 +/// `pub(crate)`,对外提供统计与内省 getter。 pub struct Vm { pub(crate) regs: [JsValue; 256], pub(crate) pc: usize, @@ -404,38 +422,47 @@ impl Vm { self.gc_state.session_gc = session_gc; } + /// 只读访问 session GC 的统计(回收次数、存活/死亡对象数、释放字节等)。 pub fn session_gc_stats(&self) -> &SessionGc { &self.gc_state.session_gc } + /// 当前 session arena 中存活(已晋升)的对象数量。 pub fn session_object_count(&self) -> usize { self.gc_state.session_object_ptrs.len() } + /// session 当前分配的字节数(对象 + 存活字符串)。 pub fn session_bytes_allocated(&self) -> usize { self.gc_state.session_bytes_allocated } + /// 当前 epoch 中已分配并跟踪的对象数量(未晋升到 session 的临时对象)。 pub fn epoch_object_count(&self) -> usize { self.gc_state.epoch_object_ptrs.len() } + /// inline cache 命中率(0.0~1.0),用于观测 IC 预热效果。 pub fn ic_hit_rate(&self) -> f64 { self.profiling.ic_hit_rate() } + /// 累计执行的指令数(profiling)。 pub fn instruction_count(&self) -> u64 { self.profiling.instruction_count } + /// 全局 symbol 注册表中已注册的 key 数量。 pub fn symbol_registry_len(&self) -> usize { self.symbols.registry_len() } + /// inline cache 命中次数。 pub fn ic_hit_count(&self) -> u64 { self.profiling.ic_hits.get() } + /// inline cache 未命中次数。 pub fn ic_miss_count(&self) -> u64 { self.profiling.ic_misses.get() } @@ -477,6 +504,7 @@ impl Vm { format_error_message(kind, msg) } + /// 推进线性同余 RNG 一步(Math.random 用)。首次调用以系统时间纳秒播种。 pub fn step_rng(&mut self) { if self.math_rng_state == 0 { self.math_rng_state = std::time::SystemTime::now() @@ -491,14 +519,17 @@ impl Vm { .wrapping_add(1442695040888963407); } + /// 读取当前 RNG 状态生成的 [0,1) 浮点数。 pub fn math_rng_value(&self) -> f64 { (self.math_rng_state >> 33) as f64 / (1u64 << 31) as f64 } + /// 只读访问 VM 共享的 `KernelCore`。 pub fn kernel_core(&self) -> &Arc { &self.kernel_core } + /// 只读访问当前 session(builtin world 与 global object)。 pub fn session(&self) -> &KernelSession { &self.session } @@ -508,18 +539,22 @@ impl Vm { std::ptr::eq(ptr, proto_ptr) } + /// 读取寄存器 `idx` 的值(`VmHost` 与 native 绑定的统一入口)。 pub fn reg(&self, idx: u8) -> JsValue { self.regs[idx as usize] } + /// 写入寄存器 `idx` 的值。 pub fn set_reg(&mut self, idx: u8, val: JsValue) { self.regs[idx as usize] = val; } + /// 只读访问 VM 的 epoch arena。 pub fn epoch(&self) -> &Epoch { &self.epoch } + /// 若 `val` 是字符串,返回其内容的 `String` 副本;否则返回 `None`。 pub fn lookup_str(&self, val: JsValue) -> Option { if !val.is_string() { return None; diff --git a/crates/oxide_vm/src/vm_pool.rs b/crates/oxide_vm/src/vm_pool.rs index c38441a..279fddd 100644 --- a/crates/oxide_vm/src/vm_pool.rs +++ b/crates/oxide_vm/src/vm_pool.rs @@ -12,6 +12,10 @@ struct VmPoolInner { total_count: usize, } +/// 共享 `Vm` 实例池:按需创建并复用 VM,避免每次执行重建 kernel 共享状态。 +/// +/// 空闲 VM 存放在 `available` 队列;达到 `max_size` 上限时 `spawn` 会阻塞等待 +/// 归还(带 5 秒超时强制扩容兜底)。线程安全,可供多线程并发领取。 pub struct VmPool { kernel_core: Arc, inner: Mutex, @@ -19,6 +23,10 @@ pub struct VmPool { max_size: Option, } +/// 从池中借出的 VM 独占句柄(RAII)。 +/// +/// 持有期间独占访问 [`VmGuard::vm`] / [`VmGuard::vm_mut`];`Drop` 时归还池: +/// 干净 VM 执行 `full_reset` 后复用,被标记 dirty 的 VM 直接丢弃并新建替补。 pub struct VmGuard { vm: Option, pool: Arc, @@ -26,6 +34,7 @@ pub struct VmGuard { } impl VmPool { + /// 创建空池。`min_size` 当前仅作预热预留(未使用),`max_size` 为池上限,`None` 表示不限。 pub fn new(kernel_core: Arc, _min_size: usize, max_size: Option) -> Arc { Arc::new(Self { kernel_core, @@ -46,6 +55,7 @@ impl VmPool { Self::new_vm(&self.kernel_core) } + /// 从池中借出一个 VM:优先复用空闲实例,否则在池未满时新建,池满则阻塞等待归还。 pub fn spawn(self: &Arc) -> VmGuard { loop { let mut inner = self.inner.lock().unwrap(); @@ -94,10 +104,12 @@ impl VmPool { } impl VmGuard { + /// 只读访问被借出的 VM。 pub fn vm(&self) -> &Vm { self.vm.as_ref().expect("VmGuard has no VM") } + /// 可变访问被借出的 VM。 pub fn vm_mut(&mut self) -> &mut Vm { self.vm.as_mut().expect("VmGuard has no VM") } diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index 59a0feb..6f7c8ae 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -142,6 +142,7 @@ impl Vm { self.pc = frame.return_addr; } + /// 重新执行当前已加载的 bytecode:清空执行状态并重置 IC 缓存后再次 dispatch。 pub fn rerun(&mut self) -> Result { vm_info!("rerun: clearing IC caches"); self.clear_execution_state(); @@ -150,6 +151,10 @@ impl Vm { self.dispatch() } + /// 加载并执行一个已编译模块,返回模块顶层执行结果或未捕获异常消息。 + /// + /// 内部初始化寄存器/bytecode/immutables 与 builtin 寄存器预绑定,然后进入 + /// dispatch 主循环;执行完成或异常展开后返回。 pub fn run(&mut self, module: &CompiledModule) -> Result { vm_debug!("run: starting bytecode execution, {} instructions", module.bytecode.len()); self.clear_execution_state(); diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index ac837e1..690ca52 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -15,6 +15,7 @@ use oxide_types::object::{JsObject, JsString, PropAttributes}; use oxide_types::value::JsValue; impl Vm { + /// 以最小配置创建独立 VM:新建 `KernelCore` + `KernelSession` 并初始化内置对象。 pub fn new() -> Self { let core = KernelCore::new(KernelConfig::minimal()); let mut session = KernelSession::new(&core); @@ -78,6 +79,7 @@ impl Vm { vm } + /// 复用共享 `KernelCore` 创建 VM(VM 池路径),共享 intern/shape/code 缓存。 pub fn with_kernel_core(core: Arc) -> Self { let mut session = KernelSession::new(&core); bindings::init_kernel_builtins(&core, &mut session); @@ -140,6 +142,9 @@ impl Vm { vm } + /// 全量隔离重置:仅重建被污染的内置对象与 global,并清空所有执行状态与内存。 + /// + /// 用于在多次 JS 执行之间达到完全隔离:session 内未被污染的 builtin 保留原指针。 pub fn full_reset(&mut self) { let dirty = self.session.selective_reset(&self.kernel_core); if dirty.any_builtin_dirty() { @@ -156,6 +161,7 @@ impl Vm { vm_info!("full_reset completed"); } + /// 旧版全量重置:总是丢弃并重建整个 session 与内置对象(benchmark 专用)。 #[doc(hidden)] pub fn full_reset_legacy_for_bench(&mut self) { self.session = KernelSession::new(&self.kernel_core); @@ -216,6 +222,7 @@ impl Vm { self.native_call_depth = 0; } + /// 轻量重置:清空执行状态并回收 epoch 内存,但保留 session 字符串与 builtin。 pub fn reset(&mut self) { self.clear_execution_state(); self.maybe_collect_session_gc(); @@ -229,6 +236,7 @@ impl Vm { self.active_reg_limit = 0; } + /// 分配一个可被 session GC 回收的字符串 `JsValue`(session-heap 字符串)。 pub fn new_string(&mut self, s: &str) -> JsValue { self.new_string_owned(s.to_string()) } @@ -242,6 +250,7 @@ impl Vm { JsValue::string(ptr) } + /// 把字符串 intern 为永久 key id(属性名/方法名),进程生命周期内稳定。 pub fn intern_key(&self, s: &str) -> u32 { self.kernel_core.perm_interner().intern(s).0 }