mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
refactor: 动态编译改经 CompilerService 注入,oxide_vm 去编译器依赖
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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` 后无法再给稳定 `&`):调用方
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::time::Duration;
|
||||
use crate::vm::Vm;
|
||||
use crate::{vm_debug, vm_trace, vm_warn};
|
||||
use oxide_kernel::kernel::KernelCore;
|
||||
use oxide_runtime_api::CompilerService;
|
||||
|
||||
struct VmPoolInner {
|
||||
available: Vec<Vm>,
|
||||
@@ -74,6 +75,9 @@ impl PoolCounters {
|
||||
/// 归还(带 5 秒超时强制扩容兜底)。线程安全,可供多线程并发领取。
|
||||
pub struct VmPool {
|
||||
kernel_core: Arc<KernelCore>,
|
||||
/// 动态编译服务句柄:构造期定、此后不可变,`new_vm` 逐个注入每个 Vm
|
||||
/// (预热与增长路径同口径,无「预热后补注入」时序问题)。
|
||||
compiler: Arc<dyn CompilerService>,
|
||||
inner: Mutex<VmPoolInner>,
|
||||
condvar: Condvar,
|
||||
max_size: Option<usize>,
|
||||
@@ -94,9 +98,12 @@ impl VmPool {
|
||||
/// 创建 VM 池并同步预热 `min_size` 个 Vm(数量受 `max_size` 钳制),
|
||||
/// 首次 `spawn` 直接命中池。`max_size` 为池上限,`None` 表示不限。
|
||||
///
|
||||
/// `compiler` 为池内全部 Vm 的动态编译服务(构造期定、此后不可变)。
|
||||
/// 计数器为本池私有(不与其他池共享)。
|
||||
pub fn new(kernel_core: Arc<KernelCore>, min_size: usize, max_size: Option<usize>) -> Arc<Self> {
|
||||
Self::with_counters(kernel_core, min_size, max_size, PoolCounters::shared())
|
||||
pub fn new(
|
||||
kernel_core: Arc<KernelCore>, compiler: Arc<dyn CompilerService>, min_size: usize, max_size: Option<usize>,
|
||||
) -> Arc<Self> {
|
||||
Self::with_counters(kernel_core, compiler, min_size, max_size, PoolCounters::shared())
|
||||
}
|
||||
|
||||
/// 创建共享外部聚合计数器的 VM 池:多个池的原子增减累加到同一聚合,
|
||||
@@ -110,15 +117,18 @@ impl VmPool {
|
||||
/// # 边界与前提
|
||||
/// - 计数器通常经 `PoolCounters::shared` 跨线程共享;池本体可跨线程移动
|
||||
/// (Send/Sync),worker 经 spawn 独占借出 Vm。
|
||||
/// - `compiler` 在构造期固化:预热与增长路径经 `new_vm` 逐个注入同一服务。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 同步创建 `min_size` 个 Vm。
|
||||
pub fn with_counters(
|
||||
kernel_core: Arc<KernelCore>, min_size: usize, max_size: Option<usize>, counters: Arc<PoolCounters>,
|
||||
kernel_core: Arc<KernelCore>, compiler: Arc<dyn CompilerService>, min_size: usize, max_size: Option<usize>,
|
||||
counters: Arc<PoolCounters>,
|
||||
) -> Arc<Self> {
|
||||
let warm = min_size.min(max_size.unwrap_or(min_size));
|
||||
let pool = Arc::new(Self {
|
||||
kernel_core: Arc::clone(&kernel_core),
|
||||
compiler: Arc::clone(&compiler),
|
||||
inner: Mutex::new(VmPoolInner {
|
||||
available: Vec::new(),
|
||||
total_count: 0,
|
||||
@@ -129,7 +139,7 @@ impl VmPool {
|
||||
});
|
||||
let mut inner = pool.inner.lock().unwrap();
|
||||
for _ in 0..warm {
|
||||
inner.available.push(Self::new_vm(&kernel_core));
|
||||
inner.available.push(pool.new_vm());
|
||||
inner.total_count += 1;
|
||||
}
|
||||
drop(inner);
|
||||
@@ -138,12 +148,15 @@ impl VmPool {
|
||||
pool
|
||||
}
|
||||
|
||||
fn new_vm(core: &Arc<KernelCore>) -> Vm {
|
||||
Vm::with_kernel_core(Arc::clone(core))
|
||||
/// 新建一个 Vm 并注入本池的动态编译服务(预热与增长路径共用)。
|
||||
fn new_vm(&self) -> Vm {
|
||||
let mut vm = Vm::with_kernel_core(Arc::clone(&self.kernel_core));
|
||||
vm.set_compiler_service(Arc::clone(&self.compiler));
|
||||
vm
|
||||
}
|
||||
|
||||
fn replace_vm(&self) -> Vm {
|
||||
Self::new_vm(&self.kernel_core)
|
||||
self.new_vm()
|
||||
}
|
||||
|
||||
/// 从池中借出一个 VM:优先复用空闲实例,否则在池未满时新建,池满则阻塞等待归还。
|
||||
@@ -173,7 +186,7 @@ impl VmPool {
|
||||
drop(inner);
|
||||
self.counters.total.fetch_add(1, Ordering::Relaxed);
|
||||
self.counters.record_borrow();
|
||||
let vm = Self::new_vm(&self.kernel_core);
|
||||
let vm = self.new_vm();
|
||||
return VmGuard {
|
||||
vm: Some(vm),
|
||||
pool: Arc::clone(self),
|
||||
@@ -189,7 +202,7 @@ impl VmPool {
|
||||
drop(inner);
|
||||
self.counters.total.fetch_add(1, Ordering::Relaxed);
|
||||
self.counters.record_borrow();
|
||||
let vm = Self::new_vm(&self.kernel_core);
|
||||
let vm = self.new_vm();
|
||||
return VmGuard {
|
||||
vm: Some(vm),
|
||||
pool: Arc::clone(self),
|
||||
@@ -291,16 +304,22 @@ impl Drop for VmGuard {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use oxide_compiler::DefaultCompilerService;
|
||||
use oxide_kernel::kernel::KernelConfig;
|
||||
|
||||
fn test_kernel() -> Arc<KernelCore> {
|
||||
KernelCore::new(KernelConfig::minimal())
|
||||
}
|
||||
|
||||
/// 测试池的编译服务(池测试不 exercise 动态编译,缺省实现即可)。
|
||||
fn test_compiler() -> Arc<dyn CompilerService> {
|
||||
Arc::new(DefaultCompilerService)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pool_spawn_returns_guard() {
|
||||
let kernel = test_kernel();
|
||||
let pool = VmPool::new(kernel, 1, None);
|
||||
let pool = VmPool::new(kernel, test_compiler(), 1, None);
|
||||
let guard = pool.spawn();
|
||||
drop(guard);
|
||||
}
|
||||
@@ -308,7 +327,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_pool_recycle_on_drop() {
|
||||
let kernel = test_kernel();
|
||||
let pool = VmPool::new(kernel, 1, None);
|
||||
let pool = VmPool::new(kernel, test_compiler(), 1, None);
|
||||
let guard = pool.spawn();
|
||||
drop(guard);
|
||||
let guard2 = pool.spawn();
|
||||
@@ -318,7 +337,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_pool_grows_if_empty() {
|
||||
let kernel = test_kernel();
|
||||
let pool = VmPool::new(Arc::clone(&kernel), 1, Some(3));
|
||||
let pool = VmPool::new(Arc::clone(&kernel), test_compiler(), 1, Some(3));
|
||||
let g1 = pool.spawn();
|
||||
let g2 = pool.spawn();
|
||||
drop(g1);
|
||||
@@ -328,7 +347,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_pool_warms_min_size_on_new() {
|
||||
let kernel = test_kernel();
|
||||
let pool = VmPool::new(kernel, 2, None);
|
||||
let pool = VmPool::new(kernel, test_compiler(), 2, None);
|
||||
{
|
||||
let inner = pool.inner.lock().unwrap();
|
||||
assert_eq!(inner.available.len(), 2);
|
||||
@@ -347,7 +366,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_pool_warmup_clamped_by_max_size() {
|
||||
let kernel = test_kernel();
|
||||
let pool = VmPool::new(kernel, 2, Some(1));
|
||||
let pool = VmPool::new(kernel, test_compiler(), 2, Some(1));
|
||||
let inner = pool.inner.lock().unwrap();
|
||||
assert_eq!(inner.available.len(), 1);
|
||||
assert_eq!(inner.total_count, 1);
|
||||
@@ -357,8 +376,8 @@ mod tests {
|
||||
fn test_with_counters_shared_aggregate() {
|
||||
let kernel = test_kernel();
|
||||
let counters = PoolCounters::shared();
|
||||
let pool_a = VmPool::with_counters(Arc::clone(&kernel), 1, Some(4), Arc::clone(&counters));
|
||||
let pool_b = VmPool::with_counters(Arc::clone(&kernel), 2, Some(4), Arc::clone(&counters));
|
||||
let pool_a = VmPool::with_counters(Arc::clone(&kernel), test_compiler(), 1, Some(4), Arc::clone(&counters));
|
||||
let pool_b = VmPool::with_counters(Arc::clone(&kernel), test_compiler(), 2, Some(4), Arc::clone(&counters));
|
||||
|
||||
// 预热后聚合等于两池之和:总数 1 + 2 = 3,全部空闲。
|
||||
assert_eq!(counters.total(), 3, "聚合总数应为两池预热之和");
|
||||
@@ -383,7 +402,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_peak_count_single_thread_exact() {
|
||||
let kernel = test_kernel();
|
||||
let pool = VmPool::new(kernel, 2, None);
|
||||
let pool = VmPool::new(kernel, test_compiler(), 2, None);
|
||||
assert_eq!(pool.peak_count(), 0, "从未借出的池峰值应为零");
|
||||
|
||||
let g1 = pool.spawn();
|
||||
@@ -401,8 +420,8 @@ mod tests {
|
||||
fn test_peak_count_shared_aggregate() {
|
||||
let kernel = test_kernel();
|
||||
let counters = PoolCounters::shared();
|
||||
let pool_a = VmPool::with_counters(Arc::clone(&kernel), 1, Some(4), Arc::clone(&counters));
|
||||
let pool_b = VmPool::with_counters(Arc::clone(&kernel), 1, Some(4), Arc::clone(&counters));
|
||||
let pool_a = VmPool::with_counters(Arc::clone(&kernel), test_compiler(), 1, Some(4), Arc::clone(&counters));
|
||||
let pool_b = VmPool::with_counters(Arc::clone(&kernel), test_compiler(), 1, Some(4), Arc::clone(&counters));
|
||||
|
||||
let g1 = pool_a.spawn();
|
||||
let g2 = pool_b.spawn();
|
||||
@@ -427,7 +446,7 @@ mod tests {
|
||||
std::thread::spawn(move || {
|
||||
// 每线程自建池、共享同一聚合计数器(Vm 经 unsafe impl Send 可
|
||||
// 跨线程,本测试仍按每线程自有池验证计数器聚合)。
|
||||
let pool = VmPool::with_counters(kernel, 0, Some(8), counters);
|
||||
let pool = VmPool::with_counters(kernel, test_compiler(), 0, Some(8), counters);
|
||||
for _ in 0..50 {
|
||||
let guard = pool.spawn();
|
||||
drop(guard);
|
||||
@@ -447,7 +466,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_collect_idle_gc_returns_idle_count() {
|
||||
let kernel = test_kernel();
|
||||
let pool = VmPool::new(kernel, 1, None);
|
||||
let pool = VmPool::new(kernel, test_compiler(), 1, None);
|
||||
let guard = pool.spawn();
|
||||
drop(guard);
|
||||
assert_eq!(pool.collect_idle_gc(), 1, "归还一个空闲 VM 后收集数应为 1");
|
||||
|
||||
@@ -68,6 +68,9 @@ impl Vm {
|
||||
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
|
||||
frames: smallvec::SmallVec::new(),
|
||||
kernel_core: core,
|
||||
// 缺省 no-op 编译服务:动态编译返 Err,生产 entry points 与
|
||||
// 动态编译测试经 set_compiler_service 注入真实实现。
|
||||
compiler: Arc::new(crate::vm::NoopCompilerService),
|
||||
realm: Arc::new(Realm {
|
||||
realm_id,
|
||||
session: RefCell::new(session),
|
||||
@@ -221,6 +224,9 @@ impl Vm {
|
||||
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
|
||||
frames: smallvec::SmallVec::new(),
|
||||
kernel_core: core,
|
||||
// 缺省 no-op 编译服务:动态编译返 Err,生产 entry points 与
|
||||
// 动态编译测试经 set_compiler_service 注入真实实现。
|
||||
compiler: Arc::new(crate::vm::NoopCompilerService),
|
||||
realm: Arc::new(Realm {
|
||||
realm_id,
|
||||
session: RefCell::new(session),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! vm_support 内联测试:全量重置状态清除与全局重建、分配上限、生成器与 BigInt 存活、动态编译及源文本断言行为。
|
||||
use super::*;
|
||||
|
||||
use oxide_compiler::DefaultCompilerService;
|
||||
|
||||
fn global_prop(vm: &Vm, name: &str) -> JsValue {
|
||||
global_prop_opt(vm, name).expect("global slot should exist")
|
||||
}
|
||||
@@ -499,6 +501,7 @@ fn immutables_filled_once_per_module() {
|
||||
#[test]
|
||||
fn dynamic_function_basic_arity() {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
// 与等价静态函数返回值逐位一致(引擎函数调用统一产 Double 数值)。
|
||||
let expected = run_source(&mut vm, "function f(a,b){return a+b} f(3,4)");
|
||||
let result = run_source(&mut vm, "new Function('a','b','return a+b')(3,4)");
|
||||
@@ -508,6 +511,7 @@ fn dynamic_function_basic_arity() {
|
||||
#[test]
|
||||
fn dynamic_function_called_without_new() {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
let expected = run_source(&mut vm, "function f(a,b){return a*b} f(6,7)");
|
||||
let result = run_source(&mut vm, "Function('a','b','return a*b')(6,7)");
|
||||
assert_eq!(result, expected);
|
||||
@@ -516,12 +520,14 @@ fn dynamic_function_called_without_new() {
|
||||
#[test]
|
||||
fn dynamic_function_empty_body_returns_undefined() {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
assert!(run_source(&mut vm, "Function()()").is_undefined());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_function_syntax_error_throws_syntax_error() {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
let result = run_source(&mut vm, "try{new Function('return {{')}catch(e){e.name}");
|
||||
assert!(result.is_string());
|
||||
assert_eq!(vm.lookup_str(result).as_deref(), Some("SyntaxError"));
|
||||
@@ -530,6 +536,7 @@ fn dynamic_function_syntax_error_throws_syntax_error() {
|
||||
#[test]
|
||||
fn dynamic_function_nested_closure_renumbering() {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
// 匿名函数体声明嵌套函数 g,返回值是引用 g 的闭包:验证子树 flat_id 重编号
|
||||
// 与 CREATE_CLOSURE imm16 重写后嵌套调用仍指向正确的子模块。
|
||||
let expected = run_source(
|
||||
@@ -546,6 +553,7 @@ fn dynamic_function_nested_closure_renumbering() {
|
||||
#[test]
|
||||
fn dynamic_function_multiple_in_one_run() {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
// 同一 run 内连续创建多个动态函数:验证 base 偏移累计正确。
|
||||
let expected = run_source(
|
||||
&mut vm,
|
||||
@@ -561,6 +569,7 @@ fn dynamic_function_multiple_in_one_run() {
|
||||
#[test]
|
||||
fn dynamic_function_name_and_length() {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
let name = run_source(&mut vm, "var f=new Function('a','b','return a'); f.name");
|
||||
assert!(name.is_string());
|
||||
assert_eq!(vm.lookup_str(name).as_deref(), Some("anonymous"));
|
||||
@@ -571,6 +580,7 @@ fn dynamic_function_name_and_length() {
|
||||
#[test]
|
||||
fn dynamic_function_comma_split_params_count() {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
// 单个实参 "a,b,c" 拼接解析为 3 个形参,length 应为解析后的形参数。
|
||||
let result = run_source(&mut vm, "new Function('a,b,c','null').length");
|
||||
assert_eq!(result, JsValue::int(3));
|
||||
@@ -579,6 +589,7 @@ fn dynamic_function_comma_split_params_count() {
|
||||
#[test]
|
||||
fn dynamic_function_name_and_length_attributes() {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
// length/name 为不可写、不可枚举、可配置的数据属性。
|
||||
let attrs = run_source(
|
||||
&mut vm,
|
||||
@@ -595,6 +606,7 @@ fn dynamic_function_name_and_length_attributes() {
|
||||
#[test]
|
||||
fn dynamic_function_rethrows_to_string_exception() {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(DefaultCompilerService));
|
||||
// 形参 ToString 回调抛出的原始值须原样传播,而非包成 TypeError。
|
||||
let result = run_source(&mut vm, "try{new Function({toString:function(){throw 7}})}catch(e){e}");
|
||||
assert_eq!(result, JsValue::int(7));
|
||||
|
||||
Reference in New Issue
Block a user