diff --git a/crates/oxide_cli/src/main.rs b/crates/oxide_cli/src/main.rs index ce7e5955..4310ac38 100644 --- a/crates/oxide_cli/src/main.rs +++ b/crates/oxide_cli/src/main.rs @@ -428,7 +428,7 @@ fn eval_repl(code: &str, kernel: &Arc, vm: &mut Vm) -> ExitCode { } }; - let compiler = Compiler::new(); + 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)) { Ok(m) => m, diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index ad14ed0d..9d3ec58c 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -7,21 +7,24 @@ use oxide_ir::lower::lower; /// 结构哈希 / 编译模块哈希 re-export(供编译缓存作键)。 pub use crate::hash::{compiled_module_hash, structural_hash}; -/// 编译入口:状态 = DCE 开关 + RegAlloc 开关,编译其余状态在 emit/lower 内部。 +/// 编译入口:状态 = DCE 开关 + RegAlloc 开关 + REPL 持久开关,编译其余状态在 emit/lower 内部。 pub struct Compiler { /// 是否在 emit 与 lower 之间运行死代码消除(默认开启)。 enable_dce: bool, /// 是否在保守 DCE 后运行 liveness→精确 DCE→RegAlloc 链(默认开启)。 /// 关闭时 vreg 原样当物理号走 lower 降级路径(>253 → RangeError)。 enable_regalloc: bool, + /// 是否为 REPL 持久模式:脚本顶层 let/const 也写全局对象属性。 + repl_persist: bool, } impl Compiler { - /// 构造 `Compiler`:默认开启 DCE 与 RegAlloc。 + /// 构造 `Compiler`:默认开启 DCE 与 RegAlloc,不开启 REPL 持久。 pub fn new() -> Self { Self { enable_dce: true, enable_regalloc: true, + repl_persist: false, } } @@ -38,10 +41,15 @@ impl Compiler { } } + /// 启用 REPL 持久模式(脚本顶层 let/const 也写全局对象)。 + pub fn with_repl_persist(self, enable: bool) -> Self { + Self { repl_persist: enable, ..self } + } + /// 编译整个 script program:AST → IR(`Emitter::emit_program`)→ 统一 IR 管线。 pub fn compile(&self, program: &oxide_parser::Program) -> Result { crate::compiler_debug!("compile: starting..."); - let ir = Emitter::new().emit_program(program)?; + let ir = Emitter::new().emit_program(program, self.repl_persist)?; self.compile_ir(ir) } diff --git a/crates/oxide_emit/src/emit.rs b/crates/oxide_emit/src/emit.rs index 8842dc7a..f90635fd 100644 --- a/crates/oxide_emit/src/emit.rs +++ b/crates/oxide_emit/src/emit.rs @@ -182,6 +182,9 @@ pub struct CompileCtx { /// 是否为脚本顶层模块(emit_program 的根上下文)。顶层 var/function 声明需 /// 同步写全局对象属性(脚本环境记录的 var 可经 globalThis 反射);函数体为 false。 pub(crate) is_global_scope: bool, + /// 是否为 REPL 持久模式:脚本顶层 let/const 也写全局对象属性,使跨轮次 + /// 读取(LOAD_GLOBAL)可见变量绑定。仅 `eval_repl` 设置。 + pub(crate) repl_persist: bool, pub(crate) static_block_this_reg: Option, pub(crate) field_buffer: Option, /// 类构造器模块中 `@@field_keys` upvalue 下标(实例字段 computed key 数组)。 @@ -315,6 +318,7 @@ impl CompileCtx { is_async: false, is_strict: false, is_global_scope: false, + repl_persist: false, static_block_this_reg: None, field_buffer: None, field_keys_uv: None, @@ -1496,11 +1500,13 @@ impl Emitter { /// /// 调用方为 `oxide_compiler::Compiler::compile`:本函数完成 emit 半程, /// 随后由 `oxide_ir::lower::lower` 降为字节码。 - pub fn emit_program(&self, program: &oxide_parser::Program) -> Result { + /// `repl_persist` 为 true 时脚本顶层 let/const 也写全局对象(REPL 跨轮次持久)。 + pub fn emit_program(&self, program: &oxide_parser::Program, repl_persist: bool) -> Result { crate::emit_debug!("emit_program: {} stmts", program.body.len()); let mut ctx = CompileCtx::new(); // 脚本顶层:var/function 声明需落到全局对象,let/const/class 不进全局。 ctx.is_global_scope = true; + ctx.repl_persist = repl_persist; // 脚本顶层严格模式由源码 "use strict" directive 决定(嵌套函数经父 ctx 继承)。 ctx.is_strict = program.has_use_strict_directive(); self.predeclare_function_declarations(&program.body, &mut ctx); diff --git a/crates/oxide_emit/src/shared/binding.rs b/crates/oxide_emit/src/shared/binding.rs index 110e4a25..1b721312 100644 --- a/crates/oxide_emit/src/shared/binding.rs +++ b/crates/oxide_emit/src/shared/binding.rs @@ -98,6 +98,10 @@ impl Emitter { if ctx.is_global_scope && matches!(kind, VariableDeclarationKind::Var) { self.emit_global_prop_write(name, src_reg, ctx); } + // REPL 持久模式:let/const 也写全局对象属性,使跨轮次读取可见。 + if ctx.repl_persist && ctx.is_global_scope && !matches!(kind, VariableDeclarationKind::Var) { + self.emit_global_prop_write(name, src_reg, ctx); + } Ok(()) }