mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 逐指令 trace 补 pc 并加 eval/run --trace 旗标
This commit is contained in:
@@ -46,11 +46,17 @@ struct Cli {
|
||||
enum Commands {
|
||||
Eval {
|
||||
code: String,
|
||||
/// 逐指令 trace:每条指令向 stderr 写一行 pc + opcode + 操作数。
|
||||
#[arg(long)]
|
||||
trace: bool,
|
||||
},
|
||||
Run {
|
||||
file: String,
|
||||
#[arg(long, default_value = "1")]
|
||||
repeat: u64,
|
||||
/// 逐指令 trace:每条指令向 stderr 写一行 pc + opcode + 操作数。
|
||||
#[arg(long)]
|
||||
trace: bool,
|
||||
},
|
||||
Compile {
|
||||
#[arg(short = 'e')]
|
||||
@@ -87,16 +93,16 @@ fn main() -> ExitCode {
|
||||
let cli = Cli::parse();
|
||||
|
||||
match cli.command {
|
||||
Some(Commands::Eval { code }) => {
|
||||
Some(Commands::Eval { code, trace }) => {
|
||||
let kernel = make_kernel(cli.verbose, cli.quiet);
|
||||
let pool = make_pool(&kernel);
|
||||
eval(&code, &kernel, &pool)
|
||||
eval(&code, &kernel, &pool, trace)
|
||||
}
|
||||
Some(Commands::Run { file, repeat }) => {
|
||||
Some(Commands::Run { file, repeat, trace }) => {
|
||||
let kernel = make_kernel(cli.verbose, cli.quiet);
|
||||
let pool = make_pool(&kernel);
|
||||
for n in 0..repeat {
|
||||
let code = run(&file, &kernel, &pool);
|
||||
let code = run(&file, &kernel, &pool, trace);
|
||||
if code != ExitCode::SUCCESS && code != ExitCode::FAILURE {
|
||||
return code;
|
||||
}
|
||||
@@ -155,7 +161,7 @@ fn make_pool(kernel: &Arc<KernelCore>) -> Arc<VmPool> {
|
||||
VmPool::new(Arc::clone(kernel), kernel.config.min_pool_size, kernel.config.max_pool_size)
|
||||
}
|
||||
|
||||
fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
||||
fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool) -> ExitCode {
|
||||
let allocator = Allocator::default();
|
||||
let program = match oxide_parser::parse(&allocator, code) {
|
||||
Ok(p) => p,
|
||||
@@ -180,6 +186,7 @@ fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
||||
};
|
||||
|
||||
let mut guard = pool.spawn();
|
||||
guard.vm_mut().set_instruction_trace(trace);
|
||||
match guard.vm_mut().run(&module) {
|
||||
Ok(result) => {
|
||||
format_result(guard.vm(), kernel.perm_interner().as_ref(), kernel.shape_forge().as_ref(), result);
|
||||
@@ -284,9 +291,9 @@ fn format_array(
|
||||
format!("[{}]", items.join(", "))
|
||||
}
|
||||
|
||||
fn run(file: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
||||
fn run(file: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool) -> ExitCode {
|
||||
match fs::read_to_string(file) {
|
||||
Ok(source) => eval(&source, kernel, pool),
|
||||
Ok(source) => eval(&source, kernel, pool, trace),
|
||||
Err(err) => {
|
||||
kernel_error!("cannot read {}: {}", file, err);
|
||||
eprintln!("{}", Red.paint(format!("Cannot read {file}: {err}")));
|
||||
|
||||
@@ -47,3 +47,34 @@ fn bench_help() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(stdout.contains("--iterations"), "bench --help should list --iterations flag");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_trace_flag_pc_lines() {
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_oxide"))
|
||||
.args(["eval", "--trace", "1 + 2"])
|
||||
.output()
|
||||
.expect("failed to run oxide eval --trace");
|
||||
|
||||
assert!(output.status.success(), "eval --trace should exit 0");
|
||||
// trace 行只走 stderr,stdout 保持纯结果。
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert_eq!(stdout.trim(), "3", "--trace 不应污染 stdout: {stdout}");
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(stderr.lines().all(|l| l.starts_with("pc=")), "trace 行应全部为 pc 行: {stderr}");
|
||||
assert!(stderr.contains("LOAD_CONST"), "trace 应含 LOAD_CONST: {stderr}");
|
||||
assert!(stderr.contains("ADD"), "trace 应含 ADD: {stderr}");
|
||||
assert!(stderr.contains("HALT"), "trace 应含 HALT: {stderr}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_no_trace_flag_no_pc_lines() {
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_oxide"))
|
||||
.args(["eval", "1 + 2"])
|
||||
.output()
|
||||
.expect("failed to run oxide eval");
|
||||
|
||||
assert!(output.status.success(), "eval should exit 0");
|
||||
// 默认路径零 trace 输出。
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(!stderr.contains("pc="), "默认路径不应有 trace 行: {stderr}");
|
||||
}
|
||||
|
||||
@@ -202,12 +202,19 @@ impl Vm {
|
||||
return Err("program counter out of bounds".into());
|
||||
}
|
||||
|
||||
let instr = self.bytecode[self.pc];
|
||||
let pc = self.pc;
|
||||
let instr = self.bytecode[pc];
|
||||
let op = opcode::opcode(instr);
|
||||
let rd = opcode::rd(instr) as usize;
|
||||
let a = opcode::a(instr) as usize;
|
||||
let b = opcode::b(instr) as usize;
|
||||
self.pc += 1;
|
||||
self.pc = pc + 1;
|
||||
|
||||
// 逐指令 trace:运行时开关短路(默认关闭,零输出零分配);
|
||||
// pc 为字节码 word 序号,与反汇编 offset 列同单位。
|
||||
if self.trace_instructions {
|
||||
eprintln!("pc={pc:4} {op:>16} rd={rd} a={a} b={b}");
|
||||
}
|
||||
|
||||
match op {
|
||||
OpCode::NOP => {}
|
||||
|
||||
@@ -350,6 +350,10 @@ pub struct Vm {
|
||||
pub(crate) saved_flat_id_stack: Vec<u32>,
|
||||
/// 帧切换时暂存调用方表代际的栈(与 saved_flat_id_stack 同步 push/pop)。
|
||||
pub(crate) saved_table_gen_stack: Vec<u32>,
|
||||
/// 逐指令 trace 开关:开启时 dispatch 主循环对每条指令向 stderr 写一行
|
||||
/// `pc + opcode + 操作数`。运行时开关,旁路 tracing 级别上限(release
|
||||
/// 构建可用);默认关闭,关闭时热路径仅一次 bool 比较,零输出零分配。
|
||||
pub trace_instructions: bool,
|
||||
}
|
||||
|
||||
impl Drop for Vm {
|
||||
@@ -413,6 +417,12 @@ impl Vm {
|
||||
self.regs[idx as usize] = val;
|
||||
}
|
||||
|
||||
/// 设置逐指令 trace 开关(开启后 dispatch 主循环逐指令向 stderr 写
|
||||
/// `pc + opcode + 操作数` 行,pc 为字节码 word 序号,与反汇编 offset 同单位)。
|
||||
pub fn set_instruction_trace(&mut self, on: bool) {
|
||||
self.trace_instructions = on;
|
||||
}
|
||||
|
||||
/// 只读访问 VM 的 epoch arena。
|
||||
pub fn epoch(&self) -> &Epoch {
|
||||
&self.epoch
|
||||
|
||||
@@ -157,6 +157,7 @@ impl Vm {
|
||||
active_table_gen: 0,
|
||||
saved_flat_id_stack: Vec::new(),
|
||||
saved_table_gen_stack: Vec::new(),
|
||||
trace_instructions: false,
|
||||
};
|
||||
vm.init_generator_intrinsics();
|
||||
vm.init_promise_intrinsics();
|
||||
@@ -291,6 +292,7 @@ impl Vm {
|
||||
active_table_gen: 0,
|
||||
saved_flat_id_stack: Vec::new(),
|
||||
saved_table_gen_stack: Vec::new(),
|
||||
trace_instructions: false,
|
||||
};
|
||||
vm.init_generator_intrinsics();
|
||||
vm.init_promise_intrinsics();
|
||||
|
||||
Reference in New Issue
Block a user