mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
3043 lines
120 KiB
Rust
3043 lines
120 KiB
Rust
use std::sync::Arc;
|
||
|
||
use oxide_compiler::compiler::Compiler;
|
||
use oxide_types::object::JsObject;
|
||
use oxide_types::value::JsValue;
|
||
use oxide_vm::vm::Vm;
|
||
|
||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||
let allocator = oxide_parser::Allocator::default();
|
||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||
vm.run(&Arc::new(module))
|
||
}
|
||
|
||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||
vm.lookup_str(val).unwrap_or_default()
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_global_exists() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "typeof Iterator").unwrap();
|
||
assert_eq!(to_str(&vm, result), "function");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_from_array_returns_values_until_done() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"var iter = Iterator.from([1, 2, 3]); \
|
||
var a = iter.next(); var b = iter.next(); var c = iter.next(); var d = iter.next(); \
|
||
a.value === 1 && a.done === false && b.value === 2 && c.value === 3 && d.done === true",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_from_string_returns_chars() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "var iter = Iterator.from('abc'); iter.next().value").unwrap();
|
||
assert_eq!(to_str(&vm, result), "a");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_from_iterator_forwards_next() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"var inner = { next: function() { return { value: 7, done: false }; } }; \
|
||
Iterator.from(inner).next().value",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(result.as_int(), 7);
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_from_duck_fallback_next_getter_read_only_once() {
|
||
// 鸭子回退的 from() 恰读一次 next getter(规范单读语义):
|
||
// 包装器缓存 from() 时读到的闭包,消费期直用缓存闭包——getter 不被重触发,
|
||
// 底层新产的值经同一闭包透出。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"var nextGets = 0; \
|
||
var nextCalls = 0; \
|
||
var obj = { \
|
||
get next() { \
|
||
++nextGets; \
|
||
var i = 0; \
|
||
return function () { \
|
||
++nextCalls; \
|
||
return i < 4 ? { value: i++, done: false } : { value: undefined, done: true }; \
|
||
}; \
|
||
}, \
|
||
}; \
|
||
var it = Iterator.from(obj); \
|
||
nextGets === 1 && nextCalls === 0",
|
||
true,
|
||
),
|
||
(
|
||
"var nextGets = 0; \
|
||
var nextCalls = 0; \
|
||
var obj = { \
|
||
get next() { \
|
||
++nextGets; \
|
||
var i = 0; \
|
||
return function () { \
|
||
++nextCalls; \
|
||
return i < 4 ? { value: i++, done: false } : { value: undefined, done: true }; \
|
||
}; \
|
||
}, \
|
||
}; \
|
||
var it = Iterator.from(obj); \
|
||
var vals = []; \
|
||
for (var k = 0; k < 5; ++k) { var r = it.next(); if (!r.done) { vals.push(r.value); } } \
|
||
nextGets === 1 && nextCalls === 5 && vals.join(',') === '0,1,2,3'",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_from_throwing_next_getter_propagates() {
|
||
// next getter 抛错经 from() 透传原异常,不降级为 "not iterable" TypeError。
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"var ok = false; \
|
||
try { Iterator.from({ get next() { throw new RangeError('read'); } }); } \
|
||
catch (e) { ok = e instanceof RangeError; } \
|
||
ok",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn new_iterator_throws_type_error() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "try { new Iterator() } catch (e) { e instanceof TypeError }").unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_from_string_full_sequence() {
|
||
// 手动 next 逐字符取完全部 ASCII 字符后 done(游标推进路径)。
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"var it = Iterator.from('ab'); \
|
||
var r = []; var x; while (!(x = it.next()).done) r.push(x.value); \
|
||
r.join('')",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, result), "ab");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_from_string_astral_scalar() {
|
||
// 字符串迭代按 Unicode 标量推进:astral 字符整体产出(标量语义未动)。
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "var it = Iterator.from('\\u{1F600}'); it.next().value").unwrap();
|
||
assert_eq!(to_str(&vm, result), "\u{1F600}");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_from_string_empty_done() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "Iterator.from('').next().done").unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn for_of_string_chars() {
|
||
// for-of 字符串循环产出逐字符(走字节游标 + 单字符缓存)。
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "var out = []; for (const c of 'abc') out.push(c); out.join('')").unwrap();
|
||
assert_eq!(to_str(&vm, result), "abc");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_proto_chain_and_self_iteration() {
|
||
let mut vm = Vm::new();
|
||
let cases = [
|
||
// 各家族迭代器自迭代恒等(%IteratorPrototype% 的 @@iterator 返回 this)。
|
||
("var it = [1,2].values(); it[Symbol.iterator]() === it", true),
|
||
("var it = new Map([['a',1]]).values(); it[Symbol.iterator]() === it", true),
|
||
("var it = new Set([1]).values(); it[Symbol.iterator]() === it", true),
|
||
("var it = new Uint8Array([1]).values(); it[Symbol.iterator]() === it", true),
|
||
("var it = 'abc'[Symbol.iterator](); it[Symbol.iterator]() === it", true),
|
||
("var it = 'ab'.matchAll(/a/g); it[Symbol.iterator]() === it", true),
|
||
// Array/TA 共享 %ArrayIteratorPrototype%;Map/Set 各自原型内部共享。
|
||
("Object.getPrototypeOf([].values()) === Object.getPrototypeOf([].entries())", true),
|
||
(
|
||
"Object.getPrototypeOf(new Uint8Array(0).values()) === Object.getPrototypeOf([].values())",
|
||
true,
|
||
),
|
||
(
|
||
"Object.getPrototypeOf(new Map().values()) === Object.getPrototypeOf(new Map().keys())",
|
||
true,
|
||
),
|
||
(
|
||
"Object.getPrototypeOf(new Set().values()) === Object.getPrototypeOf(new Set().entries())",
|
||
true,
|
||
),
|
||
// %IteratorPrototype% 自身可迭代:其 @@iterator 返回 this。
|
||
(
|
||
"var P = Object.getPrototypeOf(Object.getPrototypeOf([].values())); P[Symbol.iterator]() === P",
|
||
true,
|
||
),
|
||
// 原型链:%ArrayIteratorPrototype% → %IteratorPrototype% → Object.prototype。
|
||
(
|
||
"Object.getPrototypeOf(Object.getPrototypeOf([].values())) === \
|
||
Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf([].values())))",
|
||
false,
|
||
),
|
||
(
|
||
"Object.getPrototypeOf(Object.getPrototypeOf([].values()))[Symbol.iterator] === \
|
||
Object.getPrototypeOf(new Map().values())[Symbol.iterator]",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn array_string_symbol_iterator_bound() {
|
||
let mut vm = Vm::new();
|
||
// G4 别名:Array/String 的 @@iterator 属性存在且可迭代。
|
||
let bool_cases = [
|
||
("[][Symbol.iterator] === [].values", true),
|
||
("var out = []; for (const c of 'a\\u{1F600}b') out.push(c); out.length === 3", true),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
let str_cases = [
|
||
("typeof 'abc'[Symbol.iterator]", "function"),
|
||
("[...'ab'].join('')", "ab"),
|
||
("Array.from('ab').join('')", "ab"),
|
||
("[...[1,2,3]].join(',')", "1,2,3"),
|
||
("Array.from([1,2]).join(',')", "1,2"),
|
||
];
|
||
for (src, expected) in str_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(to_str(&vm, result), expected, "for {}", src);
|
||
}
|
||
// String 迭代器语义:null/undefined 抛 TypeError,对象走 ToString。
|
||
let err = eval(&mut vm, "try { String.prototype[Symbol.iterator].call(null) } catch (e) { e.name }").unwrap();
|
||
assert_eq!(to_str(&vm, err), "TypeError");
|
||
let result = eval(&mut vm, "[...String.prototype[Symbol.iterator].call({toString: () => 'xy'})].join('')").unwrap();
|
||
assert_eq!(to_str(&vm, result), "xy");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_proto_object_proto_parent() {
|
||
// 链:values() → %ArrayIteratorPrototype% → %IteratorPrototype% → Object.prototype。
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf([].values()))) === Object.prototype",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_function_prototype_property() {
|
||
// Iterator.prototype 属性绑定:=== %IteratorPrototype%([].values() 原型链中继),
|
||
// 且 %IteratorPrototype% 自身可迭代。
|
||
let mut vm = Vm::new();
|
||
let cases = [
|
||
("Iterator.prototype !== undefined", true),
|
||
("Object.getPrototypeOf(Object.getPrototypeOf([].values())) === Iterator.prototype", true),
|
||
(
|
||
"Object.getPrototypeOf(Object.getPrototypeOf(new Map().values())) === Iterator.prototype",
|
||
true,
|
||
),
|
||
("Iterator.prototype[Symbol.iterator]() === Iterator.prototype", true),
|
||
];
|
||
for (src, expected) in cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn string_iterator_proto_layer() {
|
||
// String 迭代器挂 %StringIteratorPrototype% 中继层:与 Array 迭代器原型不同,
|
||
// 且经链到 %IteratorPrototype%(Iterator.prototype)。
|
||
let mut vm = Vm::new();
|
||
let cases = [
|
||
(
|
||
"Object.getPrototypeOf(Object.getPrototypeOf('a'[Symbol.iterator]())) === Iterator.prototype",
|
||
true,
|
||
),
|
||
(
|
||
"Object.getPrototypeOf('a'[Symbol.iterator]()) !== Object.getPrototypeOf([].values())",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
let out = eval(&mut vm, "[...'abc'].join('')").unwrap();
|
||
assert_eq!(to_str(&vm, out), "abc");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_next_on_prototypes() {
|
||
// next 挂集合迭代器原型(%ArrayIteratorPrototype%.next 等),wrapper 无 own next。
|
||
let mut vm = Vm::new();
|
||
let cases = [
|
||
("typeof Object.getPrototypeOf([].values()).next === 'function'", true),
|
||
("Object.getPrototypeOf([].values()).hasOwnProperty('next')", true),
|
||
("[].values().hasOwnProperty('next') === false", true),
|
||
("typeof Object.getPrototypeOf(new Map().values()).next === 'function'", true),
|
||
("new Map().values().hasOwnProperty('next') === false", true),
|
||
("typeof Object.getPrototypeOf(new Set().values()).next === 'function'", true),
|
||
("new Set().values().hasOwnProperty('next') === false", true),
|
||
("typeof Object.getPrototypeOf(new Uint8Array([1]).values()).next === 'function'", true),
|
||
("new Uint8Array([1]).values().hasOwnProperty('next') === false", true),
|
||
("typeof Object.getPrototypeOf('ab'.matchAll(/a/g)).next === 'function'", true),
|
||
("'ab'.matchAll(/a/g).hasOwnProperty('next') === false", true),
|
||
];
|
||
for (src, expected) in cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_proto_next_consumption_unchanged() {
|
||
// 行为回归:next 移上原型后各家族消费路径不变(迭代协议经原型链解析)。
|
||
let mut vm = Vm::new();
|
||
let str_cases = [
|
||
("[...[1,2,3]].join(',')", "1,2,3"),
|
||
("var it = [1,2].values(); it.next().value + ',' + it.next().value", "1,2"),
|
||
("var it = ['x','y'].entries(); it.next().value.join(':')", "0:x"),
|
||
("[...new Map([['a',1]])][0].join(':')", "a:1"),
|
||
("var mk = new Map([['k',9]]); mk.keys().next().value", "k"),
|
||
("var se = new Set([7]); se.entries().next().value.join(':')", "7:7"),
|
||
("new Uint8Array([9]).entries().next().value.join(',')", "0,9"),
|
||
("[...'ab'].join('')", "ab"),
|
||
("'a\\u{1F600}b'[Symbol.iterator]().next().value", "a"),
|
||
("var mm = 'ab'.matchAll(/b/g); mm.next().value[0]", "b"),
|
||
];
|
||
for (src, expected) in str_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(to_str(&vm, result), expected, "for {}", src);
|
||
}
|
||
// 数值结果(含 int 0)用恒等比较断言,避免格式化路径差异。
|
||
let num_cases = [
|
||
("[10,20].keys().next().value === 0", true),
|
||
("[1,2].values().next().value === 1", true),
|
||
("new Map([['a',1]]).values().next().value === 1", true),
|
||
("new Set([5,6]).keys().next().value === 5", true),
|
||
("new Uint8Array([5]).values().next().value === 5", true),
|
||
("new Uint8Array([7]).entries().next().value[1] === 7", true),
|
||
];
|
||
for (src, expected) in num_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
// TA 迭代耗尽后恒 done(数组路径 target 置 undefined 防复活)。
|
||
let done = eval(&mut vm, "var it = new Uint8Array([1]).values(); it.next(); it.next().done").unwrap();
|
||
assert!(done.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_function_name_length() {
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
("Iterator.name === 'Iterator'", true),
|
||
("Iterator.length === 0", true),
|
||
("Object.getOwnPropertyDescriptor(Iterator, 'name').writable === false", true),
|
||
("Object.getOwnPropertyDescriptor(Iterator, 'name').enumerable === false", true),
|
||
("Object.getOwnPropertyDescriptor(Iterator, 'name').configurable === true", true),
|
||
("Object.getOwnPropertyDescriptor(Iterator, 'length').writable === false", true),
|
||
("Object.getOwnPropertyDescriptor(Iterator, 'length').enumerable === false", true),
|
||
("Object.getOwnPropertyDescriptor(Iterator, 'length').configurable === true", true),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_prototype_constructor_accessor() {
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// getter 动态读 global 上的 Iterator 构造器。
|
||
("Iterator.prototype.constructor === Iterator", true),
|
||
(
|
||
"typeof Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor').get === 'function'",
|
||
true,
|
||
),
|
||
(
|
||
"typeof Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor').set === 'function'",
|
||
true,
|
||
),
|
||
(
|
||
"Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor').enumerable === false",
|
||
true,
|
||
),
|
||
(
|
||
"Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor').configurable === true",
|
||
true,
|
||
),
|
||
// getter 无参调用(this=undefined)仍返回 Iterator。
|
||
(
|
||
"Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor').get.call() === Iterator",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_prototype_constructor_setter_semantics() {
|
||
let mut vm = Vm::new();
|
||
// home 对象赋值与原始值 this 抛 TypeError(SetterThatIgnoresPrototypeProperties)。
|
||
let throws = [
|
||
(
|
||
"try { Iterator.prototype.constructor = 'x'; false } catch (e) { e instanceof TypeError }",
|
||
true,
|
||
),
|
||
(
|
||
"try { var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor'); \
|
||
d.set.call(undefined, 'x'); false } catch (e) { e instanceof TypeError }",
|
||
true,
|
||
),
|
||
(
|
||
"try { var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor'); \
|
||
d.set.call(null, 'x'); false } catch (e) { e instanceof TypeError }",
|
||
true,
|
||
),
|
||
(
|
||
"try { var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor'); \
|
||
d.set.call(true, 'x'); false } catch (e) { e instanceof TypeError }",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in throws {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
// 非 home 对象:无 own 属性建 own,有 own 属性走 Set 覆盖。
|
||
let ok_cases = [
|
||
(
|
||
"var o = {}; var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor'); \
|
||
d.set.call(o, 42); o.constructor === 42 && o.hasOwnProperty('constructor')",
|
||
true,
|
||
),
|
||
(
|
||
"var o = { constructor: 1 }; var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor'); \
|
||
d.set.call(o, 2); o.constructor === 2",
|
||
true,
|
||
),
|
||
// home 不受污染。
|
||
("Iterator.prototype.constructor === Iterator", true),
|
||
];
|
||
for (src, expected) in ok_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_prototype_to_string_tag_accessor() {
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
("Iterator.prototype[Symbol.toStringTag] === 'Iterator'", true),
|
||
(
|
||
"typeof Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag).get === 'function'",
|
||
true,
|
||
),
|
||
(
|
||
"typeof Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag).set === 'function'",
|
||
true,
|
||
),
|
||
(
|
||
"Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag).enumerable === false",
|
||
true,
|
||
),
|
||
(
|
||
"Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag).configurable === true",
|
||
true,
|
||
),
|
||
(
|
||
"Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag).get.call() === 'Iterator'",
|
||
true,
|
||
),
|
||
// home 赋值抛 TypeError。
|
||
(
|
||
"try { Iterator.prototype[Symbol.toStringTag] = 'x'; false } catch (e) { e instanceof TypeError }",
|
||
true,
|
||
),
|
||
// 普通对象 setter 建 own Symbol.toStringTag。
|
||
(
|
||
"var o = {}; var d = Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag); \
|
||
d.set.call(o, 'tag'); o[Symbol.toStringTag] === 'tag'",
|
||
true,
|
||
),
|
||
// 原型继承场景:非 home 子对象赋值落到自身 own 属性(不污染祖先)。
|
||
(
|
||
"var p = Object.create(Iterator.prototype); p[Symbol.toStringTag] = 'sub'; \
|
||
p[Symbol.toStringTag] === 'sub' && Iterator.prototype[Symbol.toStringTag] === 'Iterator'",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn symbol_dispose_and_async_dispose_registered() {
|
||
let mut vm = Vm::new();
|
||
// well-known symbol 以符号原语表示,typeof 为 symbol;两符号互不相同。
|
||
let bool_cases = [
|
||
("typeof Symbol.dispose === 'symbol'", true),
|
||
("typeof Symbol.asyncDispose === 'symbol'", true),
|
||
("Symbol.dispose !== Symbol.asyncDispose", true),
|
||
("Symbol.dispose !== Symbol.iterator", true),
|
||
// 键可作属性名使用(well-known 键映射一致)。
|
||
("({ [Symbol.dispose]: 1 })[Symbol.dispose] === 1", true),
|
||
// getOwnPropertySymbols 能反解出同一符号原语。
|
||
("Object.getOwnPropertySymbols({ [Symbol.dispose]: 1 })[0] === Symbol.dispose", true),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
// String(Symbol) 走 SymbolDescriptiveString(规范 String 构造器步骤),
|
||
// 不经 ToString——well-known 符号按描述名产出。
|
||
let s = eval(&mut vm, "String(Symbol.dispose)").unwrap();
|
||
assert_eq!(to_str(&vm, s), "Symbol(Symbol.dispose)");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_prototype_symbol_dispose() {
|
||
let mut vm = Vm::new();
|
||
// @@dispose 调用 this 的 return 方法并返回 undefined。
|
||
let bool_cases = [
|
||
("typeof Iterator.prototype[Symbol.dispose] === 'function'", true),
|
||
("Iterator.prototype[Symbol.dispose].length === 0", true),
|
||
("Iterator.prototype[Symbol.dispose].name === '[Symbol.dispose]'", true),
|
||
(
|
||
"var called = false; var it = { return: function() { called = true; return { done: true }; } }; \
|
||
var r = Iterator.prototype[Symbol.dispose].call(it); r === undefined && called === true",
|
||
true,
|
||
),
|
||
// 无 return 方法:GetMethod 返回 undefined,跳过调用。
|
||
("Iterator.prototype[Symbol.dispose].call({ next: function() {} }) === undefined", true),
|
||
// return 返回任意值不影响 @@dispose 的 undefined 返回值。
|
||
(
|
||
"var it = { return: function() { return 42; } }; \
|
||
Iterator.prototype[Symbol.dispose].call(it) === undefined",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
// return 抛错经 @@dispose 透传(原值,不二次包装)。
|
||
let thrown = eval(
|
||
&mut vm,
|
||
"try { var it = { return: function() { throw new RangeError('boom'); } }; \
|
||
Iterator.prototype[Symbol.dispose].call(it); false } catch (e) { e instanceof RangeError }",
|
||
)
|
||
.unwrap();
|
||
assert!(thrown.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_prototype_links_to_iterator_proto() {
|
||
let vm = Vm::new();
|
||
// %IteratorHelperPrototype% 已建且链到 %IteratorPrototype%(链:
|
||
// helper → %IteratorPrototype% → Object.prototype)。
|
||
let helper_ptr = vm.session().builtin_world().iterator_helper_proto.as_ptr() as *mut JsObject;
|
||
let helper_proto_val = unsafe { &*helper_ptr }.proto();
|
||
let iter_ptr = vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject;
|
||
assert!(helper_proto_val.is_object());
|
||
assert!(std::ptr::eq(helper_proto_val.as_js_object_ptr(), iter_ptr));
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_constructor_plain_call_throws() {
|
||
let mut vm = Vm::new();
|
||
// 普通调用 `Iterator()` 抛 TypeError(emit 以 undefined 作 this)。
|
||
let bool_cases = [
|
||
("try { Iterator(); false } catch (e) { e instanceof TypeError }", true),
|
||
// call/apply 显式传对象 this 顶层调用同样抛(newTarget 槽为 undefined)。
|
||
("try { Iterator.call({}); false } catch (e) { e instanceof TypeError }", true),
|
||
("try { Iterator.apply({}, []); false } catch (e) { e instanceof TypeError }", true),
|
||
// 以 %IteratorPrototype% 为原型的对象顶层调用也抛(home 形态误判保护)。
|
||
(
|
||
"try { Iterator.call(Object.create(Iterator.prototype)); false } \
|
||
catch (e) { e instanceof TypeError }",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_constructor_subclassable() {
|
||
let mut vm = Vm::new();
|
||
// `class X extends Iterator {}` 的 super() 经放行路径返回 undefined,
|
||
// 实例原型按 newTarget 设为 X.prototype。
|
||
let bool_cases = [
|
||
(
|
||
"class TestIterator extends Iterator {} \
|
||
var it = new TestIterator(); \
|
||
it instanceof TestIterator && it instanceof Iterator",
|
||
true,
|
||
),
|
||
(
|
||
"class TestIterator extends Iterator {} \
|
||
Object.getPrototypeOf(new TestIterator()) === TestIterator.prototype",
|
||
true,
|
||
),
|
||
(
|
||
"class TestIterator extends Iterator {} \
|
||
Object.getPrototypeOf(TestIterator.prototype) === Iterator.prototype",
|
||
true,
|
||
),
|
||
// 显式 constructor 内 super() 与隐式等价。
|
||
(
|
||
"class TestIterator extends Iterator { constructor() { super(); } } \
|
||
new TestIterator() instanceof Iterator",
|
||
true,
|
||
),
|
||
// 多层继承:newTarget 跨层保持为最外层类。
|
||
(
|
||
"class A extends Iterator {} class B extends A {} \
|
||
var b = new B(); \
|
||
b instanceof B && b instanceof A && b instanceof Iterator",
|
||
true,
|
||
),
|
||
// 构造器 name 不受影响。
|
||
("class TestIterator extends Iterator {} TestIterator.name === 'TestIterator'", true),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_constructor_subclass_still_rejects_new_after_reset() {
|
||
let mut vm = Vm::new();
|
||
// dirty reset 后构造器经 bind_iterator_global 重绑,本体与 subclass 路径不变。
|
||
unsafe { &mut *(vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject) }.bump_generation();
|
||
vm.full_reset();
|
||
let bool_cases = [
|
||
("try { new Iterator(); false } catch (e) { e instanceof TypeError }", true),
|
||
(
|
||
"class TestIterator extends Iterator {} \
|
||
new TestIterator() instanceof Iterator",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_prototype_rebound_after_full_reset() {
|
||
let mut vm = Vm::new();
|
||
// 用户改写 %IteratorPrototype%:object 家族世代递增(global 未动)。
|
||
let old_iter_proto = vm.session().builtin_world().iterator_proto.as_ptr();
|
||
unsafe { &mut *(old_iter_proto as *mut JsObject) }.bump_generation();
|
||
|
||
vm.full_reset();
|
||
|
||
// %IteratorPrototype% 重建后访问器 / @@dispose 重绑正确,构造器 identity 保留。
|
||
let cases = [
|
||
("Iterator.prototype.constructor === Iterator", true),
|
||
("Iterator.prototype[Symbol.toStringTag] === 'Iterator'", true),
|
||
("typeof Iterator.prototype[Symbol.dispose] === 'function'", true),
|
||
("Iterator.name === 'Iterator'", true),
|
||
("Iterator.length === 0", true),
|
||
("typeof Symbol.dispose === 'symbol'", true),
|
||
("typeof Symbol.asyncDispose === 'symbol'", true),
|
||
];
|
||
for (src, expected) in cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
// helper 原型随 object 家族重建并链到新 %IteratorPrototype%。
|
||
let helper_ptr = vm.session().builtin_world().iterator_helper_proto.as_ptr() as *mut JsObject;
|
||
let helper_proto_val = unsafe { &*helper_ptr }.proto();
|
||
let new_iter_ptr = vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject;
|
||
assert!(std::ptr::eq(helper_proto_val.as_js_object_ptr(), new_iter_ptr));
|
||
// 迭代器家族整体功能完好。
|
||
let r = eval(
|
||
&mut vm,
|
||
"Iterator.prototype[Symbol.dispose].call({return: function(){return {done:true}}})",
|
||
)
|
||
.unwrap();
|
||
assert!(r.is_undefined());
|
||
}
|
||
|
||
// ── 终端 6 方法(forEach/every/some/find/reduce/toArray)──
|
||
|
||
#[test]
|
||
fn iterator_terminal_for_each_basic() {
|
||
// forEach 耗尽全部元素,回调收 (value, counter),返回 undefined。
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"var out = []; \
|
||
var r = Iterator.from([10, 20, 30]).forEach(function (v, i) { out.push(v + i); }); \
|
||
out.join(',') + '|' + (r === undefined)",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, result), "10,21,32|true");
|
||
let bool_cases = [
|
||
// 普通数组迭代器(非 from 包装)同样可消费。
|
||
("var out = 0; [1, 2, 3].values().forEach(function (v) { out += v; }); out === 6", true),
|
||
// 空迭代立即返回 undefined。
|
||
("Iterator.from([]).forEach(function () {}) === undefined", true),
|
||
// 回调 this:规范 Call(procedure, undefined) 对 sloppy 回调仍替换为全局对象。
|
||
(
|
||
"var captured; Iterator.from([1]).forEach(function () { captured = this; }); \
|
||
captured === globalThis",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_terminal_every_some_find_basic() {
|
||
// every/some/find 正常路径:全真/短路/命中值与未命中。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
("Iterator.from([1, 2, 3]).every(function (v) { return v > 0; }) === true", true),
|
||
("Iterator.from([1, 2, 3]).every(function (v) { return v > 1; }) === false", true),
|
||
("Iterator.from([1, 2, 3]).some(function (v) { return v > 2; }) === true", true),
|
||
("Iterator.from([1, 2, 3]).some(function (v) { return v > 9; }) === false", true),
|
||
("Iterator.from([1, 2, 3]).find(function (v) { return v > 1; }) === 2", true),
|
||
("Iterator.from([1, 2, 3]).find(function (v) { return v > 9; }) === undefined", true),
|
||
// 空迭代:every/some 恒为相反端点值,find 为 undefined。
|
||
("Iterator.from([]).every(function () { return false; }) === true", true),
|
||
("Iterator.from([]).some(function () { return true; }) === false", true),
|
||
("Iterator.from([]).find(function () { return true; }) === undefined", true),
|
||
// 谓词回调收 (value, counter)。
|
||
(
|
||
"var pairs = []; \
|
||
Iterator.from(['a', 'b']).every(function (v, i) { pairs.push(v + i); return true; }); \
|
||
pairs.join(',') === 'a0,b1'",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_terminal_short_circuit_closes() {
|
||
// 短路路径关底层:only 消费到命中点,return 方法被调用。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// some 命中后不再消费剩余元素,且底层 return 被调用。
|
||
(
|
||
"var reads = 0; var closed = false; \
|
||
class CI extends Iterator { \
|
||
next() { reads++; return reads === 1 ? { done: false, value: 1 } : { done: true, value: undefined }; } \
|
||
return() { closed = true; return {}; } \
|
||
} \
|
||
var r = new CI().some(function () { return true; }); \
|
||
r === true && closed === true && reads === 1",
|
||
true,
|
||
),
|
||
// every 首个 falsy 即短路关底层。
|
||
(
|
||
"var reads = 0; var closed = false; \
|
||
class CI extends Iterator { \
|
||
next() { reads++; return reads === 1 ? { done: false, value: 1 } : { done: true, value: undefined }; } \
|
||
return() { closed = true; return {}; } \
|
||
} \
|
||
var r = new CI().every(function () { return false; }); \
|
||
r === false && closed === true && reads === 1",
|
||
true,
|
||
),
|
||
// find 命中返回对应 value 且关底层。
|
||
(
|
||
"var closed = false; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 7 }; } \
|
||
return() { closed = true; return {}; } \
|
||
} \
|
||
var r = new CI().find(function () { return true; }); \
|
||
r === 7 && closed === true",
|
||
true,
|
||
),
|
||
// 自然耗尽(done=true)不调 return。
|
||
(
|
||
"var closed = false; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: true, value: undefined }; } \
|
||
return() { closed = true; return {}; } \
|
||
} \
|
||
new CI().every(function () { return true; }) === true && closed === false",
|
||
true,
|
||
),
|
||
// 短路 close 时 return getter 抛错 → 该错误胜出(正常完成形态)。
|
||
(
|
||
"class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
get return() { throw new RangeError('close'); } \
|
||
} \
|
||
try { new CI().some(function () { return true; }); false } \
|
||
catch (e) { e instanceof RangeError }",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_terminal_reduce_basic() {
|
||
// reduce:有/无初始值、单元素、空迭代 + 初始值、counter 语义。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
("Iterator.from([1, 2, 3]).reduce(function (a, b) { return a + b; }) === 6", true),
|
||
("Iterator.from([1, 2, 3]).reduce(function (a, b) { return a + b; }, 10) === 16", true),
|
||
("Iterator.from([1]).reduce(function (a, b) { return a + b; }) === 1", true),
|
||
("Iterator.from([]).reduce(function (a, b) { return a + b; }, 99) === 99", true),
|
||
// 无初始值:首元素作 accumulator,counter 从 1 起。
|
||
(
|
||
"var pairs = []; \
|
||
Iterator.from(['a', 'b', 'c']).reduce(function (acc, v, i) { pairs.push(i); return acc; }); \
|
||
pairs.join(',') === '1,2'",
|
||
true,
|
||
),
|
||
// 有初始值:counter 从 0 起。
|
||
(
|
||
"var pairs = []; \
|
||
Iterator.from(['a', 'b']).reduce(function (acc, v, i) { pairs.push(i); return acc; }, 'x'); \
|
||
pairs.join(',') === '0,1'",
|
||
true,
|
||
),
|
||
// accumulator 可为任意类型(含对象引用延续)。
|
||
(
|
||
"var acc = []; \
|
||
Iterator.from([1, 2]).reduce(function (a, v) { a.push(v); return a; }, acc) === acc \
|
||
&& acc.join(',') === '1,2'",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_terminal_reduce_empty_no_initial_throws() {
|
||
// 空迭代无初始值 → TypeError,且不关底层(规范字面:首步 done 直接抛)。
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"var closed = false; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: true, value: undefined }; } \
|
||
return() { closed = true; return {}; } \
|
||
} \
|
||
var ok = false; \
|
||
try { new CI().reduce(function (a, b) { return a + b; }); } \
|
||
catch (e) { ok = e instanceof TypeError; } \
|
||
ok && closed === false",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_terminal_to_array() {
|
||
// toArray 消费全部元素返回普通数组。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"var a = Iterator.from([1, 2, 3]).toArray(); \
|
||
a instanceof Array && a.length === 3 && a[0] === 1 && a[2] === 3",
|
||
true,
|
||
),
|
||
(
|
||
"var a = Iterator.from((function* () { yield 'x'; yield 'y'; })()).toArray(); \
|
||
a instanceof Array && a.length === 2 && a[0] === 'x' && a[1] === 'y'",
|
||
true,
|
||
),
|
||
(
|
||
"Iterator.from([]).toArray() instanceof Array && Iterator.from([]).toArray().length === 0",
|
||
true,
|
||
),
|
||
// 数组迭代器(Array.prototype.values)同样可收集。
|
||
("var a = [1, 2].values().toArray(); a instanceof Array && a.length === 2", true),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_terminal_callback_error_passthrough() {
|
||
// 回调抛错:原值透传(任意类型不二次包装),且关底层。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// 非 Error 原值 42 透传。
|
||
(
|
||
"try { Iterator.from([1]).forEach(function () { throw 42; }); 'no' } \
|
||
catch (e) { e === 42 }",
|
||
true,
|
||
),
|
||
// Error 对象身份保留(不重包)。
|
||
(
|
||
"var sentinel = new RangeError('boom'); \
|
||
try { Iterator.from([1]).every(function () { throw sentinel; }); 'no' } \
|
||
catch (e) { e === sentinel }",
|
||
true,
|
||
),
|
||
// 回调抛错后底层 return 被调用,原错误仍胜出。
|
||
(
|
||
"var closed = false; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { closed = true; throw new RangeError('close'); } \
|
||
} \
|
||
var ok = false; \
|
||
try { new CI().find(function () { throw 42; }); } \
|
||
catch (e) { ok = e === 42; } \
|
||
ok && closed === true",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_terminal_callback_validation_closes() {
|
||
// 回调不可调用:抛 TypeError 且关底层,且不读 next(2024 规范更新)。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// 无参调用(回调为 undefined)也关底层。
|
||
(
|
||
"var closed = false; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: true, value: undefined }; } \
|
||
return() { closed = true; return {}; } \
|
||
} \
|
||
var ok = false; \
|
||
try { new CI().forEach(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && closed === true",
|
||
true,
|
||
),
|
||
// 非可调用对象同样关底层;next getter 不被读取。
|
||
(
|
||
"var closed = false; var read = false; \
|
||
var it = Object.create(Iterator.prototype); \
|
||
Object.defineProperty(it, 'next', { get: function () { read = true; return function () {}; } }); \
|
||
it.return = function () { closed = true; return {}; }; \
|
||
var ok = false; \
|
||
try { it.forEach({}); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && closed === true && read === false",
|
||
true,
|
||
),
|
||
// 回调校验失败关底层对 every/some/find/reduce 一致生效。
|
||
(
|
||
"var closed = false; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: true, value: undefined }; } \
|
||
return() { closed = true; return {}; } \
|
||
} \
|
||
var all = false; \
|
||
try { new CI().some(null); } catch (e) { all = e instanceof TypeError; } \
|
||
all && closed === true",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_terminal_reentrancy_guard() {
|
||
// 终端方法消费期间底层生成器被重入(body 执行中再取 next):生成器重入守卫
|
||
// 抛 TypeError,经终端方法干净透传(不吞错、不损坏生成器状态)。
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"var caught = null; \
|
||
var gen = (function* () { \
|
||
yield 1; \
|
||
try { Iterator.from(gen).forEach(function (v) { gen.next(); }); } \
|
||
catch (e) { caught = e; } \
|
||
})(); \
|
||
gen.next(); gen.next(); \
|
||
caught !== null && caught instanceof TypeError",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_terminal_method_shape() {
|
||
// 6 方法 length/name 与绑定位置(挂 %IteratorPrototype% 原型)。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"Iterator.prototype.forEach.length === 1 && Iterator.prototype.every.length === 1 \
|
||
&& Iterator.prototype.some.length === 1 && Iterator.prototype.find.length === 1 \
|
||
&& Iterator.prototype.reduce.length === 1 && Iterator.prototype.toArray.length === 0",
|
||
true,
|
||
),
|
||
(
|
||
"Iterator.prototype.forEach.name === 'forEach' && Iterator.prototype.every.name === 'every' \
|
||
&& Iterator.prototype.some.name === 'some' && Iterator.prototype.find.name === 'find' \
|
||
&& Iterator.prototype.reduce.name === 'reduce' && Iterator.prototype.toArray.name === 'toArray'",
|
||
true,
|
||
),
|
||
// 方法经原型链对普通对象可用(this 为任意对象即可)。
|
||
(
|
||
"var it = { next: function () { return { done: true, value: undefined }; } }; \
|
||
Iterator.prototype.toArray.call(it) instanceof Array",
|
||
true,
|
||
),
|
||
// this 非对象 → TypeError。
|
||
(
|
||
"try { Iterator.prototype.forEach.call(1, function () {}); false } \
|
||
catch (e) { e instanceof TypeError }",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_terminal_rebound_after_full_reset() {
|
||
// dirty reset 后 6 终端方法随 %IteratorPrototype% 重建重绑,功能完好。
|
||
let mut vm = Vm::new();
|
||
unsafe { &mut *(vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject) }.bump_generation();
|
||
vm.full_reset();
|
||
let bool_cases = [
|
||
("Iterator.from([1, 2, 3]).forEach(function () {}) === undefined", true),
|
||
(
|
||
"var out = []; \
|
||
Iterator.from(['a', 'b']).forEach(function (v) { out.push(v); }); out.join('') === 'ab'",
|
||
true,
|
||
),
|
||
("Iterator.from([1, 2]).reduce(function (a, b) { return a + b; }, 0) === 3", true),
|
||
("Iterator.from([1, 2]).toArray().length === 2", true),
|
||
("Iterator.from([1]).some(function () { return true; }) === true", true),
|
||
("Iterator.from([1]).every(function () { return true; }) === true", true),
|
||
("Iterator.from([1]).find(function () { return true; }) === 1", true),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
// ── 返回迭代器 5 方法(map/filter/take/drop/flatMap)+ helper 状态机 ──
|
||
|
||
#[test]
|
||
fn iterator_helper_map_filter_basic() {
|
||
// map/filter 正常路径:产出、counter 实参、回调 this 为 undefined。
|
||
let mut vm = Vm::new();
|
||
let str_cases = [
|
||
("[...Iterator.from([1, 2, 3]).map(function (v) { return v * 2; })].join(',')", "2,4,6"),
|
||
// 数组迭代器(非 from 包装)同样可消费。
|
||
("[...[1, 2, 3].values().map(function (v) { return v + 10; })].join(',')", "11,12,13"),
|
||
// filter 只产出 truthy 元素。
|
||
(
|
||
"[...Iterator.from([1, 2, 3, 4]).filter(function (v) { return v % 2 === 0; })].join(',')",
|
||
"2,4",
|
||
),
|
||
// 谓词返回值被 ToBoolean 化。
|
||
("[...Iterator.from([0, 1, 2]).filter(function (v) { return v; })].join(',')", "1,2"),
|
||
// map 回调收 (value, counter)。
|
||
(
|
||
"var pairs = []; \
|
||
var it = Iterator.from(['a', 'b']).map(function (v, i) { pairs.push(v + i); return v; }); \
|
||
var x; \
|
||
while (!(x = it.next()).done) {} \
|
||
pairs.join(',')",
|
||
"a0,b1",
|
||
),
|
||
// filter 计数含被过滤元素(0 被过滤但占用计数 0,命中元素用计数 1)。
|
||
(
|
||
"var pairs = []; \
|
||
var it = Iterator.from([0, 1, 2]).filter(function (v, i) { pairs.push(i); return v > 0; }); \
|
||
var x; \
|
||
while (!(x = it.next()).done) {} \
|
||
pairs.join(',')",
|
||
"0,1,2",
|
||
),
|
||
];
|
||
for (src, expected) in str_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(to_str(&vm, result), expected, "for {}", src);
|
||
}
|
||
let bool_cases = [
|
||
// 回调 this:规范 Call(procedure, undefined) 对 sloppy 回调仍替换为全局对象。
|
||
(
|
||
"var captured; Iterator.from([1]).map(function () { captured = this; return 1; }).next(); \
|
||
captured === globalThis",
|
||
true,
|
||
),
|
||
// 空迭代立即 done。
|
||
("Iterator.from([]).map(function (v) { return v; }).next().done", true),
|
||
// 方法 length 均为 1。
|
||
("Iterator.prototype.map.length === 1 && Iterator.prototype.filter.length === 1", true),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_take_drop_basic() {
|
||
// take/drop 正常路径:计数边界、limit 校验、自然耗尽。
|
||
let mut vm = Vm::new();
|
||
let str_cases = [
|
||
("[...Iterator.from([1, 2, 3]).take(2)].join(',')", "1,2"),
|
||
("[...Iterator.from([1, 2, 3]).take(5)].join(',')", "1,2,3"),
|
||
("[...Iterator.from([1, 2, 3]).take(0)].join(',')", ""),
|
||
("[...Iterator.from([1, 2, 3]).take(Infinity)].join(',')", "1,2,3"),
|
||
("[...Iterator.from([1, 2, 3]).drop(2)].join(',')", "3"),
|
||
("[...Iterator.from([1, 2, 3]).drop(0)].join(',')", "1,2,3"),
|
||
("[...Iterator.from([1, 2, 3]).drop(10)].join(',')", ""),
|
||
("[...Iterator.from([1, 2, 3]).drop(Infinity)].join(',')", ""),
|
||
// 链式组合。
|
||
("[...Iterator.from([1, 2, 3, 4, 5]).drop(1).take(3)].join(',')", "2,3,4"),
|
||
];
|
||
for (src, expected) in str_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(to_str(&vm, result), expected, "for {}", src);
|
||
}
|
||
let bool_cases = [
|
||
// -0.5 → trunc(-0) 合法;null → 0 合法。
|
||
("[...Iterator.from([1]).take(-0.5)].join(',') === ''", true),
|
||
("[...Iterator.from([1, 2]).take(null)].join(',') === ''", true),
|
||
("[...Iterator.from([1, 2]).drop(null)].join(',') === '1,2'", true),
|
||
// limit 经 valueOf/toString 强制转换。
|
||
(
|
||
"var n = { valueOf: function () { return 1; } }; \
|
||
[...Iterator.from([1, 2]).take(n)].join(',') === '1'",
|
||
true,
|
||
),
|
||
(
|
||
"var n = { toString: function () { return '1'; } }; \
|
||
[...Iterator.from([1, 2]).drop(n)].join(',') === '2'",
|
||
true,
|
||
),
|
||
// 方法 length 均为 1。
|
||
("Iterator.prototype.take.length === 1 && Iterator.prototype.drop.length === 1", true),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_take_drop_limit_validation() {
|
||
// NaN/负值/缺失 limit → RangeError 且关底层;ToNumber 抛错原值透传且关底层;
|
||
// next getter 在 limit 校验前不被读取。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// take()/take(undefined)/NaN/-1 → RangeError。
|
||
(
|
||
"var closed = 0; \
|
||
var c = { __proto__: Iterator.prototype, get next() { throw new RangeError('read'); }, \
|
||
return() { closed++; return {}; } }; \
|
||
var ok = false; \
|
||
try { c.take(); } catch (e) { ok = e instanceof RangeError; } \
|
||
ok && closed === 1",
|
||
true,
|
||
),
|
||
(
|
||
"var closed = 0; \
|
||
var c = { __proto__: Iterator.prototype, get next() { throw new RangeError('read'); }, \
|
||
return() { closed++; return {}; } }; \
|
||
var ok = false; \
|
||
try { c.take(NaN); } catch (e) { ok = e instanceof RangeError; } \
|
||
ok && closed === 1",
|
||
true,
|
||
),
|
||
(
|
||
"var closed = 0; \
|
||
var c = { __proto__: Iterator.prototype, get next() { throw new RangeError('read'); }, \
|
||
return() { closed++; return {}; } }; \
|
||
var ok = false; \
|
||
try { c.drop(-1); } catch (e) { ok = e instanceof RangeError; } \
|
||
ok && closed === 1",
|
||
true,
|
||
),
|
||
// limit valueOf 抛错:原异常透传且关底层。
|
||
(
|
||
"var closed = 0; \
|
||
var c = { __proto__: Iterator.prototype, get next() { throw new RangeError('read'); }, \
|
||
return() { closed++; return {}; } }; \
|
||
var boom = new RangeError('boom'); \
|
||
var ok = false; \
|
||
try { c.take({ valueOf: function () { throw boom; } }); } catch (e) { ok = e === boom; } \
|
||
ok && closed === 1",
|
||
true,
|
||
),
|
||
// 校验失败不读 next(getter 抛错也不触发)。
|
||
(
|
||
"var closed = 0; var read = false; \
|
||
var c = { __proto__: Iterator.prototype, \
|
||
get next() { read = true; return function () {}; }, \
|
||
return() { closed++; return {}; } }; \
|
||
var ok = false; \
|
||
try { c.take(-1); } catch (e) { ok = e instanceof RangeError; } \
|
||
ok && closed === 1 && read === false",
|
||
true,
|
||
),
|
||
// this 非对象:直接 TypeError,不读 limit/不关。
|
||
(
|
||
"var ok = false; \
|
||
try { Iterator.prototype.take.call(1, 0); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_take_exhaustion_closes() {
|
||
// take 达标即关底层(return 被调用);自然耗尽不关;关后再调 next 全短路。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// take(0):首个 next 即关底层(return 抛错传播)。
|
||
(
|
||
"var closed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { closed++; throw new RangeError('boom'); } \
|
||
} \
|
||
var it = new CI().take(0); \
|
||
var threw = false; \
|
||
try { it.next(); } catch (e) { threw = e instanceof RangeError; } \
|
||
it.next().done && threw && closed === 1",
|
||
true,
|
||
),
|
||
// take(1):取 1 个后第二次 next 关底层。
|
||
(
|
||
"var closed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { closed++; return {}; } \
|
||
} \
|
||
var it = new CI().take(1); \
|
||
it.next().value === 1 && it.next().done && closed === 1",
|
||
true,
|
||
),
|
||
// take(5) 但底层 3 个:自然耗尽不关底层。
|
||
(
|
||
"var closed = 0; \
|
||
var i = 0; \
|
||
class CI extends Iterator { \
|
||
next() { i++; return i <= 3 ? { done: false, value: i } : { done: true, value: undefined }; } \
|
||
return() { closed++; return {}; } \
|
||
} \
|
||
var out = 0; \
|
||
var it = new CI().take(5); \
|
||
var x; \
|
||
while (!(x = it.next()).done) out += x.value; \
|
||
out === 6 && closed === 0",
|
||
true,
|
||
),
|
||
// return 方法转发:显式 return() 关底层且只关一次。
|
||
(
|
||
"var closed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { closed++; return {}; } \
|
||
} \
|
||
var it = new CI().take(100); \
|
||
it.return(); it.return(); \
|
||
closed === 1",
|
||
true,
|
||
),
|
||
// return getter 抛错:return() 时该错误胜出。
|
||
(
|
||
"class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
get return() { throw new RangeError('boom'); } \
|
||
} \
|
||
var it = new CI().take(1); \
|
||
it.next(); \
|
||
var threw = false; \
|
||
try { it.return(); } catch (e) { threw = e instanceof RangeError; } \
|
||
threw",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_drop_exhaustion_no_close() {
|
||
// drop 耗尽/跳过永不主动关底层;return() 转发仍生效。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// drop(10) 消耗 3 个元素后自然耗尽,return 不被调用。
|
||
(
|
||
"var closed = 0; \
|
||
var i = 0; \
|
||
class CI extends Iterator { \
|
||
next() { i++; return i <= 3 ? { done: false, value: i } : { done: true, value: undefined }; } \
|
||
return() { closed++; return {}; } \
|
||
} \
|
||
var it = new CI().drop(10); \
|
||
var x; \
|
||
while (!(x = it.next()).done) {} \
|
||
closed === 0",
|
||
true,
|
||
),
|
||
// 显式 return() 关底层且只关一次。
|
||
(
|
||
"var closed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { closed++; return {}; } \
|
||
} \
|
||
var it = new CI().drop(1); \
|
||
it.return(); it.return(); \
|
||
closed === 1",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_flat_map_semantics() {
|
||
// flatMap 正常路径:数组/生成器/纯 next 对象展开、回退路径、原始值拒绝。
|
||
let mut vm = Vm::new();
|
||
let str_cases = [
|
||
// mapper 返回数组:经 @@iterator 展开。
|
||
("[...Iterator.from([1, 2]).flatMap(function (v) { return [v, v * 10]; })].join(',')", "1,10,2,20"),
|
||
// mapper 返回生成器。
|
||
(
|
||
"[...Iterator.from([1, 2]).flatMap(function (v) { return (function* () { yield v; yield v + 1; })(); })].join(',')",
|
||
"1,2,2,3",
|
||
),
|
||
// mapper 返回纯 next 对象(无 @@iterator,回退鸭子 next)。
|
||
(
|
||
"function* h() { yield 0; yield 1; yield 2; } \
|
||
var it = Iterator.from([1]).flatMap(function () { \
|
||
var n = h(); \
|
||
return { [Symbol.iterator]: null, next: function () { return n.next(); } }; \
|
||
}); \
|
||
[...it].join(',')",
|
||
"0,1,2",
|
||
),
|
||
// 空数组内层:跳过并继续外层。
|
||
("[...Iterator.from([1, 2]).flatMap(function () { return []; })].join(',')", ""),
|
||
// counter 只按外层元素递增。
|
||
(
|
||
"var pairs = []; \
|
||
var it = Iterator.from(['a', 'b']).flatMap(function (v, i) { pairs.push(v + i); return [v]; }); \
|
||
var x; \
|
||
while (!(x = it.next()).done) {} \
|
||
pairs.join(',')",
|
||
"a0,b1",
|
||
),
|
||
// 字符串包装对象可展开(对象走 @@iterator)。
|
||
("[...Iterator.from([1]).flatMap(function () { return new String('ab'); })].join(',')", "a,b"),
|
||
];
|
||
for (src, expected) in str_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(to_str(&vm, result), expected, "for {}", src);
|
||
}
|
||
let bool_cases = [
|
||
// 已关闭的生成器作内层:首次 next 即 done,直接回到外层。
|
||
(
|
||
"function* g() { yield 0; yield 1; yield 2; } \
|
||
var closed = g(); \
|
||
closed.return(); \
|
||
closed.return = function () { throw new RangeError('boom'); }; \
|
||
var it = g().flatMap(function (v) { return closed; }); \
|
||
it.next().done",
|
||
true,
|
||
),
|
||
// mapper 返回原始值(含字符串)→ TypeError。
|
||
(
|
||
"var it = Iterator.from([1]).flatMap(function () { return 5; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.from([1]).flatMap(function () { return 'str'; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok",
|
||
true,
|
||
),
|
||
// @@iterator 非 null/undefined 且不可调用 → TypeError。
|
||
(
|
||
"var it = Iterator.from([1]).flatMap(function () { return { [Symbol.iterator]: 0, next: function () {} }; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok",
|
||
true,
|
||
),
|
||
// @@iterator 结果非对象 → TypeError。
|
||
(
|
||
"var it = Iterator.from([1]).flatMap(function () { return { [Symbol.iterator]: function () { return 5; } }; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok",
|
||
true,
|
||
),
|
||
// 方法 length 为 1。
|
||
("Iterator.prototype.flatMap.length === 1", true),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_wrapper_proto_and_shape() {
|
||
// wrapper 挂 %IteratorHelperPrototype%:instanceof Iterator、原型链、
|
||
// 三方法 + @@toStringTag、无 own next。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
("Iterator.from([1]).map(function (x) { return x; }) instanceof Iterator", true),
|
||
(
|
||
"var it = Iterator.from([1]).filter(function () { return true; }); \
|
||
Object.getPrototypeOf(it)[Symbol.toStringTag] === 'Iterator Helper'",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.from([1]).take(1); \
|
||
Object.getPrototypeOf(it) !== Iterator.prototype \
|
||
&& Object.getPrototypeOf(Object.getPrototypeOf(it)) === Iterator.prototype",
|
||
true,
|
||
),
|
||
(
|
||
"var p = Object.getPrototypeOf(Iterator.from([1]).map(function (x) { return x; })); \
|
||
typeof p.next === 'function' && typeof p.return === 'function' && typeof p.throw === 'function'",
|
||
true,
|
||
),
|
||
// next/return length 0,throw length 1。
|
||
(
|
||
"var p = Object.getPrototypeOf(Iterator.from([1]).map(function (x) { return x; })); \
|
||
p.next.length === 0 && p.return.length === 0 && p.throw.length === 1",
|
||
true,
|
||
),
|
||
// helper 原型经原型链可迭代(%IteratorPrototype% 的 @@iterator)。
|
||
(
|
||
"var p = Object.getPrototypeOf(Iterator.from([1]).map(function (x) { return x; })); \
|
||
p[Symbol.iterator]() === p",
|
||
true,
|
||
),
|
||
// 5 方法 name/描述符。
|
||
(
|
||
"Iterator.prototype.map.name === 'map' && Iterator.prototype.filter.name === 'filter' \
|
||
&& Iterator.prototype.take.name === 'take' && Iterator.prototype.drop.name === 'drop' \
|
||
&& Iterator.prototype.flatMap.name === 'flatMap'",
|
||
true,
|
||
),
|
||
(
|
||
"var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'map'); \
|
||
d.writable === true && d.enumerable === false && d.configurable === true",
|
||
true,
|
||
),
|
||
// this 非对象 → TypeError。
|
||
(
|
||
"try { Iterator.prototype.map.call(1, function () {}); false } \
|
||
catch (e) { e instanceof TypeError }",
|
||
true,
|
||
),
|
||
// 非 helper 对象调用原型 next → TypeError。
|
||
(
|
||
"var p = Object.getPrototypeOf(Iterator.from([1]).map(function (x) { return x; })); \
|
||
var ok = false; \
|
||
try { p.next.call({}); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_return_throw_state_machine() {
|
||
// return/throw 三方法状态机:转发、完成态短路、错误胜出。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// return 转发底层且只关一次。
|
||
(
|
||
"var closed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { closed++; return {}; } \
|
||
} \
|
||
var it = new CI().map(function (x) { return x; }); \
|
||
it.next(); \
|
||
it.return(); it.return(); \
|
||
closed === 1 && it.next().done",
|
||
true,
|
||
),
|
||
// 自然耗尽后 return 不转发。
|
||
(
|
||
"var closed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: true, value: undefined }; } \
|
||
return() { closed++; return {}; } \
|
||
} \
|
||
var it = new CI().map(function (x) { return x; }); \
|
||
it.next(); \
|
||
it.return(); \
|
||
closed === 0",
|
||
true,
|
||
),
|
||
// 底层 return 抛错:return() 传播该错误,此后 next 全 done。
|
||
(
|
||
"class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { throw new RangeError('boom'); } \
|
||
} \
|
||
var it = new CI().map(function (x) { return x; }); \
|
||
var threw = false; \
|
||
try { it.return(); } catch (e) { threw = e instanceof RangeError; } \
|
||
threw && it.next().done && it.return().done",
|
||
true,
|
||
),
|
||
// throw 注入:置完成 + 关底层 + 原值传播。
|
||
(
|
||
"var closed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { closed++; return {}; } \
|
||
} \
|
||
var it = new CI().map(function (x) { return x; }); \
|
||
var ok = false; \
|
||
try { it.throw(42); } catch (e) { ok = e === 42; } \
|
||
ok && closed === 1 && it.next().done",
|
||
true,
|
||
),
|
||
// 完成态 throw:直接抛值(不关底层)。
|
||
(
|
||
"var closed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: true, value: undefined }; } \
|
||
return() { closed++; return {}; } \
|
||
} \
|
||
var it = new CI().map(function (x) { return x; }); \
|
||
it.next(); \
|
||
var ok = false; \
|
||
try { it.throw(7); } catch (e) { ok = e === 7; } \
|
||
ok && closed === 0",
|
||
true,
|
||
),
|
||
// throw 时底层 return 也抛错:原值胜出。
|
||
(
|
||
"class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { throw new RangeError('close'); } \
|
||
} \
|
||
var it = new CI().map(function (x) { return x; }); \
|
||
var ok = false; \
|
||
try { it.throw(42); } catch (e) { ok = e === 42; } \
|
||
ok",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_flat_map_return_closes_inner_then_outer() {
|
||
// flatMap 内层活跃时 return:先关内层再关外层;内层 return 错误优先胜出。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"var innerClosed = 0; var outerClosed = 0; \
|
||
var it = Iterator.from([1]).flatMap(function (v) { \
|
||
return { next: function () { return { done: false, value: 1 }; }, \
|
||
return: function () { innerClosed++; return {}; } }; \
|
||
}); \
|
||
it.next(); \
|
||
it.return(); \
|
||
innerClosed === 1",
|
||
true,
|
||
),
|
||
// 外层 return 错误传播(内层正常关闭后)。
|
||
(
|
||
"var outerClosed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { outerClosed++; throw new RangeError('boom'); } \
|
||
} \
|
||
var it = new CI().flatMap(function (v) { \
|
||
return { next: function () { return { done: false, value: 1 }; }, \
|
||
return: function () { return {}; } }; \
|
||
}); \
|
||
it.next(); \
|
||
var threw = false; \
|
||
try { it.return(); } catch (e) { threw = e instanceof RangeError; } \
|
||
threw && outerClosed === 1",
|
||
true,
|
||
),
|
||
// 内层 return 错误优先于外层。
|
||
(
|
||
"var innerClosed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { return {}; } \
|
||
} \
|
||
var it = new CI().flatMap(function (v) { \
|
||
return { next: function () { return { done: false, value: 1 }; }, \
|
||
return: function () { innerClosed++; throw new RangeError('inner'); } }; \
|
||
}); \
|
||
it.next(); \
|
||
var ok = false; \
|
||
try { it.return(); } catch (e) { ok = e instanceof RangeError; } \
|
||
ok && innerClosed === 1",
|
||
true,
|
||
),
|
||
// 内层未起步(未 next)时 return 只关外层。
|
||
(
|
||
"var outerClosed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { outerClosed++; return {}; } \
|
||
} \
|
||
var it = new CI().flatMap(function (v) { return [v]; }); \
|
||
it.return(); \
|
||
outerClosed === 1",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_reentrancy_guard() {
|
||
// 5 方法重入守卫:推进期间再调 next → TypeError,且底层不被二次推进。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// map:mapper 内重入。
|
||
(
|
||
"var enterCount = 0; \
|
||
var gen = (function* () { yield 1; })(); \
|
||
var it = gen.map(function (v) { enterCount++; it.next(); return v; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && enterCount === 1",
|
||
true,
|
||
),
|
||
// filter:谓词内重入。
|
||
(
|
||
"var enterCount = 0; \
|
||
var gen = (function* () { yield 1; })(); \
|
||
var it = gen.filter(function (v) { enterCount++; it.next(); return true; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && enterCount === 1",
|
||
true,
|
||
),
|
||
// take:底层 next 内重入。
|
||
(
|
||
"var enterCount = 0; \
|
||
class CI extends Iterator { \
|
||
next() { enterCount++; it.next(); return { done: false, value: 1 }; } \
|
||
} \
|
||
var it = new CI().take(100); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && enterCount === 1",
|
||
true,
|
||
),
|
||
// drop:底层 next 内重入。
|
||
(
|
||
"var enterCount = 0; \
|
||
class CI extends Iterator { \
|
||
next() { enterCount++; it.next(); return { done: false, value: 1 }; } \
|
||
} \
|
||
var it = new CI().drop(0); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && enterCount === 1",
|
||
true,
|
||
),
|
||
// flatMap:mapper 内重入。
|
||
(
|
||
"var enterCount = 0; \
|
||
var gen = (function* () { yield 1; })(); \
|
||
var it = gen.flatMap(function (v) { enterCount++; it.next(); return [v]; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && enterCount === 1",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_callback_error_passthrough() {
|
||
// 回调抛错:原值透传(任意类型不二次包装),且关底层。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// 非 Error 原值 42 透传。
|
||
(
|
||
"var closed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { closed++; return {}; } \
|
||
} \
|
||
var it = new CI().map(function () { throw 42; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e === 42; } \
|
||
ok && closed === 1",
|
||
true,
|
||
),
|
||
// filter 谓词抛错同样处理。
|
||
(
|
||
"var sentinel = new RangeError('boom'); \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { return {}; } \
|
||
} \
|
||
var it = new CI().filter(function () { throw sentinel; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e === sentinel; } \
|
||
ok",
|
||
true,
|
||
),
|
||
// 回调抛错且底层 return 也抛错:原错误胜出。
|
||
(
|
||
"class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { throw new RangeError('close'); } \
|
||
} \
|
||
var it = new CI().map(function () { throw 42; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e === 42; } \
|
||
ok",
|
||
true,
|
||
),
|
||
// 回调抛错后 helper 完成:再调 next 全 done。
|
||
(
|
||
"class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { return {}; } \
|
||
} \
|
||
var it = new CI().map(function () { throw 42; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e === 42; } \
|
||
ok && it.next().done",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_validation_failure_closes() {
|
||
// 回调不可调用:抛 TypeError 且关底层,且不读 next(2024 规范更新)。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"var closed = 0; \
|
||
var c = { __proto__: Iterator.prototype, get next() { throw new RangeError('read'); }, \
|
||
return() { closed++; return {}; } }; \
|
||
var ok = false; \
|
||
try { c.map(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && closed === 1",
|
||
true,
|
||
),
|
||
(
|
||
"var closed = 0; \
|
||
var c = { __proto__: Iterator.prototype, get next() { throw new RangeError('read'); }, \
|
||
return() { closed++; return {}; } }; \
|
||
var ok = false; \
|
||
try { c.flatMap({}); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && closed === 1",
|
||
true,
|
||
),
|
||
(
|
||
"var closed = 0; var read = false; \
|
||
var c = { __proto__: Iterator.prototype, \
|
||
get next() { read = true; return function () {}; }, \
|
||
return() { closed++; return {}; } }; \
|
||
var ok = false; \
|
||
try { c.filter(null); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && closed === 1 && read === false",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_get_next_only_once_and_parallel() {
|
||
// next getter 只在建 wrapper 时读一次;底层可被并行推进(共享同一底层)。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// 5 方法各自只读一次 next。
|
||
(
|
||
"var gets = 0; \
|
||
class C extends Iterator { \
|
||
get next() { gets++; return function () { return { done: true, value: undefined }; }; } \
|
||
} \
|
||
new C().map(function (x) { return x; }); \
|
||
new C().filter(function () { return true; }); \
|
||
new C().take(1); \
|
||
new C().drop(1); \
|
||
new C().flatMap(function () { return []; }); \
|
||
gets === 5",
|
||
true,
|
||
),
|
||
// 底层直接推进后 helper 从当前位置继续。
|
||
(
|
||
"var it = (function* () { for (var i = 0; i < 5; ++i) yield i; })(); \
|
||
var mapped = it.map(function (x) { return x; }); \
|
||
it.next(); \
|
||
mapped.next().value === 1 && mapped.next().value === 2",
|
||
true,
|
||
),
|
||
// 底层提前关闭后 helper 立即 done。
|
||
(
|
||
"var it = (function* () { for (var i = 0; i < 5; ++i) yield i; })(); \
|
||
var mapped = it.map(function (x) { return x; }); \
|
||
it.return(); \
|
||
mapped.next().done",
|
||
true,
|
||
),
|
||
// 底层 next 返回非对象:helper next 抛 TypeError(不关底层)。
|
||
(
|
||
"class CI extends Iterator { \
|
||
next() { return null; } \
|
||
return() { throw new RangeError('boom'); } \
|
||
} \
|
||
var it = new CI().map(function (x) { return x; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok",
|
||
true,
|
||
),
|
||
// flatMap 内层 next 抛错:关外层不关内层。
|
||
(
|
||
"var outerClosed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { outerClosed++; return {}; } \
|
||
} \
|
||
var innerClosed = 0; \
|
||
var it = new CI().flatMap(function (v) { \
|
||
return { next: function () { throw new RangeError('inner'); }, \
|
||
return: function () { innerClosed++; return {}; } }; \
|
||
}); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof RangeError; } \
|
||
ok && outerClosed === 1 && innerClosed === 0",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_generator_proto_chain() {
|
||
// %GeneratorPrototype% 链到 %IteratorPrototype%:生成器可调用全部 helper。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"Object.getPrototypeOf(Object.getPrototypeOf(function* () {}.prototype)) === Iterator.prototype",
|
||
true,
|
||
),
|
||
(
|
||
"var gen = (function* () { yield 1; yield 2; })(); \
|
||
Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(gen))) === Iterator.prototype",
|
||
true,
|
||
),
|
||
// 生成器实例直接可用 5 helper。
|
||
(
|
||
"var g = (function* () { yield 1; yield 2; yield 3; })(); \
|
||
[...g.map(function (x) { return x * 2; })].join(',') === '2,4,6'",
|
||
true,
|
||
),
|
||
(
|
||
"var g = (function* () { yield 1; yield 2; yield 3; })(); \
|
||
[...g.filter(function (x) { return x > 1; })].join(',') === '2,3'",
|
||
true,
|
||
),
|
||
(
|
||
"var g = (function* () { yield 1; yield 2; yield 3; })(); \
|
||
[...g.take(2)].join(',') === '1,2'",
|
||
true,
|
||
),
|
||
(
|
||
"var g = (function* () { yield 1; yield 2; yield 3; })(); \
|
||
[...g.drop(1)].join(',') === '2,3'",
|
||
true,
|
||
),
|
||
(
|
||
"var g = (function* () { yield 1; })(); \
|
||
[...g.flatMap(function (x) { return [x, x]; })].join(',') === '1,1'",
|
||
true,
|
||
),
|
||
// 生成器结果对象是 Iterator。
|
||
("(function* () {})().map(function (x) { return x; }) instanceof Iterator", true),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_plain_call_and_edge_cases() {
|
||
// 非 Iterator 子类但实现迭代协议的 this(.call 路径)、已耗尽底层、
|
||
// next getter 抛错、非可调用底层 next。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
// .call 在纯 next 对象上可用(this-plain-iterator)。
|
||
(
|
||
"var it = { next: function () { var i = 0; return function () { i++; \
|
||
return i <= 2 ? { done: false, value: i } : { done: true }; }; }() }; \
|
||
var m = Iterator.prototype.map.call(it, function (x) { return x * 2; }); \
|
||
var out = []; \
|
||
var x; \
|
||
while (!(x = m.next()).done) out.push(x.value); \
|
||
out.join(',') === '2,4'",
|
||
true,
|
||
),
|
||
// 已耗尽底层:map 结果首个 next 即 done。
|
||
(
|
||
"var it = (function* () {})(); \
|
||
it.next(); \
|
||
it.map(function (x) { return x; }).next().done",
|
||
true,
|
||
),
|
||
// next getter 在方法调用时抛错:原错误透传(回调合法时读 next)。
|
||
(
|
||
"class TI extends Iterator { get next() { throw new RangeError('boom'); } } \
|
||
var ok = false; \
|
||
try { new TI().map(function (x) { return x; }); } catch (e) { ok = e instanceof RangeError; } \
|
||
ok",
|
||
true,
|
||
),
|
||
// 底层 next 非可调用:建 wrapper 成功,首个 helper next 抛 TypeError。
|
||
(
|
||
"var it = Iterator.prototype.map.call({ next: 0 }, function (x) { return x; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok",
|
||
true,
|
||
),
|
||
// 底层在 map 之前被关闭:helper next 立即 done。
|
||
(
|
||
"var it = (function* () { yield 1; })(); \
|
||
it.return(); \
|
||
var m = it.map(function (x) { return x; }); \
|
||
m.next().done",
|
||
true,
|
||
),
|
||
// 链式 map×3 的 return 逐层关闭到底层。
|
||
(
|
||
"var closed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { closed++; throw new RangeError('boom'); } \
|
||
} \
|
||
var it = new CI().map(function (x) { return x; }).map(function (x) { return x; }); \
|
||
var threw = false; \
|
||
try { it.return(); } catch (e) { threw = e instanceof RangeError; } \
|
||
threw && closed === 1",
|
||
true,
|
||
),
|
||
// flatMap 内层返回 [Symbol.iterator]: 0(不可调用)→ TypeError 且关外层。
|
||
(
|
||
"var outerClosed = 0; \
|
||
class CI extends Iterator { \
|
||
next() { return { done: false, value: 1 }; } \
|
||
return() { outerClosed++; return {}; } \
|
||
} \
|
||
var it = new CI().flatMap(function () { return { [Symbol.iterator]: 0, next: function () {} }; }); \
|
||
var ok = false; \
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||
ok && outerClosed === 1",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_helper_rebound_after_full_reset() {
|
||
// dirty reset 后 5 返回迭代器方法 + helper 原型三方法重绑正确。
|
||
let mut vm = Vm::new();
|
||
unsafe { &mut *(vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject) }.bump_generation();
|
||
vm.full_reset();
|
||
let bool_cases = [
|
||
(
|
||
"var out = []; \
|
||
var it = Iterator.from([1, 2, 3]).map(function (v) { return v * 2; }); \
|
||
var x; \
|
||
while (!(x = it.next()).done) out.push(x.value); \
|
||
out.join(',') === '2,4,6'",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.from([1, 2, 3]).filter(function (v) { return v % 2 === 1; }); \
|
||
[...it].join(',') === '1,3'",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.from([1, 2, 3]).take(2); \
|
||
[...it].join(',') === '1,2'",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.from([1, 2, 3]).drop(1); \
|
||
[...it].join(',') === '2,3'",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.from([1, 2]).flatMap(function (v) { return [v, v]; }); \
|
||
[...it].join(',') === '1,1,2,2'",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.from([1]).map(function (x) { return x; }); \
|
||
Object.getPrototypeOf(it)[Symbol.toStringTag] === 'Iterator Helper'",
|
||
true,
|
||
),
|
||
// 生成器原型链在 reset 后仍指向新 %IteratorPrototype%。
|
||
(
|
||
"Object.getPrototypeOf(Object.getPrototypeOf(function* () {}.prototype)) === Iterator.prototype",
|
||
true,
|
||
),
|
||
// helper 三方法可用。
|
||
(
|
||
"var it = Iterator.from([1, 2]).map(function (x) { return x; }); \
|
||
it.return().done === true",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn for_of_respects_overridden_array_prototype_iterator() {
|
||
let mut vm = Vm::new();
|
||
// 红形钉:内置原型上改写的 @@iterator 必须被迭代入口调用(展开与 for-of
|
||
// 同面),红形为短路直取内建迭代器、无视覆盖。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
const old = Array.prototype[Symbol.iterator];
|
||
Array.prototype[Symbol.iterator] = function* () { yield 42; };
|
||
try {
|
||
const spread = [...[1, 2, 3]].join(',');
|
||
let looped = '';
|
||
for (const v of [7]) looped += v;
|
||
return spread + '|' + looped;
|
||
} finally { Array.prototype[Symbol.iterator] = old; }
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, r), "42|42");
|
||
}
|
||
|
||
#[test]
|
||
fn for_of_respects_own_iterator_override_internal_paths_unaffected() {
|
||
let mut vm = Vm::new();
|
||
// 红形钉:实例自身属性的 @@iterator 覆盖须生效;slice 走内部元素直读面,
|
||
// 不受覆盖影响(内部路径不观测用户迭代器)。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
const a = [7, 8];
|
||
a[Symbol.iterator] = function* () { yield 99; };
|
||
return [...a].join(',') + '|' + a.slice(0, 2).join(',');
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, r), "99|7,8");
|
||
}
|
||
|
||
#[test]
|
||
fn for_of_throws_when_array_prototype_iterator_deleted() {
|
||
let mut vm = Vm::new();
|
||
// 红形钉:原型 @@iterator 被删除后 GetMethod 取不到方法,迭代入口须抛
|
||
// TypeError(红形为短路直取元素区、静默迭代成功)。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
const old = Array.prototype[Symbol.iterator];
|
||
delete Array.prototype[Symbol.iterator];
|
||
try {
|
||
try { for (const _v of [1, 2]) {} return 'no'; }
|
||
catch (e) { return e.constructor.name; }
|
||
} finally { Array.prototype[Symbol.iterator] = old; }
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, r), "TypeError");
|
||
}
|
||
|
||
#[test]
|
||
fn array_from_falls_back_to_array_like_when_iterator_unresolvable() {
|
||
let mut vm = Vm::new();
|
||
// 红形钉:真集合的 @@iterator 解析为 null/undefined(原型删除、自身置 null)
|
||
// 时,Array.from 须经 GetMethod-可选语义落 array-like 索引读臂,红形为误抛
|
||
// TypeError。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
const old = Array.prototype[Symbol.iterator];
|
||
delete Array.prototype[Symbol.iterator];
|
||
let deleted;
|
||
try { deleted = Array.from([1, 2]).join(','); }
|
||
catch (e) { deleted = e.constructor.name; }
|
||
finally { Array.prototype[Symbol.iterator] = old; }
|
||
const own = [1, 2];
|
||
own[Symbol.iterator] = null;
|
||
let ownNull;
|
||
try { ownNull = Array.from(own).join(','); }
|
||
catch (e) { ownNull = e.constructor.name; }
|
||
return deleted + '|' + ownNull;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, r), "1,2|1,2");
|
||
}
|
||
|
||
#[test]
|
||
fn typed_array_ctor_and_set_fall_back_when_iterator_unresolvable() {
|
||
let mut vm = Vm::new();
|
||
// 红形钉:真集合的 @@iterator 被删除时,TA 构造器(GetMethod-可选语义)
|
||
// 须落 array-like 臂(无 length 得 0 元素),set 恒索引读;红形为误抛
|
||
// TypeError。真数组经构造器恒走元素区直读(不受 @@iterator 解析影响),
|
||
// 一并作回归守卫。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
const old = Map.prototype[Symbol.iterator];
|
||
delete Map.prototype[Symbol.iterator];
|
||
const m = new Map(); m.set(7, 8);
|
||
let ctor;
|
||
try { ctor = new Uint8Array(m).length; }
|
||
catch (e) { ctor = e.constructor.name; }
|
||
let setResult;
|
||
const t = new Uint8Array([5, 6]);
|
||
try { t.set(m); setResult = t.join(','); }
|
||
catch (e) { setResult = e.constructor.name; }
|
||
let arrCtor;
|
||
try { arrCtor = new Uint8Array([1, 2, 3]).join(','); }
|
||
catch (e) { arrCtor = e.constructor.name; }
|
||
Map.prototype[Symbol.iterator] = old;
|
||
return ctor + '|' + setResult + '|' + arrCtor;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, r), "0|5,6|1,2,3");
|
||
}
|
||
|
||
#[test]
|
||
fn typed_array_set_reads_array_like_source_by_index() {
|
||
let mut vm = Vm::new();
|
||
// 红形钉:TA.set 对非 TypedArray 源恒按 array-like 索引读,不咨询源的
|
||
// @@iterator(即便其可调用);红形为走用户迭代器得 99 填充。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
const src = { 0: 7, 1: 8, length: 2 };
|
||
src[Symbol.iterator] = function* () { yield 99; };
|
||
const t = new Uint8Array(4);
|
||
t.set(src);
|
||
return t.join(',');
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, r), "7,8,0,0");
|
||
}
|
||
|
||
#[test]
|
||
fn array_from_throws_when_iterator_not_callable() {
|
||
let mut vm = Vm::new();
|
||
// 红形钉:GetMethod-可选入口(Array.from)对"非空不可调用" @@iterator 须抛
|
||
// TypeError(GetMethod 步 4),红形为静默回退 array-like 得元素。自身置
|
||
// 对象/原始值两形皆红;null 仍走 array-like(对照不抛)。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
const o = { length: 2, 0: 1, 1: 2 };
|
||
o[Symbol.iterator] = {};
|
||
let objErr;
|
||
try { objErr = Array.from(o).join(','); }
|
||
catch (e) { objErr = e.constructor.name; }
|
||
o[Symbol.iterator] = 5;
|
||
let primErr;
|
||
try { primErr = Array.from(o).join(','); }
|
||
catch (e) { primErr = e.constructor.name; }
|
||
o[Symbol.iterator] = null;
|
||
let nullFb;
|
||
try { nullFb = Array.from(o).join(','); }
|
||
catch (e) { nullFb = e.constructor.name; }
|
||
return objErr + '|' + primErr + '|' + nullFb;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, r), "TypeError|TypeError|1,2");
|
||
}
|
||
|
||
#[test]
|
||
fn typed_array_ctor_throws_when_iterator_not_callable() {
|
||
let mut vm = Vm::new();
|
||
// 红形钉:TA 构造器(GetMethod-可选入口)对"非空不可调用" @@iterator 须抛
|
||
// TypeError,红形为静默回退 array-like(无 length 得 0 元素)。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
const o = { length: 2, 0: 1, 1: 2 };
|
||
o[Symbol.iterator] = {};
|
||
let objErr;
|
||
try { objErr = new Uint8Array(o).length; }
|
||
catch (e) { objErr = e.constructor.name; }
|
||
o[Symbol.iterator] = true;
|
||
let boolErr;
|
||
try { boolErr = new Uint8Array(o).length; }
|
||
catch (e) { boolErr = e.constructor.name; }
|
||
return objErr + '|' + boolErr;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, r), "TypeError|TypeError");
|
||
}
|
||
|
||
#[test]
|
||
fn typed_array_from_throws_when_iterator_not_callable() {
|
||
let mut vm = Vm::new();
|
||
// 红形钉:TypedArray.from(GetMethod-可选入口)对"非空不可调用" @@iterator
|
||
// 须抛 TypeError,红形为静默回退 array-like。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
const o = { length: 2, 0: 1, 1: 2 };
|
||
o[Symbol.iterator] = {};
|
||
let objErr;
|
||
try { objErr = Uint8Array.from(o).length; }
|
||
catch (e) { objErr = e.constructor.name; }
|
||
o[Symbol.iterator] = 42;
|
||
let primErr;
|
||
try { primErr = Uint8Array.from(o).length; }
|
||
catch (e) { primErr = e.constructor.name; }
|
||
return objErr + '|' + primErr;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(to_str(&vm, r), "TypeError|TypeError");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_binding_surface() {
|
||
// 绑定面:name/length/原型/描述符/非构造/instanceof/结果原型。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"typeof Iterator.zip === 'function' && Iterator.zip.name === 'zip' \
|
||
&& Iterator.zip.length === 1",
|
||
true,
|
||
),
|
||
("Object.getPrototypeOf(Iterator.zip) === Function.prototype", true),
|
||
(
|
||
"var d = Object.getOwnPropertyDescriptor(Iterator, 'zip'); \
|
||
d.writable === true && d.enumerable === false && d.configurable === true",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { new Iterator.zip([]); } catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
("var it = Iterator.zip([]); it instanceof Iterator", true),
|
||
(
|
||
"var it = Iterator.zip([]); var p = Object.getPrototypeOf(it); \
|
||
p[Symbol.toStringTag] === 'Iterator Helper' \
|
||
&& Object.getPrototypeOf(p) === Iterator.prototype",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_options_and_mode_validation() {
|
||
// options 原始值 TypeError;iterables 原始值 TypeError 且 options 不读;
|
||
// mode 严格字符串校验(String 包装拒绝、toPrimitive 不触发、无效 mode 不读 padding)。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"Iterator.zip([], undefined) instanceof Iterator && Iterator.zip([], {}) instanceof Iterator",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { Iterator.zip([], null); } catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { Iterator.zip(1); } catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
(
|
||
"var badOptions = { get mode() { throw new RangeError('x'); } }; \
|
||
var ok = false; try { Iterator.zip(1, badOptions); } catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { Iterator.zip([], {mode: 'short'}); } catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { Iterator.zip([], {mode: new String('shortest')}); } \
|
||
catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { Iterator.zip([], {mode: {toString() { throw new RangeError('x'); }, \
|
||
valueOf() { throw new RangeError('y'); }, [Symbol.toPrimitive]() { throw new RangeError('z'); }}}); } \
|
||
catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
(
|
||
"var opts = { mode: 'bad', get padding() { throw new RangeError('x'); } }; \
|
||
var ok = false; try { Iterator.zip([], opts); } catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
(
|
||
"Iterator.zip([], {mode: 'longest', padding: []}) instanceof Iterator \
|
||
&& Iterator.zip([], {mode: 'strict'}) instanceof Iterator",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { Iterator.zip([], {mode: 'longest', padding: 42}); } \
|
||
catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_iterables_validation() {
|
||
// iterables 全 GetIterator 无鸭子回退;字符串对象经 @@iterator 接受,字符串原始值拒绝。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"var ok = false; try { Iterator.zip(Object.create(null)); } \
|
||
catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { Iterator.zip(Object.create(null, \
|
||
{next: {value: function(){}}, return: {value: function(){}}})); } \
|
||
catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
(
|
||
"var r = Array.from(Iterator.zip([Object('abc'), Object('123')])); \
|
||
r.length === 3 && r[0][0] === 'a' && r[0][1] === '1' && r[2][0] === 'c' && r[2][1] === '3'",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { Iterator.zip([['a','b'], 'cd']); } catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_collection_abrupt_close_order() {
|
||
// 收集期骤发:步进骤发只关已收集(inputIter 不关);元素骤发逆序关含 inputIter。
|
||
let mut vm = Vm::new();
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
var first = { next() { log.push('unexpected next'); },
|
||
return() { log.push('close first'); throw new RangeError('x'); } };
|
||
var second = { next() { log.push('unexpected next'); },
|
||
return() { log.push('close second'); throw new RangeError('y'); } };
|
||
var elements = [first, second];
|
||
var elementsIter = elements.values();
|
||
var iterables = { [Symbol.iterator]() { return this; },
|
||
next() { var r = elementsIter.next(); if (r.done) { throw new ReferenceError('boom'); } return r; },
|
||
return() { log.push('close iterables'); throw new RangeError('z'); } };
|
||
var threw = false;
|
||
try { Iterator.zip(iterables); } catch (e) { threw = e instanceof ReferenceError; }
|
||
return threw + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"close second\",\"close first\"]",
|
||
"step abrupt closes collected only, inputIter untouched"
|
||
);
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
var first = { next() {}, return() { log.push('close first'); throw new RangeError('x'); } };
|
||
var second = { next() {}, return() { log.push('close second'); throw new RangeError('y'); } };
|
||
var bad = { [Symbol.iterator]() { throw new ReferenceError('boom'); } };
|
||
var elements = [first, second, bad];
|
||
var elementsIter = elements.values();
|
||
var iterables = { [Symbol.iterator]() { return this; },
|
||
next() { return elementsIter.next(); },
|
||
return() { log.push('close iterables'); throw new RangeError('z'); } };
|
||
var threw = false;
|
||
try { Iterator.zip(iterables); } catch (e) { threw = e instanceof ReferenceError; }
|
||
return threw + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"close second\",\"close first\",\"close iterables\"]",
|
||
"flattenable abrupt closes inputIter last in reverse order"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_shortest_basic_values() {
|
||
// shortest 基本值、done、空迭代器、单迭代器、短输入收口、结果数组逐次新对象。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"var it = Iterator.zip([[1,2,3],[4,5,6]]); \
|
||
var r1 = it.next(); var r2 = it.next(); var r3 = it.next(); var r4 = it.next(); \
|
||
r1.value[0] === 1 && r1.value[1] === 4 && r2.value[0] === 2 && r2.value[1] === 5 \
|
||
&& r3.value[0] === 3 && r3.value[1] === 6 && r4.done === true",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.zip([[1,2,3],[4,5,6]]); \
|
||
var a = it.next().value; var b = it.next().value; a !== b",
|
||
true,
|
||
),
|
||
("Iterator.zip([]).next().done === true", true),
|
||
(
|
||
"var it = Iterator.zip([[1,2]]); var r = it.next(); \
|
||
r.value.length === 1 && r.value[0] === 1 && it.next().value[0] === 2 && it.next().done === true",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.zip([[1,2],[3,4,5]]); \
|
||
it.next().value[0] === 1 && it.next().value[0] === 2 && it.next().done === true",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_shortest_done_close_order() {
|
||
// shortest done:done 迭代器不关不读值,逆序关其余开放迭代器,关闭错误传播。
|
||
let mut vm = Vm::new();
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
var first = { next() { log.push('first next'); return {done: false}; },
|
||
return() { log.push('first return'); return {}; } };
|
||
var second = { next() { log.push('second next'); return {done: true}; },
|
||
return() { log.push('second return'); } };
|
||
var third = { next() { log.push('third next'); },
|
||
return() { log.push('third return'); throw new RangeError('x'); } };
|
||
var fourth = { next() { log.push('fourth next'); },
|
||
return() { log.push('fourth return'); throw new ReferenceError('boom'); } };
|
||
var it = Iterator.zip([first, second, third, fourth], {mode: 'shortest'});
|
||
var threw = false;
|
||
try { it.next(); } catch (e) { threw = e instanceof ReferenceError; }
|
||
return threw + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"first next\",\"second next\",\"fourth return\",\"third return\",\"first return\"]",
|
||
"done iterator stays open, others closed in reverse index order"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_abrupt_close_all_original_wins() {
|
||
// 步进骤发与 return 关闭:逆序关全部开放迭代器,原值恒胜出。
|
||
let mut vm = Vm::new();
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
var first = { next() { log.push('first next'); return {done: false}; },
|
||
return() { log.push('first return'); throw new RangeError('x'); } };
|
||
var second = { next() { log.push('second next'); return {done: false}; },
|
||
return() { log.push('second return'); throw new ReferenceError('boom'); } };
|
||
var third = { next() { log.push('third next'); return {done: false}; },
|
||
return() { log.push('third return'); return {}; } };
|
||
var it = Iterator.zip([first, second, third]);
|
||
it.next();
|
||
var threw = false;
|
||
try { it.return(); } catch (e) { threw = e instanceof ReferenceError; }
|
||
return threw + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"first next\",\"second next\",\"third next\",\"third return\",\"second return\",\"first return\"]",
|
||
"return closes all open iterators in reverse order, original value wins"
|
||
);
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var closed = 0;
|
||
var underlying = { next() { return {value: 1, done: false}; },
|
||
return() { closed++; return {}; } };
|
||
var it = Iterator.zip([underlying]);
|
||
it.next();
|
||
var ok = false;
|
||
try { it.throw(42); } catch (e) { ok = e === 42; }
|
||
return ok && closed === 1 && it.next().done === true;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert!(r.as_bool(), "throw closes all open iterators then rethrows value");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_suspended_start_return() {
|
||
// suspended-start return:逆序关全部底层,next 不转发,嵌套调用短路。
|
||
let mut vm = Vm::new();
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var returnCallCount = 0;
|
||
var underlying = { next() { throw new RangeError('unexpected next'); },
|
||
return() {
|
||
returnCallCount += 1;
|
||
var result = it.next();
|
||
if (result.value !== undefined || result.done !== true) { throw new RangeError('bad nested next'); }
|
||
return {};
|
||
} };
|
||
var it = Iterator.zip([underlying]);
|
||
var result = it.return();
|
||
return result.value === undefined && result.done === true && returnCallCount === 1;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert!(r.as_bool(), "suspended-start return closes all underlying iterators");
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var returnCallCount = 0;
|
||
var underlying = { next() { throw new RangeError('unexpected next'); },
|
||
return() {
|
||
returnCallCount += 1;
|
||
var result = it.return();
|
||
if (result.value !== undefined || result.done !== true) { throw new RangeError('bad nested return'); }
|
||
return {};
|
||
} };
|
||
var it = Iterator.zip([underlying]);
|
||
var result = it.return();
|
||
return result.value === undefined && result.done === true && returnCallCount === 1;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert!(r.as_bool(), "suspended-start return with nested return short-circuits to done");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_reentrancy_guard() {
|
||
// executing 重入守卫:底层 return 回调内再调 next/return → TypeError。
|
||
let mut vm = Vm::new();
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var returnCallCount = 0;
|
||
var underlying = { next() { return {value: 123, done: false}; },
|
||
return() {
|
||
returnCallCount += 1;
|
||
var ok = false;
|
||
try { it.next(); } catch (e) { ok = e instanceof TypeError; }
|
||
if (!ok) { throw new RangeError('no TypeError'); }
|
||
return {};
|
||
} };
|
||
var it = Iterator.zip([underlying]);
|
||
var r = it.next();
|
||
if (r.value[0] !== 123) { throw new RangeError('bad value'); }
|
||
var result = it.return();
|
||
return result.done === true && returnCallCount === 1;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert!(r.as_bool(), "executing reentry on next throws TypeError");
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var returnCallCount = 0;
|
||
var underlying = { next() { return {value: 123, done: false}; },
|
||
return() {
|
||
returnCallCount += 1;
|
||
var ok = false;
|
||
try { it.return(); } catch (e) { ok = e instanceof TypeError; }
|
||
if (!ok) { throw new RangeError('no TypeError'); }
|
||
return {};
|
||
} };
|
||
var it = Iterator.zip([underlying]);
|
||
var r = it.next();
|
||
if (r.value[0] !== 123) { throw new RangeError('bad value'); }
|
||
var result = it.return();
|
||
return result.done === true && returnCallCount === 1;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert!(r.as_bool(), "executing reentry on return throws TypeError");
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_longest_basic_values_and_padding() {
|
||
// longest 基本值与 padding 落点:无 padding 填 undefined、padding 数组、
|
||
// padding 迭代器(耗尽余位 undefined、无限迭代器步进至多 iterCount 次)。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"var it = Iterator.zip([[1,2,3],[4,5]], {mode: 'longest'}); \
|
||
var r1 = it.next().value; var r2 = it.next().value; var r3 = it.next(); \
|
||
r1[0] === 1 && r1[1] === 4 && r2[0] === 2 && r2[1] === 5 \
|
||
&& r3.value[0] === 3 && r3.value[1] === undefined && r3.done === false \
|
||
&& it.next().done === true",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.zip([[1,2],[3,4,5]], {mode: 'longest', padding: ['p','q']}); \
|
||
var r1 = it.next().value; var r2 = it.next().value; var r3 = it.next(); \
|
||
r1[0] === 1 && r1[1] === 3 && r2[0] === 2 && r2[1] === 4 \
|
||
&& r3.value[0] === 'p' && r3.value[1] === 5 && it.next().done === true",
|
||
true,
|
||
),
|
||
(
|
||
"var it = Iterator.zip([[1,2],[3,4,5]], {mode: 'longest', padding: []}); \
|
||
it.next(); it.next(); var r3 = it.next().value; r3[0] === undefined && r3[1] === 5 \
|
||
&& it.next().done === true",
|
||
true,
|
||
),
|
||
(
|
||
"var pads = { [Symbol.iterator]() { return {v: 0, next() { this.v++; return {value: this.v, done: false}; }}; } }; \
|
||
var it = Iterator.zip([[1,2],[3,4,5]], {mode: 'longest', padding: pads}); \
|
||
it.next(); it.next(); var r3 = it.next().value; r3[0] === 1 && r3[1] === 5",
|
||
true,
|
||
),
|
||
(
|
||
"var steps = 0; var rets = 0; \
|
||
var infinite = { [Symbol.iterator]() { var self = this; return { next() { steps++; return {value: steps, done: false}; }, \
|
||
return() { rets++; return {done: true}; } }; } }; \
|
||
var it = Iterator.zip([[1,2],[3]], {mode: 'longest', padding: infinite}); \
|
||
it.next(); it.next(); it.next(); \
|
||
steps === 2 && rets === 1",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_strict_basic_values() {
|
||
// strict 基本值:等长正常收口,不等长 done 位抛 TypeError。
|
||
let mut vm = Vm::new();
|
||
let bool_cases = [
|
||
(
|
||
"var it = Iterator.zip([[1,2,3],[4,5,6]], {mode: 'strict'}); \
|
||
it.next().value[0] === 1 && it.next().value[1] === 5 && it.next().value[0] === 3 \
|
||
&& it.next().done === true",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { \
|
||
var it = Iterator.zip([[1,2,3],[4,5]], {mode: 'strict'}); \
|
||
it.next(); it.next(); it.next(); \
|
||
} catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
(
|
||
"var ok = false; try { \
|
||
var it = Iterator.zip([[1,2],[3,4,5]], {mode: 'strict'}); \
|
||
it.next(); it.next(); it.next(); \
|
||
} catch (e) { ok = e instanceof TypeError; } ok",
|
||
true,
|
||
),
|
||
];
|
||
for (src, expected) in bool_cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_strict_done_close_order() {
|
||
// strict done 位 i 非 0:逆序关全部开放迭代器(throw 完成,return 错误被吞),
|
||
// 抛新 TypeError;done 位迭代器不关。
|
||
let mut vm = Vm::new();
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
var first = { next() { log.push('first next'); return {done: false}; },
|
||
return() { log.push('first return'); throw new RangeError('x'); } };
|
||
var second = { next() { log.push('second next'); return {done: true}; },
|
||
return() { log.push('second return'); } };
|
||
var third = { next() { log.push('third next'); },
|
||
return() { log.push('third return'); throw new ReferenceError('boom'); } };
|
||
var it = Iterator.zip([first, second, third], {mode: 'strict'});
|
||
var threw = false;
|
||
try { it.next(); } catch (e) { threw = e instanceof TypeError; }
|
||
return threw + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"first next\",\"second next\",\"third return\",\"first return\"]",
|
||
"strict i not zero: close all open in reverse, new TypeError wins"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_strict_i_is_zero_scan() {
|
||
// strict done 位 i 为 0:升序余者扫描(只读 done),三臂——全耗尽正常收口、
|
||
// 首个未耗尽逆序关全部开放抛新 TypeError、骤发逆序关全部透传原值。
|
||
let mut vm = Vm::new();
|
||
// 全耗尽:正常收口,无 return 调用。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
function makeIter(name) {
|
||
return { next() { log.push(name + ' next'); return {done: true}; },
|
||
return() { log.push(name + ' return'); } };
|
||
}
|
||
var it = Iterator.zip([makeIter('a'), makeIter('b'), makeIter('c')], {mode: 'strict'});
|
||
var result = it.next();
|
||
return (result.done === true) + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"a next\",\"b next\",\"c next\"]",
|
||
"strict i zero all exhausted: normal completion, no return calls"
|
||
);
|
||
// 首个未耗尽:逆序关全部开放(含已关位不重调 next),抛新 TypeError。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
var first = { next() { log.push('first next'); return {done: true}; },
|
||
return() { log.push('first return'); } };
|
||
var second = { next() { log.push('second next'); return {done: true}; },
|
||
return() { log.push('second return'); } };
|
||
var third = { next() { log.push('third next'); return {done: false}; },
|
||
return() { log.push('third return'); throw new RangeError('x'); } };
|
||
var fourth = { next() { log.push('fourth next'); },
|
||
return() { log.push('fourth return'); throw new ReferenceError('boom'); } };
|
||
var it = Iterator.zip([first, second, third, fourth], {mode: 'strict'});
|
||
var threw = false;
|
||
try { it.next(); } catch (e) { threw = e instanceof TypeError; }
|
||
return threw + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"first next\",\"second next\",\"third next\",\"fourth return\",\"third return\"]",
|
||
"strict i zero not exhausted: close all open in reverse, new TypeError wins"
|
||
);
|
||
// 骤发:首个骤发逆序关全部开放(原值胜出),骤发位不关。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
var first = { next() { log.push('first next'); return {done: true}; },
|
||
return() { log.push('first return'); } };
|
||
var second = { next() { log.push('second next'); throw new RangeError('boom'); },
|
||
return() { log.push('second return'); } };
|
||
var third = { next() { log.push('third next'); },
|
||
return() { log.push('third return'); throw new ReferenceError('x'); } };
|
||
var fourth = { next() { log.push('fourth next'); },
|
||
return() { log.push('fourth return'); throw new ReferenceError('y'); } };
|
||
var it = Iterator.zip([first, second, third, fourth], {mode: 'strict'});
|
||
var threw = false;
|
||
try { it.next(); } catch (e) { threw = e instanceof RangeError; }
|
||
return threw + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"first next\",\"second next\",\"fourth return\",\"third return\"]",
|
||
"strict i zero scan abrupt: close all open in reverse, original value wins"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_longest_abrupt_close_order() {
|
||
// longest 步进骤发:清位后逆序关其余开放迭代器(原值胜出),done 位不关。
|
||
let mut vm = Vm::new();
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
var empty = { next() { log.push('empty next'); return {done: true}; },
|
||
return() { log.push('empty return'); } };
|
||
var first = { next() { log.push('first next'); throw new RangeError('boom'); },
|
||
return() { log.push('first return'); } };
|
||
var second = { next() { log.push('second next'); },
|
||
return() { log.push('second return'); throw new ReferenceError('x'); } };
|
||
var third = { next() { log.push('third next'); },
|
||
return() { log.push('third return'); throw new ReferenceError('y'); } };
|
||
var it = Iterator.zip([empty, first, second, third], {mode: 'longest'});
|
||
var threw = false;
|
||
try { it.next(); } catch (e) { threw = e instanceof RangeError; }
|
||
return threw + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"empty next\",\"first next\",\"third return\",\"second return\"]",
|
||
"longest abrupt: done slot not closed, rest closed in reverse, original wins"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_padding_precompute_abrupt() {
|
||
// padding 预计算骤发(GetIterator 与 next/done/value getter):逆序关元素
|
||
// 迭代器,padding 迭代器不关,透传原值。
|
||
let mut vm = Vm::new();
|
||
// GetIterator 骤发(Symbol.iterator 抛错)。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
var first = { next() { log.push('unexpected next'); },
|
||
return() { log.push('first return'); throw new ReferenceError('x'); } };
|
||
var second = { next() { log.push('unexpected next'); },
|
||
return() { log.push('second return'); throw new ReferenceError('y'); } };
|
||
var third = { next() { log.push('unexpected next'); },
|
||
get return() { log.push('third return'); throw new ReferenceError('z'); } };
|
||
var padding = { [Symbol.iterator]() { throw new RangeError('boom'); } };
|
||
var threw = false;
|
||
try { Iterator.zip([first, second, third], {mode: 'longest', padding}); }
|
||
catch (e) { threw = e instanceof RangeError; }
|
||
return threw + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"third return\",\"second return\",\"first return\"]",
|
||
"padding GetIterator abrupt: close element iterators in reverse, padding not closed"
|
||
);
|
||
// 步进骤发(next 抛错 / done getter 抛错 / value getter 抛错)。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
function makeElements() {
|
||
return [
|
||
{ next() { log.push('unexpected next'); },
|
||
return() { log.push('first return'); throw new ReferenceError('x'); } },
|
||
{ next() { log.push('unexpected next'); },
|
||
return() { log.push('second return'); throw new ReferenceError('y'); } },
|
||
{ next() { log.push('unexpected next'); },
|
||
get return() { log.push('third return'); throw new ReferenceError('z'); } }
|
||
];
|
||
}
|
||
var padding = { [Symbol.iterator]() { return this; },
|
||
next() { throw new RangeError('boom'); } };
|
||
var threw = false;
|
||
try { Iterator.zip(makeElements(), {mode: 'longest', padding}); }
|
||
catch (e) { threw = e instanceof RangeError; }
|
||
var l1 = JSON.stringify(log);
|
||
log.length = 0;
|
||
var padding2 = { [Symbol.iterator]() { return this; },
|
||
next() { return { get done() { throw new RangeError('boom'); } }; } };
|
||
try { Iterator.zip(makeElements(), {mode: 'longest', padding: padding2}); }
|
||
catch (e) { threw = threw && e instanceof RangeError; }
|
||
var l2 = JSON.stringify(log);
|
||
log.length = 0;
|
||
var padding3 = { [Symbol.iterator]() { return this; },
|
||
next() { return { done: false, get value() { throw new RangeError('boom'); } }; } };
|
||
try { Iterator.zip(makeElements(), {mode: 'longest', padding: padding3}); }
|
||
catch (e) { threw = threw && e instanceof RangeError; }
|
||
var l3 = JSON.stringify(log);
|
||
return threw + '|' + l1 + '|' + l2 + '|' + l3;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"third return\",\"second return\",\"first return\"]|[\"third return\",\"second return\",\"first return\"]|[\"third return\",\"second return\",\"first return\"]",
|
||
"padding step abrupt (next/done/value): close element iterators in reverse, original wins"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn iterator_zip_padding_not_exhausted_close() {
|
||
// padding 未耗尽收口:正常关 padding 迭代器,关闭骤发逆序关元素迭代器透传。
|
||
let mut vm = Vm::new();
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var log = [];
|
||
var first = { next() { log.push('unexpected next'); },
|
||
return() { log.push('first return'); throw new ReferenceError('x'); } };
|
||
var second = { next() { log.push('unexpected next'); },
|
||
return() { log.push('second return'); throw new ReferenceError('y'); } };
|
||
var third = { next() { log.push('unexpected next'); },
|
||
get return() { log.push('third return'); throw new ReferenceError('z'); } };
|
||
var padding = { [Symbol.iterator]() { return this; },
|
||
next() { return {done: false}; },
|
||
return() { log.push('padding return'); throw new RangeError('boom'); } };
|
||
var threw = false;
|
||
try { Iterator.zip([first, second, third], {mode: 'longest', padding}); }
|
||
catch (e) { threw = e instanceof RangeError; }
|
||
return threw + '|' + JSON.stringify(log);
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(
|
||
to_str(&vm, r),
|
||
"true|[\"padding return\",\"third return\",\"second return\",\"first return\"]",
|
||
"padding not exhausted: normal close, abrupt close passes through element iterators"
|
||
);
|
||
// 耗尽:不关 padding 迭代器。
|
||
let r = eval(
|
||
&mut vm,
|
||
"(() => {
|
||
var rets = 0;
|
||
var padding = { [Symbol.iterator]() { return this; },
|
||
next() { if (this.v === undefined) { this.v = 1; return {done: false, value: 'p0'}; } return {done: true}; },
|
||
return() { rets++; return {done: true}; } };
|
||
var it = Iterator.zip([[1,2],[3]], {mode: 'longest', padding});
|
||
var r1 = it.next(); var r2 = it.next();
|
||
return r1.value[0] === 1 && r1.value[1] === 3 && r2.value[0] === 2 \
|
||
&& r2.value[1] === undefined && rets === 0 && it.next().done === true;
|
||
})()",
|
||
)
|
||
.unwrap();
|
||
assert!(r.as_bool(), "padding exhausted exactly at iterCount: no close, done landing undefined");
|
||
}
|