feat: ShadowRealm evaluate 与 importValue 真实现落地

This commit is contained in:
2026-10-19 04:47:57 +00:00
parent d765346e37
commit ed10209764
15 changed files with 629 additions and 72 deletions
Generated
+21 -21
View File
@@ -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",
+1 -1
View File
@@ -25,7 +25,7 @@ members = [
]
[workspace.package]
version = "0.21.3"
version = "0.21.4"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+1 -1
View File
@@ -1434,7 +1434,7 @@ pub fn object_define_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
///
/// # 副作用
/// - 在 epoch 分配一个描述符对象
fn own_descriptor_of<H: VmHost>(vm: &mut H, obj: &JsObject, key_si: u32) -> Result<Option<JsValue>, JsValue> {
pub fn own_descriptor_of<H: VmHost>(vm: &mut H, obj: &JsObject, key_si: u32) -> Result<Option<JsValue>, JsValue> {
// Proxy [[GetOwnProperty]]:getOwnPropertyDescriptor trap 以(target、P)
// 调用;trap 缺失落穿 target 的自身描述符(target 自身为代理时经递归再
// 路由);trap 返 undefined 表示无属性(target 自身属性不可配置或 target
+39 -2
View File
@@ -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<dyn CompilerService>` 可跨线程共享。
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<CompiledModule, String> {
// 模块源按磁盘文件读入(非 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<Program<'a>, String> {
oxide_parser::parse_module(allocator, source)
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().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<oxide_emit::module::ResolvedModule, String> {
let (source, path) = self.inner.resolve(base_dir, specifier)?;
Ok(oxide_emit::module::ResolvedModule {
source,
path,
kind: oxide_emit::module::ModuleKind::Js,
})
}
}
/// 解析源码,解析错误各条消息换行拼接为单一错误串。
@@ -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<dyn CompilerService>` 可跨线程共享
@@ -19,4 +27,12 @@ pub trait CompilerService: Send + Sync {
///
/// 顶层 var/function 声明落全局属性 configurable:true。
fn compile_eval_script(&self, source: &str) -> Result<CompiledModule, String>;
/// 编译 ES module(含依赖加载),返回完整模块树。
///
/// `module_path` 为模块文件的规范路径(依赖解析基准 = 其父目录),
/// 依赖经 `loader` 解析。
fn compile_module(
&self, source: &str, module_path: &str, loader: &mut dyn ModuleSourceLoader,
) -> Result<CompiledModule, String>;
}
+1 -1
View File
@@ -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;
+13 -1
View File
@@ -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(&params.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(
&params.paths[i],
&test_path,
&params.filter,
params.no_skip,
params.no_regalloc,
+16 -1
View File
@@ -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,
@@ -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)]);
}
}
+441 -37
View File
@@ -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<JsValue, String> = {
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<JsValue, JsValue> {
// 解析模块路径(以当前工作目录为基准,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<JsValue, JsValue> {
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<JsValue, JsValue> {
// 目标侧:保活原函数、读 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<JsValue, JsValue> {
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()))
}
}
+29 -2
View File
@@ -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<CompiledModule, String> {
Err("dynamic compilation unavailable: no compiler service installed".to_string())
}
fn compile_module(
&self, _source: &str, _module_path: &str, _loader: &mut dyn ModuleSourceLoader,
) -> Result<CompiledModule, String> {
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);
+16 -1
View File
@@ -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` 相同。
+15
View File
@@ -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<u64, (JsValue, Vm)>,
/// WrappedFunction 保活表:本 Vm 的 GC 根,存放被 WrappedFunction 包装的
/// 原函数(本 Vm 堆内对象)。WrappedFunction 持有跨 realm 逻辑边,原函数
/// 须在属主 Vm 本表保活防属主 GC 回收;条目随 WrappedFunction 消亡孤儿至
/// Vm 收尾(规模有界于包装数)。
pub(crate) shadow_keepalive: std::vec::Vec<JsValue>,
/// 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 {
+5
View File
@@ -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 原样保留。
+11
View File
@@ -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;