From d08ed7c3dd19c0b438d8ef82a0cf0ed5ba902d8f Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 27 Jun 2026 09:39:08 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20CLI/test262=20=E5=85=A8=E7=AE=A1?= =?UTF-8?q?=E7=BA=BF=E6=8E=A5=E5=85=A5=E7=BB=93=E6=9E=84=E5=8C=96=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 2 ++ crates/oxide_cli/Cargo.toml | 1 + crates/oxide_cli/src/main.rs | 35 ++++++++++++++++++++----- crates/oxide_test262/Cargo.toml | 1 + crates/oxide_test262/src/main.rs | 21 +++++++++++++++ crates/oxide_test262/src/test262_log.rs | 20 ++++++++++++++ 6 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 crates/oxide_test262/src/test262_log.rs diff --git a/Cargo.lock b/Cargo.lock index 0f441c8d..b42dfcdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1084,6 +1084,7 @@ dependencies = [ "oxide_code_cache", "oxide_compiler", "oxide_kernel", + "oxide_log", "oxide_parser", "oxide_types", "oxide_vm", @@ -1166,6 +1167,7 @@ dependencies = [ "oxide_bytecode", "oxide_compiler", "oxide_kernel", + "oxide_log", "oxide_parser", "oxide_vm", "serde", diff --git a/crates/oxide_cli/Cargo.toml b/crates/oxide_cli/Cargo.toml index de2be463..d4e35c4f 100644 --- a/crates/oxide_cli/Cargo.toml +++ b/crates/oxide_cli/Cargo.toml @@ -19,6 +19,7 @@ oxide_compiler = { path = "../oxide_compiler" } oxide_vm = { path = "../oxide_vm" } oxide_kernel = { path = "../oxide_kernel" } oxide_api = { path = "../oxide_api" } +oxide_log = { path = "../oxide_log" } clap.workspace = true rustyline.workspace = true ansi_term.workspace = true diff --git a/crates/oxide_cli/src/main.rs b/crates/oxide_cli/src/main.rs index af1a2dc6..796f3d53 100644 --- a/crates/oxide_cli/src/main.rs +++ b/crates/oxide_cli/src/main.rs @@ -8,11 +8,15 @@ use std::sync::Arc; use ansi_term::Colour::Red; use clap::{Parser, Subcommand}; use oxide_compiler::compiler::{compiled_module_hash, Compiler}; +use oxide_compiler::compiler_error; use oxide_kernel::kernel::{KernelConfig, KernelCore}; use oxide_kernel::shape_forge::{ShapeForge, EMPTY_SHAPE_ID}; 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_types::object::JsObject; +use oxide_vm::vm_error; use oxide_vm::vm_pool::VmPool; use oxide_vm::JsValue; @@ -70,12 +74,12 @@ fn main() -> ExitCode { match cli.command { Some(Commands::Eval { code }) => { - let kernel = make_kernel(); + let kernel = make_kernel(cli.verbose, cli.quiet); let pool = make_pool(&kernel); eval(&code, &kernel, &pool) } Some(Commands::Run { file }) => { - let kernel = make_kernel(); + let kernel = make_kernel(cli.verbose, cli.quiet); let pool = make_pool(&kernel); run(&file, &kernel, &pool) } @@ -89,7 +93,7 @@ fn main() -> ExitCode { update_baseline, leak_check_interval, }) => { - let kernel = make_kernel(); + let kernel = make_kernel(false, false); let pool = make_pool(&kernel); let config = bench::BenchConfig { mode: mode.unwrap_or_else(|| "js".to_string()), @@ -107,8 +111,16 @@ fn main() -> ExitCode { } } -fn make_kernel() -> Arc { - KernelCore::new(KernelConfig::standard()) +fn make_kernel(verbose: bool, quiet: bool) -> Arc { + let mut config = KernelConfig::standard(); + if verbose { + config.log_levels = [Level::Info; SUBSYSTEM_COUNT]; + } else if quiet { + config.log_levels = [Level::Off; SUBSYSTEM_COUNT]; + } else { + config.log_levels = [Level::Error; SUBSYSTEM_COUNT]; + } + KernelCore::new(config) } fn make_pool(kernel: &Arc) -> Arc { @@ -121,6 +133,7 @@ fn eval(code: &str, kernel: &Arc, pool: &Arc) -> ExitCode { Ok(p) => p, Err(errors) => { for err in &errors { + compiler_error!("parse error: {}", err); eprintln!("{}", Red.paint(err.to_string())); } return ExitCode::FAILURE; @@ -132,6 +145,7 @@ fn eval(code: &str, kernel: &Arc, pool: &Arc) -> ExitCode { let module = match kernel.code_forge().get_or_insert_with(hash, || compiler.compile(&program)) { Ok(m) => m, Err(err) => { + compiler_error!("compile error: {}", err); eprintln!("{}", Red.paint(err)); return ExitCode::FAILURE; } @@ -144,6 +158,7 @@ fn eval(code: &str, kernel: &Arc, pool: &Arc) -> ExitCode { ExitCode::SUCCESS } Err(err) => { + vm_error!("runtime error: {}", err); eprintln!("{}", Red.paint(err)); ExitCode::FAILURE } @@ -226,6 +241,7 @@ fn run(file: &str, kernel: &Arc, pool: &Arc) -> ExitCode { match fs::read_to_string(file) { Ok(source) => eval(&source, kernel, pool), Err(err) => { + kernel_error!("cannot read {}: {}", file, err); eprintln!("{}", Red.paint(format!("Cannot read {file}: {err}"))); ExitCode::FAILURE } @@ -239,11 +255,13 @@ fn compile(expr: Option, file: Option) -> ExitCode { match fs::read_to_string(&path) { Ok(s) => s, Err(err) => { + kernel_error!("cannot read {}: {}", path, err); eprintln!("{}", Red.paint(format!("Cannot read {path}: {err}"))); return ExitCode::FAILURE; } } } else { + kernel_error!("compile requires -e '' or a file path"); eprintln!("{}", Red.paint("compile requires -e '' or a file path")); return ExitCode::FAILURE; }; @@ -253,6 +271,7 @@ fn compile(expr: Option, file: Option) -> ExitCode { Ok(p) => p, Err(errors) => { for err in &errors { + compiler_error!("parse error: {}", err); eprintln!("{}", Red.paint(err.to_string())); } return ExitCode::FAILURE; @@ -266,6 +285,7 @@ fn compile(expr: Option, file: Option) -> ExitCode { ExitCode::SUCCESS } Err(err) => { + compiler_error!("compile error: {}", err); eprintln!("{}", Red.paint(err)); ExitCode::FAILURE } @@ -291,12 +311,13 @@ fn repl() -> ExitCode { let mut rl = match DefaultEditor::new() { Ok(editor) => editor, Err(err) => { + kernel_error!("failed to start REPL: {}", err); eprintln!("{}", Red.paint(format!("Failed to start REPL: {err}"))); return ExitCode::FAILURE; } }; - let kernel = make_kernel(); + let kernel = make_kernel(false, false); let pool = make_pool(&kernel); let mut source = String::new(); let mut input_buf = String::new(); @@ -347,6 +368,7 @@ fn repl() -> ExitCode { return ExitCode::SUCCESS; } Err(err) => { + vm_error!("REPL error: {}", err); eprintln!("{}", Red.paint(format!("REPL error: {err}"))); return ExitCode::FAILURE; } @@ -355,6 +377,7 @@ fn repl() -> ExitCode { } fn not_implemented(command: &str) -> ExitCode { use ansi_term::Colour::Yellow; + kernel_info!("command not yet implemented: {}", command); eprintln!("{}", Yellow.paint(format!("'{command}' is not yet implemented"))); ExitCode::SUCCESS } diff --git a/crates/oxide_test262/Cargo.toml b/crates/oxide_test262/Cargo.toml index 95ba05af..75592195 100644 --- a/crates/oxide_test262/Cargo.toml +++ b/crates/oxide_test262/Cargo.toml @@ -16,6 +16,7 @@ oxide_compiler = { path = "../oxide_compiler" } oxide_vm = { path = "../oxide_vm" } oxide_parser = { path = "../oxide_parser" } oxide_kernel = { path = "../oxide_kernel" } +oxide_log = { path = "../oxide_log" } serde = { version = "1", features = ["derive"] } serde_yaml = "0.9" walkdir = "2" diff --git a/crates/oxide_test262/src/main.rs b/crates/oxide_test262/src/main.rs index 075759f8..165c55d2 100644 --- a/crates/oxide_test262/src/main.rs +++ b/crates/oxide_test262/src/main.rs @@ -12,6 +12,9 @@ use std::sync::{Arc, OnceLock, RwLock}; use std::time::{Duration, Instant}; use walkdir::WalkDir; +mod test262_log; +use oxide_log::{Level, LogConfig, Output, SUBSYSTEM_COUNT}; + // Thread-local that records the path currently being executed. // Written before every test; read by the panic hook to identify the crash file. std::thread_local! { @@ -1063,8 +1066,10 @@ fn main() { std::panic::set_hook(Box::new(|info| { let path = CURRENT_TEST_PATH.with(|p| p.borrow().clone()); if !path.is_empty() { + test262_error!("CRASH in test: {}", path); eprintln!("CRASH in test: {path}"); } + test262_error!("panic: {}", info); eprintln!("panic: {info}"); })); @@ -1086,11 +1091,17 @@ fn run_tests() -> bool { let config = match RunConfig::parse(&args) { Ok(config) => config, Err(msg) => { + test262_error!("config error: {}", msg); eprintln!("{msg}"); return false; } }; + oxide_log::init(&LogConfig { + output: Output::Stderr, + levels: [Level::Info; SUBSYSTEM_COUNT], + }); + let test262_root = if let Some(root) = config.test262_root.clone() { root } else { @@ -1104,6 +1115,7 @@ fn run_tests() -> bool { }; if !test262_root.exists() { + test262_error!("test262 not found at {}", test262_root.display()); eprintln!( "test262 not found at: {}\n\ Run: git submodule add https://github.com/tc39/test262.git tests/test262\n\ @@ -1116,11 +1128,14 @@ fn run_tests() -> bool { let filter = config.filter.clone().map(|f| f.replace('\\', "/")); + test262_info!("discovering tests in: {}", test262_root.display()); eprintln!("discovering tests in: {}", test262_root.display()); if config.no_skip { + test262_info!("no-skip mode enabled"); eprintln!("no-skip mode: capability filters disabled; unsupported results count as failures"); } let paths = discover_tests(&test262_root); + test262_info!("found {} test files", paths.len()); eprintln!("found {} test files", paths.len()); let total = paths.len(); @@ -1144,6 +1159,7 @@ fn run_tests() -> bool { let log_running_tests = std::env::var_os("OXIDE_TEST262_RUNNING_LOG").is_some(); let heartbeat_path: Option = std::env::var_os("OXIDE_TEST262_HEARTBEAT").map(PathBuf::from); + test262_info!("running on {} worker thread(s)", workers); eprintln!("running on {workers} worker thread(s)"); let skip_until = std::env::var("OXIDE_SKIP_UNTIL") @@ -1170,16 +1186,19 @@ fn run_tests() -> bool { let allow_fail_exit = std::env::var_os("OXIDE_TEST262_ALLOW_FAIL_EXIT").is_some(); if config.supervise && !is_chunk_child && filter.is_none() { + test262_info!("supervised mode enabled"); eprintln!("supervised mode enabled: child-window execution with per-test timeout + auto-resume"); return run_supervised(&args, skip_until, end_index, config.no_skip, &paths); } if let Some(chunk_size) = chunk_size { if !is_chunk_child && filter.is_none() { + test262_info!("chunked mode enabled: {} test(s) per child", chunk_size); eprintln!("chunked mode enabled: {chunk_size} test(s) per child process"); return run_chunked(&args, skip_until, end_index, chunk_size); } } + test262_info!("kernel reset batch: {} test(s)", kernel_batch); eprintln!("kernel reset batch: {kernel_batch} test(s)"); let cursor = AtomicUsize::new(skip_until); let progress = AtomicUsize::new(skip_until); @@ -1220,6 +1239,7 @@ fn run_tests() -> bool { // diagnosing OS-level crashes that need the last stderr line. CURRENT_TEST_PATH.with(|p| *p.borrow_mut() = path_str.clone()); if log_running_tests { + test262_debug!("running: {}", path_str); eprintln!(" [{tid:?}] running: {path_str}"); } if let Some(hb) = heartbeat_ref { @@ -1240,6 +1260,7 @@ fn run_tests() -> bool { tests_since_kernel_reset = 0; } if done % 500 == 0 || done == total { + test262_info!("progress: {}/{} ({}%)", done, total, done * 100 / total); eprintln!(" progress: {done}/{total} ({}%)", done * 100 / total); } } diff --git a/crates/oxide_test262/src/test262_log.rs b/crates/oxide_test262/src/test262_log.rs new file mode 100644 index 00000000..a3f8874d --- /dev/null +++ b/crates/oxide_test262/src/test262_log.rs @@ -0,0 +1,20 @@ +#[macro_export] +macro_rules! test262_error { + ($($arg:tt)*) => { + oxide_log::__log_event!("oxide::test262", oxide_log::tracing::Level::ERROR, $($arg)*) + }; +} + +#[macro_export] +macro_rules! test262_info { + ($($arg:tt)*) => { + oxide_log::__log_event!("oxide::test262", oxide_log::tracing::Level::INFO, $($arg)*) + }; +} + +#[macro_export] +macro_rules! test262_debug { + ($($arg:tt)*) => { + oxide_log::__log_event!("oxide::test262", oxide_log::tracing::Level::DEBUG, $($arg)*) + }; +}