mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 10:41:11 +08:00
fix: 字符串迭代器函数直构并统一字节数组转换
This commit is contained in:
@@ -1190,18 +1190,6 @@ pub fn make_iterator_for_value_without_return<H: VmHost>(vm: &mut H, value: JsVa
|
||||
}
|
||||
}
|
||||
|
||||
/// 同 [`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),
|
||||
}
|
||||
}
|
||||
|
||||
/// 尝试创建迭代器包装对象,把"不可迭代"与"真异常"区分返回。
|
||||
///
|
||||
/// # 步骤
|
||||
@@ -1232,8 +1220,27 @@ pub(crate) fn try_make_iterator_inner_proto<H: VmHost>(
|
||||
Ok(None) => return Ok(None),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
// 通用包装器默认挂 %IteratorPrototype%(经原型链获得 @@iterator 返回自身);
|
||||
// 调用方指定原型时优先(如 String 迭代器的 %StringIteratorPrototype%)。
|
||||
Ok(Some(build_iterator_wrapper(vm, inner, cached_next, bind_return, wrapper_proto)))
|
||||
}
|
||||
|
||||
/// 以给定内层迭代器构造统一迭代器包装对象。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 选定包装器原型:调用方指定时优先(如 String 迭代器的
|
||||
/// %StringIteratorPrototype%),否则默认 %IteratorPrototype%(经原型链获得
|
||||
/// `@@iterator` 返回自身)。
|
||||
/// 2. 写入 `__inner__`/`__index__` 槽;鸭子回退路径把创建时读到的 `next` 闭包
|
||||
/// 缓存进 `__next__` 槽,消费期直读槽不重触发 getter。
|
||||
/// 3. 挂 `next` 方法;`bind_return` 且内层有可调用 `return` 时条件暴露 `return`。
|
||||
///
|
||||
/// # 边界
|
||||
/// - `bind_return` 控制是否暴露 `return` 方法(for-of/解构的 IteratorClose 需要,
|
||||
/// `yield*` 委托不需要且须避免创建时访问内层 return getter)。
|
||||
/// - 本函数不解析 `@@iterator`:`get_iterator` 派生内层,或默认迭代器函数直接以
|
||||
/// 已 ToString 的串为内层构造(其语义只由 `this` 决定,与属性表状态解耦)。
|
||||
pub(crate) fn build_iterator_wrapper<H: VmHost>(
|
||||
vm: &mut H, inner: JsValue, cached_next: Option<JsValue>, bind_return: bool, wrapper_proto: Option<*mut JsObject>,
|
||||
) -> JsValue {
|
||||
let iterator_proto = match wrapper_proto {
|
||||
Some(proto) => proto,
|
||||
None => vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject,
|
||||
@@ -1271,7 +1278,7 @@ pub(crate) fn try_make_iterator_inner_proto<H: VmHost>(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(JsValue::from_js_object(wrapper)))
|
||||
JsValue::from_js_object(wrapper)
|
||||
}
|
||||
|
||||
fn iterator_wrapper_return<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
@@ -849,13 +849,11 @@ pub fn string_symbol_iterator<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string"));
|
||||
}
|
||||
};
|
||||
// String 迭代器挂 %StringIteratorPrototype%(链到 %IteratorPrototype%):
|
||||
// 包装机制复用通用路径,仅替换原型为中继层。
|
||||
// 直构 String 迭代器:内层即 ToString 结果,不再回读 @@iterator。本函数语义
|
||||
// 只由 this 决定,属性表被删除/置 null/被覆盖均不影响已保存引用的直调。
|
||||
// 迭代器挂 %StringIteratorPrototype%(链到 %IteratorPrototype%);串内层不暴露 return。
|
||||
let string_iter_proto = vm.session().builtin_world().string_iterator_proto.as_ptr() as *mut JsObject;
|
||||
match crate::iterator::make_iterator_for_value_with_proto(vm, s_val, string_iter_proto) {
|
||||
Ok(iterator) => NativeResult::Ok(iterator),
|
||||
Err(err) => NativeResult::Err(err),
|
||||
}
|
||||
NativeResult::Ok(crate::iterator::build_iterator_wrapper(vm, s_val, None, true, Some(string_iter_proto)))
|
||||
}
|
||||
|
||||
/// `matchAll` 迭代器的 `next`:返回 `{value: 匹配数组, done}`,耗尽后 done 为 true。
|
||||
|
||||
@@ -457,6 +457,16 @@ fn collect_array_like<H: VmHost>(vm: &mut H, value: JsValue, consult_iterator: b
|
||||
return Ok(values);
|
||||
}
|
||||
|
||||
// 字符串源(装箱串)的 length 与索引未物化,直接按 UTF-16 单元取值:
|
||||
// 与 %TypedArray%.from 的 array-like 臂同读法,避免落通用 length 读空。
|
||||
if let Some(units) = crate::array::string_arraylike_units(vm, value) {
|
||||
let mut values = Vec::with_capacity(units.len());
|
||||
for &unit in &units {
|
||||
values.push(crate::array::unit_string_value(vm, unit));
|
||||
}
|
||||
return Ok(values);
|
||||
}
|
||||
|
||||
let length_si = vm.kernel_core().perm_interner().intern("length").0;
|
||||
let len_val = vm
|
||||
.ordinary_get(obj, length_si, value)
|
||||
|
||||
@@ -296,3 +296,95 @@ fn default_string_iterator_regression() {
|
||||
})()"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_default_iterator_still_works_after_delete_or_null() {
|
||||
// 已保存的默认迭代器引用直调:语义只由 this 决定,属性表删除/置 null/置
|
||||
// undefined 不影响,仍按码元产出(不重新回读 @@iterator)。
|
||||
assert_truthy(
|
||||
"saved iterator delete",
|
||||
r#"(function(){
|
||||
var f = String.prototype[Symbol.iterator];
|
||||
delete String.prototype[Symbol.iterator];
|
||||
var it = f.call("ab");
|
||||
return it.next().value === "a" && it.next().value === "b";
|
||||
})()"#,
|
||||
);
|
||||
assert_truthy(
|
||||
"saved iterator null",
|
||||
r#"(function(){
|
||||
var f = String.prototype[Symbol.iterator];
|
||||
String.prototype[Symbol.iterator] = null;
|
||||
var it = f.call("ab");
|
||||
return it.next().value === "a" && it.next().value === "b";
|
||||
})()"#,
|
||||
);
|
||||
assert_truthy(
|
||||
"saved iterator undefined",
|
||||
r#"(function(){
|
||||
var f = String.prototype[Symbol.iterator];
|
||||
String.prototype[Symbol.iterator] = undefined;
|
||||
var it = f.call("ab");
|
||||
return it.next().value === "a" && it.next().value === "b";
|
||||
})()"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_default_iterator_ignores_user_override() {
|
||||
// 用户把 @@iterator 覆盖成函数后,已保存的默认迭代器引用直调仍构造 String
|
||||
// 迭代器(规范忽略覆盖),不转发到覆盖函数。
|
||||
assert_truthy(
|
||||
"saved iterator override",
|
||||
r#"(function(){
|
||||
var f = String.prototype[Symbol.iterator];
|
||||
var called = false;
|
||||
String.prototype[Symbol.iterator] = function(){ called = true; return null; };
|
||||
var it = f.call("ab");
|
||||
return !called && it.next().value === "a" && it.next().value === "b";
|
||||
})()"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_default_iterator_handles_surrogate_pair_codepoint() {
|
||||
// 直构仍走码点口径:合法代理对整体产出单个元素。
|
||||
assert_truthy(
|
||||
"saved iterator gclef",
|
||||
r#"(function(){
|
||||
var f = String.prototype[Symbol.iterator];
|
||||
delete String.prototype[Symbol.iterator];
|
||||
var it = f.call("\uD834\uDD1E");
|
||||
var first = it.next().value;
|
||||
return first === "\uD834\uDD1E" && it.next().done === true;
|
||||
})()"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_array_constructor_boxed_string_uses_units_after_delete() {
|
||||
// 构造器 array-like 臂与 %TypedArray%.from 对称:装箱串按 UTF-16 单元取值,
|
||||
// 不读自身可调用 next,也不因 length 未物化读空。
|
||||
assert_truthy(
|
||||
"new Uint8Array boxed duck-next delete",
|
||||
r#"(function(){
|
||||
var s = new String("ab");
|
||||
var duckCalled = false;
|
||||
s.next = function(){ duckCalled = true; return { value: 99, done: false }; };
|
||||
delete String.prototype[Symbol.iterator];
|
||||
var u = new Uint8Array(s);
|
||||
return u.length === 2 && u[0] === 0 && u[1] === 0 && !duckCalled;
|
||||
})()"#,
|
||||
);
|
||||
assert_truthy(
|
||||
"Uint8Array.from boxed duck-next delete symmetric",
|
||||
r#"(function(){
|
||||
var s = new String("ab");
|
||||
var duckCalled = false;
|
||||
s.next = function(){ duckCalled = true; return { value: 99, done: false }; };
|
||||
delete String.prototype[Symbol.iterator];
|
||||
var u = Uint8Array.from(s);
|
||||
return u.length === 2 && u[0] === 0 && u[1] === 0 && !duckCalled;
|
||||
})()"#,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user