mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
perf: 缓存 harness 前缀编译产物免重编译
This commit is contained in:
Generated
+19
-19
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1227,7 +1227,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1236,7 +1236,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1256,7 +1256,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1270,7 +1270,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1285,7 +1285,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_engine"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1297,7 +1297,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1306,7 +1306,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1320,7 +1320,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1332,7 +1332,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1341,7 +1341,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1356,7 +1356,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1372,7 +1372,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1385,7 +1385,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1403,7 +1403,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1416,7 +1416,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
//! 前缀拼接与缓存。全部 `include_str!` 路径相对上 3 级到仓库根,依赖本文件平铺 `src/` 一级,禁子目录。
|
||||
|
||||
use crate::meta::TestMeta;
|
||||
use oxide_bytecode::CompiledModule;
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_engine::Engine;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, OnceLock, RwLock};
|
||||
|
||||
@@ -13,6 +16,10 @@ pub(crate) struct HarnessSources {
|
||||
/// harness 前缀缓存键:由测试 `includes` 列表唯一确定。
|
||||
pub(crate) type HarnessPrefixCache = HashMap<Vec<String>, String>;
|
||||
|
||||
/// harness 前缀编译产物缓存:键为 includes 组合(含 onlyStrict / async 旗标),
|
||||
/// 值为编译后的前缀模块。命中时直接复用,免每测试重解析重编译前缀。
|
||||
pub(crate) type HarnessModuleCache = HashMap<Vec<String>, Arc<CompiledModule>>;
|
||||
|
||||
impl HarnessSources {
|
||||
/// 构建 harness 源注册表(键为文件名,值为编译期内嵌源码)。
|
||||
pub(crate) fn new() -> Self {
|
||||
@@ -94,17 +101,47 @@ pub(crate) fn append_source_chunk(out: &mut String, name: &str, source: &str) {
|
||||
out.push('\n');
|
||||
}
|
||||
|
||||
/// 取测试元数据的 harness 缓存键(即其 `includes` 列表)。
|
||||
/// 取测试元数据的 harness 缓存键(`includes` 列表 + onlyStrict / async 旗标)。
|
||||
/// 旗标入键是因为它们改变前缀内容(use strict 指令 / async 捕获注入)。
|
||||
fn harness_key(meta: &TestMeta) -> Vec<String> {
|
||||
let mut key = meta.includes.clone();
|
||||
if meta.flags.iter().any(|f| f == "onlyStrict") {
|
||||
key.push("__onlyStrict__".into());
|
||||
}
|
||||
if meta.flags.iter().any(|f| f == "async") {
|
||||
key.push("__async__".into());
|
||||
}
|
||||
key
|
||||
}
|
||||
|
||||
/// 把拼接源码中顶层(行首)let/const 声明改写为 var,使其落全局对象、跨 run 可见。
|
||||
///
|
||||
/// 引擎的全局词法环境不跨 run 持久化:顶层 let/const 只活在当前 run 的词法环境,
|
||||
/// 下一个 run(测试体脚本)看不到。改写为 var 后落全局对象,跨 run 共享。
|
||||
/// 仅作用于行首声明:harness 文件内函数体代码均缩进,不受影响。
|
||||
fn demote_top_level_lexical(source: &str) -> String {
|
||||
source
|
||||
.lines()
|
||||
.map(|line| {
|
||||
if let Some(rest) = line.strip_prefix("let ") {
|
||||
format!("var {rest}")
|
||||
} else if let Some(rest) = line.strip_prefix("const ") {
|
||||
format!("var {rest}")
|
||||
} else {
|
||||
line.to_string()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
/// 按测试元数据拼接完整的 harness 前缀源码(prelude + sta/assert + 各 include)。
|
||||
fn build_harness_source(meta: &TestMeta, harness: &HarnessSources) -> Result<String, String> {
|
||||
///
|
||||
/// `demote` 为真时把顶层 let/const 降级为 var(供两模块方案跨 run 共享),
|
||||
/// 为假时保留原样(供单模块方案,同模块内 let/const 本可见)。
|
||||
fn build_harness_source(
|
||||
meta: &TestMeta, harness: &HarnessSources, demote: bool,
|
||||
) -> Result<String, String> {
|
||||
let mut source = String::new();
|
||||
// onlyStrict 测试在严格模式下运行:指令必须位于脚本最前(harness 之前)。
|
||||
if meta.flags.iter().any(|f| f == "onlyStrict") {
|
||||
@@ -131,7 +168,24 @@ fn build_harness_source(meta: &TestMeta, harness: &HarnessSources) -> Result<Str
|
||||
let include_source = harness.get(include).ok_or_else(|| format!("unknown harness: {include}"))?;
|
||||
append_source_chunk(&mut source, include, include_source);
|
||||
}
|
||||
Ok(source)
|
||||
// 异步测试:注入 print 捕获 + $DONE(asyncTests 用 $DONE 报告结果)。
|
||||
// 结果写入 globalThis 全局字符串,run() 结束后由 runner 读取并按 marker 判定。
|
||||
if meta.flags.iter().any(|f| f == "async") {
|
||||
append_source_chunk(
|
||||
&mut source,
|
||||
"async capture",
|
||||
"globalThis.$__test262_async_result = \"\";\n\
|
||||
function print(msg) { globalThis.$__test262_async_result += String(msg) + \"\\n\"; }",
|
||||
);
|
||||
if let Some(done_src) = harness.get("doneprintHandle.js") {
|
||||
append_source_chunk(&mut source, "doneprintHandle.js", done_src);
|
||||
}
|
||||
}
|
||||
if demote {
|
||||
Ok(demote_top_level_lexical(&source))
|
||||
} else {
|
||||
Ok(source)
|
||||
}
|
||||
}
|
||||
|
||||
/// 取得(并缓存)测试所需的 harness 前缀源码;缓存键为 includes 组合。
|
||||
@@ -146,7 +200,29 @@ pub(crate) fn get_harness_prefix(
|
||||
}
|
||||
}
|
||||
|
||||
let source = build_harness_source(meta, harness)?;
|
||||
let source = build_harness_source(meta, harness, false)?;
|
||||
cache.write().unwrap().insert(key, source.clone());
|
||||
Ok(source)
|
||||
}
|
||||
|
||||
/// 取得(并缓存)测试所需的 harness 前缀编译产物;缓存键为 includes 组合
|
||||
/// (含 onlyStrict / async 旗标)。命中直接返回缓存模块,未命中编译后存入。
|
||||
///
|
||||
/// 前缀经 `demote` 改写(顶层 let/const → var),使其落全局对象、跨 run 共享,
|
||||
/// 供两模块方案(前缀模块 + 测试体模块顺序执行)复用。
|
||||
pub(crate) fn get_harness_module(
|
||||
meta: &TestMeta, harness: &HarnessSources, engine: &Engine, compiler: &Compiler,
|
||||
cache: &Arc<RwLock<HarnessModuleCache>>,
|
||||
) -> Result<Arc<CompiledModule>, String> {
|
||||
let key = harness_key(meta);
|
||||
{
|
||||
let guard = cache.read().unwrap();
|
||||
if let Some(m) = guard.get(&key) {
|
||||
return Ok(Arc::clone(m));
|
||||
}
|
||||
}
|
||||
let source = build_harness_source(meta, harness, true)?;
|
||||
let module = engine.compile_with(&source, compiler)?;
|
||||
cache.write().unwrap().insert(key, Arc::clone(&module));
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ mod test262_log;
|
||||
use chunked::run_chunked;
|
||||
use config::RunConfig;
|
||||
use discovery::discover_tests;
|
||||
use harness::{HarnessPrefixCache, HarnessSources, HARNESS};
|
||||
use harness::{HarnessModuleCache, HarnessPrefixCache, HarnessSources, HARNESS};
|
||||
use heartbeat::write_heartbeat;
|
||||
use judge::{categorize_fail, TestOutcome};
|
||||
use oxide_log::{Level, LogConfig, Output, SUBSYSTEM_COUNT};
|
||||
@@ -220,6 +220,7 @@ fn run_tests() -> bool {
|
||||
let paths_ref = &paths;
|
||||
let heartbeat_ref = &heartbeat_path;
|
||||
let harness_cache = Arc::new(RwLock::new(HarnessPrefixCache::new()));
|
||||
let harness_module_cache = Arc::new(RwLock::new(HarnessModuleCache::new()));
|
||||
|
||||
// 保持内存平稳:worker 只返回聚合统计。保留数万个 `TestResult` 会使
|
||||
// `--no-skip` 运行在套件末尾附近累积 path/error 字符串直至进程 OOM。
|
||||
@@ -229,6 +230,7 @@ fn run_tests() -> bool {
|
||||
let cursor = &cursor;
|
||||
let progress = &progress;
|
||||
let harness_cache = Arc::clone(&harness_cache);
|
||||
let harness_module_cache = Arc::clone(&harness_module_cache);
|
||||
// 与 main() 的 16MB 栈一致:VM 在深层嵌套测试程序上递归,
|
||||
// 默认 worker 栈会溢出。
|
||||
std::thread::Builder::new()
|
||||
@@ -263,6 +265,7 @@ fn run_tests() -> bool {
|
||||
&engine,
|
||||
harness_sources,
|
||||
&harness_cache,
|
||||
&harness_module_cache,
|
||||
);
|
||||
stats.record(i, &result);
|
||||
// COMPLETED 心跳:先写心跳再追加旁路失败行(崩溃恢复
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
#![allow(clippy::arc_with_non_send_sync)]
|
||||
|
||||
use crate::discovery::{eval_family_excluded, is_skipped, pre_existing_excluded};
|
||||
use crate::harness::{append_source_chunk, get_harness_prefix, HarnessPrefixCache, HarnessSources};
|
||||
use crate::harness::{
|
||||
append_source_chunk, get_harness_module, get_harness_prefix, HarnessModuleCache,
|
||||
HarnessPrefixCache, HarnessSources,
|
||||
};
|
||||
use crate::judge::{
|
||||
classify_compile_error, judge_async_result, judge_vm_error, panic_payload_str, TestOutcome, TestResult,
|
||||
};
|
||||
@@ -39,12 +42,16 @@ fn pc_watch_path() -> Option<&'static PathBuf> {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn run_test(
|
||||
path: &Path, source: &str, meta: &TestMeta, engine: &Engine, harness: &HarnessSources,
|
||||
harness_cache: &Arc<RwLock<HarnessPrefixCache>>, no_skip: bool, no_regalloc: bool,
|
||||
harness_cache: &Arc<RwLock<HarnessPrefixCache>>,
|
||||
harness_module_cache: &Arc<RwLock<HarnessModuleCache>>, 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, engine, harness, harness_cache, no_skip, no_regalloc)
|
||||
run_test_inner(
|
||||
path, source, meta, engine, harness, harness_cache, harness_module_cache, no_skip,
|
||||
no_regalloc,
|
||||
)
|
||||
}));
|
||||
|
||||
match result {
|
||||
@@ -94,56 +101,44 @@ impl oxide_emit::module::ModuleSourceLoader for Test262ModuleLoader {
|
||||
}
|
||||
}
|
||||
|
||||
/// 单测执行主流程:拼 harness 前缀 → parse → compile → run;
|
||||
/// 依据 `negative` 元数据校验期望错误,未实现特性按 no_skip 选择跳过或失败。
|
||||
/// 单测执行主流程:编译 → run → 判定;依据 `negative` 元数据校验期望错误,
|
||||
/// 未实现特性按 no_skip 选择跳过或失败。
|
||||
///
|
||||
/// script 臂经 `Engine::compile_with` 便捷路径(parse + CodeForge 缓存);module 臂
|
||||
/// 保留本模块的 `parse_module` + 依赖加载器(引擎暂不吸收 module 加载器面)。
|
||||
/// 两臂共享同一 compiler,`no_regalloc` 旗标对两臂同口径生效。
|
||||
/// module 臂保留本模块的 `parse_module` + 依赖加载器(引擎暂不吸收 module 加载器面),
|
||||
/// 前缀与测试体拼为单模块编译。script 臂走两模块方案:前缀模块按 includes 组合
|
||||
/// 缓存(免每测试重解析重编译),测试体模块每测试编译,顺序执行。两臂共享同一
|
||||
/// compiler,`no_regalloc` 旗标对两臂同口径生效。
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn run_test_inner(
|
||||
path: &Path, source: &str, meta: &TestMeta, engine: &Engine, harness: &HarnessSources,
|
||||
harness_cache: &Arc<RwLock<HarnessPrefixCache>>, no_skip: bool, no_regalloc: bool,
|
||||
harness_cache: &Arc<RwLock<HarnessPrefixCache>>,
|
||||
harness_module_cache: &Arc<RwLock<HarnessModuleCache>>, no_skip: bool, no_regalloc: bool,
|
||||
) -> TestResult {
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
let is_async = meta.flags.iter().any(|f| f == "async");
|
||||
let is_module = meta.flags.iter().any(|f| f == "module");
|
||||
let is_only_strict = meta.flags.iter().any(|f| f == "onlyStrict");
|
||||
|
||||
let code = match get_harness_prefix(meta, harness, harness_cache) {
|
||||
Ok(prefix) => {
|
||||
let mut code = prefix;
|
||||
if is_async {
|
||||
// 异步测试:注入 print 捕获 + $DONE(asyncTests 用 $DONE 报告结果)。
|
||||
// 结果写入 globalThis 全局字符串,run() 结束后由 runner 读取并按 marker 判定。
|
||||
// print 为顶层函数声明,经全局对象反射落 globalThis;此处仅初始化累加器。
|
||||
append_source_chunk(
|
||||
&mut code,
|
||||
"async capture",
|
||||
"globalThis.$__test262_async_result = \"\";\n\
|
||||
function print(msg) { globalThis.$__test262_async_result += String(msg) + \"\\n\"; }",
|
||||
);
|
||||
if let Some(done_src) = harness.get("doneprintHandle.js") {
|
||||
append_source_chunk(&mut code, "doneprintHandle.js", done_src);
|
||||
}
|
||||
}
|
||||
append_source_chunk(&mut code, "test source", strip_meta(source));
|
||||
code
|
||||
}
|
||||
Err(e) => {
|
||||
let dur = start.elapsed().as_millis() as u64;
|
||||
if no_skip {
|
||||
return TestResult::fail(path.to_path_buf(), dur, e);
|
||||
}
|
||||
return TestResult::skip(path.to_path_buf(), e);
|
||||
}
|
||||
};
|
||||
|
||||
// module 臂保留本模块的 parse_module + 依赖加载器;script 臂经引擎
|
||||
// 便捷路径(parse + CodeForge 缓存)。compiler 由两臂共享,
|
||||
// no_regalloc 旗标对两臂同口径生效。
|
||||
// compiler 由两臂共享,no_regalloc 旗标对两臂同口径生效。
|
||||
let compiler = if no_regalloc { Compiler::new().with_regalloc(false) } else { Compiler::new() };
|
||||
let module_result = if is_module {
|
||||
|
||||
// 编译模块:module 臂单模块(前缀 + 测试体),script 臂两模块(缓存前缀模块 + 测试体模块)。
|
||||
let modules: Vec<Arc<oxide_bytecode::CompiledModule>> = if is_module {
|
||||
let code = match get_harness_prefix(meta, harness, harness_cache) {
|
||||
Ok(prefix) => {
|
||||
let mut code = prefix;
|
||||
append_source_chunk(&mut code, "test source", strip_meta(source));
|
||||
code
|
||||
}
|
||||
Err(e) => {
|
||||
let dur = start.elapsed().as_millis() as u64;
|
||||
if no_skip {
|
||||
return TestResult::fail(path.to_path_buf(), dur, e);
|
||||
}
|
||||
return TestResult::skip(path.to_path_buf(), e);
|
||||
}
|
||||
};
|
||||
let alloc = oxide_parser::Allocator::default();
|
||||
let program = match oxide_parser::parse_module(&alloc, &code) {
|
||||
Ok(p) => p,
|
||||
@@ -157,34 +152,75 @@ fn run_test_inner(
|
||||
}
|
||||
};
|
||||
let mut loader = Test262ModuleLoader;
|
||||
compiler
|
||||
.compile_module(&program, path.to_string_lossy().as_ref(), &mut loader)
|
||||
.map(Arc::new)
|
||||
match compiler.compile_module(&program, path.to_string_lossy().as_ref(), &mut loader) {
|
||||
Ok(m) => vec![Arc::new(m)],
|
||||
Err(e) => {
|
||||
let dur = start.elapsed().as_millis() as u64;
|
||||
let msg = format!("compile error: {e}");
|
||||
match classify_compile_error(&msg, meta.negative.is_some(), no_skip) {
|
||||
TestOutcome::Pass(m) => return TestResult::pass(path.to_path_buf(), dur, m),
|
||||
TestOutcome::Fail(m) => return TestResult::fail(path.to_path_buf(), dur, m),
|
||||
TestOutcome::Skip(m) => return TestResult::skip(path.to_path_buf(), m),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
engine.compile_with(&code, &compiler)
|
||||
};
|
||||
|
||||
let module = match module_result {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
let dur = start.elapsed().as_millis() as u64;
|
||||
let msg = format!("compile error: {e}");
|
||||
match classify_compile_error(&msg, meta.negative.is_some(), no_skip) {
|
||||
TestOutcome::Pass(m) => return TestResult::pass(path.to_path_buf(), dur, m),
|
||||
TestOutcome::Fail(m) => return TestResult::fail(path.to_path_buf(), dur, m),
|
||||
TestOutcome::Skip(m) => return TestResult::skip(path.to_path_buf(), m),
|
||||
// script 臂两模块方案:前缀模块按 includes 组合缓存(免每测试重解析重编译),
|
||||
// 测试体模块每测试编译。onlyStrict 测试给测试体补 "use strict" 保持严格性。
|
||||
let harness_module = match get_harness_module(meta, harness, engine, &compiler, harness_module_cache)
|
||||
{
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
let dur = start.elapsed().as_millis() as u64;
|
||||
if no_skip {
|
||||
return TestResult::fail(path.to_path_buf(), dur, e);
|
||||
}
|
||||
return TestResult::skip(path.to_path_buf(), e);
|
||||
}
|
||||
};
|
||||
let mut test_body = String::from(strip_meta(source));
|
||||
if is_only_strict {
|
||||
test_body = format!("\"use strict\";\n{test_body}");
|
||||
}
|
||||
match engine.compile_with(&test_body, &compiler) {
|
||||
Ok(test_module) => vec![harness_module, test_module],
|
||||
Err(e) => {
|
||||
let dur = start.elapsed().as_millis() as u64;
|
||||
let msg = format!("compile error: {e}");
|
||||
match classify_compile_error(&msg, meta.negative.is_some(), no_skip) {
|
||||
TestOutcome::Pass(m) => return TestResult::pass(path.to_path_buf(), dur, m),
|
||||
TestOutcome::Fail(m) => return TestResult::fail(path.to_path_buf(), dur, m),
|
||||
TestOutcome::Skip(m) => return TestResult::skip(path.to_path_buf(), m),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 独立 Vm 不经池;动态编译服务由 `new_vm` 同口径注入(语料含 eval /
|
||||
// Function 动态编译面)。
|
||||
run_and_judge(path, meta, engine, &modules, is_async, no_skip, &start)
|
||||
}
|
||||
|
||||
/// 在独立 Vm 上顺序执行各模块并判定。独立 Vm 不经池;动态编译服务由 `new_vm`
|
||||
/// 同口径注入(语料含 eval / Function 动态编译面)。任一模块失败即停,不跑后续模块。
|
||||
/// 完成值取最后一个模块的结果(script 臂即测试体模块)。
|
||||
fn run_and_judge(
|
||||
path: &Path, meta: &TestMeta, engine: &Engine, modules: &[Arc<oxide_bytecode::CompiledModule>],
|
||||
is_async: bool, no_skip: bool, start: &std::time::Instant,
|
||||
) -> TestResult {
|
||||
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(&module);
|
||||
let mut run_result: Result<JsValue, String> = Ok(JsValue::undefined());
|
||||
for module in modules {
|
||||
match vm.run(module) {
|
||||
Ok(v) => run_result = Ok(v),
|
||||
Err(e) => {
|
||||
run_result = Err(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let dur = start.elapsed().as_millis() as u64;
|
||||
|
||||
if is_async {
|
||||
@@ -222,10 +258,12 @@ pub(crate) fn read_async_output(vm: &Vm) -> String {
|
||||
|
||||
/// 串行与并行执行路径共享的每测试管线:
|
||||
/// 读文件、解析元数据、应用跳过过滤,然后运行。恰好返回一个 `TestResult`。
|
||||
/// worker 自有状态(`engine`、`harness_sources`、`harness_cache`)永不跨线程。
|
||||
/// worker 自有状态(`engine`、`harness_sources`、`harness_cache`、`harness_module_cache`)永不跨线程。
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub(crate) fn process_path(
|
||||
path: &Path, filter: &Option<String>, no_skip: bool, no_regalloc: bool, engine: &Engine,
|
||||
harness_sources: &HarnessSources, harness_cache: &Arc<RwLock<HarnessPrefixCache>>,
|
||||
harness_module_cache: &Arc<RwLock<HarnessModuleCache>>,
|
||||
) -> TestResult {
|
||||
let source = match std::fs::read_to_string(path) {
|
||||
Ok(s) => s,
|
||||
@@ -264,7 +302,10 @@ pub(crate) fn process_path(
|
||||
}
|
||||
}
|
||||
|
||||
run_test(path, &source, &meta, engine, harness_sources, harness_cache, no_skip, no_regalloc)
|
||||
run_test(
|
||||
path, &source, &meta, engine, harness_sources, harness_cache, harness_module_cache,
|
||||
no_skip, no_regalloc,
|
||||
)
|
||||
}
|
||||
|
||||
/// 构建带步数上限的 runner 引擎。每个并行 worker 拥有自己的引擎,
|
||||
|
||||
Reference in New Issue
Block a user