diff --git a/crates/oxide_builtins/src/array/mod.rs b/crates/oxide_builtins/src/array/mod.rs index 53d752d9..b2ab2f9e 100644 --- a/crates/oxide_builtins/src/array/mod.rs +++ b/crates/oxide_builtins/src/array/mod.rs @@ -1,11 +1,8 @@ -use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; -use oxide_types::object::JsObject; use oxide_types::value::JsValue; use oxide_runtime_api::{NativeResult, VmHost}; use crate::builtins_debug; -use crate::builtins_error; mod common; pub(crate) use common::*; @@ -19,243 +16,8 @@ pub use element::*; mod iterate; pub use iterate::*; -/// `Array.prototype.sort(compareFn)`:原地排序。默认按字符串字典序; -/// 提供比较函数时按其返回值(<0/=0/>0)排序,回调抛错则中止。 -/// 对元素向量执行 Array.prototype.sort 的比较语义(原地)。 -/// 比较回调抛错时中止并把异常返回;默认按字符串字典序。 -fn sort_values_inner(vm: &mut H, vals: &mut [JsValue], comparator: Option) -> Result<(), JsValue> { - let mut sort_error = None; - vals.sort_by(|a, b| { - if sort_error.is_some() { - return std::cmp::Ordering::Equal; - } - if let Some(callback) = comparator { - match invoke_native_callback(vm, callback, JsValue::undefined(), &[*a, *b]) { - NativeResult::Ok(result) => { - let n = oxide_runtime_api::to_number(result); - if n.is_nan() || n == 0.0 { - std::cmp::Ordering::Equal - } else if n < 0.0 { - std::cmp::Ordering::Less - } else { - std::cmp::Ordering::Greater - } - } - NativeResult::Err(err) => { - sort_error = Some(err); - std::cmp::Ordering::Equal - } - NativeResult::TailCall { .. } => { - sort_error = Some(crate::error::create_type_error(vm, "unexpected tail call in array callback")); - std::cmp::Ordering::Equal - } - } - } else { - let sa = oxide_runtime_api::to_string(*a); - let sb = oxide_runtime_api::to_string(*b); - sa.cmp(&sb) - } - }); - if let Some(err) = sort_error { - return Err(err); - } - Ok(()) -} - -/// 解析 sort/toSorted 的比较回调参数(undefined 视为默认排序)。 -fn parse_sort_comparator(vm: &mut H, candidate: JsValue) -> Result, JsValue> { - if candidate.is_undefined() { - return Ok(None); - } - match require_callback(vm, candidate) { - Ok(callback) => Ok(Some(callback)), - Err(err) => Err(err), - } -} - -pub fn array_sort(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.sort called with {} args", args.len()); - let arr_ptr = array_ptr!(vm, args); - let len = unsafe { (*arr_ptr).prop_count() as usize }; - let mut vals: Vec = (0..len).map(|i| unsafe { (*arr_ptr).get_prop_at(i) }).collect(); - let comparator = if args.len() > 1 { - match parse_sort_comparator(vm, vm.reg(args[1])) { - Ok(c) => c, - Err(err) => { - builtins_error!("Array.prototype.sort: invalid receiver"); - return NativeResult::Err(err); - } - } - } else { - None - }; - if let Err(err) = sort_values_inner(vm, &mut vals, comparator) { - builtins_error!("Array.prototype.sort: invalid receiver"); - return NativeResult::Err(err); - } - let arr = unsafe { &mut *arr_ptr }; - for (i, &v) in vals.iter().enumerate() { - arr.set_prop_at(i, v); - } - NativeResult::Ok(vm.reg(args[0])) -} - -/// `Array.prototype.values()`:返回迭代数组元素的迭代器(%ArrayIteratorPrototype%)。 -pub fn array_values(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.values called with {} args", args.len()); - let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); - match make_array_iterator(vm, this_val, ARRAY_ITER_KIND_VALUES) { - Ok(iterator) => NativeResult::Ok(iterator), - Err(err) => { - builtins_error!("Array.prototype.values: invalid receiver"); - NativeResult::Err(err) - } - } -} - -/// Array Iterator 的内部 kind 编码:0=values,1=keys,2=entries。 -/// TypedArray 迭代器复用同一编码与 %ArrayIteratorPrototype%(规范同族)。 -pub(crate) const ARRAY_ITER_KIND_VALUES: i32 = 0; -pub(crate) const ARRAY_ITER_KIND_KEYS: i32 = 1; -pub(crate) const ARRAY_ITER_KIND_ENTRIES: i32 = 2; - -const ARRAY_ITER_TARGET_PROP: &str = "__target__"; -const ARRAY_ITER_INDEX_PROP: &str = "__index__"; -const ARRAY_ITER_KIND_PROP: &str = "__kind__"; - -/// 创建 Array/TA Iterator 对象:记录目标、当前下标与迭代种类,原型挂 -/// `%ArrayIteratorPrototype%`(链到 `%IteratorPrototype%`,Array 与 TA 共享)。 -/// `next` 不挂实例 own——由原型提供。 -pub(crate) fn make_array_iterator(vm: &mut H, this_val: JsValue, kind: i32) -> Result { - let target = match oxide_runtime_api::to_object(this_val, vm) { - Ok(v) => v, - Err(msg) => return Err(array_type_error(vm, &msg)), - }; - let array_iter_proto = vm.session().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject; - let iter = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(array_iter_proto))); - let target_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_TARGET_PROP).0; - let index_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_INDEX_PROP).0; - let kind_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_KIND_PROP).0; - let iter_ref = unsafe { &mut *iter }; - vm.set_or_create_prop_value(iter_ref, target_si, target); - vm.set_or_create_prop_value(iter_ref, index_si, JsValue::int(0)); - vm.set_or_create_prop_value(iter_ref, kind_si, JsValue::int(kind)); - Ok(JsValue::from_js_object(iter)) -} - -/// Array.prototype.entries():返回按索引产出 [index, value] 对的迭代器。 -pub fn array_entries(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.entries called with {} args", args.len()); - let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); - match make_array_iterator(vm, this_val, ARRAY_ITER_KIND_ENTRIES) { - Ok(iter) => NativeResult::Ok(iter), - Err(err) => NativeResult::Err(err), - } -} - -/// Array.prototype.keys():返回按索引产出下标的迭代器。 -pub fn array_keys(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.keys called with {} args", args.len()); - let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); - match make_array_iterator(vm, this_val, ARRAY_ITER_KIND_KEYS) { - Ok(iter) => NativeResult::Ok(iter), - Err(err) => NativeResult::Err(err), - } -} - -/// Array Iterator 的 next:读取目标当前下标处的值,按种类产出后推进下标。 -pub fn array_iterator_next(vm: &mut H, args: &[u8]) -> NativeResult { - let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); - if !this_val.is_object() { - return NativeResult::Err(array_type_error(vm, "Array Iterator next called on non-object")); - } - let iter = unsafe { &mut *this_val.as_js_object_ptr() }; - let target_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_TARGET_PROP).0; - let index_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_INDEX_PROP).0; - let kind_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_KIND_PROP).0; - let target = match vm.ordinary_get(iter, target_si, this_val) { - Ok(v) if v.is_undefined() => { - return NativeResult::Ok(crate::iterator::make_iter_result(vm, JsValue::undefined(), true)); - } - Ok(v) if v.is_object() => v, - _ => return NativeResult::Err(array_type_error(vm, "Array Iterator has no iterated object")), - }; - let index = vm - .ordinary_get(iter, index_si, this_val) - .ok() - .and_then(|v| if v.is_int() { Some(v.as_int()) } else { None }) - .unwrap_or(0); - let kind = vm - .ordinary_get(iter, kind_si, this_val) - .ok() - .and_then(|v| if v.is_int() { Some(v.as_int()) } else { None }) - .unwrap_or(ARRAY_ITER_KIND_VALUES); - - let target_obj = unsafe { &*target.as_js_object_ptr() }; - // TypedArray 目标走底层 buffer 读取(元素非普通属性,length 语义不同): - // 与 Array 共享 %ArrayIteratorPrototype%,须在此分支区分两种取数路径。 - if target_obj.is_typed_array_obj() { - let view = match crate::typed_array::get_typed_array_data(vm, target) { - Ok(view) => view, - Err(err) => return NativeResult::Err(err), - }; - if (index as usize) >= view.length { - vm.set_or_create_prop_value(iter, target_si, JsValue::undefined()); - return NativeResult::Ok(crate::iterator::make_iter_result(vm, JsValue::undefined(), true)); - } - let element = match crate::typed_array::typed_array_element_get(vm, target_obj, index as u32) { - Ok(v) => v, - Err(e) => return NativeResult::Err(crate::error::create_type_error(vm, &e)), - }; - let value = match kind { - ARRAY_ITER_KIND_KEYS => JsValue::int(index), - ARRAY_ITER_KIND_ENTRIES => { - let pair = create_new_array(vm, 2); - let pair_ref = unsafe { &mut *pair }; - pair_ref.set_prop_at(0, JsValue::int(index)); - pair_ref.set_prop_at(1, element); - JsValue::from_js_object(pair) - } - _ => element, - }; - if let Err(msg) = vm.ordinary_set(iter, index_si, JsValue::int(index + 1), this_val, true) { - return NativeResult::Err(crate::error::create_from_text(vm, &msg)); - } - return NativeResult::Ok(crate::iterator::make_iter_result(vm, value, false)); - } - - let length_key = vm.new_string("length"); - let length_si = vm.property_key_si(length_key); - let len_val = match vm.ordinary_get(target_obj, length_si, target) { - Ok(v) => v, - Err(err) => return NativeResult::Err(crate::error::create_from_text(vm, &err)), - }; - let len_num = vm.coerce_number_bounded(len_val).unwrap_or(0.0); - let len = if !len_num.is_finite() || len_num <= 0.0 { 0 } else { len_num as usize }; - - if (index as usize) >= len { - vm.set_or_create_prop_value(iter, target_si, JsValue::undefined()); - return NativeResult::Ok(crate::iterator::make_iter_result(vm, JsValue::undefined(), true)); - } - - let _ = target_obj.is_array(); - let element = arraylike_get_or_err!(vm, target.as_js_object_ptr(), index as usize); - let value = match kind { - ARRAY_ITER_KIND_KEYS => JsValue::int(index), - ARRAY_ITER_KIND_ENTRIES => { - let pair = create_new_array(vm, 2); - let pair_ref = unsafe { &mut *pair }; - pair_ref.set_prop_at(0, JsValue::int(index)); - pair_ref.set_prop_at(1, element); - JsValue::from_js_object(pair) - } - _ => element, - }; - if let Err(msg) = vm.ordinary_set(iter, index_si, JsValue::int(index + 1), this_val, true) { - return NativeResult::Err(crate::error::create_from_text(vm, &msg)); - } - NativeResult::Ok(crate::iterator::make_iter_result(vm, value, false)) -} +mod sort_iterator; +pub use sort_iterator::*; /// Array.prototype.toReversed():返回元素逆序的新数组,原数组不变。 pub fn array_to_reversed(vm: &mut H, args: &[u8]) -> NativeResult { diff --git a/crates/oxide_builtins/src/array/sort_iterator.rs b/crates/oxide_builtins/src/array/sort_iterator.rs new file mode 100644 index 00000000..07e68793 --- /dev/null +++ b/crates/oxide_builtins/src/array/sort_iterator.rs @@ -0,0 +1,253 @@ +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_types::object::JsObject; +use oxide_types::value::JsValue; + +use oxide_runtime_api::{NativeResult, VmHost}; + +use crate::builtins_debug; +use crate::builtins_error; + +use super::common::{ + array_ptr, array_type_error, arraylike_get, arraylike_get_or_err, create_new_array, get_this_array_ref, + invoke_native_callback, require_callback, +}; + +/// `Array.prototype.sort(compareFn)`:原地排序。默认按字符串字典序; +/// 提供比较函数时按其返回值(<0/=0/>0)排序,回调抛错则中止。 +/// 对元素向量执行 Array.prototype.sort 的比较语义(原地)。 +/// 比较回调抛错时中止并把异常返回;默认按字符串字典序。 +pub(crate) fn sort_values_inner( + vm: &mut H, vals: &mut [JsValue], comparator: Option, +) -> Result<(), JsValue> { + let mut sort_error = None; + vals.sort_by(|a, b| { + if sort_error.is_some() { + return std::cmp::Ordering::Equal; + } + if let Some(callback) = comparator { + match invoke_native_callback(vm, callback, JsValue::undefined(), &[*a, *b]) { + NativeResult::Ok(result) => { + let n = oxide_runtime_api::to_number(result); + if n.is_nan() || n == 0.0 { + std::cmp::Ordering::Equal + } else if n < 0.0 { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + } + } + NativeResult::Err(err) => { + sort_error = Some(err); + std::cmp::Ordering::Equal + } + NativeResult::TailCall { .. } => { + sort_error = Some(crate::error::create_type_error(vm, "unexpected tail call in array callback")); + std::cmp::Ordering::Equal + } + } + } else { + let sa = oxide_runtime_api::to_string(*a); + let sb = oxide_runtime_api::to_string(*b); + sa.cmp(&sb) + } + }); + if let Some(err) = sort_error { + return Err(err); + } + Ok(()) +} + +/// 解析 sort/toSorted 的比较回调参数(undefined 视为默认排序)。 +pub(crate) fn parse_sort_comparator(vm: &mut H, candidate: JsValue) -> Result, JsValue> { + if candidate.is_undefined() { + return Ok(None); + } + match require_callback(vm, candidate) { + Ok(callback) => Ok(Some(callback)), + Err(err) => Err(err), + } +} + +pub fn array_sort(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.sort called with {} args", args.len()); + let arr_ptr = array_ptr!(vm, args); + let len = unsafe { (*arr_ptr).prop_count() as usize }; + let mut vals: Vec = (0..len).map(|i| unsafe { (*arr_ptr).get_prop_at(i) }).collect(); + let comparator = if args.len() > 1 { + match parse_sort_comparator(vm, vm.reg(args[1])) { + Ok(c) => c, + Err(err) => { + builtins_error!("Array.prototype.sort: invalid receiver"); + return NativeResult::Err(err); + } + } + } else { + None + }; + if let Err(err) = sort_values_inner(vm, &mut vals, comparator) { + builtins_error!("Array.prototype.sort: invalid receiver"); + return NativeResult::Err(err); + } + let arr = unsafe { &mut *arr_ptr }; + for (i, &v) in vals.iter().enumerate() { + arr.set_prop_at(i, v); + } + NativeResult::Ok(vm.reg(args[0])) +} + +/// `Array.prototype.values()`:返回迭代数组元素的迭代器(%ArrayIteratorPrototype%)。 +pub fn array_values(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.values called with {} args", args.len()); + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + match make_array_iterator(vm, this_val, ARRAY_ITER_KIND_VALUES) { + Ok(iterator) => NativeResult::Ok(iterator), + Err(err) => { + builtins_error!("Array.prototype.values: invalid receiver"); + NativeResult::Err(err) + } + } +} + +/// Array Iterator 的内部 kind 编码:0=values,1=keys,2=entries。 +/// TypedArray 迭代器复用同一编码与 %ArrayIteratorPrototype%(规范同族)。 +pub(crate) const ARRAY_ITER_KIND_VALUES: i32 = 0; +pub(crate) const ARRAY_ITER_KIND_KEYS: i32 = 1; +pub(crate) const ARRAY_ITER_KIND_ENTRIES: i32 = 2; + +const ARRAY_ITER_TARGET_PROP: &str = "__target__"; +const ARRAY_ITER_INDEX_PROP: &str = "__index__"; +const ARRAY_ITER_KIND_PROP: &str = "__kind__"; + +/// 创建 Array/TA Iterator 对象:记录目标、当前下标与迭代种类,原型挂 +/// `%ArrayIteratorPrototype%`(链到 `%IteratorPrototype%`,Array 与 TA 共享)。 +/// `next` 不挂实例 own——由原型提供。 +pub(crate) fn make_array_iterator(vm: &mut H, this_val: JsValue, kind: i32) -> Result { + let target = match oxide_runtime_api::to_object(this_val, vm) { + Ok(v) => v, + Err(msg) => return Err(array_type_error(vm, &msg)), + }; + let array_iter_proto = vm.session().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject; + let iter = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(array_iter_proto))); + let target_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_TARGET_PROP).0; + let index_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_INDEX_PROP).0; + let kind_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_KIND_PROP).0; + let iter_ref = unsafe { &mut *iter }; + vm.set_or_create_prop_value(iter_ref, target_si, target); + vm.set_or_create_prop_value(iter_ref, index_si, JsValue::int(0)); + vm.set_or_create_prop_value(iter_ref, kind_si, JsValue::int(kind)); + Ok(JsValue::from_js_object(iter)) +} + +/// Array.prototype.entries():返回按索引产出 [index, value] 对的迭代器。 +pub fn array_entries(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.entries called with {} args", args.len()); + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + match make_array_iterator(vm, this_val, ARRAY_ITER_KIND_ENTRIES) { + Ok(iter) => NativeResult::Ok(iter), + Err(err) => NativeResult::Err(err), + } +} + +/// Array.prototype.keys():返回按索引产出下标的迭代器。 +pub fn array_keys(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.keys called with {} args", args.len()); + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + match make_array_iterator(vm, this_val, ARRAY_ITER_KIND_KEYS) { + Ok(iter) => NativeResult::Ok(iter), + Err(err) => NativeResult::Err(err), + } +} + +/// Array Iterator 的 next:读取目标当前下标处的值,按种类产出后推进下标。 +pub fn array_iterator_next(vm: &mut H, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + if !this_val.is_object() { + return NativeResult::Err(array_type_error(vm, "Array Iterator next called on non-object")); + } + let iter = unsafe { &mut *this_val.as_js_object_ptr() }; + let target_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_TARGET_PROP).0; + let index_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_INDEX_PROP).0; + let kind_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_KIND_PROP).0; + let target = match vm.ordinary_get(iter, target_si, this_val) { + Ok(v) if v.is_undefined() => { + return NativeResult::Ok(crate::iterator::make_iter_result(vm, JsValue::undefined(), true)); + } + Ok(v) if v.is_object() => v, + _ => return NativeResult::Err(array_type_error(vm, "Array Iterator has no iterated object")), + }; + let index = vm + .ordinary_get(iter, index_si, this_val) + .ok() + .and_then(|v| if v.is_int() { Some(v.as_int()) } else { None }) + .unwrap_or(0); + let kind = vm + .ordinary_get(iter, kind_si, this_val) + .ok() + .and_then(|v| if v.is_int() { Some(v.as_int()) } else { None }) + .unwrap_or(ARRAY_ITER_KIND_VALUES); + + let target_obj = unsafe { &*target.as_js_object_ptr() }; + // TypedArray 目标走底层 buffer 读取(元素非普通属性,length 语义不同): + // 与 Array 共享 %ArrayIteratorPrototype%,须在此分支区分两种取数路径。 + if target_obj.is_typed_array_obj() { + let view = match crate::typed_array::get_typed_array_data(vm, target) { + Ok(view) => view, + Err(err) => return NativeResult::Err(err), + }; + if (index as usize) >= view.length { + vm.set_or_create_prop_value(iter, target_si, JsValue::undefined()); + return NativeResult::Ok(crate::iterator::make_iter_result(vm, JsValue::undefined(), true)); + } + let element = match crate::typed_array::typed_array_element_get(vm, target_obj, index as u32) { + Ok(v) => v, + Err(e) => return NativeResult::Err(crate::error::create_type_error(vm, &e)), + }; + let value = match kind { + ARRAY_ITER_KIND_KEYS => JsValue::int(index), + ARRAY_ITER_KIND_ENTRIES => { + let pair = create_new_array(vm, 2); + let pair_ref = unsafe { &mut *pair }; + pair_ref.set_prop_at(0, JsValue::int(index)); + pair_ref.set_prop_at(1, element); + JsValue::from_js_object(pair) + } + _ => element, + }; + if let Err(msg) = vm.ordinary_set(iter, index_si, JsValue::int(index + 1), this_val, true) { + return NativeResult::Err(crate::error::create_from_text(vm, &msg)); + } + return NativeResult::Ok(crate::iterator::make_iter_result(vm, value, false)); + } + + let length_key = vm.new_string("length"); + let length_si = vm.property_key_si(length_key); + let len_val = match vm.ordinary_get(target_obj, length_si, target) { + Ok(v) => v, + Err(err) => return NativeResult::Err(crate::error::create_from_text(vm, &err)), + }; + let len_num = vm.coerce_number_bounded(len_val).unwrap_or(0.0); + let len = if !len_num.is_finite() || len_num <= 0.0 { 0 } else { len_num as usize }; + + if (index as usize) >= len { + vm.set_or_create_prop_value(iter, target_si, JsValue::undefined()); + return NativeResult::Ok(crate::iterator::make_iter_result(vm, JsValue::undefined(), true)); + } + + let _ = target_obj.is_array(); + let element = arraylike_get_or_err!(vm, target.as_js_object_ptr(), index as usize); + let value = match kind { + ARRAY_ITER_KIND_KEYS => JsValue::int(index), + ARRAY_ITER_KIND_ENTRIES => { + let pair = create_new_array(vm, 2); + let pair_ref = unsafe { &mut *pair }; + pair_ref.set_prop_at(0, JsValue::int(index)); + pair_ref.set_prop_at(1, element); + JsValue::from_js_object(pair) + } + _ => element, + }; + if let Err(msg) = vm.ordinary_set(iter, index_si, JsValue::int(index + 1), this_val, true) { + return NativeResult::Err(crate::error::create_from_text(vm, &msg)); + } + NativeResult::Ok(crate::iterator::make_iter_result(vm, value, false)) +}