feat: TypedArray 原型方法族全量解锁(回调/查找/迭代/排序/ES2023 copy 族)
This commit is contained in:
@@ -567,6 +567,66 @@ pub(crate) fn set_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> Nativ
|
||||
map_set_next_dispatch::<H>(vm, args, MapSetMode::SetEntries)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum TypedArrayMode {
|
||||
Values,
|
||||
Keys,
|
||||
Entries,
|
||||
}
|
||||
|
||||
/// 按模式让 TypedArray 迭代器包装器推进一步:values 产出元素,keys 产出索引,
|
||||
/// entries 产出 `[index, element]` 对;耗尽后把下标推进到哨兵值防止"复活"。
|
||||
fn typed_array_step<H: VmHost>(
|
||||
vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, mode: TypedArrayMode,
|
||||
) -> Result<JsValue, JsValue> {
|
||||
let index = current_index(vm, wrapper, index_si);
|
||||
let view = crate::typed_array::get_typed_array_data(vm, inner)?;
|
||||
if index >= view.length {
|
||||
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(i32::MAX));
|
||||
return Ok(make_iter_result(vm, JsValue::undefined(), true));
|
||||
}
|
||||
let elem = crate::typed_array::typed_array_element_get(vm, unsafe { &*inner.as_js_object_ptr() }, index as u32)
|
||||
.map_err(|e| crate::error::create_type_error(vm, &e))?;
|
||||
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32));
|
||||
let value = match mode {
|
||||
TypedArrayMode::Values => elem,
|
||||
TypedArrayMode::Keys => JsValue::int(index as i32),
|
||||
TypedArrayMode::Entries => make_map_set_pair(vm, JsValue::int(index as i32), elem),
|
||||
};
|
||||
Ok(make_iter_result(vm, value, false))
|
||||
}
|
||||
|
||||
/// TypedArray 模式迭代器 `next` 的分发:校验包装器后按模式推进。
|
||||
fn typed_array_next_dispatch<H: VmHost>(vm: &mut H, args: &[u8], mode: TypedArrayMode) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !this_val.is_object() {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "iterator next called on non-object"));
|
||||
}
|
||||
let wrapper = unsafe { &mut *this_val.as_js_object_ptr() };
|
||||
let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0;
|
||||
let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0;
|
||||
let inner = match vm.ordinary_get(wrapper, inner_si, this_val) {
|
||||
Ok(inner) if !inner.is_undefined() => inner,
|
||||
_ => return NativeResult::Err(crate::error::create_type_error(vm, "iterator has no inner typed array")),
|
||||
};
|
||||
match typed_array_step(vm, wrapper, inner, index_si, mode) {
|
||||
Ok(result) => NativeResult::Ok(result),
|
||||
Err(err) => NativeResult::Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn typed_array_values_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
typed_array_next_dispatch::<H>(vm, args, TypedArrayMode::Values)
|
||||
}
|
||||
|
||||
pub(crate) fn typed_array_keys_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
typed_array_next_dispatch::<H>(vm, args, TypedArrayMode::Keys)
|
||||
}
|
||||
|
||||
pub(crate) fn typed_array_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
typed_array_next_dispatch::<H>(vm, args, TypedArrayMode::Entries)
|
||||
}
|
||||
|
||||
/// 构造迭代器包装器,其 `next` 委托给调用方指定的按模式分发的 native 函数。
|
||||
/// 与 `make_iterator_for_value` 一致,但允许 Map/Set 原型方法选择
|
||||
/// values/keys/entries 变体。
|
||||
|
||||
@@ -32,7 +32,10 @@ fn range_error<H: VmHost>(vm: &mut H, msg: &str) -> JsValue {
|
||||
}
|
||||
|
||||
fn to_index<H: VmHost>(vm: &mut H, value: JsValue, msg: &str) -> Result<usize, JsValue> {
|
||||
let n = vm.coerce_number_bounded(value).unwrap_or(f64::NAN);
|
||||
let n = match vm.coerce_number_bounded(value) {
|
||||
Ok(n) => n,
|
||||
Err(e) => return Err(crate::iterator::engine_error(vm, &e)),
|
||||
};
|
||||
if n.is_nan() {
|
||||
return Ok(0);
|
||||
}
|
||||
@@ -42,16 +45,21 @@ fn to_index<H: VmHost>(vm: &mut H, value: JsValue, msg: &str) -> Result<usize, J
|
||||
Ok(n.trunc() as usize)
|
||||
}
|
||||
|
||||
fn normalize_index<H: VmHost>(vm: &mut H, value: JsValue, len: usize) -> usize {
|
||||
let n = vm.coerce_number_bounded(value).unwrap_or(f64::NAN);
|
||||
/// 按 ToIntegerOrInfinity 语义把索引归一化到 `[0, len]`(越界夹取);符号等不可
|
||||
/// 转换值透传异常。
|
||||
fn normalize_index<H: VmHost>(vm: &mut H, value: JsValue, len: usize) -> Result<usize, JsValue> {
|
||||
let n = match vm.coerce_number_bounded(value) {
|
||||
Ok(n) => n,
|
||||
Err(e) => return Err(crate::iterator::engine_error(vm, &e)),
|
||||
};
|
||||
if n.is_nan() {
|
||||
return 0;
|
||||
return Ok(0);
|
||||
}
|
||||
let int = n.trunc() as isize;
|
||||
if int < 0 {
|
||||
len.saturating_sub((-int) as usize)
|
||||
Ok(len.saturating_sub((-int) as usize))
|
||||
} else {
|
||||
(int as usize).min(len)
|
||||
Ok((int as usize).min(len))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +458,7 @@ pub fn typed_array_at<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
let raw_index = if args.len() > 1 {
|
||||
numeric_value(vm, vm.reg(args[1])).trunc() as isize
|
||||
native_try!(ta_to_number(vm, vm.reg(args[1]))).trunc() as isize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
@@ -468,17 +476,17 @@ pub fn typed_array_fill<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
let n = native_try!(ta_to_number(vm, value));
|
||||
let start = if args.len() > 2 {
|
||||
normalize_index(vm, vm.reg(args[2]), view.length)
|
||||
native_try!(normalize_index(vm, vm.reg(args[2]), view.length))
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let end = if args.len() > 3 {
|
||||
normalize_index(vm, vm.reg(args[3]), view.length)
|
||||
let end = if args.len() > 3 && !vm.reg(args[3]).is_undefined() {
|
||||
native_try!(normalize_index(vm, vm.reg(args[3]), view.length))
|
||||
} else {
|
||||
view.length
|
||||
};
|
||||
let n = numeric_value(vm, value);
|
||||
let buffer_ptr = native_try!(array_buffer_data_ptr(vm, view.buffer));
|
||||
let buffer = unsafe { &mut *buffer_ptr };
|
||||
for idx in start..end.max(start) {
|
||||
@@ -492,12 +500,12 @@ pub fn typed_array_slice<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
let start = if args.len() > 1 {
|
||||
normalize_index(vm, vm.reg(args[1]), view.length)
|
||||
native_try!(normalize_index(vm, vm.reg(args[1]), view.length))
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let end = if args.len() > 2 {
|
||||
normalize_index(vm, vm.reg(args[2]), view.length)
|
||||
let end = if args.len() > 2 && !vm.reg(args[2]).is_undefined() {
|
||||
native_try!(normalize_index(vm, vm.reg(args[2]), view.length))
|
||||
} else {
|
||||
view.length
|
||||
};
|
||||
@@ -518,12 +526,12 @@ pub fn typed_array_subarray<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
let start = if args.len() > 1 {
|
||||
normalize_index(vm, vm.reg(args[1]), view.length)
|
||||
native_try!(normalize_index(vm, vm.reg(args[1]), view.length))
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let end = if args.len() > 2 {
|
||||
normalize_index(vm, vm.reg(args[2]), view.length)
|
||||
let end = if args.len() > 2 && !vm.reg(args[2]).is_undefined() {
|
||||
native_try!(normalize_index(vm, vm.reg(args[2]), view.length))
|
||||
} else {
|
||||
view.length
|
||||
};
|
||||
@@ -661,6 +669,730 @@ pub fn typed_array_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(new_obj)
|
||||
}
|
||||
|
||||
/// 读 TypedArray 指定索引的元素(视图 data 已取出的形式,供原型方法内部使用);
|
||||
/// 越界返回 undefined。
|
||||
fn ta_read<H: VmHost>(vm: &mut H, view: TypedArrayData, index: usize) -> Result<JsValue, JsValue> {
|
||||
if index >= view.length {
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
let buffer_ptr = array_buffer_data_ptr(vm, view.buffer)?;
|
||||
let buffer = unsafe { &*buffer_ptr };
|
||||
Ok(read_element(view.kind, buffer, absolute_byte_offset(view, index)))
|
||||
}
|
||||
|
||||
/// 把数值写入 TypedArray 指定索引(视图 data 已取出的形式,供原型方法内部使用)。
|
||||
fn ta_write<H: VmHost>(vm: &mut H, view: TypedArrayData, index: usize, value: f64) -> Result<(), JsValue> {
|
||||
let buffer_ptr = array_buffer_data_ptr(vm, view.buffer)?;
|
||||
let buffer = unsafe { &mut *buffer_ptr };
|
||||
write_element(view.kind, buffer, absolute_byte_offset(view, index), value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 把一组数值写成同类型的新 TypedArray(map/filter/toReversed/toSorted/with 共用)。
|
||||
fn create_ta_from_numbers<H: VmHost>(
|
||||
vm: &mut H, kind: TypedArrayKind, numbers: Vec<f64>,
|
||||
) -> Result<*mut JsObject, JsValue> {
|
||||
let bpe = kind.bytes_per_element();
|
||||
let len = numbers.len();
|
||||
let buffer = JsValue::from_js_object(new_array_buffer(vm, vec![0; len * bpe]));
|
||||
let buffer_ptr = array_buffer_data_ptr(vm, buffer)?;
|
||||
let buffer_ref = unsafe { &mut *buffer_ptr };
|
||||
for (idx, n) in numbers.into_iter().enumerate() {
|
||||
write_element(kind, buffer_ref, idx * bpe, n);
|
||||
}
|
||||
Ok(create_typed_array(vm, kind, buffer, 0, len))
|
||||
}
|
||||
|
||||
/// 调用 TypedArray 回调并收敛异常:tail call 一律视为内部错误。
|
||||
fn invoke_cb<H: VmHost>(vm: &mut H, cb: JsValue, this_arg: JsValue, cb_args: &[JsValue]) -> Result<JsValue, JsValue> {
|
||||
match crate::array::invoke_native_callback(vm, cb, this_arg, cb_args) {
|
||||
NativeResult::Ok(v) => Ok(v),
|
||||
NativeResult::Err(e) => Err(e),
|
||||
NativeResult::TailCall { .. } => Err(type_error(vm, "unexpected tail call in TypedArray callback")),
|
||||
}
|
||||
}
|
||||
|
||||
/// ToNumber 并恢复引擎错误为原始异常值。
|
||||
fn ta_to_number<H: VmHost>(vm: &mut H, value: JsValue) -> Result<f64, JsValue> {
|
||||
oxide_runtime_api::to_number_full(value, vm).map_err(|e| crate::iterator::engine_error(vm, &e))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.forEach(callback, thisArg)`:对每个元素调用 callback,返回 undefined。
|
||||
pub fn typed_array_for_each<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
native_try!(invoke_cb(vm, callback, this_arg, &[elem, JsValue::int(i as i32), this_val]));
|
||||
}
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.map(callback, thisArg)`:对每个元素调用 callback,
|
||||
/// 结果 ToNumber 后写入同类型的新 TypedArray。
|
||||
pub fn typed_array_map<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
let mut numbers = Vec::with_capacity(view.length);
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
let mapped = native_try!(invoke_cb(vm, callback, this_arg, &[elem, JsValue::int(i as i32), this_val]));
|
||||
numbers.push(native_try!(ta_to_number(vm, mapped)));
|
||||
}
|
||||
let new_obj = native_try!(create_ta_from_numbers(vm, view.kind, numbers));
|
||||
NativeResult::Ok(JsValue::from_js_object(new_obj))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.filter(callback, thisArg)`:保留 callback 为真的元素,
|
||||
/// 组成同类型的新 TypedArray。
|
||||
pub fn typed_array_filter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
let mut numbers = Vec::new();
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
let result = native_try!(invoke_cb(vm, callback, this_arg, &[elem, JsValue::int(i as i32), this_val]));
|
||||
if oxide_runtime_api::to_boolean(result) {
|
||||
numbers.push(native_try!(ta_to_number(vm, elem)));
|
||||
}
|
||||
}
|
||||
let new_obj = native_try!(create_ta_from_numbers(vm, view.kind, numbers));
|
||||
NativeResult::Ok(JsValue::from_js_object(new_obj))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.reduce(callback, initialValue)`:从左到右累计归约;
|
||||
/// 空数组且无初始值抛 TypeError。
|
||||
pub fn typed_array_reduce<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if view.length == 0 && args.len() < 3 {
|
||||
return NativeResult::Err(type_error(vm, "Reduce of empty array with no initial value"));
|
||||
}
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let (mut accumulator, start_idx) = if args.len() > 2 {
|
||||
(vm.reg(args[2]), 0usize)
|
||||
} else {
|
||||
(native_try!(ta_read(vm, view, 0)), 1)
|
||||
};
|
||||
for i in start_idx..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
accumulator = native_try!(invoke_cb(
|
||||
vm,
|
||||
callback,
|
||||
JsValue::undefined(),
|
||||
&[accumulator, elem, JsValue::int(i as i32), this_val],
|
||||
));
|
||||
}
|
||||
NativeResult::Ok(accumulator)
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.reduceRight(callback, initialValue)`:从右到左累计归约。
|
||||
pub fn typed_array_reduce_right<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if view.length == 0 && args.len() < 3 {
|
||||
return NativeResult::Err(type_error(vm, "Reduce of empty array with no initial value"));
|
||||
}
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let (mut accumulator, start_idx): (JsValue, i32) = if args.len() > 2 {
|
||||
(vm.reg(args[2]), view.length as i32 - 1)
|
||||
} else {
|
||||
(native_try!(ta_read(vm, view, view.length - 1)), view.length as i32 - 2)
|
||||
};
|
||||
for i in (0..=start_idx).rev() {
|
||||
let elem = native_try!(ta_read(vm, view, i as usize));
|
||||
accumulator = native_try!(invoke_cb(
|
||||
vm,
|
||||
callback,
|
||||
JsValue::undefined(),
|
||||
&[accumulator, elem, JsValue::int(i), this_val],
|
||||
));
|
||||
}
|
||||
NativeResult::Ok(accumulator)
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.every(callback, thisArg)`:所有元素满足 callback 才返回 true。
|
||||
pub fn typed_array_every<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
let result = native_try!(invoke_cb(vm, callback, this_arg, &[elem, JsValue::int(i as i32), this_val]));
|
||||
if !oxide_runtime_api::to_boolean(result) {
|
||||
return NativeResult::Ok(JsValue::bool(false));
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::bool(true))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.some(callback, thisArg)`:任一元素满足 callback 返回 true。
|
||||
pub fn typed_array_some<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
let result = native_try!(invoke_cb(vm, callback, this_arg, &[elem, JsValue::int(i as i32), this_val]));
|
||||
if oxide_runtime_api::to_boolean(result) {
|
||||
return NativeResult::Ok(JsValue::bool(true));
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::bool(false))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.find(callback, thisArg)`:返回首个 callback 为真的元素,否则 undefined。
|
||||
pub fn typed_array_find<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
let result = native_try!(invoke_cb(vm, callback, this_arg, &[elem, JsValue::int(i as i32), this_val]));
|
||||
if oxide_runtime_api::to_boolean(result) {
|
||||
return NativeResult::Ok(elem);
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.findIndex(callback, thisArg)`:返回首个 callback 为真的索引,否则 -1。
|
||||
pub fn typed_array_find_index<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
let result = native_try!(invoke_cb(vm, callback, this_arg, &[elem, JsValue::int(i as i32), this_val]));
|
||||
if oxide_runtime_api::to_boolean(result) {
|
||||
return NativeResult::Ok(JsValue::int(i as i32));
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.findLast(callback, thisArg)`:从后往前返回首个 callback 为真的元素。
|
||||
pub fn typed_array_find_last<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in (0..view.length).rev() {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
let result = native_try!(invoke_cb(vm, callback, this_arg, &[elem, JsValue::int(i as i32), this_val]));
|
||||
if oxide_runtime_api::to_boolean(result) {
|
||||
return NativeResult::Ok(elem);
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.findLastIndex(callback, thisArg)`:从后往前返回首个 callback
|
||||
/// 为真的索引,否则 -1。
|
||||
pub fn typed_array_find_last_index<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "callback is not a function"));
|
||||
}
|
||||
let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1])));
|
||||
let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in (0..view.length).rev() {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
let result = native_try!(invoke_cb(vm, callback, this_arg, &[elem, JsValue::int(i as i32), this_val]));
|
||||
if oxide_runtime_api::to_boolean(result) {
|
||||
return NativeResult::Ok(JsValue::int(i as i32));
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
}
|
||||
|
||||
/// 规范化 indexOf/includes 的 fromIndex:NaN 视为 0,负值折算后从 0 夹取,正值夹到长度。
|
||||
fn normalize_from_index<H: VmHost>(vm: &mut H, value: JsValue, len: usize) -> Result<usize, JsValue> {
|
||||
let n = match vm.coerce_number_bounded(value) {
|
||||
Ok(n) => n,
|
||||
Err(e) => return Err(crate::iterator::engine_error(vm, &e)),
|
||||
};
|
||||
if n.is_nan() {
|
||||
return Ok(0);
|
||||
}
|
||||
let int = n.trunc();
|
||||
if int >= 0.0 {
|
||||
Ok((int as usize).min(len))
|
||||
} else {
|
||||
let from = len as f64 + int;
|
||||
if from < 0.0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
Ok(from as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.indexOf(searchElement, fromIndex)`:用严格相等查找首个匹配索引。
|
||||
pub fn typed_array_index_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if view.length == 0 || args.len() < 2 {
|
||||
return NativeResult::Ok(JsValue::int(-1));
|
||||
}
|
||||
let target = vm.reg(args[1]);
|
||||
let from_index = if args.len() >= 3 {
|
||||
native_try!(normalize_from_index(vm, vm.reg(args[2]), view.length))
|
||||
} else {
|
||||
0
|
||||
};
|
||||
for i in from_index..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
if oxide_runtime_api::strict_eq(elem, target) {
|
||||
return NativeResult::Ok(JsValue::int(i as i32));
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.lastIndexOf(searchElement, fromIndex)`:从后往前查找首个匹配索引。
|
||||
pub fn typed_array_last_index_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if view.length == 0 {
|
||||
return NativeResult::Ok(JsValue::int(-1));
|
||||
}
|
||||
let target = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
let from_index: isize = if args.len() >= 3 {
|
||||
let v = vm.reg(args[2]);
|
||||
let f = match vm.coerce_number_bounded(v) {
|
||||
Ok(n) => n,
|
||||
Err(e) => return NativeResult::Err(crate::iterator::engine_error(vm, &e)),
|
||||
};
|
||||
if f.is_nan() {
|
||||
return NativeResult::Ok(JsValue::int(-1));
|
||||
}
|
||||
let f = f.trunc();
|
||||
if f >= 0.0 {
|
||||
(f as isize).min(view.length as isize - 1)
|
||||
} else {
|
||||
view.length as isize + f as isize
|
||||
}
|
||||
} else {
|
||||
view.length as isize - 1
|
||||
};
|
||||
if from_index < 0 {
|
||||
return NativeResult::Ok(JsValue::int(-1));
|
||||
}
|
||||
for i in (0..=from_index as usize).rev() {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
if oxide_runtime_api::strict_eq(elem, target) {
|
||||
return NativeResult::Ok(JsValue::int(i as i32));
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::int(-1))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.includes(searchElement, fromIndex)`:用 SameValueZero 判断是否包含
|
||||
/// (NaN 视为存在、+0/-0 视为相同)。
|
||||
pub fn typed_array_includes<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if view.length == 0 {
|
||||
return NativeResult::Ok(JsValue::bool(false));
|
||||
}
|
||||
let target = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
let from_index = if args.len() >= 3 {
|
||||
native_try!(normalize_from_index(vm, vm.reg(args[2]), view.length))
|
||||
} else {
|
||||
0
|
||||
};
|
||||
for i in from_index..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
if oxide_runtime_api::same_value_zero(elem, target) {
|
||||
return NativeResult::Ok(JsValue::bool(true));
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::bool(false))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.join(separator)`:用分隔符连接元素字符串(元素恒为数值)。
|
||||
pub fn typed_array_join<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
let sep = if args.len() > 1 {
|
||||
native_try!(
|
||||
oxide_runtime_api::to_string_full(vm.reg(args[1]), vm).map_err(|e| crate::iterator::engine_error(vm, &e))
|
||||
)
|
||||
} else {
|
||||
",".to_string()
|
||||
};
|
||||
let mut parts = Vec::with_capacity(view.length);
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
parts.push(oxide_runtime_api::to_string(elem));
|
||||
}
|
||||
NativeResult::Ok(vm.new_string(&parts.join(&sep)))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.values()`:返回迭代元素值的迭代器。
|
||||
pub fn typed_array_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
native_try!(get_typed_array_data(vm, this_val));
|
||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
crate::iterator::typed_array_values_iter_next::<H> as *const (),
|
||||
))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.keys()`:返回迭代元素索引的迭代器。
|
||||
pub fn typed_array_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
native_try!(get_typed_array_data(vm, this_val));
|
||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
crate::iterator::typed_array_keys_iter_next::<H> as *const (),
|
||||
))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.entries()`:返回迭代 `[index, element]` 对的迭代器。
|
||||
pub fn typed_array_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
native_try!(get_typed_array_data(vm, this_val));
|
||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
crate::iterator::typed_array_entries_iter_next::<H> as *const (),
|
||||
))
|
||||
}
|
||||
|
||||
/// 默认数值排序比较:NaN 视为最大排到末尾,其余按数值升序。
|
||||
fn default_ta_order(a: f64, b: f64) -> std::cmp::Ordering {
|
||||
if a.is_nan() && b.is_nan() {
|
||||
std::cmp::Ordering::Equal
|
||||
} else if a.is_nan() {
|
||||
std::cmp::Ordering::Greater
|
||||
} else if b.is_nan() {
|
||||
std::cmp::Ordering::Less
|
||||
} else {
|
||||
a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)
|
||||
}
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.sort(comparefn)`:原地排序,默认按数值升序(NaN 排末尾)。
|
||||
pub fn typed_array_sort<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
let comparator = if args.len() > 1 {
|
||||
let c = vm.reg(args[1]);
|
||||
if c.is_undefined() {
|
||||
None
|
||||
} else {
|
||||
Some(native_try!(crate::array::require_callback(vm, c)))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut vals: Vec<f64> = Vec::with_capacity(view.length);
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
vals.push(native_try!(ta_to_number(vm, elem)));
|
||||
}
|
||||
let mut sort_error = None;
|
||||
vals.sort_by(|a, b| {
|
||||
if sort_error.is_some() {
|
||||
return std::cmp::Ordering::Equal;
|
||||
}
|
||||
if let Some(cb) = comparator {
|
||||
match crate::array::invoke_native_callback(
|
||||
vm,
|
||||
cb,
|
||||
JsValue::undefined(),
|
||||
&[JsValue::float(*a), JsValue::float(*b)],
|
||||
) {
|
||||
NativeResult::Ok(r) => {
|
||||
let n = oxide_runtime_api::to_number(r);
|
||||
if n.is_nan() || n == 0.0 {
|
||||
std::cmp::Ordering::Equal
|
||||
} else if n < 0.0 {
|
||||
std::cmp::Ordering::Less
|
||||
} else {
|
||||
std::cmp::Ordering::Greater
|
||||
}
|
||||
}
|
||||
NativeResult::Err(err) => {
|
||||
sort_error = Some(err);
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
NativeResult::TailCall { .. } => {
|
||||
sort_error =
|
||||
Some(crate::error::create_type_error(vm, "unexpected tail call in typed array callback"));
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
} else {
|
||||
default_ta_order(*a, *b)
|
||||
}
|
||||
});
|
||||
if let Some(err) = sort_error {
|
||||
return NativeResult::Err(err);
|
||||
}
|
||||
for (i, n) in vals.into_iter().enumerate() {
|
||||
native_try!(ta_write(vm, view, i, n));
|
||||
}
|
||||
NativeResult::Ok(this_val)
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.reverse()`:原地反转元素顺序,返回 this。
|
||||
pub fn typed_array_reverse<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
let mut i = 0;
|
||||
let mut j = view.length.saturating_sub(1);
|
||||
while i < j {
|
||||
let tmp = native_try!(ta_read(vm, view, i));
|
||||
let n = native_try!(ta_to_number(vm, tmp));
|
||||
let jtmp = native_try!(ta_read(vm, view, j));
|
||||
let jv = native_try!(ta_to_number(vm, jtmp));
|
||||
native_try!(ta_write(vm, view, j, n));
|
||||
native_try!(ta_write(vm, view, i, jv));
|
||||
i += 1;
|
||||
j = j.saturating_sub(1);
|
||||
}
|
||||
NativeResult::Ok(this_val)
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.copyWithin(target, start, end)`:在数组内部复制元素区间,返回 this。
|
||||
pub fn typed_array_copy_within<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
let len = view.length;
|
||||
let target = if args.len() > 1 {
|
||||
native_try!(normalize_index(vm, vm.reg(args[1]), len))
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let start = if args.len() > 2 {
|
||||
native_try!(normalize_index(vm, vm.reg(args[2]), len))
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let end = if args.len() > 3 && !vm.reg(args[3]).is_undefined() {
|
||||
native_try!(normalize_index(vm, vm.reg(args[3]), len))
|
||||
} else {
|
||||
len
|
||||
};
|
||||
let count = end.max(start).saturating_sub(start).min(len.saturating_sub(target));
|
||||
// 目标区间前移与源区间重叠时逆序遍历,避免覆盖未读的源元素。
|
||||
let (mut from, mut to, direction) = if start < target && target < start + count {
|
||||
(start + count - 1, target + count - 1, -1isize)
|
||||
} else {
|
||||
(start, target, 1isize)
|
||||
};
|
||||
for _ in 0..count {
|
||||
let elem = native_try!(ta_read(vm, view, from));
|
||||
let v = native_try!(ta_to_number(vm, elem));
|
||||
native_try!(ta_write(vm, view, to, v));
|
||||
from = (from as isize + direction) as usize;
|
||||
to = (to as isize + direction) as usize;
|
||||
}
|
||||
NativeResult::Ok(this_val)
|
||||
}
|
||||
|
||||
/// 以元素为 receiver 调用其 `toLocaleString` 方法,返回结果(失败透传原异常)。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// `toLocaleString` 方法缺失时回退为 ToString 结果(无 Intl 的最小实现)。
|
||||
fn invoke_element_to_locale_string<H: VmHost>(vm: &mut H, element: JsValue) -> Result<JsValue, JsValue> {
|
||||
let obj_val = oxide_runtime_api::to_object(element, vm).map_err(|e| crate::iterator::engine_error(vm, &e))?;
|
||||
let obj = unsafe { &*obj_val.as_js_object_ptr() };
|
||||
let method_si = vm.kernel_core().perm_interner().intern("toLocaleString").0;
|
||||
let method = vm
|
||||
.ordinary_get(obj, method_si, element)
|
||||
.map_err(|e| crate::iterator::engine_error(vm, &e))?;
|
||||
if !crate::iterator::is_callable(method) {
|
||||
return Ok(vm.new_string(&oxide_runtime_api::to_string(element)));
|
||||
}
|
||||
match vm.call_function_sync(method, element, &[]) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(e) => Err(crate::iterator::engine_error(vm, &e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.toLocaleString()`:元素逐个调用 `toLocaleString` 后用 `,` 连接。
|
||||
pub fn typed_array_to_locale_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if view.length == 0 {
|
||||
return NativeResult::Ok(vm.new_string(""));
|
||||
}
|
||||
let mut parts = Vec::with_capacity(view.length);
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
let r = native_try!(invoke_element_to_locale_string(vm, elem));
|
||||
let s =
|
||||
native_try!(oxide_runtime_api::to_string_full(r, vm).map_err(|e| crate::iterator::engine_error(vm, &e)));
|
||||
parts.push(s);
|
||||
}
|
||||
NativeResult::Ok(vm.new_string(&parts.join(",")))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.toReversed()`:返回元素反转的同类型新 TypedArray(原对象不变)。
|
||||
pub fn typed_array_to_reversed<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
let mut numbers = Vec::with_capacity(view.length);
|
||||
for i in (0..view.length).rev() {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
numbers.push(native_try!(ta_to_number(vm, elem)));
|
||||
}
|
||||
let new_obj = native_try!(create_ta_from_numbers(vm, view.kind, numbers));
|
||||
NativeResult::Ok(JsValue::from_js_object(new_obj))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.toSorted(comparefn)`:返回元素排序后的同类型新 TypedArray
|
||||
/// (原对象不变)。
|
||||
pub fn typed_array_to_sorted<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
let comparator = if args.len() > 1 {
|
||||
let c = vm.reg(args[1]);
|
||||
if c.is_undefined() {
|
||||
None
|
||||
} else {
|
||||
Some(native_try!(crate::array::require_callback(vm, c)))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut vals: Vec<f64> = Vec::with_capacity(view.length);
|
||||
for i in 0..view.length {
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
vals.push(native_try!(ta_to_number(vm, elem)));
|
||||
}
|
||||
let mut sort_error = None;
|
||||
vals.sort_by(|a, b| {
|
||||
if sort_error.is_some() {
|
||||
return std::cmp::Ordering::Equal;
|
||||
}
|
||||
if let Some(cb) = comparator {
|
||||
match crate::array::invoke_native_callback(
|
||||
vm,
|
||||
cb,
|
||||
JsValue::undefined(),
|
||||
&[JsValue::float(*a), JsValue::float(*b)],
|
||||
) {
|
||||
NativeResult::Ok(r) => {
|
||||
let n = oxide_runtime_api::to_number(r);
|
||||
if n.is_nan() || n == 0.0 {
|
||||
std::cmp::Ordering::Equal
|
||||
} else if n < 0.0 {
|
||||
std::cmp::Ordering::Less
|
||||
} else {
|
||||
std::cmp::Ordering::Greater
|
||||
}
|
||||
}
|
||||
NativeResult::Err(err) => {
|
||||
sort_error = Some(err);
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
NativeResult::TailCall { .. } => {
|
||||
sort_error =
|
||||
Some(crate::error::create_type_error(vm, "unexpected tail call in typed array callback"));
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
} else {
|
||||
default_ta_order(*a, *b)
|
||||
}
|
||||
});
|
||||
if let Some(err) = sort_error {
|
||||
return NativeResult::Err(err);
|
||||
}
|
||||
let new_obj = native_try!(create_ta_from_numbers(vm, view.kind, vals));
|
||||
NativeResult::Ok(JsValue::from_js_object(new_obj))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.with(index, value)`:返回替换指定索引元素后的同类型新 TypedArray;
|
||||
/// 负索引从尾部折算,折算后越界抛 RangeError。
|
||||
pub fn typed_array_with<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_typed_array_data(vm, this_val));
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(type_error(vm, "TypedArray.prototype.with requires an index"));
|
||||
}
|
||||
// ToIntegerOrInfinity(index),负值折算为 len + index。
|
||||
let raw = native_try!(ta_to_number(vm, vm.reg(args[1])));
|
||||
let relative_index = if raw.is_nan() || raw == 0.0 {
|
||||
0.0
|
||||
} else if raw.is_infinite() {
|
||||
raw
|
||||
} else {
|
||||
raw.trunc()
|
||||
};
|
||||
let actual_index = if relative_index >= 0.0 {
|
||||
relative_index
|
||||
} else {
|
||||
view.length as f64 + relative_index
|
||||
};
|
||||
// 先 ToNumber(value)(可触发副作用/抛错),再做索引范围校验。
|
||||
let value = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
let replacement = native_try!(ta_to_number(vm, value));
|
||||
if actual_index.is_nan() || actual_index < 0.0 || actual_index >= view.length as f64 {
|
||||
return NativeResult::Err(range_error(vm, "Invalid typed array index"));
|
||||
}
|
||||
let index = actual_index as usize;
|
||||
let mut numbers = Vec::with_capacity(view.length);
|
||||
for i in 0..view.length {
|
||||
if i == index {
|
||||
numbers.push(replacement);
|
||||
continue;
|
||||
}
|
||||
let elem = native_try!(ta_read(vm, view, i));
|
||||
numbers.push(native_try!(ta_to_number(vm, elem)));
|
||||
}
|
||||
let new_obj = native_try!(create_ta_from_numbers(vm, view.kind, numbers));
|
||||
NativeResult::Ok(JsValue::from_js_object(new_obj))
|
||||
}
|
||||
|
||||
/// `%TypedArray%.from(source, mapfn?, thisArg?)`:从可迭代对象或 array-like 构造
|
||||
/// 以 `this`(构造器 C)为类型的 TypedArray;`mapfn` 逐元素映射(`thisArg` 作回调
|
||||
/// this),元素经 ToNumber 写入。
|
||||
|
||||
Reference in New Issue
Block a user