mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
refactor: array 不可变方法族拆出 immutable 模块
This commit is contained in:
@@ -0,0 +1,179 @@
|
|||||||
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
|
use oxide_runtime_api::{NativeResult, VmHost};
|
||||||
|
|
||||||
|
use crate::builtins_debug;
|
||||||
|
|
||||||
|
use super::common::{
|
||||||
|
arraylike_get, arraylike_get_or_err, check_array_create_len, create_new_array, get_this_arraylike,
|
||||||
|
};
|
||||||
|
use super::from::from_engine_error;
|
||||||
|
use super::sort_iterator::{parse_sort_comparator, sort_values_inner};
|
||||||
|
|
||||||
|
/// Array.prototype.toReversed():返回元素逆序的新数组,原数组不变。
|
||||||
|
pub fn array_to_reversed<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
builtins_debug!("Array.prototype.toReversed called with {} args", args.len());
|
||||||
|
let this_val = vm.reg(args[0]);
|
||||||
|
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return NativeResult::Err(err),
|
||||||
|
};
|
||||||
|
let new_arr = create_new_array(vm, n);
|
||||||
|
if let Err(err) = check_array_create_len(vm, n) {
|
||||||
|
return NativeResult::Err(err);
|
||||||
|
}
|
||||||
|
// ????????????????getter ???????????
|
||||||
|
for i in (0..n).rev() {
|
||||||
|
let elem = arraylike_get_or_err!(vm, arr_ptr, i);
|
||||||
|
unsafe {
|
||||||
|
(*new_arr).set_prop_at(n - 1 - i, elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Array.prototype.with(index, value):返回把指定下标替换为新值的新数组,原数组不变。
|
||||||
|
pub fn array_with<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
builtins_debug!("Array.prototype.with called with {} args", args.len());
|
||||||
|
let this_val = vm.reg(args[0]);
|
||||||
|
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return NativeResult::Err(err),
|
||||||
|
};
|
||||||
|
let index_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||||
|
let idx = match vm.coerce_number_bounded(index_val) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
|
||||||
|
};
|
||||||
|
// ToIntegerOrInfinity:NaN/-0/+0 -> 0,其余截断。
|
||||||
|
let rel = if idx.is_nan() || idx == 0.0 { 0.0 } else { idx.trunc() };
|
||||||
|
let actual = if rel < 0.0 { n as f64 + rel } else { rel };
|
||||||
|
if actual < 0.0 || actual >= n as f64 {
|
||||||
|
return NativeResult::Err(crate::error::create_range_error(vm, "invalid index for Array.prototype.with"));
|
||||||
|
}
|
||||||
|
let actual_idx = actual as usize;
|
||||||
|
let value = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||||
|
if let Err(err) = check_array_create_len(vm, n) {
|
||||||
|
return NativeResult::Err(err);
|
||||||
|
}
|
||||||
|
let new_arr = create_new_array(vm, n);
|
||||||
|
for i in 0..n {
|
||||||
|
// 规范:被替换位置不执行 Get。
|
||||||
|
let elem = if i == actual_idx { value } else { arraylike_get_or_err!(vm, arr_ptr, i) };
|
||||||
|
unsafe {
|
||||||
|
(*new_arr).set_prop_at(i, elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||||
|
}
|
||||||
|
pub fn array_to_sorted<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
builtins_debug!("Array.prototype.toSorted called with {} args", args.len());
|
||||||
|
// 规范先校验 compareFn 可调用,再读取 this/length。
|
||||||
|
let comparator = if args.len() > 1 {
|
||||||
|
match parse_sort_comparator(vm, vm.reg(args[1])) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(err) => return NativeResult::Err(err),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let this_val = vm.reg(args[0]);
|
||||||
|
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return NativeResult::Err(err),
|
||||||
|
};
|
||||||
|
if let Err(err) = check_array_create_len(vm, n) {
|
||||||
|
return NativeResult::Err(err);
|
||||||
|
}
|
||||||
|
let mut vals: Vec<JsValue> = match (0..n)
|
||||||
|
.map(|i| arraylike_get(vm, arr_ptr, i))
|
||||||
|
.collect::<Result<Vec<JsValue>, JsValue>>()
|
||||||
|
{
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return NativeResult::Err(err),
|
||||||
|
};
|
||||||
|
if let Err(err) = sort_values_inner(vm, &mut vals, comparator) {
|
||||||
|
return NativeResult::Err(err);
|
||||||
|
}
|
||||||
|
let new_arr = create_new_array(vm, n);
|
||||||
|
for (i, &v) in vals.iter().enumerate() {
|
||||||
|
unsafe {
|
||||||
|
(*new_arr).set_prop_at(i, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||||
|
}
|
||||||
|
pub fn array_to_spliced<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
builtins_debug!("Array.prototype.toSpliced called with {} args", args.len());
|
||||||
|
let this_val = vm.reg(args[0]);
|
||||||
|
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return NativeResult::Err(err),
|
||||||
|
};
|
||||||
|
let relative_start = if args.len() > 1 {
|
||||||
|
match vm.coerce_number_bounded(vm.reg(args[1])) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
let relative_start = if relative_start.is_nan() { 0.0 } else { relative_start.trunc() };
|
||||||
|
let actual_start = if relative_start < 0.0 {
|
||||||
|
(n as f64 + relative_start).max(0.0) as usize
|
||||||
|
} else {
|
||||||
|
relative_start.min(n as f64) as usize
|
||||||
|
};
|
||||||
|
// 缺省规则:start 与 deleteCount 都缺省 -> 拷贝;仅缺省 deleteCount -> 删到末尾。
|
||||||
|
let delete_count = if args.len() <= 1 {
|
||||||
|
0
|
||||||
|
} else if args.len() == 2 {
|
||||||
|
n - actual_start
|
||||||
|
} else {
|
||||||
|
let dc = match vm.coerce_number_bounded(vm.reg(args[2])) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
|
||||||
|
};
|
||||||
|
let dc = if dc.is_nan() || dc <= 0.0 { 0 } else { dc.trunc() as usize };
|
||||||
|
dc.min(n - actual_start)
|
||||||
|
};
|
||||||
|
let insert_count = if args.len() > 3 { args.len() - 3 } else { 0 };
|
||||||
|
// ???newLen = len + insertCount - actualDeleteCount?> 2**53-1 ? TypeError?
|
||||||
|
// ArrayCreate(newLen) ? 2**32-1 ? RangeError??????/???????
|
||||||
|
let new_len = n.saturating_add(insert_count).saturating_sub(delete_count);
|
||||||
|
if new_len > 9_007_199_254_740_991 {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "Invalid array length"));
|
||||||
|
}
|
||||||
|
if let Err(err) = check_array_create_len(vm, new_len) {
|
||||||
|
return NativeResult::Err(err);
|
||||||
|
}
|
||||||
|
let mut out: Vec<JsValue> = Vec::with_capacity(new_len);
|
||||||
|
out.extend(
|
||||||
|
match (0..actual_start)
|
||||||
|
.map(|i| arraylike_get(vm, arr_ptr, i))
|
||||||
|
.collect::<Result<Vec<JsValue>, JsValue>>()
|
||||||
|
{
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return NativeResult::Err(err),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
for k in 0..insert_count {
|
||||||
|
out.push(vm.reg(args[3 + k]));
|
||||||
|
}
|
||||||
|
out.extend(
|
||||||
|
match (actual_start + delete_count..n)
|
||||||
|
.map(|i| arraylike_get(vm, arr_ptr, i))
|
||||||
|
.collect::<Result<Vec<JsValue>, JsValue>>()
|
||||||
|
{
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return NativeResult::Err(err),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let new_arr = create_new_array(vm, out.len());
|
||||||
|
for (i, &v) in out.iter().enumerate() {
|
||||||
|
unsafe {
|
||||||
|
(*new_arr).set_prop_at(i, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||||
|
}
|
||||||
@@ -1,9 +1,3 @@
|
|||||||
use oxide_types::value::JsValue;
|
|
||||||
|
|
||||||
use oxide_runtime_api::{NativeResult, VmHost};
|
|
||||||
|
|
||||||
use crate::builtins_debug;
|
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
pub(crate) use common::*;
|
pub(crate) use common::*;
|
||||||
|
|
||||||
@@ -19,170 +13,5 @@ pub use iterate::*;
|
|||||||
mod sort_iterator;
|
mod sort_iterator;
|
||||||
pub use sort_iterator::*;
|
pub use sort_iterator::*;
|
||||||
|
|
||||||
/// Array.prototype.toReversed():返回元素逆序的新数组,原数组不变。
|
mod immutable;
|
||||||
pub fn array_to_reversed<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub use immutable::*;
|
||||||
builtins_debug!("Array.prototype.toReversed called with {} args", args.len());
|
|
||||||
let this_val = vm.reg(args[0]);
|
|
||||||
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => return NativeResult::Err(err),
|
|
||||||
};
|
|
||||||
let new_arr = create_new_array(vm, n);
|
|
||||||
if let Err(err) = check_array_create_len(vm, n) {
|
|
||||||
return NativeResult::Err(err);
|
|
||||||
}
|
|
||||||
// ????????????????getter ???????????
|
|
||||||
for i in (0..n).rev() {
|
|
||||||
let elem = arraylike_get_or_err!(vm, arr_ptr, i);
|
|
||||||
unsafe {
|
|
||||||
(*new_arr).set_prop_at(n - 1 - i, elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Array.prototype.with(index, value):返回把指定下标替换为新值的新数组,原数组不变。
|
|
||||||
pub fn array_with<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|
||||||
builtins_debug!("Array.prototype.with called with {} args", args.len());
|
|
||||||
let this_val = vm.reg(args[0]);
|
|
||||||
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => return NativeResult::Err(err),
|
|
||||||
};
|
|
||||||
let index_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
|
||||||
let idx = match vm.coerce_number_bounded(index_val) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
|
|
||||||
};
|
|
||||||
// ToIntegerOrInfinity:NaN/-0/+0 -> 0,其余截断。
|
|
||||||
let rel = if idx.is_nan() || idx == 0.0 { 0.0 } else { idx.trunc() };
|
|
||||||
let actual = if rel < 0.0 { n as f64 + rel } else { rel };
|
|
||||||
if actual < 0.0 || actual >= n as f64 {
|
|
||||||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid index for Array.prototype.with"));
|
|
||||||
}
|
|
||||||
let actual_idx = actual as usize;
|
|
||||||
let value = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
|
||||||
if let Err(err) = check_array_create_len(vm, n) {
|
|
||||||
return NativeResult::Err(err);
|
|
||||||
}
|
|
||||||
let new_arr = create_new_array(vm, n);
|
|
||||||
for i in 0..n {
|
|
||||||
// 规范:被替换位置不执行 Get。
|
|
||||||
let elem = if i == actual_idx { value } else { arraylike_get_or_err!(vm, arr_ptr, i) };
|
|
||||||
unsafe {
|
|
||||||
(*new_arr).set_prop_at(i, elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
|
||||||
}
|
|
||||||
pub fn array_to_sorted<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|
||||||
builtins_debug!("Array.prototype.toSorted called with {} args", args.len());
|
|
||||||
// 规范先校验 compareFn 可调用,再读取 this/length。
|
|
||||||
let comparator = if args.len() > 1 {
|
|
||||||
match parse_sort_comparator(vm, vm.reg(args[1])) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(err) => return NativeResult::Err(err),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let this_val = vm.reg(args[0]);
|
|
||||||
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => return NativeResult::Err(err),
|
|
||||||
};
|
|
||||||
if let Err(err) = check_array_create_len(vm, n) {
|
|
||||||
return NativeResult::Err(err);
|
|
||||||
}
|
|
||||||
let mut vals: Vec<JsValue> = match (0..n)
|
|
||||||
.map(|i| arraylike_get(vm, arr_ptr, i))
|
|
||||||
.collect::<Result<Vec<JsValue>, JsValue>>()
|
|
||||||
{
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => return NativeResult::Err(err),
|
|
||||||
};
|
|
||||||
if let Err(err) = sort_values_inner(vm, &mut vals, comparator) {
|
|
||||||
return NativeResult::Err(err);
|
|
||||||
}
|
|
||||||
let new_arr = create_new_array(vm, n);
|
|
||||||
for (i, &v) in vals.iter().enumerate() {
|
|
||||||
unsafe {
|
|
||||||
(*new_arr).set_prop_at(i, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
|
||||||
}
|
|
||||||
pub fn array_to_spliced<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|
||||||
builtins_debug!("Array.prototype.toSpliced called with {} args", args.len());
|
|
||||||
let this_val = vm.reg(args[0]);
|
|
||||||
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => return NativeResult::Err(err),
|
|
||||||
};
|
|
||||||
let relative_start = if args.len() > 1 {
|
|
||||||
match vm.coerce_number_bounded(vm.reg(args[1])) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
let relative_start = if relative_start.is_nan() { 0.0 } else { relative_start.trunc() };
|
|
||||||
let actual_start = if relative_start < 0.0 {
|
|
||||||
(n as f64 + relative_start).max(0.0) as usize
|
|
||||||
} else {
|
|
||||||
relative_start.min(n as f64) as usize
|
|
||||||
};
|
|
||||||
// 缺省规则:start 与 deleteCount 都缺省 -> 拷贝;仅缺省 deleteCount -> 删到末尾。
|
|
||||||
let delete_count = if args.len() <= 1 {
|
|
||||||
0
|
|
||||||
} else if args.len() == 2 {
|
|
||||||
n - actual_start
|
|
||||||
} else {
|
|
||||||
let dc = match vm.coerce_number_bounded(vm.reg(args[2])) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
|
|
||||||
};
|
|
||||||
let dc = if dc.is_nan() || dc <= 0.0 { 0 } else { dc.trunc() as usize };
|
|
||||||
dc.min(n - actual_start)
|
|
||||||
};
|
|
||||||
let insert_count = if args.len() > 3 { args.len() - 3 } else { 0 };
|
|
||||||
// ???newLen = len + insertCount - actualDeleteCount?> 2**53-1 ? TypeError?
|
|
||||||
// ArrayCreate(newLen) ? 2**32-1 ? RangeError??????/???????
|
|
||||||
let new_len = n.saturating_add(insert_count).saturating_sub(delete_count);
|
|
||||||
if new_len > 9_007_199_254_740_991 {
|
|
||||||
return NativeResult::Err(crate::error::create_type_error(vm, "Invalid array length"));
|
|
||||||
}
|
|
||||||
if let Err(err) = check_array_create_len(vm, new_len) {
|
|
||||||
return NativeResult::Err(err);
|
|
||||||
}
|
|
||||||
let mut out: Vec<JsValue> = Vec::with_capacity(new_len);
|
|
||||||
out.extend(
|
|
||||||
match (0..actual_start)
|
|
||||||
.map(|i| arraylike_get(vm, arr_ptr, i))
|
|
||||||
.collect::<Result<Vec<JsValue>, JsValue>>()
|
|
||||||
{
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => return NativeResult::Err(err),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
for k in 0..insert_count {
|
|
||||||
out.push(vm.reg(args[3 + k]));
|
|
||||||
}
|
|
||||||
out.extend(
|
|
||||||
match (actual_start + delete_count..n)
|
|
||||||
.map(|i| arraylike_get(vm, arr_ptr, i))
|
|
||||||
.collect::<Result<Vec<JsValue>, JsValue>>()
|
|
||||||
{
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => return NativeResult::Err(err),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let new_arr = create_new_array(vm, out.len());
|
|
||||||
for (i, &v) in out.iter().enumerate() {
|
|
||||||
unsafe {
|
|
||||||
(*new_arr).set_prop_at(i, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user