diff --git a/Cargo.lock b/Cargo.lock index 37eadc29..0582515a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.21.3" +version = "0.21.4" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.21.3" +version = "0.21.4" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.21.3" +version = "0.21.4" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.21.3" +version = "0.21.4" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.21.3" +version = "0.21.4" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.21.3" +version = "0.21.4" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1258,7 +1258,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.21.3" +version = "0.21.4" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1272,7 +1272,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.21.3" +version = "0.21.4" dependencies = [ "num-bigint", "oxc_allocator", @@ -1288,7 +1288,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.21.3" +version = "0.21.4" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1300,7 +1300,7 @@ dependencies = [ [[package]] name = "oxide_fold" -version = "0.21.3" +version = "0.21.4" dependencies = [ "num-bigint", "oxide_bytecode", @@ -1312,7 +1312,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.21.3" +version = "0.21.4" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.21.3" +version = "0.21.4" dependencies = [ "bumpalo", "dashmap", @@ -1335,7 +1335,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.21.3" +version = "0.21.4" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1347,7 +1347,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.21.3" +version = "0.21.4" dependencies = [ "tracing", "tracing-appender", @@ -1356,11 +1356,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.21.3" +version = "0.21.4" [[package]] name = "oxide_parser" -version = "0.21.3" +version = "0.21.4" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1375,7 +1375,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.21.3" +version = "0.21.4" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.21.3" +version = "0.21.4" dependencies = [ "num-bigint", "num-traits", @@ -1404,7 +1404,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.21.3" +version = "0.21.4" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.21.3" +version = "0.21.4" dependencies = [ "bumpalo", "dashmap", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.21.3" +version = "0.21.4" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index dfc15e09..e7d90613 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ members = [ ] [workspace.package] -version = "0.21.3" +version = "0.21.4" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index 12be6993..8b9bc8f1 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -1434,7 +1434,7 @@ pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResul /// /// # 副作用 /// - 在 epoch 分配一个描述符对象 -fn own_descriptor_of(vm: &mut H, obj: &JsObject, key_si: u32) -> Result, JsValue> { +pub fn own_descriptor_of(vm: &mut H, obj: &JsObject, key_si: u32) -> Result, JsValue> { // Proxy [[GetOwnProperty]]:getOwnPropertyDescriptor trap 以(target、P) // 调用;trap 缺失落穿 target 的自身描述符(target 自身为代理时经递归再 // 路由);trap 返 undefined 表示无属性(target 自身属性不可配置或 target diff --git a/crates/oxide_compiler/src/compiler_service.rs b/crates/oxide_compiler/src/compiler_service.rs index a9ccd4ae..eb342def 100644 --- a/crates/oxide_compiler/src/compiler_service.rs +++ b/crates/oxide_compiler/src/compiler_service.rs @@ -3,11 +3,11 @@ use oxide_bytecode::module::CompiledModule; use oxide_parser::Program; -use oxide_runtime_api::CompilerService; +use oxide_runtime_api::{CompilerService, ModuleSourceLoader}; use crate::compiler::Compiler; -/// 无状态默认编译服务:两方法均无内部可变状态, +/// 无状态默认编译服务:三方法均无内部可变状态, /// `Arc` 可跨线程共享。 pub struct DefaultCompilerService; @@ -37,6 +37,43 @@ impl CompilerService for DefaultCompilerService { .compile(&program) }) } + + fn compile_module( + &self, source: &str, module_path: &str, loader: &mut dyn ModuleSourceLoader, + ) -> Result { + // 模块源按磁盘文件读入(非 source_encoded 形态),故不设 source_encoded。 + oxide_parser::with_parse_allocator(|allocator| { + let program = parse_module_program(allocator, source)?; + // 适配 runtime_api 加载器到 emit 层加载器(依赖解析回调)。 + let mut emit_loader = EmitLoaderAdapter { inner: loader }; + Compiler::new().compile_module(&program, module_path, &mut emit_loader) + }) + } +} + +/// 按 ES module 文法解析源码,解析错误各条消息换行拼接为单一错误串。 +fn parse_module_program<'a>(allocator: &'a oxide_parser::Allocator, source: &'a str) -> Result, String> { + oxide_parser::parse_module(allocator, source) + .map_err(|errs| errs.into_iter().map(|e| e.message).collect::>().join("\n")) +} + +/// 把 `runtime_api` 的模块加载器适配为 `oxide_emit` 的加载器: +/// 依赖解析回调的桥接,种类恒为 JS(数据模块种类由 emit 层按需判定)。 +struct EmitLoaderAdapter<'a> { + inner: &'a mut dyn ModuleSourceLoader, +} + +impl oxide_emit::module::ModuleSourceLoader for EmitLoaderAdapter<'_> { + fn resolve( + &mut self, base_dir: &str, specifier: &str, _attributes: &[(&str, &str)], + ) -> Result { + let (source, path) = self.inner.resolve(base_dir, specifier)?; + Ok(oxide_emit::module::ResolvedModule { + source, + path, + kind: oxide_emit::module::ModuleKind::Js, + }) + } } /// 解析源码,解析错误各条消息换行拼接为单一错误串。 diff --git a/crates/oxide_runtime_api/src/compiler_service.rs b/crates/oxide_runtime_api/src/compiler_service.rs index f82e9720..84602ea5 100644 --- a/crates/oxide_runtime_api/src/compiler_service.rs +++ b/crates/oxide_runtime_api/src/compiler_service.rs @@ -3,6 +3,14 @@ use oxide_bytecode::module::CompiledModule; +/// 模块源码加载器:按 `base_dir` 与 `specifier` 定位依赖模块源码。 +/// +/// 供 `compile_module` 解析 ES module 依赖。返回 (源码, 规范路径)。 +pub trait ModuleSourceLoader { + /// 解析依赖模块:返回 (源码, 规范路径)。 + fn resolve(&mut self, base_dir: &str, specifier: &str) -> Result<(String, String), String>; +} + /// 动态编译服务:`Vm` 经它把源码编译为 `CompiledModule`。 /// /// 无状态实现 `Send + Sync`,`Arc` 可跨线程共享 @@ -19,4 +27,12 @@ pub trait CompilerService: Send + Sync { /// /// 顶层 var/function 声明落全局属性 configurable:true。 fn compile_eval_script(&self, source: &str) -> Result; + + /// 编译 ES module(含依赖加载),返回完整模块树。 + /// + /// `module_path` 为模块文件的规范路径(依赖解析基准 = 其父目录), + /// 依赖经 `loader` 解析。 + fn compile_module( + &self, source: &str, module_path: &str, loader: &mut dyn ModuleSourceLoader, + ) -> Result; } diff --git a/crates/oxide_runtime_api/src/lib.rs b/crates/oxide_runtime_api/src/lib.rs index a4372586..568cba1d 100644 --- a/crates/oxide_runtime_api/src/lib.rs +++ b/crates/oxide_runtime_api/src/lib.rs @@ -30,7 +30,7 @@ pub use coercion::{ to_string_for_string_constructor, 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 compiler_service::{CompilerService, ModuleSourceLoader}; pub use event_target::{EventTargetState, ListenerEntry}; pub use host::{ModuleEvalState, ProtoKind, ShapeNode, VmHost}; pub use native_result::NativeResult; diff --git a/crates/oxide_test262/src/main.rs b/crates/oxide_test262/src/main.rs index ca324fe6..3795367d 100644 --- a/crates/oxide_test262/src/main.rs +++ b/crates/oxide_test262/src/main.rs @@ -103,19 +103,31 @@ fn run_worker_window( let harness_sources = HARNESS.get_or_init(HarnessSources::new); let mut stats = RunStats::default(); let mut tests_since_kernel_reset = 0usize; + // test262 宿主约定:每测试以测试文件所在目录为工作目录(importValue 等 + // 按相对路径解析 fixture 的 API 依赖此约定)。相对路径以初始工作目录为 + // 基准转绝对:cwd 随测试切换,基准必须在循环前固定。 + let base_dir = std::env::current_dir().unwrap_or_default(); for i in start..end { let path_str = params.paths[i].display().to_string(); // 始终记录当前测试路径,使 panic hook 能标识 Rust panic。 // 诊断需要最后一行 stderr 的 OS 级崩溃时设置 OXIDE_TEST262_RUNNING_LOG=1。 CURRENT_TEST_PATH.with(|p| *p.borrow_mut() = path_str.clone()); + let test_path = if params.paths[i].is_absolute() { + params.paths[i].clone() + } else { + base_dir.join(¶ms.paths[i]) + }; + if let Some(dir) = test_path.parent() { + let _ = std::env::set_current_dir(dir); + } if params.log_running_tests { test262_debug!("running: {}", path_str); eprintln!(" [worker] running: {path_str}"); } let result = process_path( - ¶ms.paths[i], + &test_path, ¶ms.filter, params.no_skip, params.no_regalloc, diff --git a/crates/oxide_test262/src/worker.rs b/crates/oxide_test262/src/worker.rs index bd2a316d..88abdc8c 100644 --- a/crates/oxide_test262/src/worker.rs +++ b/crates/oxide_test262/src/worker.rs @@ -169,6 +169,10 @@ pub(crate) fn run_worker(config: &RunConfig) -> bool { .map(|n| n.min(512)) .unwrap_or(256); let mut tests_since_kernel_reset = 0usize; + // test262 宿主约定:每测试以测试文件所在目录为工作目录(importValue 等 + // 按相对路径解析 fixture 的 API 依赖此约定)。相对路径以初始工作目录为 + // 基准转绝对:cwd 随测试切换,基准必须在窗口循环前固定。 + let base_dir = std::env::current_dir().unwrap_or_default(); for msg in msg_rx { let window = match msg { @@ -194,9 +198,20 @@ pub(crate) fn run_worker(config: &RunConfig) -> bool { let path = PathBuf::from(path_str); let path_str = path.display().to_string(); CURRENT_TEST_PATH.with(|p| *p.borrow_mut() = path_str.clone()); + // test262 宿主约定:每测试以测试文件所在目录为工作目录(importValue + // 等按相对路径解析 fixture 的 API 依赖此约定)。相对路径以初始工作 + // 目录为基准转绝对:cwd 随测试切换,基准在窗口循环前固定。 + let test_path = if path.is_absolute() { + path + } else { + base_dir.join(&path) + }; + if let Some(dir) = test_path.parent() { + let _ = std::env::set_current_dir(dir); + } let result = process_path( - &path, + &test_path, &None, config.no_skip, config.no_regalloc, diff --git a/crates/oxide_vm/src/bindings/bind_shadow_realm.rs b/crates/oxide_vm/src/bindings/bind_shadow_realm.rs index a5ef3105..57827fd3 100644 --- a/crates/oxide_vm/src/bindings/bind_shadow_realm.rs +++ b/crates/oxide_vm/src/bindings/bind_shadow_realm.rs @@ -25,7 +25,7 @@ use oxide_types::value::JsValue; /// - 构造器与原型对象经 `Box::into_raw` 登记 world 释放登记表(session 收尾统一 /// 释放);全局 `ShadowRealm` 槽经 `bind_constructor!` 原位安装。 /// - prototype 上装 `constructor` 指回构造器、`@@toStringTag` 数据属性 -/// "ShadowRealm" 与 `evaluate` / `importValue` 方法(not supported 桩)。 +/// "ShadowRealm" 与 `evaluate` / `importValue` 方法(真实现)。 /// /// `realm_id` 经 `@@toStringTag` 符号键编码使用(well-known 符号键按 /// (realm 编号,局部下标)编码)。 @@ -121,14 +121,14 @@ pub fn bind_shadow_realm(env: BindEnv<'_>, global: &mut JsObject) { ); } - // evaluate / importValue 方法(not supported 桩,幂等守卫)。 + // evaluate / importValue 方法(真实现,幂等守卫)。 let si_evaluate = sf.intern("evaluate").0; if sh.lookup_position(proto.shape_id(), si_evaluate).is_none() { - apply_binding_table(env, proto, &[("evaluate", crate::shadow_realm::evaluate_stub as *const (), 1)]); + apply_binding_table(env, proto, &[("evaluate", crate::shadow_realm::evaluate as *const (), 1)]); } let si_import_value = sf.intern("importValue").0; if sh.lookup_position(proto.shape_id(), si_import_value).is_none() { - apply_binding_table(env, proto, &[("importValue", crate::shadow_realm::import_value_stub as *const (), 2)]); + apply_binding_table(env, proto, &[("importValue", crate::shadow_realm::import_value as *const (), 2)]); } } diff --git a/crates/oxide_vm/src/shadow_realm.rs b/crates/oxide_vm/src/shadow_realm.rs index 55b7eb92..7b5b5e1d 100644 --- a/crates/oxide_vm/src/shadow_realm.rs +++ b/crates/oxide_vm/src/shadow_realm.rs @@ -1,5 +1,6 @@ //! ShadowRealm native 实现:构造器(建影子 realm 与实例对象)与 `evaluate` / -//! `importValue` 的 not supported 桩。 +//! `importValue` 真实现(影子 realm 脚本执行、跨 realm 结果结算、WrappedFunction +//! 包装、模块文件加载)。 //! //! 关键约定: //! - 函数是 concrete `&mut Vm` 形态(同 `worker_constructor`),不挂 `VmHost` @@ -7,15 +8,25 @@ //! - 实例对象是 PLAIN 对象(`[[Prototype]]` → ShadowRealm.prototype),主 Vm //! 侧表 `shadow_realm_objects` 是 brand 校验依据(this 指针在侧表),不占 //! OBJ_TYPE 槽位。 -//! - `evaluate` / `importValue` 现为 not supported 桩(抛含 `not supported` -//! 子串的 TypeError),真实现是后续任务。 +//! - `evaluate` / `importValue` 真实现:在影子 realm 内编译执行(脚本 / 模块), +//! 结果经跨 realm 结算(原始值直返、函数包装为 WrappedFunction、非原始非函数 +//! 抛 TypeError、符号按 realm 重映射)后返回。 +//! - WrappedFunction 是调用方 realm 的函数对象(native 函数项),持有跨 realm +//! 逻辑边:原函数在属主 Vm 的保活表(`shadow_keepalive`,属主 Vm 的 GC 根) +//! 保活,包装方 Vm 经 `wrapped_function_state`(包装方 Vm 的 GC 根)反查。 +//! 包装方向双向:evaluate 结果包装影子函数(包装在主 realm),包装函数实参 +//! 包装主 realm 函数(包装在影子 realm),嵌套影子 realm 沿属主链上溯解析。 + +use std::sync::Arc; use oxide_builtins::error; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; -use oxide_runtime_api::{NativeResult, ProtoKind, VmHost}; -use oxide_types::object::JsObject; +use oxide_runtime_api::{to_integer_or_infinity, NativeResult, ProtoKind, VmHost}; +use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes}; +use oxide_types::private_key::WELL_KNOWN_SYMBOL_COUNT; use oxide_types::value::JsValue; +use crate::native::NativeFn; use crate::vm::Vm; /// `new ShadowRealm()`:建影子 realm(独立 Vm)与实例对象。 @@ -44,6 +55,8 @@ pub(crate) fn shadow_realm_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResul // 建影子 Vm:共享主 Vm 的 KernelCore 与动态编译服务。 let mut shadow_vm = Vm::with_kernel_core(vm.kernel_core.clone()); shadow_vm.set_compiler_service(vm.compiler.clone()); + // 属主链:影子 Vm 记录创建它的 Vm,跨 realm 结算沿链上溯解析远端 realm。 + shadow_vm.owner_vm = vm as *mut Vm; // 建实例对象:PLAIN 对象,[[Prototype]] → ShadowRealm.prototype。 let proto_val = shadow_realm_prototype_value(vm); @@ -58,22 +71,27 @@ pub(crate) fn shadow_realm_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResul NativeResult::Ok(JsValue::from_js_object(ptr)) } -/// `ShadowRealm.prototype.evaluate` 桩:按规范前置步骤校验后抛 not supported。 +/// `ShadowRealm.prototype.evaluate`:在影子 realm 内把 `source` 按普通脚本编译 +/// 执行,结果经跨 realm 结算后返回。 /// /// # 步骤 /// 1. brand 校验(`this` 须是 ShadowRealm 实例)。 -/// 2. 实参 `source` 非字符串抛 TypeError(规范只接受 String,不做 ToString)。 -/// 3. 编译 `source`(编译期 SyntaxError 原样透传;语法判定与 realm 无关, -/// 经调用方编译服务完成)。 -/// 4. 抛 not supported。 +/// 2. `source` 非字符串抛 TypeError(规范只接受 String,不做 ToString)。 +/// 3. 在影子 realm 内把 `source` 编译为普通脚本(编译期 SyntaxError 原样透传)。 +/// 4. 执行脚本(运行期异常包 TypeError)。 +/// 5. 结果结算:原始值直返、函数包装为 WrappedFunction、符号按 realm 重映射、 +/// 非原始非函数抛 TypeError。 /// /// # 边界 -/// - 桩消息含 `not supported` 子串(test262 判定器据此判能力缺失)。 -pub(crate) fn evaluate_stub(vm: &mut Vm, args: &[u8]) -> NativeResult { +/// - 编译期 SyntaxError 透传(规范把编译期异常暴露给调用方 realm)。 +/// - 运行期异常(含影子 realm 内的 TypeError/ReferenceError 与原始值抛出) +/// 在调用方 realm 包装为 TypeError。 +pub(crate) fn evaluate(vm: &mut Vm, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if let Err(e) = shadow_realm_brand_check(vm, this_val) { return NativeResult::Err(e); } + let realm_id = shadow_realm_id_of(vm, this_val.as_js_object_ptr()); // 实参 source(args[1]):非字符串抛 TypeError。 let source_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; @@ -87,48 +105,405 @@ pub(crate) fn evaluate_stub(vm: &mut Vm, args: &[u8]) -> NativeResult { } }; - // 编译期语法错误原样透传(SyntaxError)。 - if let Err(msg) = vm.compiler.compile_script(&source) { - return NativeResult::Err(error::create_syntax_error(vm, &msg)); - } + // 在影子 realm 内编译并执行:编译期 SyntaxError 原样透传,运行期异常包 TypeError。 + let raw_result: Result = { + let shadow_vm = &mut vm.shadow_realm_objects.get_mut(&realm_id).unwrap().1; + let script = match shadow_vm.create_plain_dynamic_script(&source) { + Ok(s) => s, + Err(msg) => return NativeResult::Err(error::create_syntax_error(vm, &msg)), + }; + let global = shadow_vm.global_object(); + let global_val = JsValue::from_js_object(global.as_ptr() as *mut JsObject); + shadow_vm.call_function_sync(script, global_val, &[]) + }; + let value = match raw_result { + Ok(v) => v, + Err(_) => { + return NativeResult::Err(error::create_type_error( + vm, + "Error evaluating code in ShadowRealm", + )) + } + }; - NativeResult::Err(error::create_type_error(vm, "ShadowRealm.prototype.evaluate is not supported")) + // 结果结算:GetWrappedValue(callerRealm, value)。 + match settle_value(vm, vm.realm_id() as u64, realm_id, value) { + Ok(v) => NativeResult::Ok(v), + Err(e) => NativeResult::Err(e), + } } -/// `ShadowRealm.prototype.importValue` 桩:按规范前置步骤校验后抛 not supported。 +/// `ShadowRealm.prototype.importValue`:在影子 realm 内按 `specifier` 解析并加载 +/// 模块文件,编译求值后取 `exportName` 的导出值,以 Promise 结算返回。 /// /// # 步骤 /// 1. brand 校验(`this` 须是 ShadowRealm 实例)。 -/// 2. `ToString(specifier)`(abrupt completion 原值传播)。 -/// 3. 实参 `exportName` 非字符串抛 TypeError(规范只接受 String,不做 ToString)。 -/// 4. 抛 not supported。 +/// 2. `ToString(specifier)`(abrupt completion 原值传播);`exportName` 须为 +/// 字符串,非字符串抛 TypeError。 +/// 3. 建 Promise 能力(调用方 realm)。 +/// 4. 在影子 realm 内编译并求值模块(语法错误 / 求值抛错 / 导出缺失均 reject +/// TypeError)。 +/// 5. 导出值结算(原始值直返、函数包装、符号重映射、对象 reject TypeError)。 +/// 6. 返回 Promise 本体。 /// /// # 边界 -/// - 桩消息含 `not supported` 子串(test262 判定器据此判能力缺失)。 -pub(crate) fn import_value_stub(vm: &mut Vm, args: &[u8]) -> NativeResult { +/// - 模块解析失败(语法错误)与求值抛错以 TypeError reject。 +/// - 导出值非原始非函数以 TypeError reject。 +pub(crate) fn import_value(vm: &mut Vm, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if let Err(e) = shadow_realm_brand_check(vm, this_val) { return NativeResult::Err(e); } + let realm_id = shadow_realm_id_of(vm, this_val.as_js_object_ptr()); - // ToString(specifier):abrupt completion 传播原异常(native 重入点原值已物化 - // 入异常通道)。 + // ToString(specifier):abrupt completion 传播原异常。 let specifier_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - if let Err(e) = oxide_runtime_api::to_string_full(specifier_val, vm) { - let exc = vm.exception_value.take().unwrap_or_else(|| error::create_from_text(vm, &e)); - return NativeResult::Err(exc); - } - - // 实参 exportName(args[2]):非字符串抛 TypeError。 + let specifier = match oxide_runtime_api::to_string_full(specifier_val, vm) { + Ok(s) => s, + Err(e) => { + let exc = vm.exception_value.take().unwrap_or_else(|| error::create_from_text(vm, &e)); + return NativeResult::Err(exc); + } + }; + // exportName 须为字符串(不走 ToString):非字符串抛 TypeError,不触发 + // 目标值的 toString 调用。 let export_name_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - if !export_name_val.is_string() { - return NativeResult::Err(error::create_type_error( - vm, - "ShadowRealm.prototype.importValue exportName must be a string", - )); + let export_name = match vm.lookup_str(export_name_val) { + Some(s) => s, + None => { + return NativeResult::Err(error::create_type_error( + vm, + "ShadowRealm.prototype.importValue exportName must be a string", + )) + } + }; + + // 建 Promise 能力(调用方 realm)。 + let (promise, _resolve, _reject) = vm.new_promise_capability(); + + // 在影子 realm 内加载并求值模块,取导出值。 + let result = (|| -> Result { + // 解析模块路径(以当前工作目录为基准,test262 runner 以测试目录为工作目录)。 + let current_dir = std::env::current_dir() + .map_err(|e| error::create_type_error(vm, &format!("cannot get current directory: {e}")))?; + let module_path = current_dir.join(&specifier); + let module_path = module_path + .canonicalize() + .map_err(|e| error::create_type_error(vm, &format!("cannot resolve module {specifier}: {e}")))?; + let module_path_str = module_path.to_string_lossy().to_string(); + let source = std::fs::read_to_string(&module_path) + .map_err(|e| error::create_type_error(vm, &format!("cannot read module {specifier}: {e}")))?; + + // 追加导出值提取语句(exportName 是模块作用域内的绑定名,追加语句可读)。 + let source_with_result = format!("{source}\nglobalThis.__oxide_import_result = {export_name};"); + + // 编译并求值模块(语法错误 / 求值抛错 → TypeError)。 + let mut loader = ImportValueLoader; + let module = { + let shadow_vm = &mut vm.shadow_realm_objects.get_mut(&realm_id).unwrap().1; + shadow_vm + .compiler + .compile_module(&source_with_result, &module_path_str, &mut loader) + } + .map_err(|msg| error::create_type_error(vm, &msg))?; + { + let shadow_vm = &mut vm.shadow_realm_objects.get_mut(&realm_id).unwrap().1; + shadow_vm + .run(&Arc::new(module)) + .map_err(|msg| error::create_type_error(vm, &msg))?; + } + + // 读导出值(追加语句在模块求值末执行,导出绑定已初始化)。 + let export_value = { + let shadow_vm = &mut vm.shadow_realm_objects.get_mut(&realm_id).unwrap().1; + let global = shadow_vm.global_object(); + let global_ptr = global.as_ptr() as *mut JsObject; + // SAFETY: global_ptr 是影子 realm 的 global 对象,存活。 + let global_obj = unsafe { &*global_ptr }; + let global_val = JsValue::from_js_object(global_ptr); + let result_si = shadow_vm.perm_intern("__oxide_import_result"); + shadow_vm.ordinary_get(global_obj, result_si, global_val) + } + .map_err(|msg| error::create_type_error(vm, &msg))?; + + // 导出值结算:GetWrappedValue(callerRealm, exportValue)。 + settle_value(vm, vm.realm_id() as u64, realm_id, export_value) + })(); + + // 结算 Promise。 + match result { + Ok(value) => { + let _ = vm.resolve_promise(promise, value); + } + Err(e) => { + let _ = vm.reject_promise(promise, e); + } + } + NativeResult::Ok(promise) +} + +/// WrappedFunction 的 native 函数项:结算 this 与实参、调用属主 realm 原函数、 +/// 结算返回值。 +/// +/// # 步骤 +/// 1. 从 callee 反查状态(原函数属主 realm 编号、保活表下标)。 +/// 2. this 与实参逐个结算进目标 realm(可调用对象包装为内层 WrappedFunction、 +/// 非可调用对象抛 TypeError)。 +/// 3. 调用原函数(经属主 Vm 路由,异常退出包 TypeError)。 +/// 4. 返回值结算进调用方 realm。 +fn wrapped_function_call(vm: &mut Vm, args: &[u8]) -> NativeResult { + let callee = vm.reg(254); + let callee_ptr = callee.as_js_object_ptr(); + let (target_realm_id, keepalive_index) = match vm.wrapped_function_state.get(&callee_ptr) { + Some(state) => *state, + None => { + return NativeResult::Err(error::create_type_error(vm, "invalid WrappedFunction")); + } + }; + let caller_realm_id = vm.realm_id() as u64; + + // this 结算:GetWrappedValue(targetRealm, thisArgument)。native 调用协议把 + // receiver 寄存器下标放在实参表首位(`args[0]`,即 253 槽)。 + let this_val = vm.reg(args[0]); + let settled_this = match settle_value(vm, target_realm_id, caller_realm_id, this_val) { + Ok(v) => v, + Err(e) => return NativeResult::Err(e), + }; + + // 实参逐个结算:GetWrappedValue(targetRealm, arg)。 + let arg_count = vm.native_arg_count(args); + let mut settled_args = Vec::with_capacity(arg_count); + for i in 0..arg_count { + let arg = vm.native_arg_at(args, i); + match settle_value(vm, target_realm_id, caller_realm_id, arg) { + Ok(v) => settled_args.push(v), + Err(e) => return NativeResult::Err(e), + } } - NativeResult::Err(error::create_type_error(vm, "ShadowRealm.prototype.importValue is not supported")) + // 调用原函数(经属主 Vm 路由,异常退出包 TypeError)。 + let result = { + let target_vm = resolve_vm(vm, target_realm_id); + let target_fn = target_vm.shadow_keepalive[keepalive_index]; + target_vm.call_function_sync(target_fn, settled_this, &settled_args) + }; + let value = match result { + Ok(v) => v, + Err(_) => { + return NativeResult::Err(error::create_type_error( + vm, + "Error calling wrapped function", + )) + } + }; + + // 返回值结算:GetWrappedValue(callerRealm, result)。 + match settle_value(vm, caller_realm_id, target_realm_id, value) { + Ok(v) => NativeResult::Ok(v), + Err(e) => NativeResult::Err(e), + } +} + +/// 按 realm 编号解析属主 Vm:自身、直接子影子 realm,或沿属主链上溯。 +/// +/// # 边界与前提 +/// - `realm_id` 从 `vm` 可达(自身、子影子 realm 或属主链祖先):状态表只登记 +/// 可达 realm 编号,前提恒成立。 +/// - 目标 Vm 与 `vm` 同线程且非当前执行中(调用方在 `vm` 的 native 调用内), +/// 返回的 `&mut` 安全。 +fn resolve_vm(vm: &mut Vm, realm_id: u64) -> &mut Vm { + if vm.realm_id() as u64 == realm_id { + return vm; + } + if let Some(entry) = vm.shadow_realm_objects.get_mut(&realm_id) { + return &mut entry.1; + } + // 沿属主链上溯(影子 Vm → 属主 Vm → … → 主 Vm)。 + let mut cur = vm.owner_vm; + while !cur.is_null() { + // SAFETY: 属主 Vm 与 vm 同线程存活且非当前执行中。 + let owner = unsafe { &mut *cur }; + if owner.realm_id() as u64 == realm_id { + return owner; + } + if let Some(entry) = owner.shadow_realm_objects.get_mut(&realm_id) { + return &mut entry.1; + } + cur = owner.owner_vm; + } + panic!("ShadowRealm: realm {realm_id} 不可从属主链解析") +} + +/// 跨 realm 值结算:GetWrappedValue 语义(包装在 `wrapper_realm_id` realm, +/// 原函数属主在 `target_realm_id` realm)。 +/// +/// # 边界 +/// - 可调用对象:包装为 `wrapper_realm_id` realm 的 WrappedFunction,原函数在 +/// `target_realm_id` realm 的 Vm 保活表保活。 +/// - 非可调用对象:抛 TypeError。 +/// - 符号:按 realm 重映射(well-known 同下标、注册表符号按 key 复用 / 新建、 +/// 普通符号按描述新建)。 +/// - 其余原始值:直返。 +fn settle_value( + vm: &mut Vm, wrapper_realm_id: u64, target_realm_id: u64, value: JsValue, +) -> Result { + if value.is_object() { + if oxide_builtins::iterator::is_callable(value) { + return settle_function(vm, wrapper_realm_id, target_realm_id, value); + } + return Err(error::create_type_error(vm, "ShadowRealm: cannot return non-primitive value")); + } + if value.is_symbol() { + return settle_symbol(vm, wrapper_realm_id, target_realm_id, value); + } + Ok(value) +} + +/// 函数结算:WrappedFunctionCreate(wrapper_realm, target)。 +/// +/// # 步骤 +/// 1. 目标侧:原函数入属主 Vm 保活表、读 name / length(getter 抛错包 TypeError)。 +/// 2. 包装侧:计算 name / length、建函数对象、登记状态表。 +fn settle_function( + vm: &mut Vm, wrapper_realm_id: u64, target_realm_id: u64, target_fn: JsValue, +) -> Result { + // 目标侧:保活原函数、读 name / length。 + let (keepalive_index, name_result, length_result, has_own_length) = { + let target_vm = resolve_vm(vm, target_realm_id); + let keepalive_index = target_vm.shadow_keepalive.len(); + target_vm.shadow_keepalive.push(target_fn); + let target_ptr = target_fn.as_js_object_ptr(); + // SAFETY: target_fn 是属主 Vm 的函数对象,存活。 + let target_obj = unsafe { &*target_ptr }; + let name_si = target_vm.perm_intern("name"); + let length_si = target_vm.perm_intern("length"); + // HasOwnProperty 代理感知:getOwnPropertyDescriptor trap 可抛(撤销代理 + // 或抛错 trap),抛错包 TypeError(规范:CopyNameAndLength abrupt 抛 TypeError)。 + let has_own_length = match oxide_builtins::object::own_descriptor_of(target_vm, target_obj, length_si) { + Ok(Some(_)) => true, + Ok(None) => false, + Err(_) => { + return Err(error::create_type_error( + vm, + "ShadowRealm: error checking function length", + )) + } + }; + let name_result = target_vm.ordinary_get(target_obj, name_si, target_fn); + let length_result = if has_own_length { + target_vm.ordinary_get(target_obj, length_si, target_fn) + } else { + Ok(JsValue::undefined()) + }; + (keepalive_index, name_result, length_result, has_own_length) + }; + + // getter 抛错包 TypeError(规范:WrappedFunctionCreate 从调用方 realm 抛 TypeError)。 + let target_name = match name_result { + Ok(v) => v, + Err(_) => { + return Err(error::create_type_error(vm, "ShadowRealm: error reading function name")) + } + }; + let target_length = match length_result { + Ok(v) => v, + Err(_) => { + return Err(error::create_type_error(vm, "ShadowRealm: error reading function length")) + } + }; + + // 计算包装后的 name / length。 + let wrapped_name = { + let wrapper_vm = resolve_vm(vm, wrapper_realm_id); + wrapper_vm.lookup_str(target_name).unwrap_or_default() + }; + let wrapped_length = compute_wrapped_length(target_length, has_own_length); + + // 包装侧:建 WrappedFunction。 + let wrapper_vm = resolve_vm(vm, wrapper_realm_id); + Ok(create_wrapped_function( + wrapper_vm, target_realm_id, keepalive_index, &wrapped_name, wrapped_length, + )) +} + +/// 符号结算:按 realm 重映射到包装 realm。 +/// +/// # 步骤 +/// 1. 目标侧:读符号描述与注册表 key。 +/// 2. 包装侧:well-known 按同下标、注册表符号按 key 复用 / 新建、 +/// 普通符号按描述新建(不入注册表)。 +fn settle_symbol( + vm: &mut Vm, wrapper_realm_id: u64, target_realm_id: u64, value: JsValue, +) -> Result { + let local_index = value.as_symbol_local_index(); + // 目标侧:读符号描述与注册表 key。 + let (description, registry_key) = { + let target_vm = resolve_vm(vm, target_realm_id); + (target_vm.symbol_description(local_index), target_vm.symbol_key_for_id(local_index)) + }; + + // 包装侧:建 / 复用。 + let wrapper_vm = resolve_vm(vm, wrapper_realm_id); + let wrapper_realm_id = wrapper_vm.realm_id(); + if local_index < WELL_KNOWN_SYMBOL_COUNT { + // well-known 符号:跨 realm 同下标。 + return Ok(JsValue::symbol_realm(wrapper_realm_id, local_index)); + } + if let Some(key) = registry_key { + // 注册表符号:包装 realm 已有则复用,否则新建并登记。 + if let Some(idx) = wrapper_vm.symbol_lookup_global(&key) { + return Ok(JsValue::symbol_realm(wrapper_realm_id, idx)); + } + let idx = wrapper_vm.symbol_intern(description); + wrapper_vm.symbol_register_global(key, idx); + return Ok(JsValue::symbol_realm(wrapper_realm_id, idx)); + } + // 普通符号:按描述新建(不入注册表)。 + let idx = wrapper_vm.symbol_intern(description); + Ok(JsValue::symbol_realm(wrapper_realm_id, idx)) +} + +/// 建 WrappedFunction:包装 realm 的函数对象(native 函数项),登记状态表。 +/// +/// # 副作用 +/// - 包装 realm 新建函数对象(session 分配);状态表登记一条(GC 根)。 +fn create_wrapped_function( + vm: &mut Vm, target_realm_id: u64, keepalive_index: usize, name: &str, length: JsValue, +) -> JsValue { + let native_fn: NativeFn = wrapped_function_call; + let fn_proto = vm.realm.session.borrow().builtin_world().fn_proto_val(); + let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); + func.set_function(true); + // SAFETY: wrapped_function_call 是 NativeFn 函数项。 + func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(native_fn as *const ()) })); + let ptr = vm.alloc_object(func); + // SAFETY: alloc_object 返回非空 session 对象指针;借出期间无别名。 + let obj = unsafe { &mut *ptr }; + let attrs = PropAttributes::new(false, false, true); + let length_si = vm.kernel_core.perm_interner().intern("length").0; + let name_si = vm.kernel_core.perm_interner().intern("name").0; + let _ = vm.define_data_property(obj, length_si, length, attrs); + let name_val = vm.new_string(name); + let _ = vm.define_data_property(obj, name_si, name_val, attrs); + vm.wrapped_function_state.insert(ptr, (target_realm_id, keepalive_index)); + JsValue::from_js_object(ptr) +} + +/// CopyNameAndLength 的 length 规则:无自身 length 归 0、非 Number 归 0、 +/// +Infinity 归 +Infinity、-Infinity / NaN 归 0、有限数归 max(ToIntegerOrInfinity, 0)。 +fn compute_wrapped_length(target_length: JsValue, has_own_length: bool) -> JsValue { + if !has_own_length || !target_length.is_number() { + return JsValue::float(0.0); + } + let n = oxide_runtime_api::to_number(target_length); + if n == f64::INFINITY { + return JsValue::float(f64::INFINITY); + } + if n == f64::NEG_INFINITY || n.is_nan() { + return JsValue::float(0.0); + } + let len = to_integer_or_infinity(target_length); + JsValue::float(len.max(0.0)) } /// brand 校验:`this` 须是对象且指针在主 Vm 侧表内(实例对象注册表)。 @@ -147,6 +522,15 @@ fn shadow_realm_brand_check(vm: &mut Vm, this_val: JsValue) -> Result<(), JsValu Ok(()) } +/// 据实例对象指针反查影子 realm 编号(brand 校验已保证存在)。 +fn shadow_realm_id_of(vm: &Vm, this_ptr: *mut JsObject) -> u64 { + vm.shadow_realm_objects + .iter() + .find(|(_, (obj, _))| obj.as_js_object_ptr() == this_ptr) + .map(|(id, _)| *id) + .unwrap() +} + /// 读全局 `ShadowRealm` 构造器的 `prototype` 属性(ShadowRealm.prototype)。 /// /// # 边界 @@ -180,3 +564,23 @@ fn shadow_realm_prototype_value(vm: &mut Vm) -> JsValue { object_proto } } + +/// importValue 的模块加载器:以 `base_dir` 为基准解析相对导入,读磁盘 fixture 源码。 +struct ImportValueLoader; + +impl oxide_runtime_api::ModuleSourceLoader for ImportValueLoader { + fn resolve(&mut self, base_dir: &str, specifier: &str) -> Result<(String, String), String> { + let base = std::path::Path::new(base_dir); + let full = if specifier.starts_with('/') { + std::path::PathBuf::from(specifier.trim_start_matches('/')) + } else { + base.join(specifier) + }; + let canonical = full + .canonicalize() + .map_err(|e| format!("cannot resolve module {specifier}: {e}"))?; + let content = + std::fs::read_to_string(&canonical).map_err(|e| format!("cannot read module {specifier}: {e}"))?; + Ok((content, canonical.to_string_lossy().to_string())) + } +} diff --git a/crates/oxide_vm/src/vm/dynamic.rs b/crates/oxide_vm/src/vm/dynamic.rs index 065e4c96..e713490e 100644 --- a/crates/oxide_vm/src/vm/dynamic.rs +++ b/crates/oxide_vm/src/vm/dynamic.rs @@ -6,14 +6,14 @@ use std::sync::{Arc, OnceLock}; use oxide_bytecode::module::CompiledModule; use oxide_bytecode::opcode::{self, OpCode}; -use oxide_runtime_api::CompilerService; +use oxide_runtime_api::{CompilerService, ModuleSourceLoader}; use oxide_types::object::PropAttributes; use oxide_types::value::JsValue; use super::Vm; use crate::vm_support::FunctionFlags; -/// 缺省 no-op 编译服务:两方法均返 `Err`(错误消息指向「未安装编译服务」)。 +/// 缺省 no-op 编译服务:三方法均返 `Err`(错误消息指向「未安装编译服务」)。 /// /// 供不需要动态编译的 `Vm` 实例(构造器缺省值);生产 entry points 与 /// 动态编译测试经 `set_compiler_service` 注入真实实现。 @@ -27,6 +27,12 @@ impl CompilerService for NoopCompilerService { fn compile_eval_script(&self, _source: &str) -> Result { Err("dynamic compilation unavailable: no compiler service installed".to_string()) } + + fn compile_module( + &self, _source: &str, _module_path: &str, _loader: &mut dyn ModuleSourceLoader, + ) -> Result { + Err("dynamic compilation unavailable: no compiler service installed".to_string()) + } } impl Vm { @@ -90,6 +96,13 @@ impl Vm { // 子树重编号 + 追加:base = 当前代际平表长度,DFS 前序压入,push 序即新 // flat_id。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。 let table = self.current_table_mut(); + // 空表(新 Vm / full_reset 后首个动态函数)补占位模块:函数对象落 index 0 + // 会被 sub_module_index()==0 守卫误判为无本体函数对象。 + if table.modules.is_empty() { + Arc::make_mut(&mut table.modules).insert(0, Arc::new(CompiledModule::new())); + table.immutables.insert(0, OnceLock::new()); + table.si_tables.insert(0, OnceLock::new()); + } let base = table.modules.len() as u32; let mut added = Vec::new(); rehome_subtree(&anonymous, base, &mut added); @@ -150,6 +163,13 @@ impl Vm { // 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0 // 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。 let table = self.current_table_mut(); + // 空表(新 Vm / full_reset 后首段动态脚本)补占位模块:首个动态脚本的根 + // 落 index 0 会被 sub_module_index()==0 守卫误判为无本体函数对象。 + if table.modules.is_empty() { + Arc::make_mut(&mut table.modules).insert(0, Arc::new(CompiledModule::new())); + table.immutables.insert(0, OnceLock::new()); + table.si_tables.insert(0, OnceLock::new()); + } let base = table.modules.len() as u32; let mut added = Vec::new(); rehome_subtree(&module, base + 1, &mut added); @@ -189,6 +209,13 @@ impl Vm { // 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0 // 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。 let table = self.current_table_mut(); + // 空表(新 Vm / full_reset 后首段动态脚本)补占位模块:首个动态脚本的根 + // 落 index 0 会被 sub_module_index()==0 守卫误判为无本体函数对象。 + if table.modules.is_empty() { + Arc::make_mut(&mut table.modules).insert(0, Arc::new(CompiledModule::new())); + table.immutables.insert(0, OnceLock::new()); + table.si_tables.insert(0, OnceLock::new()); + } let base = table.modules.len() as u32; let mut added = Vec::new(); rehome_subtree(&module, base + 1, &mut added); diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index b64bd6c8..7a14f84b 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -45,11 +45,13 @@ pub(crate) enum RootGroup { DisposeStack, EventTargets, ModuleEvalStates, + ShadowKeepalive, + WrappedFunctionState, } impl RootGroup { /// 根组总数(与变体数同源,绑定逐组计数数组的长度)。 - pub const COUNT: usize = 30; + pub const COUNT: usize = 32; } impl Vm { @@ -205,6 +207,19 @@ impl Vm { for state in self.module_eval_states.values() { f(RootGroup::ModuleEvalStates, state.namespace); } + // WrappedFunction 保活表(影子 realm 侧):原函数是 GC 根,漏根 → + // 影子 GC 回收 → WrappedFunction 持悬垂引用。 + for &value in &self.shadow_keepalive { + f(RootGroup::ShadowKeepalive, value); + } + // WrappedFunction 状态表(主 realm 侧):键(WrappedFunction 对象)是 GC 根, + // 漏根 → sweep 释放 → 调用点反查悬垂。 + for &wrapped_ptr in self.wrapped_function_state.keys() { + if wrapped_ptr.is_null() { + continue; + } + f(RootGroup::WrappedFunctionState, JsValue::from_js_object(wrapped_ptr)); + } } /// GC 根统一枚举入口:遍历的字段清单与 `for_each_value` 相同。 diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index a747a2d3..eed83b5f 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -481,6 +481,21 @@ pub struct Vm { /// Vm)。实例对象是 GC 根(`for_each_value` 遍历);影子 Vm 不跨线程, /// 随主 Vm 整体迁移,在 `full_reset` / `Drop` 清表时统一释放。 pub(crate) shadow_realm_objects: std::collections::HashMap, + /// WrappedFunction 保活表:本 Vm 的 GC 根,存放被 WrappedFunction 包装的 + /// 原函数(本 Vm 堆内对象)。WrappedFunction 持有跨 realm 逻辑边,原函数 + /// 须在属主 Vm 本表保活防属主 GC 回收;条目随 WrappedFunction 消亡孤儿至 + /// Vm 收尾(规模有界于包装数)。 + pub(crate) shadow_keepalive: std::vec::Vec, + /// WrappedFunction 状态表:键 = WrappedFunction 对象指针,值 = + /// (原函数属主 realm 编号,属主 Vm 保活表下标)。是 GC 根(键保活 + /// WrappedFunction);值非 GC 边。主 Vm 与影子 Vm 均可持有(包装方向 + /// 双向:evaluate 结果包装影子函数、包装函数实参包装主 realm 函数)。 + pub(crate) wrapped_function_state: std::collections::HashMap<*mut JsObject, (u64, usize)>, + /// 属主 Vm 指针(影子 realm 侧):影子 Vm 建时指向创建它的属主 Vm,主 Vm + /// 恒空。供跨 realm 结算沿属主链上溯解析远端 realm 的属主 Vm(嵌套影子 + /// realm 包装主 realm 函数场景)。与属主 Vm 同线程、同生命周期(属主 Vm + /// 侧表持影子 Vm,先于影子 Vm 存活),指针有效。 + pub(crate) owner_vm: *mut Vm, } impl Drop for Vm { diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index a3c69ece..17cd57ca 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -505,6 +505,11 @@ impl Vm { for i in 0..sub.n_args as usize { self.regs[sub.param_base as usize + i] = args.get(i).copied().unwrap_or(JsValue::undefined()); } + // 函数名不可写绑定槽写入:把 callee 函数对象写入名槽寄存器(内联调用时置位, + // 函数体入口的 MAKE_CELL / 读取据此取函数对象)。无名绑定(None)不写。 + if let Some(name_reg) = sub.function_name_reg { + self.regs[name_reg as usize] = callee; + } // this 绑定:箭头函数恒用词法捕获;sloppy 普通函数 this 为 null/undefined 时 // 替换为全局对象,其余原始值经 ToObject 盒装(ECMA-262 10.4.3);严格模式 // this 原样保留。 diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 1a5482c0..98640829 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -258,6 +258,9 @@ impl Vm { worker_next_id: 0, worker_objects: std::collections::HashMap::new(), shadow_realm_objects: std::collections::HashMap::new(), + shadow_keepalive: std::vec::Vec::new(), + wrapped_function_state: std::collections::HashMap::new(), + owner_vm: std::ptr::null_mut(), }; vm.init_generator_intrinsics(); vm.init_promise_intrinsics(); @@ -428,6 +431,9 @@ impl Vm { worker_next_id: 0, worker_objects: std::collections::HashMap::new(), shadow_realm_objects: std::collections::HashMap::new(), + shadow_keepalive: std::vec::Vec::new(), + wrapped_function_state: std::collections::HashMap::new(), + owner_vm: std::ptr::null_mut(), }; vm.init_generator_intrinsics(); vm.init_promise_intrinsics(); @@ -567,6 +573,11 @@ impl Vm { // ShadowRealm 实例注册表同持 session 对象指针(实例对象):teardown // 释放全部 session 对象前须清表,影子 Vm 随条目 drop 统一释放。 self.shadow_realm_objects.clear(); + // WrappedFunction 保活表与状态表同持 session 对象指针(原函数 / + // WrappedFunction 对象):teardown 释放全部 session 对象前须清表, + // 否则残留悬垂指针成 GC 根。 + self.shadow_keepalive.clear(); + self.wrapped_function_state.clear(); self.realm.teardown_session_heap_data(); self.realm.gc.borrow_mut().session_bytes_allocated = 0; self.realm.gc.borrow_mut().session_bytes_peak = 0;