mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
831 lines
37 KiB
Rust
831 lines
37 KiB
Rust
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||
use oxide_types::object::{JsObject, PropAttributes};
|
||
use oxide_types::private_key::{make_int_key, make_well_known_symbol_key};
|
||
use oxide_types::value::JsValue;
|
||
|
||
use oxide_runtime_api::{to_object, NativeResult, VmHost};
|
||
|
||
const INNER_PROP: &str = "__inner__";
|
||
const INDEX_PROP: &str = "__index__";
|
||
const MODE_PROP: &str = "__mode__";
|
||
|
||
/// `Iterator` 构造逻辑:不是构造函数,`new Iterator()` / `Iterator()` 均抛 TypeError;
|
||
/// subclass `super()` 路径放行并返回 undefined(原型由调用方按 newTarget 设置)。
|
||
///
|
||
/// 引擎无 native [[Construct]]/newTarget 通道,用与 `Symbol` 构造器同款的启发式
|
||
/// 判别调用形态:
|
||
///
|
||
/// # 步骤
|
||
/// 1. `this` 非对象 → TypeError(普通调用 `Iterator()`:emit 以 undefined 作 this)。
|
||
/// 2. `this.proto === %IteratorPrototype%` → TypeError(`new Iterator()`:调用方以
|
||
/// ctor.prototype 为原型建新对象传入)。
|
||
/// 3. newTarget 槽(regs[255])非对象 → TypeError(`Iterator.call(x)` 顶层调用,
|
||
/// 顶层 newTarget 初始化为 undefined)。
|
||
/// 4. 否则返回 undefined(subclass `super()`:调用方随后按 new.target.prototype
|
||
/// 设置实例原型)。
|
||
///
|
||
/// # 注意事项
|
||
/// 类构造器内调用 `Iterator.call(x)` 时 regs[255] 为类对象会被放行(规范外罕见
|
||
/// 场景,与 `Symbol` 构造器同款已知边界)。
|
||
pub fn iterator_constructor<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 is not a constructor"));
|
||
}
|
||
let ptr = this_val.as_js_object_ptr();
|
||
if !ptr.is_null() {
|
||
let obj = unsafe { &*ptr };
|
||
let proto = obj.proto();
|
||
if proto.is_object() {
|
||
let proto_ptr = proto.as_js_object_ptr();
|
||
if !proto_ptr.is_null() {
|
||
let home = vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject;
|
||
if std::ptr::eq(proto_ptr, home) {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "Iterator is not a constructor"));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if !vm.reg(255).is_object() {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "Iterator is not a constructor"));
|
||
}
|
||
NativeResult::Ok(JsValue::undefined())
|
||
}
|
||
|
||
/// `%IteratorPrototype%` 上 `constructor` 访问器的 getter:返回当前 global 上的
|
||
/// `Iterator` 构造器。
|
||
///
|
||
/// 动态查 global(shape 槽查找)而非缓存指针:dirty reset 重建 global 时会换新的
|
||
/// `Iterator` 函数对象,缓存旧指针会指向已释放对象。lookup 失败(极端:global 无
|
||
/// `Iterator`)返回 undefined,不 panic。
|
||
pub fn iterator_constructor_getter<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||
let global = vm.session().global_object();
|
||
let si = vm.kernel_core().perm_interner().intern("Iterator").0;
|
||
let Some(pos) = vm.kernel_core().shape_forge().lookup_position(global.shape_id(), si) else {
|
||
return NativeResult::Ok(JsValue::undefined());
|
||
};
|
||
NativeResult::Ok(global.get_prop_at(pos))
|
||
}
|
||
|
||
/// `%IteratorPrototype%` 上 `Symbol.toStringTag` 访问器的 getter:返回 `"Iterator"`。
|
||
pub fn iterator_to_string_tag_getter<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||
let sf = vm.kernel_core().perm_interner().as_ref();
|
||
NativeResult::Ok(JsValue::perm_string(sf.string_ptr(sf.intern("Iterator").0)))
|
||
}
|
||
|
||
/// SetterThatIgnoresPrototypeProperties 的共享实现:`%IteratorPrototype%` 的
|
||
/// `constructor` 与 `Symbol.toStringTag` 访问器共用同一语义,仅属性键不同。
|
||
///
|
||
/// # 步骤
|
||
/// 1. `this` 非对象 → TypeError(原始值直接抛,不建 own 属性)。
|
||
/// 2. `this` 为 `%IteratorPrototype%`(home 对象)→ TypeError(模拟对 home 不可写
|
||
/// 数据属性的严格模式赋值)。
|
||
/// 3. `this` 无 own 指定键属性 → CreateDataPropertyOrThrow(写全可写可枚举)。
|
||
/// 4. 有 own 属性 → 普通 Set(继承访问器时在此被调用的场景)。
|
||
fn iterator_setter_ignore_proto_props<H: VmHost>(vm: &mut H, args: &[u8], key: u32) -> 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 property setter called on non-object"));
|
||
}
|
||
let home = vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject;
|
||
if std::ptr::eq(this_val.as_js_object_ptr(), home) {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Cannot assign to read only property of Iterator prototype",
|
||
));
|
||
}
|
||
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||
let this_obj = unsafe { &mut *this_val.as_js_object_ptr() };
|
||
if vm
|
||
.kernel_core()
|
||
.shape_forge()
|
||
.lookup_position(this_obj.shape_id(), key)
|
||
.is_none()
|
||
{
|
||
match vm.define_data_property(this_obj, key, val, PropAttributes::new(true, true, true)) {
|
||
Ok(()) => NativeResult::Ok(JsValue::undefined()),
|
||
Err(err) => NativeResult::Err(crate::error::create_type_error(vm, &err)),
|
||
}
|
||
} else {
|
||
match vm.ordinary_set(this_obj, key, val, this_val) {
|
||
Ok(()) => NativeResult::Ok(JsValue::undefined()),
|
||
Err(err) => NativeResult::Err(crate::error::create_type_error(vm, &err)),
|
||
}
|
||
}
|
||
}
|
||
|
||
/// `constructor` 访问器的 setter(键 = "constructor")。
|
||
pub fn iterator_constructor_setter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let key = vm.kernel_core().perm_interner().intern("constructor").0;
|
||
iterator_setter_ignore_proto_props(vm, args, key)
|
||
}
|
||
|
||
/// `Symbol.toStringTag` 访问器的 setter(键 = well-known symbol 9)。
|
||
pub fn iterator_to_string_tag_setter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
iterator_setter_ignore_proto_props(vm, args, make_well_known_symbol_key(9))
|
||
}
|
||
|
||
/// `%IteratorPrototype%[@@dispose]`:GetMethod(this, "return"),有则调用并返回
|
||
/// undefined。
|
||
///
|
||
/// # 步骤
|
||
/// 1. `this` 非对象 → TypeError(GetMethod 的 GetV 语义要求对象)。
|
||
/// 2. 读 `return` 方法:不可调用(undefined/null)则跳过;getter 抛错透传原值。
|
||
/// 3. 调用 `return()` 成功/抛错均最终返回 undefined 或透传原异常。
|
||
pub fn iterator_dispose<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,
|
||
"%IteratorPrototype%[@@dispose] called on non-object",
|
||
));
|
||
}
|
||
let obj = unsafe { &*this_val.as_js_object_ptr() };
|
||
let return_si = vm.kernel_core().perm_interner().intern("return").0;
|
||
let return_fn = match vm.ordinary_get(obj, return_si, this_val) {
|
||
Ok(f) if is_callable(f) => f,
|
||
Ok(_) => JsValue::undefined(),
|
||
Err(err) => return NativeResult::Err(engine_error(vm, &err)),
|
||
};
|
||
if !return_fn.is_undefined() {
|
||
if let Err(err) = vm.call_function_sync(return_fn, this_val, &[]) {
|
||
return NativeResult::Err(engine_error(vm, &err));
|
||
}
|
||
}
|
||
NativeResult::Ok(JsValue::undefined())
|
||
}
|
||
|
||
/// `%IteratorPrototype%[@@iterator]`:返回 this(迭代器对象自迭代)。
|
||
///
|
||
/// 挂在 `%IteratorPrototype%` 上,所有集合迭代器经原型链继承,保证
|
||
/// `it[Symbol.iterator]() === it` 恒等成立;this 为任意值(含原始值)时原样返回。
|
||
pub fn iterator_symbol_iterator<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_val = if args.is_empty() { JsValue::undefined() } else { vm.reg(args[0]) };
|
||
NativeResult::Ok(this_val)
|
||
}
|
||
|
||
/// `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> {
|
||
match try_make_iterator_inner(vm, value, true) {
|
||
Ok(Some(iterator)) => Ok(iterator),
|
||
Ok(None) => Err(crate::error::create_type_error(vm, "value is not iterable")),
|
||
Err(err) => Err(err),
|
||
}
|
||
}
|
||
|
||
/// 同 [`make_iterator_for_value`],但包装器不绑定 `return` 方法(`yield*` 委托专用):
|
||
/// 委托转发对内层迭代器延迟 GetMethod,避免创建包装器时访问内层 return getter。
|
||
pub fn make_iterator_for_value_without_return<H: VmHost>(vm: &mut H, value: JsValue) -> Result<JsValue, JsValue> {
|
||
match try_make_iterator_inner(vm, value, false) {
|
||
Ok(Some(iterator)) => Ok(iterator),
|
||
Ok(None) => Err(crate::error::create_type_error(vm, "value is not iterable")),
|
||
Err(err) => Err(err),
|
||
}
|
||
}
|
||
|
||
/// 同 [`make_iterator_for_value`],但包装器原型指向调用方指定的原型
|
||
/// (`String.prototype[@@iterator]` 用 %StringIteratorPrototype%)。
|
||
pub(crate) fn make_iterator_for_value_with_proto<H: VmHost>(
|
||
vm: &mut H, value: JsValue, wrapper_proto: *mut JsObject,
|
||
) -> Result<JsValue, JsValue> {
|
||
match try_make_iterator_inner_proto(vm, value, true, Some(wrapper_proto)) {
|
||
Ok(Some(iterator)) => Ok(iterator),
|
||
Ok(None) => Err(crate::error::create_type_error(vm, "value is not iterable")),
|
||
Err(err) => Err(err),
|
||
}
|
||
}
|
||
|
||
/// 尝试创建迭代器包装对象,把"不可迭代"与"真异常"区分返回。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 经迭代协议取内层迭代器(String/Array/Map/Set 直接作为内层,其余对象调用
|
||
/// `@@iterator` 或回退可调用的 `next`)。
|
||
/// 2. 包装成统一迭代器对象(带 `next` 与可选 `return`),供调用方逐个取元素。
|
||
///
|
||
/// # 返回值
|
||
/// - `Ok(Some(iterator))`:可迭代,返回包装器;
|
||
/// - `Ok(None)`:不可迭代(调用方回退 array-like 路径);
|
||
/// - `Err`:`@@iterator` getter/call 抛错,透传原异常值。
|
||
/// - `bind_return` 控制是否暴露 `return` 方法(for-of/解构的 IteratorClose 需要,
|
||
/// `yield*` 委托不需要且须避免创建时访问内层 return getter)。
|
||
pub(crate) fn try_make_iterator_inner<H: VmHost>(
|
||
vm: &mut H, value: JsValue, bind_return: bool,
|
||
) -> Result<Option<JsValue>, JsValue> {
|
||
try_make_iterator_inner_proto(vm, value, bind_return, None)
|
||
}
|
||
|
||
/// 同 [`try_make_iterator_inner`],但允许调用方指定包装器原型
|
||
/// (String 迭代器挂 %StringIteratorPrototype%,其余默认 %IteratorPrototype%)。
|
||
pub(crate) fn try_make_iterator_inner_proto<H: VmHost>(
|
||
vm: &mut H, value: JsValue, bind_return: bool, wrapper_proto: Option<*mut JsObject>,
|
||
) -> Result<Option<JsValue>, JsValue> {
|
||
let inner = match get_iterator(vm, value) {
|
||
Ok(Some(inner)) => inner,
|
||
Ok(None) => return Ok(None),
|
||
Err(err) => return Err(err),
|
||
};
|
||
// 通用包装器默认挂 %IteratorPrototype%(经原型链获得 @@iterator 返回自身);
|
||
// 调用方指定原型时优先(如 String 迭代器的 %StringIteratorPrototype%)。
|
||
let iterator_proto = match wrapper_proto {
|
||
Some(proto) => proto,
|
||
None => vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject,
|
||
};
|
||
let wrapper = vm
|
||
.epoch()
|
||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(iterator_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);
|
||
|
||
// for-of/解构的 IteratorClose 需要 return 方法:条件暴露(内层有可调用 return 时)。
|
||
// `yield*` 委托(bind_return=false)不绑定,转发时对内层延迟 GetMethod。
|
||
let return_si = vm.kernel_core().perm_interner().intern("return").0;
|
||
if bind_return && inner.is_object() {
|
||
let inner_obj = unsafe { &*inner.as_js_object_ptr() };
|
||
if let Ok(return_fn) = vm.ordinary_get(inner_obj, return_si, inner) {
|
||
if is_callable(return_fn) {
|
||
let wrapper_return = make_native_function(vm, "return", iterator_wrapper_return::<H> as *const (), 0);
|
||
vm.set_or_create_prop_value(wrapper_obj, return_si, wrapper_return);
|
||
}
|
||
}
|
||
}
|
||
|
||
Ok(Some(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()),
|
||
};
|
||
// 延迟 GetMethod:内层无 return 方法时返回 undefined(IteratorClose 跳过)。
|
||
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()),
|
||
};
|
||
// 转发调用实参(`yield*` 委托的 return(v) 语义),缺省为 undefined。
|
||
let arg = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||
match vm.call_function_sync(return_fn, inner, &[arg]) {
|
||
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) => {
|
||
// GetMethod 的 next getter 抛错:透传原异常,不重新包装成 TypeError。
|
||
return match vm.take_uncaught_value() {
|
||
Some(original) => NativeResult::Err(original),
|
||
None => NativeResult::Err(crate::error::create_type_error(vm, &err)),
|
||
};
|
||
}
|
||
};
|
||
// 转发调用实参(`yield*` 委托的 next(v) 语义),缺省为 undefined。
|
||
let arg = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||
return match vm.call_function_sync(next, inner, &[arg]) {
|
||
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"))
|
||
}
|
||
|
||
/// 判断 value 是否可迭代,只读取 `@@iterator` 方法而不调用它(GetMethod 语义)。
|
||
///
|
||
/// 与 [`get_iterator`] 的判定一致:内建集合(String/Array/TypedArray/Map/Set)恒可迭代;
|
||
/// 其它对象读取 `@@iterator`,可调用即视为可迭代,否则回退到自身可调用的 `next`。
|
||
/// `@@iterator` getter 抛错时透传 `Err`。
|
||
pub(crate) fn peek_iterator_method<H: VmHost>(vm: &mut H, value: JsValue) -> Result<bool, JsValue> {
|
||
if value.is_string()
|
||
|| is_array_value(value)
|
||
|| is_typed_array_value(value)
|
||
|| is_map_value(value)
|
||
|| is_set_value(value)
|
||
{
|
||
return Ok(true);
|
||
}
|
||
if value.is_object() {
|
||
let obj = unsafe { &*value.as_js_object_ptr() };
|
||
let sym_iter_si = make_well_known_symbol_key(0);
|
||
let method = match vm.ordinary_get(obj, sym_iter_si, value) {
|
||
Ok(m) => m,
|
||
Err(err) => {
|
||
// GetMethod 取 @@iterator 时 getter 抛出:透传原值,不落入鸭子回退。
|
||
let exc = vm
|
||
.take_uncaught_value()
|
||
.unwrap_or_else(|| crate::error::create_type_error(vm, &err));
|
||
return Err(exc);
|
||
}
|
||
};
|
||
if is_callable(method) {
|
||
return Ok(true);
|
||
}
|
||
// 鸭子回退:对象自身有可调用 next。
|
||
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(true);
|
||
}
|
||
}
|
||
}
|
||
Ok(false)
|
||
}
|
||
|
||
fn get_iterator<H: VmHost>(vm: &mut H, value: JsValue) -> Result<Option<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(Some(value));
|
||
}
|
||
|
||
// 非字符串 primitive(boolean/number/symbol/bigint):GetIterator 先 ToObject,
|
||
// 再走迭代协议(如 `yield* true` 委托 Boolean.prototype[Symbol.iterator])。
|
||
// null/undefined 的 ToObject 失败按不可迭代处理。
|
||
let obj_value = if value.is_object() {
|
||
value
|
||
} else {
|
||
match to_object(value, vm) {
|
||
Ok(obj) => obj,
|
||
Err(_) => return Ok(None),
|
||
}
|
||
};
|
||
let obj = unsafe { &*obj_value.as_js_object_ptr() };
|
||
// 迭代协议:GetIterator 先取 value[Symbol.iterator] 并调用。
|
||
let sym_iter_si = make_well_known_symbol_key(0);
|
||
let method = match vm.ordinary_get(obj, sym_iter_si, obj_value) {
|
||
Ok(m) => m,
|
||
Err(err) => {
|
||
// GetMethod 取 @@iterator 时 getter 抛出:透传原值,不落入鸭子回退。
|
||
let exc = vm
|
||
.take_uncaught_value()
|
||
.unwrap_or_else(|| crate::error::create_type_error(vm, &err));
|
||
return Err(exc);
|
||
}
|
||
};
|
||
if is_callable(method) {
|
||
let iterator = match vm.call_function_sync(method, obj_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(Some(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, obj_value) {
|
||
if is_callable(next) {
|
||
return Ok(Some(obj_value));
|
||
}
|
||
}
|
||
|
||
Ok(None)
|
||
}
|
||
|
||
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 并透传异常。整数键免 intern。
|
||
let key_si = make_int_key(index as u32);
|
||
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() {
|
||
// index 槽在字符串分支存字节游标(每步推进一个 char 的 UTF-8 宽度)而非
|
||
// 元素序号:该槽只被 next 内部读写、无外部消费者,包装器创建后类型固定
|
||
// 不跨类型复用,故可安全借用语义(字符串=字节偏移)。
|
||
let byteoff = current_index(vm, wrapper, index_si);
|
||
// 源串裸指针借用压缩到单个表达式:ch 是 Copy 的 char,不携带借用,
|
||
// 之后对 VM 状态的可变访问不再与源串借用共存。
|
||
let ch = unsafe { &*inner.as_string_ptr() }.as_str()[byteoff..].chars().next();
|
||
if let Some(ch) = ch {
|
||
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((byteoff + ch.len_utf8()) as i32));
|
||
let value = match vm.single_char(ch) {
|
||
Some(v) => v,
|
||
None => 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,
|
||
}
|
||
}
|
||
|
||
/// 构造迭代器结果对象 `{value, done}`(生成器 next/return 结果复用)。
|
||
pub 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)
|
||
}
|
||
|
||
pub(crate) 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()
|
||
}
|
||
|
||
/// 判断值是否为可调用对象(native 或字节码函数)。
|
||
pub 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()
|
||
}
|
||
|
||
/// 把 `call_function_sync` 返回的 `Err` 文本恢复为原始异常值;
|
||
/// 无保留的 uncaught 值时回退为普通 TypeError。
|
||
pub(crate) fn engine_error<H: VmHost>(vm: &mut H, err: &str) -> JsValue {
|
||
vm.take_uncaught_value()
|
||
.unwrap_or_else(|| crate::error::create_type_error(vm, err))
|
||
}
|
||
|
||
/// IteratorClose:异常退出时调用迭代器包装器的 `return()`(转发给内层迭代器),
|
||
/// 丢弃 return 自身抛出的错误,保留在途异常。
|
||
pub(crate) 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 is_callable(ret) {
|
||
// return() 的抛错被忽略,其值不得外泄进槽覆盖在途异常。
|
||
let saved_uncaught = vm.take_uncaught_value();
|
||
let _ = vm.call_function_sync(ret, iterator, &[]);
|
||
vm.restore_uncaught_value(saved_uncaught);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 遍历可迭代值,把每个元素交给 `on_elem`。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 经迭代协议取迭代器包装器,逐次 `next` 读取 `{done, value}`。
|
||
/// 2. 每个元素调用 `on_elem`;元素读取或回调抛错时先 IteratorClose 再透传原异常。
|
||
///
|
||
/// # 返回值
|
||
/// - `Ok(())`:迭代完成;
|
||
/// - `Err`:迭代或回调抛出的原异常值(任意类型)。
|
||
pub fn iterate_elements<H: VmHost, F>(vm: &mut H, iterable: JsValue, mut on_elem: F) -> Result<(), JsValue>
|
||
where
|
||
F: FnMut(&mut H, JsValue) -> Result<(), JsValue>,
|
||
{
|
||
let iterator = make_iterator_for_value(vm, iterable)?;
|
||
iterate_iterator(vm, iterator, &mut on_elem)
|
||
}
|
||
|
||
/// 遍历一个已取得的迭代器对象(带 `next`),把每个元素交给 `on_elem`。
|
||
/// 与 [`iterate_elements`] 的差异:入参是迭代器本身而非可迭代值,
|
||
/// 用于 Set 方法从 SetRecord 的 `keys` 方法返回值继续取元素。
|
||
///
|
||
/// # 副作用
|
||
/// 迭代或回调抛错时调用迭代器的 `return()`(IteratorClose)后透传原异常。
|
||
pub fn iterate_iterator<H: VmHost, F>(vm: &mut H, iterator: JsValue, mut on_elem: F) -> Result<(), JsValue>
|
||
where
|
||
F: FnMut(&mut H, JsValue) -> Result<(), JsValue>,
|
||
{
|
||
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 run: Result<(), JsValue> = (|| {
|
||
loop {
|
||
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
|
||
let next_fn = vm.ordinary_get(iter_obj, next_si, iterator).map_err(|e| engine_error(vm, &e))?;
|
||
let result = vm
|
||
.call_function_sync(next_fn, iterator, &[])
|
||
.map_err(|e| engine_error(vm, &e))?;
|
||
if !result.is_object() {
|
||
return Err(crate::error::create_type_error(vm, "iterator result is not an object"));
|
||
}
|
||
let result_obj = unsafe { &*result.as_js_object_ptr() };
|
||
let done = vm.ordinary_get(result_obj, done_si, result).map_err(|e| engine_error(vm, &e))?;
|
||
if oxide_runtime_api::to_boolean(done) {
|
||
break;
|
||
}
|
||
let elem = vm
|
||
.ordinary_get(result_obj, value_si, result)
|
||
.map_err(|e| engine_error(vm, &e))?;
|
||
on_elem(vm, elem)?;
|
||
}
|
||
Ok(())
|
||
})();
|
||
if run.is_err() {
|
||
close_iterator(vm, iterator);
|
||
}
|
||
run
|
||
}
|
||
|
||
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)
|
||
}
|
||
|
||
/// Map/Set 迭代器模式编码:创建时存 wrapper `__mode__` 槽,%MapIteratorPrototype%/
|
||
/// %SetIteratorPrototype% 的单一 next 读槽后按模式推进(值含家族区分,
|
||
/// 分发不依赖具体原型)。判别值稳定,改动须同步 `from_i32`。
|
||
#[derive(Clone, Copy)]
|
||
#[repr(i32)]
|
||
pub(crate) enum MapSetMode {
|
||
MapEntries = 0,
|
||
MapValues = 1,
|
||
MapKeys = 2,
|
||
SetValues = 3,
|
||
SetEntries = 4,
|
||
}
|
||
|
||
impl MapSetMode {
|
||
fn from_i32(value: i32) -> Self {
|
||
match value {
|
||
0 => Self::MapEntries,
|
||
1 => Self::MapValues,
|
||
2 => Self::MapKeys,
|
||
3 => Self::SetValues,
|
||
4 => Self::SetEntries,
|
||
_ => Self::MapEntries,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 把 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 => {
|
||
// 迭代器已耗尽:把下标推进到永不匹配的哨兵值,使后续 next() 恒返回 done,
|
||
// 即使集合之后又新增元素也不会"复活"。
|
||
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(i32::MAX));
|
||
make_iter_result(vm, JsValue::undefined(), true)
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Map/Set 迭代器 `next`:模式从 wrapper `__mode__` 槽读取后按模式推进。
|
||
///
|
||
/// %MapIteratorPrototype%/%SetIteratorPrototype% 绑定同一实现——家族与模式
|
||
/// 在创建时编码进 `__mode__` 槽,`next` 挂在原型上(wrapper 不设 own next)。
|
||
pub fn map_set_iterator_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 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 mode_si = vm.kernel_core().perm_interner().intern(MODE_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")),
|
||
};
|
||
let mode = vm
|
||
.ordinary_get(wrapper, mode_si, this_val)
|
||
.ok()
|
||
.and_then(|v| if v.is_int() { Some(MapSetMode::from_i32(v.as_int())) } else { None })
|
||
.unwrap_or(MapSetMode::MapEntries);
|
||
NativeResult::Ok(map_set_step(vm, wrapper, inner, index_si, mode))
|
||
}
|
||
|
||
/// 构造 Map/Set 迭代器包装器:`__inner__`/`__index__`/`__mode__` 三槽记录状态。
|
||
///
|
||
/// `next` 不挂实例 own——由 %MapIteratorPrototype%/%SetIteratorPrototype% 上的
|
||
/// 单一 next(读 `__mode__` 分发)经原型链提供,符合规范原型形状。
|
||
pub(crate) fn make_collection_iterator<H: VmHost>(
|
||
vm: &mut H, inner: JsValue, proto_val: JsValue, mode: MapSetMode,
|
||
) -> JsValue {
|
||
let proto_ptr = if proto_val.is_object() {
|
||
proto_val.as_js_object_ptr()
|
||
} else {
|
||
std::ptr::null_mut()
|
||
};
|
||
let wrapper = vm
|
||
.epoch()
|
||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_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 mode_si = vm.kernel_core().perm_interner().intern(MODE_PROP).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));
|
||
vm.set_or_create_prop_value(wrapper_obj, mode_si, JsValue::int(mode as i32));
|
||
JsValue::from_js_object(wrapper)
|
||
}
|