Files
OxideJS-New/crates/oxide_builtins/tests/iterator_wrapper_tests.rs
T

152 lines
5.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 迭代器包装器(make_iterator_for_value 建出的统一迭代器对象)的
//! return/next 转发语义测试:零参转发、关闭期惰性 GetMethod(getter 抛错
//! 传播、不可调用 TypeError、null 无操作)、构造期不触碰内层 return getter。
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
fn eval(source: &str) -> String {
let allocator = Allocator::default();
let program = match oxide_parser::parse(&allocator, source) {
Ok(p) => p,
Err(e) => return format!("parse error: {}", e[0].message),
};
let module = match Compiler::new().compile(&program) {
Ok(m) => m,
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
}
}
#[test]
fn wrapper_return_forwards_zero_args() {
// 包装器 return 默认零参转发内层 return(IteratorClose 步 4c)。
assert_eq!(
eval(
"var it={next:function(){return {value:1,done:false};},\
return:function(){globalThis.n=arguments.length;return {};}};\
for(var v of it){break;}globalThis.n===0"
),
"true",
"wrapper return must call inner return with zero arguments"
);
}
#[test]
fn wrapper_next_forwards_zero_args() {
// 包装器 next 默认零参转发内层 next(IteratorNext 步 4)。
assert_eq!(
eval(
"var it={next:function(){globalThis.n=arguments.length;\
return {value:1,done:true};}};\
for(var v of it){}globalThis.n===0"
),
"true",
"wrapper next must call inner next with zero arguments"
);
}
#[test]
fn wrapper_return_getter_throw_propagates_on_normal_close() {
// 正常完成关闭时,内层 return getter 抛错必须传播(关闭期惰性 GetMethod)。
assert_eq!(
eval(
"var it={next:function(){return {value:1,done:false};},\
get return(){throw new TypeError('getter');}};\
globalThis.r=false;\
try{for(var v of it){break;}}\
catch(e){globalThis.r=(e.name==='TypeError'&&e.message==='getter');}\
globalThis.r"
),
"true",
"getter throw on inner return must propagate on normal-completion close"
);
}
#[test]
fn wrapper_return_non_callable_throws_type_error() {
// 正常完成关闭时,内层 return 为已定义不可调用值必须抛 TypeError。
assert_eq!(
eval(
"var it={next:function(){return {value:1,done:false};},return:1};\
globalThis.r=false;\
try{for(var v of it){break;}}\
catch(e){globalThis.r=(e instanceof TypeError);}\
globalThis.r"
),
"true",
"defined non-callable inner return must throw TypeError on close"
);
}
#[test]
fn wrapper_return_null_is_noop() {
// 内层 return 为 null 时关闭无操作(GetMethod 步 2 返回 unused)。
assert_eq!(
eval(
"var it={next:function(){return {value:1,done:false};},return:null};\
var threw=false;\
try{for(var v of it){break;}}catch(e){threw=true;}\
threw===false"
),
"true",
"null inner return must be a no-op on close"
);
}
#[test]
fn wrapper_return_getter_throw_suppressed_on_throw_close() {
// throw 完成关闭时,内层 return getter 抛错被在途异常替代(原异常胜出)。
assert_eq!(
eval(
"var it={next:function(){return {value:1,done:false};},\
get return(){throw new TypeError('getter');}};\
globalThis.r=false;\
try{for(var v of it){throw new Error('body');}}\
catch(e){globalThis.r=(e.name==='Error'&&e.message==='body');}\
globalThis.r"
),
"true",
"getter throw on inner return must be suppressed when close follows a throw"
);
}
#[test]
fn wrapper_return_not_eagerly_bound_at_construction() {
// 构造包装器时不得触碰内层 return getter(计数时点在关闭期)。
assert_eq!(
eval(
"var count=0;\
var it={next:function(){return {value:1,done:false};},\
get return(){count++;throw new TypeError('getter');}};\
var w=Iterator.from(it);count===0"
),
"true",
"wrapper construction must not touch the inner return getter"
);
}
#[test]
fn wrapper_return_getter_called_once_at_close() {
// 内层 return getter 在关闭期恰好被调用一次。
assert_eq!(
eval(
"var count=0;\
var it={next:function(){return {value:1,done:false};},\
get return(){count++;return function(){return {};};}};\
for(var v of it){break;}count===1"
),
"true",
"inner return getter must be invoked exactly once, at close"
);
}