refactor: 新建 oxide_engine crate 统一两入口 compile-run 装配

This commit is contained in:
2026-10-08 06:26:01 +00:00
parent 4f9b6ab444
commit 10f97100cd
10 changed files with 317 additions and 165 deletions
+1
View File
@@ -17,6 +17,7 @@ oxide_emit = { path = "../oxide_emit" }
oxide_vm = { path = "../oxide_vm" }
oxide_parser = { path = "../oxide_parser" }
oxide_kernel = { path = "../oxide_kernel" }
oxide_engine = { path = "../oxide_engine" }
oxide_types = { path = "../oxide_types" }
oxide_log = { path = "../oxide_log" }
serde = { version = "1", features = ["derive"] }
+7 -7
View File
@@ -25,7 +25,7 @@ use heartbeat::write_heartbeat;
use judge::{categorize_fail, TestOutcome};
use oxide_log::{Level, LogConfig, Output, SUBSYSTEM_COUNT};
use report::{append_fail_log, first_line, format_fail_categories, format_fail_groupings, format_fail_list};
use runner::{build_runner_kernel, process_path, CURRENT_TEST_PATH};
use runner::{build_runner_engine, process_path, CURRENT_TEST_PATH};
use stats::RunStats;
use supervise::run_supervised;
@@ -145,7 +145,7 @@ fn run_tests() -> bool {
// 确定 worker 数。`KernelCore` + session 状态是 `!Send`(它持有
// 经 Arc 共享的一个 kernel;相反每个 worker 构建并拥有自己的
// kernel + harness 源注册表 + 前缀缓存。只有 `PathBuf` 和
// 引擎 + harness 源注册表 + 前缀缓存。只有 `PathBuf` 和
// `TestResult`(均 `Send`)跨线程。worker 从共享原子游标取测试下标,
// 实现动态负载均衡。
let default_workers = if config.no_skip {
@@ -228,7 +228,7 @@ fn run_tests() -> bool {
std::thread::Builder::new()
.stack_size(16 * 1024 * 1024)
.spawn_scoped(scope, move || {
let mut kernel = build_runner_kernel();
let mut engine = build_runner_engine();
let harness_sources = HARNESS.get_or_init(HarnessSources::new);
let mut stats = RunStats::default();
let mut tests_since_kernel_reset = 0usize;
@@ -254,7 +254,7 @@ fn run_tests() -> bool {
filter,
no_skip,
no_regalloc,
&kernel,
&engine,
harness_sources,
&harness_cache,
);
@@ -302,11 +302,11 @@ fn run_tests() -> bool {
// 重建即全新 forge(结构性必清):旧核连同其表整体丢弃,
// 先 sweep 是对 doomed 核白做一次 O(50k) 清理。
// 重建边界契约:旧核上无存活 VM(VM 每测试局部,此处作用域外)。
debug_assert!(kernel.active_vms() == 0, "kernel rebuild requires no live VMs");
kernel = build_runner_kernel();
debug_assert!(engine.kernel().active_vms() == 0, "kernel rebuild requires no live VMs");
engine = build_runner_engine();
tests_since_kernel_reset = 0;
} else if done % 500 == 0 {
kernel.sweep_runner_forges(); // 批内 50k 兜底(数据依赖)
engine.kernel().sweep_runner_forges(); // 批内 50k 兜底(数据依赖)
}
if done % 500 == 0 || done == total {
test262_info!("progress: {}/{} ({}%)", done, total, done * 100 / total);
+41 -37
View File
@@ -11,8 +11,8 @@ use crate::judge::{
};
use crate::meta::{parse_meta, strip_meta, TestMeta};
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_kernel::kernel::{KernelConfig, KernelCore};
use oxide_engine::Engine;
use oxide_kernel::kernel::KernelConfig;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
use std::path::{Path, PathBuf};
@@ -38,13 +38,13 @@ fn pc_watch_path() -> Option<&'static PathBuf> {
/// 在 catch_unwind 保护下运行单个测试,把引擎 panic 记为失败。
#[expect(clippy::too_many_arguments)]
fn run_test(
path: &Path, source: &str, meta: &TestMeta, kernel: &Arc<KernelCore>, harness: &HarnessSources,
path: &Path, source: &str, meta: &TestMeta, engine: &Engine, harness: &HarnessSources,
harness_cache: &Arc<RwLock<HarnessPrefixCache>>, no_skip: bool, no_regalloc: bool,
) -> TestResult {
let start = std::time::Instant::now();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
run_test_inner(path, source, meta, kernel, harness, harness_cache, no_skip, no_regalloc)
run_test_inner(path, source, meta, engine, harness, harness_cache, no_skip, no_regalloc)
}));
match result {
@@ -96,9 +96,12 @@ impl oxide_emit::module::ModuleSourceLoader for Test262ModuleLoader {
/// 单测执行主流程:拼 harness 前缀 → parse → compile → run;
/// 依据 `negative` 元数据校验期望错误,未实现特性按 no_skip 选择跳过或失败。
///
/// script 臂经 `Engine::compile` 便捷路径(parse + CodeForge 缓存);module 臂
/// 保留本模块的 `parse_module` + 依赖加载器(引擎暂不吸收 module 加载器面)。
#[expect(clippy::too_many_arguments)]
fn run_test_inner(
path: &Path, source: &str, meta: &TestMeta, kernel: &Arc<KernelCore>, harness: &HarnessSources,
path: &Path, source: &str, meta: &TestMeta, engine: &Engine, harness: &HarnessSources,
harness_cache: &Arc<RwLock<HarnessPrefixCache>>, no_skip: bool, no_regalloc: bool,
) -> TestResult {
let start = std::time::Instant::now();
@@ -135,30 +138,31 @@ fn run_test_inner(
}
};
let alloc = oxide_parser::Allocator::default();
let program = match if is_module {
oxide_parser::parse_module(&alloc, &code)
} else {
oxide_parser::parse(&alloc, &code)
} {
Ok(p) => p,
Err(errs) => {
let dur = start.elapsed().as_millis() as u64;
let msg = format!("parse error: {}", errs[0].message);
if meta.negative.is_some() {
return TestResult::pass(path.to_path_buf(), dur, msg);
// module 臂保留本模块的 parse_module + 依赖加载器;script 臂经引擎
// 便捷路径(parse + CodeForge 缓存)。
let module_result = if is_module {
let alloc = oxide_parser::Allocator::default();
let program = match oxide_parser::parse_module(&alloc, &code) {
Ok(p) => p,
Err(errs) => {
let dur = start.elapsed().as_millis() as u64;
let msg = format!("parse error: {}", errs[0].message);
if meta.negative.is_some() {
return TestResult::pass(path.to_path_buf(), dur, msg);
}
return TestResult::fail(path.to_path_buf(), dur, msg);
}
return TestResult::fail(path.to_path_buf(), dur, msg);
}
};
let compiler = if no_regalloc { Compiler::new().with_regalloc(false) } else { Compiler::new() };
let mut loader = Test262ModuleLoader;
compiler
.compile_module(&program, path.to_string_lossy().as_ref(), &mut loader)
.map(Arc::new)
} else {
engine.compile(&code)
};
let compiler = if no_regalloc { Compiler::new().with_regalloc(false) } else { Compiler::new() };
let module = match if is_module {
let mut loader = Test262ModuleLoader;
compiler.compile_module(&program, path.to_string_lossy().as_ref(), &mut loader)
} else {
compiler.compile(&program)
} {
let module = match module_result {
Ok(m) => m,
Err(e) => {
let dur = start.elapsed().as_millis() as u64;
@@ -171,14 +175,14 @@ fn run_test_inner(
}
};
let mut vm = Vm::with_kernel_core(Arc::clone(kernel));
// test262 语料含 eval / Function 动态编译面,注入真实编译服务。
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 独立 Vm 不经池;动态编译服务由 `new_vm` 同口径注入(语料含 eval /
// Function 动态编译面)。
let mut vm = engine.new_vm();
// 监督者注入现场文件路径时开启 VM 的 last-pc 定频写(未注入时零开销)。
if let Some(watch) = pc_watch_path() {
vm.set_pc_watch(Some(watch.clone()));
}
let run_result = vm.run(&Arc::new(module));
let run_result = vm.run(&module);
let dur = start.elapsed().as_millis() as u64;
if is_async {
@@ -216,9 +220,9 @@ pub(crate) fn read_async_output(vm: &Vm) -> String {
/// 串行与并行执行路径共享的每测试管线:
/// 读文件、解析元数据、应用跳过过滤,然后运行。恰好返回一个 `TestResult`。
/// worker 自有状态(`kernel`、`harness_sources`、`harness_cache`)永不跨线程。
/// worker 自有状态(`engine`、`harness_sources`、`harness_cache`)永不跨线程。
pub(crate) fn process_path(
path: &Path, filter: &Option<String>, no_skip: bool, no_regalloc: bool, kernel: &Arc<KernelCore>,
path: &Path, filter: &Option<String>, no_skip: bool, no_regalloc: bool, engine: &Engine,
harness_sources: &HarnessSources, harness_cache: &Arc<RwLock<HarnessPrefixCache>>,
) -> TestResult {
let source = match std::fs::read_to_string(path) {
@@ -258,14 +262,14 @@ pub(crate) fn process_path(
}
}
run_test(path, &source, &meta, kernel, harness_sources, harness_cache, no_skip, no_regalloc)
run_test(path, &source, &meta, engine, harness_sources, harness_cache, no_skip, no_regalloc)
}
/// 构建带步数上限的 runner kernel。每个并行 worker 拥有自己的 kernel,
/// 构建带步数上限的 runner 引擎。每个并行 worker 拥有自己的引擎,
/// 因为 `KernelCore` + session 状态是 `!Send`(持有 `P<JsObject>` =
/// `Arc<JsObject>`,而 `JsObject` 存有裸 `*mut u8` 属性指针)。任何 kernel
/// 形态的对象都不能跨线程边界,因此共享不可能;每 worker 自建是唯一正确设计。
pub(crate) fn build_runner_kernel() -> Arc<KernelCore> {
pub(crate) fn build_runner_engine() -> Engine {
// 约束每个测试的执行步数,使单个死循环 / 未支持特性循环失败(或跳过)
// 而非拖垮整个运行。VM 超限时抛 "VM step limit exceeded" 错误,
// runner 将其归类为 step-limit 结果(默认 skip,--no-skip 下 fail)。
@@ -283,7 +287,7 @@ pub(crate) fn build_runner_kernel() -> Arc<KernelCore> {
// character-class 生成大表),512MiB 上限留 ~252MiB 余量;
// 主要作用是把失控测试的驻留面封顶到上限本身。
kernel_config.max_alloc_bytes = Some(512 * 1024 * 1024);
KernelCore::new(kernel_config)
Engine::new(kernel_config)
}
#[cfg(test)]
@@ -293,6 +297,6 @@ mod tests {
/// 分配上限常量与错误串单测的字面值机器耦合:改 cap 本钉必红。
#[test]
fn runner_alloc_cap_matches_error_string_tests() {
assert_eq!(build_runner_kernel().config.max_alloc_bytes, Some(512 * 1024 * 1024));
assert_eq!(build_runner_engine().kernel().config.max_alloc_bytes, Some(512 * 1024 * 1024));
}
}