docs: 补充注释与格式化
This commit is contained in:
@@ -373,7 +373,7 @@ pub fn array_concat<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if !o_ptr.is_null() {
|
||||
let o = unsafe { &*o_ptr };
|
||||
let on = o.prop_count() as usize;
|
||||
// spread array elements if it's a real array
|
||||
// 真数组时展开元素。
|
||||
if o.is_array() && on > 0 {
|
||||
for i in 0..on {
|
||||
all.push(o.get_prop_at(i));
|
||||
@@ -423,8 +423,8 @@ pub fn array_join<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
/// `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.prototype.toString() 委托给 join,默认用 "," 分隔,
|
||||
// 按规范忽略自身参数。
|
||||
array_join(vm, &[args[0]])
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ pub fn array_index_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
return NativeResult::Ok(JsValue::int(-1));
|
||||
}
|
||||
let target = vm.reg(args[1]);
|
||||
// fromIndex
|
||||
// fromIndex:负索引从尾部折算,NaN 视为 0。
|
||||
let from_index = if args.len() >= 3 {
|
||||
let v = vm.reg(args[2]);
|
||||
let f = vm.coerce_number_bounded(v).unwrap_or(0.0);
|
||||
@@ -499,7 +499,7 @@ pub fn array_includes<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
for i in from_index..n {
|
||||
let elem = arraylike_get(vm, ptr, is_array, i);
|
||||
// SameValueZero: NaN === NaN, +0 === -0
|
||||
// SameValueZero:NaN 视为相等、+0 与 -0 视为相同。
|
||||
if same_value_zero(elem, target) {
|
||||
return NativeResult::Ok(JsValue::bool(true));
|
||||
}
|
||||
@@ -507,9 +507,9 @@ pub fn array_includes<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(false))
|
||||
}
|
||||
|
||||
/// SameValueZero (ES2015 7.2.10): NaN === NaN, +0 === -0
|
||||
/// SameValueZero(ES2015 §7.2.10):NaN 视为相等、+0 与 -0 视为相同。
|
||||
fn same_value_zero(a: JsValue, b: JsValue) -> bool {
|
||||
// 都是数字时特殊处理
|
||||
// 双数字时特殊处理。
|
||||
let a_is_num = a.is_int() || a.is_double();
|
||||
let b_is_num = b.is_int() || b.is_double();
|
||||
if a_is_num && b_is_num {
|
||||
@@ -518,7 +518,7 @@ fn same_value_zero(a: JsValue, b: JsValue) -> bool {
|
||||
if av.is_nan() && bv.is_nan() {
|
||||
return true;
|
||||
}
|
||||
return av == bv; // +0 == -0 in Rust f64
|
||||
return av == bv; // Rust f64 中 +0 == -0。
|
||||
}
|
||||
oxide_runtime_api::strict_equality(a, b)
|
||||
}
|
||||
@@ -1042,7 +1042,7 @@ pub fn array_last_index_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
return NativeResult::Ok(JsValue::int(-1));
|
||||
}
|
||||
let search = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
// fromIndex (default: n-1)
|
||||
// fromIndex(缺省:n-1)。
|
||||
let from_index_isize: isize = if args.len() >= 3 {
|
||||
let v = vm.reg(args[2]);
|
||||
let f = vm.coerce_number_bounded(v).unwrap_or(0.0);
|
||||
|
||||
@@ -54,8 +54,8 @@ pub(crate) fn new_array_buffer<H: VmHost>(vm: &mut H, data: Vec<u8>) -> *mut JsO
|
||||
obj.type_tag = JsObject::OBJ_TYPE_ARRAY_BUFFER;
|
||||
let len = data.len();
|
||||
let data_ptr = Box::into_raw(Box::new(data));
|
||||
// SAFETY: ArrayBuffer objects are not callable, so their native_fn slot is reused
|
||||
// as an opaque Box<Vec<u8>> pointer, matching the existing RegExp storage pattern.
|
||||
// SAFETY: ArrayBuffer 对象不可调用,故 native_fn 槽复用作不透明 `Box<Vec<u8>>`
|
||||
// 指针,与既有 RegExp 存储模式一致。
|
||||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(data_ptr as *const ()) }));
|
||||
set_named_prop(
|
||||
vm,
|
||||
@@ -95,7 +95,7 @@ pub fn drop_array_buffer_native(obj: &mut JsObject) -> u64 {
|
||||
if data_ptr.is_null() {
|
||||
return 0;
|
||||
}
|
||||
// SAFETY: ArrayBuffer stores `Box<Vec<u8>>` in native_fn.
|
||||
// SAFETY: ArrayBuffer 在 native_fn 槽中存 `Box<Vec<u8>>`。
|
||||
let data = unsafe { Box::from_raw(data_ptr) };
|
||||
let bytes = std::mem::size_of::<Vec<u8>>() + data.capacity();
|
||||
obj.set_native_fn(None);
|
||||
|
||||
@@ -14,7 +14,7 @@ pub fn boolean_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
} else if arg.is_bool() {
|
||||
arg.as_bool()
|
||||
} else if arg.is_string() {
|
||||
// SAFETY: arg is a string value.
|
||||
// SAFETY: arg 已确认是字符串值。
|
||||
!unsafe { (*arg.as_string_ptr()).is_empty() }
|
||||
} else if arg.is_object() {
|
||||
true
|
||||
|
||||
@@ -155,8 +155,8 @@ pub fn data_view_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
byte_offset,
|
||||
byte_length,
|
||||
}));
|
||||
// SAFETY: DataView objects are not callable, so native_fn is an opaque Box<DataViewData>
|
||||
// pointer, matching ArrayBuffer and RegExp typed-storage patterns.
|
||||
// SAFETY: DataView 对象不可调用,native_fn 存不透明 `Box<DataViewData>` 指针,
|
||||
// 与 ArrayBuffer / RegExp 的类型化存储模式一致。
|
||||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(data as *const ()) }));
|
||||
set_named_prop(vm, &mut obj, "buffer", buffer, PropAttributes::new(false, false, false));
|
||||
set_named_prop(
|
||||
|
||||
@@ -155,7 +155,7 @@ pub fn date_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
} else {
|
||||
let val = vm.reg(args[1]);
|
||||
if val.is_string() {
|
||||
// SAFETY: val is a string value.
|
||||
// SAFETY: val 已确认是字符串值。
|
||||
let s = unsafe { (*val.as_string_ptr()).data.clone() };
|
||||
let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"];
|
||||
let mut ts = f64::NAN;
|
||||
@@ -231,7 +231,7 @@ pub fn date_parse<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if !val.is_string() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
// SAFETY: val is a string value.
|
||||
// SAFETY: val 已确认是字符串值。
|
||||
let s = unsafe { (*val.as_string_ptr()).data.clone() };
|
||||
let mut ts = f64::NAN;
|
||||
if let Ok(dt) = chrono::DateTime::parse_from_rfc2822(&s) {
|
||||
@@ -804,8 +804,8 @@ pub fn date_get_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
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);
|
||||
// A missing year argument coerces to NaN; per B.2.4.2 a NaN year sets the date value to
|
||||
// NaN and returns NaN.
|
||||
// 缺省年份参数强制转为 NaN;按 Annex B 语义,NaN 年份把日期值置为
|
||||
// NaN 并返回 NaN。
|
||||
let arg = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
let y_num = oxide_runtime_api::to_number(arg);
|
||||
if y_num.is_nan() {
|
||||
|
||||
@@ -25,7 +25,7 @@ fn bind_dispatcher<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
.and_then(|v| v.get(1).copied())
|
||||
.unwrap_or(JsValue::undefined());
|
||||
|
||||
// Forward bound call arguments (skip args[0], the bound-wrapper receiver).
|
||||
// 转发绑定后的调用实参(跳过 args[0] 即绑定包装器的 receiver)。
|
||||
let arg_regs: Vec<u8> = args.iter().skip(1).copied().collect();
|
||||
invoke_target(vm, bound_target, bound_this, &arg_regs)
|
||||
}
|
||||
@@ -59,7 +59,7 @@ pub fn function_apply<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if !arr_ptr.is_null() {
|
||||
let arr = unsafe { &*arr_ptr };
|
||||
if arr.is_array() {
|
||||
let max_args = 55usize; // base=200, max safe register is 255.
|
||||
let max_args = 55usize; // base=200,255 号寄存器之前的安全上限。
|
||||
let n = arr.hash_props_vec().map_or(0, |v| v.len()).min(max_args);
|
||||
let base = 200u8;
|
||||
arg_regs = (0..n).map(|i| base + i as u8).collect();
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn make_iterator_for_value<H: VmHost>(vm: &mut H, value: JsValue) -> Result<
|
||||
let next_fn = make_native_function(vm, "next", iterator_wrapper_next::<H> as *const (), 0);
|
||||
vm.set_or_create_prop_value(wrapper_obj, next_si, next_fn);
|
||||
|
||||
// Forward IteratorClose to the inner iterator so for-of abrupt completion can clean up.
|
||||
// 把 IteratorClose 转发给内层迭代器,使 for-of 异常退出时可清理。
|
||||
let return_si = vm.kernel_core().perm_interner().intern("return").0;
|
||||
let return_fn = make_native_function(vm, "return", iterator_wrapper_return::<H> as *const (), 0);
|
||||
vm.set_or_create_prop_value(wrapper_obj, return_si, return_fn);
|
||||
@@ -104,8 +104,8 @@ pub fn iterator_wrapper_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
};
|
||||
return match vm.call_function_sync(next, inner, &[]) {
|
||||
Ok(result) => NativeResult::Ok(result),
|
||||
// Forward the ORIGINAL thrown value (any type) instead of re-wrapping it as a
|
||||
// TypeError, so a surrounding try/catch sees the real error.
|
||||
// 透传原始抛出的值(任意类型)而非重新包装成 TypeError,
|
||||
// 使外围 try/catch 能看到真正的错误。
|
||||
Err(err) => match vm.take_uncaught_value() {
|
||||
Some(original) => NativeResult::Err(original),
|
||||
None => NativeResult::Err(crate::error::create_type_error(vm, &err)),
|
||||
@@ -158,7 +158,7 @@ fn next_array_like<H: VmHost>(vm: &mut H, wrapper: &mut JsObject, inner: JsValue
|
||||
return Some(make_iter_result(vm, JsValue::undefined(), true));
|
||||
}
|
||||
|
||||
// for-of loop default iterators: Map yields [key, value] entries, Set yields values.
|
||||
// for-of 循环默认迭代:Map 产出 [key, value] 对,Set 产出值。
|
||||
if is_map_value(inner) {
|
||||
return Some(map_set_step(vm, wrapper, inner, index_si, MapSetMode::MapEntries));
|
||||
}
|
||||
@@ -194,7 +194,7 @@ fn make_native_function<H: VmHost>(vm: &mut H, name: &str, native_fn: *const (),
|
||||
let function_proto = vm.session().builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto));
|
||||
func.set_function(true);
|
||||
// SAFETY: native_fn is provided from a NativeFn fn item.
|
||||
// SAFETY: native_fn 来自 NativeFn 函数项。
|
||||
func.set_native_fn(Some(unsafe { oxide_types::object::NativeFnPtr::from_raw(native_fn) }));
|
||||
func.set_native_arg_count(arg_count);
|
||||
let func = vm.alloc_object(func);
|
||||
@@ -245,7 +245,7 @@ fn make_map_set_pair<H: VmHost>(vm: &mut H, a: JsValue, b: JsValue) -> JsValue {
|
||||
2,
|
||||
vm.epoch().bump(),
|
||||
));
|
||||
// SAFETY: pair is a freshly allocated array JsObject in the current epoch.
|
||||
// SAFETY: pair 是当前 epoch 内新分配的数组 JsObject。
|
||||
unsafe {
|
||||
(*pair).set_prop_at(0, a);
|
||||
(*pair).set_prop_at(1, b);
|
||||
@@ -263,10 +263,9 @@ enum MapSetMode {
|
||||
SetValues,
|
||||
}
|
||||
|
||||
/// Advance a Map/Set iterator wrapper one step, yielding the {value, done} result for
|
||||
/// the requested mode. Entries live in the indexmap stored in the collection's
|
||||
/// native-data slot; the (a, b) pair is copied out before any allocation so no borrow
|
||||
/// of the native collection is held across a `vm` call.
|
||||
/// 把 Map/Set 迭代器包装器推进一步,按指定模式产出 `{value, done}` 结果。
|
||||
/// 条目存放在集合 native-data 槽的 indexmap 中;(a, b) 对在任意分配之前拷出,
|
||||
/// 使对 native 集合的借用不会跨越 `vm` 调用保持。
|
||||
fn map_set_step<H: VmHost>(
|
||||
vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, mode: MapSetMode,
|
||||
) -> JsValue {
|
||||
@@ -342,9 +341,9 @@ pub(crate) fn set_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> Nativ
|
||||
map_set_next_dispatch::<H>(vm, args, MapSetMode::SetEntries)
|
||||
}
|
||||
|
||||
/// Build an iterator wrapper whose `next` delegates to a caller-supplied mode-specific
|
||||
/// native function. Mirrors `make_iterator_for_value` but lets Map/Set prototype methods
|
||||
/// pick the values/keys/entries variant.
|
||||
/// 构造迭代器包装器,其 `next` 委托给调用方指定的按模式分发的 native 函数。
|
||||
/// 与 `make_iterator_for_value` 一致,但允许 Map/Set 原型方法选择
|
||||
/// values/keys/entries 变体。
|
||||
pub(crate) fn make_mode_iterator<H: VmHost>(vm: &mut H, inner: JsValue, next_fn: *const ()) -> JsValue {
|
||||
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let wrapper = vm
|
||||
|
||||
@@ -20,7 +20,7 @@ pub fn json_parse<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
return NativeResult::Err(crate::error::create_syntax_error(vm, "JSON.parse: argument is not a string"));
|
||||
}
|
||||
let text = {
|
||||
// SAFETY: val is a string value.
|
||||
// SAFETY: val 已确认是字符串值。
|
||||
unsafe { (*val.as_string_ptr()).data.clone() }
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn json_parse<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
let mut result = value_to_jsvalue(vm, &parsed);
|
||||
|
||||
// Apply reviver if provided (D-05)
|
||||
// 提供 reviver 时以后序遍历逐属性调用,重建解析后的值。
|
||||
if args.len() > 2 {
|
||||
let reviver_val = vm.reg(args[2]);
|
||||
if reviver_val.is_object() {
|
||||
@@ -66,7 +66,7 @@ fn walk_reviver<H: VmHost>(
|
||||
};
|
||||
let mut val = holder.get_prop_at(pos);
|
||||
|
||||
// Post-order: recurse on children first
|
||||
// 后序遍历:先递归处理子节点。
|
||||
if val.is_object() {
|
||||
let obj_ptr = val.as_js_object_ptr();
|
||||
if !obj_ptr.is_null() {
|
||||
@@ -78,20 +78,20 @@ fn walk_reviver<H: VmHost>(
|
||||
let child_si = vm.kernel_core().perm_interner().intern(&index_str).0;
|
||||
walk_reviver(vm, obj_ptr, child_si, reviver)?;
|
||||
}
|
||||
// Re-read value after children modified it
|
||||
// 子节点可能已改写父级,重新读取当前值。
|
||||
val = holder.get_prop_at(pos);
|
||||
} else {
|
||||
let keys = walk_own_keys(vm, obj);
|
||||
for (child_si, _child_pos) in keys {
|
||||
walk_reviver(vm, obj_ptr, child_si, reviver)?;
|
||||
}
|
||||
// Re-read value after children modified it
|
||||
// 子节点可能已改写父级,重新读取当前值。
|
||||
val = holder.get_prop_at(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call reviver on current value
|
||||
// 对当前值调用 reviver,用返回值覆盖属性槽。
|
||||
let kc = vm.kernel_core().clone();
|
||||
let key_str = kc.perm_interner().lookup(key_si).unwrap_or("");
|
||||
let key_val = vm.new_string(key_str);
|
||||
@@ -364,7 +364,7 @@ fn stringify_object<H: VmHost>(
|
||||
out.push('{');
|
||||
|
||||
let keys = walk_own_keys(vm, obj);
|
||||
// Pre-compute (name, position) pairs with whitelist filtering
|
||||
// 预计算 (名称, 槽位) 对,并应用白名单过滤。
|
||||
let kc = vm.kernel_core().clone();
|
||||
let perm_interner = kc.perm_interner();
|
||||
let entries: Vec<(String, u32)> = keys
|
||||
@@ -384,7 +384,7 @@ fn stringify_object<H: VmHost>(
|
||||
for (name, pos) in entries {
|
||||
let val = obj.get_prop_at(pos);
|
||||
|
||||
// Replacer function callback (toJSON handled by jsvalue_to_json)
|
||||
// replacer 函数回调(toJSON 已在 jsvalue_to_json 中处理)。
|
||||
let val = if let Some(replacer) = replacer_fn {
|
||||
let key_val = vm.new_string(&name);
|
||||
let holder = JsValue::from_js_object(obj as *const JsObject as *mut JsObject);
|
||||
@@ -468,7 +468,7 @@ fn stringify_array<H: VmHost>(
|
||||
|
||||
let index_str = i.to_string();
|
||||
|
||||
// Replacer function callback (toJSON handled by jsvalue_to_json)
|
||||
// replacer 函数回调(toJSON 已在 jsvalue_to_json 中处理)。
|
||||
let val = if let Some(replacer) = replacer_fn {
|
||||
let key_val = vm.new_string(&index_str);
|
||||
let holder = JsValue::from_js_object(obj as *const JsObject as *mut JsObject);
|
||||
|
||||
@@ -17,16 +17,15 @@ macro_rules! native_try {
|
||||
|
||||
pub(crate) type MapInner = indexmap::IndexMap<SetKey, JsValue>;
|
||||
|
||||
/// Retrieve the `IndexMap` pointer stored in a Map object's native-data slot.
|
||||
/// 取出 Map 对象 native-data 槽中存储的 `IndexMap` 指针。
|
||||
///
|
||||
/// # Safety contract maintained by callers
|
||||
/// # 调用方维护的安全性契约
|
||||
///
|
||||
/// The pointer is valid as long as the Map `JsObject` itself is alive, which is guaranteed
|
||||
/// because the `JsObject` lives in the current `Epoch` arena and `Epoch::reset()` is never
|
||||
/// called while a native builtin is executing. The `Box<IndexMap>` that owns the allocation
|
||||
/// is created in `new_map_inner()` and is never freed until the process exits (intentional
|
||||
/// leak — lifetime is tied to the epoch). Only one live `*mut` alias exists at a time
|
||||
/// per Map object because native calls are single-threaded.
|
||||
/// 指针在 Map `JsObject` 存活期间有效:`JsObject` 分配于当前 `Epoch` arena,
|
||||
/// native builtin 执行期间不会调用 `Epoch::reset()`。持有分配的
|
||||
/// `Box<IndexMap>` 由 `new_map_inner()` 创建,进程退出前不释放(生命周期与
|
||||
/// epoch 绑定,属有意为之)。native 调用为单线程,同一 Map 对象同时至多
|
||||
/// 存在一个活 `*mut` 别名。
|
||||
fn get_map_inner<H: VmHost>(vm: &mut H, this_val: JsValue) -> Result<*mut MapInner, JsValue> {
|
||||
if !this_val.is_object() {
|
||||
return Err(crate::error::create_type_error(vm, "called on non-Map object"));
|
||||
@@ -35,9 +34,8 @@ fn get_map_inner<H: VmHost>(vm: &mut H, this_val: JsValue) -> Result<*mut MapInn
|
||||
if map_ptr.is_null() {
|
||||
return Err(crate::error::create_type_error(vm, "Map internal state invalid"));
|
||||
}
|
||||
// SAFETY: map_ptr is a non-null, aligned pointer to a JsObject bump-allocated in the
|
||||
// current Epoch. It remains valid for the duration of this call (epoch is not reset
|
||||
// during native execution).
|
||||
// SAFETY: map_ptr 是当前 Epoch bump 分配的 JsObject 的非空、对齐指针;
|
||||
// native 执行期间 epoch 不重置,指针在本调用内有效。
|
||||
let map_obj = unsafe { &*map_ptr };
|
||||
if !map_obj.is_map() {
|
||||
return Err(crate::error::create_type_error(
|
||||
@@ -45,9 +43,9 @@ fn get_map_inner<H: VmHost>(vm: &mut H, this_val: JsValue) -> Result<*mut MapInn
|
||||
"Map.prototype method called on incompatible receiver",
|
||||
));
|
||||
}
|
||||
// SAFETY: native_data holds the raw pointer written by `alloc_map`.
|
||||
// The pointer is a valid, heap-allocated `Box<IndexMap<SetKey, JsValue>>`.
|
||||
// Alignment: IndexMap requires at most 8-byte alignment; the global allocator satisfies this.
|
||||
// SAFETY: native_data 持有 `alloc_map` 写入的裸指针,即有效的
|
||||
// 堆分配 `Box<IndexMap<SetKey, JsValue>>`;IndexMap 至多要求 8 字节对齐,
|
||||
// 全局分配器满足该要求。
|
||||
let inner_ptr = map_obj.native_data() as *mut MapInner;
|
||||
if inner_ptr.is_null() {
|
||||
return Err(crate::error::create_type_error(vm, "Map internal state invalid"));
|
||||
|
||||
@@ -15,7 +15,7 @@ fn arg1<H: VmHost>(vm: &mut H, args: &[u8]) -> f64 {
|
||||
}
|
||||
|
||||
fn arg2<H: VmHost>(vm: &mut H, args: &[u8]) -> (f64, f64) {
|
||||
// Sequenced (not a tuple literal) so the two coercions take &mut H one at a time.
|
||||
// 顺序求值(非元组字面量):两次 coercion 各自单独借用 &mut H。
|
||||
let a = if args.len() > 1 { num(vm, args[1]) } else { f64::NAN };
|
||||
let b = if args.len() > 2 { num(vm, args[2]) } else { f64::NAN };
|
||||
(a, b)
|
||||
|
||||
@@ -84,7 +84,6 @@ pub fn object_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
.map(|(si, _offset)| vm.kernel_core().perm_interner().lookup(*si).unwrap_or("").to_string())
|
||||
.collect();
|
||||
}
|
||||
// keys
|
||||
let n = key_names.len();
|
||||
let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let arr = vm.alloc_object(JsObject::new_array(
|
||||
@@ -721,8 +720,8 @@ pub fn object_has_own<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
/// `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.
|
||||
// Object.prototype.valueOf 原样返回 this 对象;OrdinaryToPrimitive
|
||||
// 因结果非原始值而继续走 toString。
|
||||
NativeResult::Ok(vm.reg(args[0]))
|
||||
}
|
||||
|
||||
|
||||
@@ -88,10 +88,10 @@ pub fn regexp_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
match compiled {
|
||||
Ok(re) => {
|
||||
let re_ptr = Box::into_raw(Box::new(re));
|
||||
// SAFETY: re_ptr is a Box<regex::Regex> pointer stored by the constructor via
|
||||
// NativeFnPtr::from_raw(re_ptr as *const ()). RegExp objects repurpose the native_fn
|
||||
// field to hold the compiled Regex — not a NativeFn pointer. Valid for the object's
|
||||
// lifetime; VM reset drops the Box through `drop_regexp_native`.
|
||||
// SAFETY: re_ptr 是构造器经 `NativeFnPtr::from_raw(re_ptr as *const ())`
|
||||
// 写入的 `Box<regex::Regex>` 指针。RegExp 对象把 native_fn 字段复用作
|
||||
// 已编译 Regex 的存放处——而非 NativeFn 指针。对象存活期间有效;
|
||||
// VM 重置经 `drop_regexp_native` 释放该 Box。
|
||||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(re_ptr as *const ()) }));
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -149,7 +149,7 @@ pub fn regexp_test<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Some(p) => p,
|
||||
};
|
||||
|
||||
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
|
||||
// SAFETY: fn_ptr 持有 regexp_constructor 存放的 `Box<regex::Regex>` 指针。
|
||||
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
|
||||
let haystack = oxide_runtime_api::to_string(vm.reg(if args.len() > 1 { args[1] } else { args[0] }));
|
||||
let last_index = vm.coerce_number_bounded(get_prop(re, 0)).unwrap_or(f64::NAN) as usize;
|
||||
@@ -181,7 +181,7 @@ pub fn regexp_exec<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
};
|
||||
|
||||
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
|
||||
// SAFETY: fn_ptr 持有 regexp_constructor 存放的 `Box<regex::Regex>` 指针。
|
||||
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
|
||||
let haystack = oxide_runtime_api::to_string(vm.reg(if args.len() > 1 { args[1] } else { args[0] }));
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ impl PartialEq for SetKey {
|
||||
}
|
||||
return a == b;
|
||||
}
|
||||
// SAFETY: JsValue is an 8-byte NaN-boxed Copy value; raw bits define non-double identity here.
|
||||
// SAFETY: JsValue 是 8 字节 NaN-box Copy 值;此处以原始位定义非 double 值的同一性。
|
||||
unsafe { std::mem::transmute::<JsValue, u64>(self.0) == std::mem::transmute::<JsValue, u64>(other.0) }
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ impl Hash for SetKey {
|
||||
d.to_bits().hash(state);
|
||||
}
|
||||
} else {
|
||||
// SAFETY: JsValue is an 8-byte NaN-boxed Copy value; hashing raw bits matches equality above.
|
||||
// SAFETY: JsValue 是 8 字节 NaN-box Copy 值;哈希原始位与上面的相等判定一致。
|
||||
unsafe { std::mem::transmute::<JsValue, u64>(self.0).hash(state) }
|
||||
}
|
||||
}
|
||||
@@ -60,15 +60,14 @@ impl Hash for SetKey {
|
||||
|
||||
pub(crate) type SetInner = indexmap::IndexSet<SetKey>;
|
||||
|
||||
/// Retrieve the `IndexSet` pointer stored in a Set object's native-data slot.
|
||||
/// 取出 Set 对象 native-data 槽中存储的 `IndexSet` 指针。
|
||||
///
|
||||
/// # Safety contract maintained by callers
|
||||
/// # 调用方维护的安全性契约
|
||||
///
|
||||
/// The pointer is valid as long as the Set `JsObject` itself is alive. The `JsObject`
|
||||
/// lives in the current `Epoch` arena and `Epoch::reset()` is never called while a
|
||||
/// native builtin is executing. The `Box<IndexSet>` is allocated in `new_set_inner()`
|
||||
/// and is never freed during the Set's lifetime. Only one live `*mut` alias exists per
|
||||
/// Set object at a time because native calls are single-threaded.
|
||||
/// 指针在 Set `JsObject` 存活期间有效:`JsObject` 分配于当前 `Epoch` arena,
|
||||
/// native builtin 执行期间不会调用 `Epoch::reset()`。持有分配的
|
||||
/// `Box<IndexSet>` 由 `new_set_inner()` 创建,Set 生命周期内不释放。
|
||||
/// native 调用为单线程,同一 Set 对象同时至多存在一个活 `*mut` 别名。
|
||||
fn get_set_inner<H: VmHost>(vm: &mut H, this_val: JsValue) -> Result<*mut SetInner, JsValue> {
|
||||
if !this_val.is_object() {
|
||||
return Err(crate::error::create_type_error(vm, "called on non-Set object"));
|
||||
@@ -77,15 +76,15 @@ fn get_set_inner<H: VmHost>(vm: &mut H, this_val: JsValue) -> Result<*mut SetInn
|
||||
if set_ptr.is_null() {
|
||||
return Err(crate::error::create_type_error(vm, "Set internal state invalid"));
|
||||
}
|
||||
// SAFETY: set_ptr is a non-null, aligned pointer to a JsObject bump-allocated in the
|
||||
// current Epoch. Remains valid for the duration of this call.
|
||||
// SAFETY: set_ptr 是当前 Epoch bump 分配的 JsObject 的非空、对齐指针,
|
||||
// 本调用期间有效。
|
||||
let set_obj = unsafe { &*set_ptr };
|
||||
if !set_obj.is_set() {
|
||||
return Err(crate::error::create_type_error(vm, "Set.prototype.add called on incompatible receiver"));
|
||||
}
|
||||
// SAFETY: native_data holds the raw pointer written by `alloc_set`.
|
||||
// The pointer is a valid, heap-allocated `Box<IndexSet<SetKey>>`.
|
||||
// Alignment: IndexSet requires at most 8-byte alignment; the global allocator satisfies this.
|
||||
// SAFETY: native_data 持有 `alloc_set` 写入的裸指针,即有效的
|
||||
// 堆分配 `Box<IndexSet<SetKey>>`;IndexSet 至多要求 8 字节对齐,
|
||||
// 全局分配器满足该要求。
|
||||
let inner_ptr = set_obj.native_data() as *mut SetInner;
|
||||
if inner_ptr.is_null() {
|
||||
return Err(crate::error::create_type_error(vm, "Set internal state invalid"));
|
||||
@@ -265,7 +264,7 @@ pub fn set_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
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));
|
||||
// Set keys() is an alias for values() — same per-element iterator.
|
||||
// Set 的 keys() 是 values() 的别名——返回同样的逐元素迭代器。
|
||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
|
||||
@@ -544,13 +544,13 @@ pub fn string_ends_with<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
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);
|
||||
// Spec: if separator is undefined, return [string]
|
||||
// 规范:separator 为 undefined 时返回 [string]。
|
||||
if args.len() < 2 || vm.reg(args[1]).is_undefined() {
|
||||
let parts = vec![s.clone()];
|
||||
return NativeResult::Ok(make_string_array(vm, &parts));
|
||||
}
|
||||
let sep_val = vm.reg(args[1]);
|
||||
// ToUint32(limit), default 2^32-1
|
||||
// ToUint32(limit),缺省为 2^32-1。
|
||||
let limit = if args.len() > 2 {
|
||||
let l = oxide_runtime_api::to_integer_or_infinity(vm.reg(args[2]));
|
||||
if l.is_infinite() {
|
||||
@@ -591,7 +591,7 @@ pub fn string_split<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
return NativeResult::Ok(make_string_array(vm, &parts));
|
||||
}
|
||||
// Fall back to string path for regex-like objects without native engine regex
|
||||
// 无原生正则的类正则对象回退到字符串路径。
|
||||
}
|
||||
|
||||
let sep = as_string(vm, sep_val);
|
||||
@@ -661,7 +661,7 @@ pub fn string_replace<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
return NativeResult::Ok(vm.new_string(&result));
|
||||
}
|
||||
// Fall back to string path for regex-like objects without native engine regex
|
||||
// 无原生正则的类正则对象回退到字符串路径。
|
||||
}
|
||||
|
||||
let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() };
|
||||
@@ -727,7 +727,7 @@ pub fn string_search<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Some(p) => p,
|
||||
None => return NativeResult::Ok(JsValue::int(-1)),
|
||||
};
|
||||
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
|
||||
// SAFETY: fn_ptr 持有 regexp_constructor 存放的 `Box<regex::Regex>` 指针。
|
||||
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
|
||||
if let Some(m) = regex.find(&s) {
|
||||
return NativeResult::Ok(JsValue::int(m.start() as i32));
|
||||
@@ -841,7 +841,7 @@ pub fn string_match_all<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
JsValue::from_js_object(stub_ptr)
|
||||
};
|
||||
|
||||
// Build wrapper object
|
||||
// 构建包装对象。
|
||||
builder_wrapper(vm, &s, re_obj)
|
||||
}
|
||||
|
||||
@@ -962,7 +962,7 @@ pub fn string_replace_all<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Some(p) => p,
|
||||
None => return NativeResult::Ok(vm.new_string(&s)),
|
||||
};
|
||||
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
|
||||
// SAFETY: fn_ptr 持有 regexp_constructor 存放的 `Box<regex::Regex>` 指针。
|
||||
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
|
||||
let result = regex.replace_all(&s, replacement.as_str()).to_string();
|
||||
return NativeResult::Ok(vm.new_string(&result));
|
||||
|
||||
@@ -4,36 +4,36 @@ fn stub_error<H: VmHost>(vm: &mut H, name: &str) -> NativeResult {
|
||||
NativeResult::Err(crate::error::create_type_error(vm, &format!("{name} is not implemented")))
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 架构性延后:这些特性按设计排除在受支持语言子集之外。
|
||||
/// `Proxy` 全局占位:始终抛 TypeError。Proxy 按设计延后实现。
|
||||
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.
|
||||
/// `BigInt` 全局占位:始终抛 TypeError。BigInt 按设计延后实现。
|
||||
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.
|
||||
/// `WeakMap` 全局占位:始终抛 TypeError。WeakMap 按设计延后实现。
|
||||
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.
|
||||
/// `WeakSet` 全局占位:始终抛 TypeError。WeakSet 按设计延后实现。
|
||||
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.
|
||||
/// `WeakRef` 全局占位:始终抛 TypeError。WeakRef 按设计延后实现。
|
||||
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.
|
||||
/// `FinalizationRegistry` 全局占位:始终抛 TypeError。按设计延后实现。
|
||||
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.
|
||||
/// `SharedArrayBuffer` 全局占位:始终抛 TypeError。按设计延后实现。
|
||||
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.
|
||||
/// `Atomics` 全局占位:始终抛 TypeError。按设计延后实现。
|
||||
pub fn atomics_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
stub_error(vm, "Atomics")
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ fn create_typed_array<H: VmHost>(
|
||||
byte_offset,
|
||||
length,
|
||||
}));
|
||||
// SAFETY: TypedArray instances are not callable; native_fn stores opaque Box<TypedArrayData>,
|
||||
// matching ArrayBuffer/DataView typed-object storage in this VM.
|
||||
// SAFETY: TypedArray 实例不可调用,native_fn 存不透明 `Box<TypedArrayData>`,
|
||||
// 与本 VM 中 ArrayBuffer/DataView 的类型化对象存储一致。
|
||||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(data as *const ()) }));
|
||||
|
||||
let byte_length = length.saturating_mul(kind.bytes_per_element());
|
||||
|
||||
@@ -82,7 +82,7 @@ fn tojson_called_before_serialize() {
|
||||
#[test]
|
||||
fn tojson_non_callable_ignored() {
|
||||
let (_, result) = eval(r#"JSON.stringify({toJSON:'not-a-function', a:1})"#).unwrap();
|
||||
// toJSON property is a string, should be serialized normally
|
||||
// toJSON 属性是字符串,应按普通属性序列化。
|
||||
assert!(stringify_val(&result).contains("toJSON"));
|
||||
assert!(stringify_val(&result).contains(r#""a":1"#));
|
||||
}
|
||||
@@ -117,8 +117,8 @@ fn reviver_transform_values() {
|
||||
fn reviver_delete_property() {
|
||||
let (_, result) =
|
||||
eval(r#"JSON.parse('{"a":1,"b":2}', function(k,v){if(k==='a')return undefined;return v})"#).unwrap();
|
||||
// The property is soft-deleted (set to undefined)
|
||||
// For test262 compatibility, stringify should omit undefined properties
|
||||
// 属性被软删除(置为 undefined)。
|
||||
// 为 test262 兼容,stringify 应省略 undefined 属性。
|
||||
assert!(result.is_object());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user