diff --git a/crates/oxide_vm/src/promise.rs b/crates/oxide_vm/src/promise.rs index 85ecd24..fc33c09 100644 --- a/crates/oxide_vm/src/promise.rs +++ b/crates/oxide_vm/src/promise.rs @@ -15,7 +15,7 @@ use oxide_types::private_key::make_int_key; use oxide_types::value::JsValue; use crate::native::NativeFn; -use crate::vm::Vm; +use crate::vm::{FrameArgs, FrameContinuation, Vm}; use crate::vm_warn; /// Promise 的 settled 状态。 @@ -188,28 +188,86 @@ impl Vm { JsValue::from_js_object(ptr) } - /// 构造调用:分配 proto = ctor.prototype(缺省 Object.prototype)的 this 后以 - /// 普通调用执行 ctor,返回值非对象时回退到 this(与 Reflect.construct 同路径)。 + /// 构造调用(Construct(C, args)):native 构造器值传递调用,bytecode 构造器 + /// 压构造帧执行(含 derived 构造器 super() 语义),返回值非对象时回退到新对象。 fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result { - if !oxide_builtins::iterator::is_callable(ctor) { - return Err(oxide_builtins::error::create_type_error(self, "constructor is not callable")); - } - let ctor_obj = unsafe { &*ctor.as_js_object_ptr() }; - if ctor_obj.is_arrow() - || (ctor_obj.native_fn().is_some() && ctor_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR) - { + // IsConstructor 校验:arrow / 非构造 native / 普通值拒绝。 + if !is_constructor_value(ctor) { return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); } + let ctor_obj = unsafe { &*ctor.as_js_object_ptr() }; + // native 构造器:receiver 为新对象,值传递调用(%Promise% 主路径)。 + if ctor_obj.native_fn().is_some() { + let this_ptr = self.alloc_ctor_this(ctor_obj)?; + let this_val = JsValue::from_js_object(this_ptr); + return match self.call_function_sync(ctor, this_val, args) { + Ok(ret) if ret.is_object() => Ok(ret), + Ok(_) => Ok(this_val), + Err(e) => Err(self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e))), + }; + } + self.call_constructor_bytecode_inline(ctor, ctor_obj, args) + } + + /// 分配构造 this:proto = ctor.prototype(缺省 Object.prototype)。 + fn alloc_ctor_this(&mut self, ctor_obj: &JsObject) -> Result<*mut JsObject, JsValue> { let proto_si = self.kernel_core.perm_interner().intern("prototype").0; let proto_val = match self.resolve_property(ctor_obj, proto_si) { Some(p) if p.is_object() => p, _ => JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject), }; - let this_ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); - let this_val = JsValue::from_js_object(this_ptr); - match self.call_function_sync(ctor, this_val, args) { - Ok(ret) if ret.is_object() => Ok(ret), - Ok(_) => Ok(this_val), + Ok(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val))) + } + + /// bytecode 构造器构造调用:压构造帧内嵌 dispatch 执行(derived 构造器 + /// super() 前 this = undefined,new.target = ctor),结果经 `do_return` 交付 + /// regs[0](非对象回退构造 this)。 + /// + /// # 副作用 + /// - 经 `save_inline_state` / `restore_inline_state` 保存恢复调用方执行状态。 + /// - `construct_dispatch` 标志令构造帧弹出时交付结果而非继续执行。 + fn call_constructor_bytecode_inline( + &mut self, ctor: JsValue, ctor_obj: &JsObject, args: &[JsValue], + ) -> Result { + let sub_idx = ctor_obj.sub_module_index() as usize; + if sub_idx == 0 || sub_idx >= self.sub_modules.len() { + return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); + } + // 生成器/异步函数不是构造器。 + if self.sub_modules[sub_idx].is_generator || self.sub_modules[sub_idx].is_async { + return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); + } + let new_obj_ptr = self.alloc_ctor_this(ctor_obj)?; + let new_obj_val = JsValue::from_js_object(new_obj_ptr); + // derived 构造器 super() 前 this 为 undefined,基类 this = 新对象。 + let this_value = if ctor_obj.is_derived_constructor() { + JsValue::undefined() + } else { + new_obj_val + }; + let window = self.active_reg_limit.max(self.sub_modules[sub_idx].n_registers).max(1) as usize; + let saved = self.save_inline_state(window); + let prev_construct = self.construct_dispatch; + self.construct_dispatch = true; + let result = self + .push_bytecode_frame( + ctor, + this_value, + FrameArgs::Slice(args), + Some(0), + Some(new_obj_val), + ctor, + FrameContinuation::None, + 0, + ) + .and_then(|_| self.dispatch()); + self.construct_dispatch = prev_construct; + self.restore_inline_state(saved); + match result { + Ok(v) => Ok(v), Err(e) => Err(self .last_uncaught_value .take() @@ -261,7 +319,9 @@ impl Vm { } let obj = unsafe { &*callee.as_js_object_ptr() }; let si = self.kernel_core.perm_interner().intern(DELEGATED_PROP).0; - self.resolve_property(obj, si).map(|v| v.is_bool() && v.as_bool()).unwrap_or(false) + self.resolve_property(obj, si) + .map(|v| v.is_bool() && v.as_bool()) + .unwrap_or(false) } /// 取出 Promise 状态盒指针(调用方须先校验 `is_promise_value`)。 @@ -436,9 +496,23 @@ impl Vm { Ok(promise) } - /// `PerformPromiseThen` 核心:注册 fulfill/reject 两条反应;已 settle 则直接入队。 + /// `PerformPromiseThen` 入口(内置能力):注册 fulfill/reject 反应并返回派生 + /// promise。能力恒为 `%Promise%`——供引擎内部 await 机制(async / + /// async generator / AsyncFromSync)使用,规范上这些路径由调用方提供能力, + /// 不读 `this.constructor`,避免 await 子类 promise 时意外经子类构造器派生。 pub(crate) fn perform_promise_then( &mut self, this_val: JsValue, on_fulfilled: JsValue, on_rejected: JsValue, + ) -> Result { + let capability = self.new_promise_capability(); + self.perform_promise_then_cap(this_val, on_fulfilled, on_rejected, capability) + } + + /// `PerformPromiseThen` 核心:以调用方给定能力注册 fulfill/reject 两条反应; + /// 已 settle 则直接入队。能力三元组由调用方提供——`promise_then_species` + /// 传 species 派生能力,内部 await 路径经薄包装传内置能力。 + fn perform_promise_then_cap( + &mut self, this_val: JsValue, on_fulfilled: JsValue, on_rejected: JsValue, + capability: (JsValue, JsValue, JsValue), ) -> Result { if !self.is_promise_value(this_val) { return Err(oxide_builtins::error::create_type_error( @@ -456,7 +530,7 @@ impl Vm { } else { JsValue::undefined() }; - let (new_promise, resolve, reject) = self.new_promise_capability(); + let (new_promise, resolve, reject) = capability; let state_ptr = self.promise_state_ptr(this_val); let (kind, result) = { let state = unsafe { &mut *state_ptr }; @@ -502,6 +576,56 @@ impl Vm { Ok(new_promise) } + /// `Promise.prototype.then` 的派生入口:IsPromise 校验后按 `this.constructor` + /// 选派生构造器(SpeciesConstructor 的 constructor 语义),建能力并注册反应。 + fn promise_then_species( + &mut self, this_val: JsValue, on_fulfilled: JsValue, on_rejected: JsValue, + ) -> Result { + if !self.is_promise_value(this_val) { + return Err(oxide_builtins::error::create_type_error( + self, + "Method Promise.prototype.then called on incompatible receiver", + )); + } + let ctor = self.species_constructor(this_val)?; + let capability = self.new_promise_capability_with_ctor(ctor)?; + self.perform_promise_then_cap(this_val, on_fulfilled, on_rejected, capability) + } + + /// 读 `promise.constructor` 选派生构造器(SpeciesConstructor 的 constructor + /// 语义):constructor 为 undefined 时退回内置 `%Promise%`,非可构造函数抛 + /// TypeError。constructor getter 抛错时透传原异常值。 + /// + /// # 边界与前提 + /// - 调用方已做 IsPromise 校验,`promise` 必为 Promise 对象。 + /// - constructor 为 null/非对象/非构造器均抛 TypeError。 + /// + /// # 注意事项 + /// - 不读 `@@species`:`%Promise%` 未注册 species getter,子类经原型链读到 + /// undefined 会错误退回内置;`@@species` 改写支持待补 + /// `Promise[Symbol.species]` getter 时一并落地。 + fn species_constructor(&mut self, promise: JsValue) -> Result { + let obj = unsafe { &*promise.as_js_object_ptr() }; + let ctor_si = self.kernel_core.perm_interner().intern("constructor").0; + let ctor = match self.ordinary_get(obj, ctor_si, promise) { + Ok(c) => c, + Err(e) => { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); + return Err(exc); + } + }; + if ctor.is_undefined() { + return Ok(JsValue::from_js_object(self.promise_constructor.as_ptr() as *mut JsObject)); + } + if !is_constructor_value(ctor) { + return Err(oxide_builtins::error::create_type_error(self, "Species constructor is not a constructor")); + } + Ok(ctor) + } + /// drain 微任务队列:FIFO 逐条处理直到清空或达到上限。 /// /// # 副作用 @@ -672,6 +796,18 @@ impl Vm { } } +/// IsConstructor 近似判定:可调用、非 arrow,且 native 函数须带构造器 tag +/// (与 `construct_ctor` 的校验一致)。 +fn is_constructor_value(c: JsValue) -> bool { + if !c.is_object() { + return false; + } + let c_obj = unsafe { &*c.as_js_object_ptr() }; + c_obj.is_function() + && !c_obj.is_arrow() + && !(c_obj.native_fn().is_some() && c_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR) +} + /// 读取 Promise 的 settled 值(CLI 格式化用):`Some((is_fulfilled, value))`, /// Pending 返回 `None`。 pub fn promise_settled_value(obj: &JsObject) -> Option<(bool, JsValue)> { @@ -768,12 +904,13 @@ fn promise_reject_closure(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -/// `Promise.prototype.then(onFulfilled, onRejected)`:注册反应并返回派生 promise。 +/// `Promise.prototype.then(onFulfilled, onRejected)`:按 `this.constructor` 派生 +/// 并注册反应,返回派生 promise(子类实例走子类构造器)。 fn promise_then(vm: &mut Vm, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let on_fulfilled = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let on_rejected = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - match vm.perform_promise_then(this_val, on_fulfilled, on_rejected) { + match vm.promise_then_species(this_val, on_fulfilled, on_rejected) { Ok(promise) => NativeResult::Ok(promise), Err(err) => NativeResult::Err(err), } @@ -986,7 +1123,8 @@ impl Vm { let sf = self.kernel_core.perm_interner().as_ref(); let sh = self.kernel_core.shape_forge().as_ref(); let fn_proto_val = self.session.builtin_world().fn_proto_val(); - let error_proto_val = JsValue::from_js_object(self.session.builtin_world().error_proto.as_ptr() as *mut JsObject); + let error_proto_val = + JsValue::from_js_object(self.session.builtin_world().error_proto.as_ptr() as *mut JsObject); // %AggregateError.prototype%:proto = %Error.prototype%,constructor/name/message 数据属性。 let mut proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); @@ -1033,10 +1171,8 @@ impl Vm { self.aggregate_error_proto = P::new(*proto); self.aggregate_error_constructor = P::new(*ctor); let proto_mut = unsafe { &mut *self.aggregate_error_proto.as_mut_ptr() }; - proto_mut.set_prop_at( - 0u32, - JsValue::from_js_object(self.aggregate_error_constructor.as_ptr() as *mut JsObject), - ); + proto_mut + .set_prop_at(0u32, JsValue::from_js_object(self.aggregate_error_constructor.as_ptr() as *mut JsObject)); let ctor_mut = unsafe { &mut *self.aggregate_error_constructor.as_mut_ptr() }; ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.aggregate_error_proto.as_ptr() as *mut JsObject)); @@ -1322,7 +1458,8 @@ fn perform_promise_combine( Ok(()) => { // 迭代完成:哨兵 1 递减;为 0 时结算(race 无计数语义)。 let remaining = agg_read_remaining(vm, record) - 1; - agg_write_remaining(vm, record, remaining); match kind { + agg_write_remaining(vm, record, remaining); + match kind { AggregateKind::All | AggregateKind::AllSettled => { if remaining == 0 { let values = agg_record_val(vm, record, AGG_VALUES_PROP); @@ -1366,7 +1503,10 @@ fn agg_element_state(vm: &mut Vm) -> Option<(JsValue, i32)> { let al_si = vm.kernel_core.perm_interner().intern(AGG_ALREADY_PROP).0; // SAFETY: callee 是当前调用的存活函数对象。 let callee_ref = unsafe { &*callee_ptr }; - if vm.resolve_property(callee_ref, al_si).is_some_and(oxide_runtime_api::to_boolean) { + if vm + .resolve_property(callee_ref, al_si) + .is_some_and(oxide_runtime_api::to_boolean) + { return None; } let record = vm.resolve_property(callee_ref, rec_si).unwrap_or(JsValue::undefined()); @@ -1556,24 +1696,14 @@ fn aggregate_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { let msg_si = vm.kernel_core.perm_interner().intern("message").0; let msg_val = vm.new_string(&msg_str); // SAFETY: this 是本次构造的存活对象。 - let _ = vm.define_data_property( - unsafe { &mut *this }, - msg_si, - msg_val, - PropAttributes::new(true, false, true), - ); + let _ = vm.define_data_property(unsafe { &mut *this }, msg_si, msg_val, PropAttributes::new(true, false, true)); } let errors_list = match vm.aggregate_errors_to_list(errors) { Ok(list) => list, Err(err) => return NativeResult::Err(err), }; let err_si = vm.kernel_core.perm_interner().intern("errors").0; - let _ = vm.define_data_property( - unsafe { &mut *this }, - err_si, - errors_list, - PropAttributes::new(true, false, true), - ); + let _ = vm.define_data_property(unsafe { &mut *this }, err_si, errors_list, PropAttributes::new(true, false, true)); NativeResult::Ok(JsValue::from_js_object(this)) } diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 7158282..d110448 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -436,6 +436,10 @@ pub struct Vm { /// 当前是否处于异步函数内嵌 dispatch 循环:异步帧弹出且 frames 清空时, /// `do_return` 据此把结果交付给恢复方(与 generator_dispatch 同语义)。 pub(crate) async_dispatch: bool, + /// 当前是否处于构造器内嵌 dispatch 循环(`call_constructor_bytecode_inline`): + /// 构造帧弹出且 frames 清空时,`do_return` 据此把构造结果(regs[0], + /// 已做非对象回退 this)交付给恢复方(与 generator_dispatch 同语义)。 + pub(crate) construct_dispatch: bool, /// 当前正在执行的异步生成器上下文对象(`OBJ_TYPE_ASYNC_GENERATOR`,持有 /// `AsyncGeneratorState` 快照)。AWAIT dispatch 据此登记异步生成器恢复反应; /// 跨嵌套 async 调用保存/恢复(与 `async_context` 同生命周期,二者互斥占用)。 diff --git a/crates/oxide_vm/src/vm_dispatch_ctrl.rs b/crates/oxide_vm/src/vm_dispatch_ctrl.rs index 1a02c7e..d9a32dd 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -447,9 +447,11 @@ impl Vm { } } } - // 生成器/异步内嵌 dispatch:帧全部弹出后把结果交付恢复方,而非继续执行。 - if self.frames.is_empty() && (self.generator_dispatch || self.async_dispatch) { - return Ok(Some(result)); + // 生成器/异步/构造内嵌 dispatch:帧全部弹出后把结果交付恢复方,而非继续执行。 + if self.frames.is_empty() && (self.generator_dispatch || self.async_dispatch || self.construct_dispatch) { + // 构造内嵌:弹帧已把构造结果(非对象回退 this)写入 regs[0],直接交付。 + let delivered = if self.construct_dispatch { self.regs[0] } else { result }; + return Ok(Some(delivered)); } Ok(None) } else { diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 5115f91..133d2ff 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -84,6 +84,7 @@ impl Vm { async_context: None, async_suspended: false, async_dispatch: false, + construct_dispatch: false, async_gen_context: None, async_gen_dispatch: false, async_gen_suspended: false, @@ -188,6 +189,7 @@ impl Vm { async_context: None, async_suspended: false, async_dispatch: false, + construct_dispatch: false, async_gen_context: None, async_gen_dispatch: false, async_gen_suspended: false, diff --git a/crates/oxide_vm/tests/promise_tests.rs b/crates/oxide_vm/tests/promise_tests.rs index 41ba8de..0e0a9b3 100644 --- a/crates/oxide_vm/tests/promise_tests.rs +++ b/crates/oxide_vm/tests/promise_tests.rs @@ -123,7 +123,10 @@ fn double_resolve_second_noop() { #[test] fn reject_then_resolve_keeps_rejected() { let mut vm = Vm::new(); - let (ok, val) = settled(&mut vm, "new Promise((res, rej) => { rej('e'); res(1); }).then(v => v, e => 'rejected:' + e)"); + let (ok, val) = settled( + &mut vm, + "new Promise((res, rej) => { rej('e'); res(1); }).then(v => v, e => 'rejected:' + e)", + ); assert!(ok); assert_eq!(vm.lookup_str(val).as_deref(), Some("rejected:e")); } @@ -151,3 +154,145 @@ fn thenable_chain_after_already_resolved_noop() { assert!(ok); assert_eq!(val.as_int(), 5); } + +#[test] +fn subclass_then_derives_subclass_instance() { + // 子类 then 派生:class P extends Promise {},p.then 返回 P 实例(经子类构造器)。 + // 单次 eval 完成断言:跨 eval 时旧 module 的 bytecode 函数对象随 sub_modules + // 替换失效(引擎既有局限),故不拆分执行。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var P = class extends Promise {}; var d = new P(r => r(1)).then(v => v); d.constructor === P", + ) + .unwrap(); + assert_eq!(result.as_bool(), true, "subclass then should derive via subclass ctor"); +} + +#[test] +fn subclass_catch_finally_derive_subclass() { + // catch/finally 经 this.then 调用,自动获得子类派生语义。 + // 每次 eval 独立定义 P:跨 eval 复用旧 module 函数对象会随 sub_modules 替换失效。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var P = class extends Promise {}; var d = new P(r => r(1)).catch(()=>{}); d.constructor === P", + ) + .unwrap(); + assert_eq!(result.as_bool(), true); + let result = eval( + &mut vm, + "var Q = class extends Promise {}; var f = new Q(r => r(1)).finally(()=>{}); f.constructor === Q", + ) + .unwrap(); + assert_eq!(result.as_bool(), true); +} + +#[test] +fn subclass_chain_keeps_subclass_ctor() { + // 链式 then 每次派生均保持子类构造器。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var P = class extends Promise {}; var d = new P(r => r(1)).then(v => v + 1).then(v => v); d.constructor === P", + ) + .unwrap(); + assert_eq!(result.as_bool(), true); +} + +#[test] +fn rewritten_prototype_constructor_used_for_derivation() { + // P.prototype.constructor 改写后,then 用改写值派生。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var P = class extends Promise {}; function Alt(exec) { exec(function(){}, function(){}); } \ + P.prototype.constructor = Alt; var d = new P(r => r(1)).then(v => v); d.constructor === Alt", + ) + .unwrap(); + assert_eq!(result.as_bool(), true); +} + +#[test] +fn constructor_undefined_uses_intrinsic() { + // constructor 为 undefined → 退回内置 %Promise% 派生。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var q = new Promise(r => r(1)); Object.defineProperty(q, 'constructor', { value: undefined }); \ + var d = q.then(v => v); d.constructor === Promise", + ) + .unwrap(); + assert_eq!(result.as_bool(), true); +} + +#[test] +fn constructor_non_object_throws_type_error() { + // constructor 非对象 → then 抛 TypeError。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var r = new Promise(res => res(1)); Object.defineProperty(r, 'constructor', { value: 42 }); \ + try { r.then(()=>{}); 'no-throw' } catch (e) { e.name }", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).as_deref(), Some("TypeError")); +} + +#[test] +fn constructor_getter_throw_preserves_original_value() { + // constructor getter 抛错 → 透传原异常对象(catch 收到同一引用)。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var boom = new Error('boom'); var r = new Promise(res => res(1)); \ + Object.defineProperty(r, 'constructor', { get() { throw boom; } }); \ + try { r.then(()=>{}); 'no-throw' } catch (e) { e === boom }", + ) + .unwrap(); + assert_eq!(result.as_bool(), true); +} + +#[test] +fn intrinsic_promise_then_regression() { + // 回归:内置 promise 的 then 派生路径不变。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "var d = new Promise(r => r(1)).then(v => v); d.constructor === Promise").unwrap(); + assert_eq!(result.as_bool(), true); +} + +#[test] +fn await_subclass_promise_resumes_with_value() { + // async function 内 await 子类 promise:值正确,额外派生仅来自 thenable + // 委托(await 机制自身的能力恒为内置,不额外调用子类构造器)。 + let mut vm = Vm::new(); + let (ok, val) = settled( + &mut vm, + "var ctorCalls = 0; var P = class extends Promise { constructor(exec) { ctorCalls++; super(exec); } }; \ + async function f() { return await new P(r => r(1)); } \ + f().then(v => [v, ctorCalls])", + ); + assert!(ok); + let obj = unsafe { &*val.as_js_object_ptr() }; + assert_eq!(obj.prop_count(), 2); + assert_eq!(obj.get_prop_at(0).as_int(), 1, "await value should pass through"); + assert_eq!(obj.get_prop_at(1).as_int(), 2, "ctor calls: new once + thenable delegation once"); +} + +#[test] +fn async_gen_yield_subclass_promise_skips_species() { + // 内部 await 能力不走 species:async generator yield 子类 promise 时, + // yield 包装(await 展开)不得再经子类构造器派生。 + let mut vm = Vm::new(); + let (ok, val) = settled( + &mut vm, + "var ctorCalls = 0; var P = class extends Promise { constructor(exec) { ctorCalls++; super(exec); } }; \ + async function* g() { yield new P(r => r(1)); } \ + var it = g(); it.next().then(r => [r.value, ctorCalls])", + ); + assert!(ok); + let obj = unsafe { &*val.as_js_object_ptr() }; + assert_eq!(obj.prop_count(), 2); + assert_eq!(obj.get_prop_at(0).as_int(), 1, "yield value should pass through"); + assert_eq!(obj.get_prop_at(1).as_int(), 1, "internal await must not derive via subclass ctor"); +}