feat: compiler/test262 runner 接线 + 模块顶层 this 绑定

- Compiler::compile_module:Emitter::emit_program_module → 统一 IR 管线(DCE/regalloc/lower/flatten)
- CompiledModule.is_es_module:VM run 时模块顶层 this=undefined(脚本仍为全局对象)
- 全局注册 __moduleLinkGet
- test262 runner:Test262ModuleLoader(相对路径解析 + import attributes 判定 json/text/bytes),is_module 走 compile_module
This commit is contained in:
爱特每羊队
2026-08-12 17:37:24 +08:00
parent 6da248872b
commit 4303462d19
8 changed files with 92 additions and 7 deletions
+5
View File
@@ -79,6 +79,9 @@ pub struct CompiledModule {
/// 全局扁平模块 id:编译末端 flatten 阶段分配(顶层 0,子模块 DFS 递增)。
/// `CREATE_CLOSURE` 的 imm16 在 flatten 后即此 id,运行时以它为平表下标。
pub flat_id: u32,
/// 是否为 ES module 顶层:VM 据此把顶层 `this` 绑定为 undefined
/// (模块环境记录 GetThisBinding 返回 undefined,区别于脚本全局 this)。
pub is_es_module: bool,
}
impl CompiledModule {
@@ -104,6 +107,7 @@ impl CompiledModule {
upvalue_captures: Vec::new(),
cells_needed: 0,
flat_id: 0,
is_es_module: false,
}
}
}
@@ -136,6 +140,7 @@ impl Clone for CompiledModule {
upvalue_captures: self.upvalue_captures.clone(),
cells_needed: self.cells_needed,
flat_id: self.flat_id,
is_es_module: self.is_es_module,
}
}
}
+23 -4
View File
@@ -38,12 +38,31 @@ impl Compiler {
}
}
/// 编译整个 program:AST → IR(`Emitter::emit_program`)→(可选 DCE)→(可选
/// liveness/精确 DCE/RegAlloc)→ bytecode(`lower`)。
/// 编译整个 script program:AST → IR(`Emitter::emit_program`)→ 统一 IR 管线。
pub fn compile(&self, program: &oxide_parser::Program) -> Result<CompiledModule, String> {
crate::compiler_debug!("compile: starting...");
let mut ir = Emitter::new().emit_program(program)?;
crate::compiler_debug!("compile: after emit {} insts", ir.insts.len());
let ir = Emitter::new().emit_program(program)?;
self.compile_ir(ir)
}
/// 编译 ES module:AST → IR(`Emitter::emit_program_module`,含依赖模块链接)→ 统一 IR 管线。
/// `module_path` 为模块文件的规范路径(依赖解析基准 = 其父目录),依赖加载经
/// `loader` 解析。
pub fn compile_module(
&self, program: &oxide_parser::Program, module_path: &str,
loader: &mut dyn oxide_emit::module::ModuleSourceLoader,
) -> Result<CompiledModule, String> {
crate::compiler_debug!("compile_module: starting...");
let ir = Emitter::new().emit_program_module(program, module_path, loader)?;
let mut module = self.compile_ir(ir)?;
module.is_es_module = true;
Ok(module)
}
/// 统一 IR 后处理管线:(可选 DCE)→(可选 liveness/精确 DCE/RegAlloc)→
/// bytecode(`lower`)→ 子模块拍平。
fn compile_ir(&self, mut ir: oxide_ir::IRFunction) -> Result<CompiledModule, String> {
crate::compiler_debug!("compile_ir: after emit {} insts", ir.insts.len());
if self.enable_dce {
oxide_dce::dce(&mut ir); // 顶层函数;nested 不递归
crate::compiler_debug!("compile: after DCE {} insts", ir.insts.len());
+1
View File
@@ -97,6 +97,7 @@ pub fn lower(f: &IRFunction) -> Result<CompiledModule, String> {
upvalue_captures: f.upvalue_captures.clone(),
cells_needed: f.cells_needed,
flat_id: 0,
is_es_module: false,
})
}
+1
View File
@@ -13,6 +13,7 @@ path = "src/main.rs"
[dependencies]
oxide_bytecode = { path = "../oxide_bytecode" }
oxide_compiler = { path = "../oxide_compiler" }
oxide_emit = { path = "../oxide_emit" }
oxide_vm = { path = "../oxide_vm" }
oxide_parser = { path = "../oxide_parser" }
oxide_kernel = { path = "../oxide_kernel" }
+44 -1
View File
@@ -436,6 +436,44 @@ fn run_test(
}
}
/// test262 模块用例的依赖加载器:以测试文件父目录为基准解析相对导入,
/// 读取磁盘上的 fixture 源码;json/text/bytes 经 import attributes 判定。
struct Test262ModuleLoader;
impl oxide_emit::module::ModuleSourceLoader for Test262ModuleLoader {
fn resolve(
&mut self, base_dir: &str, specifier: &str, attributes: &[(&str, &str)],
) -> Result<oxide_emit::module::ResolvedModule, String> {
use oxide_emit::module::{ModuleKind, ResolvedModule};
let kind = attributes
.iter()
.find(|(k, _)| *k == "type")
.map(|(_, v)| match *v {
"json" => ModuleKind::Json,
"text" => ModuleKind::Text,
"bytes" => ModuleKind::Bytes,
_ => ModuleKind::Js,
})
.unwrap_or(ModuleKind::Js);
let base = Path::new(base_dir);
let full = if specifier.starts_with('/') {
PathBuf::from(specifier.trim_start_matches('/'))
} else {
base.join(specifier)
};
let canonical = full
.canonicalize()
.map_err(|e| format!("cannot resolve module {specifier}: {e}"))?;
let content = std::fs::read_to_string(&canonical)
.map_err(|e| format!("cannot read module {specifier}: {e}"))?;
Ok(ResolvedModule {
source: content,
path: canonical.to_string_lossy().to_string(),
kind,
})
}
}
/// 单测执行主流程:拼 harness 前缀 → parse → compile → run;
/// 依据 `negative` 元数据校验期望错误,未实现特性按 no_skip 选择跳过或失败。
#[expect(clippy::too_many_arguments)]
@@ -495,7 +533,12 @@ fn run_test_inner(
};
let compiler = if no_regalloc { Compiler::new().with_regalloc(false) } else { Compiler::new() };
let module = match compiler.compile(&program) {
let module = match if is_module {
let mut loader = Test262ModuleLoader;
compiler.compile_module(&program, &path.to_string_lossy().to_string(), &mut loader)
} else {
compiler.compile(&program)
} {
Ok(m) => m,
Err(e) => {
let dur = start.elapsed().as_millis() as u64;
@@ -26,6 +26,15 @@ pub fn bind_global(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
oxide_builtins::global::decode_uri_component::<crate::vm::Vm> as *const (),
1,
),
// 模块求值 / 命名空间内部辅助(import 实现;非 JS 标准全局,以下划线开头避免撞名)。
("__moduleObject", oxide_builtins::module::module_object::<crate::vm::Vm> as *const (), 0),
("__moduleSet", oxide_builtins::module::module_set::<crate::vm::Vm> as *const (), 3),
("__moduleGet", oxide_builtins::module::module_get::<crate::vm::Vm> as *const (), 2),
("__moduleLinkGet", oxide_builtins::module::module_link_get::<crate::vm::Vm> as *const (), 2),
("__moduleStar", oxide_builtins::module::module_star::<crate::vm::Vm> as *const (), 2),
("__moduleSeal", oxide_builtins::module::module_seal::<crate::vm::Vm> as *const (), 1),
("__moduleEval", oxide_builtins::module::module_eval::<crate::vm::Vm> as *const (), 1),
("__moduleData", oxide_builtins::module::module_data::<crate::vm::Vm> as *const (), 2),
],
);
}
+7 -2
View File
@@ -333,9 +333,14 @@ impl Vm {
}
}
// 全局代码的 this 绑定为全局对象(ECMA-262 全局执行上下文)。
// 顶层 this:脚本为全局对象(ECMA-262 全局执行上下文);
// ES module 顶层环境 GetThisBinding 返回 undefined。
let global = self.session.global_object();
self.regs[254] = JsValue::from_js_object(global.as_ptr() as *mut JsObject);
self.regs[254] = if module.is_es_module {
JsValue::undefined()
} else {
JsValue::from_js_object(global.as_ptr() as *mut JsObject)
};
let result = self.dispatch();
// 顶层执行结束后 drain 微任务队列:Promise reactions 与 thenable 委托在此执行。