fix: Array 方法 array-like receiver/Get 异常传播、species 构造、slice/toSpliced/toSorted 规范化,数组字面量空洞语义
This commit is contained in:
+345
-146
@@ -1,5 +1,6 @@
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_types::object::{JsObject, PropAttributes, MAX_DENSE_PROPS};
|
||||
use oxide_types::private_key::make_well_known_symbol_key;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use oxide_runtime_api::{NativeResult, VmHost};
|
||||
@@ -79,35 +80,97 @@ fn get_this_arraylike<H: VmHost>(vm: &mut H, val: JsValue) -> Result<(*mut JsObj
|
||||
return Err(array_type_error(vm, "Array.prototype method called on null or undefined"));
|
||||
}
|
||||
let obj = unsafe { &*ptr };
|
||||
if obj.is_array() {
|
||||
return Ok((ptr, obj.prop_count() as usize, true));
|
||||
}
|
||||
// 读取 length 属性
|
||||
let is_array = obj.is_array();
|
||||
// 读取 length 属性(按规范 Get 语义,getter 抛错需向上传播)。
|
||||
// 真数组也走 Get:`arr.length = N` 已由 ordinary_set 的数组分支同步元素区,
|
||||
// 直接 prop_count() 会漏掉 length 赋值。
|
||||
let length_key = vm.new_string("length");
|
||||
let length_si = vm.property_key_si(length_key);
|
||||
let len_val = vm.ordinary_get(unsafe { &*ptr }, length_si, val).unwrap_or(JsValue::int(0));
|
||||
let len_num = vm.coerce_number_bounded(len_val).unwrap_or(0.0);
|
||||
let len = if !len_num.is_finite() || len_num <= 0.0 {
|
||||
let len_val = match vm.ordinary_get(unsafe { &*ptr }, length_si, val) {
|
||||
Ok(v) => v,
|
||||
Err(msg) => return Err(from_engine_error(vm, &msg)),
|
||||
};
|
||||
if len_val.is_symbol() {
|
||||
return Err(array_type_error(vm, "Cannot convert a Symbol value to a number"));
|
||||
}
|
||||
let len_num = match vm.coerce_number_bounded(len_val) {
|
||||
Ok(v) => v,
|
||||
Err(msg) => return Err(from_engine_error(vm, &msg)),
|
||||
};
|
||||
// ToLength:min(max(0, ToIntegerOrInfinity(len)), 2**53-1)
|
||||
let len = if len_num.is_nan() || len_num <= 0.0 {
|
||||
0
|
||||
} else {
|
||||
(len_num as usize).min(MAX_DENSE_PROPS)
|
||||
len_num.min(9_007_199_254_740_991.0) as usize
|
||||
};
|
||||
Ok((ptr, len, false))
|
||||
Ok((ptr, len, is_array))
|
||||
}
|
||||
|
||||
/// 从 arraylike 读取 index 位置元素
|
||||
/// 从 arraylike 读取 index 位置元素(按规范 Get:访问器/原型链/异常完整传播)。
|
||||
#[inline(always)]
|
||||
fn arraylike_get<H: VmHost>(vm: &mut H, ptr: *mut JsObject, is_array: bool, i: usize) -> JsValue {
|
||||
if is_array {
|
||||
unsafe { (*ptr).get_prop_at(i) }
|
||||
} else {
|
||||
let key_str = vm.new_string(&i.to_string());
|
||||
let key_si = vm.property_key_si(key_str);
|
||||
let recv = JsValue::from_js_object(ptr);
|
||||
vm.ordinary_get(unsafe { &*ptr }, key_si, recv).unwrap_or(JsValue::undefined())
|
||||
fn arraylike_get<H: VmHost>(vm: &mut H, ptr: *mut JsObject, i: usize) -> Result<JsValue, JsValue> {
|
||||
let key_str = vm.new_string(&i.to_string());
|
||||
let key_si = vm.property_key_si(key_str);
|
||||
let recv = JsValue::from_js_object(ptr);
|
||||
match vm.ordinary_get(unsafe { &*ptr }, key_si, recv) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(msg) => Err(from_engine_error(vm, &msg)),
|
||||
}
|
||||
}
|
||||
|
||||
/// 元素读取失败时直接把异常作为 NativeResult::Err 返回。
|
||||
macro_rules! arraylike_get_or_err {
|
||||
($vm:expr, $ptr:expr, $i:expr) => {
|
||||
match arraylike_get($vm, $ptr, $i) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// 数组下标转 JsValue:超出 i32 范围时用 double 精确表示。
|
||||
#[inline(always)]
|
||||
fn js_array_index(i: usize) -> JsValue {
|
||||
if i <= i32::MAX as usize {
|
||||
JsValue::int(i as i32)
|
||||
} else {
|
||||
JsValue::float(i as f64)
|
||||
}
|
||||
}
|
||||
|
||||
/// ToIntegerOrInfinity 的 builtins 版本:对象经 ToPrimitive(number) 后取整,
|
||||
/// NaN/±0 → 0,±Inf 保留;转换异常向上传播。
|
||||
fn to_integer_or_infinity_bounded<H: VmHost>(vm: &mut H, val: JsValue) -> Result<f64, JsValue> {
|
||||
let n = match vm.coerce_number_bounded(val) {
|
||||
Ok(v) => v,
|
||||
Err(msg) => return Err(from_engine_error(vm, &msg)),
|
||||
};
|
||||
Ok(if n.is_nan() || n == 0.0 { 0.0 } else { n.trunc() })
|
||||
}
|
||||
|
||||
/// 把 ToIntegerOrInfinity 结果夹到 [0, len]:负数按 len+rel,正无穷取 len。
|
||||
#[inline(always)]
|
||||
fn clamp_relative(rel: f64, n_f: f64, n: usize) -> usize {
|
||||
if rel.is_infinite() {
|
||||
if rel < 0.0 { 0 } else { n }
|
||||
} else if rel < 0.0 {
|
||||
(n_f + rel).max(0.0) as usize
|
||||
} else {
|
||||
rel.min(n_f) as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// IsConstructor 的近似判定(与 construct_array_from_result 保持一致)。
|
||||
#[inline]
|
||||
fn is_constructor_value(c: JsValue) -> bool {
|
||||
if !c.is_object() {
|
||||
return false;
|
||||
}
|
||||
let c_obj = unsafe { &*c.as_js_object_ptr() };
|
||||
c_obj.is_function()
|
||||
&& !c_obj.is_arrow()
|
||||
&& !(c_obj.native_fn().is_some() && c_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR)
|
||||
}
|
||||
pub(crate) fn require_callback<H: VmHost>(vm: &mut H, callback_val: JsValue) -> Result<JsValue, JsValue> {
|
||||
if !callback_val.is_object() {
|
||||
return Err(array_type_error(vm, "callback is not a function"));
|
||||
@@ -129,6 +192,14 @@ fn create_new_array<H: VmHost>(vm: &mut H, n: usize) -> *mut JsObject {
|
||||
))
|
||||
}
|
||||
|
||||
/// ArrayCreate ?????length > 2**32 - 1 ? RangeError??????/???????
|
||||
fn check_array_create_len<H: VmHost>(vm: &mut H, n: usize) -> Result<(), JsValue> {
|
||||
if n > u32::MAX as usize {
|
||||
return Err(crate::error::create_range_error(vm, "Invalid array length"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn array_length_arg<H: VmHost>(vm: &mut H, value: JsValue) -> Result<usize, JsValue> {
|
||||
let n = vm.coerce_number_bounded(value).unwrap_or(f64::NAN);
|
||||
if !n.is_finite() || n < 0.0 || n.fract() != 0.0 || n > MAX_DENSE_PROPS as f64 {
|
||||
@@ -361,7 +432,11 @@ pub fn array_from<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Ok(v) => v,
|
||||
Err(err) => return NativeResult::Err(from_engine_error(vm, &err)),
|
||||
};
|
||||
let len = oxide_runtime_api::to_length(len_val).min(MAX_DENSE_PROPS as u64) as usize;
|
||||
let len_u64 = oxide_runtime_api::to_length(len_val);
|
||||
if len_u64 > u32::MAX as u64 {
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "Invalid array length"));
|
||||
}
|
||||
let len = len_u64.min(MAX_DENSE_PROPS as u64) as usize;
|
||||
|
||||
a_ptr = match construct_array_from_result(vm, c, &[JsValue::int(len as i32)]) {
|
||||
Ok((ptr, is_arr)) => {
|
||||
@@ -371,9 +446,9 @@ pub fn array_from<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
for i in 0..len {
|
||||
let elem = arraylike_get(vm, obj_ptr, false, i);
|
||||
let elem = arraylike_get_or_err!(vm, obj_ptr, i);
|
||||
let mapped = match mapping {
|
||||
Some(cb) => match invoke_native_callback(vm, cb, this_arg, &[elem, JsValue::int(i as i32)]) {
|
||||
Some(cb) => match invoke_native_callback(vm, cb, this_arg, &[elem, js_array_index(i)]) {
|
||||
NativeResult::Ok(m) => m,
|
||||
NativeResult::Err(err) => return NativeResult::Err(err),
|
||||
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
|
||||
@@ -451,6 +526,44 @@ fn construct_array_from_result<H: VmHost>(
|
||||
}
|
||||
}
|
||||
|
||||
/// ArraySpeciesCreate(originalArray, length):真数组读取 constructor / @@species
|
||||
/// 并构造目标对象;array-like(非数组)直接 ArrayCreate(length)。
|
||||
fn array_species_create<H: VmHost>(
|
||||
vm: &mut H, o_val: JsValue, is_array: bool, count: usize,
|
||||
) -> Result<*mut JsObject, JsValue> {
|
||||
if !is_array {
|
||||
return Ok(create_new_array(vm, count));
|
||||
}
|
||||
let o_ptr = o_val.as_js_object_ptr();
|
||||
let o_obj = unsafe { &*o_ptr };
|
||||
let ctor_key = vm.kernel_core().perm_interner().intern("constructor").0;
|
||||
let mut c = match vm.ordinary_get(o_obj, ctor_key, o_val) {
|
||||
Ok(v) => v,
|
||||
Err(msg) => return Err(from_engine_error(vm, &msg)),
|
||||
};
|
||||
if !is_constructor_value(c) {
|
||||
if !c.is_object() {
|
||||
return Err(crate::error::create_type_error(vm, "Species constructor not a constructor"));
|
||||
}
|
||||
let species_key = make_well_known_symbol_key(10);
|
||||
let s = match vm.ordinary_get(unsafe { &*c.as_js_object_ptr() }, species_key, c) {
|
||||
Ok(v) => v,
|
||||
Err(msg) => return Err(from_engine_error(vm, &msg)),
|
||||
};
|
||||
c = if s.is_null() { JsValue::undefined() } else { s };
|
||||
}
|
||||
if c.is_undefined() {
|
||||
return Ok(create_new_array(vm, count));
|
||||
}
|
||||
if !is_constructor_value(c) {
|
||||
return Err(crate::error::create_type_error(vm, "Species constructor not a constructor"));
|
||||
}
|
||||
match construct_array_from_result(vm, c, &[js_array_index(count)]) {
|
||||
Ok((ptr, _)) => Ok(ptr),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// CreateDataPropertyOrThrow:以 `{writable, enumerable, configurable}=true` 写入属性,
|
||||
/// 对象不可扩展或与既有不可配置属性冲突时抛 TypeError。
|
||||
fn create_data_property_or_throw<H: VmHost>(
|
||||
@@ -552,32 +665,61 @@ pub fn array_slice<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
let n_isize = n as isize;
|
||||
let n_f = n as f64;
|
||||
// ToIntegerOrInfinity(start),缺省 0;对象经 ToPrimitive(number)。
|
||||
let rel_start = if args.len() > 1 {
|
||||
oxide_runtime_api::to_integer_or_infinity(vm.reg(args[1])) as isize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let rel_end = if args.len() > 2 {
|
||||
oxide_runtime_api::to_integer_or_infinity(vm.reg(args[2])) as isize
|
||||
} else {
|
||||
n_isize
|
||||
};
|
||||
let start = if rel_start < 0 {
|
||||
(n_isize + rel_start).max(0)
|
||||
} else {
|
||||
rel_start.min(n_isize)
|
||||
} as usize;
|
||||
let end = if rel_end < 0 { (n_isize + rel_end).max(0) } else { rel_end.min(n_isize) } as usize;
|
||||
let count = end.saturating_sub(start);
|
||||
let new_arr = create_new_array(vm, count);
|
||||
unsafe {
|
||||
for i in 0..count {
|
||||
(*new_arr).set_prop_at(i, arraylike_get(vm, arr_ptr, is_array, start + i));
|
||||
match to_integer_or_infinity_bounded(vm, vm.reg(args[1])) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
}
|
||||
(*new_arr).set_prop_count(count);
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
// 规范:end 为 undefined 时使用 len;其余走 ToIntegerOrInfinity。
|
||||
let rel_end = if args.len() > 2 && !vm.reg(args[2]).is_undefined() {
|
||||
match to_integer_or_infinity_bounded(vm, vm.reg(args[2])) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
}
|
||||
} else {
|
||||
n_f
|
||||
};
|
||||
let start = clamp_relative(rel_start, n_f, n);
|
||||
let end = clamp_relative(rel_end, n_f, n);
|
||||
let count = end.saturating_sub(start);
|
||||
if let Err(err) = check_array_create_len(vm, count) {
|
||||
return NativeResult::Err(err);
|
||||
}
|
||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||
// ArraySpeciesCreate(O, count):真数组按 constructor/@@species 构造目标。
|
||||
let a_ptr = match array_species_create(vm, this_val, is_array, count) {
|
||||
Ok(p) => p,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
// HasProperty + Get + CreateDataPropertyOrThrow 逐元素拷贝。
|
||||
let mut k = start;
|
||||
let mut out_idx = 0usize;
|
||||
while k < end {
|
||||
let key_str = vm.new_string(&k.to_string());
|
||||
let key_si = vm.property_key_si(key_str);
|
||||
if vm.resolve_property(unsafe { &*arr_ptr }, key_si).is_some() {
|
||||
let elem = arraylike_get_or_err!(vm, arr_ptr, k);
|
||||
let out_key = vm.new_string(&out_idx.to_string());
|
||||
let out_key_si = vm.property_key_si(out_key);
|
||||
if let Err(err) = create_data_property_or_throw(vm, unsafe { &mut *a_ptr }, out_key_si, elem) {
|
||||
return NativeResult::Err(err);
|
||||
}
|
||||
out_idx += 1;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
// 收尾 Set(A, "length", final):普通 Set 语义(可触发继承 setter/异常)。
|
||||
let a_val = JsValue::from_js_object(a_ptr);
|
||||
let length_key = vm.new_string("length");
|
||||
let length_si = vm.property_key_si(length_key);
|
||||
if let Err(err) = vm.ordinary_set(unsafe { &mut *a_ptr }, length_si, js_array_index(out_idx), a_val) {
|
||||
return NativeResult::Err(from_engine_error(vm, &err));
|
||||
}
|
||||
NativeResult::Ok(JsValue::from_js_object(a_ptr))
|
||||
}
|
||||
|
||||
/// `Array.prototype.splice(start, deleteCount, ...items)`:删除并/或插入元素,
|
||||
@@ -649,13 +791,16 @@ pub fn array_splice<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn array_concat<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
builtins_debug!("Array.prototype.concat 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) {
|
||||
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
let mut all: Vec<JsValue> = Vec::new();
|
||||
if let Err(err) = check_array_create_len(vm, n) {
|
||||
return NativeResult::Err(err);
|
||||
}
|
||||
for i in 0..n {
|
||||
all.push(arraylike_get(vm, arr_ptr, is_array, i));
|
||||
all.push(arraylike_get_or_err!(vm, arr_ptr, i));
|
||||
}
|
||||
for &arg_reg in args.iter().skip(1) {
|
||||
let val = vm.reg(arg_reg);
|
||||
@@ -689,7 +834,7 @@ pub fn array_concat<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn array_join<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
builtins_debug!("Array.prototype.join 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) {
|
||||
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
@@ -698,16 +843,20 @@ pub fn array_join<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
} else {
|
||||
",".to_string()
|
||||
};
|
||||
let parts: Vec<String> = (0..n)
|
||||
let parts: Vec<String> = match (0..n)
|
||||
.map(|i| {
|
||||
let v = arraylike_get(vm, arr_ptr, is_array, i);
|
||||
let v = arraylike_get(vm, arr_ptr, i)?;
|
||||
if v.is_undefined() || v.is_null() {
|
||||
String::new()
|
||||
Ok(String::new())
|
||||
} else {
|
||||
oxide_runtime_api::to_string(v)
|
||||
Ok(oxide_runtime_api::to_string(v))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
.collect::<Result<Vec<String>, JsValue>>()
|
||||
{
|
||||
Ok(v) => v,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
let joined = parts.join(&sep);
|
||||
NativeResult::Ok(vm.new_string(&joined))
|
||||
}
|
||||
@@ -723,7 +872,7 @@ pub fn array_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn array_index_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
builtins_debug!("Array.prototype.indexOf called with {} args", args.len());
|
||||
let this_val = vm.reg(args[0]);
|
||||
let (ptr, n, is_array) = match get_this_arraylike(vm, this_val) {
|
||||
let (ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
@@ -750,9 +899,9 @@ pub fn array_index_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
0
|
||||
};
|
||||
for i in from_index..n {
|
||||
let elem = arraylike_get(vm, ptr, is_array, i);
|
||||
let elem = arraylike_get_or_err!(vm, ptr, i);
|
||||
if oxide_runtime_api::strict_eq(elem, target) {
|
||||
return NativeResult::Ok(JsValue::int(i as i32));
|
||||
return NativeResult::Ok(js_array_index(i));
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
@@ -763,7 +912,7 @@ pub fn array_index_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn array_includes<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
builtins_debug!("Array.prototype.includes called with {} args", args.len());
|
||||
let this_val = vm.reg(args[0]);
|
||||
let (ptr, n, is_array) = match get_this_arraylike(vm, this_val) {
|
||||
let (ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
@@ -789,7 +938,7 @@ pub fn array_includes<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
0
|
||||
};
|
||||
for i in from_index..n {
|
||||
let elem = arraylike_get(vm, ptr, is_array, i);
|
||||
let elem = arraylike_get_or_err!(vm, ptr, i);
|
||||
// SameValueZero:NaN 视为相等、+0 与 -0 视为相同。
|
||||
if same_value_zero(elem, target) {
|
||||
return NativeResult::Ok(JsValue::bool(true));
|
||||
@@ -893,7 +1042,7 @@ pub fn array_flat<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// `Array.prototype.forEach(callback, thisArg)`:对每个元素调用 callback,返回 undefined。
|
||||
pub fn array_for_each<H: VmHost>(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 (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"));
|
||||
@@ -904,8 +1053,8 @@ pub fn array_for_each<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..n {
|
||||
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), o_val]) {
|
||||
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),
|
||||
@@ -917,7 +1066,7 @@ pub fn array_for_each<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// `Array.prototype.map(callback, thisArg)`:对每个元素调用 callback 生成新数组。
|
||||
pub fn array_map<H: VmHost>(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 (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"));
|
||||
@@ -928,9 +1077,12 @@ pub fn array_map<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
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(vm, arr_ptr, is_array, i);
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), o_val]) {
|
||||
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);
|
||||
},
|
||||
@@ -947,7 +1099,7 @@ pub fn array_map<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// `Array.prototype.filter(callback, thisArg)`:保留 callback 返回真值的元素形成新数组。
|
||||
pub fn array_filter<H: VmHost>(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 (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");
|
||||
@@ -962,9 +1114,12 @@ 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 mut kept: Vec<JsValue> = 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(vm, arr_ptr, is_array, i);
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), o_val]) {
|
||||
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);
|
||||
@@ -988,7 +1143,7 @@ pub fn array_filter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// 空数组且无初始值抛 TypeError。
|
||||
pub fn array_reduce<H: VmHost>(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 (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");
|
||||
@@ -1012,18 +1167,13 @@ pub fn array_reduce<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
accumulator = vm.reg(args[2]);
|
||||
start_idx = 0;
|
||||
} else {
|
||||
accumulator = arraylike_get(vm, arr_ptr, is_array, 0);
|
||||
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(vm, arr_ptr, is_array, i);
|
||||
match invoke_native_callback(
|
||||
vm,
|
||||
callback_val,
|
||||
this_val,
|
||||
&[accumulator, elem, JsValue::int(i as i32), o_val],
|
||||
) {
|
||||
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");
|
||||
@@ -1041,7 +1191,7 @@ pub fn array_reduce<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// `Array.prototype.find(callback, thisArg)`:返回首个 callback 为真的元素,否则 undefined。
|
||||
pub fn array_find<H: VmHost>(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 (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");
|
||||
@@ -1056,8 +1206,8 @@ pub fn array_find<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..n {
|
||||
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), o_val]) {
|
||||
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);
|
||||
@@ -1079,7 +1229,7 @@ pub fn array_find<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// `Array.prototype.some(callback, thisArg)`:任一元素满足 callback 返回 true。
|
||||
pub fn array_some<H: VmHost>(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 (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");
|
||||
@@ -1094,8 +1244,8 @@ pub fn array_some<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..n {
|
||||
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), o_val]) {
|
||||
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));
|
||||
@@ -1117,7 +1267,7 @@ pub fn array_some<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// `Array.prototype.every(callback, thisArg)`:所有元素满足 callback 才返回 true。
|
||||
pub fn array_every<H: VmHost>(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 (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");
|
||||
@@ -1132,8 +1282,8 @@ pub fn array_every<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..n {
|
||||
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), o_val]) {
|
||||
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));
|
||||
@@ -1155,7 +1305,7 @@ pub fn array_every<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// `Array.prototype.flatMap(callback, thisArg)`:map 后把返回的数组展开一层。
|
||||
pub fn array_flat_map<H: VmHost>(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 (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");
|
||||
@@ -1170,9 +1320,12 @@ pub fn array_flat_map<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
let mut flat: Vec<JsValue> = 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(vm, arr_ptr, is_array, i);
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), o_val]) {
|
||||
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();
|
||||
@@ -1333,7 +1486,7 @@ pub fn array_at<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn array_last_index_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
builtins_debug!("Array.prototype.lastIndexOf called with {} args", args.len());
|
||||
let this_val = vm.reg(args[0]);
|
||||
let (ptr, n, is_array) = match get_this_arraylike(vm, this_val) {
|
||||
let (ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
@@ -1361,9 +1514,9 @@ pub fn array_last_index_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
return NativeResult::Ok(JsValue::int(-1));
|
||||
}
|
||||
for i in (0..=from_index_isize as usize).rev() {
|
||||
let elem = arraylike_get(vm, ptr, is_array, i);
|
||||
let elem = arraylike_get_or_err!(vm, ptr, i);
|
||||
if oxide_runtime_api::strict_eq(elem, search) {
|
||||
return NativeResult::Ok(JsValue::int(i as i32));
|
||||
return NativeResult::Ok(js_array_index(i));
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
@@ -1372,7 +1525,7 @@ pub fn array_last_index_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// `Array.prototype.findIndex(callback, thisArg)`:返回首个 callback 为真的索引,否则 -1。
|
||||
pub fn array_find_index<H: VmHost>(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 (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");
|
||||
@@ -1387,11 +1540,11 @@ pub fn array_find_index<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..n {
|
||||
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), o_val]) {
|
||||
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(JsValue::int(i as i32));
|
||||
return NativeResult::Ok(js_array_index(i));
|
||||
}
|
||||
}
|
||||
NativeResult::Err(err) => {
|
||||
@@ -1475,12 +1628,7 @@ pub fn array_reduce_right<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
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],
|
||||
) {
|
||||
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");
|
||||
@@ -1499,9 +1647,7 @@ pub fn array_reduce_right<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// 提供比较函数时按其返回值(<0/=0/>0)排序,回调抛错则中止。
|
||||
/// 对元素向量执行 Array.prototype.sort 的比较语义(原地)。
|
||||
/// 比较回调抛错时中止并把异常返回;默认按字符串字典序。
|
||||
fn sort_values_inner<H: VmHost>(
|
||||
vm: &mut H, vals: &mut Vec<JsValue>, comparator: Option<JsValue>,
|
||||
) -> Result<(), JsValue> {
|
||||
fn sort_values_inner<H: VmHost>(vm: &mut H, vals: &mut [JsValue], comparator: Option<JsValue>) -> Result<(), JsValue> {
|
||||
let mut sort_error = None;
|
||||
vals.sort_by(|a, b| {
|
||||
if sort_error.is_some() {
|
||||
@@ -1682,12 +1828,12 @@ pub fn array_iterator_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let len = if !len_num.is_finite() || len_num <= 0.0 { 0 } else { len_num as usize };
|
||||
|
||||
if (index as usize) >= len {
|
||||
let _ = vm.set_or_create_prop_value(iter, target_si, JsValue::undefined());
|
||||
vm.set_or_create_prop_value(iter, target_si, JsValue::undefined());
|
||||
return NativeResult::Ok(crate::iterator::make_iter_result(vm, JsValue::undefined(), true));
|
||||
}
|
||||
|
||||
let is_array = target_obj.is_array();
|
||||
let element = arraylike_get(vm, target.as_js_object_ptr(), is_array, index as usize);
|
||||
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 => {
|
||||
@@ -1708,7 +1854,7 @@ pub fn array_iterator_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// Array.prototype.findLastIndex(callback, thisArg):从后往前返回首个 callback 为真的下标。
|
||||
pub fn array_find_last_index<H: VmHost>(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 (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");
|
||||
@@ -1723,11 +1869,11 @@ pub fn array_find_last_index<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in (0..n).rev() {
|
||||
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), o_val]) {
|
||||
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(JsValue::int(i as i32));
|
||||
return NativeResult::Ok(js_array_index(i));
|
||||
}
|
||||
}
|
||||
NativeResult::Err(err) => {
|
||||
@@ -1747,13 +1893,17 @@ pub fn array_find_last_index<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
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) {
|
||||
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);
|
||||
for i in 0..n {
|
||||
let elem = arraylike_get(vm, arr_ptr, is_array, i);
|
||||
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);
|
||||
}
|
||||
@@ -1765,38 +1915,39 @@ pub fn array_to_reversed<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
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) {
|
||||
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 = vm.coerce_number_bounded(index_val).unwrap_or(f64::NAN);
|
||||
if !idx.is_finite() || idx.fract() != 0.0 || idx < 0.0 || idx >= n as f64 {
|
||||
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 {
|
||||
let elem = arraylike_get(vm, arr_ptr, is_array, i);
|
||||
// 规范:被替换位置不执行 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);
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
(*new_arr).set_prop_at(idx as usize, value);
|
||||
}
|
||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||
}
|
||||
|
||||
/// Array.prototype.toSorted(compareFn):返回排序后的新数组,原数组不变。
|
||||
pub fn array_to_sorted<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
builtins_debug!("Array.prototype.toSorted 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 mut vals: Vec<JsValue> = (0..n).map(|i| arraylike_get(vm, arr_ptr, is_array, i)).collect();
|
||||
// 规范先校验 compareFn 可调用,再读取 this/length。
|
||||
let comparator = if args.len() > 1 {
|
||||
match parse_sort_comparator(vm, vm.reg(args[1])) {
|
||||
Ok(c) => c,
|
||||
@@ -1805,6 +1956,21 @@ pub fn array_to_sorted<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
} 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);
|
||||
}
|
||||
@@ -1816,39 +1982,72 @@ pub fn array_to_sorted<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
NativeResult::Ok(JsValue::from_js_object(new_arr))
|
||||
}
|
||||
|
||||
/// Array.prototype.toSpliced(start, deleteCount, ...items):返回删除/插入后的新数组,原数组不变。
|
||||
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) {
|
||||
let (arr_ptr, n, _is_array) = match get_this_arraylike(vm, this_val) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
let start = if args.len() > 1 {
|
||||
let v = vm.coerce_number_bounded(vm.reg(args[1])).unwrap_or(f64::NAN);
|
||||
let s = v as i32;
|
||||
if s < 0 {
|
||||
(n as i32 + s).max(0) as usize
|
||||
} else {
|
||||
(s as usize).min(n)
|
||||
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.0
|
||||
};
|
||||
let delete_count = if args.len() > 2 {
|
||||
let v = vm.coerce_number_bounded(vm.reg(args[2])).unwrap_or(f64::NAN);
|
||||
(v as usize).min(n - start)
|
||||
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 {
|
||||
n - start
|
||||
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 };
|
||||
let mut out: Vec<JsValue> = Vec::with_capacity(n + insert_count.saturating_sub(delete_count));
|
||||
out.extend((0..start).map(|i| arraylike_get(vm, arr_ptr, is_array, i)));
|
||||
// ???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((start + delete_count..n).map(|i| arraylike_get(vm, arr_ptr, is_array, i)));
|
||||
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 {
|
||||
|
||||
@@ -22,6 +22,12 @@ impl Emitter {
|
||||
ctx.inst(Inst::new(OpCode::NEW_ARRAY, Operand::Reg(arr_reg), Operand::Imm(n), Operand::None));
|
||||
for (i, elem) in arr.elements.iter().enumerate() {
|
||||
let Some(e) = elem.as_expression() else {
|
||||
// hole?????????????????????????
|
||||
// ??? delete ?????? hole meta?length ????
|
||||
let scratch = ctx.alloc_reg();
|
||||
ctx.inst(Inst::inst_mov(Operand::Reg(scratch), Operand::Reg(arr_reg)));
|
||||
let idx = ctx.add_constant(Constant::Int(i as i32));
|
||||
ctx.inst(Inst::delete_prop_static(Operand::Reg(scratch), idx as u32));
|
||||
continue;
|
||||
};
|
||||
let val_reg = self.emit_expression(e, ctx)?;
|
||||
|
||||
Reference in New Issue
Block a user