refactor: array from 族拆出 from 模块

This commit is contained in:
2026-09-17 11:50:22 +00:00
parent 24f15d3a7c
commit c509f78758
3 changed files with 411 additions and 398 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ use oxide_types::value::JsValue;
use oxide_runtime_api::{NativeResult, VmHost};
use super::from_engine_error;
use super::from::from_engine_error;
macro_rules! array_ptr {
($vm:expr, $args:expr) => {{
+407
View File
@@ -0,0 +1,407 @@
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};
use super::common::{
array_length_arg, array_type_error, arraylike_get, arraylike_get_or_err, create_new_array, invoke_native_callback,
is_constructor_value, js_array_index, require_callback, unexpected_tail_call_error,
};
/// JS `Array()` 构造逻辑:单个数字参数创建指定长度空数组,其余情况把参数作为元素。
pub fn array_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let proto_val = JsValue::from_js_object(proto);
if args.len() == 2 {
let val = vm.reg(args[1]);
if val.is_int() || val.is_double() {
let n = match array_length_arg(vm, val) {
Ok(n) => n,
Err(err) => return NativeResult::Err(err),
};
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n, vm.epoch().bump()));
return NativeResult::Ok(JsValue::from_js_object(arr));
}
}
let n_elems = if args.len() > 1 { args.len() - 1 } else { 0 };
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n_elems, vm.epoch().bump()));
for i in 0..n_elems {
unsafe {
(*arr).set_prop_at(i, vm.reg(args[1 + i]));
}
}
unsafe {
(*arr).set_prop_count(n_elems);
}
NativeResult::Ok(JsValue::from_js_object(arr))
}
/// `Array.isArray(value)`:参数是否为真正的 Array 对象。
pub fn array_is_array<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let val = vm.reg(args[1]);
if !val.is_object() {
return NativeResult::Ok(JsValue::bool(false));
}
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
return NativeResult::Ok(JsValue::bool(false));
}
NativeResult::Ok(JsValue::bool(unsafe { &*ptr }.is_array()))
}
/// `Array.of(...items)`:以参数为元素构造新数组。
///
/// # 步骤
/// 1. 按 `this`(构造函数 C)以参数个数构造结果对象:C 可构造时 `new C(len)`,
/// 否则新建普通数组。
/// 2. 元素逐个以 CreateDataProperty 语义写入结果对象。
/// 3. 收尾设置结果对象的 length(可触发继承的 length setter)。
pub fn array_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let n_elems = if args.len() > 1 { args.len() - 1 } else { 0 };
let c = vm.reg(args[0]);
let (a_ptr, is_array) = match construct_array_from_result(vm, c, &[JsValue::int(n_elems as i32)]) {
Ok(v) => v,
Err(err) => return NativeResult::Err(err),
};
for i in 0..n_elems {
let val = vm.reg(args[1 + i]);
if let Err(err) = array_from_set_prop(vm, a_ptr, is_array, i, val) {
return NativeResult::Err(err);
}
}
if let Err(err) = array_from_set_length(vm, a_ptr, is_array, n_elems) {
return NativeResult::Err(err);
}
NativeResult::Ok(JsValue::from_js_object(a_ptr))
}
/// `Array.from(items, mapFn?, thisArg?)`:从可迭代对象或 array-like 构造新数组,
/// 可选 `mapFn` 逐元素映射(`thisArg` 作回调 this),返回新数组。
///
/// # 步骤
/// 1. `items` 为 null/undefined 抛 TypeError;`mapFn` 非 undefined 时须可调用。
/// 2. 读取 `@@iterator` 判定可迭代(不调用):可迭代路径先按 `this`(构造函数 C)
/// `new C()` 构造结果对象,再经迭代协议逐个取元素;array-like 路径先 ToObject
/// 读 `length`,再 `new C(len)` 构造结果对象,逐索引取值(缺失属性取 undefined)。
/// 3. 元素经 `mapFn` 映射后以 CreateDataProperty 语义写入结果对象,收尾设置 length。
/// 4. 迭代器或 `mapFn` 抛错时透传原异常值;迭代中途异常先执行 IteratorClose。
///
/// # 边界与前提
/// - C 不可构造(非函数/箭头/普通 native 方法)时回退到普通空数组。
/// - 写入元素遇不可扩展对象或不可配置属性冲突时抛 TypeError(CreateDataPropertyOrThrow)。
///
/// # 注意事项
/// - 迭代只读包装器 `next`/`done`/`value` 属性,与 spread 物化共用同一迭代协议。
/// - new.target 不向被构造的 C 传播(与 Reflect.construct 同一简化)。
pub fn array_from<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let items = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
if items.is_null() || items.is_undefined() {
return NativeResult::Err(array_type_error(vm, "Array.from requires an iterable or array-like object"));
}
let mapfn = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
let mapping = if mapfn.is_undefined() {
None
} else {
match require_callback(vm, mapfn) {
Ok(cb) => Some(cb),
Err(err) => return NativeResult::Err(err),
}
};
let this_arg = if args.len() > 3 { vm.reg(args[3]) } else { JsValue::undefined() };
let c = vm.reg(args[0]);
// 先只读取 @@iterator 判定可迭代(不调用),保证"构造结果对象"先于"获取迭代器",
// 与规范的执行序一致。
let iterable = match crate::iterator::peek_iterator_method(vm, items) {
Ok(b) => b,
Err(err) => return NativeResult::Err(err),
};
let a_ptr;
let is_array;
if iterable {
// 可迭代路径:先 new C() 得到结果对象,再获取迭代器逐个取元素。
a_ptr = match construct_array_from_result(vm, c, &[]) {
Ok((ptr, is_arr)) => {
is_array = is_arr;
ptr
}
Err(err) => return NativeResult::Err(err),
};
let iterator = match crate::iterator::make_iterator_for_value(vm, items) {
Ok(it) => it,
Err(err) => return NativeResult::Err(err),
};
let next_si = vm.kernel_core().perm_interner().intern("next").0;
let done_si = vm.kernel_core().perm_interner().intern("done").0;
let value_si = vm.kernel_core().perm_interner().intern("value").0;
let mut k = 0usize;
let iter_result: Result<(), JsValue> = (|| {
loop {
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
let next_fn = match vm.ordinary_get(iter_obj, next_si, iterator) {
Ok(v) => v,
Err(err) => return Err(from_engine_error(vm, &err)),
};
let result = match vm.call_function_sync(next_fn, iterator, &[]) {
Ok(v) => v,
Err(err) => return Err(from_engine_error(vm, &err)),
};
if !result.is_object() {
return Err(array_type_error(vm, "iterator result is not an object"));
}
let result_obj = unsafe { &*result.as_js_object_ptr() };
let done = match vm.ordinary_get(result_obj, done_si, result) {
Ok(v) => oxide_runtime_api::to_boolean(v),
Err(err) => return Err(from_engine_error(vm, &err)),
};
if done {
break;
}
let elem = match vm.ordinary_get(result_obj, value_si, result) {
Ok(v) => v,
Err(err) => return Err(from_engine_error(vm, &err)),
};
let mapped = match mapping {
Some(cb) => match invoke_native_callback(vm, cb, this_arg, &[elem, JsValue::int(k as i32)]) {
NativeResult::Ok(m) => m,
NativeResult::Err(err) => return Err(err),
NativeResult::TailCall { .. } => {
return Err(array_type_error(vm, "unexpected tail call in array callback"))
}
},
None => elem,
};
array_from_set_prop(vm, a_ptr, is_array, k, mapped)?;
k += 1;
}
Ok(())
})();
if let Err(err) = iter_result {
// IteratorClose:异常退出先关闭迭代器(return() 转发给内层),再抛原异常。
close_iterator(vm, iterator);
return NativeResult::Err(err);
}
if let Err(err) = array_from_set_length(vm, a_ptr, is_array, k) {
return NativeResult::Err(err);
}
} else {
// array-like 路径:ToObject 后读 length,构造结果对象,逐索引取值。
let obj_val = match oxide_runtime_api::to_object(items, vm) {
Ok(o) => o,
Err(err) => return NativeResult::Err(crate::error::create_type_error(vm, &err)),
};
let obj_ptr = obj_val.as_js_object_ptr();
let obj = unsafe { &*obj_ptr };
let length_key = vm.new_string("length");
let length_si = vm.property_key_si(length_key);
let len_val = match vm.ordinary_get(obj, length_si, obj_val) {
Ok(v) => v,
Err(err) => return NativeResult::Err(from_engine_error(vm, &err)),
};
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)) => {
is_array = is_arr;
ptr
}
Err(err) => return NativeResult::Err(err),
};
for i in 0..len {
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, js_array_index(i)]) {
NativeResult::Ok(m) => m,
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
},
None => elem,
};
if let Err(err) = array_from_set_prop(vm, a_ptr, is_array, i, mapped) {
return NativeResult::Err(err);
}
}
if let Err(err) = array_from_set_length(vm, a_ptr, is_array, len) {
return NativeResult::Err(err);
}
}
NativeResult::Ok(JsValue::from_js_object(a_ptr))
}
/// 引擎调用边界抛出的错误恢复为原始异常值(透传),否则按错误文本构造对应 kind 的错误。
pub(crate) fn from_engine_error<H: VmHost>(vm: &mut H, err: &str) -> JsValue {
vm.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_from_text(vm, err))
}
/// 按构造函数 C 构造 Array.from 的结果对象:C 可构造时以 C.prototype 分配 `this`
/// 后调用 C(返回非对象时回退 `this`),否则返回普通空数组。
///
/// # 返回值
/// `(结果对象指针, 是否为真数组)`。
fn construct_array_from_result<H: VmHost>(
vm: &mut H, c: JsValue, args: &[JsValue],
) -> Result<(*mut JsObject, bool), JsValue> {
let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let proto_val = JsValue::from_js_object(proto);
let mut fallback_array = || {
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, 0, vm.epoch().bump()));
(arr, true)
};
if !c.is_object() {
return Ok(fallback_array());
}
let c_obj = unsafe { &*c.as_js_object_ptr() };
// 不可构造:箭头函数、非构造器标记的 native 方法。
let constructible = c_obj.is_function()
&& !c_obj.is_arrow()
&& !(c_obj.native_fn().is_some() && c_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR);
if !constructible {
return Ok(fallback_array());
}
// 仿 Reflect.construct:以 C.prototype 分配 this 后调用 C。
let proto_si = vm.kernel_core().perm_interner().intern("prototype").0;
let ctor_proto = match vm.resolve_property(c_obj, proto_si) {
Some(p) if p.is_object() => p,
_ => JsValue::from_js_object(vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject),
};
let this_ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, ctor_proto));
let this_val = JsValue::from_js_object(this_ptr);
match vm.call_function_sync(c, this_val, args) {
Ok(ret) if ret.is_object() => {
let ret_ptr = ret.as_js_object_ptr();
let ret_tag = unsafe { &*ret_ptr }.type_tag;
// 原始值包装对象(如 `Object(4)` 返回的 Number 包装)的索引属性存储不完整,
// 回退到刚分配的对象;真数组/普通对象按规范使用构造返回对象。
if matches!(
ret_tag,
JsObject::OBJ_TYPE_STRING_OBJ | JsObject::OBJ_TYPE_NUMBER_OBJ | JsObject::OBJ_TYPE_BOOLEAN_OBJ
) {
return Ok((this_ptr, false));
}
Ok((ret_ptr, unsafe { &*ret_ptr }.is_array()))
}
Ok(_) => Ok((this_ptr, false)),
Err(err) => Err(from_engine_error(vm, &err)),
}
}
/// ArraySpeciesCreate(originalArray, length):真数组读取 constructor / @@species
/// 并构造目标对象;array-like(非数组)直接 ArrayCreate(length)。
pub(crate) 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。
pub(crate) fn create_data_property_or_throw<H: VmHost>(
vm: &mut H, obj: &mut JsObject, prop_name_si: u32, val: JsValue,
) -> Result<(), JsValue> {
if vm.get_own_property_slot(obj, prop_name_si).is_none() && !obj.is_extensible() {
return Err(array_type_error(vm, "Cannot define property: object is not extensible"));
}
match vm.define_data_property(obj, prop_name_si, val, PropAttributes::new(true, true, true)) {
Ok(()) => Ok(()),
Err(err) => Err(crate::error::create_type_error(vm, &err)),
}
}
/// 把元素写入 Array.from 的结果对象:真数组走密集元素区,普通对象走
/// CreateDataProperty 语义(属性名按十进制索引字符串)。
fn array_from_set_prop<H: VmHost>(
vm: &mut H, a: *mut JsObject, is_array: bool, i: usize, val: JsValue,
) -> Result<(), JsValue> {
if is_array {
unsafe {
(*a).set_prop_at(i, val);
}
return Ok(());
}
let key = vm.new_string(&i.to_string());
let key_si = vm.property_key_si(key);
create_data_property_or_throw(vm, unsafe { &mut *a }, key_si, val)
}
/// 收尾设置 Array.from 结果对象的 length:真数组直接改元素计数,普通对象走
/// 普通 Set(可触发继承的 length setter 并透传其异常)。
fn array_from_set_length<H: VmHost>(vm: &mut H, a: *mut JsObject, is_array: bool, len: usize) -> Result<(), JsValue> {
if is_array {
unsafe {
(*a).set_prop_count_fast(len);
}
return Ok(());
}
let a_obj = unsafe { &mut *a };
let length_key = vm.new_string("length");
let length_si = vm.property_key_si(length_key);
let a_val = JsValue::from_js_object(a);
match vm.ordinary_set(a_obj, length_si, JsValue::int(len as i32), a_val, true) {
Ok(()) => Ok(()),
Err(err) => Err(from_engine_error(vm, &err)),
}
}
/// IteratorClose:迭代异常退出时调用迭代器包装器的 `return()`(转发给内层迭代器),
/// 丢弃其自身错误,保留在途异常。
fn close_iterator<H: VmHost>(vm: &mut H, iterator: JsValue) {
if !iterator.is_object() {
return;
}
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
let return_si = vm.kernel_core().perm_interner().intern("return").0;
if let Ok(ret) = vm.ordinary_get(iter_obj, return_si, iterator) {
if ret.is_object() && unsafe { &*ret.as_js_object_ptr() }.is_function() {
// return() 的抛错被忽略,其值不得外泄进槽覆盖在途异常。
let saved_uncaught = vm.take_uncaught_value();
let _ = vm.call_function_sync(ret, iterator, &[]);
vm.restore_uncaught_value(saved_uncaught);
}
}
}
+3 -397
View File
@@ -1,6 +1,5 @@
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::object::JsObject;
use oxide_types::value::JsValue;
use oxide_runtime_api::{NativeResult, VmHost};
@@ -11,401 +10,8 @@ use crate::builtins_error;
mod common;
pub(crate) use common::*;
/// JS `Array()` 构造逻辑:单个数字参数创建指定长度空数组,其余情况把参数作为元素。
pub fn array_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let proto_val = JsValue::from_js_object(proto);
if args.len() == 2 {
let val = vm.reg(args[1]);
if val.is_int() || val.is_double() {
let n = match array_length_arg(vm, val) {
Ok(n) => n,
Err(err) => return NativeResult::Err(err),
};
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n, vm.epoch().bump()));
return NativeResult::Ok(JsValue::from_js_object(arr));
}
}
let n_elems = if args.len() > 1 { args.len() - 1 } else { 0 };
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n_elems, vm.epoch().bump()));
for i in 0..n_elems {
unsafe {
(*arr).set_prop_at(i, vm.reg(args[1 + i]));
}
}
unsafe {
(*arr).set_prop_count(n_elems);
}
NativeResult::Ok(JsValue::from_js_object(arr))
}
/// `Array.isArray(value)`:参数是否为真正的 Array 对象。
pub fn array_is_array<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let val = vm.reg(args[1]);
if !val.is_object() {
return NativeResult::Ok(JsValue::bool(false));
}
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
return NativeResult::Ok(JsValue::bool(false));
}
NativeResult::Ok(JsValue::bool(unsafe { &*ptr }.is_array()))
}
/// `Array.of(...items)`:以参数为元素构造新数组。
///
/// # 步骤
/// 1. 按 `this`(构造函数 C)以参数个数构造结果对象:C 可构造时 `new C(len)`,
/// 否则新建普通数组。
/// 2. 元素逐个以 CreateDataProperty 语义写入结果对象。
/// 3. 收尾设置结果对象的 length(可触发继承的 length setter)。
pub fn array_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let n_elems = if args.len() > 1 { args.len() - 1 } else { 0 };
let c = vm.reg(args[0]);
let (a_ptr, is_array) = match construct_array_from_result(vm, c, &[JsValue::int(n_elems as i32)]) {
Ok(v) => v,
Err(err) => return NativeResult::Err(err),
};
for i in 0..n_elems {
let val = vm.reg(args[1 + i]);
if let Err(err) = array_from_set_prop(vm, a_ptr, is_array, i, val) {
return NativeResult::Err(err);
}
}
if let Err(err) = array_from_set_length(vm, a_ptr, is_array, n_elems) {
return NativeResult::Err(err);
}
NativeResult::Ok(JsValue::from_js_object(a_ptr))
}
/// `Array.from(items, mapFn?, thisArg?)`:从可迭代对象或 array-like 构造新数组,
/// 可选 `mapFn` 逐元素映射(`thisArg` 作回调 this),返回新数组。
///
/// # 步骤
/// 1. `items` 为 null/undefined 抛 TypeError;`mapFn` 非 undefined 时须可调用。
/// 2. 读取 `@@iterator` 判定可迭代(不调用):可迭代路径先按 `this`(构造函数 C)
/// `new C()` 构造结果对象,再经迭代协议逐个取元素;array-like 路径先 ToObject
/// 读 `length`,再 `new C(len)` 构造结果对象,逐索引取值(缺失属性取 undefined)。
/// 3. 元素经 `mapFn` 映射后以 CreateDataProperty 语义写入结果对象,收尾设置 length。
/// 4. 迭代器或 `mapFn` 抛错时透传原异常值;迭代中途异常先执行 IteratorClose。
///
/// # 边界与前提
/// - C 不可构造(非函数/箭头/普通 native 方法)时回退到普通空数组。
/// - 写入元素遇不可扩展对象或不可配置属性冲突时抛 TypeError(CreateDataPropertyOrThrow)。
///
/// # 注意事项
/// - 迭代只读包装器 `next`/`done`/`value` 属性,与 spread 物化共用同一迭代协议。
/// - new.target 不向被构造的 C 传播(与 Reflect.construct 同一简化)。
pub fn array_from<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let items = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
if items.is_null() || items.is_undefined() {
return NativeResult::Err(array_type_error(vm, "Array.from requires an iterable or array-like object"));
}
let mapfn = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
let mapping = if mapfn.is_undefined() {
None
} else {
match require_callback(vm, mapfn) {
Ok(cb) => Some(cb),
Err(err) => return NativeResult::Err(err),
}
};
let this_arg = if args.len() > 3 { vm.reg(args[3]) } else { JsValue::undefined() };
let c = vm.reg(args[0]);
// 先只读取 @@iterator 判定可迭代(不调用),保证"构造结果对象"先于"获取迭代器",
// 与规范的执行序一致。
let iterable = match crate::iterator::peek_iterator_method(vm, items) {
Ok(b) => b,
Err(err) => return NativeResult::Err(err),
};
let a_ptr;
let is_array;
if iterable {
// 可迭代路径:先 new C() 得到结果对象,再获取迭代器逐个取元素。
a_ptr = match construct_array_from_result(vm, c, &[]) {
Ok((ptr, is_arr)) => {
is_array = is_arr;
ptr
}
Err(err) => return NativeResult::Err(err),
};
let iterator = match crate::iterator::make_iterator_for_value(vm, items) {
Ok(it) => it,
Err(err) => return NativeResult::Err(err),
};
let next_si = vm.kernel_core().perm_interner().intern("next").0;
let done_si = vm.kernel_core().perm_interner().intern("done").0;
let value_si = vm.kernel_core().perm_interner().intern("value").0;
let mut k = 0usize;
let iter_result: Result<(), JsValue> = (|| {
loop {
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
let next_fn = match vm.ordinary_get(iter_obj, next_si, iterator) {
Ok(v) => v,
Err(err) => return Err(from_engine_error(vm, &err)),
};
let result = match vm.call_function_sync(next_fn, iterator, &[]) {
Ok(v) => v,
Err(err) => return Err(from_engine_error(vm, &err)),
};
if !result.is_object() {
return Err(array_type_error(vm, "iterator result is not an object"));
}
let result_obj = unsafe { &*result.as_js_object_ptr() };
let done = match vm.ordinary_get(result_obj, done_si, result) {
Ok(v) => oxide_runtime_api::to_boolean(v),
Err(err) => return Err(from_engine_error(vm, &err)),
};
if done {
break;
}
let elem = match vm.ordinary_get(result_obj, value_si, result) {
Ok(v) => v,
Err(err) => return Err(from_engine_error(vm, &err)),
};
let mapped = match mapping {
Some(cb) => match invoke_native_callback(vm, cb, this_arg, &[elem, JsValue::int(k as i32)]) {
NativeResult::Ok(m) => m,
NativeResult::Err(err) => return Err(err),
NativeResult::TailCall { .. } => {
return Err(array_type_error(vm, "unexpected tail call in array callback"))
}
},
None => elem,
};
array_from_set_prop(vm, a_ptr, is_array, k, mapped)?;
k += 1;
}
Ok(())
})();
if let Err(err) = iter_result {
// IteratorClose:异常退出先关闭迭代器(return() 转发给内层),再抛原异常。
close_iterator(vm, iterator);
return NativeResult::Err(err);
}
if let Err(err) = array_from_set_length(vm, a_ptr, is_array, k) {
return NativeResult::Err(err);
}
} else {
// array-like 路径:ToObject 后读 length,构造结果对象,逐索引取值。
let obj_val = match oxide_runtime_api::to_object(items, vm) {
Ok(o) => o,
Err(err) => return NativeResult::Err(crate::error::create_type_error(vm, &err)),
};
let obj_ptr = obj_val.as_js_object_ptr();
let obj = unsafe { &*obj_ptr };
let length_key = vm.new_string("length");
let length_si = vm.property_key_si(length_key);
let len_val = match vm.ordinary_get(obj, length_si, obj_val) {
Ok(v) => v,
Err(err) => return NativeResult::Err(from_engine_error(vm, &err)),
};
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)) => {
is_array = is_arr;
ptr
}
Err(err) => return NativeResult::Err(err),
};
for i in 0..len {
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, js_array_index(i)]) {
NativeResult::Ok(m) => m,
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
},
None => elem,
};
if let Err(err) = array_from_set_prop(vm, a_ptr, is_array, i, mapped) {
return NativeResult::Err(err);
}
}
if let Err(err) = array_from_set_length(vm, a_ptr, is_array, len) {
return NativeResult::Err(err);
}
}
NativeResult::Ok(JsValue::from_js_object(a_ptr))
}
/// 引擎调用边界抛出的错误恢复为原始异常值(透传),否则按错误文本构造对应 kind 的错误。
fn from_engine_error<H: VmHost>(vm: &mut H, err: &str) -> JsValue {
vm.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_from_text(vm, err))
}
/// 按构造函数 C 构造 Array.from 的结果对象:C 可构造时以 C.prototype 分配 `this`
/// 后调用 C(返回非对象时回退 `this`),否则返回普通空数组。
///
/// # 返回值
/// `(结果对象指针, 是否为真数组)`。
fn construct_array_from_result<H: VmHost>(
vm: &mut H, c: JsValue, args: &[JsValue],
) -> Result<(*mut JsObject, bool), JsValue> {
let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let proto_val = JsValue::from_js_object(proto);
let mut fallback_array = || {
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, 0, vm.epoch().bump()));
(arr, true)
};
if !c.is_object() {
return Ok(fallback_array());
}
let c_obj = unsafe { &*c.as_js_object_ptr() };
// 不可构造:箭头函数、非构造器标记的 native 方法。
let constructible = c_obj.is_function()
&& !c_obj.is_arrow()
&& !(c_obj.native_fn().is_some() && c_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR);
if !constructible {
return Ok(fallback_array());
}
// 仿 Reflect.construct:以 C.prototype 分配 this 后调用 C。
let proto_si = vm.kernel_core().perm_interner().intern("prototype").0;
let ctor_proto = match vm.resolve_property(c_obj, proto_si) {
Some(p) if p.is_object() => p,
_ => JsValue::from_js_object(vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject),
};
let this_ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, ctor_proto));
let this_val = JsValue::from_js_object(this_ptr);
match vm.call_function_sync(c, this_val, args) {
Ok(ret) if ret.is_object() => {
let ret_ptr = ret.as_js_object_ptr();
let ret_tag = unsafe { &*ret_ptr }.type_tag;
// 原始值包装对象(如 `Object(4)` 返回的 Number 包装)的索引属性存储不完整,
// 回退到刚分配的对象;真数组/普通对象按规范使用构造返回对象。
if matches!(
ret_tag,
JsObject::OBJ_TYPE_STRING_OBJ | JsObject::OBJ_TYPE_NUMBER_OBJ | JsObject::OBJ_TYPE_BOOLEAN_OBJ
) {
return Ok((this_ptr, false));
}
Ok((ret_ptr, unsafe { &*ret_ptr }.is_array()))
}
Ok(_) => Ok((this_ptr, false)),
Err(err) => Err(from_engine_error(vm, &err)),
}
}
/// 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>(
vm: &mut H, obj: &mut JsObject, prop_name_si: u32, val: JsValue,
) -> Result<(), JsValue> {
if vm.get_own_property_slot(obj, prop_name_si).is_none() && !obj.is_extensible() {
return Err(array_type_error(vm, "Cannot define property: object is not extensible"));
}
match vm.define_data_property(obj, prop_name_si, val, PropAttributes::new(true, true, true)) {
Ok(()) => Ok(()),
Err(err) => Err(crate::error::create_type_error(vm, &err)),
}
}
/// 把元素写入 Array.from 的结果对象:真数组走密集元素区,普通对象走
/// CreateDataProperty 语义(属性名按十进制索引字符串)。
fn array_from_set_prop<H: VmHost>(
vm: &mut H, a: *mut JsObject, is_array: bool, i: usize, val: JsValue,
) -> Result<(), JsValue> {
if is_array {
unsafe {
(*a).set_prop_at(i, val);
}
return Ok(());
}
let key = vm.new_string(&i.to_string());
let key_si = vm.property_key_si(key);
create_data_property_or_throw(vm, unsafe { &mut *a }, key_si, val)
}
/// 收尾设置 Array.from 结果对象的 length:真数组直接改元素计数,普通对象走
/// 普通 Set(可触发继承的 length setter 并透传其异常)。
fn array_from_set_length<H: VmHost>(vm: &mut H, a: *mut JsObject, is_array: bool, len: usize) -> Result<(), JsValue> {
if is_array {
unsafe {
(*a).set_prop_count_fast(len);
}
return Ok(());
}
let a_obj = unsafe { &mut *a };
let length_key = vm.new_string("length");
let length_si = vm.property_key_si(length_key);
let a_val = JsValue::from_js_object(a);
match vm.ordinary_set(a_obj, length_si, JsValue::int(len as i32), a_val, true) {
Ok(()) => Ok(()),
Err(err) => Err(from_engine_error(vm, &err)),
}
}
/// IteratorClose:迭代异常退出时调用迭代器包装器的 `return()`(转发给内层迭代器),
/// 丢弃其自身错误,保留在途异常。
fn close_iterator<H: VmHost>(vm: &mut H, iterator: JsValue) {
if !iterator.is_object() {
return;
}
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
let return_si = vm.kernel_core().perm_interner().intern("return").0;
if let Ok(ret) = vm.ordinary_get(iter_obj, return_si, iterator) {
if ret.is_object() && unsafe { &*ret.as_js_object_ptr() }.is_function() {
// return() 的抛错被忽略,其值不得外泄进槽覆盖在途异常。
let saved_uncaught = vm.take_uncaught_value();
let _ = vm.call_function_sync(ret, iterator, &[]);
vm.restore_uncaught_value(saved_uncaught);
}
}
}
mod from;
pub use from::*;
/// `Array.prototype.push(...items)`:追加元素到尾部,返回新长度。
pub fn array_push<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {