From c509f78758252f627d0409d252b189944d82dbe7 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 17 Sep 2026 11:50:22 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20array=20from=20=E6=97=8F=E6=8B=86?= =?UTF-8?q?=E5=87=BA=20from=20=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/array/common.rs | 2 +- crates/oxide_builtins/src/array/from.rs | 407 ++++++++++++++++++++++ crates/oxide_builtins/src/array/mod.rs | 400 +-------------------- 3 files changed, 411 insertions(+), 398 deletions(-) create mode 100644 crates/oxide_builtins/src/array/from.rs diff --git a/crates/oxide_builtins/src/array/common.rs b/crates/oxide_builtins/src/array/common.rs index 86444355..c09c5bc4 100644 --- a/crates/oxide_builtins/src/array/common.rs +++ b/crates/oxide_builtins/src/array/common.rs @@ -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) => {{ diff --git a/crates/oxide_builtins/src/array/from.rs b/crates/oxide_builtins/src/array/from.rs new file mode 100644 index 00000000..cb361fac --- /dev/null +++ b/crates/oxide_builtins/src/array/from.rs @@ -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(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(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,构造结果对象,逐索引取值。 + 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(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); + } + } +} diff --git a/crates/oxide_builtins/src/array/mod.rs b/crates/oxide_builtins/src/array/mod.rs index 296b52b1..a958dbd9 100644 --- a/crates/oxide_builtins/src/array/mod.rs +++ b/crates/oxide_builtins/src/array/mod.rs @@ -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(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(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,构造结果对象,逐索引取值。 - 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(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)。 -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。 -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); - } - } -} +mod from; +pub use from::*; /// `Array.prototype.push(...items)`:追加元素到尾部,返回新长度。 pub fn array_push(vm: &mut H, args: &[u8]) -> NativeResult {