feat: Promise 基础子系统 + 微任务队列(resolve/reject/then/catch/finally + run() drain)

This commit is contained in:
爱特每羊队
2026-08-09 21:08:53 +08:00
parent c4e7080883
commit dcbe3d9320
11 changed files with 1265 additions and 40 deletions
+31 -28
View File
@@ -459,36 +459,36 @@ impl Vm {
} else {
// 注入模式:next(v) 写 reg 0;throw(e) 恢复异常上下文;return(v) 完成穿越。
match mode {
GeneratorResumeMode::Next(arg) => {
self.regs[0] = arg;
}
GeneratorResumeMode::Throw(exc) => {
self.exception_value = Some(exc);
self.pending_error_kind = Some(self.thrown_error_kind(exc));
if self.unwind().is_err() {
self.restore_inline_state(saved);
self.native_call_depth -= 1;
let thrown = self.last_uncaught_value.take().unwrap_or(exc);
unsafe { (*state_ptr).phase = GeneratorPhase::Completed };
return Ok(GeneratorStep::Thrown { value: thrown });
GeneratorResumeMode::Next(arg) => {
self.regs[0] = arg;
}
}
GeneratorResumeMode::Return(value) => {
// 等价于在挂起点执行 return:穿越 finally 或直接交付返回值。
// 直接交付路径走 do_return(帧弹出后需按内嵌 dispatch 语义返回)。
let prev_dispatch = self.generator_dispatch;
self.generator_dispatch = true;
let completed = self.complete_generator_return(value);
self.generator_dispatch = prev_dispatch;
if let Some(completed) = completed? {
self.restore_inline_state(saved);
self.native_call_depth -= 1;
let state = unsafe { &mut *state_ptr };
state.phase = GeneratorPhase::Completed;
state.result = completed;
return Ok(GeneratorStep::Completed { value: completed });
GeneratorResumeMode::Throw(exc) => {
self.exception_value = Some(exc);
self.pending_error_kind = Some(self.thrown_error_kind(exc));
if self.unwind().is_err() {
self.restore_inline_state(saved);
self.native_call_depth -= 1;
let thrown = self.last_uncaught_value.take().unwrap_or(exc);
unsafe { (*state_ptr).phase = GeneratorPhase::Completed };
return Ok(GeneratorStep::Thrown { value: thrown });
}
}
GeneratorResumeMode::Return(value) => {
// 等价于在挂起点执行 return:穿越 finally 或直接交付返回值。
// 直接交付路径走 do_return(帧弹出后需按内嵌 dispatch 语义返回)。
let prev_dispatch = self.generator_dispatch;
self.generator_dispatch = true;
let completed = self.complete_generator_return(value);
self.generator_dispatch = prev_dispatch;
if let Some(completed) = completed? {
self.restore_inline_state(saved);
self.native_call_depth -= 1;
let state = unsafe { &mut *state_ptr };
state.phase = GeneratorPhase::Completed;
state.result = completed;
return Ok(GeneratorStep::Completed { value: completed });
}
}
}
}
}
}
@@ -1039,6 +1039,9 @@ pub(crate) fn rewrite_generator_native(obj: &JsObject, mut rewrite: impl FnMut(J
/// 释放生成器状态盒(对象被 GC 回收时),返回释放字节数。
pub(crate) fn drop_generator_native(obj: &JsObject) -> u64 {
if !obj.is_generator_obj() {
return 0;
}
let ptr = obj.native_data() as *mut GeneratorState;
if ptr.is_null() {
return 0;
+2
View File
@@ -14,6 +14,8 @@ mod generator;
mod ic_helper;
/// native 函数签名类型([`native::NativeFn`]),builtin 绑定与 VM 调用约定依赖它。
pub mod native;
/// Promise 运行时(状态盒 + 微任务队列 + 构造器/方法实现)。
pub mod promise;
mod session_arena;
/// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。
pub mod session_gc;
File diff suppressed because it is too large Load Diff
+5
View File
@@ -70,6 +70,11 @@ impl Vm {
crate::generator::rewrite_generator_native(dst_ref, |value| {
self.promote_value_if_epoch_object(value, forwarding)
});
} else if src_ref.is_promise_obj() {
// Promise 状态盒深拷贝到新对象:源盒随 epoch 释放,互不共享。
crate::promise::clone_promise_native_with_rewrite(src_ref, dst_ref, |value| {
self.promote_value_if_epoch_object(value, forwarding)
});
}
dst
}
+19
View File
@@ -92,6 +92,9 @@ impl SessionGc {
if obj.is_generator_obj() {
edges.extend(crate::generator::generator_native_edges(obj));
}
if obj.is_promise_obj() {
edges.extend(crate::promise::promise_native_edges(obj));
}
// 遍历 upvalue cell 中的对象引用。
for cell_ptr in obj.upvalues_slice() {
if cell_ptr.is_null() {
@@ -154,6 +157,13 @@ impl SessionGc {
}
}
}
if obj.is_promise_obj() {
for value in crate::promise::promise_native_edges(obj) {
if value.is_string() {
live.insert(value.as_string_ptr_mut());
}
}
}
}
pub(crate) fn mark(&mut self, vm: &Vm) {
@@ -264,6 +274,7 @@ impl SessionGc {
freed_bytes += typed_array::drop_typed_array_native(obj);
freed_bytes += data_view::drop_data_view_native(obj);
freed_bytes += crate::generator::drop_generator_native(obj);
freed_bytes += crate::promise::drop_promise_native(obj);
freed_bytes
}
@@ -320,6 +331,8 @@ impl SessionGc {
typed_array::clone_typed_array_native_with_rewrite(old_ref, new_ref, |value| value);
} else if old_ref.is_data_view_obj() {
data_view::clone_data_view_native_with_rewrite(old_ref, new_ref, |value| value);
} else if old_ref.is_promise_obj() {
crate::promise::clone_promise_native_with_rewrite(old_ref, new_ref, |value| value);
}
forwarding.insert(old_ptr, new_ptr);
freed_bytes += Self::drop_session_object_heap_data(old_ptr);
@@ -351,6 +364,8 @@ impl SessionGc {
data_view::rewrite_data_view_native(obj, |value| rewrite_forwarded_value(value, &forwarding));
} else if obj.is_generator_obj() {
crate::generator::rewrite_generator_native(obj, |value| rewrite_forwarded_value(value, &forwarding));
} else if obj.is_promise_obj() {
crate::promise::rewrite_promise_native(obj, |value| rewrite_forwarded_value(value, &forwarding));
}
}
@@ -533,6 +548,10 @@ fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObje
*value = rewrite_forwarded_value(*value, forwarding);
}
vm.iters.last_for_of_result = rewrite_forwarded_value(vm.iters.last_for_of_result, forwarding);
// 微任务队列中的值随 sweep 重写。
for job in &mut vm.job_queue {
crate::promise::rewrite_job_values(job, |value| rewrite_forwarded_value(value, forwarding));
}
// 已转换的不可变常量不是 session 值(标量 + perm 字符串)——不重写。
for iter in &mut vm.iters.for_in_iters {
if iter.is_null() {
+11
View File
@@ -1,5 +1,6 @@
#![allow(clippy::arc_with_non_send_sync)]
use std::collections::VecDeque;
use std::sync::{Arc, OnceLock};
use oxide_bytecode::module::{CompiledModule, Constant};
@@ -244,6 +245,12 @@ pub struct Vm {
/// `%GeneratorFunction.prototype%`:生成器函数对象的原型(`constructor` 指向
/// `%GeneratorFunction%`,使 `g.constructor.name` 解析为 "GeneratorFunction")。
pub generator_function_proto: P<JsObject>,
/// `%Promise%` 构造器(resolve/reject 静态方法挂此,global 的 Promise 槽指向它)。
pub promise_constructor: P<JsObject>,
/// `%Promise.prototype%`:Promise 实例的原型(then/catch/finally 方法挂此)。
pub promise_proto: P<JsObject>,
/// 微任务队列(Promise reactions / thenable 委托),`run()` 末尾 FIFO drain。
pub(crate) job_queue: VecDeque<crate::promise::Microtask>,
pub math_rng_state: u64,
/// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。
/// 闭包 `sub_module_index` 即 flat_id,逃逸闭包也能自足解析。
@@ -526,6 +533,10 @@ impl Vm {
f(v);
}
f(self.iters.last_for_of_result);
// 微任务队列中的处理器/能力/值都是 GC 根。
for job in &self.job_queue {
crate::promise::for_each_job_value(job, &mut f);
}
// 已转换的不可变常量(标量 + perm 字符串)不作为 session GC 根,不参与扫描。
for iter in &self.iters.for_in_iters {
if iter.is_null() {
+4 -1
View File
@@ -236,7 +236,10 @@ impl Vm {
let global = self.session.global_object();
self.regs[254] = JsValue::from_js_object(global.as_ptr() as *mut JsObject);
self.dispatch()
let result = self.dispatch();
// 顶层执行结束后 drain 微任务队列:Promise reactions 与 thenable 委托在此执行。
self.drain_job_queue();
result
}
pub(crate) fn unwind(&mut self) -> Result<(), String> {
+20 -1
View File
@@ -1,5 +1,6 @@
#![allow(clippy::arc_with_non_send_sync)]
use std::collections::VecDeque;
use std::sync::Arc;
use oxide_bytecode::module::Constant;
@@ -34,6 +35,9 @@ impl Vm {
object_prototype: obj_proto,
generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
promise_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
promise_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
job_queue: VecDeque::new(),
math_rng_state: 0,
sub_modules: Arc::new(Vec::new()),
saved_bytecode_stack: Vec::new(),
@@ -86,6 +90,9 @@ impl Vm {
cell_stack: Vec::new(),
};
vm.init_generator_intrinsics();
vm.init_promise_intrinsics();
// Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。
vm.session.record_snapshot();
vm_info!("Vm created");
vm
}
@@ -108,6 +115,9 @@ impl Vm {
object_prototype: obj_proto,
generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
promise_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
promise_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
job_queue: VecDeque::new(),
math_rng_state: 0,
sub_modules: Arc::new(Vec::new()),
saved_bytecode_stack: Vec::new(),
@@ -160,6 +170,9 @@ impl Vm {
cell_stack: Vec::new(),
};
vm.init_generator_intrinsics();
vm.init_promise_intrinsics();
// Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。
vm.session.record_snapshot();
vm_info!("Vm created (pool)");
vm
}
@@ -185,9 +198,11 @@ impl Vm {
let global = unsafe { &mut *global_ptr };
bindings::bind_global_builtin_slots(&self.kernel_core, &self.session, global);
}
self.session.record_snapshot();
self.object_prototype = P::clone(&self.session.builtin_world().object_proto);
self.init_generator_intrinsics();
self.init_promise_intrinsics();
// 快照须在 Promise 全局绑定之后采集:绑定会修改 global 世代。
self.session.record_snapshot();
self.clear_full_reset_state();
vm_info!("full_reset completed");
}
@@ -199,6 +214,8 @@ impl Vm {
bindings::init_kernel_builtins(&self.kernel_core, &mut self.session);
self.object_prototype = P::clone(&self.session.builtin_world().object_proto);
self.init_generator_intrinsics();
self.init_promise_intrinsics();
self.session.record_snapshot();
self.clear_full_reset_state();
}
@@ -258,6 +275,8 @@ impl Vm {
self.generator_init_step = false;
self.generator_body_started = false;
self.native_call_depth = 0;
// 微任务队列是执行期状态:跨 run 不保留。
self.job_queue.clear();
}
/// 轻量重置:清空执行状态并回收 epoch 内存,但保留 session 字符串与 builtin。
+101
View File
@@ -0,0 +1,101 @@
use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::promise::promise_settled_value;
use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module)
}
/// 顶层表达式返回 Promise 时,run() 末尾 drain 已执行微任务,读其结算值。
fn settled(vm: &mut Vm, source: &str) -> (bool, JsValue) {
let result = eval(vm, source).expect("run should succeed");
assert!(result.is_object(), "expected a promise, got {result:?}");
let obj = unsafe { &*result.as_js_object_ptr() };
promise_settled_value(obj).expect("promise should be settled")
}
#[test]
fn resolve_then_drains_chain() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "Promise.resolve(42).then(v => v * 2)");
assert!(ok);
assert_eq!(val.as_double(), 84.0);
}
#[test]
fn chain_orders_fifo() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "Promise.resolve(1).then(v => v + 1).then(v => v * 10)");
assert!(ok);
assert_eq!(val.as_double(), 20.0);
}
#[test]
fn settle_only_once() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "new Promise((res, rej) => { res(1); res(2); rej(3); }).then(v => v)");
assert!(ok);
assert_eq!(val.as_int(), 1);
}
#[test]
fn throw_in_handler_goes_to_catch() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "Promise.resolve(1).then(v => { throw 'x' }).catch(e => e)");
assert!(ok);
assert_eq!(vm.lookup_str(val).as_deref(), Some("x"));
}
#[test]
fn thenable_delegation() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "Promise.resolve({ then: function(r) { r(7) } }).then(v => v)");
assert!(ok);
assert_eq!(val.as_int(), 7);
}
#[test]
fn finally_passes_through() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "new Promise(r => { r(10) }).finally(() => {}).then(v => v)");
assert!(ok);
assert_eq!(val.as_int(), 10);
}
#[test]
fn reject_propagates_through_empty_then() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "Promise.reject('e').then(undefined, e => 'caught:' + e)");
assert!(ok);
assert_eq!(vm.lookup_str(val).as_deref(), Some("caught:e"));
}
#[test]
fn ctor_receiver_used_for_capability() {
let mut vm = Vm::new();
// Promise.resolve.call(NotPromise) 应构造 NotPromise 并捕获其 executor。
let result = eval(
&mut vm,
"var ef; function NotP(exec) { ef = exec; exec(function(){}, function(){}); } \
Promise.resolve.call(NotP); ef.length",
)
.unwrap();
assert_eq!(result.as_int(), 2);
}
#[test]
fn custom_ctor_failure_is_type_error() {
let mut vm = Vm::new();
// executor 未捕获 resolve/reject → TypeError。
let result = eval(
&mut vm,
"function NotP(exec) { exec(function(){}); } \
try { Promise.resolve.call(NotP); 'no-throw' } catch (e) { e.name }",
)
.unwrap();
assert_eq!(vm.lookup_str(result).as_deref(), Some("TypeError"));
}