//! Array 构造器与静态方法(constructor / isArray / of / from)及 species 构造辅助。 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, string_arraylike_units, unexpected_tail_call_error, unit_string_value, }; /// JS `Array()` 构造逻辑:单个数字参数创建指定长度空数组,其余情况把参数作为元素。 pub fn array_constructor(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())); // 数值长度分支建 n 个空洞而非 present-undefined:`in`、for-in、 // Object.keys 按缺失处理,元素 Get 落原型链。 for i in 0..n { unsafe { (*arr).mark_hole_at(i); } } 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(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(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(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,构造结果对象,逐索引取值。 // 字符串源(原始串/装箱串)的 length 与索引未物化,直接按 UTF-16 单元读。 let string_units = string_arraylike_units(vm, items); let obj_val = match &string_units { Some(_) => JsValue::undefined(), None => match oxide_runtime_api::to_object(items, vm) { Ok(o) => o, Err(err) => return NativeResult::Err(crate::error::create_type_error(vm, &err)), }, }; let len = match &string_units { Some(units) => units.len().min(MAX_DENSE_PROPS), None => { 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")); } 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 = match &string_units { Some(units) => unit_string_value(vm, units[i]), None => arraylike_get_or_err!(vm, obj_val.as_js_object_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(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( 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( 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( 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( 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(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(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); } } }