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
Generated
+20 -18
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1225,7 +1225,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1234,7 +1234,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1246,6 +1246,7 @@ dependencies = [
"oxide_log",
"oxide_parser",
"oxide_regalloc",
"oxide_runtime_api",
"rustc-hash",
"serde",
"serde_json",
@@ -1253,7 +1254,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1267,7 +1268,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1282,7 +1283,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1291,7 +1292,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"bumpalo",
"dashmap",
@@ -1305,7 +1306,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1317,7 +1318,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"tracing",
"tracing-appender",
@@ -1326,7 +1327,7 @@ dependencies = [
[[package]]
name = "oxide_parser"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1341,7 +1342,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1357,10 +1358,11 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"num-bigint",
"num-traits",
"oxide_bytecode",
"oxide_kernel",
"oxide_log",
"oxide_types",
@@ -1369,7 +1371,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1386,7 +1388,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"bumpalo",
"dashmap",
@@ -1399,7 +1401,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.8.68"
version = "0.8.69"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -22,7 +22,7 @@ members = [
]
[workspace.package]
version = "0.8.68"
version = "0.8.69"
edition = "2021"
license = "MIT"
rust-version = "1.80"
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -9,6 +10,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -9,6 +10,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_runtime_api::VmHost;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -10,6 +11,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -9,6 +10,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -9,6 +10,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
@@ -101,6 +103,7 @@ fn number_to_string_cache_slot_correctness() {
// 十六个槽位互异的值两轮各调一次:命中须键全等才返回,
// 钉住槽位碰撞不得错返。
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let mut seen = [false; 16];
for &d in &CACHE_TEST_VALUES {
let slot = (d.to_bits() as usize) & 15;
@@ -122,6 +125,7 @@ fn number_to_string_cache_survives_session_gc() {
// 填满十六槽后强制完整 session 收集:缓存串已登记为 GC 根,
// 收集后读回仍为原内容(漏根登记此测必挂)。
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
for &d in &CACHE_TEST_VALUES {
vm.number_to_string_cached(d);
}
@@ -144,6 +148,7 @@ fn number_to_string_cache_survives_session_gc() {
fn number_to_string_cache_identity() {
// 同值两次调用命中同槽:返回指针相等(命中复用 session 串)。
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let a = vm.number_to_string_cached(2.5);
let b = vm.number_to_string_cached(2.5);
assert!(a.is_string() && b.is_string());
+14 -1
View File
@@ -4,6 +4,7 @@ use std::sync::Arc;
use std::time::Instant;
use oxide_compiler::compiler::{compiled_module_hash, Compiler};
use oxide_compiler::DefaultCompilerService;
use oxide_kernel::kernel::{KernelConfig, KernelCore};
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
@@ -225,6 +226,7 @@ pub fn run_mem_vm_creation_leak(kernel: &Arc<KernelCore>) -> ExitCode {
for i in 0..ITERATIONS {
{
let mut vm = Vm::with_kernel_core(Arc::clone(kernel));
vm.set_compiler_service(Arc::new(DefaultCompilerService));
if let Err(e) = vm.run(&module) {
eprintln!("[vm_creation] iteration {i} run failed: {e}");
return ExitCode::FAILURE;
@@ -318,6 +320,7 @@ pub fn run_mem_dirty_rebuild_leak(kernel: &Arc<KernelCore>) -> ExitCode {
const SAMPLE_EVERY: usize = 10;
let mut vm = Vm::with_kernel_core(Arc::clone(kernel));
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let compiler = Compiler::new();
let mut sampler = LeakSampler::new(20);
@@ -421,6 +424,7 @@ pub fn run_mem_closure_dead_leak(kernel: &Arc<KernelCore>) -> ExitCode {
let mut series: Vec<(usize, f64)> = Vec::new();
for window in 0..WINDOWS {
let mut vm = Vm::with_kernel_core(Arc::clone(kernel));
vm.set_compiler_service(Arc::new(DefaultCompilerService));
for round in 0..ROUNDS_PER_WINDOW {
if let Err(e) = vm.run(create) {
eprintln!("[closure_dead_leak] window {window} round {round} create run failed: {e}");
@@ -479,6 +483,7 @@ pub fn run_mem_pool_high_water(kernel: &Arc<KernelCore>) -> ExitCode {
let light = compile_one(light_js);
let mut vm = Vm::with_kernel_core(Arc::clone(kernel));
vm.set_compiler_service(Arc::new(DefaultCompilerService));
if let Err(e) = vm.run(&heavy) {
eprintln!("[pool_high_water] heavy run failed: {e}");
return ExitCode::FAILURE;
@@ -548,6 +553,7 @@ pub fn run_mem_object_churn_peak(kernel: &Arc<KernelCore>) -> ExitCode {
};
let mut vm = Vm::with_kernel_core(Arc::clone(kernel));
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let mut series: Vec<(usize, f64)> = Vec::new();
if let Some(kb) = read_vmrss_kb() {
series.push((0, kb as f64));
@@ -652,6 +658,7 @@ pub fn run_mem_kernel_lifetime() -> ExitCode {
let rss_pre = read_vmrss_kb().unwrap_or(0);
{
let mut vm = Vm::with_kernel_core(Arc::clone(&kernel));
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = match vm.run(&js_module) {
Ok(v) => v,
Err(e) => {
@@ -769,7 +776,12 @@ pub fn run_mem_startup_breakdown() -> ExitCode {
let t1 = Instant::now();
// 池构造:同步预热 min_size 个 Vm,预热成本计入本段。
let pool = VmPool::new(Arc::clone(&kernel), config.min_pool_size, config.max_pool_size);
let pool = VmPool::new(
Arc::clone(&kernel),
Arc::new(DefaultCompilerService),
config.min_pool_size,
config.max_pool_size,
);
let t2 = Instant::now();
// 首次 spawn:命中预热池,近零成本。
@@ -904,6 +916,7 @@ mod tests {
fn session_accounting_zero_after_full_reset() {
let kernel = KernelCore::new(KernelConfig::standard());
let mut vm = Vm::with_kernel_core(Arc::clone(&kernel));
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let allocator = Allocator::default();
let program = oxide_parser::parse(&allocator, "var o = { a: 1 }; o").expect("parse");
let hash = compiled_module_hash(&program);
+9 -1
View File
@@ -19,6 +19,7 @@ use oxide_cli::server::spawn;
use oxide_cli::server::watchdog;
use oxide_compiler::compiler::{compiled_module_hash, Compiler};
use oxide_compiler::compiler_error;
use oxide_compiler::DefaultCompilerService;
use oxide_kernel::kernel::{KernelConfig, KernelCore};
use oxide_kernel::shape_forge::ShapeForge;
use oxide_kernel::string_forge::PermInterner;
@@ -620,7 +621,12 @@ fn make_kernel(verbose: bool, quiet: bool) -> Arc<KernelCore> {
}
fn make_pool(kernel: &Arc<KernelCore>) -> Arc<VmPool> {
VmPool::new(Arc::clone(kernel), kernel.config.min_pool_size, kernel.config.max_pool_size)
VmPool::new(
Arc::clone(kernel),
Arc::new(DefaultCompilerService),
kernel.config.min_pool_size,
kernel.config.max_pool_size,
)
}
fn eval(
@@ -767,6 +773,8 @@ fn repl() -> ExitCode {
let kernel = make_kernel(false, false);
let mut vm = Vm::with_kernel_core(Arc::clone(&kernel));
// REPL 源经 eval 路径动态编译(含 `new Function` / eval 内置),注入真实编译服务。
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let mut input_buf = String::new();
loop {
+2 -1
View File
@@ -100,12 +100,13 @@ fn panic_payload_str(payload: &Box<dyn std::any::Any + Send>) -> String {
#[cfg(test)]
mod tests {
use super::*;
use oxide_compiler::DefaultCompilerService;
use oxide_kernel::kernel::KernelConfig;
/// 测试环境:minimal 内核加单 VM 池(与池既有测试同口径)。
fn test_env() -> (Arc<KernelCore>, Arc<VmPool>) {
let kernel = KernelCore::new(KernelConfig::minimal());
let pool = VmPool::new(Arc::clone(&kernel), 1, Some(2));
let pool = VmPool::new(Arc::clone(&kernel), Arc::new(DefaultCompilerService), 1, Some(2));
(kernel, pool)
}
+2 -1
View File
@@ -73,12 +73,13 @@ pub fn handle_forge_query(
mod tests {
use super::*;
use crate::server::eval;
use oxide_compiler::DefaultCompilerService;
use oxide_kernel::kernel::KernelConfig;
/// 测试环境:minimal 内核加单 VM 池(与 eval 测试同口径)。
fn test_env() -> (Arc<KernelCore>, Arc<VmPool>) {
let kernel = KernelCore::new(KernelConfig::minimal());
let pool = VmPool::new(Arc::clone(&kernel), 1, Some(2));
let pool = VmPool::new(Arc::clone(&kernel), Arc::new(DefaultCompilerService), 1, Some(2));
(kernel, pool)
}
+9 -1
View File
@@ -13,6 +13,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{mpsc, Arc, Mutex};
use std::thread::JoinHandle;
use oxide_compiler::DefaultCompilerService;
use oxide_kernel::kernel::KernelCore;
use oxide_vm::vm_pool::{PoolCounters, VmPool};
@@ -111,7 +112,14 @@ pub fn spawn_workers(
let counters = Arc::clone(&counters);
handles.push(std::thread::spawn(move || {
// 池在本线程上创建,永不跨线程移动(Vm 不是 Send)。
let pool = VmPool::with_counters(Arc::clone(&kernel), min_pool_size, max_pool_size, counters);
// 编译服务随池构造注入:worker 池内 Vm 的 eval 动态编译面由此供给。
let pool = VmPool::with_counters(
Arc::clone(&kernel),
Arc::new(DefaultCompilerService),
min_pool_size,
max_pool_size,
counters,
);
worker_loop(rx, kernel, pool);
}));
senders.push(tx);
+1
View File
@@ -9,6 +9,7 @@ description = "OxideJS - AST to bytecode compiler"
[dependencies]
oxide_bytecode = { path = "../oxide_bytecode" }
oxide_parser = { path = "../oxide_parser" }
oxide_runtime_api = { path = "../oxide_runtime_api" }
oxide_log = { path = "../oxide_log" }
oxide_dce = { path = "../oxide_dce" }
oxide_cfg = { path = "../oxide_cfg" }
@@ -0,0 +1,39 @@
//! 默认编译服务:串联 `oxide_parser::parse` 与 `Compiler::compile`,
//! parse 错误映射与 `Allocator` 构造的唯一实现源。
use oxide_bytecode::module::CompiledModule;
use oxide_parser::Program;
use oxide_runtime_api::CompilerService;
use crate::compiler::Compiler;
/// 无状态默认编译服务:两方法均无内部可变状态,
/// `Arc<dyn CompilerService>` 可跨线程共享。
pub struct DefaultCompilerService;
impl CompilerService for DefaultCompilerService {
fn compile_script(&self, source: &str) -> Result<CompiledModule, String> {
// 局部分配器:AST 节点经 oxc bump 分配器分配,编译完成即弃。
let allocator = oxide_parser::Allocator::default();
let program = parse_program(&allocator, source)?;
// 动态路径源契约:调用方以源码域转义(`string_forge::source_escape`)
// 形态传源,故置 source_encoded。
Compiler::new().with_source_encoded(true).compile(&program)
}
fn compile_eval_script(&self, source: &str) -> Result<CompiledModule, String> {
let allocator = oxide_parser::Allocator::default();
let program = parse_program(&allocator, source)?;
// eval 脚本:顶层 var/function 声明落全局属性 configurable:true。
Compiler::new()
.with_eval_script(true)
.with_source_encoded(true)
.compile(&program)
}
}
/// 解析源码,解析错误各条消息换行拼接为单一错误串。
fn parse_program<'a>(allocator: &'a oxide_parser::Allocator, source: &'a str) -> Result<Program<'a>, String> {
oxide_parser::parse(allocator, source)
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))
}
+3
View File
@@ -6,5 +6,8 @@
pub mod compiler;
pub mod compiler_log;
pub mod compiler_service;
pub mod flatten;
pub mod hash;
pub use compiler_service::DefaultCompilerService;
+1
View File
@@ -7,6 +7,7 @@ rust-version.workspace = true
description = "OxideJS - VmHost trait: the abstract Vm interface builtins are written against"
[dependencies]
oxide_bytecode = { path = "../oxide_bytecode" }
oxide_types = { path = "../oxide_types" }
num-bigint = "0.4"
num-traits = "0.2"
@@ -0,0 +1,22 @@
//! 动态编译服务接口:`Vm` 经它把源码编译为 `CompiledModule`,
//! 解耦 `oxide_vm` 对 `oxide_compiler` / `oxide_parser` 的直接依赖。
use oxide_bytecode::module::CompiledModule;
/// 动态编译服务:`Vm` 经它把源码编译为 `CompiledModule`。
///
/// 无状态实现 `Send + Sync`,`Arc<dyn CompilerService>` 可跨线程共享
/// (worker 调度、多 realm 场景)。trait 对象安全,`Vm` 以
/// `Arc<dyn CompilerService>` 持服务句柄。
pub trait CompilerService: Send + Sync {
/// 编译普通脚本(source_encoded 形态),返回完整模块树
/// (根 flat_id=0 + 子模块)。
///
/// 供 `Function` 构造器(wrap 源码)与 `$262.evalScript`(普通脚本)。
fn compile_script(&self, source: &str) -> Result<CompiledModule, String>;
/// 编译 eval 脚本(source_encoded + eval_script 形态),返回完整模块树。
///
/// 顶层 var/function 声明落全局属性 configurable:true。
fn compile_eval_script(&self, source: &str) -> Result<CompiledModule, String>;
}
+3
View File
@@ -12,6 +12,8 @@
/// ECMAScript 强转函数族。
mod coercion;
/// 动态编译服务接口(`Vm` 经它解耦对编译器的直接依赖)。
pub mod compiler_service;
/// builtins 依赖的 `VmHost` 能力面。
mod host;
/// builtin native 函数的三态返回值。
@@ -26,5 +28,6 @@ pub use coercion::{
to_string_full, to_string_value_full, to_uint32, to_units_full, well_known_symbol_name, write_number_into,
ToPrimitiveHint,
};
pub use compiler_service::CompilerService;
pub use host::VmHost;
pub use native_result::NativeResult;
+3
View File
@@ -11,6 +11,7 @@ use crate::judge::{
};
use crate::meta::{parse_meta, strip_meta, TestMeta};
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_kernel::kernel::{KernelConfig, KernelCore};
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -171,6 +172,8 @@ fn run_test_inner(
};
let mut vm = Vm::with_kernel_core(Arc::clone(kernel));
// test262 语料含 eval / Function 动态编译面,注入真实编译服务。
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 监督者注入现场文件路径时开启 VM 的 last-pc 定频写(未注入时零开销)。
if let Some(watch) = pc_watch_path() {
vm.set_pc_watch(Some(watch.clone()));
+2 -2
View File
@@ -13,8 +13,6 @@ oxide_runtime_api = { path = "../oxide_runtime_api" }
oxide_builtins = { path = "../oxide_builtins" }
oxide_types = { path = "../oxide_types" }
oxide_log = { path = "../oxide_log" }
oxide_compiler = { path = "../oxide_compiler" }
oxide_parser = { path = "../oxide_parser" }
bumpalo.workspace = true
fast-float.workspace = true
ryu.workspace = true
@@ -34,6 +32,8 @@ proptest.workspace = true
regress.workspace = true
oxide_ir = { path = "../oxide_ir" }
oxide_emit = { path = "../oxide_emit" }
oxide_compiler = { path = "../oxide_compiler" }
oxide_parser = { path = "../oxide_parser" }
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
+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` 后无法再给稳定 `&`):调用方
+40 -21
View File
@@ -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");
+6
View File
@@ -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),
+12
View File
@@ -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));
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -27,6 +29,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse ok");
let module = Compiler::new().compile(&program).expect("compile ok");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let val = vm.run(&Arc::new(module)).expect("run ok");
if val.is_string() {
vm.lookup_str(val).unwrap_or_default()
+3
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -16,6 +17,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {}", e),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{}", result),
Err(e) => format!("vm error: {}", e),
@@ -33,6 +35,7 @@ fn eval_val(source: &str) -> (Vm, Result<JsValue, String>) {
Err(e) => return (Vm::new(), Err(format!("compile error: {}", e))),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module));
(vm, result)
}
+2
View File
@@ -4,6 +4,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::promise::promise_settled_value;
use oxide_vm::vm::Vm;
@@ -20,6 +21,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format_value(&vm, result),
Err(e) => format!("vm error: {e}"),
@@ -8,6 +8,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -16,6 +17,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -10,6 +10,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -19,6 +20,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -9,6 +9,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -17,6 +18,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -8,6 +8,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -16,6 +17,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -8,6 +8,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -22,6 +23,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -2,6 +2,7 @@ use std::sync::Arc;
use oxide_builtins::array::{array_constructor, array_push};
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -10,6 +11,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
@@ -40,6 +42,7 @@ fn array_push_returns_length() {
#[test]
fn array_push_returns_true_length_beyond_31() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.set_reg(1, oxide_types::value::JsValue::int(31));
let array = array_constructor(&mut vm, &[0, 1]).unwrap();
vm.set_reg(0, array);
@@ -2,13 +2,16 @@ use std::sync::Arc;
use oxide_builtins::error;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::mem::P;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
fn make_vm() -> Vm {
Vm::new()
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm
}
fn eval(source: &str) -> Result<JsValue, String> {
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -249,6 +250,7 @@ fn function_apply_non_callable_this_throws_catchable_type_error() {
// 不可调用 this(实例原型链含函数)经 apply 转发:抛可捕获 TypeError,
// 不得绕过 try/catch 成为引擎级错误。
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let name = eval(
&mut vm,
"function FACTORY(){} FACTORY.prototype = Function(); var o = new FACTORY(); try { o.apply(); } catch (e) { e.name }",
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -10,6 +11,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -18,6 +20,7 @@ fn eval_with_vm(source: &str) -> (Vm, Result<JsValue, String>) {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module));
(vm, result)
}
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -9,6 +10,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -18,6 +19,7 @@ fn to_str(vm: &Vm, val: JsValue) -> String {
#[test]
fn to_base64_rfc4648_vectors() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -43,6 +45,7 @@ fn to_base64_rfc4648_vectors() {
#[test]
fn to_base64_alphabet_and_omit_padding() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -65,6 +68,7 @@ fn to_base64_alphabet_and_omit_padding() {
#[test]
fn to_base64_option_coercion_and_receiver() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -90,6 +94,7 @@ fn to_base64_option_coercion_and_receiver() {
#[test]
fn to_hex_and_receiver_validation() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -110,6 +115,7 @@ fn to_hex_and_receiver_validation() {
#[test]
fn set_from_base64_results_and_target_size() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -151,6 +157,7 @@ fn set_from_base64_results_and_target_size() {
#[test]
fn set_from_base64_last_chunk_handling() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -190,6 +197,7 @@ fn set_from_base64_last_chunk_handling() {
#[test]
fn set_from_base64_subarray_and_option_order() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -234,6 +242,7 @@ fn set_from_base64_subarray_and_option_order() {
#[test]
fn set_from_base64_writes_up_to_error() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -251,6 +260,7 @@ fn set_from_base64_writes_up_to_error() {
#[test]
fn set_from_hex_results_and_odd_length() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -285,6 +295,7 @@ fn set_from_hex_results_and_odd_length() {
#[test]
fn from_base64_static() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -330,6 +341,7 @@ fn from_base64_static() {
#[test]
fn from_hex_static() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -353,6 +365,7 @@ fn from_hex_static() {
#[test]
fn typed_array_prototype_to_string_chain() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -378,6 +391,7 @@ fn typed_array_prototype_to_string_chain() {
#[test]
fn clamped_inherits_base64_methods_but_rejects() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -401,6 +415,7 @@ fn clamped_inherits_base64_methods_but_rejects() {
#[test]
fn base64_method_metadata() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
r#"(function() {
@@ -9,6 +9,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -23,6 +24,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
+5
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_runtime_api as coercion;
use oxide_types::value::JsValue;
@@ -17,6 +18,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -88,6 +90,7 @@ fn test_strict_equality_null_undefined() {
#[test]
fn test_to_int32_and_to_uint32() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let string_three = vm.new_string("3");
let string_bad = vm.new_string("x");
@@ -151,6 +154,7 @@ fn to_primitive_symbol_hint_invoked() {
#[test]
fn to_number_full_symbol_throws() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let sym = JsValue::symbol(0);
let result = coercion::to_number_full(sym, &mut vm);
assert!(result.is_err(), "to_number_full(Symbol) must error, got {result:?}");
@@ -323,6 +327,7 @@ fn eval_ty(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -7,6 +7,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -21,6 +22,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -12,6 +12,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -20,6 +21,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -9,6 +10,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
@@ -10,6 +10,7 @@ use std::sync::Arc;
use oxide_bytecode::module::CompiledModule;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -25,6 +26,7 @@ fn eval_js(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("parse: {e:?}"))?;
let module = Arc::new(Compiler::new().compile(&program).map_err(|e| format!("compile: {e}"))?);
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let val = vm.run(&module)?;
Ok((vm, val))
}
@@ -15,6 +15,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -23,6 +24,7 @@ fn eval_truthy(source: &str) {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
assert!(result.is_bool() && result.as_bool(), "expected true, got: {result:?}\nsource: {source}");
}
@@ -6,6 +6,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -14,6 +15,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -23,6 +25,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
vm.lookup_str(result).unwrap_or_else(|| format!("{result}"))
}
@@ -4,6 +4,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -12,6 +13,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -32,6 +34,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
vm.lookup_str(result).unwrap_or_else(|| format!("{result}"))
}
+26
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -21,6 +22,7 @@ fn assert_num(result: JsValue, expected: f64) {
#[test]
fn eval_string_expression_completion_value() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 脚本模式完成值保留:`eval('1+2')` 返回 3(档 2 起,不再被函数模式 body wrap 吞掉)。
let result = eval(&mut vm, "eval('1+2')").unwrap();
assert_num(result, 3.0);
@@ -29,6 +31,7 @@ fn eval_string_expression_completion_value() {
#[test]
fn eval_string_number_completion_value() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 脚本模式完成值保留:`eval('42')` 返回 42。
let result = eval(&mut vm, "eval('42')").unwrap();
assert_num(result, 42.0);
@@ -37,6 +40,7 @@ fn eval_string_number_completion_value() {
#[test]
fn eval_string_var_declaration_inside() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 档 2:eval 内 var 声明落全局对象,完成值保留为末表达式值。
let result = eval(&mut vm, "eval('var y = 1; y')").unwrap();
assert_num(result, 1.0);
@@ -47,6 +51,7 @@ fn eval_string_var_declaration_inside() {
#[test]
fn eval_non_string_returns_as_is() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(&mut vm, "eval(123)").unwrap();
assert_num(result, 123.0);
}
@@ -54,6 +59,7 @@ fn eval_non_string_returns_as_is() {
#[test]
fn eval_object_identity() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 非字符串实参原样返回:同一对象引用。
let result = eval(&mut vm, "var o = {}; eval(o) === o").unwrap();
assert_eq!(result, JsValue::bool(true));
@@ -62,6 +68,7 @@ fn eval_object_identity() {
#[test]
fn eval_new_string_not_tostring() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// new String 是非字符串对象:不 ToString,原样返回同一对象(非原始值)。
let result = eval(&mut vm, "var s = new String('1+1'); eval(s) === s").unwrap();
assert_eq!(result, JsValue::bool(true));
@@ -70,6 +77,7 @@ fn eval_new_string_not_tostring() {
#[test]
fn eval_throw_primitive_rethrown() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// eval 内 throw 1 重抛原始值 1,可被外层 catch 捕获(非 Error 包装)。
let result = eval(&mut vm, "try { eval('throw 1') } catch(e) { e }").unwrap();
assert_num(result, 1.0);
@@ -78,6 +86,7 @@ fn eval_throw_primitive_rethrown() {
#[test]
fn eval_syntax_error_throws() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 换行分隔的 `x` 与 `++` 为非法语法,eval 抛 SyntaxError。
let result = eval(&mut vm, "try { eval('x\\u000A++') } catch(e) { e.name }").unwrap();
let rendered = vm.lookup_str(result).unwrap_or_default();
@@ -87,6 +96,7 @@ fn eval_syntax_error_throws() {
#[test]
fn eval_length_is_one() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(&mut vm, "eval.length").unwrap();
assert_num(result, 1.0);
}
@@ -94,6 +104,7 @@ fn eval_length_is_one() {
#[test]
fn eval_name_is_eval() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(&mut vm, "eval.name").unwrap();
let rendered = vm.lookup_str(result).unwrap_or_default();
assert_eq!(rendered, "eval");
@@ -102,6 +113,7 @@ fn eval_name_is_eval() {
#[test]
fn eval_global_descriptor() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 全局 eval 属性描述符:{writable:true, enumerable:false, configurable:true}。
let result = eval(
&mut vm,
@@ -115,6 +127,7 @@ fn eval_global_descriptor() {
#[test]
fn eval_new_throws_type_error() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// eval 不是构造器:new eval() 抛 TypeError。
let result = eval(&mut vm, "try { new eval() } catch(e) { e.name }").unwrap();
let rendered = vm.lookup_str(result).unwrap_or_default();
@@ -124,6 +137,7 @@ fn eval_new_throws_type_error() {
#[test]
fn eval_no_arg_and_undefined_are_undefined() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(&mut vm, "eval() === undefined && eval(undefined) === undefined").unwrap();
assert_eq!(result, JsValue::bool(true));
}
@@ -131,6 +145,7 @@ fn eval_no_arg_and_undefined_are_undefined() {
#[test]
fn eval_typeof_is_function() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(&mut vm, "typeof eval").unwrap();
let rendered = vm.lookup_str(result).unwrap_or_default();
assert_eq!(rendered, "function");
@@ -139,6 +154,7 @@ fn eval_typeof_is_function() {
#[test]
fn eval_survives_full_reset_rebuild() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 仅 global 脏:full_reset 走 bind_global_functions 重建路径,eval 须保留。
let g_ptr = vm.session().global_object().as_ptr() as *mut JsObject;
unsafe { (&mut *g_ptr).bump_generation() };
@@ -157,6 +173,7 @@ fn eval_survives_full_reset_rebuild() {
#[test]
fn eval_script_var_lands_on_global() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 间接 eval 脚本模式:var 声明落全局对象,外层 this 可见。
let result = eval(&mut vm, "(0,eval)('var q = 9'); this.q === 9").unwrap();
assert_eq!(result, JsValue::bool(true));
@@ -165,6 +182,7 @@ fn eval_script_var_lands_on_global() {
#[test]
fn eval_script_function_decl_lands_on_global() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 脚本模式:顶层函数声明落全局对象。
let result = eval(&mut vm, "(0,eval)('function f(){}'); typeof f === 'function'").unwrap();
assert_eq!(result, JsValue::bool(true));
@@ -173,6 +191,7 @@ fn eval_script_function_decl_lands_on_global() {
#[test]
fn eval_script_let_is_isolated() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// let/const 词法隔离:不落全局,不泄漏到外层。
let result = eval(&mut vm, "(0,eval)('let z = 1'); typeof z === 'undefined'").unwrap();
assert_eq!(result, JsValue::bool(true));
@@ -184,6 +203,7 @@ fn eval_script_let_is_isolated() {
#[test]
fn eval_script_this_is_global() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 脚本模式:this 与 globalThis 恒等(inline 调用 receiver 传 global)。
let result = eval(&mut vm, "(0,eval)('this === globalThis')").unwrap();
assert_eq!(result, JsValue::bool(true));
@@ -192,6 +212,7 @@ fn eval_script_this_is_global() {
#[test]
fn eval_nested_eval() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 嵌套 eval:内层完成值透传为外层脚本值。
let result = eval(&mut vm, "eval(\"eval('1+1')\")").unwrap();
assert_num(result, 2.0);
@@ -200,6 +221,7 @@ fn eval_nested_eval() {
#[test]
fn eval_script_completion_value() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 间接 eval 脚本模式完成值:var 初始化 + 表达式。
let result = eval(&mut vm, "(0,eval)('var a = 1; a')").unwrap();
assert_num(result, 1.0);
@@ -210,6 +232,7 @@ fn eval_script_completion_value() {
#[test]
fn eval_script_empty_and_decl_only() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 空脚本 / 纯声明脚本:完成值为 undefined。
let result = eval(&mut vm, "eval('') === undefined").unwrap();
assert_eq!(result, JsValue::bool(true));
@@ -220,6 +243,7 @@ fn eval_script_empty_and_decl_only() {
#[test]
fn eval_script_throw_still_rethrows() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 脚本模式异常路径回归:eval 内 throw 原始值,外层 catch 捕获同一值。
let result = eval(&mut vm, "try { (0,eval)('throw 5') } catch(e) { e }").unwrap();
assert_num(result, 5.0);
@@ -228,6 +252,7 @@ fn eval_script_throw_still_rethrows() {
#[test]
fn compound_statement_completion_values() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// 混合形完成值:switch 命中 case 链最后非空值、无命中 undefined;空命中不沿用前值。
let result = eval(&mut vm, "7; switch(1){case 1: 9}").unwrap();
assert_num(result, 9.0);
@@ -321,6 +346,7 @@ fn compound_statement_completion_values() {
#[test]
fn iteration_labeled_completion_values() {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
// do-while 循环体完成值 = 末次体值。
let result = eval(&mut vm, "7; do { 8 } while (false)").unwrap();
assert_num(result, 8.0);
@@ -8,6 +8,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -22,6 +23,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -7,6 +7,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -21,6 +22,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -7,6 +7,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -21,6 +22,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -7,6 +7,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -21,6 +22,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -10,6 +10,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -19,6 +20,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -5,6 +5,7 @@ use std::sync::Arc;
use oxide_bytecode::module::CompiledModule;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -18,6 +19,7 @@ fn eval(source: &str) -> JsValue {
fn run(module: &Arc<CompiledModule>) -> JsValue {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(module).expect("vm run failed")
}
@@ -26,6 +28,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse failed");
let module = Arc::new(Compiler::new().compile(&program).expect("compile failed"));
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let v = vm.run(&module).expect("vm run failed");
// 字符串结果须在同一 VM 上读:perm 串指向 VM 私有内核,VM drop 后指针悬垂。
if v.is_string() {
@@ -4,6 +4,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -18,6 +19,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => {
if result.is_string() {
@@ -8,6 +8,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -19,12 +20,14 @@ fn compile(source: &str) -> oxide_bytecode::module::CompiledModule {
fn run_truthy(source: &str) {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(compile(source))).expect("run");
assert!(result.is_bool() && result.as_bool(), "expected true, got: {result:?}\nsource: {source}");
}
fn run_string(source: &str) -> String {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(compile(source))).expect("run");
if result.is_undefined() {
return "undefined".to_string();
@@ -35,6 +38,7 @@ fn run_string(source: &str) -> String {
/// 求值并返回完成值字符串;运行期异常返回错误文本(供 TypeError 断言)。
fn eval(source: &str) -> String {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(compile(source))) {
Ok(result) => vm.lookup_str(result).unwrap_or_default(),
Err(e) => e,
@@ -9,6 +9,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -17,6 +18,7 @@ fn eval_truthy(source: &str) {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
assert!(result.is_bool() && result.as_bool(), "expected true, got: {result:?}\nsource: {source}");
}
@@ -8,6 +8,7 @@ use std::sync::Arc;
use oxide_bytecode::module::CompiledModule;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_emit::module::{ModuleSourceLoader, ResolvedModule};
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -17,6 +18,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -33,6 +35,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
vm.lookup_str(result).unwrap_or_else(|| format!("{result}"))
}
@@ -53,6 +56,7 @@ fn script_err_text(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Err(e) => e,
Ok(v) => panic!("expected uncaught error, got: {v:?}\nsource: {source}"),
@@ -68,6 +72,7 @@ fn err_then_script_str(decl: &str, probe: &str) -> String {
Arc::new(Compiler::new().compile(&program).expect("compile"))
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let err = vm.run(&compile(decl));
assert!(err.is_err(), "声明脚本应抛未捕获异常,实际: {err:?}\ndecl: {decl}");
let result = vm.run(&compile(probe)).expect("probe run");
@@ -181,6 +186,7 @@ fn module_fn_decl_undefined_keeps_global_constant() {
.compile_module(&program, "test.mjs", &mut NoopLoader)
.expect("compile module");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module)).expect("module run");
assert_eq!(
@@ -10,6 +10,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_emit::module::{ModuleSourceLoader, ResolvedModule};
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -19,6 +20,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -60,6 +62,7 @@ fn eval_module(source: &str) -> Result<JsValue, String> {
.compile_module(&program, "test.mjs", &mut Loader)
.map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -12,6 +12,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -21,6 +22,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -29,6 +31,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
vm.lookup_str(result).unwrap_or_else(|| format!("{result}"))
}
@@ -3,6 +3,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -12,6 +13,7 @@ fn eval_truthy(source: &str) {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
assert!(result.is_bool() && result.as_bool(), "expected true, got: {result:?}\nsource: {source}");
}
@@ -21,6 +23,7 @@ fn eval_string(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
vm.lookup_str(result).unwrap_or_default()
}
@@ -34,6 +37,7 @@ fn compile(source: &str) -> oxide_bytecode::module::CompiledModule {
/// 两阶段执行:phase1 → `reset()`(轻量重置,epoch 清空、session 保留)→ phase2。
fn eval_two_phases(phase1: &str, phase2: &str) -> bool {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(compile(phase1))).expect("run1");
vm.reset();
let result = vm.run(&Arc::new(compile(phase2))).expect("run2");
@@ -43,6 +47,7 @@ fn eval_two_phases(phase1: &str, phase2: &str) -> bool {
/// 两阶段 run-Err 变体:phase2 返回运行结果,供未捕获异常(TypeError 等)断言。
fn run_two_phases(phase1: &str, phase2: &str) -> Result<JsValue, String> {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(compile(phase1))).expect("run1");
vm.reset();
vm.run(&Arc::new(compile(phase2)))
@@ -69,6 +74,7 @@ fn phase2_err_text(phase1: &str, phase2: &str) -> String {
/// "true"/"false")。
fn phase2_err_then_probe(phase1: &str, phase2: &str, probe: &str) -> String {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(compile(phase1))).expect("run1");
vm.reset();
assert!(vm.run(&Arc::new(compile(phase2))).is_err(), "phase2 应抛未捕获异常: {phase2}");
@@ -576,6 +582,7 @@ fn strict_eval_var_init_on_nonwritable_user_property_throws() {
fn nonextensible_global_strict_var_prologue_throws_before_body() {
// 序言期抛:体副作用不执行(touched 未写入)。
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(compile("Object.preventExtensions(globalThis)")))
.expect("run1");
vm.reset();
@@ -6,6 +6,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -14,6 +15,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -34,6 +36,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
vm.lookup_str(result).unwrap_or_else(|| format!("{result}"))
}
+2
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -4,6 +4,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -12,6 +13,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
+4
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -28,6 +30,7 @@ fn object_create_and_read() {
let program = oxide_parser::parse(&allocator, source).expect("parse failed");
let module = Compiler::new().compile(&program).expect("compile failed");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let obj = vm.run(&Arc::new(module)).expect("vm run failed");
assert!(obj.is_object());
let obj_ref = unsafe { &*obj.as_js_object_ptr() };
@@ -273,6 +276,7 @@ fn object_literal_getter_setter_pair() {
fn eval_string(source: &str) -> String {
let allocator = Allocator::default();
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let program = oxide_parser::parse(&allocator, source).expect("parse failed");
let module = Compiler::new().compile(&program).expect("compile failed");
let result = vm.run(&Arc::new(module)).expect("vm run failed");
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -9,6 +10,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -9,6 +10,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -8,6 +8,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -16,6 +17,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -36,6 +38,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
vm.lookup_str(result).unwrap_or_else(|| format!("{result}"))
}
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_kernel::kernel::{KernelConfig, KernelCore};
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -16,6 +17,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -33,6 +35,7 @@ fn eval_with_kernel(source: &str, kernel: Arc<KernelCore>) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::with_kernel_core(kernel);
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -52,6 +55,7 @@ fn regression_rerun_clears_ic_cache() {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
assert_eq!(format!("{}", vm.run(&Arc::new(module)).unwrap()), "1");
let source2 = "var o = {a: 2}; o.a";
@@ -188,6 +192,7 @@ fn eval_shallow(source: &str, depth: usize) -> String {
let mut cfg = KernelConfig::minimal();
cfg.max_call_depth = depth;
let mut vm = Vm::with_kernel_core(KernelCore::new(cfg));
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(val) => {
if val.is_string() {
@@ -4,6 +4,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -13,6 +14,7 @@ fn eval(source: &str) -> JsValue {
let program = oxide_parser::parse(&allocator, source).expect("parse failed");
let module = Compiler::new().compile(&program).expect("compile failed");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module)).expect("vm run failed")
}
@@ -32,6 +34,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse failed");
let module = Compiler::new().compile(&program).expect("compile failed");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let v = vm.run(&Arc::new(module)).expect("vm run failed");
// 字符串结果须在同一 VM 上读:perm 串指向 VM 私有内核,VM drop 后指针悬垂。
if v.is_string() {
+2
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -9,6 +10,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -9,6 +9,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -24,6 +25,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => js_value_to_str(result),
Err(e) => format!("vm error: {e}"),
@@ -41,6 +43,7 @@ fn eval_json(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => js_value_to_str(result),
Err(e) => format!("vm error: {e}"),
@@ -6,6 +6,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -14,6 +15,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -34,6 +36,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run");
vm.lookup_str(result).unwrap_or_else(|| format!("{result}"))
}
@@ -9,6 +9,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -17,6 +18,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -7,6 +7,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -24,6 +26,7 @@ fn eval_str(source: &str) -> String {
let program = oxide_parser::parse(&allocator, source).expect("parse");
let module = Compiler::new().compile(&program).expect("compile");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let value = vm.run(&Arc::new(module)).expect("run");
assert!(value.is_string(), "expected string, got {value:?}");
// SAFETY: is_string 已确认值为字符串指针,内容在 VM 析构前拷贝为自有 String。
@@ -11,6 +11,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<oxide_types::value::JsValue, String> {
@@ -18,6 +19,7 @@ fn eval(source: &str) -> Result<oxide_types::value::JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("parse error: {}", e[0].message))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("compile error: {e}"))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
@@ -9,6 +9,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -23,6 +24,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
+4
View File
@@ -3,6 +3,7 @@ use std::sync::Arc;
use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
use oxide_ir::IRFunction;
@@ -21,6 +22,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {}", e),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{}", result),
Err(e) => format!("vm error: {}", e),
@@ -38,6 +40,7 @@ fn eval_val(source: &str) -> (Vm, Result<JsValue, String>) {
Err(e) => return (Vm::new(), Err(format!("compile error: {}", e))),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module));
(vm, result)
}
@@ -136,6 +139,7 @@ fn template_expression_reads_physical_register_above_127() {
let module = oxide_ir::lower::lower(&ir).expect("lower template IR");
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = vm.run(&Arc::new(module)).expect("run template IR");
assert_eq!(to_str(&vm, result), "value");
}
@@ -4,6 +4,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_kernel::kernel::{KernelConfig, KernelCore};
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
@@ -21,6 +22,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => vm.lookup_str(result).unwrap_or_else(|| format!("{result}")),
Err(e) => format!("vm error: {e}"),
@@ -104,6 +106,7 @@ fn gc_keeps_reachable_session_objects_alive() {
let mut config = KernelConfig::minimal();
config.set_session_gc_threshold(4096);
let mut vm = Vm::with_kernel_core(KernelCore::new(config));
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval_on(
&mut vm,
"(function () { \
@@ -7,6 +7,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -21,6 +22,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -6,6 +6,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
@@ -14,12 +15,14 @@ fn eval(source: &str) -> Result<JsValue, String> {
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
vm.run(&Arc::new(module))
}
/// 求值并取字符串结果内容(字符串值走 lookup_str,非字符串走 Display)。
fn eval_str(source: &str) -> Result<String, String> {
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
+2
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
+2
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
@@ -15,6 +16,7 @@ fn eval(source: &str) -> String {
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),