feat: Promise 基础子系统 + 微任务队列(resolve/reject/then/catch/finally + run() drain)
This commit is contained in:
@@ -175,7 +175,7 @@ fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
|||||||
let mut guard = pool.spawn();
|
let mut guard = pool.spawn();
|
||||||
match guard.vm_mut().run(&module) {
|
match guard.vm_mut().run(&module) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
format_result(kernel.perm_interner().as_ref(), kernel.shape_forge().as_ref(), result);
|
format_result(guard.vm(), kernel.perm_interner().as_ref(), kernel.shape_forge().as_ref(), result);
|
||||||
ExitCode::SUCCESS
|
ExitCode::SUCCESS
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -186,23 +186,35 @@ fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_result(string_forge: &PermInterner, shape_forge: &ShapeForge, val: JsValue) {
|
fn format_result(vm: &oxide_vm::vm::Vm, string_forge: &PermInterner, shape_forge: &ShapeForge, val: JsValue) {
|
||||||
println!("{}", format_js_value(string_forge, shape_forge, val));
|
println!("{}", format_js_value(vm, string_forge, shape_forge, val));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_js_value(string_forge: &PermInterner, shape_forge: &ShapeForge, val: JsValue) -> String {
|
fn format_js_value(
|
||||||
|
vm: &oxide_vm::vm::Vm, string_forge: &PermInterner, shape_forge: &ShapeForge, val: JsValue,
|
||||||
|
) -> String {
|
||||||
if val.is_string() {
|
if val.is_string() {
|
||||||
// SAFETY: val 已确认是字符串值。
|
// SAFETY: val 已确认是字符串值。
|
||||||
let s = unsafe { (*val.as_string_ptr()).data.clone() };
|
let s = unsafe { (*val.as_string_ptr()).data.clone() };
|
||||||
format!("\"{s}\"")
|
format!("\"{s}\"")
|
||||||
} else if val.is_object() {
|
} else if val.is_object() {
|
||||||
let obj = unsafe { &*val.as_js_object_ptr() };
|
let obj = unsafe { &*val.as_js_object_ptr() };
|
||||||
|
if obj.is_promise_obj() {
|
||||||
|
// 已 settle 的 Promise 打印其结算值(便于 eval 观察微任务结果)。
|
||||||
|
return match oxide_vm::promise::promise_settled_value(obj) {
|
||||||
|
Some((true, v)) => format_js_value(vm, string_forge, shape_forge, v),
|
||||||
|
Some((false, v)) => {
|
||||||
|
format!("Promise {{ <rejected> {} }}", format_js_value(vm, string_forge, shape_forge, v))
|
||||||
|
}
|
||||||
|
None => "Promise { <pending> }".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
if obj.is_function() {
|
if obj.is_function() {
|
||||||
"[Function]".to_string()
|
"[Function]".to_string()
|
||||||
} else if obj.is_array() {
|
} else if obj.is_array() {
|
||||||
format_array(string_forge, shape_forge, obj)
|
format_array(vm, string_forge, shape_forge, obj)
|
||||||
} else {
|
} else {
|
||||||
format_object(string_forge, shape_forge, obj)
|
format_object(vm, string_forge, shape_forge, obj)
|
||||||
}
|
}
|
||||||
} else if val.is_undefined() {
|
} else if val.is_undefined() {
|
||||||
"undefined".to_string()
|
"undefined".to_string()
|
||||||
@@ -211,7 +223,9 @@ fn format_js_value(string_forge: &PermInterner, shape_forge: &ShapeForge, val: J
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_object(string_forge: &PermInterner, shape_forge: &ShapeForge, obj: &JsObject) -> String {
|
fn format_object(
|
||||||
|
vm: &oxide_vm::vm::Vm, string_forge: &PermInterner, shape_forge: &ShapeForge, obj: &JsObject,
|
||||||
|
) -> String {
|
||||||
let mut entries = Vec::new();
|
let mut entries = Vec::new();
|
||||||
let shape_id = obj.shape_id();
|
let shape_id = obj.shape_id();
|
||||||
let mut shape_ids = Vec::new();
|
let mut shape_ids = Vec::new();
|
||||||
@@ -239,7 +253,7 @@ fn format_object(string_forge: &PermInterner, shape_forge: &ShapeForge, obj: &Js
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let name = string_forge.lookup(shape.property_name).unwrap_or_default();
|
let name = string_forge.lookup(shape.property_name).unwrap_or_default();
|
||||||
let val_str = format_js_value(string_forge, shape_forge, prop_val);
|
let val_str = format_js_value(vm, string_forge, shape_forge, prop_val);
|
||||||
entries.push(format!("\"{name}\": {val_str}"));
|
entries.push(format!("\"{name}\": {val_str}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,12 +262,14 @@ fn format_object(string_forge: &PermInterner, shape_forge: &ShapeForge, obj: &Js
|
|||||||
format!("{{{}}}", entries.join(", "))
|
format!("{{{}}}", entries.join(", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_array(string_forge: &PermInterner, shape_forge: &ShapeForge, obj: &JsObject) -> String {
|
fn format_array(
|
||||||
|
vm: &oxide_vm::vm::Vm, string_forge: &PermInterner, shape_forge: &ShapeForge, obj: &JsObject,
|
||||||
|
) -> String {
|
||||||
let len = obj.prop_vec_len();
|
let len = obj.prop_vec_len();
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
for i in 0..len {
|
for i in 0..len {
|
||||||
let val = obj.get_prop_at(i);
|
let val = obj.get_prop_at(i);
|
||||||
items.push(format_js_value(string_forge, shape_forge, val));
|
items.push(format_js_value(vm, string_forge, shape_forge, val));
|
||||||
}
|
}
|
||||||
format!("[{}]", items.join(", "))
|
format!("[{}]", items.join(", "))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -400,6 +400,8 @@ impl JsObject {
|
|||||||
pub const OBJ_TYPE_CONSTRUCTOR: u8 = 9;
|
pub const OBJ_TYPE_CONSTRUCTOR: u8 = 9;
|
||||||
/// 生成器迭代器对象(`function*` 调用返回):状态快照存于 `native_data`。
|
/// 生成器迭代器对象(`function*` 调用返回):状态快照存于 `native_data`。
|
||||||
pub const OBJ_TYPE_GENERATOR: u8 = 10;
|
pub const OBJ_TYPE_GENERATOR: u8 = 10;
|
||||||
|
/// Promise 对象:状态盒(Pending/Fulfilled/Rejected + reactions)存于 `native_data`。
|
||||||
|
pub const OBJ_TYPE_PROMISE: u8 = 11;
|
||||||
/// `is_session_epoch` 字段中的 session 标记位。
|
/// `is_session_epoch` 字段中的 session 标记位。
|
||||||
pub const SESSION_EPOCH_BIT: u8 = 0x01;
|
pub const SESSION_EPOCH_BIT: u8 = 0x01;
|
||||||
/// `is_session_epoch` 字段中的 GC 标记位。
|
/// `is_session_epoch` 字段中的 GC 标记位。
|
||||||
@@ -450,6 +452,11 @@ impl JsObject {
|
|||||||
pub fn is_generator_obj(&self) -> bool {
|
pub fn is_generator_obj(&self) -> bool {
|
||||||
self.type_tag == Self::OBJ_TYPE_GENERATOR
|
self.type_tag == Self::OBJ_TYPE_GENERATOR
|
||||||
}
|
}
|
||||||
|
/// 是否 Promise 对象。
|
||||||
|
#[inline]
|
||||||
|
pub fn is_promise_obj(&self) -> bool {
|
||||||
|
self.type_tag == Self::OBJ_TYPE_PROMISE
|
||||||
|
}
|
||||||
|
|
||||||
/// 构造无属性、可扩展的空对象(`new Object()` 的基础对象)。
|
/// 构造无属性、可扩展的空对象(`new Object()` 的基础对象)。
|
||||||
pub fn new_empty(shape_id: ShapeId, proto: JsValue) -> Self {
|
pub fn new_empty(shape_id: ShapeId, proto: JsValue) -> Self {
|
||||||
|
|||||||
@@ -459,36 +459,36 @@ impl Vm {
|
|||||||
} else {
|
} else {
|
||||||
// 注入模式:next(v) 写 reg 0;throw(e) 恢复异常上下文;return(v) 完成穿越。
|
// 注入模式:next(v) 写 reg 0;throw(e) 恢复异常上下文;return(v) 完成穿越。
|
||||||
match mode {
|
match mode {
|
||||||
GeneratorResumeMode::Next(arg) => {
|
GeneratorResumeMode::Next(arg) => {
|
||||||
self.regs[0] = 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::Throw(exc) => {
|
||||||
GeneratorResumeMode::Return(value) => {
|
self.exception_value = Some(exc);
|
||||||
// 等价于在挂起点执行 return:穿越 finally 或直接交付返回值。
|
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||||||
// 直接交付路径走 do_return(帧弹出后需按内嵌 dispatch 语义返回)。
|
if self.unwind().is_err() {
|
||||||
let prev_dispatch = self.generator_dispatch;
|
self.restore_inline_state(saved);
|
||||||
self.generator_dispatch = true;
|
self.native_call_depth -= 1;
|
||||||
let completed = self.complete_generator_return(value);
|
let thrown = self.last_uncaught_value.take().unwrap_or(exc);
|
||||||
self.generator_dispatch = prev_dispatch;
|
unsafe { (*state_ptr).phase = GeneratorPhase::Completed };
|
||||||
if let Some(completed) = completed? {
|
return Ok(GeneratorStep::Thrown { value: thrown });
|
||||||
self.restore_inline_state(saved);
|
}
|
||||||
self.native_call_depth -= 1;
|
}
|
||||||
let state = unsafe { &mut *state_ptr };
|
GeneratorResumeMode::Return(value) => {
|
||||||
state.phase = GeneratorPhase::Completed;
|
// 等价于在挂起点执行 return:穿越 finally 或直接交付返回值。
|
||||||
state.result = completed;
|
// 直接交付路径走 do_return(帧弹出后需按内嵌 dispatch 语义返回)。
|
||||||
return Ok(GeneratorStep::Completed { value: completed });
|
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 回收时),返回释放字节数。
|
/// 释放生成器状态盒(对象被 GC 回收时),返回释放字节数。
|
||||||
pub(crate) fn drop_generator_native(obj: &JsObject) -> u64 {
|
pub(crate) fn drop_generator_native(obj: &JsObject) -> u64 {
|
||||||
|
if !obj.is_generator_obj() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let ptr = obj.native_data() as *mut GeneratorState;
|
let ptr = obj.native_data() as *mut GeneratorState;
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ mod generator;
|
|||||||
mod ic_helper;
|
mod ic_helper;
|
||||||
/// native 函数签名类型([`native::NativeFn`]),builtin 绑定与 VM 调用约定依赖它。
|
/// native 函数签名类型([`native::NativeFn`]),builtin 绑定与 VM 调用约定依赖它。
|
||||||
pub mod native;
|
pub mod native;
|
||||||
|
/// Promise 运行时(状态盒 + 微任务队列 + 构造器/方法实现)。
|
||||||
|
pub mod promise;
|
||||||
mod session_arena;
|
mod session_arena;
|
||||||
/// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。
|
/// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。
|
||||||
pub mod session_gc;
|
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| {
|
crate::generator::rewrite_generator_native(dst_ref, |value| {
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
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
|
dst
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ impl SessionGc {
|
|||||||
if obj.is_generator_obj() {
|
if obj.is_generator_obj() {
|
||||||
edges.extend(crate::generator::generator_native_edges(obj));
|
edges.extend(crate::generator::generator_native_edges(obj));
|
||||||
}
|
}
|
||||||
|
if obj.is_promise_obj() {
|
||||||
|
edges.extend(crate::promise::promise_native_edges(obj));
|
||||||
|
}
|
||||||
// 遍历 upvalue cell 中的对象引用。
|
// 遍历 upvalue cell 中的对象引用。
|
||||||
for cell_ptr in obj.upvalues_slice() {
|
for cell_ptr in obj.upvalues_slice() {
|
||||||
if cell_ptr.is_null() {
|
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) {
|
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 += typed_array::drop_typed_array_native(obj);
|
||||||
freed_bytes += data_view::drop_data_view_native(obj);
|
freed_bytes += data_view::drop_data_view_native(obj);
|
||||||
freed_bytes += crate::generator::drop_generator_native(obj);
|
freed_bytes += crate::generator::drop_generator_native(obj);
|
||||||
|
freed_bytes += crate::promise::drop_promise_native(obj);
|
||||||
|
|
||||||
freed_bytes
|
freed_bytes
|
||||||
}
|
}
|
||||||
@@ -320,6 +331,8 @@ impl SessionGc {
|
|||||||
typed_array::clone_typed_array_native_with_rewrite(old_ref, new_ref, |value| value);
|
typed_array::clone_typed_array_native_with_rewrite(old_ref, new_ref, |value| value);
|
||||||
} else if old_ref.is_data_view_obj() {
|
} else if old_ref.is_data_view_obj() {
|
||||||
data_view::clone_data_view_native_with_rewrite(old_ref, new_ref, |value| value);
|
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);
|
forwarding.insert(old_ptr, new_ptr);
|
||||||
freed_bytes += Self::drop_session_object_heap_data(old_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));
|
data_view::rewrite_data_view_native(obj, |value| rewrite_forwarded_value(value, &forwarding));
|
||||||
} else if obj.is_generator_obj() {
|
} else if obj.is_generator_obj() {
|
||||||
crate::generator::rewrite_generator_native(obj, |value| rewrite_forwarded_value(value, &forwarding));
|
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);
|
*value = rewrite_forwarded_value(*value, forwarding);
|
||||||
}
|
}
|
||||||
vm.iters.last_for_of_result = rewrite_forwarded_value(vm.iters.last_for_of_result, 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 字符串)——不重写。
|
// 已转换的不可变常量不是 session 值(标量 + perm 字符串)——不重写。
|
||||||
for iter in &mut vm.iters.for_in_iters {
|
for iter in &mut vm.iters.for_in_iters {
|
||||||
if iter.is_null() {
|
if iter.is_null() {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::arc_with_non_send_sync)]
|
#![allow(clippy::arc_with_non_send_sync)]
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::sync::{Arc, OnceLock};
|
use std::sync::{Arc, OnceLock};
|
||||||
|
|
||||||
use oxide_bytecode::module::{CompiledModule, Constant};
|
use oxide_bytecode::module::{CompiledModule, Constant};
|
||||||
@@ -244,6 +245,12 @@ pub struct Vm {
|
|||||||
/// `%GeneratorFunction.prototype%`:生成器函数对象的原型(`constructor` 指向
|
/// `%GeneratorFunction.prototype%`:生成器函数对象的原型(`constructor` 指向
|
||||||
/// `%GeneratorFunction%`,使 `g.constructor.name` 解析为 "GeneratorFunction")。
|
/// `%GeneratorFunction%`,使 `g.constructor.name` 解析为 "GeneratorFunction")。
|
||||||
pub generator_function_proto: P<JsObject>,
|
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,
|
pub math_rng_state: u64,
|
||||||
/// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。
|
/// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。
|
||||||
/// 闭包 `sub_module_index` 即 flat_id,逃逸闭包也能自足解析。
|
/// 闭包 `sub_module_index` 即 flat_id,逃逸闭包也能自足解析。
|
||||||
@@ -526,6 +533,10 @@ impl Vm {
|
|||||||
f(v);
|
f(v);
|
||||||
}
|
}
|
||||||
f(self.iters.last_for_of_result);
|
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 根,不参与扫描。
|
// 已转换的不可变常量(标量 + perm 字符串)不作为 session GC 根,不参与扫描。
|
||||||
for iter in &self.iters.for_in_iters {
|
for iter in &self.iters.for_in_iters {
|
||||||
if iter.is_null() {
|
if iter.is_null() {
|
||||||
|
|||||||
@@ -236,7 +236,10 @@ impl Vm {
|
|||||||
let global = self.session.global_object();
|
let global = self.session.global_object();
|
||||||
self.regs[254] = JsValue::from_js_object(global.as_ptr() as *mut JsObject);
|
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> {
|
pub(crate) fn unwind(&mut self) -> Result<(), String> {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::arc_with_non_send_sync)]
|
#![allow(clippy::arc_with_non_send_sync)]
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use oxide_bytecode::module::Constant;
|
use oxide_bytecode::module::Constant;
|
||||||
@@ -34,6 +35,9 @@ impl Vm {
|
|||||||
object_prototype: obj_proto,
|
object_prototype: obj_proto,
|
||||||
generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
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())),
|
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,
|
math_rng_state: 0,
|
||||||
sub_modules: Arc::new(Vec::new()),
|
sub_modules: Arc::new(Vec::new()),
|
||||||
saved_bytecode_stack: Vec::new(),
|
saved_bytecode_stack: Vec::new(),
|
||||||
@@ -86,6 +90,9 @@ impl Vm {
|
|||||||
cell_stack: Vec::new(),
|
cell_stack: Vec::new(),
|
||||||
};
|
};
|
||||||
vm.init_generator_intrinsics();
|
vm.init_generator_intrinsics();
|
||||||
|
vm.init_promise_intrinsics();
|
||||||
|
// Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。
|
||||||
|
vm.session.record_snapshot();
|
||||||
vm_info!("Vm created");
|
vm_info!("Vm created");
|
||||||
vm
|
vm
|
||||||
}
|
}
|
||||||
@@ -108,6 +115,9 @@ impl Vm {
|
|||||||
object_prototype: obj_proto,
|
object_prototype: obj_proto,
|
||||||
generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
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())),
|
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,
|
math_rng_state: 0,
|
||||||
sub_modules: Arc::new(Vec::new()),
|
sub_modules: Arc::new(Vec::new()),
|
||||||
saved_bytecode_stack: Vec::new(),
|
saved_bytecode_stack: Vec::new(),
|
||||||
@@ -160,6 +170,9 @@ impl Vm {
|
|||||||
cell_stack: Vec::new(),
|
cell_stack: Vec::new(),
|
||||||
};
|
};
|
||||||
vm.init_generator_intrinsics();
|
vm.init_generator_intrinsics();
|
||||||
|
vm.init_promise_intrinsics();
|
||||||
|
// Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。
|
||||||
|
vm.session.record_snapshot();
|
||||||
vm_info!("Vm created (pool)");
|
vm_info!("Vm created (pool)");
|
||||||
vm
|
vm
|
||||||
}
|
}
|
||||||
@@ -185,9 +198,11 @@ impl Vm {
|
|||||||
let global = unsafe { &mut *global_ptr };
|
let global = unsafe { &mut *global_ptr };
|
||||||
bindings::bind_global_builtin_slots(&self.kernel_core, &self.session, global);
|
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.object_prototype = P::clone(&self.session.builtin_world().object_proto);
|
||||||
self.init_generator_intrinsics();
|
self.init_generator_intrinsics();
|
||||||
|
self.init_promise_intrinsics();
|
||||||
|
// 快照须在 Promise 全局绑定之后采集:绑定会修改 global 世代。
|
||||||
|
self.session.record_snapshot();
|
||||||
self.clear_full_reset_state();
|
self.clear_full_reset_state();
|
||||||
vm_info!("full_reset completed");
|
vm_info!("full_reset completed");
|
||||||
}
|
}
|
||||||
@@ -199,6 +214,8 @@ impl Vm {
|
|||||||
bindings::init_kernel_builtins(&self.kernel_core, &mut self.session);
|
bindings::init_kernel_builtins(&self.kernel_core, &mut self.session);
|
||||||
self.object_prototype = P::clone(&self.session.builtin_world().object_proto);
|
self.object_prototype = P::clone(&self.session.builtin_world().object_proto);
|
||||||
self.init_generator_intrinsics();
|
self.init_generator_intrinsics();
|
||||||
|
self.init_promise_intrinsics();
|
||||||
|
self.session.record_snapshot();
|
||||||
self.clear_full_reset_state();
|
self.clear_full_reset_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +275,8 @@ impl Vm {
|
|||||||
self.generator_init_step = false;
|
self.generator_init_step = false;
|
||||||
self.generator_body_started = false;
|
self.generator_body_started = false;
|
||||||
self.native_call_depth = 0;
|
self.native_call_depth = 0;
|
||||||
|
// 微任务队列是执行期状态:跨 run 不保留。
|
||||||
|
self.job_queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 轻量重置:清空执行状态并回收 epoch 内存,但保留 session 字符串与 builtin。
|
/// 轻量重置:清空执行状态并回收 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