mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
433 lines
18 KiB
Rust
433 lines
18 KiB
Rust
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||
use oxide_types::object::JsObject;
|
||
use oxide_types::value::JsValue;
|
||
|
||
use oxide_runtime_api::{NativeResult, VmHost};
|
||
|
||
const INNER_PROP: &str = "__inner__";
|
||
const INDEX_PROP: &str = "__index__";
|
||
|
||
/// 占位构造函数:`Iterator` 不是构造函数,任何调用都抛 TypeError。
|
||
pub fn iterator_constructor<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||
NativeResult::Err(crate::error::create_type_error(vm, "Iterator is not a constructor"))
|
||
}
|
||
|
||
/// `Iterator.from(iterable)`:为任意可迭代值包装一个迭代器对象。
|
||
/// 包装器带 `next` 与 `return`(用于 for-of 提前退出时的 IteratorClose 清理)。
|
||
pub fn iterator_from<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let iterable = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||
match make_iterator_for_value(vm, iterable) {
|
||
Ok(iterator) => NativeResult::Ok(iterator),
|
||
Err(err) => NativeResult::Err(err),
|
||
}
|
||
}
|
||
|
||
/// 为任意值创建统一迭代器包装对象:String/Array/Map/Set 直接支持索引遍历,
|
||
/// 其它对象则要求提供可调用的 `next`。不可迭代时返回 TypeError。
|
||
pub fn make_iterator_for_value<H: VmHost>(vm: &mut H, value: JsValue) -> Result<JsValue, JsValue> {
|
||
let inner = get_iterator(vm, value)?;
|
||
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||
let wrapper = vm
|
||
.epoch()
|
||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
|
||
|
||
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 next_si = vm.kernel_core().perm_interner().intern("next").0;
|
||
let wrapper_obj = unsafe { &mut *wrapper };
|
||
vm.set_or_create_prop_value(wrapper_obj, inner_si, inner);
|
||
vm.set_or_create_prop_value(wrapper_obj, index_si, JsValue::int(0));
|
||
|
||
let next_fn = make_native_function(vm, "next", iterator_wrapper_next::<H> as *const (), 0);
|
||
vm.set_or_create_prop_value(wrapper_obj, next_si, next_fn);
|
||
|
||
// 把 IteratorClose 转发给内层迭代器,使 for-of 异常退出时可清理。
|
||
let return_si = vm.kernel_core().perm_interner().intern("return").0;
|
||
let return_fn = make_native_function(vm, "return", iterator_wrapper_return::<H> as *const (), 0);
|
||
vm.set_or_create_prop_value(wrapper_obj, return_si, return_fn);
|
||
|
||
Ok(JsValue::from_js_object(wrapper))
|
||
}
|
||
|
||
fn iterator_wrapper_return<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||
if !this_val.is_object() {
|
||
return NativeResult::Ok(JsValue::undefined());
|
||
}
|
||
let wrapper = unsafe { &*this_val.as_js_object_ptr() };
|
||
let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0;
|
||
let inner = match vm.ordinary_get(wrapper, inner_si, this_val) {
|
||
Ok(inner) if inner.is_object() => inner,
|
||
_ => return NativeResult::Ok(JsValue::undefined()),
|
||
};
|
||
let inner_obj = unsafe { &*inner.as_js_object_ptr() };
|
||
let return_si = vm.kernel_core().perm_interner().intern("return").0;
|
||
let return_fn = match vm.ordinary_get(inner_obj, return_si, inner) {
|
||
Ok(f) if is_callable(f) => f,
|
||
_ => return NativeResult::Ok(JsValue::undefined()),
|
||
};
|
||
match vm.call_function_sync(return_fn, inner, &[]) {
|
||
Ok(result) => NativeResult::Ok(result),
|
||
Err(err) => match vm.take_uncaught_value() {
|
||
Some(original) => NativeResult::Err(original),
|
||
None => NativeResult::Err(crate::error::create_type_error(vm, &err)),
|
||
},
|
||
}
|
||
}
|
||
|
||
/// 迭代器包装器的 `next` 方法:对 Array/String/Map/Set 直接按索引取值,
|
||
/// 其它对象委托其自身 `next`;底层抛出异常时透传原始值(不做二次包装)。
|
||
pub fn iterator_wrapper_next<H: VmHost>(vm: &mut H, args: &[u8]) -> 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 wrapper 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 wrapper has no inner iterator")),
|
||
};
|
||
|
||
match next_array_like(vm, wrapper, inner, index_si) {
|
||
Ok(Some(result)) => return NativeResult::Ok(result),
|
||
Ok(None) => {}
|
||
Err(original) => return NativeResult::Err(original),
|
||
}
|
||
|
||
if inner.is_object() {
|
||
let inner_obj = unsafe { &*inner.as_js_object_ptr() };
|
||
let next_si = vm.kernel_core().perm_interner().intern("next").0;
|
||
let next = match vm.ordinary_get(inner_obj, next_si, inner) {
|
||
Ok(next) => next,
|
||
Err(err) => return NativeResult::Err(crate::error::create_type_error(vm, &err)),
|
||
};
|
||
return match vm.call_function_sync(next, inner, &[]) {
|
||
Ok(result) => NativeResult::Ok(result),
|
||
// 透传原始抛出的值(任意类型)而非重新包装成 TypeError,
|
||
// 使外围 try/catch 能看到真正的错误。
|
||
Err(err) => match vm.take_uncaught_value() {
|
||
Some(original) => NativeResult::Err(original),
|
||
None => NativeResult::Err(crate::error::create_type_error(vm, &err)),
|
||
},
|
||
};
|
||
}
|
||
|
||
NativeResult::Err(crate::error::create_type_error(vm, "value is not iterable"))
|
||
}
|
||
|
||
fn get_iterator<H: VmHost>(vm: &mut H, value: JsValue) -> Result<JsValue, JsValue> {
|
||
if value.is_string()
|
||
|| is_array_value(value)
|
||
|| is_typed_array_value(value)
|
||
|| is_map_value(value)
|
||
|| is_set_value(value)
|
||
{
|
||
return Ok(value);
|
||
}
|
||
|
||
if value.is_object() {
|
||
let obj = unsafe { &*value.as_js_object_ptr() };
|
||
// 迭代协议:GetIterator 先取 value[Symbol.iterator] 并调用。
|
||
let sym_iter_si = vm.kernel_core().perm_interner().intern("@@iterator").0;
|
||
let method = vm.ordinary_get(obj, sym_iter_si, value);
|
||
if let Ok(method) = method {
|
||
if is_callable(method) {
|
||
let iterator = match vm.call_function_sync(method, value, &[]) {
|
||
Ok(it) => it,
|
||
Err(err) => {
|
||
let exc = vm
|
||
.take_uncaught_value()
|
||
.unwrap_or_else(|| crate::error::create_type_error(vm, &err));
|
||
return Err(exc);
|
||
}
|
||
};
|
||
if !iterator.is_object() {
|
||
return Err(crate::error::create_type_error(
|
||
vm,
|
||
"Result of the Symbol.iterator method is not an object",
|
||
));
|
||
}
|
||
return Ok(iterator);
|
||
}
|
||
}
|
||
// 鸭子回退:对象自身有可调用 next(Map/Set 迭代器包装等既有用法)。
|
||
let next_si = vm.kernel_core().perm_interner().intern("next").0;
|
||
if let Ok(next) = vm.ordinary_get(obj, next_si, value) {
|
||
if is_callable(next) {
|
||
return Ok(value);
|
||
}
|
||
}
|
||
}
|
||
|
||
Err(crate::error::create_type_error(vm, "value is not iterable"))
|
||
}
|
||
|
||
fn next_array_like<H: VmHost>(
|
||
vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32,
|
||
) -> Result<Option<JsValue>, JsValue> {
|
||
if is_array_value(inner) {
|
||
let index = current_index(vm, wrapper, index_si);
|
||
let arr = unsafe { &*inner.as_js_object_ptr() };
|
||
if index < arr.prop_count() as usize {
|
||
// 数组元素读取走 GetValue:普通数据属性返回槽值,访问器属性
|
||
// (defineProperty getter)触发 getter 并透传异常。
|
||
let key_si = vm.kernel_core().perm_interner().intern(&index.to_string()).0;
|
||
let value = match vm.ordinary_get(arr, key_si, inner) {
|
||
Ok(v) => v,
|
||
Err(err) => {
|
||
let exc = vm
|
||
.take_uncaught_value()
|
||
.unwrap_or_else(|| crate::error::create_error(vm, &err));
|
||
return Err(exc);
|
||
}
|
||
};
|
||
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32));
|
||
return Ok(Some(make_iter_result(vm, value, false)));
|
||
}
|
||
return Ok(Some(make_iter_result(vm, JsValue::undefined(), true)));
|
||
}
|
||
|
||
if inner.is_string() {
|
||
let index = current_index(vm, wrapper, index_si);
|
||
let source = oxide_runtime_api::to_string(inner);
|
||
let mut chars = source.chars();
|
||
if let Some(ch) = chars.nth(index) {
|
||
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32));
|
||
let value = vm.new_string(&ch.to_string());
|
||
return Ok(Some(make_iter_result(vm, value, false)));
|
||
}
|
||
return Ok(Some(make_iter_result(vm, JsValue::undefined(), true)));
|
||
}
|
||
|
||
if is_typed_array_value(inner) {
|
||
let index = current_index(vm, wrapper, index_si);
|
||
let length_si = vm.kernel_core().perm_interner().intern("length").0;
|
||
let obj = unsafe { &*inner.as_js_object_ptr() };
|
||
let len = vm
|
||
.ordinary_get(obj, length_si, inner)
|
||
.map(|v| if v.is_int() { v.as_int().max(0) as usize } else { 0 })
|
||
.unwrap_or(0);
|
||
if index < len {
|
||
let value = match crate::typed_array::typed_array_element_get(vm, obj, index as u32) {
|
||
Ok(v) => v,
|
||
Err(e) => return Err(crate::error::create_type_error(vm, &e)),
|
||
};
|
||
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32));
|
||
return Ok(Some(make_iter_result(vm, value, false)));
|
||
}
|
||
return Ok(Some(make_iter_result(vm, JsValue::undefined(), true)));
|
||
}
|
||
|
||
// for-of 循环默认迭代:Map 产出 [key, value] 对,Set 产出值。
|
||
if is_map_value(inner) {
|
||
return Ok(Some(map_set_step(vm, wrapper, inner, index_si, MapSetMode::MapEntries)));
|
||
}
|
||
if is_set_value(inner) {
|
||
return Ok(Some(map_set_step(vm, wrapper, inner, index_si, MapSetMode::SetValues)));
|
||
}
|
||
|
||
Ok(None)
|
||
}
|
||
|
||
fn current_index<H: VmHost>(vm: &mut H, wrapper: &JsObject, index_si: u32) -> usize {
|
||
match vm.ordinary_get(wrapper, index_si, JsValue::undefined()) {
|
||
Ok(value) if value.is_int() => value.as_int().max(0) as usize,
|
||
Ok(value) if value.is_double() => value.as_double().max(0.0) as usize,
|
||
_ => 0,
|
||
}
|
||
}
|
||
|
||
fn make_iter_result<H: VmHost>(vm: &mut H, value: JsValue, done: bool) -> JsValue {
|
||
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||
let obj = vm
|
||
.epoch()
|
||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
|
||
let value_si = vm.kernel_core().perm_interner().intern("value").0;
|
||
let done_si = vm.kernel_core().perm_interner().intern("done").0;
|
||
let obj_ref = unsafe { &mut *obj };
|
||
vm.set_or_create_prop_value(obj_ref, value_si, value);
|
||
vm.set_or_create_prop_value(obj_ref, done_si, JsValue::bool(done));
|
||
JsValue::from_js_object(obj)
|
||
}
|
||
|
||
fn make_native_function<H: VmHost>(vm: &mut H, name: &str, native_fn: *const (), arg_count: u8) -> JsValue {
|
||
let function_proto = vm.session().builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto));
|
||
func.set_function(true);
|
||
// SAFETY: native_fn 来自 NativeFn 函数项。
|
||
func.set_native_fn(Some(unsafe { oxide_types::object::NativeFnPtr::from_raw(native_fn) }));
|
||
func.set_native_arg_count(arg_count);
|
||
let func = vm.alloc_object(func);
|
||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||
let value = vm.new_string(name);
|
||
let func_ref = unsafe { &mut *func };
|
||
vm.set_or_create_prop_value(func_ref, name_si, value);
|
||
JsValue::from_js_object(func)
|
||
}
|
||
|
||
fn is_array_value(value: JsValue) -> bool {
|
||
if !value.is_object() {
|
||
return false;
|
||
}
|
||
let ptr = value.as_js_object_ptr();
|
||
!ptr.is_null() && unsafe { &*ptr }.is_array()
|
||
}
|
||
|
||
fn is_typed_array_value(value: JsValue) -> bool {
|
||
if !value.is_object() {
|
||
return false;
|
||
}
|
||
let ptr = value.as_js_object_ptr();
|
||
!ptr.is_null() && unsafe { &*ptr }.is_typed_array_obj()
|
||
}
|
||
|
||
fn is_callable(value: JsValue) -> bool {
|
||
if !value.is_object() {
|
||
return false;
|
||
}
|
||
let ptr = value.as_js_object_ptr();
|
||
!ptr.is_null() && unsafe { &*ptr }.is_function()
|
||
}
|
||
|
||
fn is_map_value(value: JsValue) -> bool {
|
||
if !value.is_object() {
|
||
return false;
|
||
}
|
||
let ptr = value.as_js_object_ptr();
|
||
!ptr.is_null() && unsafe { &*ptr }.is_map()
|
||
}
|
||
|
||
fn is_set_value(value: JsValue) -> bool {
|
||
if !value.is_object() {
|
||
return false;
|
||
}
|
||
let ptr = value.as_js_object_ptr();
|
||
!ptr.is_null() && unsafe { &*ptr }.is_set()
|
||
}
|
||
|
||
fn make_map_set_pair<H: VmHost>(vm: &mut H, a: JsValue, b: JsValue) -> JsValue {
|
||
let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||
let pair = vm.alloc_object(JsObject::new_array(
|
||
EMPTY_SHAPE_ID,
|
||
JsValue::from_js_object(array_proto),
|
||
2,
|
||
vm.epoch().bump(),
|
||
));
|
||
// SAFETY: pair 是当前 epoch 内新分配的数组 JsObject。
|
||
unsafe {
|
||
(*pair).set_prop_at(0, a);
|
||
(*pair).set_prop_at(1, b);
|
||
(*pair).set_prop_count(2);
|
||
}
|
||
JsValue::from_js_object(pair)
|
||
}
|
||
|
||
#[derive(Clone, Copy)]
|
||
enum MapSetMode {
|
||
MapEntries,
|
||
MapValues,
|
||
MapKeys,
|
||
SetEntries,
|
||
SetValues,
|
||
}
|
||
|
||
/// 把 Map/Set 迭代器包装器推进一步,按指定模式产出 `{value, done}` 结果。
|
||
/// 条目存放在集合 native-data 槽的 indexmap 中;(a, b) 对在任意分配之前拷出,
|
||
/// 使对 native 集合的借用不会跨越 `vm` 调用保持。
|
||
fn map_set_step<H: VmHost>(
|
||
vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, mode: MapSetMode,
|
||
) -> JsValue {
|
||
let index = current_index(vm, wrapper, index_si);
|
||
let is_map = matches!(mode, MapSetMode::MapEntries | MapSetMode::MapValues | MapSetMode::MapKeys);
|
||
let entry: Option<(JsValue, JsValue)> = unsafe {
|
||
let obj_ptr = inner.as_js_object_ptr();
|
||
if obj_ptr.is_null() {
|
||
None
|
||
} else if is_map {
|
||
let p = (*obj_ptr).native_data() as *const crate::map::MapInner;
|
||
if p.is_null() {
|
||
None
|
||
} else {
|
||
(*p).get_index(index).map(|(key, value)| (key.0, *value))
|
||
}
|
||
} else {
|
||
let p = (*obj_ptr).native_data() as *const crate::set::SetInner;
|
||
if p.is_null() {
|
||
None
|
||
} else {
|
||
(*p).get_index(index).map(|elem| (elem.0, elem.0))
|
||
}
|
||
}
|
||
};
|
||
|
||
match entry {
|
||
Some((a, b)) => {
|
||
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32));
|
||
let value = match mode {
|
||
MapSetMode::MapEntries | MapSetMode::SetEntries => make_map_set_pair(vm, a, b),
|
||
MapSetMode::MapValues => b,
|
||
MapSetMode::MapKeys | MapSetMode::SetValues => a,
|
||
};
|
||
make_iter_result(vm, value, false)
|
||
}
|
||
None => make_iter_result(vm, JsValue::undefined(), true),
|
||
}
|
||
}
|
||
|
||
fn map_set_next_dispatch<H: VmHost>(vm: &mut H, args: &[u8], mode: MapSetMode) -> 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 collection")),
|
||
};
|
||
NativeResult::Ok(map_set_step(vm, wrapper, inner, index_si, mode))
|
||
}
|
||
|
||
pub(crate) fn map_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
map_set_next_dispatch::<H>(vm, args, MapSetMode::MapEntries)
|
||
}
|
||
|
||
pub(crate) fn map_values_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
map_set_next_dispatch::<H>(vm, args, MapSetMode::MapValues)
|
||
}
|
||
|
||
pub(crate) fn map_keys_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
map_set_next_dispatch::<H>(vm, args, MapSetMode::MapKeys)
|
||
}
|
||
|
||
pub(crate) fn set_values_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
map_set_next_dispatch::<H>(vm, args, MapSetMode::SetValues)
|
||
}
|
||
|
||
pub(crate) fn set_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
map_set_next_dispatch::<H>(vm, args, MapSetMode::SetEntries)
|
||
}
|
||
|
||
/// 构造迭代器包装器,其 `next` 委托给调用方指定的按模式分发的 native 函数。
|
||
/// 与 `make_iterator_for_value` 一致,但允许 Map/Set 原型方法选择
|
||
/// values/keys/entries 变体。
|
||
pub(crate) fn make_mode_iterator<H: VmHost>(vm: &mut H, inner: JsValue, next_fn: *const ()) -> JsValue {
|
||
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||
let wrapper = vm
|
||
.epoch()
|
||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
|
||
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 next_si = vm.kernel_core().perm_interner().intern("next").0;
|
||
let wrapper_obj = unsafe { &mut *wrapper };
|
||
vm.set_or_create_prop_value(wrapper_obj, inner_si, inner);
|
||
vm.set_or_create_prop_value(wrapper_obj, index_si, JsValue::int(0));
|
||
let next = make_native_function(vm, "next", next_fn, 0);
|
||
vm.set_or_create_prop_value(wrapper_obj, next_si, next);
|
||
JsValue::from_js_object(wrapper)
|
||
}
|