diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index f5f6785f..dfe3fc5d 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -739,14 +739,22 @@ impl Vm { } let iter_obj = unsafe { &*iterator.as_js_object_ptr() }; let return_si = self.kernel_core.perm_interner().intern("return").0; - let return_fn = self.ordinary_get(iter_obj, return_si, iterator)?; + let return_fn = match self.ordinary_get(iter_obj, return_si, iterator) { + Ok(v) => v, + // return getter 抛出:恢复原始异常值并就地展开,使外围 try/catch 可捕获。 + Err(e) => return self.throw_for_of_close_error(&e), + }; if !return_fn.is_object() { return Ok(()); } if !unsafe { &*return_fn.as_js_object_ptr() }.is_function() { return Ok(()); } - let inner = self.call_function_sync(return_fn, iterator, &[])?; + let inner = match self.call_function_sync(return_fn, iterator, &[]) { + Ok(v) => v, + // return() 抛出:恢复原始异常值并就地展开,使外围 try/catch 可捕获。 + Err(e) => return self.throw_for_of_close_error(&e), + }; if !inner.is_object() { return self.raise_type_error("iterator return() result is not an object"); } @@ -862,6 +870,23 @@ impl Vm { self.unwind() } + /// for-await-of 收尾抛错(return getter 或 return() 调用):恢复原始异常值并就地 + /// 展开,使外围 try/catch 可捕获。本函数入口已弹出本迭代器条目,故展开路径不得 + /// 再弹 for-of 栈(与 throw_for_of_error_value 的唯一区别)。 + /// + /// 直接 unwind 而非经 raise_call_error:后者以 native_call_depth 门控展开, + /// 异步函数恢复期深度恒大于零会误判为不展开,使异常绕过循环内 catch 直接 + /// 拒绝 capability promise。 + fn throw_for_of_close_error(&mut self, msg: &str) -> Result<(), String> { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, msg)); + self.exception_value = Some(exc); + self.pending_error_kind = Some(self.thrown_error_kind(exc)); + self.unwind() + } + pub(crate) fn dispatch_for_of_close(&mut self) -> Result<(), String> { vm_trace!("FOR_OF_CLOSE"); let Some(entry) = self.iters.pop_for_of() else { diff --git a/crates/oxide_vm/tests/async_tests.rs b/crates/oxide_vm/tests/async_tests.rs index 8b59b062..3b3c470d 100644 --- a/crates/oxide_vm/tests/async_tests.rs +++ b/crates/oxide_vm/tests/async_tests.rs @@ -239,6 +239,48 @@ fn for_await_of_return_escape_defers_async_close() { ); } +#[test] +fn for_await_of_close_return_getter_throw_propagates_original() { + // 收尾时 return 的 getter 抛出:外围 catch 须收到原始抛出值, + // 不得是重建的 Error 对象。 + assert_eq!( + eval( + "let caught;\ + const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},get return(){throw 'ret-getter';}}}};\ + (async()=>{ try { for await (const x of it) { break; } } catch (e) { caught = e; } return 'caught:' + caught; })()" + ), + "\"caught:ret-getter\"" + ); +} + +#[test] +fn for_await_of_close_return_call_throw_propagates_original() { + // 收尾时 return() 调用抛出:外围 catch 须收到原始抛出值。 + assert_eq!( + eval( + "let caught;\ + const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},return(){throw 'ret-call';}}}};\ + (async()=>{ try { for await (const x of it) { break; } } catch (e) { caught = e; } return 'caught:' + caught; })()" + ), + "\"caught:ret-call\"" + ); +} + +#[test] +fn for_await_of_close_throw_object_identity_preserved() { + // 收尾时 return() 抛出对象:外围 catch 收到的须是同一对象(身份保留), + // 证明原始异常值经异常通道传播而非按文本重建。 + assert_eq!( + eval( + "let caught;\ + const marker={tag:'x'};\ + const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},return(){throw marker;}}}};\ + (async()=>{ try { for await (const x of it) { break; } } catch (e) { caught = e; } return caught === marker ? 'same' : 'diff:' + caught; })()" + ), + "\"same\"" + ); +} + // ── class/object 生成器方法(异步) ── // class async 生成器方法:yield 顺序与 done 收敛。顶层 then 链驱动