feat: Promise 基础子系统 + 微任务队列(resolve/reject/then/catch/finally + run() drain)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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。
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
Reference in New Issue
Block a user