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:
Generated
+32
-18
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1213,6 +1213,7 @@ dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_code_cache",
|
||||
"oxide_compiler",
|
||||
"oxide_engine",
|
||||
"oxide_kernel",
|
||||
"oxide_log",
|
||||
"oxide_parser",
|
||||
@@ -1225,7 +1226,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1234,7 +1235,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1254,7 +1255,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1268,7 +1269,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1281,9 +1282,21 @@ dependencies = [
|
||||
"regress",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oxide_engine"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
"oxide_kernel",
|
||||
"oxide_parser",
|
||||
"oxide_types",
|
||||
"oxide_vm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1292,7 +1305,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1306,7 +1319,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1318,7 +1331,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1327,7 +1340,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1342,7 +1355,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1358,7 +1371,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1371,11 +1384,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
"oxide_emit",
|
||||
"oxide_engine",
|
||||
"oxide_kernel",
|
||||
"oxide_log",
|
||||
"oxide_parser",
|
||||
@@ -1388,7 +1402,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1401,7 +1415,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+2
-1
@@ -17,12 +17,13 @@ members = [
|
||||
"crates/oxide_kernel",
|
||||
"crates/oxide_runtime_api",
|
||||
"crates/oxide_builtins",
|
||||
"crates/oxide_engine",
|
||||
"crates/oxide_cli",
|
||||
"crates/oxide_test262",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.8.69"
|
||||
version = "0.8.70"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -18,6 +18,7 @@ oxide_types = { path = "../oxide_types" }
|
||||
oxide_compiler = { path = "../oxide_compiler" }
|
||||
oxide_vm = { path = "../oxide_vm" }
|
||||
oxide_kernel = { path = "../oxide_kernel" }
|
||||
oxide_engine = { path = "../oxide_engine" }
|
||||
oxide_log = { path = "../oxide_log" }
|
||||
bumpalo.workspace = true
|
||||
clap.workspace = true
|
||||
|
||||
@@ -19,10 +19,8 @@ pub mod profile;
|
||||
pub mod rust_bench;
|
||||
|
||||
use std::process::ExitCode;
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_kernel::kernel::KernelCore;
|
||||
use oxide_vm::vm_pool::VmPool;
|
||||
use oxide_engine::Engine;
|
||||
|
||||
/// 一次 bench 运行的全部配置(来自 CLI `bench` 子命令参数)。
|
||||
pub struct BenchConfig {
|
||||
@@ -39,22 +37,27 @@ pub struct BenchConfig {
|
||||
}
|
||||
|
||||
/// 按 mode 分派到具体 benchmark 实现(js/rust/leak)。
|
||||
pub fn run_benchmarks(config: BenchConfig, kernel: Arc<KernelCore>, pool: Arc<VmPool>) -> ExitCode {
|
||||
///
|
||||
/// 引擎由调用方构造(内核与池的生命周期随引擎);子模块按 `kernel` / `pool`
|
||||
/// 参数口径透传,内部逻辑不变。
|
||||
pub fn run_benchmarks(config: BenchConfig, engine: &Engine) -> ExitCode {
|
||||
let kernel = engine.kernel();
|
||||
let pool = engine.pool();
|
||||
match config.mode.as_str() {
|
||||
"js" => js_stress::run_js_stress_bench(&config, &kernel, &pool),
|
||||
"js" => js_stress::run_js_stress_bench(&config, kernel, pool),
|
||||
"rust" => rust_bench::run_rust_bench(config.filter.as_deref()),
|
||||
// leak 模式按 case 过滤参数分派校准用例;无匹配 case 时跑默认的固定脚本内存回归(run_leak_detect)。
|
||||
"leak" => match config.filter.as_deref() {
|
||||
Some("vm_creation") => leak_detect::run_mem_vm_creation_leak(&kernel),
|
||||
Some("builtin_world_build") => leak_detect::run_mem_builtin_world_build(&kernel),
|
||||
Some("dirty_rebuild") => leak_detect::run_mem_dirty_rebuild_leak(&kernel),
|
||||
Some("closure_dead_leak") => leak_detect::run_mem_closure_dead_leak(&kernel),
|
||||
Some("pool_high_water") => leak_detect::run_mem_pool_high_water(&kernel),
|
||||
Some("object_churn_peak") => leak_detect::run_mem_object_churn_peak(&kernel),
|
||||
Some("vm_creation") => leak_detect::run_mem_vm_creation_leak(kernel),
|
||||
Some("builtin_world_build") => leak_detect::run_mem_builtin_world_build(kernel),
|
||||
Some("dirty_rebuild") => leak_detect::run_mem_dirty_rebuild_leak(kernel),
|
||||
Some("closure_dead_leak") => leak_detect::run_mem_closure_dead_leak(kernel),
|
||||
Some("pool_high_water") => leak_detect::run_mem_pool_high_water(kernel),
|
||||
Some("object_churn_peak") => leak_detect::run_mem_object_churn_peak(kernel),
|
||||
Some("kernel_lifetime") => leak_detect::run_mem_kernel_lifetime(),
|
||||
Some("startup_breakdown") => leak_detect::run_mem_startup_breakdown(),
|
||||
Some("alloc_ab") => alloc_ab::run_alloc_ab(&config, &kernel, &pool),
|
||||
_ => leak_detect::run_leak_detect(&config, &kernel, &pool),
|
||||
Some("alloc_ab") => alloc_ab::run_alloc_ab(&config, kernel, pool),
|
||||
_ => leak_detect::run_leak_detect(&config, kernel, pool),
|
||||
},
|
||||
_ => {
|
||||
eprintln!("Unknown bench mode: {}. Use: js, rust, or leak", config.mode);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use std::fs;
|
||||
use std::process::ExitCode;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use ansi_term::Colour::Red;
|
||||
@@ -17,18 +16,16 @@ use oxide_cli::server::server::{run_server, run_server_rm, RmServerConfig, Serve
|
||||
use oxide_cli::server::sidecar;
|
||||
use oxide_cli::server::spawn;
|
||||
use oxide_cli::server::watchdog;
|
||||
use oxide_compiler::compiler::{compiled_module_hash, Compiler};
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_compiler::compiler_error;
|
||||
use oxide_compiler::DefaultCompilerService;
|
||||
use oxide_kernel::kernel::{KernelConfig, KernelCore};
|
||||
use oxide_engine::Engine;
|
||||
use oxide_kernel::kernel::KernelConfig;
|
||||
use oxide_kernel::shape_forge::ShapeForge;
|
||||
use oxide_kernel::string_forge::PermInterner;
|
||||
use oxide_kernel::{kernel_error, kernel_info};
|
||||
use oxide_log::{Level, SUBSYSTEM_COUNT};
|
||||
use oxide_parser::Allocator;
|
||||
use oxide_vm::vm::Vm;
|
||||
use oxide_vm::vm_error;
|
||||
use oxide_vm::vm_pool::VmPool;
|
||||
use oxide_vm::JsValue;
|
||||
|
||||
mod bench;
|
||||
@@ -231,15 +228,13 @@ fn main() -> ExitCode {
|
||||
|
||||
match cli.command {
|
||||
Some(Commands::Eval { code, trace }) => {
|
||||
let kernel = make_kernel(cli.verbose, cli.quiet);
|
||||
let pool = make_pool(&kernel);
|
||||
eval(&code, &kernel, &pool, trace, cli.profile, true)
|
||||
let engine = make_engine(cli.verbose, cli.quiet);
|
||||
eval(&code, &engine, trace, cli.profile, true)
|
||||
}
|
||||
Some(Commands::Run { file, repeat, trace }) => {
|
||||
let kernel = make_kernel(cli.verbose, cli.quiet);
|
||||
let pool = make_pool(&kernel);
|
||||
let engine = make_engine(cli.verbose, cli.quiet);
|
||||
for n in 0..repeat {
|
||||
let code = run(&file, &kernel, &pool, trace, cli.profile);
|
||||
let code = run(&file, &engine, trace, cli.profile);
|
||||
// 失败即终止并传播退出码,供脚本与 CI 区分成败。
|
||||
if code != ExitCode::SUCCESS {
|
||||
return code;
|
||||
@@ -255,7 +250,10 @@ fn main() -> ExitCode {
|
||||
file,
|
||||
no_dce,
|
||||
no_regalloc,
|
||||
}) => compile(expr, file, no_dce, no_regalloc),
|
||||
}) => {
|
||||
let engine = make_engine(false, false);
|
||||
compile(&engine, expr, file, no_dce, no_regalloc)
|
||||
}
|
||||
Some(Commands::Bench {
|
||||
mode,
|
||||
filter,
|
||||
@@ -265,8 +263,7 @@ fn main() -> ExitCode {
|
||||
sample,
|
||||
sample_top,
|
||||
}) => {
|
||||
let kernel = make_kernel(false, false);
|
||||
let pool = make_pool(&kernel);
|
||||
let engine = make_engine(false, false);
|
||||
// 采样周期边界检查:须为 0(关闭)或 2 的幂,否则指令边界的
|
||||
// `steps & (period - 1) == 0` 判定退化(周期不整除步数序列)。
|
||||
if sample != 0 && sample & (sample - 1) != 0 {
|
||||
@@ -282,7 +279,7 @@ fn main() -> ExitCode {
|
||||
sample_period: sample,
|
||||
sample_top,
|
||||
};
|
||||
bench::run_benchmarks(config, kernel, pool)
|
||||
bench::run_benchmarks(config, &engine)
|
||||
}
|
||||
Some(Commands::Test { .. }) => not_implemented("test"),
|
||||
Some(Commands::Server { command }) => server_command(command),
|
||||
@@ -608,7 +605,8 @@ fn start_result(result: Result<(), oxide_cli::server::server::ServerError>) -> E
|
||||
}
|
||||
}
|
||||
|
||||
fn make_kernel(verbose: bool, quiet: bool) -> Arc<KernelCore> {
|
||||
/// 建引擎:`KernelConfig::standard()` 按 -v / -q 填 `log_levels` 后 `Engine::new`。
|
||||
fn make_engine(verbose: bool, quiet: bool) -> Engine {
|
||||
let mut config = KernelConfig::standard();
|
||||
if verbose {
|
||||
config.log_levels = [Level::Info; SUBSYSTEM_COUNT];
|
||||
@@ -617,36 +615,11 @@ fn make_kernel(verbose: bool, quiet: bool) -> Arc<KernelCore> {
|
||||
} else {
|
||||
config.log_levels = [Level::Error; SUBSYSTEM_COUNT];
|
||||
}
|
||||
KernelCore::new(config)
|
||||
Engine::new(config)
|
||||
}
|
||||
|
||||
fn make_pool(kernel: &Arc<KernelCore>) -> Arc<VmPool> {
|
||||
VmPool::new(
|
||||
Arc::clone(kernel),
|
||||
Arc::new(DefaultCompilerService),
|
||||
kernel.config.min_pool_size,
|
||||
kernel.config.max_pool_size,
|
||||
)
|
||||
}
|
||||
|
||||
fn eval(
|
||||
code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool, profile: bool, print_result: bool,
|
||||
) -> ExitCode {
|
||||
let allocator = Allocator::default();
|
||||
let program = match oxide_parser::parse(&allocator, code) {
|
||||
Ok(p) => p,
|
||||
Err(errors) => {
|
||||
for err in &errors {
|
||||
compiler_error!("parse error: {}", err);
|
||||
eprintln!("{}", Red.paint(err.to_string()));
|
||||
}
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
let compiler = Compiler::new();
|
||||
let hash = compiled_module_hash(&program);
|
||||
let module = match kernel.code_forge().get_or_insert_with(hash, || compiler.compile(&program)) {
|
||||
fn eval(code: &str, engine: &Engine, trace: bool, profile: bool, print_result: bool) -> ExitCode {
|
||||
let module = match engine.compile(code) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
compiler_error!("compile error: {}", err);
|
||||
@@ -655,7 +628,7 @@ fn eval(
|
||||
}
|
||||
};
|
||||
|
||||
let mut guard = pool.spawn();
|
||||
let mut guard = engine.spawn();
|
||||
guard.vm_mut().set_instruction_trace(trace);
|
||||
let exec_start = Instant::now();
|
||||
match guard.vm_mut().run(&module) {
|
||||
@@ -669,6 +642,7 @@ fn eval(
|
||||
}
|
||||
// eval 臂按 REPL 语义打印完成值;run 臂脚本只输出自身产生内容。
|
||||
if print_result {
|
||||
let kernel = engine.kernel();
|
||||
format_result(guard.vm(), kernel.perm_interner().as_ref(), kernel.shape_forge().as_ref(), result);
|
||||
}
|
||||
ExitCode::SUCCESS
|
||||
@@ -685,9 +659,9 @@ fn format_result(vm: &oxide_vm::vm::Vm, string_forge: &PermInterner, shape_forge
|
||||
println!("{}", format_js_value(vm, string_forge, shape_forge, val));
|
||||
}
|
||||
|
||||
fn run(file: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool, profile: bool) -> ExitCode {
|
||||
fn run(file: &str, engine: &Engine, trace: bool, profile: bool) -> ExitCode {
|
||||
match fs::read_to_string(file) {
|
||||
Ok(source) => eval(&source, kernel, pool, trace, profile, false),
|
||||
Ok(source) => eval(&source, engine, trace, profile, false),
|
||||
Err(err) => {
|
||||
kernel_error!("cannot read {}: {}", file, err);
|
||||
eprintln!("{}", Red.paint(format!("Cannot read {file}: {err}")));
|
||||
@@ -696,7 +670,7 @@ fn run(file: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool, pr
|
||||
}
|
||||
}
|
||||
|
||||
fn compile(expr: Option<String>, file: Option<String>, no_dce: bool, no_regalloc: bool) -> ExitCode {
|
||||
fn compile(engine: &Engine, expr: Option<String>, file: Option<String>, no_dce: bool, no_regalloc: bool) -> ExitCode {
|
||||
let source = if let Some(code) = expr {
|
||||
code
|
||||
} else if let Some(path) = file {
|
||||
@@ -714,26 +688,9 @@ fn compile(expr: Option<String>, file: Option<String>, no_dce: bool, no_regalloc
|
||||
return ExitCode::FAILURE;
|
||||
};
|
||||
|
||||
let allocator = Allocator::default();
|
||||
let program = match oxide_parser::parse(&allocator, &source) {
|
||||
Ok(p) => p,
|
||||
Err(errors) => {
|
||||
for err in &errors {
|
||||
compiler_error!("parse error: {}", err);
|
||||
eprintln!("{}", Red.paint(err.to_string()));
|
||||
}
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
let mut compiler = Compiler::new();
|
||||
if no_dce {
|
||||
compiler = compiler.with_dce(false);
|
||||
}
|
||||
if no_regalloc {
|
||||
compiler = compiler.with_regalloc(false);
|
||||
}
|
||||
match compiler.compile(&program) {
|
||||
// 旗标面经自定义编译器透传(无缺省臂,漏传即旗标失效)。
|
||||
let compiler = Compiler::new().with_dce(!no_dce).with_regalloc(!no_regalloc);
|
||||
match engine.compile_with(&source, &compiler) {
|
||||
Ok(module) => {
|
||||
print!("{module}");
|
||||
ExitCode::SUCCESS
|
||||
@@ -771,10 +728,9 @@ fn repl() -> ExitCode {
|
||||
}
|
||||
};
|
||||
|
||||
let kernel = make_kernel(false, false);
|
||||
let mut vm = Vm::with_kernel_core(Arc::clone(&kernel));
|
||||
// REPL 源经 eval 路径动态编译(含 `new Function` / eval 内置),注入真实编译服务。
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
let engine = make_engine(false, false);
|
||||
// 常驻 Vm 不经池;动态编译服务由 `new_vm` 同口径注入。
|
||||
let mut vm = engine.new_vm();
|
||||
let mut input_buf = String::new();
|
||||
|
||||
loop {
|
||||
@@ -801,7 +757,7 @@ fn repl() -> ExitCode {
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = eval_repl(&input_buf, &kernel, &mut vm);
|
||||
let result = eval_repl(&input_buf, &engine, &mut vm);
|
||||
input_buf.clear();
|
||||
|
||||
if result == ExitCode::FAILURE {
|
||||
@@ -825,22 +781,10 @@ fn repl() -> ExitCode {
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_repl(code: &str, kernel: &Arc<KernelCore>, vm: &mut Vm) -> ExitCode {
|
||||
let allocator = Allocator::default();
|
||||
let program = match oxide_parser::parse(&allocator, code) {
|
||||
Ok(p) => p,
|
||||
Err(errors) => {
|
||||
for err in &errors {
|
||||
compiler_error!("parse error: {}", err);
|
||||
eprintln!("{}", Red.paint(err.to_string()));
|
||||
}
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
fn eval_repl(code: &str, engine: &Engine, vm: &mut Vm) -> ExitCode {
|
||||
// REPL 持久模式:顶层 let/const 也写全局对象属性,经缓存编译。
|
||||
let compiler = Compiler::new().with_repl_persist(true);
|
||||
let hash = compiled_module_hash(&program);
|
||||
let module = match kernel.code_forge().get_or_insert_with(hash, || compiler.compile(&program)) {
|
||||
let module = match engine.compile_with(code, &compiler) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
compiler_error!("compile error: {}", err);
|
||||
@@ -851,6 +795,7 @@ fn eval_repl(code: &str, kernel: &Arc<KernelCore>, vm: &mut Vm) -> ExitCode {
|
||||
|
||||
match vm.run(&module) {
|
||||
Ok(result) => {
|
||||
let kernel = engine.kernel();
|
||||
format_result(vm, kernel.perm_interner().as_ref(), kernel.shape_forge().as_ref(), result);
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "oxide_engine"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
description = "OxideJS - compile+run convenience layer (Engine)"
|
||||
|
||||
[dependencies]
|
||||
oxide_bytecode = { path = "../oxide_bytecode" }
|
||||
oxide_parser = { path = "../oxide_parser" }
|
||||
oxide_compiler = { path = "../oxide_compiler" }
|
||||
oxide_kernel = { path = "../oxide_kernel" }
|
||||
oxide_vm = { path = "../oxide_vm" }
|
||||
oxide_types = { path = "../oxide_types" }
|
||||
@@ -0,0 +1,168 @@
|
||||
//! oxide_engine:compile+run 便捷层(执行层与入口层之间)。
|
||||
//!
|
||||
//! `Engine` 持 `Arc<KernelCore>` + `Arc<VmPool>`,把入口 crate 各自内联装配的
|
||||
//! 「parse → compile(经 CodeForge 缓存)→ run(经 VmPool 或独立 Vm)」流水线
|
||||
//! 收口为单一实现源。
|
||||
//!
|
||||
//! 关键约定:
|
||||
//! - 池恒建(`min/max_pool_size` 取自内核配置);不借池的入口(每测试独立
|
||||
//! Vm)付一次可忽略的预热成本,不引入可选池分支。
|
||||
//! - `compile` / `compile_with` 统一经 CodeForge 缓存;命中返回同一模块
|
||||
//! (结构哈希键,语义等价)。
|
||||
//! - `new_vm` 是不经池的独立 Vm;动态编译服务与池内 Vm 同口径注入
|
||||
//! (缺省编译服务),调用方无需再注入。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_bytecode::module::CompiledModule;
|
||||
use oxide_compiler::compiler::{compiled_module_hash, Compiler};
|
||||
use oxide_compiler::DefaultCompilerService;
|
||||
use oxide_kernel::kernel::{KernelConfig, KernelCore};
|
||||
use oxide_vm::vm::Vm;
|
||||
use oxide_vm::vm_pool::{VmGuard, VmPool};
|
||||
use oxide_vm::JsValue;
|
||||
|
||||
/// 引擎便捷类型:持共享内核与 VM 池,提供 compile / eval / spawn / new_vm 面。
|
||||
///
|
||||
/// 入口 crate(CLI、test262 runner)按进程或按 worker 各建一个,经其方法完成
|
||||
/// parse → compile → run 流水线;同一引擎内全部 Vm 共享同一内核与池。
|
||||
pub struct Engine {
|
||||
kernel: Arc<KernelCore>,
|
||||
pool: Arc<VmPool>,
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
/// 由内核配置建引擎:`KernelCore::new` 后按配置 `min/max_pool_size` 建池。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `min_pool_size` 受 `max_pool_size` 钳制(池预热路径同口径)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 初始化日志系统(`KernelCore::new` 内完成,幂等)。
|
||||
/// - 同步预热 `min_pool_size` 个 Vm。
|
||||
pub fn new(config: KernelConfig) -> Self {
|
||||
let kernel = KernelCore::new(config);
|
||||
let pool = VmPool::new(
|
||||
Arc::clone(&kernel),
|
||||
Arc::new(DefaultCompilerService),
|
||||
kernel.config.min_pool_size,
|
||||
kernel.config.max_pool_size,
|
||||
);
|
||||
Self { kernel, pool }
|
||||
}
|
||||
|
||||
/// parse + 缺省编译(`Compiler::new()`),经 CodeForge 缓存,返回模块。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 解析失败返回各条消息换行拼接的单一错误串。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 缓存未命中时编译产物写入 CodeForge。
|
||||
pub fn compile(&self, source: &str) -> Result<Arc<CompiledModule>, String> {
|
||||
self.compile_with(source, &Compiler::new())
|
||||
}
|
||||
|
||||
/// parse + 自定义编译器编译,经 CodeForge 缓存,返回模块。
|
||||
///
|
||||
/// 供 `no_dce` / `no_regalloc` / `repl_persist` 等旗标面:编译器由调用方
|
||||
/// 构造传入,无缺省臂,漏传即旗标失效。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 局部分配器上解析源码为 Program。
|
||||
/// 2. 结构哈希作缓存键,CodeForge 未命中时调给定编译器编译并缓存。
|
||||
pub fn compile_with(&self, source: &str, compiler: &Compiler) -> Result<Arc<CompiledModule>, String> {
|
||||
// 局部分配器:AST 节点经 oxc bump 分配器分配,编译完成即弃。
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, source)
|
||||
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))?;
|
||||
|
||||
let hash = compiled_module_hash(&program);
|
||||
self.kernel.code_forge().get_or_insert_with(hash, || compiler.compile(&program))
|
||||
}
|
||||
|
||||
/// 编译 + 池借出 Vm 执行,返回完成值(eval 便捷路径)。
|
||||
pub fn eval(&self, source: &str) -> Result<JsValue, String> {
|
||||
let module = self.compile(source)?;
|
||||
let mut guard = self.pool.spawn();
|
||||
guard.vm_mut().run(&module)
|
||||
}
|
||||
|
||||
/// 池借出 Vm(RAII `VmGuard`,drop 归还池)。
|
||||
pub fn spawn(&self) -> VmGuard {
|
||||
self.pool.spawn()
|
||||
}
|
||||
|
||||
/// 建独立 Vm(不经池),供常驻 / 每测试独立场景。
|
||||
///
|
||||
/// 动态编译服务与池内 Vm 同口径注入(缺省编译服务),调用方无需再注入。
|
||||
pub fn new_vm(&self) -> Vm {
|
||||
let mut vm = Vm::with_kernel_core(Arc::clone(&self.kernel));
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
vm
|
||||
}
|
||||
|
||||
/// 只读访问共享内核。
|
||||
pub fn kernel(&self) -> &Arc<KernelCore> {
|
||||
&self.kernel
|
||||
}
|
||||
|
||||
/// 只读访问 VM 池。
|
||||
pub fn pool(&self) -> &Arc<VmPool> {
|
||||
&self.pool
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use oxide_kernel::kernel::KernelConfig;
|
||||
|
||||
fn test_engine() -> Engine {
|
||||
Engine::new(KernelConfig::minimal())
|
||||
}
|
||||
|
||||
/// compile 把产物写入 CodeForge 缓存:同源码二次编译命中缓存,
|
||||
/// 返回同一模块且条目数不增。
|
||||
#[test]
|
||||
fn compile_caches_module_in_code_forge() {
|
||||
let engine = test_engine();
|
||||
let first = engine.compile("1 + 2").expect("编译应成功");
|
||||
assert_eq!(engine.kernel().code_forge().len(), 1, "首次编译应产生一个缓存条目");
|
||||
|
||||
let second = engine.compile("1 + 2").expect("编译应成功");
|
||||
assert!(Arc::ptr_eq(&first, &second), "同源码二次编译应命中缓存返回同一模块");
|
||||
assert_eq!(engine.kernel().code_forge().len(), 1, "缓存命中不应新增条目");
|
||||
}
|
||||
|
||||
/// eval 走编译 + 池借出 + 执行全路径,返回完成值。
|
||||
#[test]
|
||||
fn eval_returns_completion_value() {
|
||||
let engine = test_engine();
|
||||
let value = engine.eval("6 * 7.0").expect("执行应成功");
|
||||
assert!(value.is_number(), "完成值应为数字");
|
||||
assert_eq!(value.as_double(), 42.0, "完成值应为 42");
|
||||
}
|
||||
|
||||
/// new_vm 是不经池的独立 Vm:能独立执行引擎的编译产物。
|
||||
#[test]
|
||||
fn new_vm_runs_independently() {
|
||||
let engine = test_engine();
|
||||
let module = engine.compile("10 + 5").expect("编译应成功");
|
||||
let mut vm = engine.new_vm();
|
||||
let value = vm.run(&module).expect("执行应成功");
|
||||
assert!(value.is_number(), "完成值应为数字");
|
||||
assert_eq!(value.as_int(), 15, "完成值应为 15");
|
||||
}
|
||||
|
||||
/// 解析失败映射为单一错误串(各条消息换行拼接),且不污染缓存。
|
||||
#[test]
|
||||
fn parse_error_maps_to_joined_message() {
|
||||
let engine = test_engine();
|
||||
let err = match engine.compile("const x = {") {
|
||||
Ok(_) => panic!("解析失败应返回错误"),
|
||||
Err(e) => e,
|
||||
};
|
||||
assert!(!err.is_empty(), "解析错误消息不应为空");
|
||||
assert_eq!(engine.kernel().code_forge().len(), 0, "解析失败不应写入缓存");
|
||||
}
|
||||
}
|
||||
@@ -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