//! 动态编译:`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 { Err("dynamic compilation unavailable: no compiler service installed".to_string()) } fn compile_eval_script(&self, _source: &str) -> Result { Err("dynamic compilation unavailable: no compiler service installed".to_string()) } } impl Vm { /// 动态编译函数(`Function` 构造器路径):把参数列表与函数体 wrap 成匿名函数 /// 源码,走完整编译链后取匿名函数模块追加进 VM 平表,返回对应函数对象。 /// /// # 源契约 /// `params` 与 `body` 须为 `string_forge::source_escape` 产物(源码域转义 /// 形态):孤立 surrogate / FFFD 单元以 `\uXXXX` 转义文本承载,反斜杠 /// 原样透传。 /// /// # 步骤 /// 1. wrap 源码 `{prefix} anonymous(p...) { body }`(prefix 按生成器 / /// 异步标志取 `function` / `function*` / `async function` / /// `async function*`),parse + compile。 /// 2. 取 `sub_modules[0]`(匿名函数模块),把其子树 flat_id 重编号到平表末尾 /// 并重写子树内每条 `CREATE_CLOSURE` 的 imm16。 /// 3. 同步扩容当前代际的常量缓存,建函数对象并设置 name/length。 /// /// # 边界与前提 /// - 编译或解析失败返回 `Err`(由 builtin 层转 SyntaxError)。 /// - 追加的子树原 flat_id 自 1 连续(flatten 后 1=匿名体,2…=其嵌套函数); /// 新 id = 平表长度 + (old - 1)。 /// - 动态函数在函数对象存活期间跨 run 有效:扩展落当前代际平表,存活函数 /// 对象按代际引用保活该表;full_reset 重建 session 后失效。 /// /// # 副作用 /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。 pub fn create_dynamic_function( &mut self, params: &[String], body: &str, is_generator: bool, is_async: bool, ) -> Result { // wrap 源码:body 两端换行防止以行注释结尾吞掉右花括号;形参串后补 // 换行,使形参以 HTML 注释(``)结尾时注释在行末终止、 // 右括号不被吞(规范按形参串与体串各自独立解析,此处以换行等价)。 // 函数形态按生成器 / 异步标志分流,编译标志(is_generator / is_async) // 由 emit 层从 AST 读回,函数对象原型与 prototype 属性面随之自动分流。 let prefix = if is_async { if is_generator { "async function*" } else { "async function" } } else if is_generator { "function*" } else { "function" }; let params_str = params.join(", "); let source = format!("{prefix} anonymous({params_str}\n) {{\n{body}\n}}"); // 动态路径源契约:调用方以源码域转义(`string_forge::source_escape`) // 形态传源;正则字面量源切片经 `source_escape_to_key` 还原注入 // marker 后入池(编码源口径,区别于静态源的 `pool_key_plain`)。 // 编译经注入的编译服务完成(parse 与 compile 串联在实现侧)。 let mut module = self.compiler.compile_script(&source)?; let anonymous = module.sub_modules.remove(0); // 形参数以编译结果为准:单个实参 "a,b,c" 拼接后解析为 3 个形参 // (ES 动态函数把非末位实参以逗号连接成参数串再解析)。 let formal_count = anonymous.n_args as i32; // 子树重编号 + 追加:base = 当前代际平表长度,DFS 前序压入,push 序即新 // flat_id。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。 let table = self.current_table_mut(); let base = table.modules.len() as u32; let mut added = Vec::new(); rehome_subtree(&anonymous, base, &mut added); Arc::make_mut(&mut table.modules).extend(added); // 平表变长后同步扩容常量缓存与 si 侧表,否则激活新模块常量时越界 panic。 table.immutables.resize(table.modules.len(), OnceLock::new()); table.si_tables.resize(table.modules.len(), OnceLock::new()); let func_val = self.create_function_object(base, self.current_gen, false, false, false, false); let func_obj = unsafe { &mut *func_val.as_js_object_ptr() }; let length_si = self.kernel_core.perm_interner().intern("length").0; let name_si = self.kernel_core.perm_interner().intern("name").0; // length/name 为不可写不可枚举可配置,且 length 先于 name(规范属性顺序)。 let attrs = PropAttributes::new(false, false, true); let length_val = JsValue::int(formal_count); let name_val = self.new_string("anonymous"); self.define_data_property(func_obj, length_si, length_val, attrs)?; self.define_data_property(func_obj, name_si, name_val, attrs)?; Ok(func_val) } /// 动态编译脚本(eval 脚本模式):把源码按脚本模式编译,var/函数声明落全局对象 /// (属性 configurable:true,区别于普通脚本顶层的 false)。 /// /// # 步骤 /// 1. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true, /// Compiler 置 is_eval_script=true)。 /// 2. 整棵模块树(根 flat_id=0 + 嵌套函数)追加进平表:`rehome_subtree(&module, base+1)`, /// 使根落 base、子函数 old→base+old,CREATE_CLOSURE imm16 同步重写。 /// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。 /// /// # 边界与前提 /// - 顶层 return 不报 SyntaxError(emit 无此检查,与 CLI 脚本路径一致)——已知偏差。 /// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。 /// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。 /// /// # 副作用 /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。 pub fn create_dynamic_script(&mut self, code: &str) -> Result { // eval 脚本:顶层 var/function 声明落全局属性 configurable:true。 // 动态路径源契约同 `create_dynamic_function`(源码域转义形态传源)。 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(); let base = table.modules.len() as u32; let mut added = Vec::new(); rehome_subtree(&module, base + 1, &mut added); Arc::make_mut(&mut table.modules).extend(added); // 平表变长后同步扩容常量缓存与 si 侧表,否则激活新模块常量时越界 panic。 table.immutables.resize(table.modules.len(), OnceLock::new()); table.si_tables.resize(table.modules.len(), OnceLock::new()); Ok(self.create_function_object(base, self.current_gen, false, false, false, false)) } /// 动态编译脚本(普通脚本模式):顶层 var/function 声明落全局对象 /// configurable:false,与静态脚本顶层一致(区别于 [`create_dynamic_script`] /// 的 eval 脚本 configurable:true 面)。 /// /// 供 test262 宿主 `$262.evalScript` 使用:按 test262 harness 规范它是 /// 当前 realm 的一段普通 script,不是 eval。 /// /// # 步骤 /// 1. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true, /// 不设 is_eval_script,与普通脚本同一编译臂)。 /// 2. 整棵模块树追加进平表:同 [`create_dynamic_script`] /// (`rehome_subtree(&module, base+1)`,根落 base、子函数 old→base+old)。 /// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。 /// /// # 边界与前提 /// - 编译或解析失败返回 `Err`(由宿主层转 SyntaxError)。 /// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。 /// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。 /// /// # 副作用 /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。 pub fn create_plain_dynamic_script(&mut self, code: &str) -> Result { // 普通脚本:顶层 var/function 声明落全局属性 configurable:false, // let/const 落全局词法环境。动态路径源契约同 `create_dynamic_function` // (源码域转义形态传源)。 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(); let base = table.modules.len() as u32; let mut added = Vec::new(); rehome_subtree(&module, base + 1, &mut added); Arc::make_mut(&mut table.modules).extend(added); // 平表变长后同步扩容常量缓存与 si 侧表,否则激活新模块常量时越界 panic。 table.immutables.resize(table.modules.len(), OnceLock::new()); table.si_tables.resize(table.modules.len(), OnceLock::new()); Ok(self.create_function_object(base, self.current_gen, false, false, false, false)) } } /// 把 flatten 后的子模块子树重编号到平表偏移 `base`:DFS 前序拷贝进 `out`, /// 新 flat_id = base + (old - 1),子树内每条 `CREATE_CLOSURE` 的 imm16 同步重写。 /// 原子树 flat_id 自 1 连续,因此拷贝顺序即新 id 顺序,`out` 下标对齐平表槽位。 fn rehome_subtree(module: &CompiledModule, base: u32, out: &mut Vec>) { let new_id = base + module.flat_id - 1; let mut bytecode = module.bytecode.to_vec(); for instr in &mut bytecode { if opcode::opcode(*instr) == OpCode::CREATE_CLOSURE { let old = opcode::imm16(*instr) as u32; let new_flat = base + (old - 1); *instr = opcode::encode( OpCode::CREATE_CLOSURE, opcode::rd(*instr), (new_flat & 0xFF) as u8, ((new_flat >> 8) & 0xFF) as u8, ); } } let mut rehomed = module.clone(); rehomed.bytecode = Arc::from(bytecode); rehomed.flat_id = new_id; out.push(Arc::new(rehomed)); for sub in &module.sub_modules { rehome_subtree(sub, base, out); } }