mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 迭代器包装器零参转发加关闭期惰性return读取
This commit is contained in:
Generated
+19
-19
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1226,7 +1226,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1235,7 +1235,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1255,7 +1255,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1269,7 +1269,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1284,7 +1284,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_engine"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1296,7 +1296,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1305,7 +1305,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1319,7 +1319,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1331,7 +1331,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1340,7 +1340,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1355,7 +1355,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1371,7 +1371,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1384,7 +1384,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1402,7 +1402,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1415,7 +1415,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.13.27"
|
||||
version = "0.13.28"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -1230,11 +1230,13 @@ pub(crate) fn try_make_iterator_inner_proto<H: VmHost>(
|
||||
/// `@@iterator` 返回自身)。
|
||||
/// 2. 写入 `__inner__`/`__index__` 槽;鸭子回退路径把创建时读到的 `next` 闭包
|
||||
/// 缓存进 `__next__` 槽,消费期直读槽不重触发 getter。
|
||||
/// 3. 挂 `next` 方法;`bind_return` 且内层有可调用 `return` 时条件暴露 `return`。
|
||||
/// 3. 挂 `next` 方法;`bind_return` 且内层是对象时恒挂 `return` 方法
|
||||
/// (内层 return 在关闭期惰性读取,构造期不触碰其 getter)。
|
||||
///
|
||||
/// # 边界
|
||||
/// - `bind_return` 控制是否暴露 `return` 方法(for-of/解构的 IteratorClose 需要,
|
||||
/// `yield*` 委托不需要且须避免创建时访问内层 return getter)。
|
||||
/// `yield*` 委托不需要);内层 return 一律关闭期惰性 GetMethod,构造期不触碰
|
||||
/// 其 getter,getter 抛错与不可调用判定在关闭期发生。
|
||||
/// - 本函数不解析 `@@iterator`:`get_iterator` 派生内层,或默认迭代器函数直接以
|
||||
/// 已 ToString 的串为内层构造(其语义只由 `this` 决定,与属性表状态解耦)。
|
||||
pub(crate) fn build_iterator_wrapper<H: VmHost>(
|
||||
@@ -1264,22 +1266,28 @@ pub(crate) fn build_iterator_wrapper<H: VmHost>(
|
||||
let wrapper_next = make_native_function(vm, "next", iterator_wrapper_next::<H> as *const (), 0);
|
||||
vm.set_or_create_prop_value(wrapper_obj, next_si, wrapper_next);
|
||||
|
||||
// for-of/解构的 IteratorClose 需要 return 方法:条件暴露(内层有可调用 return 时)。
|
||||
// for-of/解构的 IteratorClose 需要 return 方法:bind_return 且内层是对象时恒挂,
|
||||
// 内层 return 由包装器 return 方法在关闭期惰性 GetMethod(getter 抛错传播、
|
||||
// 不可调用抛 TypeError、undefined/null 无操作)。
|
||||
// `yield*` 委托(bind_return=false)不绑定,转发时对内层延迟 GetMethod。
|
||||
let return_si = vm.perm_intern("return");
|
||||
if bind_return && inner.is_object() {
|
||||
let inner_obj = unsafe { &*inner.as_js_object_ptr() };
|
||||
if let Ok(return_fn) = vm.ordinary_get(inner_obj, return_si, inner) {
|
||||
if is_callable(return_fn) {
|
||||
let wrapper_return = make_native_function(vm, "return", iterator_wrapper_return::<H> as *const (), 0);
|
||||
vm.set_or_create_prop_value(wrapper_obj, return_si, wrapper_return);
|
||||
}
|
||||
}
|
||||
let wrapper_return = make_native_function(vm, "return", iterator_wrapper_return::<H> as *const (), 0);
|
||||
vm.set_or_create_prop_value(wrapper_obj, return_si, wrapper_return);
|
||||
}
|
||||
|
||||
JsValue::from_js_object(wrapper)
|
||||
}
|
||||
|
||||
/// 迭代器包装器的 `return` 方法:关闭期惰性读取内层 `return`(完整 GetMethod
|
||||
/// 步序)并转发调用,默认零参。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 读 `__inner__` 槽;this 非对象或槽缺失/非对象时返回 undefined(无操作)。
|
||||
/// 2. GetMethod(inner, "return"):getter 抛错透传原异常;undefined/null 返回空
|
||||
/// 对象(无操作,使调用方步 7 的对象校验通过);已定义不可调用值抛 TypeError。
|
||||
/// 3. 调用方显式传参时转发该实参(Iterator.prototype.return 的 return(v) 语义),
|
||||
/// 否则零参调用(IteratorClose 步 4c);结果原样返回,步 7 由调用方执行。
|
||||
fn iterator_wrapper_return<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !this_val.is_object() {
|
||||
@@ -1291,16 +1299,29 @@ fn iterator_wrapper_return<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Ok(inner) if inner.is_object() => inner,
|
||||
_ => return NativeResult::Ok(JsValue::undefined()),
|
||||
};
|
||||
// 延迟 GetMethod:内层无 return 方法时返回 undefined(IteratorClose 跳过)。
|
||||
// 延迟 GetMethod:内层 return 在关闭期读取,getter 抛错透传原异常。
|
||||
let inner_obj = unsafe { &*inner.as_js_object_ptr() };
|
||||
let return_si = vm.perm_intern("return");
|
||||
let return_fn = match vm.ordinary_get(inner_obj, return_si, inner) {
|
||||
Ok(f) if is_callable(f) => f,
|
||||
_ => return NativeResult::Ok(JsValue::undefined()),
|
||||
// undefined/null:GetMethod 返回 unused,无操作,返空对象使调用方步 7 通过。
|
||||
Ok(f) if f.is_undefined() || f.is_null() => {
|
||||
let object_proto = vm.builtin_proto(ProtoKind::ObjectProto);
|
||||
let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
|
||||
return NativeResult::Ok(JsValue::from_js_object(obj));
|
||||
}
|
||||
// 已定义不可调用值:GetMethod 抛 TypeError。
|
||||
Ok(_) => return NativeResult::Err(crate::error::create_type_error(vm, "iterator return is not a function")),
|
||||
Err(err) => return NativeResult::Err(engine_error(vm, &err)),
|
||||
};
|
||||
// 转发调用实参(`yield*` 委托的 return(v) 语义),缺省为 undefined。
|
||||
let arg = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
match vm.call_function_sync(return_fn, inner, &[arg]) {
|
||||
// 默认零参(IteratorClose 步 4c);调用方显式传参时转发(Iterator.prototype.return
|
||||
// 的 return(v) 语义)。结果原样返回,步 7 由调用方 IteratorClose 执行。
|
||||
let result = if args.len() > 1 {
|
||||
vm.call_function_sync(return_fn, inner, &[vm.reg(args[1])])
|
||||
} else {
|
||||
vm.call_function_sync(return_fn, inner, &[])
|
||||
};
|
||||
match result {
|
||||
Ok(result) => NativeResult::Ok(result),
|
||||
Err(err) => match vm.take_uncaught_value() {
|
||||
Some(original) => NativeResult::Err(original),
|
||||
@@ -1363,9 +1384,14 @@ pub fn iterator_wrapper_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
}
|
||||
}
|
||||
};
|
||||
// 转发调用实参(`yield*` 委托的 next(v) 语义),缺省为 undefined。
|
||||
let arg = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
return match vm.call_function_sync(next, inner, &[arg]) {
|
||||
// 默认零参(IteratorNext 步 4 零参调用);调用方显式传参时转发(Iterator.
|
||||
// prototype.next 的 next(v) 语义)。
|
||||
let result = if args.len() > 1 {
|
||||
vm.call_function_sync(next, inner, &[vm.reg(args[1])])
|
||||
} else {
|
||||
vm.call_function_sync(next, inner, &[])
|
||||
};
|
||||
return match result {
|
||||
Ok(result) => NativeResult::Ok(result),
|
||||
// 透传原始抛出的值(任意类型)而非重新包装成 TypeError,
|
||||
// 使外围 try/catch 能看到真正的错误。
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
//! 迭代器包装器(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"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user