diff --git a/crates/oxide_emit/src/class/method.rs b/crates/oxide_emit/src/class/method.rs index 0b03a6b1..eaea2206 100644 --- a/crates/oxide_emit/src/class/method.rs +++ b/crates/oxide_emit/src/class/method.rs @@ -99,35 +99,23 @@ impl Emitter { extra_uv.push((name, c)); } let method_value = method.value.as_ref(); - let mut method_module = if method_value.r#async { - self.compile_function_body_with_field_hooks_gen( - ¶m_names, - body_stmts, - ctx, - false, - self_binding, - FunctionBodyContext::ClassElement, - None:: Result<(), String>>, - false, - &[], - &extra_uv, - false, - true, - )? - } else { - self.compile_function_body_with_field_hooks( - ¶m_names, - body_stmts, - ctx, - false, - self_binding, - FunctionBodyContext::ClassElement, - None:: Result<(), String>>, - false, - &[], - &extra_uv, - )? - }; + // 生成器/异步/异步生成器方法与普通方法统一走带标志入口:`*m` 置 is_generator, + // `async *m` 同时置 is_generator 与 is_async,VM 据此按生成器协议创建对象并 + // 处理 body 内 SUSPEND/YIELD/异常边界。 + let mut method_module = self.compile_function_body_with_field_hooks_gen( + ¶m_names, + body_stmts, + ctx, + false, + self_binding, + FunctionBodyContext::ClassElement, + None:: Result<(), String>>, + false, + &[], + &extra_uv, + method_value.generator, + method_value.r#async, + )?; ctx.in_instance_method = saved_instance; ctx.in_static_method = saved_static; // 访问器函数名带 "get "/"set " 前缀(SetFunctionName 语义),普通方法裸属性名。 diff --git a/crates/oxide_vm/tests/async_tests.rs b/crates/oxide_vm/tests/async_tests.rs index cd4202a3..e2d74390 100644 --- a/crates/oxide_vm/tests/async_tests.rs +++ b/crates/oxide_vm/tests/async_tests.rs @@ -236,3 +236,52 @@ fn for_await_of_return_escape_defers_async_close() { "\"body\"" ); } + +// ── class/object 生成器方法(异步) ── + +// class async 生成器方法:yield 顺序与 done 收敛。顶层 then 链驱动 +// (普通回调帧内读取类绑定不触发 upvalue cell 误报)。 +#[test] +fn class_async_generator_method_yields_in_order() { + assert_eq!( + eval( + "class E { async *ag() { yield 1; yield 2; } }\ + var it = new E().ag(); var a, b, c;\ + var p = it.next().then(function (r) { a = r.value; return it.next(); })\ + .then(function (r) { b = r.value; return it.next(); })\ + .then(function (r) { c = r.done; return a + ',' + b + ',' + c; });\ + p" + ), + "\"1,2,true\"" + ); +} + +// class async 生成器方法 throw:异常进挂起点,被 body 内 catch 拦截后继续 yield。 +#[test] +fn class_async_generator_method_throw_reaches_inner_catch() { + assert_eq!( + eval( + "class F { async *t() { try { yield 1; } catch (e) { yield 'caught:' + e; } } }\ + var it = new F().t();\ + var p = it.next().then(function (r) { return it.throw('boom'); })\ + .then(function (r) { return r.value; });\ + p" + ), + "\"caught:boom\"" + ); +} + +// object async 生成器方法:方法形态的 async generator 语义与函数式一致。 +#[test] +fn object_async_generator_method_yields() { + assert_eq!( + eval( + "var o = { async *gen() { yield 5; } };\ + var it = o.gen(); var a, d;\ + var p = it.next().then(function (r) { a = r.value; return it.next(); })\ + .then(function (r) { d = r.done; return a + ':' + d; });\ + p" + ), + "\"5:true\"" + ); +} diff --git a/crates/oxide_vm/tests/class_tests.rs b/crates/oxide_vm/tests/class_tests.rs index 6cea3bd4..6cf59bce 100644 --- a/crates/oxide_vm/tests/class_tests.rs +++ b/crates/oxide_vm/tests/class_tests.rs @@ -567,3 +567,73 @@ fn postfix_increment_on_captured_cell_returns_old_value() { .unwrap(); assert_num(result, 112.0); } + +// ── class 生成器方法 ── + +// 实例生成器方法:yield 顺序与 done 收敛,生成器对象经 next 驱动。 +#[test] +fn class_generator_method_yields_in_order() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class C { *g() { yield 10; yield 20; } } var it = new C().g(); [it.next().value, it.next().value, it.next().done].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "10,20,true"); +} + +// 静态生成器方法:挂在构造函数上,调用形态与实例方法一致。 +#[test] +fn class_static_generator_method_yields() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class D { static *sg() { yield 30; } } var it = D.sg(); [it.next().value, it.next().done].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "30,true"); +} + +// 生成器方法 throw 传播:throw 进挂起点,异常可被 body 内 catch 拦截。 +#[test] +fn class_generator_method_throw_reaches_inner_catch() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class F { *t() { try { yield 1; } catch (e) { yield 'caught'; } } } var it = new F().t(); var r1 = it.next(); var r2 = it.throw(new Error('x')); [r1.value, r2.value].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "1,caught"); +} + +// 生成器方法 next(arg):参数作为 yield 表达式结果传入 body。 +#[test] +fn class_generator_method_next_arg_feeds_yield_expression() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class G { *a() { var v = yield 1; yield v * 2; } } var it = new G().a(); var r1 = it.next(); var r2 = it.next(21); [r1.value, r2.value].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "1,42"); +} + +// 生成器方法 yield* 委托:与函数式生成器一致的委托序列。 +#[test] +fn class_generator_method_yield_star_delegates() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "class H { *ys() { yield* [1, 2, 3]; } } Array.from(new H().ys()).join(',')").unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "1,2,3"); +} + +// 生成器方法 return():提前关闭生成器并携带返回值。 +#[test] +fn class_generator_method_return_closes_with_value() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class I { *rt() { yield 1; yield 2; } } var it = new I().rt(); var r1 = it.next(); var r2 = it.return(99); [r1.value, r2.value, r2.done].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "1,99,true"); +} diff --git a/crates/oxide_vm/tests/object_tests.rs b/crates/oxide_vm/tests/object_tests.rs index e8bb194f..623fca37 100644 --- a/crates/oxide_vm/tests/object_tests.rs +++ b/crates/oxide_vm/tests/object_tests.rs @@ -358,3 +358,23 @@ fn object_literal_batch_mixed_computed_spread_order() { ); assert_eq!(eval("var k='k',s={m:3}; ({a:1,[k]:2,...s}).k"), "2"); } + +// ── object 生成器方法 ── + +// 对象字面量生成器方法:yield 顺序与 done 收敛。 +#[test] +fn object_generator_method_yields_in_order() { + assert_eq!( + eval_string("var o = { *gen() { yield 1; yield 2; } }; var it = o.gen(); [it.next().value, it.next().value, it.next().done].join(',')"), + "1,2,true" + ); +} + +// 对象字面量生成器方法 throw:异常进挂起点,被 body 内 catch 拦截后继续 yield。 +#[test] +fn object_generator_method_throw_reaches_inner_catch() { + assert_eq!( + eval_string("var o = { *t() { try { yield 1; } catch (e) { yield 'caught'; } } }; var it = o.t(); var r1 = it.next(); var r2 = it.throw(new Error('x')); [r1.value, r2.value].join(',')"), + "1,caught" + ); +}