perf: forEach/map 支持 ArrayLike,新增 array_ptr_len3! 宏

This commit is contained in:
2026-06-28 18:35:02 +08:00
parent c7708729d3
commit 45ecf8142e
+31 -49
View File
@@ -33,6 +33,20 @@ macro_rules! array_ptr_len {
}}; }};
} }
macro_rules! array_ptr_len3 {
($vm:expr, $args:expr) => {{
let this_val = $vm.reg($args[0]);
let (arr_ptr, len, is_arr) = match get_this_arraylike($vm, this_val) {
Ok(v) => v,
Err(err) => {
builtins_error!("Array method: invalid receiver");
return NativeResult::Err(err);
}
};
(arr_ptr, len, is_arr)
}};
}
fn array_type_error<H: VmHost>(vm: &mut H, msg: &str) -> JsValue { fn array_type_error<H: VmHost>(vm: &mut H, msg: &str) -> JsValue {
crate::error::create_type_error(vm, msg) crate::error::create_type_error(vm, msg)
} }
@@ -554,31 +568,21 @@ pub fn array_flat<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn array_for_each<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn array_for_each<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
builtins_debug!("Array.prototype.forEach called with {} args", args.len()); builtins_debug!("Array.prototype.forEach called with {} args", args.len());
let (arr_ptr, n) = array_ptr_len!(vm, args); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args);
if args.len() < 2 { if args.len() < 2 {
builtins_error!("Array.prototype.forEach: invalid receiver");
return NativeResult::Err(array_type_error(vm, "callback is not a function")); return NativeResult::Err(array_type_error(vm, "callback is not a function"));
} }
let callback_val = match require_callback(vm, vm.reg(args[1])) { let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback, Ok(callback) => callback,
Err(err) => { Err(err) => return NativeResult::Err(err),
builtins_error!("Array.prototype.forEach: invalid receiver");
return NativeResult::Err(err);
}
}; };
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = arraylike_get(vm, arr_ptr, is_array, i);
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) { match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
NativeResult::Ok(_) => {} NativeResult::Ok(_) => {}
NativeResult::Err(err) => { NativeResult::Err(err) => return NativeResult::Err(err),
builtins_error!("Array.prototype.forEach: invalid receiver"); NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
return NativeResult::Err(err);
}
NativeResult::TailCall { .. } => {
builtins_error!("Array.prototype.forEach: invalid receiver");
return unexpected_tail_call_error(vm);
}
} }
} }
NativeResult::Ok(JsValue::undefined()) NativeResult::Ok(JsValue::undefined())
@@ -586,45 +590,31 @@ pub fn array_for_each<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn array_map<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn array_map<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
builtins_debug!("Array.prototype.map called with {} args", args.len()); builtins_debug!("Array.prototype.map called with {} args", args.len());
let (arr_ptr, n) = array_ptr_len!(vm, args); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args);
if args.len() < 2 { if args.len() < 2 {
builtins_error!("Array.prototype.map: invalid receiver");
return NativeResult::Err(array_type_error(vm, "callback is not a function")); return NativeResult::Err(array_type_error(vm, "callback is not a function"));
} }
let callback_val = match require_callback(vm, vm.reg(args[1])) { let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback, Ok(callback) => callback,
Err(err) => { Err(err) => return NativeResult::Err(err),
builtins_error!("Array.prototype.map: invalid receiver");
return NativeResult::Err(err);
}
}; };
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
let new_arr = create_new_array(vm, n); let new_arr = create_new_array(vm, n);
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = arraylike_get(vm, arr_ptr, is_array, i);
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) { match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
NativeResult::Ok(mapped) => unsafe { NativeResult::Ok(mapped) => unsafe { (*new_arr).set_prop_at(i, mapped); },
(*new_arr).set_prop_at(i, mapped); NativeResult::Err(err) => return NativeResult::Err(err),
}, NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
NativeResult::Err(err) => {
builtins_error!("Array.prototype.map: invalid receiver");
return NativeResult::Err(err);
}
NativeResult::TailCall { .. } => {
builtins_error!("Array.prototype.map: invalid receiver");
return unexpected_tail_call_error(vm);
}
} }
} }
unsafe { unsafe { (*new_arr).set_prop_count(n); }
(*new_arr).set_prop_count(n);
}
NativeResult::Ok(JsValue::from_js_object(new_arr)) NativeResult::Ok(JsValue::from_js_object(new_arr))
} }
pub fn array_filter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn array_filter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
builtins_debug!("Array.prototype.filter called with {} args", args.len()); builtins_debug!("Array.prototype.filter called with {} args", args.len());
let (arr_ptr, n) = array_ptr_len!(vm, args); let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args);
if args.len() < 2 { if args.len() < 2 {
builtins_error!("Array.prototype.filter: invalid receiver"); builtins_error!("Array.prototype.filter: invalid receiver");
return NativeResult::Err(array_type_error(vm, "callback is not a function")); return NativeResult::Err(array_type_error(vm, "callback is not a function"));
@@ -639,21 +629,13 @@ pub fn array_filter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
let mut kept: Vec<JsValue> = Vec::new(); let mut kept: Vec<JsValue> = Vec::new();
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = arraylike_get(vm, arr_ptr, is_array, i);
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) { match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
NativeResult::Ok(result_val) => { NativeResult::Ok(result_val) => {
if oxide_runtime_api::to_boolean(result_val) { if oxide_runtime_api::to_boolean(result_val) { kept.push(elem); }
kept.push(elem);
}
}
NativeResult::Err(err) => {
builtins_error!("Array.prototype.filter: invalid receiver");
return NativeResult::Err(err);
}
NativeResult::TailCall { .. } => {
builtins_error!("Array.prototype.filter: invalid receiver");
return unexpected_tail_call_error(vm);
} }
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
} }
} }
let new_arr = create_new_array(vm, kept.len()); let new_arr = create_new_array(vm, kept.len());