refactor: 动态编译改经 CompilerService 注入,oxide_vm 去编译器依赖

This commit is contained in:
2026-10-08 05:51:58 +00:00
parent 66735f802f
commit 4f9b6ab444
94 changed files with 459 additions and 67 deletions
+23 -19
View File
@@ -1,14 +1,33 @@
//! 动态编译:`Function` 构造器与脚本动态编译入口,及子树 flat_id 重编号。
//! 编译经 `Vm` 持有的 `CompilerService` 句柄完成(`self.compiler`),
//! 本 crate 生产代码不直接依赖 `oxide_compiler` / `oxide_parser`。
use std::sync::{Arc, OnceLock};
use oxide_bytecode::module::CompiledModule;
use oxide_bytecode::opcode::{self, OpCode};
use oxide_runtime_api::CompilerService;
use oxide_types::object::PropAttributes;
use oxide_types::value::JsValue;
use super::Vm;
/// 缺省 no-op 编译服务:两方法均返 `Err`(错误消息指向「未安装编译服务」)。
///
/// 供不需要动态编译的 `Vm` 实例(构造器缺省值);生产 entry points 与
/// 动态编译测试经 `set_compiler_service` 注入真实实现。
pub(crate) struct NoopCompilerService;
impl CompilerService for NoopCompilerService {
fn compile_script(&self, _source: &str) -> Result<CompiledModule, String> {
Err("dynamic compilation unavailable: no compiler service installed".to_string())
}
fn compile_eval_script(&self, _source: &str) -> Result<CompiledModule, String> {
Err("dynamic compilation unavailable: no compiler service installed".to_string())
}
}
impl Vm {
/// 动态编译函数(`Function` 构造器路径):把参数列表与函数体 wrap 成匿名函数
/// 源码,走完整编译链后取匿名函数模块追加进 VM 平表,返回对应函数对象。
@@ -57,15 +76,11 @@ impl Vm {
let params_str = params.join(", ");
let source = format!("{prefix} anonymous({params_str}\n) {{\n{body}\n}}");
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, &source)
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))?;
// 动态路径源契约:调用方以源码域转义(`string_forge::source_escape`)
// 形态传源;正则字面量源切片经 `source_escape_to_key` 还原注入
// marker 后入池(编码源口径,区别于静态源的 `pool_key_plain`)。
let mut module = oxide_compiler::compiler::Compiler::new()
.with_source_encoded(true)
.compile(&program)?;
// 编译经注入的编译服务完成(parse 与 compile 串联在实现侧)。
let mut module = self.compiler.compile_script(&source)?;
let anonymous = module.sub_modules.remove(0);
// 形参数以编译结果为准:单个实参 "a,b,c" 拼接后解析为 3 个形参
// (ES 动态函数把非末位实参以逗号连接成参数串再解析)。
@@ -113,15 +128,9 @@ impl Vm {
/// # 副作用
/// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。
pub fn create_dynamic_script(&mut self, code: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, code)
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))?;
// eval 脚本:顶层 var/function 声明落全局属性 configurable:true。
// 动态路径源契约同 `create_dynamic_function`(源码域转义形态传源)。
let module = oxide_compiler::compiler::Compiler::new()
.with_eval_script(true)
.with_source_encoded(true)
.compile(&program)?;
let module = self.compiler.compile_eval_script(code)?;
// 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0
// 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。
let table = self.current_table_mut();
@@ -157,15 +166,10 @@ impl Vm {
/// # 副作用
/// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。
pub fn create_plain_dynamic_script(&mut self, code: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, code)
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))?;
// 普通脚本:顶层 var/function 声明落全局属性 configurable:false,
// let/const 落全局词法环境。动态路径源契约同 `create_dynamic_function`
// (源码域转义形态传源)。
let module = oxide_compiler::compiler::Compiler::new()
.with_source_encoded(true)
.compile(&program)?;
let module = self.compiler.compile_script(code)?;
// 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0
// 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。
let table = self.current_table_mut();
+19
View File
@@ -37,6 +37,7 @@ mod props;
mod tables;
mod vmhost;
pub(crate) use dynamic::NoopCompilerService;
pub(crate) use frames::FrameArgs;
pub use frames::{CallFrame, Completion, ForInIter, FrameContinuation, TryHandler};
pub(crate) use gc_hooks::RootGroup;
@@ -175,6 +176,12 @@ pub struct Vm {
pub(crate) active_immutables: *const [JsValue],
pub(crate) frames: SmallVec<[CallFrame; 16]>,
pub(crate) kernel_core: Arc<KernelCore>,
/// 动态编译服务句柄:`Function` 构造器 / eval / `$262.evalScript` 经它编译
/// 源码(`dynamic.rs` 三方法)。缺省为 no-op stub(返 `Err`),生产 entry
/// points 与动态编译测试经 `set_compiler_service` 注入真实实现。
/// `Arc<dyn CompilerService>` 因 trait `Send + Sync` 而 Send + Sync,
/// 不破坏 `unsafe impl Send for Vm`。
pub(crate) compiler: Arc<dyn oxide_runtime_api::CompilerService>,
/// 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、
/// session GC 簿记与 10 个内建原型槽(见 `realm` 模块)。以 `Arc` 持有:
/// 可变组经 `RefCell` 内部可变性在 `&Arc<Realm>` 下改写,teardown 归
@@ -445,6 +452,18 @@ impl Vm {
&self.kernel_core
}
/// 注入动态编译服务(`Function` 构造器 / eval / `$262.evalScript` 的编译面)。
///
/// # 边界与前提
/// - 构造后缺省为 no-op stub(动态编译返 `Err`);需要动态编译的调用方
/// (生产 entry points 与动态编译测试)须在首次动态编译前注入真实实现。
///
/// # 副作用
/// - 替换 `compiler` 句柄(`Arc` 克隆,无其他状态变更)。
pub fn set_compiler_service(&mut self, service: Arc<dyn oxide_runtime_api::CompilerService>) {
self.compiler = service;
}
/// 只读访问当前 session(builtin world 与 global object)。
///
/// 返回 `Ref` 守卫(session 入 `RefCell` 后无法再给稳定 `&`):调用方