From f2a13208108faae8e5e1c260d69f7e2e56fa1d42 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 17 Sep 2026 12:05:28 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20array=20=E8=BF=AD=E4=BB=A3=E4=B8=8E?= =?UTF-8?q?=E6=9F=A5=E6=89=BE=E6=97=8F=E6=8B=86=E5=87=BA=20iterate=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/array/iterate.rs | 493 +++++++++++++++++++++ crates/oxide_builtins/src/array/mod.rs | 483 +------------------- 2 files changed, 495 insertions(+), 481 deletions(-) create mode 100644 crates/oxide_builtins/src/array/iterate.rs diff --git a/crates/oxide_builtins/src/array/iterate.rs b/crates/oxide_builtins/src/array/iterate.rs new file mode 100644 index 00000000..449c8091 --- /dev/null +++ b/crates/oxide_builtins/src/array/iterate.rs @@ -0,0 +1,493 @@ +use oxide_types::value::JsValue; + +use oxide_runtime_api::{NativeResult, VmHost}; + +use crate::builtins_debug; +use crate::builtins_error; + +use super::common::{ + array_ptr_len3, array_type_error, arraylike_get, arraylike_get_or_err, check_array_create_len, create_new_array, + get_this_arraylike, invoke_native_callback, js_array_index, require_callback, unexpected_tail_call_error, +}; + +/// `Array.prototype.forEach(callback, thisArg)`:对每个元素调用 callback,返回 undefined。 +pub fn array_for_each(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.forEach called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if args.len() < 2 { + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => return NativeResult::Err(err), + }; + let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + for i in 0..n { + let elem = arraylike_get_or_err!(vm, arr_ptr, i); + match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { + NativeResult::Ok(_) => {} + NativeResult::Err(err) => return NativeResult::Err(err), + NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + } + } + NativeResult::Ok(JsValue::undefined()) +} + +/// `Array.prototype.map(callback, thisArg)`:对每个元素调用 callback 生成新数组。 +pub fn array_map(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.map called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if args.len() < 2 { + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => return NativeResult::Err(err), + }; + let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + let new_arr = create_new_array(vm, n); + if let Err(err) = check_array_create_len(vm, n) { + return NativeResult::Err(err); + } + for i in 0..n { + let elem = arraylike_get_or_err!(vm, arr_ptr, i); + match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { + NativeResult::Ok(mapped) => unsafe { + (*new_arr).set_prop_at(i, mapped); + }, + NativeResult::Err(err) => return NativeResult::Err(err), + NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + } + } + unsafe { + (*new_arr).set_prop_count(n); + } + NativeResult::Ok(JsValue::from_js_object(new_arr)) +} + +/// `Array.prototype.filter(callback, thisArg)`:保留 callback 返回真值的元素形成新数组。 +pub fn array_filter(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.filter called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if args.len() < 2 { + builtins_error!("Array.prototype.filter: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => { + builtins_error!("Array.prototype.filter: invalid receiver"); + return NativeResult::Err(err); + } + }; + let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + let mut kept: Vec = Vec::new(); + if let Err(err) = check_array_create_len(vm, n) { + return NativeResult::Err(err); + } + for i in 0..n { + let elem = arraylike_get_or_err!(vm, arr_ptr, i); + match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { + NativeResult::Ok(result_val) => { + if oxide_runtime_api::to_boolean(result_val) { + kept.push(elem); + } + } + NativeResult::Err(err) => return NativeResult::Err(err), + NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + } + } + let new_arr = create_new_array(vm, kept.len()); + unsafe { + for (i, val) in kept.iter().enumerate() { + (*new_arr).set_prop_at(i, *val); + } + (*new_arr).set_prop_count(kept.len()); + } + NativeResult::Ok(JsValue::from_js_object(new_arr)) +} + +/// `Array.prototype.reduce(callback, initialValue)`:从左到右累计归约; +/// 空数组且无初始值抛 TypeError。 +pub fn array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.reduce called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if n == 0 && args.len() < 3 { + builtins_error!("Array.prototype.reduce: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "Reduce of empty array with no initial value")); + } + if args.len() < 2 { + builtins_error!("Array.prototype.reduce: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => { + builtins_error!("Array.prototype.reduce: invalid receiver"); + return NativeResult::Err(err); + } + }; + let has_initial = args.len() > 2; + let mut accumulator; + let start_idx; + if has_initial { + accumulator = vm.reg(args[2]); + start_idx = 0; + } else { + accumulator = arraylike_get_or_err!(vm, arr_ptr, 0); + start_idx = 1; + } + let this_val = JsValue::undefined(); + for i in start_idx..n { + let elem = arraylike_get_or_err!(vm, arr_ptr, i); + match invoke_native_callback(vm, callback_val, this_val, &[accumulator, elem, js_array_index(i), o_val]) { + NativeResult::Ok(result) => accumulator = result, + NativeResult::Err(err) => { + builtins_error!("Array.prototype.reduce: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.reduce: invalid receiver"); + return unexpected_tail_call_error(vm); + } + } + } + NativeResult::Ok(accumulator) +} + +/// `Array.prototype.find(callback, thisArg)`:返回首个 callback 为真的元素,否则 undefined。 +pub fn array_find(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.find called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if args.len() < 2 { + builtins_error!("Array.prototype.find: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => { + builtins_error!("Array.prototype.find: invalid receiver"); + return NativeResult::Err(err); + } + }; + let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + for i in 0..n { + let elem = arraylike_get_or_err!(vm, arr_ptr, i); + match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { + NativeResult::Ok(result_val) => { + if oxide_runtime_api::to_boolean(result_val) { + return NativeResult::Ok(elem); + } + } + NativeResult::Err(err) => { + builtins_error!("Array.prototype.find: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.find: invalid receiver"); + return unexpected_tail_call_error(vm); + } + } + } + NativeResult::Ok(JsValue::undefined()) +} + +/// `Array.prototype.some(callback, thisArg)`:任一元素满足 callback 返回 true。 +pub fn array_some(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.some called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if args.len() < 2 { + builtins_error!("Array.prototype.some: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => { + builtins_error!("Array.prototype.some: invalid receiver"); + return NativeResult::Err(err); + } + }; + let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + for i in 0..n { + let elem = arraylike_get_or_err!(vm, arr_ptr, i); + match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { + NativeResult::Ok(result_val) => { + if oxide_runtime_api::to_boolean(result_val) { + return NativeResult::Ok(JsValue::bool(true)); + } + } + NativeResult::Err(err) => { + builtins_error!("Array.prototype.some: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.some: invalid receiver"); + return unexpected_tail_call_error(vm); + } + } + } + NativeResult::Ok(JsValue::bool(false)) +} + +/// `Array.prototype.every(callback, thisArg)`:所有元素满足 callback 才返回 true。 +pub fn array_every(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.every called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if args.len() < 2 { + builtins_error!("Array.prototype.every: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => { + builtins_error!("Array.prototype.every: invalid receiver"); + return NativeResult::Err(err); + } + }; + let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + for i in 0..n { + let elem = arraylike_get_or_err!(vm, arr_ptr, i); + match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { + NativeResult::Ok(result_val) => { + if !oxide_runtime_api::to_boolean(result_val) { + return NativeResult::Ok(JsValue::bool(false)); + } + } + NativeResult::Err(err) => { + builtins_error!("Array.prototype.every: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.every: invalid receiver"); + return unexpected_tail_call_error(vm); + } + } + } + NativeResult::Ok(JsValue::bool(true)) +} + +/// `Array.prototype.flatMap(callback, thisArg)`:map 后把返回的数组展开一层。 +pub fn array_flat_map(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.flatMap called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if args.len() < 2 { + builtins_error!("Array.prototype.flatMap: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => { + builtins_error!("Array.prototype.flatMap: invalid receiver"); + return NativeResult::Err(err); + } + }; + let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + let mut flat: Vec = Vec::new(); + if let Err(err) = check_array_create_len(vm, n) { + return NativeResult::Err(err); + } + for i in 0..n { + let elem = arraylike_get_or_err!(vm, arr_ptr, i); + match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { + NativeResult::Ok(result) => { + if result.is_object() { + let r_ptr = result.as_js_object_ptr(); + if !r_ptr.is_null() { + let r = unsafe { &*r_ptr }; + if r.is_array() { + let rn = r.prop_count() as usize; + for j in 0..rn { + flat.push(r.get_prop_at(j)); + } + continue; + } + } + } + flat.push(result); + } + NativeResult::Err(err) => { + builtins_error!("Array.prototype.flatMap: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.flatMap: invalid receiver"); + return unexpected_tail_call_error(vm); + } + } + } + let new_arr = create_new_array(vm, flat.len()); + unsafe { + for (i, val) in flat.iter().enumerate() { + (*new_arr).set_prop_at(i, *val); + } + (*new_arr).set_prop_count(flat.len()); + } + NativeResult::Ok(JsValue::from_js_object(new_arr)) +} + +/// `Array.prototype.findIndex(callback, thisArg)`:返回首个 callback 为真的索引,否则 -1。 +pub fn array_find_index(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.findIndex called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if args.len() < 2 { + builtins_error!("Array.prototype.findIndex: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => { + builtins_error!("Array.prototype.findIndex: invalid receiver"); + return NativeResult::Err(err); + } + }; + let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + for i in 0..n { + let elem = arraylike_get_or_err!(vm, arr_ptr, i); + match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { + NativeResult::Ok(r) => { + if oxide_runtime_api::to_boolean(r) { + return NativeResult::Ok(js_array_index(i)); + } + } + NativeResult::Err(err) => { + builtins_error!("Array.prototype.findIndex: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.findIndex: invalid receiver"); + return unexpected_tail_call_error(vm); + } + } + } + NativeResult::Ok(JsValue::int(-1)) +} + +/// `Array.prototype.findLast(callback, thisArg)`:从后往前返回首个 callback 为真的元素。 +pub fn array_find_last(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.findLast called with {} args", args.len()); + let (arr_ptr, n) = { + let (arr_ptr, len, _is_array) = array_ptr_len3!(vm, args); + (arr_ptr, len as i32) + }; + let o_val = vm.reg(args[0]); + if args.len() < 2 { + builtins_error!("Array.prototype.findLast: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => { + builtins_error!("Array.prototype.findLast: invalid receiver"); + return NativeResult::Err(err); + } + }; + let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + for i in (0..n).rev() { + let elem = unsafe { (*arr_ptr).get_prop_at(i) }; + match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i), o_val]) { + NativeResult::Ok(r) => { + if oxide_runtime_api::to_boolean(r) { + return NativeResult::Ok(elem); + } + } + NativeResult::Err(err) => { + builtins_error!("Array.prototype.findLast: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.findLast: invalid receiver"); + return unexpected_tail_call_error(vm); + } + } + } + NativeResult::Ok(JsValue::undefined()) +} + +/// `Array.prototype.reduceRight(callback, initialValue)`:从右到左累计归约。 +pub fn array_reduce_right(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.reduceRight called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if args.len() < 2 { + builtins_error!("Array.prototype.reduceRight: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => { + builtins_error!("Array.prototype.reduceRight: invalid receiver"); + return NativeResult::Err(err); + } + }; + let (mut acc, start_idx): (JsValue, i32) = if args.len() > 2 { + (vm.reg(args[2]), n as i32 - 1) + } else { + if n == 0 { + builtins_error!("Array.prototype.reduceRight: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "Reduce of empty array with no initial value")); + } + (unsafe { (*arr_ptr).get_prop_at(n - 1) }, n as i32 - 2) + }; + for i in (0..=start_idx).rev() { + let elem = unsafe { (*arr_ptr).get_prop_at(i as usize) }; + match invoke_native_callback(vm, callback_val, JsValue::undefined(), &[acc, elem, JsValue::int(i), o_val]) { + NativeResult::Ok(r) => acc = r, + NativeResult::Err(err) => { + builtins_error!("Array.prototype.reduceRight: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.reduceRight: invalid receiver"); + return unexpected_tail_call_error(vm); + } + } + } + NativeResult::Ok(acc) +} + +/// Array.prototype.findLastIndex(callback, thisArg):从后往前返回首个 callback 为真的下标。 +pub fn array_find_last_index(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.findLastIndex called with {} args", args.len()); + let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); + let o_val = vm.reg(args[0]); + if args.len() < 2 { + builtins_error!("Array.prototype.findLastIndex: invalid receiver"); + return NativeResult::Err(array_type_error(vm, "callback is not a function")); + } + let callback_val = match require_callback(vm, vm.reg(args[1])) { + Ok(callback) => callback, + Err(err) => { + builtins_error!("Array.prototype.findLastIndex: invalid receiver"); + return NativeResult::Err(err); + } + }; + let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + for i in (0..n).rev() { + let elem = arraylike_get_or_err!(vm, arr_ptr, i); + match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { + NativeResult::Ok(r) => { + if oxide_runtime_api::to_boolean(r) { + return NativeResult::Ok(js_array_index(i)); + } + } + NativeResult::Err(err) => { + builtins_error!("Array.prototype.findLastIndex: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.findLastIndex: invalid receiver"); + return unexpected_tail_call_error(vm); + } + } + } + NativeResult::Ok(JsValue::int(-1)) +} diff --git a/crates/oxide_builtins/src/array/mod.rs b/crates/oxide_builtins/src/array/mod.rs index 84d41748..53d752d9 100644 --- a/crates/oxide_builtins/src/array/mod.rs +++ b/crates/oxide_builtins/src/array/mod.rs @@ -16,449 +16,8 @@ pub use from::*; mod element; pub use element::*; -/// `Array.prototype.forEach(callback, thisArg)`:对每个元素调用 callback,返回 undefined。 -pub fn array_for_each(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.forEach called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if args.len() < 2 { - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), - }; - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - for i in 0..n { - let elem = arraylike_get_or_err!(vm, arr_ptr, i); - match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { - NativeResult::Ok(_) => {} - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), - } - } - NativeResult::Ok(JsValue::undefined()) -} - -/// `Array.prototype.map(callback, thisArg)`:对每个元素调用 callback 生成新数组。 -pub fn array_map(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.map called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if args.len() < 2 { - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), - }; - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - let new_arr = create_new_array(vm, n); - if let Err(err) = check_array_create_len(vm, n) { - return NativeResult::Err(err); - } - for i in 0..n { - let elem = arraylike_get_or_err!(vm, arr_ptr, i); - match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { - NativeResult::Ok(mapped) => unsafe { - (*new_arr).set_prop_at(i, mapped); - }, - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), - } - } - unsafe { - (*new_arr).set_prop_count(n); - } - NativeResult::Ok(JsValue::from_js_object(new_arr)) -} - -/// `Array.prototype.filter(callback, thisArg)`:保留 callback 返回真值的元素形成新数组。 -pub fn array_filter(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.filter called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if args.len() < 2 { - builtins_error!("Array.prototype.filter: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => { - builtins_error!("Array.prototype.filter: invalid receiver"); - return NativeResult::Err(err); - } - }; - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - let mut kept: Vec = Vec::new(); - if let Err(err) = check_array_create_len(vm, n) { - return NativeResult::Err(err); - } - for i in 0..n { - let elem = arraylike_get_or_err!(vm, arr_ptr, i); - match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { - NativeResult::Ok(result_val) => { - if oxide_runtime_api::to_boolean(result_val) { - kept.push(elem); - } - } - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), - } - } - let new_arr = create_new_array(vm, kept.len()); - unsafe { - for (i, val) in kept.iter().enumerate() { - (*new_arr).set_prop_at(i, *val); - } - (*new_arr).set_prop_count(kept.len()); - } - NativeResult::Ok(JsValue::from_js_object(new_arr)) -} - -/// `Array.prototype.reduce(callback, initialValue)`:从左到右累计归约; -/// 空数组且无初始值抛 TypeError。 -pub fn array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.reduce called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if n == 0 && args.len() < 3 { - builtins_error!("Array.prototype.reduce: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "Reduce of empty array with no initial value")); - } - if args.len() < 2 { - builtins_error!("Array.prototype.reduce: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => { - builtins_error!("Array.prototype.reduce: invalid receiver"); - return NativeResult::Err(err); - } - }; - let has_initial = args.len() > 2; - let mut accumulator; - let start_idx; - if has_initial { - accumulator = vm.reg(args[2]); - start_idx = 0; - } else { - accumulator = arraylike_get_or_err!(vm, arr_ptr, 0); - start_idx = 1; - } - let this_val = JsValue::undefined(); - for i in start_idx..n { - let elem = arraylike_get_or_err!(vm, arr_ptr, i); - match invoke_native_callback(vm, callback_val, this_val, &[accumulator, elem, js_array_index(i), o_val]) { - NativeResult::Ok(result) => accumulator = result, - NativeResult::Err(err) => { - builtins_error!("Array.prototype.reduce: invalid receiver"); - return NativeResult::Err(err); - } - NativeResult::TailCall { .. } => { - builtins_error!("Array.prototype.reduce: invalid receiver"); - return unexpected_tail_call_error(vm); - } - } - } - NativeResult::Ok(accumulator) -} - -/// `Array.prototype.find(callback, thisArg)`:返回首个 callback 为真的元素,否则 undefined。 -pub fn array_find(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.find called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if args.len() < 2 { - builtins_error!("Array.prototype.find: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => { - builtins_error!("Array.prototype.find: invalid receiver"); - return NativeResult::Err(err); - } - }; - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - for i in 0..n { - let elem = arraylike_get_or_err!(vm, arr_ptr, i); - match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { - NativeResult::Ok(result_val) => { - if oxide_runtime_api::to_boolean(result_val) { - return NativeResult::Ok(elem); - } - } - NativeResult::Err(err) => { - builtins_error!("Array.prototype.find: invalid receiver"); - return NativeResult::Err(err); - } - NativeResult::TailCall { .. } => { - builtins_error!("Array.prototype.find: invalid receiver"); - return unexpected_tail_call_error(vm); - } - } - } - NativeResult::Ok(JsValue::undefined()) -} - -/// `Array.prototype.some(callback, thisArg)`:任一元素满足 callback 返回 true。 -pub fn array_some(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.some called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if args.len() < 2 { - builtins_error!("Array.prototype.some: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => { - builtins_error!("Array.prototype.some: invalid receiver"); - return NativeResult::Err(err); - } - }; - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - for i in 0..n { - let elem = arraylike_get_or_err!(vm, arr_ptr, i); - match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { - NativeResult::Ok(result_val) => { - if oxide_runtime_api::to_boolean(result_val) { - return NativeResult::Ok(JsValue::bool(true)); - } - } - NativeResult::Err(err) => { - builtins_error!("Array.prototype.some: invalid receiver"); - return NativeResult::Err(err); - } - NativeResult::TailCall { .. } => { - builtins_error!("Array.prototype.some: invalid receiver"); - return unexpected_tail_call_error(vm); - } - } - } - NativeResult::Ok(JsValue::bool(false)) -} - -/// `Array.prototype.every(callback, thisArg)`:所有元素满足 callback 才返回 true。 -pub fn array_every(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.every called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if args.len() < 2 { - builtins_error!("Array.prototype.every: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => { - builtins_error!("Array.prototype.every: invalid receiver"); - return NativeResult::Err(err); - } - }; - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - for i in 0..n { - let elem = arraylike_get_or_err!(vm, arr_ptr, i); - match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { - NativeResult::Ok(result_val) => { - if !oxide_runtime_api::to_boolean(result_val) { - return NativeResult::Ok(JsValue::bool(false)); - } - } - NativeResult::Err(err) => { - builtins_error!("Array.prototype.every: invalid receiver"); - return NativeResult::Err(err); - } - NativeResult::TailCall { .. } => { - builtins_error!("Array.prototype.every: invalid receiver"); - return unexpected_tail_call_error(vm); - } - } - } - NativeResult::Ok(JsValue::bool(true)) -} - -/// `Array.prototype.flatMap(callback, thisArg)`:map 后把返回的数组展开一层。 -pub fn array_flat_map(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.flatMap called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if args.len() < 2 { - builtins_error!("Array.prototype.flatMap: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => { - builtins_error!("Array.prototype.flatMap: invalid receiver"); - return NativeResult::Err(err); - } - }; - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - let mut flat: Vec = Vec::new(); - if let Err(err) = check_array_create_len(vm, n) { - return NativeResult::Err(err); - } - for i in 0..n { - let elem = arraylike_get_or_err!(vm, arr_ptr, i); - match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { - NativeResult::Ok(result) => { - if result.is_object() { - let r_ptr = result.as_js_object_ptr(); - if !r_ptr.is_null() { - let r = unsafe { &*r_ptr }; - if r.is_array() { - let rn = r.prop_count() as usize; - for j in 0..rn { - flat.push(r.get_prop_at(j)); - } - continue; - } - } - } - flat.push(result); - } - NativeResult::Err(err) => { - builtins_error!("Array.prototype.flatMap: invalid receiver"); - return NativeResult::Err(err); - } - NativeResult::TailCall { .. } => { - builtins_error!("Array.prototype.flatMap: invalid receiver"); - return unexpected_tail_call_error(vm); - } - } - } - let new_arr = create_new_array(vm, flat.len()); - unsafe { - for (i, val) in flat.iter().enumerate() { - (*new_arr).set_prop_at(i, *val); - } - (*new_arr).set_prop_count(flat.len()); - } - NativeResult::Ok(JsValue::from_js_object(new_arr)) -} - -/// `Array.prototype.findIndex(callback, thisArg)`:返回首个 callback 为真的索引,否则 -1。 -pub fn array_find_index(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.findIndex called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if args.len() < 2 { - builtins_error!("Array.prototype.findIndex: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => { - builtins_error!("Array.prototype.findIndex: invalid receiver"); - return NativeResult::Err(err); - } - }; - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - for i in 0..n { - let elem = arraylike_get_or_err!(vm, arr_ptr, i); - match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { - NativeResult::Ok(r) => { - if oxide_runtime_api::to_boolean(r) { - return NativeResult::Ok(js_array_index(i)); - } - } - NativeResult::Err(err) => { - builtins_error!("Array.prototype.findIndex: invalid receiver"); - return NativeResult::Err(err); - } - NativeResult::TailCall { .. } => { - builtins_error!("Array.prototype.findIndex: invalid receiver"); - return unexpected_tail_call_error(vm); - } - } - } - NativeResult::Ok(JsValue::int(-1)) -} - -/// `Array.prototype.findLast(callback, thisArg)`:从后往前返回首个 callback 为真的元素。 -pub fn array_find_last(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.findLast called with {} args", args.len()); - let (arr_ptr, n) = { - let (arr_ptr, len, _is_array) = array_ptr_len3!(vm, args); - (arr_ptr, len as i32) - }; - let o_val = vm.reg(args[0]); - if args.len() < 2 { - builtins_error!("Array.prototype.findLast: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => { - builtins_error!("Array.prototype.findLast: invalid receiver"); - return NativeResult::Err(err); - } - }; - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - for i in (0..n).rev() { - let elem = unsafe { (*arr_ptr).get_prop_at(i) }; - match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i), o_val]) { - NativeResult::Ok(r) => { - if oxide_runtime_api::to_boolean(r) { - return NativeResult::Ok(elem); - } - } - NativeResult::Err(err) => { - builtins_error!("Array.prototype.findLast: invalid receiver"); - return NativeResult::Err(err); - } - NativeResult::TailCall { .. } => { - builtins_error!("Array.prototype.findLast: invalid receiver"); - return unexpected_tail_call_error(vm); - } - } - } - NativeResult::Ok(JsValue::undefined()) -} - -/// `Array.prototype.reduceRight(callback, initialValue)`:从右到左累计归约。 -pub fn array_reduce_right(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.reduceRight called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if args.len() < 2 { - builtins_error!("Array.prototype.reduceRight: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => { - builtins_error!("Array.prototype.reduceRight: invalid receiver"); - return NativeResult::Err(err); - } - }; - let (mut acc, start_idx): (JsValue, i32) = if args.len() > 2 { - (vm.reg(args[2]), n as i32 - 1) - } else { - if n == 0 { - builtins_error!("Array.prototype.reduceRight: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "Reduce of empty array with no initial value")); - } - (unsafe { (*arr_ptr).get_prop_at(n - 1) }, n as i32 - 2) - }; - for i in (0..=start_idx).rev() { - let elem = unsafe { (*arr_ptr).get_prop_at(i as usize) }; - match invoke_native_callback(vm, callback_val, JsValue::undefined(), &[acc, elem, JsValue::int(i), o_val]) { - NativeResult::Ok(r) => acc = r, - NativeResult::Err(err) => { - builtins_error!("Array.prototype.reduceRight: invalid receiver"); - return NativeResult::Err(err); - } - NativeResult::TailCall { .. } => { - builtins_error!("Array.prototype.reduceRight: invalid receiver"); - return unexpected_tail_call_error(vm); - } - } - } - NativeResult::Ok(acc) -} +mod iterate; +pub use iterate::*; /// `Array.prototype.sort(compareFn)`:原地排序。默认按字符串字典序; /// 提供比较函数时按其返回值(<0/=0/>0)排序,回调抛错则中止。 @@ -698,44 +257,6 @@ pub fn array_iterator_next(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(crate::iterator::make_iter_result(vm, value, false)) } -/// Array.prototype.findLastIndex(callback, thisArg):从后往前返回首个 callback 为真的下标。 -pub fn array_find_last_index(vm: &mut H, args: &[u8]) -> NativeResult { - builtins_debug!("Array.prototype.findLastIndex called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); - if args.len() < 2 { - builtins_error!("Array.prototype.findLastIndex: invalid receiver"); - return NativeResult::Err(array_type_error(vm, "callback is not a function")); - } - let callback_val = match require_callback(vm, vm.reg(args[1])) { - Ok(callback) => callback, - Err(err) => { - builtins_error!("Array.prototype.findLastIndex: invalid receiver"); - return NativeResult::Err(err); - } - }; - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - for i in (0..n).rev() { - let elem = arraylike_get_or_err!(vm, arr_ptr, i); - match invoke_native_callback(vm, callback_val, this_val, &[elem, js_array_index(i), o_val]) { - NativeResult::Ok(r) => { - if oxide_runtime_api::to_boolean(r) { - return NativeResult::Ok(js_array_index(i)); - } - } - NativeResult::Err(err) => { - builtins_error!("Array.prototype.findLastIndex: invalid receiver"); - return NativeResult::Err(err); - } - NativeResult::TailCall { .. } => { - builtins_error!("Array.prototype.findLastIndex: invalid receiver"); - return unexpected_tail_call_error(vm); - } - } - } - NativeResult::Ok(JsValue::int(-1)) -} - /// Array.prototype.toReversed():返回元素逆序的新数组,原数组不变。 pub fn array_to_reversed(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.toReversed called with {} args", args.len());