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:
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 委托在此执行。
|
||||
|
||||
Reference in New Issue
Block a user