mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
refactor: 新建 oxide_engine crate 统一两入口 compile-run 装配
This commit is contained in:
@@ -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"] }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user