docs: 添加 Rustdoc 注释
This commit is contained in:
@@ -181,6 +181,7 @@ fn unexpected_tail_call_error<H: VmHost>(vm: &mut H) -> NativeResult {
|
||||
NativeResult::Err(crate::error::create_type_error(vm, "unexpected tail call in array callback"))
|
||||
}
|
||||
|
||||
/// JS `Array()` 构造逻辑:单个数字参数创建指定长度空数组,其余情况把参数作为元素。
|
||||
pub fn array_constructor<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(arr))
|
||||
}
|
||||
|
||||
/// `Array.isArray(value)`:参数是否为真正的 Array 对象。
|
||||
pub fn array_is_array<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(unsafe { &*ptr }.is_array()))
|
||||
}
|
||||
|
||||
/// `Array.prototype.push(...items)`:追加元素到尾部,返回新长度。
|
||||
pub fn array_push<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::int(len as i32))
|
||||
}
|
||||
|
||||
/// `Array.prototype.pop()`:移除并返回末位元素;空数组返回 undefined。
|
||||
pub fn array_pop<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(last)
|
||||
}
|
||||
|
||||
/// `Array.prototype.slice(start, end)`:复制区间元素返回新数组(支持负索引)。
|
||||
pub fn array_slice<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||
}
|
||||
|
||||
/// `Array.prototype.splice(start, deleteCount, ...items)`:删除并/或插入元素,
|
||||
/// 返回被删除元素组成的新数组。
|
||||
pub fn array_splice<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(removed_arr))
|
||||
}
|
||||
|
||||
/// `Array.prototype.concat(...items)`:连接 this 与参数(数组参数展开)返回新数组。
|
||||
pub fn array_concat<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||
}
|
||||
|
||||
/// `Array.prototype.join(separator)`:用分隔符连接元素字符串(null/undefined 视为空串)。
|
||||
pub fn array_join<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string(&joined))
|
||||
}
|
||||
|
||||
/// `Array.prototype.toString`:委托给 join,默认用 `,` 分隔。
|
||||
pub fn array_to_string<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
}
|
||||
|
||||
/// `Array.prototype.includes(searchElement, fromIndex)`:用 SameValueZero 判断是否包含
|
||||
/// (NaN 视为存在、+0/-0 视为相同)。
|
||||
pub fn array_includes<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.reg(args[0]))
|
||||
}
|
||||
|
||||
/// `Array.prototype.flat(depth)`:按深度递归展开嵌套数组(含循环引用保护)返回新数组。
|
||||
pub fn array_flat<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `Array.prototype.map(callback, thisArg)`:对每个元素调用 callback 生成新数组。
|
||||
pub fn array_map<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||
}
|
||||
|
||||
/// `Array.prototype.filter(callback, thisArg)`:保留 callback 返回真值的元素形成新数组。
|
||||
pub fn array_filter<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||
}
|
||||
|
||||
/// `Array.prototype.reduce(callback, initialValue)`:从左到右累计归约;
|
||||
/// 空数组且无初始值抛 TypeError。
|
||||
pub fn array_reduce<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(accumulator)
|
||||
}
|
||||
|
||||
/// `Array.prototype.find(callback, thisArg)`:返回首个 callback 为真的元素,否则 undefined。
|
||||
pub fn array_find<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `Array.prototype.some(callback, thisArg)`:任一元素满足 callback 返回 true。
|
||||
pub fn array_some<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(false))
|
||||
}
|
||||
|
||||
/// `Array.prototype.every(callback, thisArg)`:所有元素满足 callback 才返回 true。
|
||||
pub fn array_every<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(true))
|
||||
}
|
||||
|
||||
/// `Array.prototype.flatMap(callback, thisArg)`:map 后把返回的数组展开一层。
|
||||
pub fn array_flat_map<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||
}
|
||||
|
||||
/// `Array.prototype.shift()`:移除并返回首元素,其余元素前移;空数组返回 undefined。
|
||||
pub fn array_shift<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(first)
|
||||
}
|
||||
|
||||
/// `Array.prototype.unshift(...items)`:插入元素到头部,返回新长度。
|
||||
pub fn array_unshift<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.reg(args[0]))
|
||||
}
|
||||
|
||||
/// `Array.prototype.copyWithin(target, start, end)`:在数组内部复制元素区间,返回 this。
|
||||
pub fn array_copy_within<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.reg(args[0]))
|
||||
}
|
||||
|
||||
/// `Array.prototype.at(index)`:按索引取元素,支持负索引;越界返回 undefined。
|
||||
pub fn array_at<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(arr.get_prop_at(index))
|
||||
}
|
||||
|
||||
/// `Array.prototype.lastIndexOf(searchElement, fromIndex)`:从后往前查找首个匹配索引。
|
||||
pub fn array_last_index_of<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
}
|
||||
|
||||
/// `Array.prototype.findIndex(callback, thisArg)`:返回首个 callback 为真的索引,否则 -1。
|
||||
pub fn array_find_index<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
}
|
||||
|
||||
/// `Array.prototype.findLast(callback, thisArg)`:从后往前返回首个 callback 为真的元素。
|
||||
pub fn array_find_last<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `Array.prototype.reduceRight(callback, initialValue)`:从右到左累计归约。
|
||||
pub fn array_reduce_right<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(acc)
|
||||
}
|
||||
|
||||
/// `Array.prototype.sort(compareFn)`:原地排序。默认按字符串字典序;
|
||||
/// 提供比较函数时按其返回值(<0/=0/>0)排序,回调抛错则中止。
|
||||
pub fn array_sort<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.reg(args[0]))
|
||||
}
|
||||
|
||||
/// `Array.prototype.values()`:返回迭代数组元素的迭代器。
|
||||
pub fn array_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
builtins_debug!("Array.prototype.values called with {} args", args.len());
|
||||
let this_val = vm.reg(args[0]);
|
||||
|
||||
@@ -74,6 +74,7 @@ fn array_buffer_vec_ptr(obj: &JsObject) -> Option<*mut Vec<u8>> {
|
||||
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut Vec<u8>)
|
||||
}
|
||||
|
||||
/// 克隆 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<Vec<u8>>`),返回字节数。
|
||||
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<H: VmHost>(vm: &mut H, this_val: JsValue) ->
|
||||
Ok(data_ptr)
|
||||
}
|
||||
|
||||
/// `ArrayBuffer(length)` 构造逻辑:分配 length 字节(0 填充)的新缓冲区。
|
||||
pub fn array_buffer_constructor<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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() {
|
||||
|
||||
@@ -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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(this_val)
|
||||
}
|
||||
|
||||
/// `Boolean.prototype.valueOf`:返回包装对象的原始 bool 值。
|
||||
/// this 为原始 bool 时直接返回;否则必须是 Boolean 包装对象,否则抛 TypeError。
|
||||
pub fn boolean_prototype_value_of<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeR
|
||||
NativeResult::Ok(val)
|
||||
}
|
||||
|
||||
/// `Boolean.prototype.toString`:返回 `"true"` 或 `"false"`。
|
||||
/// 支持原始 bool 与 Boolean 包装对象;其它 this 抛 TypeError。
|
||||
pub fn boolean_prototype_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if this_val.is_bool() {
|
||||
|
||||
@@ -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)*) => {
|
||||
|
||||
@@ -121,6 +121,7 @@ fn write_bytes<const N: usize, H: VmHost>(vm: &mut H, args: &[u8], bytes: [u8; N
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `DataView(buffer, byteOffset, byteLength)` 构造逻辑:在 ArrayBuffer 上建立定点字节视图。
|
||||
pub fn data_view_constructor<H: VmHost>(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<JsValue> {
|
||||
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<JsValue> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 克隆 DataView 视图数据到新对象,用 `rewrite` 改写 buffer 引用。
|
||||
pub fn clone_data_view_native_with_rewrite<F>(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<F>(obj: &mut JsObject, mut rewrite: F)
|
||||
where
|
||||
F: FnMut(JsValue) -> JsValue,
|
||||
@@ -228,6 +232,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// 释放 DataView 视图数据(`Box<DataViewData>`),返回释放字节数。
|
||||
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::<DataViewData>() as u64
|
||||
}
|
||||
|
||||
/// `DataView.prototype.getInt8(byteOffset)`:读取 1 字节有符号整数。
|
||||
pub fn data_view_get_int8<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::int(n))
|
||||
}
|
||||
|
||||
/// `DataView.prototype.getUint32(byteOffset, littleEndian)`:读取 4 字节无符号整数。
|
||||
pub fn data_view_get_uint32<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `DataView.prototype.setUint16(byteOffset, value, littleEndian)`:写入 2 字节无符号整数。
|
||||
pub fn data_view_set_uint16<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `DataView.prototype.setInt32(byteOffset, value, littleEndian)`:写入 4 字节有符号整数。
|
||||
pub fn data_view_set_int32<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `DataView.prototype.setUint32(byteOffset, value, littleEndian)`:写入 4 字节无符号整数。
|
||||
pub fn data_view_set_uint32<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `DataView.prototype.setFloat32(byteOffset, value, littleEndian)`:写入单精度浮点。
|
||||
pub fn data_view_set_float32<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `DataView.prototype.setFloat64(byteOffset, value, littleEndian)`:写入双精度浮点。
|
||||
pub fn data_view_set_float64<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRes
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `DataView.prototype.toString`:校验 receiver 后返回 `[object DataView]`。
|
||||
pub fn data_view_to_string<H: VmHost>(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));
|
||||
|
||||
@@ -84,6 +84,8 @@ fn naive_from_ms(ms: f64) -> Option<NaiveDateTime> {
|
||||
dt_from_ms(ms).map(|dt| dt.naive_utc())
|
||||
}
|
||||
|
||||
/// JS `Date()` 构造逻辑:无参取当前时间;单参支持时间戳/字符串/Date 对象;
|
||||
/// 多参按本地时间字段(年/月/日/时/分/秒/毫秒)组合。非构造调用返回日期字符串。
|
||||
pub fn date_constructor<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(ptr))
|
||||
}
|
||||
|
||||
/// `Date.now()`:返回当前时间戳(毫秒)。
|
||||
pub fn date_now<H: VmHost>(_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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &H, args: &[u8], idx: usize, default: u32) -> u32
|
||||
}
|
||||
}
|
||||
|
||||
/// `Date.prototype.setTime(ms)`:直接设置时间戳,返回新时间戳。
|
||||
pub fn date_set_time<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::float(val))
|
||||
}
|
||||
|
||||
/// `Date.prototype.setFullYear(y, m, d)`:设置本地年份(可选月/日),返回新时间戳。
|
||||
pub fn date_set_full_year<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
set_timestamp(obj, ts);
|
||||
NativeResult::Ok(JsValue::float(ts))
|
||||
}
|
||||
/// `Date.prototype.setDate(d)`:设置本地日,返回新时间戳。
|
||||
pub fn date_set_date<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
set_timestamp(obj, ts);
|
||||
NativeResult::Ok(JsValue::float(ts))
|
||||
}
|
||||
/// `Date.prototype.setMilliseconds(ms)`:设置本地毫秒,返回新时间戳。
|
||||
pub fn date_set_milliseconds<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
||||
NativeResult::Ok(JsValue::float(ts))
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCMonth(m, d)`:设置 UTC 月份(可选日),返回新时间戳。
|
||||
pub fn date_set_utc_month<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::float(ts))
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCDate(d)`:设置 UTC 日,返回新时间戳。
|
||||
pub fn date_set_utc_date<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::float(ts))
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCMinutes(min, s, ms)`:设置 UTC 分钟(可选秒/毫秒),返回新时间戳。
|
||||
pub fn date_set_utc_minutes<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
NativeResult::Ok(JsValue::float(ts))
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCSeconds(s, ms)`:设置 UTC 秒(可选毫秒),返回新时间戳。
|
||||
pub fn date_set_utc_seconds<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
NativeResult::Ok(JsValue::float(ts))
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCMilliseconds(ms)`:设置 UTC 毫秒,返回新时间戳。
|
||||
pub fn date_set_utc_milliseconds<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRe
|
||||
NativeResult::Ok(JsValue::float(ts))
|
||||
}
|
||||
|
||||
/// Annex B `Date.prototype.getYear()`:返回本地年减 1900。
|
||||
pub fn date_get_year<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// Annex B `Date.prototype.setYear(y)`:设置年份,0..99 自动加 1900;
|
||||
/// 缺参/NaN 置为 Invalid Date。
|
||||
pub fn date_set_year<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::float(ts))
|
||||
}
|
||||
|
||||
/// `Date.prototype.toGMTString()`:别名 `toUTCString`(GMT 格式)。
|
||||
pub fn date_to_gmt_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
date_to_utc_string(vm, args)
|
||||
}
|
||||
|
||||
/// `Date.prototype.toLocaleDateString()`:本地化日期字符串(当前等同于 `toDateString`)。
|
||||
pub fn date_to_locale_date_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
date_to_date_string(vm, args)
|
||||
}
|
||||
|
||||
/// `Date.prototype.toLocaleString()`:本地化日期时间字符串(当前等同于 `toString`)。
|
||||
pub fn date_to_locale_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
date_to_string(vm, args)
|
||||
}
|
||||
|
||||
/// `Date.prototype.toLocaleTimeString()`:本地化时间字符串(当前等同于 `toTimeString`)。
|
||||
pub fn date_to_locale_time_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
date_to_time_string(vm, args)
|
||||
}
|
||||
@@ -828,10 +860,12 @@ fn date_to_string_inner<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj = unsafe { &*native_try!(date_this(vm, args)) };
|
||||
let ms = get_timestamp(obj);
|
||||
|
||||
@@ -26,6 +26,8 @@ fn set_own_message<H: VmHost>(host: &mut H, this: *mut JsObject, args: &[u8]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// 按错误类型名创建对应 Error 对象(message 非空时写为自身属性)。
|
||||
/// 这是引擎内部构造错误的统一入口,供 VM/native 层抛错使用。
|
||||
pub fn create_kind_error<H: VmHost>(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<H: VmHost>(host: &mut H, kind: &str, msg: &str) -> JsVa
|
||||
JsValue::from_js_object(obj)
|
||||
}
|
||||
|
||||
/// 创建一个带指定 message 的 TypeError 对象。
|
||||
pub fn create_type_error<H: VmHost>(host: &mut H, msg: &str) -> JsValue {
|
||||
create_kind_error(host, "TypeError", msg)
|
||||
}
|
||||
|
||||
/// 创建一个带指定 message 的普通 Error 对象。
|
||||
pub fn create_error<H: VmHost>(host: &mut H, msg: &str) -> JsValue {
|
||||
create_kind_error(host, "Error", msg)
|
||||
}
|
||||
|
||||
/// 创建一个带指定 message 的 ReferenceError 对象。
|
||||
pub fn create_reference_error<H: VmHost>(host: &mut H, msg: &str) -> JsValue {
|
||||
create_kind_error(host, "ReferenceError", msg)
|
||||
}
|
||||
|
||||
/// 创建一个带指定 message 的 RangeError 对象。
|
||||
pub fn create_range_error<H: VmHost>(host: &mut H, msg: &str) -> JsValue {
|
||||
create_kind_error(host, "RangeError", msg)
|
||||
}
|
||||
|
||||
/// 创建一个带指定 message 的 SyntaxError 对象。
|
||||
pub fn create_syntax_error<H: VmHost>(host: &mut H, msg: &str) -> JsValue {
|
||||
create_kind_error(host, "SyntaxError", msg)
|
||||
}
|
||||
|
||||
/// 创建一个带指定 message 的 URIError 对象。
|
||||
pub fn create_uri_error<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(host: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(host.new_string(&result))
|
||||
}
|
||||
|
||||
/// `Error.prototype.stack` getter:输出 `name: message` 头后附调用栈函数名列表。
|
||||
pub fn error_stack_getter<H: VmHost>(host: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = host.reg(args[0]);
|
||||
let (name_str, msg_str) = if this_val.is_object() {
|
||||
|
||||
@@ -30,6 +30,8 @@ fn bind_dispatcher<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.is_empty() {
|
||||
return NativeResult::Err(JsValue::undefined());
|
||||
@@ -40,6 +42,8 @@ pub fn function_call<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.is_empty() {
|
||||
return NativeResult::Err(JsValue::undefined());
|
||||
@@ -80,6 +84,8 @@ pub fn function_apply<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !this_val.is_object() {
|
||||
|
||||
@@ -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<H: VmHost>(vm: &mut H, args: &[u8]) -> String {
|
||||
|
||||
@@ -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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let input = super::string_arg(vm, args);
|
||||
NativeResult::Ok(vm.new_string(&unescape_string(&input)))
|
||||
|
||||
@@ -85,16 +85,22 @@ fn uri_error<H: VmHost>(vm: &mut H) -> NativeResult {
|
||||
NativeResult::Err(crate::error::create_uri_error(vm, URI_ERROR_MESSAGE))
|
||||
}
|
||||
|
||||
/// `encodeURI`:编码输入为 URI,保留未转义字符与保留字符 `;/?:@&=+$,#`,
|
||||
/// 其余按 UTF-8 字节转成 `%XX`。
|
||||
pub fn encode_uri<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `decodeURIComponent`:解码所有 `%XX` 序列(含保留字符),
|
||||
/// 非法或截断的 UTF-8 序列抛 URIError。
|
||||
pub fn decode_uri_component<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let input = super::string_arg(vm, args);
|
||||
match decode_uri_string(&input, false) {
|
||||
|
||||
@@ -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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// 为任意值创建统一迭代器包装对象:String/Array/Map/Set 直接支持索引遍历,
|
||||
/// 其它对象则要求提供可调用的 `next`。不可迭代时返回 TypeError。
|
||||
pub fn make_iterator_for_value<H: VmHost>(vm: &mut H, value: JsValue) -> Result<JsValue, JsValue> {
|
||||
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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// 迭代器包装器的 `next` 方法:对 Array/String/Map/Set 直接按索引取值,
|
||||
/// 其它对象委托其自身 `next`;底层抛出异常时透传原始值(不做二次包装)。
|
||||
pub fn iterator_wrapper_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !this_val.is_object() {
|
||||
|
||||
@@ -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<H: VmHost>(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<H: VmHost>(vm: &mut H, obj_val: JsValue, key: &str) -> Result<Js
|
||||
}
|
||||
}
|
||||
|
||||
/// `JSON.stringify(value, replacer, space)`:将 JS 值序列化为 JSON 文本。
|
||||
/// 支持 replacer 函数/属性白名单、toJSON 钩子、缩进与循环引用检测。
|
||||
pub fn json_stringify<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Ok(JsValue::undefined());
|
||||
|
||||
@@ -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<H: VmHost>(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;
|
||||
|
||||
@@ -68,6 +68,7 @@ fn alloc_map<H: VmHost>(vm: &mut H) -> *mut JsObject {
|
||||
vm.alloc_object(obj)
|
||||
}
|
||||
|
||||
/// 收集 Map 中作为对象引用的键和值(GC 根边),供跨 epoch 遍历/重写时追踪。
|
||||
pub fn map_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||||
if !obj.is_map() {
|
||||
return Vec::new();
|
||||
@@ -85,6 +86,8 @@ pub fn map_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 克隆 Map 的 native 数据到新对象,用 `rewrite` 改写其中的对象引用
|
||||
/// (供跨 epoch 的对象重写/克隆流程使用)。
|
||||
pub fn clone_map_native_with_rewrite<F>(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<F>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(this_val)
|
||||
}
|
||||
|
||||
/// `Map.prototype.get(key)`:返回 key 对应的值;不存在返回 undefined。
|
||||
pub fn map_get<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(found.unwrap_or(JsValue::undefined()))
|
||||
}
|
||||
|
||||
/// `Map.prototype.has(key)`:key 是否存在。
|
||||
pub fn map_has<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(found))
|
||||
}
|
||||
|
||||
/// `Map.prototype.delete(key)`:删除键并返回是否删除成功。
|
||||
pub fn map_delete<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(removed.is_some()))
|
||||
}
|
||||
|
||||
/// `Map.prototype.clear()`:清空全部键值对,返回 undefined。
|
||||
pub fn map_clear<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `Map.prototype.size` getter:返回键值对数量。
|
||||
pub fn map_size<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
))
|
||||
}
|
||||
|
||||
/// `Map.prototype.values()`:返回按插入序迭代值的迭代器。
|
||||
pub fn map_values<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
))
|
||||
}
|
||||
|
||||
/// `Map.prototype.keys()`:返回按插入序迭代键的迭代器。
|
||||
pub fn map_keys<H: VmHost>(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));
|
||||
|
||||
@@ -21,6 +21,7 @@ fn arg2<H: VmHost>(vm: &mut H, args: &[u8]) -> (f64, f64) {
|
||||
(a, b)
|
||||
}
|
||||
|
||||
/// `Math.abs`:返回参数的绝对值。int 参数走 int 运算,其余按 double 返回。
|
||||
pub fn math_abs<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
macro_rules! math_unary {
|
||||
($name:ident, $op:ident) => {
|
||||
/// 一元数学函数(`Math.acos` 等):对首个参数做 Rust 同名浮点运算,
|
||||
/// 返回 double 结果;缺参或不可转数字时按 NaN 处理。
|
||||
pub fn $name<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let x = arg1(vm, args);
|
||||
if x.is_nan() {
|
||||
@@ -89,30 +95,36 @@ pub fn math_sign<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Math.clz32`:返回 32 位无符号整数表示的前导零个数。
|
||||
pub fn math_clz32<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::float(m))
|
||||
}
|
||||
|
||||
/// `Math.min`:返回参数中的最小值;无参时返回 Infinity,任一 NaN 时返回 NaN。
|
||||
pub fn math_min<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::float(m))
|
||||
}
|
||||
|
||||
/// `Math.random`:推进 RNG 并返回 [0, 1) 的伪随机浮点数。
|
||||
pub fn math_random<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
vm.step_rng();
|
||||
NativeResult::Ok(JsValue::float(vm.math_rng_value()))
|
||||
|
||||
@@ -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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Number.isNaN`:参数严格等于 NaN 才返回 true(不做隐式类型转换)。
|
||||
pub fn number_is_nan<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(n.is_nan()))
|
||||
}
|
||||
|
||||
/// `Number.isFinite`:参数为有限数才返回 true(不做隐式类型转换)。
|
||||
pub fn number_is_finite<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::float(f64::NAN))
|
||||
}
|
||||
|
||||
/// `parseFloat(string)`:解析尽可能长的十进制浮点前缀;无法解析返回 NaN。
|
||||
pub fn number_parse_float<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Number.prototype.toString(radix)`:按指定进制(2..36)转字符串。
|
||||
/// 十进制走 ryu 快速格式化,NaN/Infinity 有专名输出。
|
||||
pub fn number_to_string<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Number.prototype.toFixed(digits)`:固定小数位数(0..100)输出字符串,
|
||||
/// 超出范围抛 RangeError;NaN/Infinity 输出专名。
|
||||
pub fn number_to_fixed<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string(&formatted))
|
||||
}
|
||||
|
||||
/// `Number.isInteger`:参数是有限且无小数部分的数值才返回 true。
|
||||
pub fn number_is_integer<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
||||
NativeResult::Ok(JsValue::bool(safe))
|
||||
}
|
||||
|
||||
/// `Number.prototype.toPrecision(precision)`:按有效数字位数(1..100)输出,
|
||||
/// 科学计数法与定点表示按指数自动切换,超范围抛 RangeError。
|
||||
pub fn number_to_precision<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Number.prototype.toExponential(digits)`:按科学计数法输出;
|
||||
/// digits 缺省时自动决定小数位数,超范围抛 RangeError。
|
||||
pub fn number_to_exponential<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(args[0]);
|
||||
if this_val.is_int() || this_val.is_double() {
|
||||
|
||||
@@ -13,6 +13,7 @@ fn is_integer_index(key: &str) -> bool {
|
||||
key.bytes().all(|b| b.is_ascii_digit()) && key.parse::<u64>().unwrap_or(u64::MAX) < (1u64 << 32) - 1
|
||||
}
|
||||
|
||||
/// 收集对象全部自身属性(shape 链),按规范顺序排列:整数索引在前升序,其余保持插入序。
|
||||
pub(crate) fn walk_own_keys<H: VmHost>(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<H: VmHost>(vm: &H, obj: &JsObject) -> Vec<(u32, u32)
|
||||
keys
|
||||
}
|
||||
|
||||
/// JS `Object()` 构造逻辑:创建空对象(prototype 为 null,由 VM 补装内置原型)。
|
||||
pub fn object_constructor<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(arr))
|
||||
}
|
||||
|
||||
/// `Object.create(proto, properties)`:以指定 prototype 创建新对象。
|
||||
pub fn object_create<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(obj))
|
||||
}
|
||||
|
||||
/// `Object.assign(target, ...sources)`:拷贝各源对象的可枚举自身属性到目标,返回目标。
|
||||
pub fn object_assign<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(target_val)
|
||||
}
|
||||
|
||||
/// `Object.is(a, b)`:按 SameValue 语义比较(NaN 相等、+0/-0 不等)。
|
||||
pub fn object_is<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
||||
NativeResult::Ok(obj_val)
|
||||
}
|
||||
|
||||
/// `Object.getOwnPropertyDescriptor(obj, key)`:返回自身属性的描述符对象;
|
||||
/// 不存在返回 undefined。
|
||||
pub fn object_get_own_property_descriptor<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(val)
|
||||
}
|
||||
|
||||
/// `Object.seal(obj)`:密封对象(不可扩展 + 全部属性不可配置),返回原对象。
|
||||
pub fn object_seal<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(val)
|
||||
}
|
||||
|
||||
/// `Object.preventExtensions(obj)`:禁止添加新属性,返回原对象。
|
||||
pub fn object_prevent_extensions<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRe
|
||||
NativeResult::Ok(val)
|
||||
}
|
||||
|
||||
/// `Object.isFrozen(obj)`:对象是否冻结(检查 extensible 及全部属性 writable/configurable)。
|
||||
pub fn object_is_frozen<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(true))
|
||||
}
|
||||
|
||||
/// `Object.isSealed(obj)`:对象是否密封(检查 extensible 及全部属性 configurable)。
|
||||
pub fn object_is_sealed<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(true))
|
||||
}
|
||||
|
||||
/// `Object.isExtensible(obj)`:对象是否可扩展。
|
||||
pub fn object_is_extensible<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
NativeResult::Ok(JsValue::bool(obj.is_extensible()))
|
||||
}
|
||||
|
||||
/// `Object.getOwnPropertyNames(obj)`:返回全部自身属性名(含不可枚举)。
|
||||
pub fn object_get_own_property_names<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> Nati
|
||||
NativeResult::Ok(JsValue::from_js_object(arr))
|
||||
}
|
||||
|
||||
/// `Object.defineProperties(obj, descriptors)`:批量定义数据属性,返回目标对象。
|
||||
pub fn object_define_properties<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRes
|
||||
NativeResult::Ok(target_val)
|
||||
}
|
||||
|
||||
/// `Object.fromEntries(entries)`:由 `[key, value]` 对数组构建对象。
|
||||
pub fn object_from_entries<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(target_val)
|
||||
}
|
||||
|
||||
/// `Object.getPrototypeOf(obj)`:返回对象的 prototype。
|
||||
pub fn object_get_prototype_of<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -
|
||||
NativeResult::Ok(JsValue::bool(enumerable))
|
||||
}
|
||||
|
||||
/// `Object.entries(obj)`:返回可枚举自身属性的 `[key, value]` 对数组。
|
||||
pub fn object_entries<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(arr))
|
||||
}
|
||||
|
||||
/// `Object.values(obj)`:返回可枚举自身属性的值数组。
|
||||
pub fn object_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj_ptr = native_try!(require_obj_arg(vm, args, "values"));
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
|
||||
@@ -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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Reflect.construct(target, argumentsList, newTarget)`:以 newTarget 的 prototype
|
||||
/// 分配 this 后调用 target;返回值非对象时回退到新建的 this。
|
||||
pub fn reflect_construct<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Reflect.defineProperty(target, key, descriptor)`:按 descriptor 定义属性,
|
||||
/// 数据/访问器属性混用或非法 getter/setter 返回 false。
|
||||
pub fn reflect_define_property<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
|
||||
NativeResult::Ok(JsValue::bool(result.is_ok()))
|
||||
}
|
||||
|
||||
/// `Reflect.deleteProperty(target, key)`:删除自身属性;
|
||||
/// 属性不可配置时返回 false,否则重构属性表并返回 true。
|
||||
pub fn reflect_delete_property<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Reflect.getOwnPropertyDescriptor(target, key)`:复用 Object 同名实现。
|
||||
pub fn reflect_get_own_property_descriptor<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -
|
||||
crate::object::object_get_own_property_descriptor(vm, args)
|
||||
}
|
||||
|
||||
/// `Reflect.getPrototypeOf(target)`:返回对象的 prototype。
|
||||
pub fn reflect_get_prototype_of<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRes
|
||||
NativeResult::Ok(unsafe { &*target_ptr }.proto())
|
||||
}
|
||||
|
||||
/// `Reflect.has(target, key)`:属性是否存在(含原型链)。
|
||||
pub fn reflect_has<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
NativeResult::Ok(JsValue::bool(unsafe { &*target_ptr }.is_extensible()))
|
||||
}
|
||||
|
||||
/// `Reflect.ownKeys(target)`:返回对象全部自身属性名(字符串数组)。
|
||||
pub fn reflect_own_keys<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(make_string_array(vm, &key_names))
|
||||
}
|
||||
|
||||
/// `Reflect.preventExtensions(target)`:阻止扩展,返回 true。
|
||||
pub fn reflect_prevent_extensions<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeR
|
||||
NativeResult::Ok(JsValue::bool(true))
|
||||
}
|
||||
|
||||
/// `Reflect.set(target, key, value, receiver)`:写入属性;
|
||||
/// 不可扩展且属性不存在时返回 false。
|
||||
pub fn reflect_set<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let target_val = arg(vm, args, 1);
|
||||
let Some(target_ptr) = object_ptr(target_val) else {
|
||||
|
||||
@@ -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<H: VmHost>(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<H: VmHost>(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::<regex::Regex>() as u64
|
||||
}
|
||||
|
||||
/// `RegExp.prototype.test(string)`:判断是否匹配。global 模式下从 lastIndex 开始匹配。
|
||||
pub fn regexp_test<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `RegExp.prototype.exec(string)`:执行匹配并返回数组(含捕获组、index、input)。
|
||||
/// 无匹配返回 null;global 模式推进/重置 lastIndex。
|
||||
pub fn regexp_exec<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `RegExp.prototype.toString`:按 `/source/flags` 形式返回。
|
||||
pub fn regexp_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let re_ptr = match get_regexp_ptr(vm, args) {
|
||||
Ok(ptr) => ptr,
|
||||
|
||||
@@ -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<H: VmHost>(vm: &mut H) -> *mut JsObject {
|
||||
vm.alloc_object(obj)
|
||||
}
|
||||
|
||||
/// 收集 Set 中作为对象引用的元素(GC 根边),供跨 epoch 遍历/重写时追踪。
|
||||
pub fn set_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||||
if !obj.is_set() {
|
||||
return Vec::new();
|
||||
@@ -121,6 +124,7 @@ pub fn set_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 克隆 Set 的 native 数据到新对象,用 `rewrite` 改写其中的对象引用。
|
||||
pub fn clone_set_native_with_rewrite<F>(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<F>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(this_val)
|
||||
}
|
||||
|
||||
/// `Set.prototype.has(value)`:元素是否存在。
|
||||
pub fn set_has<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(found))
|
||||
}
|
||||
|
||||
/// `Set.prototype.delete(value)`:删除元素并返回是否删除成功。
|
||||
pub fn set_delete<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(removed))
|
||||
}
|
||||
|
||||
/// `Set.prototype.clear()`:清空全部元素,返回 undefined。
|
||||
pub fn set_clear<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `Set.prototype.size` getter:返回元素数量。
|
||||
pub fn set_size<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
))
|
||||
}
|
||||
|
||||
/// `Set.prototype.values()`:返回按插入序迭代值的迭代器。
|
||||
pub fn set_values<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
))
|
||||
}
|
||||
|
||||
/// `Set.prototype.keys()`:别名 `values()`(Set 无独立键),返回同样的迭代器。
|
||||
pub fn set_keys<H: VmHost>(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));
|
||||
|
||||
@@ -17,6 +17,7 @@ fn this_string<H: VmHost>(vm: &H, args: &[u8]) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// `String.fromCharCode(...codes)`:把各参数按低 16 位转成字符拼接为字符串。
|
||||
pub fn string_from_char_code<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
NativeResult::Ok(vm.new_string(&out))
|
||||
}
|
||||
|
||||
/// `String.prototype.valueOf`:返回包装对象的原始字符串;其它 this 抛 TypeError。
|
||||
pub fn string_value_of<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
))
|
||||
}
|
||||
|
||||
/// JS `String()` 构造逻辑:把参数转成字符串;new 语义返回 `[[StringData]]` 包装对象。
|
||||
pub fn string_constructor<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(this_val)
|
||||
}
|
||||
|
||||
/// `String.prototype.toString`:返回包装对象的原始字符串;其它 this 抛 TypeError。
|
||||
pub fn string_to_string<H: VmHost>(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<H: VmHost>(val: JsValue, vm: &H) -> bool {
|
||||
std::ptr::eq(proto_ptr, rp)
|
||||
}
|
||||
|
||||
/// `String.prototype.indexOf(searchString, position)`:按字符索引查找首次出现位置。
|
||||
pub fn string_index_of<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
}
|
||||
|
||||
/// `String.prototype.includes(searchString, position)`:是否包含子串。
|
||||
pub fn string_includes<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `String.prototype.charAt(index)`:返回指定位置的单字符;越界返回空串。
|
||||
pub fn string_char_at<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string(&result))
|
||||
}
|
||||
|
||||
/// `String.prototype.slice(start, end)`:按字符区间(支持负索引)取子串。
|
||||
pub fn string_slice<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string(result))
|
||||
}
|
||||
|
||||
/// `String.prototype.substring(start, end)`:取子串,start/end 自动对调且取非负。
|
||||
pub fn string_substring<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string(&result))
|
||||
}
|
||||
|
||||
/// `String.prototype.at(index)`:按字符索引取字符(支持负索引);越界返回 undefined。
|
||||
pub fn string_at<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string(&ch))
|
||||
}
|
||||
|
||||
/// `String.prototype.lastIndexOf(searchString, position)`:从后往前查找首次出现位置。
|
||||
pub fn string_last_index_of<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
}
|
||||
|
||||
/// `String.prototype.toUpperCase`:全大写转换。
|
||||
pub fn string_to_upper_case<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string(&out))
|
||||
}
|
||||
|
||||
/// `String.prototype.padEnd(targetLength, padString)`:在尾部补足 padString 到目标长度。
|
||||
pub fn string_pad_end<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string(&out))
|
||||
}
|
||||
|
||||
/// `String.prototype.startsWith(searchString, position)`:是否以指定子串开头。
|
||||
pub fn string_starts_with<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(make_string_array(vm, &parts))
|
||||
}
|
||||
|
||||
/// `String.prototype.replace(pattern, replacement)`:替换首个匹配;
|
||||
/// 支持 RegExp(global 全替换)、字符串以及函数替换器。
|
||||
pub fn string_replace<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string(&result))
|
||||
}
|
||||
|
||||
/// `String.prototype.match(pattern)`:按 RegExp 匹配;global 返回全部匹配数组,
|
||||
/// 否则返回首个匹配及捕获组,无匹配返回 null。
|
||||
pub fn string_match_fn<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::null())
|
||||
}
|
||||
|
||||
/// `String.prototype.search(pattern)`:返回首个匹配位置,无匹配返回 -1。
|
||||
pub fn string_search<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
}
|
||||
|
||||
/// `String.prototype.trimStart`:去除头部空白。
|
||||
pub fn string_trim_start<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, value: JsValue) -> NativeResult
|
||||
NativeResult::Ok(JsValue::from_js_object(obj))
|
||||
}
|
||||
|
||||
/// `String.prototype.replaceAll(pattern, replacement)`:替换全部匹配
|
||||
/// (RegExp 或字符串模式)。
|
||||
pub fn string_replace_all<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
builtins_debug!("String.prototype.replaceAll called with {} args", args.len());
|
||||
let s = this_string(vm, args);
|
||||
|
||||
@@ -5,27 +5,35 @@ fn stub_error<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
stub_error(vm, "Atomics")
|
||||
}
|
||||
|
||||
@@ -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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::symbol(idx))
|
||||
}
|
||||
|
||||
/// `Symbol.prototype.toString`:返回 `Symbol(description)` 形式字符串。
|
||||
/// this 必须是 Symbol,否则抛 TypeError。
|
||||
pub fn symbol_to_string<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string(&result))
|
||||
}
|
||||
|
||||
/// `Symbol.for(key)`:在全局 symbol 注册表中查找并返回同 key 的 Symbol;
|
||||
/// 不存在则新建并登记。同 key 的 Symbol 在全局唯一。
|
||||
pub fn symbol_for<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::symbol(idx))
|
||||
}
|
||||
|
||||
/// `Symbol.keyFor(sym)`:返回全局注册表中该 Symbol 的 key;未登记返回 undefined。
|
||||
/// 参数非 Symbol 抛 TypeError。
|
||||
pub fn symbol_key_for<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let sym = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
if !sym.is_symbol() {
|
||||
|
||||
@@ -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<JsValue> {
|
||||
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<JsValue> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 克隆 TypedArray 的视图数据到新对象,用 `rewrite` 改写 buffer 引用。
|
||||
pub fn clone_typed_array_native_with_rewrite<F>(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<F>(obj: &mut JsObject, mut rewrite: F)
|
||||
where
|
||||
F: FnMut(JsValue) -> JsValue,
|
||||
@@ -172,6 +175,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// 释放 TypedArray 的视图数据(`Box<TypedArrayData>`),返回释放字节数。
|
||||
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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(this_val)
|
||||
}
|
||||
|
||||
/// `TypedArray.prototype.slice(start, end)`:复制区间元素生成新同类型 TypedArray。
|
||||
pub fn typed_array_slice<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
)))
|
||||
}
|
||||
|
||||
/// `TypedArray.prototype.set(source, offset)`:从 array-like/另一个 TypedArray 拷贝元素;
|
||||
/// 越界抛 RangeError。
|
||||
pub fn typed_array_set<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `TypedArray.prototype.toString`:校验 receiver 后返回 `[object TypedArray]`。
|
||||
pub fn typed_array_to_string<H: VmHost>(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));
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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<opcode::Instr>,
|
||||
pub constants: Vec<Constant>,
|
||||
@@ -49,6 +72,7 @@ pub struct CompiledModule {
|
||||
}
|
||||
|
||||
impl CompiledModule {
|
||||
/// 构造空模块:空字节码、空常量池、零寄存器与全部标志默认关闭。
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
bytecode: Vec::new(),
|
||||
|
||||
@@ -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<u8>` 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;
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
use crate::bench::metrics::MetricCollection;
|
||||
|
||||
/// 基准结果集合,持久化到 `benchmark_baseline.json` 用于后续回归对比。
|
||||
pub struct Baseline {
|
||||
pub entries: Vec<MetricCollection>,
|
||||
}
|
||||
|
||||
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<Baseline, String> {
|
||||
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<Baseline, String> {
|
||||
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<Regression> {
|
||||
let baseline = match load_baseline() {
|
||||
Ok(b) => b,
|
||||
|
||||
@@ -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<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
||||
let mut results: Vec<MetricCollection> = Vec::new();
|
||||
let compiler = Compiler::new();
|
||||
|
||||
@@ -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<LeakVerdict> {
|
||||
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<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
||||
let js =
|
||||
"var ITERATIONS = 1000; var obj = {}; for (var i = 0; i < ITERATIONS; i++) { obj['key' + i] = i; } obj['key0']";
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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<String>,
|
||||
@@ -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<KernelCore>, pool: Arc<VmPool>) -> ExitCode {
|
||||
match config.mode.as_str() {
|
||||
"js" => js_stress::run_js_stress_bench(&config, &kernel, &pool),
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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"]);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 缓存日志宏:`code_cache_debug!`,经由 oxide_log 上报。
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! code_cache_debug {
|
||||
($($arg:tt)*) => {
|
||||
|
||||
@@ -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<Arc<CompiledModule>> {
|
||||
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<CompiledModule> {
|
||||
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<F>(&self, hash: u64, compile: F) -> Result<Arc<CompiledModule>, String>
|
||||
where
|
||||
F: Fn() -> Result<CompiledModule, String>,
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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<CompiledModule, String> {
|
||||
crate::compiler_debug!("compile: starting...");
|
||||
let ir = Emitter::new().emit_program(program)?;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 编译日志宏:`compiler_error!/warn!/info!/debug!/trace!`,经由 oxide_log 上报。
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! compiler_error {
|
||||
($($arg:tt)*) => {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 表达式 AST 节点的结构哈希。
|
||||
|
||||
use super::*;
|
||||
|
||||
pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, include_binding_names: bool) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 语句 AST 节点的结构哈希。
|
||||
|
||||
use super::*;
|
||||
|
||||
pub(super) fn hash_statement(stmt: &Statement, h: &mut rustc_hash::FxHasher, include_binding_names: bool) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 赋值目标(SimpleAssignmentTarget)的结构哈希。
|
||||
|
||||
use super::*;
|
||||
|
||||
pub(super) fn hash_simple_assignment_target(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
//! 类编译核心预留模块(当前为空,为后续类相关公共状态预留位置)。
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
//! class 声明整体 emit:驱动头、原型、方法、字段各阶段,组装最终类对象。
|
||||
//!
|
||||
//! 函数:`emit_class`。
|
||||
|
||||
use crate::{CompileCtx, Emitter, FunctionBodyContext};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 类静态字段/元素 emit:`emit_class_static_elements`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{ClassElement, PropertyKey};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 类头 emit:类名绑定、继承链(`extends`)设置,见 `emit_class_header`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
|
||||
impl Emitter {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 类编译域:class 声明的整体 emit 拆分为头/原型/方法/字段等子模块。
|
||||
|
||||
pub mod core;
|
||||
pub mod emit;
|
||||
pub mod field;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 类原型对象构造:`emit_class_prototype` 建立 `Class.prototype` 与属性初始化。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<LabelId>,
|
||||
}
|
||||
|
||||
/// 单函数编译上下文:执行流 + 作用域 + 闭包捕获的聚合状态。
|
||||
/// 指令、常量池、寄存器分配平铺于此,作用域/绑定见 `ScopeCtx`,跳转见 `LabelCtx`。
|
||||
pub struct CompileCtx {
|
||||
pub(crate) insts: Vec<Inst>,
|
||||
pub(crate) constants: Vec<Constant>,
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! 赋值表达式 emit:简单 `=`、复合赋值与解构赋值(数组/对象 pattern)。
|
||||
//! 函数:`emit_assignment_expression`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 标识符表达式 emit:`emit_identifier_expression` 按绑定/builtin/全局解析寄存器。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
//! 表达式编译域:表达式分发 + sequence/unsupported。
|
||||
//!
|
||||
//! `emit_expression` 按 `Expression` 变体分发到各子模块;本文件还处理
|
||||
//! 序列表达式(逐项求值取末值)与不支持表达式的报错。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::Expression;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 数组字面量 emit:`emit_array_expression` 逐元素求值并建数组(含 holes)。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 对象表达式域:对象/数组字面量子模块与域分发 `emit_object_domain`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::Expression;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 对象字面量 emit:`emit_object_expression` 逐属性定义(含 getter/setter/展开)。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 括号表达式 emit:`emit_parenthesized_expression` 透传内层表达式。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
|
||||
impl Emitter {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 模板字符串域:普通/标签模板子模块与域分发 `emit_template_domain`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::Expression;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! `this` 表达式 emit:`emit_this_expression` 解析当前函数的 this 寄存器。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
//! emit 前置 pass:builtin 引用预扫描 + 声明预登记。
|
||||
//!
|
||||
//! 在生成临时寄存器前遍历 AST,把内置全局标识符预先登记到固定寄存器槽
|
||||
//! (builtin_reg_map),避免与临时寄存器池冲突;同时预声明函数/var 声明,
|
||||
//! 支持提升语义。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::{Expression, Statement, VariableDeclarationKind};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 类字段初始化 emit:公有/私有实例字段与私有方法的实例化初始化。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 跨语法域共享的 emit 辅助:解构绑定、可选链、类字段初始化、类属性键。
|
||||
|
||||
pub mod binding;
|
||||
pub mod chain;
|
||||
pub mod class_field;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! break/continue 语句 emit:解析标签与最近循环的跳转目标。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 表达式语句 emit:`emit_expression_statement` 求值并(按需)丢弃结果。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 标签语句 emit:`emit_labeled_statement` 登记 break/continue 标签作用域。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 基本语句域:表达式语句/return/空语句的分发与子模块聚合。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! return 语句 emit:`emit_return_statement` 求值返回值并生成返回指令。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 块语句 emit:`emit_block_statement` 压/弹作用域并顺序编译子语句(含域分发)。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 控制流语句 emit:`if` 条件分支(含 else)与域分发 `emit_control_domain`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 类声明语句 emit:`emit_class_declaration_statement` 声明类名并初始化类对象。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 函数声明语句 emit:`emit_function_declaration_statement` 创建闭包并绑定函数名。
|
||||
|
||||
use crate::{CompileCtx, Emitter, ParamSpec};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 声明语句域:变量/函数/类声明的分发与子模块聚合。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! var/let/const 声明语句 emit:声明 + 初始化表达式,处理提升与解构 pattern。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 异常语句域:throw/try 的分发与子模块聚合。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! throw 语句 emit:`emit_throw_statement` 求值异常值并生成 THROW。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! do-while 语句 emit:`emit_do_while_statement` 先执行体后测条件。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 迭代语句域:while/do-while/for/for-in/for-of 的分发与子模块聚合。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! while 语句 emit:`emit_while_statement` 先测条件后执行体。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 语句编译域:`emit_statement` 分发到块/控制流/声明/异常/迭代等子模块。
|
||||
|
||||
pub mod basic;
|
||||
pub mod block;
|
||||
pub mod control;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user