mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
refactor: 从 run 抽 execute_task 与 drain_microtasks 两原语
This commit is contained in:
Generated
+19
-19
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1226,7 +1226,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1235,7 +1235,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1255,7 +1255,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1269,7 +1269,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1284,7 +1284,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_engine"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1296,7 +1296,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1305,7 +1305,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1319,7 +1319,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1331,7 +1331,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1340,7 +1340,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1355,7 +1355,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1371,7 +1371,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1383,7 +1383,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1401,7 +1401,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1414,7 +1414,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.8.78"
|
||||
version = "0.8.79"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -11,15 +11,17 @@ use super::{Microtask, MAX_DRAIN_JOBS};
|
||||
impl Vm {
|
||||
/// drain 微任务队列:FIFO 逐条处理直到清空或达到上限。
|
||||
///
|
||||
/// 调用时机:`run()` 末尾与事件循环每个 turn 边界。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 任务内调用 JS 回调(`call_function_sync`),可能入队新任务。
|
||||
/// - 单任务抛错不中断 drain(未处理拒绝按规范不可见)。
|
||||
pub(crate) fn drain_job_queue(&mut self) {
|
||||
pub fn drain_microtasks(&mut self) {
|
||||
let mut count = 0usize;
|
||||
while let Some(job) = self.job_queue.pop_front() {
|
||||
count += 1;
|
||||
if count > MAX_DRAIN_JOBS {
|
||||
vm_warn!("drain_job_queue: exceeded {MAX_DRAIN_JOBS} microtasks, aborting");
|
||||
vm_warn!("drain_microtasks: exceeded {MAX_DRAIN_JOBS} microtasks, aborting");
|
||||
break;
|
||||
}
|
||||
self.run_microtask(job);
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
//! Promise 对象把 `PromiseState` 状态盒(Box)挂在 `native_data`;resolve/reject
|
||||
//! 是携带目标 promise 的 native 闭包函数(函数对象 prop 存 promise 引用)。
|
||||
//! 反应(reaction)与 thenable 委托以 `Microtask` 入队 `Vm::job_queue`,
|
||||
//! 由 `run()` 末尾的 drain 循环 FIFO 执行。所有盒内 JsValue 由 session GC
|
||||
//! 由 `drain_microtasks` 在 `run()` 末尾与事件循环每个 turn 边界 FIFO 执行。
|
||||
//! 所有盒内 JsValue 由 session GC
|
||||
//! 经 Promise 对象边追踪(见 `promise_native_edges` / `drop_promise_native`)。
|
||||
|
||||
use oxide_types::object::JsObject;
|
||||
|
||||
@@ -201,7 +201,8 @@ pub struct Vm {
|
||||
/// f64→string 十六槽 last-value 缓存的值(session 串,经 `for_each_value`
|
||||
/// 登记为 GC 根;`full_reset` 在 session 串释放前清空)。
|
||||
pub(crate) number_to_string_cache_vals: [JsValue; 16],
|
||||
/// 微任务队列(Promise reactions / thenable 委托),`run()` 末尾 FIFO drain。
|
||||
/// 微任务队列(Promise reactions / thenable 委托),由 `drain_microtasks`
|
||||
/// 在 `run()` 末尾与事件循环每个 turn 边界 FIFO drain。
|
||||
pub(crate) job_queue: VecDeque<crate::promise::Microtask>,
|
||||
/// Atomics.waitAsync waiter 表:键 = (缓冲对象指针, 元素字节偏移),值 =
|
||||
/// 登记的 promise FIFO。键取登记时刻视图的 `buffer` 现指针(同 run 无 GC 时
|
||||
|
||||
@@ -647,6 +647,23 @@ impl Vm {
|
||||
self.tables.retain(|gen, _| live_gens.contains(gen));
|
||||
}
|
||||
|
||||
/// 执行一个 JS 任务:闭包是任务体,本方法执行它并原样返回结果。
|
||||
///
|
||||
/// 任务体在 `run()` 是 `dispatch` 主循环,在 worker 消息是单次函数调用。
|
||||
/// 本方法是任务边界原语,不排空微任务队列(排空时机由事件循环决定)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 闭包返回 `Err`(未捕获异常)时原样上交,不吞并、不改写。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 任务体对 `self` 的全部改写(寄存器 / 帧栈 / 对象表 / 异常侧通道)原样保留。
|
||||
pub fn execute_task(
|
||||
&mut self,
|
||||
task: impl FnOnce(&mut Self) -> Result<JsValue, String>,
|
||||
) -> Result<JsValue, String> {
|
||||
task(self)
|
||||
}
|
||||
|
||||
/// 加载并执行一个已编译模块,返回模块顶层执行结果或未捕获异常消息。
|
||||
///
|
||||
/// 模块以 `Arc` 与调用方共享(如 CodeForge 缓存条目):平表装载只做
|
||||
@@ -701,9 +718,9 @@ impl Vm {
|
||||
self.top_level_this = this_val;
|
||||
self.regs[254] = this_val;
|
||||
|
||||
let result = self.dispatch();
|
||||
let result = self.execute_task(|vm| vm.dispatch());
|
||||
// 顶层执行结束后 drain 微任务队列:Promise reactions 与 thenable 委托在此执行。
|
||||
self.drain_job_queue();
|
||||
self.drain_microtasks();
|
||||
// 指令周期采样:run 末聚合本 run 的样本并输出 top-K 直方图(关闭时零开销
|
||||
// 短路)。放 dispatch 返回后而非循环内:嵌套 dispatch 会多次返回,直方图
|
||||
// 只应输出一次。
|
||||
@@ -1004,4 +1021,82 @@ mod tests {
|
||||
assert_eq!(vm.regs[254], JsValue::float(254.0));
|
||||
assert_eq!(vm.regs[255], JsValue::float(255.0));
|
||||
}
|
||||
|
||||
fn global_value(vm: &mut Vm, name: &str) -> JsValue {
|
||||
let key = vm.kernel_core.perm_interner().intern(name).0;
|
||||
let session = vm.realm.session.borrow();
|
||||
let global = session.global_object();
|
||||
let pos = vm
|
||||
.kernel_core
|
||||
.shape_forge()
|
||||
.lookup_position(global.shape_id(), key)
|
||||
.expect("global slot");
|
||||
global.get_prop_at(pos)
|
||||
}
|
||||
|
||||
fn as_number(v: &JsValue) -> f64 {
|
||||
if v.is_int() {
|
||||
v.as_int() as f64
|
||||
} else {
|
||||
v.as_double()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_task_returns_closure_result() {
|
||||
// 任务体为单次 call_function_sync 调用,execute_task 原样返回闭包结果。
|
||||
let mut vm = Vm::new();
|
||||
let module = Arc::new(compile("function f() { return 42; }"));
|
||||
vm.run(&module).expect("run");
|
||||
let f = global_value(&mut vm, "f");
|
||||
let result = vm.execute_task(|vm| vm.call_function_sync(f, JsValue::undefined(), &[]));
|
||||
assert_eq!(as_number(&result.expect("execute_task")), 42.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_task_propagates_closure_error() {
|
||||
// 任务体抛未捕获异常:execute_task 原样上交 Err,last_uncaught_value 侧通道按既有语义填充。
|
||||
let mut vm = Vm::new();
|
||||
let module = Arc::new(compile("function f() { throw new Error('boom'); }"));
|
||||
vm.run(&module).expect("run");
|
||||
let f = global_value(&mut vm, "f");
|
||||
let result = vm.execute_task(|vm| vm.call_function_sync(f, JsValue::undefined(), &[]));
|
||||
assert!(result.is_err(), "未捕获异常须以 Err 上交");
|
||||
assert!(vm.last_uncaught_value.is_some(), "last_uncaught_value 侧通道须填充");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drain_microtasks_runs_promise_reactions() {
|
||||
// 任务体建 promise 并挂 .then:execute_task 不排空微任务队列,反应留在队列,
|
||||
// drain_microtasks 执行之,侧效应发生。
|
||||
let mut vm = Vm::new();
|
||||
let module = Arc::new(compile(
|
||||
"var done = false; \
|
||||
function make() { Promise.resolve(1).then(function (x) { done = true; }); }",
|
||||
));
|
||||
vm.run(&module).expect("run");
|
||||
let make = global_value(&mut vm, "make");
|
||||
vm.execute_task(|vm| vm.call_function_sync(make, JsValue::undefined(), &[]))
|
||||
.expect("execute_task");
|
||||
assert!(!vm.job_queue.is_empty(), "反应应已入队");
|
||||
vm.drain_microtasks();
|
||||
assert!(vm.job_queue.is_empty(), "drain 后队列应清空");
|
||||
assert_eq!(
|
||||
global_value(&mut vm, "done"),
|
||||
JsValue::bool(true),
|
||||
"promise 反应应已置 done = true"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drain_microtasks_empty_queue_idempotent() {
|
||||
// run 后队列为空,连续两次 drain_microtasks 无副作用。
|
||||
let mut vm = Vm::new();
|
||||
let module = Arc::new(compile("var x = 1;"));
|
||||
vm.run(&module).expect("run");
|
||||
assert!(vm.job_queue.is_empty());
|
||||
vm.drain_microtasks();
|
||||
vm.drain_microtasks();
|
||||
assert!(vm.job_queue.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user