diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index bc405a83..aba7176e 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -109,7 +109,8 @@ fn decode_symbol_key(vm: &H, key: u32) -> JsValue { 4 => world.sym_split.as_ptr(), 5 => world.sym_to_primitive.as_ptr(), 6 => world.sym_has_instance.as_ptr(), - _ => world.sym_match_all.as_ptr(), + 7 => world.sym_match_all.as_ptr(), + _ => world.sym_async_iterator.as_ptr(), }; return JsValue::from_js_object(ptr as *mut JsObject); } diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index 985d0e3f..5af70f6e 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -207,6 +207,13 @@ define_opcodes! { YIELD_STAR = 0x78 => "YIELD_STAR", // `await`:rd=被等待的值(PromiseResolve 包装);挂起异步帧,恢复值经 reg 0 交付。 AWAIT = 0x79 => "AWAIT", + // for-await-of 步进:INIT(a=可迭代值) 取异步迭代器;NEXT 调用迭代器 next(), + // 结果写 rd(随后由 AWAIT 等待);DONE 读取 AWAIT 恢复值(a 槽)的 done 写 rd; + // CLOSE 执行异步 IteratorClose(return() 结果 await 后继续)。 + FOR_AWAIT_OF_INIT = 0x7A => "FOR_AWAIT_OF_INIT", + FOR_AWAIT_OF_NEXT = 0x7B => "FOR_AWAIT_OF_NEXT", + FOR_AWAIT_OF_DONE = 0x7C => "FOR_AWAIT_OF_DONE", + FOR_AWAIT_OF_CLOSE = 0x7D => "FOR_AWAIT_OF_CLOSE", // ── 位运算 (0x80-0x8F) ── BIT_AND = 0x80 => "BIT_AND", diff --git a/crates/oxide_emit/src/stmt/iteration/for_of.rs b/crates/oxide_emit/src/stmt/iteration/for_of.rs index a12e6fbc..90faa1e2 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_of.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_of.rs @@ -1,16 +1,28 @@ -//! for-of 语句 emit:`emit_for_of_statement` 迭代器协议遍历。 +//! for-of 与 for-await-of 语句 emit。 +//! +//! 同步 for-of 用 FOR_OF_* 指令;`for await` 用 FOR_AWAIT_OF_*:初始化取异步 +//! 迭代器(缺失 `@@asyncIterator` 时回退 AsyncFromSyncIterator),每步 +//! `next()` 结果经 `AWAIT` 挂起后检查 done。体内 await 语义由 `AWAIT` 指令 +//! 原生提供(每次迭代都 await)。 use crate::{CompileCtx, Emitter}; use oxide_bytecode::opcode::OpCode; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; -use oxide_parser::{ForStatementLeft, Statement}; +use oxide_parser::{ForOfStatement, ForStatementLeft, Statement}; impl Emitter { pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::ForOfStatement(fo) = stmt else { return Ok(None); }; + if fo.r#await { + return self.emit_for_await_of_statement(fo, ctx); + } + self.emit_sync_for_of_statement(fo, ctx) + } + + fn emit_sync_for_of_statement(&self, fo: &ForOfStatement, ctx: &mut CompileCtx) -> Result, String> { let start_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); let iter_src_reg = self.emit_expression(&fo.right, ctx)?; @@ -23,7 +35,59 @@ impl Emitter { ctx.inst(Inst::jmp_if_false(has_reg, end_label)); let val_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None)); - match &fo.left { + self.emit_for_of_left_assignment(&fo.left, val_reg, ctx)?; + self.emit_statement(&fo.body, ctx)?; + ctx.inst(Inst::jmp(start_label)); + ctx.labels.set_label_pos(end_label, ctx.insts.len()); + ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None)); + ctx.pop_label_scopes(n_labeled); + ctx.pop_loop(); + Ok(None) + } + + /// for-await-of:异步迭代器协议遍历,每步经 `AWAIT` 挂起等待 next() 结果。 + /// + /// 指令序列:INIT 取异步迭代器 → 循环顶 NEXT 调 next() 写 promise → `AWAIT` + /// 挂起(恢复值经 reg 0 交付)→ LOAD_VAR 拷贝为结果对象 → DONE 检查 done → + /// FOR_OF_NEXT 读 value → 绑定左侧 → 循环体 → CLOSE 异步收尾。 + fn emit_for_await_of_statement(&self, fo: &ForOfStatement, ctx: &mut CompileCtx) -> Result, String> { + let start_label = ctx.next_label_id(); + let end_label = ctx.next_label_id(); + let iter_src_reg = self.emit_expression(&fo.right, ctx)?; + ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_INIT, Operand::None, Operand::Reg(iter_src_reg), Operand::None)); + ctx.labels.set_label_pos(start_label, ctx.insts.len()); + ctx.push_loop(end_label, start_label); + let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); + let next_reg = ctx.alloc_reg(); + ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_NEXT, Operand::Reg(next_reg), Operand::None, Operand::None)); + ctx.inst(Inst::await_expr(Operand::Reg(next_reg))); + let result_reg = ctx.alloc_reg(); + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::None, Operand::None)); + let has_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::FOR_AWAIT_OF_DONE, + Operand::Reg(has_reg), + Operand::Reg(result_reg), + Operand::None, + )); + ctx.inst(Inst::jmp_if_false(has_reg, end_label)); + let val_reg = ctx.alloc_reg(); + ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None)); + self.emit_for_of_left_assignment(&fo.left, val_reg, ctx)?; + self.emit_statement(&fo.body, ctx)?; + ctx.inst(Inst::jmp(start_label)); + ctx.labels.set_label_pos(end_label, ctx.insts.len()); + ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_CLOSE, Operand::None, Operand::None, Operand::None)); + ctx.pop_label_scopes(n_labeled); + ctx.pop_loop(); + Ok(None) + } + + /// for-of/for-await-of 左侧绑定:把当前迭代值 `val_reg` 写入声明/赋值目标。 + fn emit_for_of_left_assignment( + &self, left: &ForStatementLeft, val_reg: u32, ctx: &mut CompileCtx, + ) -> Result<(), String> { + match left { ForStatementLeft::VariableDeclaration(decl) => { for d in &decl.declarations { self.emit_binding_pattern(&d.id, val_reg, decl.kind, false, ctx)?; @@ -42,12 +106,6 @@ impl Emitter { } _ => return Err("unsupported for-of left-hand side".into()), } - self.emit_statement(&fo.body, ctx)?; - ctx.inst(Inst::jmp(start_label)); - ctx.labels.set_label_pos(end_label, ctx.insts.len()); - ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None)); - ctx.pop_label_scopes(n_labeled); - ctx.pop_loop(); - Ok(None) + Ok(()) } } diff --git a/crates/oxide_ir/src/contract.rs b/crates/oxide_ir/src/contract.rs index 306b4baa..056e4e06 100644 --- a/crates/oxide_ir/src/contract.rs +++ b/crates/oxide_ir/src/contract.rs @@ -71,8 +71,10 @@ impl Inst { | OpCode::SPILL | OpCode::FOR_IN_INIT | OpCode::FOR_OF_INIT + | OpCode::FOR_AWAIT_OF_INIT | OpCode::FOR_IN_CLEANUP | OpCode::FOR_OF_CLOSE + | OpCode::FOR_AWAIT_OF_CLOSE | OpCode::SUSPEND_BODY => None, // 其余指令 rd 即 def(算术/比较/位/逻辑/加载族/迭代器 NEXT 等) _ => reg_of(&self.rd), @@ -308,8 +310,11 @@ impl Inst { push_operand(&mut uses, &self.a); } // for-in/of 迭代器:INIT 读 a;NEXT/DONE 读迭代器栈隐式状态 - OpCode::FOR_IN_INIT | OpCode::FOR_OF_INIT => push_operand(&mut uses, &self.a), + OpCode::FOR_IN_INIT | OpCode::FOR_OF_INIT | OpCode::FOR_AWAIT_OF_INIT => push_operand(&mut uses, &self.a), OpCode::FOR_IN_NEXT | OpCode::FOR_IN_DONE | OpCode::FOR_OF_NEXT | OpCode::FOR_OF_DONE => {} + // for-await-of:NEXT 无寄存器 use;DONE 读取 AWAIT 恢复值(a 槽)的 done + OpCode::FOR_AWAIT_OF_NEXT | OpCode::FOR_AWAIT_OF_CLOSE => {} + OpCode::FOR_AWAIT_OF_DONE => push_operand(&mut uses, &self.a), // REST_OBJECT:读 a(src)与可选 b(运行时 excluded 数组),ext 是 excluded_idx 常量 OpCode::REST_OBJECT => { push_operand(&mut uses, &self.a); diff --git a/crates/oxide_kernel/src/builtin.rs b/crates/oxide_kernel/src/builtin.rs index fe4fa250..338a2aa6 100644 --- a/crates/oxide_kernel/src/builtin.rs +++ b/crates/oxide_kernel/src/builtin.rs @@ -242,6 +242,7 @@ pub struct BuiltinWorld { pub sym_to_primitive: P, pub sym_has_instance: P, pub sym_match_all: P, + pub sym_async_iterator: P, pub stub_objects: Vec>, } @@ -628,6 +629,7 @@ impl BuiltinWorld { BuiltinId::SymToPrimitive => &self.sym_to_primitive, BuiltinId::SymHasInstance => &self.sym_has_instance, BuiltinId::SymMatchAll => &self.sym_match_all, + BuiltinId::SymAsyncIterator => &self.sym_async_iterator, } } @@ -668,6 +670,7 @@ impl BuiltinWorld { let sym_to_primitive = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let sym_has_instance = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let sym_match_all = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); + let sym_async_iterator = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let stub_objects = Vec::new(); let world = Self { @@ -739,6 +742,7 @@ impl BuiltinWorld { sym_to_primitive, sym_has_instance, sym_match_all, + sym_async_iterator, stub_objects, }; wire_builtin_world_links(&world); @@ -811,6 +815,7 @@ impl BuiltinWorld { sym_to_primitive, sym_has_instance, sym_match_all, + sym_async_iterator, ) = if dirty.symbol_family { let (symbol_proto, symbol_constructor) = make_named_pair(string_forge, shape_forge, labels, "Symbol"); ( @@ -824,6 +829,7 @@ impl BuiltinWorld { P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), ) } else { ( @@ -837,6 +843,7 @@ impl BuiltinWorld { current.sym_to_primitive.clone(), current.sym_has_instance.clone(), current.sym_match_all.clone(), + current.sym_async_iterator.clone(), ) }; @@ -981,6 +988,7 @@ impl BuiltinWorld { sym_to_primitive, sym_has_instance, sym_match_all, + sym_async_iterator, stub_objects, }; wire_builtin_world_links(&world); diff --git a/crates/oxide_kernel/src/kernel.rs b/crates/oxide_kernel/src/kernel.rs index 7a241b77..a38f699d 100644 --- a/crates/oxide_kernel/src/kernel.rs +++ b/crates/oxide_kernel/src/kernel.rs @@ -225,7 +225,7 @@ pub struct KernelSession { /// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及 /// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的 /// builtin 家族。 -pub const NUM_BUILTINS: usize = 67; +pub const NUM_BUILTINS: usize = 68; /// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。 /// @@ -301,6 +301,7 @@ pub enum BuiltinId { SymToPrimitive = 64, SymHasInstance = 65, SymMatchAll = 66, + SymAsyncIterator = 67, } impl BuiltinId { @@ -373,6 +374,7 @@ impl BuiltinId { BuiltinId::SymToPrimitive, BuiltinId::SymHasInstance, BuiltinId::SymMatchAll, + BuiltinId::SymAsyncIterator, ]; } @@ -557,7 +559,8 @@ impl KernelSession { || gen(BuiltinId::SymIterator) != snap(BuiltinId::SymIterator) || gen(BuiltinId::SymToPrimitive) != snap(BuiltinId::SymToPrimitive) || gen(BuiltinId::SymHasInstance) != snap(BuiltinId::SymHasInstance) - || gen(BuiltinId::SymMatchAll) != snap(BuiltinId::SymMatchAll), + || gen(BuiltinId::SymMatchAll) != snap(BuiltinId::SymMatchAll) + || gen(BuiltinId::SymAsyncIterator) != snap(BuiltinId::SymAsyncIterator), math: gen(BuiltinId::MathObject) != snap(BuiltinId::MathObject), json: gen(BuiltinId::JsonObject) != snap(BuiltinId::JsonObject), date: gen(BuiltinId::DateConstructor) != snap(BuiltinId::DateConstructor) diff --git a/crates/oxide_types/src/private_key.rs b/crates/oxide_types/src/private_key.rs index 0e2f85d3..fc6db39f 100644 --- a/crates/oxide_types/src/private_key.rs +++ b/crates/oxide_types/src/private_key.rs @@ -17,7 +17,7 @@ pub const PRIVATE_NAME_BASE: u32 = 0x8000_0000; pub const SYMBOL_KEY_BASE: u32 = 0xA000_0000; /// 保留给 well-known symbol 的键槽位数(位于 Symbol 键区间最低端)。 -pub const WELL_KNOWN_SYMBOL_COUNT: u32 = 8; +pub const WELL_KNOWN_SYMBOL_COUNT: u32 = 9; /// 用户 symbol 下标的保留位掩码(低 28 位)。 const SYMBOL_INDEX_MASK: u32 = 0x0FFF_FFFF; diff --git a/crates/oxide_vm/src/async_from_sync.rs b/crates/oxide_vm/src/async_from_sync.rs new file mode 100644 index 00000000..6775fccb --- /dev/null +++ b/crates/oxide_vm/src/async_from_sync.rs @@ -0,0 +1,402 @@ +//! AsyncFromSyncIterator:把同步迭代器包装为异步迭代器,供 for-await-of 回退消费。 +//! +//! GetAsyncIterator 对没有 `@@asyncIterator` 的普通可迭代对象(数组/字符串等) +//! 走同步迭代协议取迭代器,再包一层 AsyncFromSyncIterator:其 `next`/`return`/`throw` +//! 转发内层同步迭代器并把结果包成 Promise(非对象结果按 TypeError 拒绝), +//! `@@asyncIterator` 返回自身。 + +use oxide_builtins::iterator::make_iter_result; +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_runtime_api::NativeResult; +use oxide_types::object::{JsObject, NativeFnPtr}; +use oxide_types::private_key::make_well_known_symbol_key; +use oxide_types::value::JsValue; + +use crate::vm::Vm; + +/// AsyncFromSyncIterator 对象上存内层同步迭代器的属性名。 +const INNER_PROP: &str = "__inner__"; +/// `@@asyncIterator` 的 well-known symbol 键序号。 +const ASYNC_ITERATOR_SYMBOL_ID: u32 = 8; + +/// GetAsyncIterator(ECMA-262):取 `value[@@asyncIterator]`,不可调用时回退同步 +/// 迭代器并包 AsyncFromSyncIterator。 +/// +/// # 步骤 +/// 1. ToObject 后读取 `@@asyncIterator`,可调用则调用并校验结果为对象。 +/// 2. 否则经同步迭代协议取迭代器包装器,包成 AsyncFromSyncIterator。 +/// +/// # 返回值 +/// - `Ok(iterator)`:异步迭代器对象; +/// - `Err`:`@@asyncIterator` getter/调用抛错或 ToObject 失败,透传原异常值。 +pub(crate) fn make_async_iterator(vm: &mut Vm, value: JsValue) -> Result { + let obj_value = if value.is_object() { + value + } else { + match oxide_runtime_api::to_object(value, vm) { + Ok(o) => o, + Err(e) => return Err(oxide_builtins::error::create_type_error(vm, &e)), + } + }; + let obj = unsafe { &*obj_value.as_js_object_ptr() }; + let aiter_key = make_well_known_symbol_key(ASYNC_ITERATOR_SYMBOL_ID); + let method = match vm.ordinary_get(obj, aiter_key, obj_value) { + Ok(m) => m, + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_type_error(vm, &e)); + return Err(exc); + } + }; + if oxide_builtins::iterator::is_callable(method) { + let iterator = match vm.call_function_sync(method, obj_value, &[]) { + Ok(it) => it, + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_type_error(vm, &e)); + return Err(exc); + } + }; + if !iterator.is_object() { + return Err(oxide_builtins::error::create_type_error( + vm, + "Result of the Symbol.asyncIterator method is not an object", + )); + } + return Ok(iterator); + } + let sync = oxide_builtins::iterator::make_iterator_for_value(vm, value)?; + Ok(vm.create_async_from_sync_iterator(sync)) +} + +impl Vm { + /// 创建 AsyncFromSyncIterator 包装器:挂内层同步迭代器,装 next/return/throw 与 + /// `@@asyncIterator`(返回自身)方法。 + pub(crate) fn create_async_from_sync_iterator(&mut self, sync_iter: JsValue) -> JsValue { + let next_fn = make_wrapper_fn(self, async_from_sync_next as *const ()); + let return_fn = make_wrapper_fn(self, async_from_sync_return as *const ()); + let throw_fn = make_wrapper_fn(self, async_from_sync_throw as *const ()); + let self_fn = make_wrapper_fn(self, async_from_sync_symbol_async_iterator as *const ()); + let object_proto = self.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto))); + let obj = unsafe { &mut *ptr }; + let inner_si = self.kernel_core.perm_interner().intern(INNER_PROP).0; + self.set_or_create_prop_value(obj, inner_si, sync_iter); + let next_si = self.kernel_core.perm_interner().intern("next").0; + self.set_or_create_prop_value(obj, next_si, next_fn); + let return_si = self.kernel_core.perm_interner().intern("return").0; + self.set_or_create_prop_value(obj, return_si, return_fn); + let throw_si = self.kernel_core.perm_interner().intern("throw").0; + self.set_or_create_prop_value(obj, throw_si, throw_fn); + let aiter_key = make_well_known_symbol_key(ASYNC_ITERATOR_SYMBOL_ID); + self.set_or_create_prop_value(obj, aiter_key, self_fn); + JsValue::from_js_object(ptr) + } +} + +/// 构造包装器方法 native 函数对象(name/length 属性由 `add_fn_name_length` 补全)。 +fn make_wrapper_fn(vm: &mut Vm, native_fn: *const ()) -> JsValue { + let fn_proto = vm.session.builtin_world().fn_proto_val(); + let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); + func.set_function(true); + // SAFETY: native_fn 来自 NativeFn 函数项。 + func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(native_fn) })); + func.set_native_arg_count(1); + let ptr = vm.alloc_object(func); + let obj = unsafe { &mut *ptr }; + vm.add_fn_name_length(obj, "", 1); + JsValue::from_js_object(ptr) +} + +/// 读取包装器对象上的内层同步迭代器;接收者非法或内层缺失时返回 TypeError。 +fn sync_inner(vm: &mut Vm, this_val: JsValue) -> Result { + if !this_val.is_object() { + return Err(oxide_builtins::error::create_type_error( + vm, + "AsyncFromSyncIterator methods called on non-object", + )); + } + let obj = unsafe { &*this_val.as_js_object_ptr() }; + let inner_si = vm.kernel_core.perm_interner().intern(INNER_PROP).0; + match vm.ordinary_get(obj, inner_si, this_val) { + Ok(inner) if !inner.is_undefined() => Ok(inner), + _ => Err(oxide_builtins::error::create_type_error(vm, "AsyncFromSyncIterator has no sync iterator")), + } +} + +/// AsyncFromSyncIteratorContinuation 的 unwrap 函数闭包上存目标 resolve 的属性名。 +const ASFS_RESOLVE_PROP: &str = "__oxide_asfs_resolve__"; +/// unwrap 函数闭包上存 done 标志的属性名。 +const ASFS_DONE_PROP: &str = "__oxide_asfs_done__"; +/// reject-close 闭包上存目标 reject 的属性名。 +const ASFS_REJECT_PROP: &str = "__oxide_asfs_reject__"; +/// reject-close 闭包上存内层同步迭代器的属性名。 +const ASFS_INNER_PROP: &str = "__oxide_asfs_inner__"; + +/// AsyncFromSyncIteratorContinuation:解出同步迭代器结果对象的 done/value, +/// value 经 PromiseResolve 后 await,最终以 `{value: 解开值, done}` 结算能力。 +/// 任一步骤抛错(done/value getter、PromiseResolve 的 constructor getter)都拒绝; +/// `close_on_rejection` 时 valueWrapper 拒绝还会先关闭内层同步迭代器。 +/// +/// # 参数 +/// - `inner`:内层同步迭代器(关闭目标)。 +/// - `result`:内层同步方法调用结果(`Err` 为调用抛出的字符串展平文本)。 +fn continue_async_from_sync(vm: &mut Vm, inner: JsValue, result: Result, close_on_rejection: bool) -> NativeResult { + let (promise, resolve, reject) = vm.new_promise_capability(); + let reject_with = |vm: &mut Vm, exc: JsValue| { + if close_on_rejection { + close_sync_iterator(vm, inner); + } + let _ = vm.reject_promise(promise, exc); + }; + match result { + Ok(r) if r.is_object() => { + let result_obj = unsafe { &*r.as_js_object_ptr() }; + let done_si = vm.kernel_core.perm_interner().intern("done").0; + let done = match vm.ordinary_get(result_obj, done_si, r) { + Ok(d) => oxide_runtime_api::to_boolean(d), + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(vm, &e)); + reject_with(vm, exc); + return NativeResult::Ok(promise); + } + }; + let value_si = vm.kernel_core.perm_interner().intern("value").0; + let value = match vm.ordinary_get(result_obj, value_si, r) { + Ok(v) => v, + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(vm, &e)); + reject_with(vm, exc); + return NativeResult::Ok(promise); + } + }; + let value_wrapper = match vm.promise_resolve(value) { + Ok(w) => w, + Err(e) => { + reject_with(vm, e); + return NativeResult::Ok(promise); + } + }; + let fulfill = make_unwrap_fn(vm, resolve, done); + let on_rejected = if close_on_rejection { + make_reject_close_fn(vm, reject, inner) + } else { + reject + }; + let _ = vm.perform_promise_then(value_wrapper, fulfill, on_rejected); + NativeResult::Ok(promise) + } + Ok(_) => { + let err = oxide_builtins::error::create_type_error(vm, "sync iterator result is not an object"); + reject_with(vm, err); + NativeResult::Ok(promise) + } + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(vm, &e)); + reject_with(vm, exc); + NativeResult::Ok(promise) + } + } +} + +/// 同步关闭内层迭代器(IteratorClose 的 return 调用),忽略 return 自身抛错。 +fn close_sync_iterator(vm: &mut Vm, inner: JsValue) { + if !inner.is_object() { + return; + } + let inner_obj = unsafe { &*inner.as_js_object_ptr() }; + let return_si = vm.kernel_core.perm_interner().intern("return").0; + let return_fn = match vm.ordinary_get(inner_obj, return_si, inner) { + Ok(f) => f, + Err(_) => return, + }; + if oxide_builtins::iterator::is_callable(return_fn) { + let _ = vm.call_function_sync(return_fn, inner, &[]); + } +} + +/// 构造 valueWrapper 拒绝时关闭迭代器的闭包:携带目标 reject 与内层同步迭代器。 +fn make_reject_close_fn(vm: &mut Vm, reject: JsValue, inner: JsValue) -> JsValue { + let fn_proto = vm.session.builtin_world().fn_proto_val(); + let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); + func.set_function(true); + // SAFETY: async_from_sync_reject_close 是 NativeFn 函数项。 + func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(async_from_sync_reject_close as *const ()) })); + func.set_native_arg_count(1); + let ptr = vm.alloc_object(func); + let obj = unsafe { &mut *ptr }; + let rej_si = vm.kernel_core.perm_interner().intern(ASFS_REJECT_PROP).0; + vm.set_or_create_prop_value(obj, rej_si, reject); + let inner_si = vm.kernel_core.perm_interner().intern(ASFS_INNER_PROP).0; + vm.set_or_create_prop_value(obj, inner_si, inner); + vm.add_fn_name_length(obj, "", 1); + JsValue::from_js_object(ptr) +} + +/// reject-close 闭包:关闭内层同步迭代器后以原拒绝原因结算能力。 +fn async_from_sync_reject_close(vm: &mut Vm, args: &[u8]) -> NativeResult { + let callee = vm.reg(254); + if !callee.is_object() { + return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "reject handler is invalid")); + } + let callee_obj = unsafe { &*callee.as_js_object_ptr() }; + let rej_si = vm.kernel_core.perm_interner().intern(ASFS_REJECT_PROP).0; + let reject = vm.resolve_property(callee_obj, rej_si).unwrap_or(JsValue::undefined()); + let inner_si = vm.kernel_core.perm_interner().intern(ASFS_INNER_PROP).0; + let inner = vm.resolve_property(callee_obj, inner_si).unwrap_or(JsValue::undefined()); + let error = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + close_sync_iterator(vm, inner); + let _ = vm.call_function_sync(reject, JsValue::undefined(), &[error]); + NativeResult::Ok(JsValue::undefined()) +} + +/// 构造 AsyncFromSyncIteratorContinuation 的 value unwrap 闭包:携带目标 resolve 与 +/// done 标志,valueWrapper settle 后把解开值包成 `{value, done}` 结算。 +fn make_unwrap_fn(vm: &mut Vm, resolve: JsValue, done: bool) -> JsValue { + let fn_proto = vm.session.builtin_world().fn_proto_val(); + let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); + func.set_function(true); + // SAFETY: async_from_sync_unwrap 是 NativeFn 函数项。 + func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(async_from_sync_unwrap as *const ()) })); + func.set_native_arg_count(1); + let ptr = vm.alloc_object(func); + let obj = unsafe { &mut *ptr }; + let res_si = vm.kernel_core.perm_interner().intern(ASFS_RESOLVE_PROP).0; + vm.set_or_create_prop_value(obj, res_si, resolve); + let done_si = vm.kernel_core.perm_interner().intern(ASFS_DONE_PROP).0; + vm.set_or_create_prop_value(obj, done_si, JsValue::bool(done)); + vm.add_fn_name_length(obj, "", 1); + JsValue::from_js_object(ptr) +} + +/// value unwrap 闭包:读自身 prop 的 resolve 与 done,把入参包装为迭代器结果结算。 +fn async_from_sync_unwrap(vm: &mut Vm, args: &[u8]) -> NativeResult { + let callee = vm.reg(254); + if !callee.is_object() { + return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "unwrap handler is invalid")); + } + let callee_obj = unsafe { &*callee.as_js_object_ptr() }; + let res_si = vm.kernel_core.perm_interner().intern(ASFS_RESOLVE_PROP).0; + let resolve = vm.resolve_property(callee_obj, res_si).unwrap_or(JsValue::undefined()); + let done_si = vm.kernel_core.perm_interner().intern(ASFS_DONE_PROP).0; + let done = vm + .resolve_property(callee_obj, done_si) + .map_or(false, oxide_runtime_api::to_boolean); + let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let result = make_iter_result(vm, value, done); + let _ = vm.call_function_sync(resolve, JsValue::undefined(), &[result]); + NativeResult::Ok(JsValue::undefined()) +} + +/// `AsyncFromSyncIterator.next(value)`:转发内层 `next`,结果经 continuation 结算。 +/// 未传实参时不向 `next` 传参(与 IteratorNext 的空实参调用一致)。 +fn async_from_sync_next(vm: &mut Vm, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let inner = match sync_inner(vm, this_val) { + Ok(i) => i, + Err(e) => return NativeResult::Err(e), + }; + let next_si = vm.kernel_core.perm_interner().intern("next").0; + let next_fn = match vm.ordinary_get(unsafe { &*inner.as_js_object_ptr() }, next_si, inner) { + Ok(f) => f, + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(vm, &e)); + return NativeResult::Err(exc); + } + }; + let result = if args.len() > 1 { + vm.call_function_sync(next_fn, inner, &[vm.reg(args[1])]) + } else { + vm.call_function_sync(next_fn, inner, &[]) + }; + continue_async_from_sync(vm, inner, result, true) +} + +/// `AsyncFromSyncIterator.return(value)`:内层无 `return` 时直接以 `{value, done:true}` +/// 完成,否则转发并经 continuation 结算。 +fn async_from_sync_return(vm: &mut Vm, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let arg = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let inner = match sync_inner(vm, this_val) { + Ok(i) => i, + Err(e) => return NativeResult::Err(e), + }; + let return_si = vm.kernel_core.perm_interner().intern("return").0; + let return_fn = match vm.ordinary_get(unsafe { &*inner.as_js_object_ptr() }, return_si, inner) { + Ok(f) => f, + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(vm, &e)); + return NativeResult::Err(exc); + } + }; + if !oxide_builtins::iterator::is_callable(return_fn) { + let result = make_iter_result(vm, arg, true); + let (promise, _, _) = vm.new_promise_capability(); + let _ = vm.resolve_promise(promise, result); + return NativeResult::Ok(promise); + } + let result = if args.len() > 1 { + vm.call_function_sync(return_fn, inner, &[arg]) + } else { + vm.call_function_sync(return_fn, inner, &[]) + }; + continue_async_from_sync(vm, inner, result, false) +} + +/// `AsyncFromSyncIterator.throw(value)`:内层无 `throw` 时拒绝,否则转发并经 continuation 结算。 +fn async_from_sync_throw(vm: &mut Vm, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let arg = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let inner = match sync_inner(vm, this_val) { + Ok(i) => i, + Err(e) => return NativeResult::Err(e), + }; + let throw_si = vm.kernel_core.perm_interner().intern("throw").0; + let throw_fn = match vm.ordinary_get(unsafe { &*inner.as_js_object_ptr() }, throw_si, inner) { + Ok(f) => f, + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(vm, &e)); + return NativeResult::Err(exc); + } + }; + if !oxide_builtins::iterator::is_callable(throw_fn) { + let (promise, _, _reject) = vm.new_promise_capability(); + let err = oxide_builtins::error::create_type_error(vm, "sync iterator has no throw method"); + let _ = vm.reject_promise(promise, err); + return NativeResult::Ok(promise); + } + let result = if args.len() > 1 { + vm.call_function_sync(throw_fn, inner, &[arg]) + } else { + vm.call_function_sync(throw_fn, inner, &[]) + }; + continue_async_from_sync(vm, inner, result, false) +} + +/// `AsyncFromSyncIterator[@@asyncIterator]`:返回自身。 +fn async_from_sync_symbol_async_iterator(vm: &mut Vm, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + NativeResult::Ok(this_val) +} diff --git a/crates/oxide_vm/src/async_func.rs b/crates/oxide_vm/src/async_func.rs index f5f26b86..96b6b412 100644 --- a/crates/oxide_vm/src/async_func.rs +++ b/crates/oxide_vm/src/async_func.rs @@ -391,11 +391,22 @@ impl Vm { /// - 异步生成器内(`async_gen_dispatch`)登记异步生成器恢复闭包并置 /// `async_gen_suspended`;普通异步函数走 `async_suspended`。 pub(crate) fn dispatch_await(&mut self, rd: usize) -> Result<(), String> { + let value = self.regs[rd]; + self.perform_await(value) + } + + /// await 核心:PromiseResolve 包装被等待值、登记恢复反应,置挂起信号。 + /// + /// # 副作用 + /// - 被等待值为原生 Promise 时直接复用,否则建新 promise 并 PromiseResolve。 + /// - 经 `perform_promise_then` 登记 fulfill/reject 恢复反应(微任务入队)。 + /// - 异步生成器内(`async_gen_dispatch`)登记异步生成器恢复闭包并置 + /// `async_gen_suspended`;普通异步函数走 `async_suspended`。 + pub(crate) fn perform_await(&mut self, value: JsValue) -> Result<(), String> { let ctx = match self.async_context { Some(c) => c, None => return Err("AWAIT executed outside async function".into()), }; - let value = self.regs[rd]; let promise = if self.is_promise_value(value) { value } else { diff --git a/crates/oxide_vm/src/async_generator.rs b/crates/oxide_vm/src/async_generator.rs index 96378194..7f1cd051 100644 --- a/crates/oxide_vm/src/async_generator.rs +++ b/crates/oxide_vm/src/async_generator.rs @@ -826,6 +826,12 @@ pub(crate) fn async_generator_throw(vm: &mut Vm, args: &[u8]) -> NativeResult { } } +/// `%AsyncGeneratorPrototype%[@@asyncIterator]`:异步生成器自身即是异步迭代器,返回 `this`。 +pub(crate) fn async_generator_symbol_async_iterator(vm: &mut Vm, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + NativeResult::Ok(this_val) +} + /// 初始化/重建异步生成器内建对象:`%AsyncGeneratorPrototype%`、 /// `%AsyncGeneratorFunction.prototype%` 与占位 `%AsyncGeneratorFunction%`。 /// @@ -853,6 +859,18 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) { ag_proto.set_shape_id(tag_shape); let tag_pos = ag_proto.push_prop(JsValue::perm_string(sf.string_ptr(sf.intern("AsyncGenerator").0))); ag_proto.set_data_meta(tag_pos, PropAttributes::new(false, false, true)); + // @@asyncIterator:返回自身(异步生成器是异步可迭代对象,供 for-await-of 消费)。 + let aiter_key = oxide_types::private_key::make_well_known_symbol_key(8); + let _ = oxide_kernel::builtin::BuiltinWorld::bind_method_key_static( + &mut ag_proto, + sh, + sf, + aiter_key, + "@@asyncIterator", + unsafe { oxide_types::object::NativeFnPtr::from_raw(async_generator_symbol_async_iterator as *const ()) }, + 0, + fn_proto_val, + ); vm.async_generator_proto = P::new(*ag_proto); // %AsyncGeneratorFunction.prototype%:proto = Function.prototype,constructor = %AsyncGeneratorFunction%。 diff --git a/crates/oxide_vm/src/bindings/bind_symbol.rs b/crates/oxide_vm/src/bindings/bind_symbol.rs index 378331ad..f1e8247d 100644 --- a/crates/oxide_vm/src/bindings/bind_symbol.rs +++ b/crates/oxide_vm/src/bindings/bind_symbol.rs @@ -66,6 +66,10 @@ pub fn bind_symbol(core: &Arc, session: &KernelSession, global: &mut "matchAll", JsValue::from_js_object(session.builtin_world().sym_match_all.as_ptr() as *mut JsObject), ), + ( + "asyncIterator", + JsValue::from_js_object(session.builtin_world().sym_async_iterator.as_ptr() as *mut JsObject), + ), ] { bind_well_known_symbol(core, ctor, name, val); } diff --git a/crates/oxide_vm/src/lib.rs b/crates/oxide_vm/src/lib.rs index 3a478e76..5cc32b45 100644 --- a/crates/oxide_vm/src/lib.rs +++ b/crates/oxide_vm/src/lib.rs @@ -8,6 +8,7 @@ //! session 内存回收;`bindings` 把内置对象绑定到每个 session 的 global 上。 mod async_func; +mod async_from_sync; mod async_generator; /// 内置对象绑定模块:向 session 的 global 对象安装 Object/Array/... 及各原型。 pub mod bindings; diff --git a/crates/oxide_vm/src/promise.rs b/crates/oxide_vm/src/promise.rs index 190453e7..77641c9a 100644 --- a/crates/oxide_vm/src/promise.rs +++ b/crates/oxide_vm/src/promise.rs @@ -373,6 +373,39 @@ impl Vm { Ok(()) } + /// PromiseResolve(%Promise%, value)核心:value 为原生 Promise 时读取其 + /// `constructor`,与 %Promise% 相同则原样返回;否则新建能力并经能力 resolve + /// 结算(thenable 委托)。constructor getter / 结算抛错时透传原异常值。 + pub(crate) fn promise_resolve(&mut self, value: JsValue) -> Result { + if self.is_promise_value(value) { + let obj = unsafe { &*value.as_js_object_ptr() }; + let ctor_si = self.kernel_core.perm_interner().intern("constructor").0; + let ctor = match self.ordinary_get(obj, ctor_si, value) { + Ok(c) => c, + Err(e) => { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(self, &e)); + return Err(exc); + } + }; + let intrinsic = JsValue::from_js_object(self.promise_constructor.as_ptr() as *mut JsObject); + if oxide_runtime_api::same_value(ctor, intrinsic) { + return Ok(value); + } + } + let (promise, _resolve, _) = self.new_promise_capability(); + if let Err(e) = self.resolve_promise(promise, value) { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(self, &e)); + return Err(exc); + } + Ok(promise) + } + /// `PerformPromiseThen` 核心:注册 fulfill/reject 两条反应;已 settle 则直接入队。 pub(crate) fn perform_promise_then( &mut self, this_val: JsValue, on_fulfilled: JsValue, on_rejected: JsValue, diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 221c21bd..ed9ae0df 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -782,6 +782,8 @@ impl Vm { Some(6) } else if std::ptr::eq(ptr, world.sym_match_all.as_ptr()) { Some(7) + } else if std::ptr::eq(ptr, world.sym_async_iterator.as_ptr()) { + Some(8) } else { None }; @@ -1520,6 +1522,28 @@ impl Vm { self.dispatch_for_of_close()?; } + OpCode::FOR_AWAIT_OF_INIT => { + self.dispatch_for_await_of_init(a)?; + } + + OpCode::FOR_AWAIT_OF_NEXT => { + self.dispatch_for_await_of_next(rd)?; + } + + OpCode::FOR_AWAIT_OF_DONE => { + self.dispatch_for_await_of_done(rd, a)?; + } + + OpCode::FOR_AWAIT_OF_CLOSE => { + self.dispatch_for_await_of_close()?; + // 异步 IteratorClose 可能经 await 挂起(return() 的 promise), + // 挂起时须像 AWAIT 一样让内嵌 dispatch 返回,由恢复方快照状态。 + if self.async_suspended || self.async_gen_suspended { + self.profiling.set_instruction_count(steps); + return Ok(JsValue::undefined()); + } + } + OpCode::REST_OBJECT => { self.dispatch_rest_object(rd, a, b)?; } diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index fd36223b..21d35690 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -460,6 +460,111 @@ impl Vm { } } + /// for-await-of 初始化:按 GetAsyncIterator 协议取异步迭代器(缺失 `@@asyncIterator` + /// 时回退同步迭代器并包 AsyncFromSyncIterator),压入 for-of 迭代器栈。 + pub(crate) fn dispatch_for_await_of_init(&mut self, a: usize) -> Result<(), String> { + vm_trace!("FOR_AWAIT_OF_INIT r{}={:?}", a, self.regs[a]); + let iterable = self.regs[a]; + match crate::async_from_sync::make_async_iterator(self, iterable) { + Ok(iterator) => { + self.iters.push_for_of(iterator); + self.iters.clear_last_for_of_result(); + Ok(()) + } + Err(err) => { + self.exception_value = Some(err); + self.pending_error_kind = Some(self.thrown_error_kind(err)); + self.unwind() + } + } + } + + /// for-await-of 步进:调用迭代器 `next()`,把返回的 promise 写入 rd。 + /// 随后的 `AWAIT` 负责挂起等待;next() 抛出时弹出迭代器并透传(与同步 for-of 一致)。 + pub(crate) fn dispatch_for_await_of_next(&mut self, rd: usize) -> Result<(), String> { + vm_trace!("FOR_AWAIT_OF_NEXT rd={}", rd); + self.last_uncaught_value = None; + let Some(iterator) = self.iters.last_for_of() else { + return Err("FOR_AWAIT_OF_NEXT without active iterator".into()); + }; + if !iterator.is_object() { + return Err("FOR_AWAIT_OF_NEXT iterator is not an object".into()); + } + let iter_obj = unsafe { &*iterator.as_js_object_ptr() }; + let next_si = self.kernel_core.perm_interner().intern("next").0; + let next_fn = match self.ordinary_get(iter_obj, next_si, iterator) { + Ok(v) => v, + Err(e) => return self.throw_for_of_error(e), + }; + let result = match self.call_function_sync(next_fn, iterator, &[]) { + Ok(v) => v, + Err(e) => return self.throw_for_of_error(e), + }; + self.regs[rd] = result; + Ok(()) + } + + /// for-await-of 步进完成检查:读取 `AWAIT` 恢复值(a 槽,即迭代器结果对象)的 + /// `done`,写入 last_for_of_result 并把 `!done` 写 rd(供 JMP_IF_FALSE 分支)。 + pub(crate) fn dispatch_for_await_of_done(&mut self, rd: usize, a: usize) -> Result<(), String> { + vm_trace!("FOR_AWAIT_OF_DONE rd={} r{}={:?}", rd, a, self.regs[a]); + let result = self.regs[a]; + if !result.is_object() { + return self.raise_type_error("iterator result is not an object"); + } + self.iters.set_last_for_of_result(result); + let result_obj = unsafe { &*result.as_js_object_ptr() }; + let done_si = self.kernel_core.perm_interner().intern("done").0; + let done_val = match self.ordinary_get(result_obj, done_si, result) { + Ok(v) => v, + Err(e) => return self.throw_for_of_error(e), + }; + self.regs[rd] = JsValue::bool(!to_boolean(done_val)); + Ok(()) + } + + /// for-await-of 收尾:迭代器自然 done 时跳过;否则执行异步 IteratorClose—— + /// 调用 `return()`,其返回的 promise 经 await 挂起,恢复后继续循环后的指令。 + pub(crate) fn dispatch_for_await_of_close(&mut self) -> Result<(), String> { + vm_trace!("FOR_AWAIT_OF_CLOSE"); + let Some(iterator) = self.iters.pop_for_of() else { + return Ok(()); + }; + let result = self.iters.last_for_of_result(); + if result.is_object() { + let result_obj = unsafe { &*result.as_js_object_ptr() }; + let done_si = self.kernel_core.perm_interner().intern("done").0; + if let Ok(done_val) = self.ordinary_get(result_obj, done_si, result) { + if to_boolean(done_val) { + return Ok(()); + } + } + } + if !iterator.is_object() { + return Ok(()); + } + let iter_obj = unsafe { &*iterator.as_js_object_ptr() }; + let return_si = self.kernel_core.perm_interner().intern("return").0; + let return_fn = match self.ordinary_get(iter_obj, return_si, iterator) { + Ok(v) => v, + Err(e) => return Err(e), + }; + if !return_fn.is_object() { + return Ok(()); + } + if !unsafe { &*return_fn.as_js_object_ptr() }.is_function() { + return Ok(()); + } + let inner = match self.call_function_sync(return_fn, iterator, &[]) { + Ok(v) => v, + Err(e) => return Err(e), + }; + if !inner.is_object() { + return self.raise_type_error("iterator return() result is not an object"); + } + self.perform_await(inner) + } + pub(crate) fn dispatch_for_of_done(&mut self, rd: usize) -> Result<(), String> { vm_trace!("FOR_OF_DONE rd={}", rd); let Some(iterator) = self.iters.last_for_of() else {