diff --git a/src/binding.cc b/src/binding.cc index b2853d3d..c3519cb5 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -3252,11 +3252,12 @@ int v8__Module__ScriptId(const v8::Module& self) { return const_cast(self).ScriptId(); } -MaybeBool v8__Module__InstantiateModule(const v8::Module& self, - const v8::Context& context, - v8::Module::ResolveModuleCallback cb) { - return maybe_to_maybe_bool( - ptr_to_local(&self)->InstantiateModule(ptr_to_local(&context), cb)); +MaybeBool v8__Module__InstantiateModule( + const v8::Module& self, const v8::Context& context, + v8::Module::ResolveModuleCallback cb, + v8::Module::ResolveSourceCallback source_callback) { + return maybe_to_maybe_bool(ptr_to_local(&self)->InstantiateModule( + ptr_to_local(&context), cb, source_callback)); } const v8::Value* v8__Module__Evaluate(const v8::Module& self, diff --git a/src/module.rs b/src/module.rs index ce963d70..958684e1 100644 --- a/src/module.rs +++ b/src/module.rs @@ -16,6 +16,7 @@ use crate::Local; use crate::Message; use crate::Module; use crate::ModuleRequest; +use crate::Object; use crate::String; use crate::UnboundModuleScript; use crate::Value; @@ -146,6 +147,64 @@ where } } +// System V ABI +#[cfg(not(target_os = "windows"))] +#[repr(C)] +pub struct ResolveSourceCallbackRet(*const Object); + +#[cfg(not(target_os = "windows"))] +pub type ResolveSourceCallback<'a> = extern "C" fn( + Local<'a, Context>, + Local<'a, String>, + Local<'a, FixedArray>, + Local<'a, Module>, +) -> ResolveSourceCallbackRet; + +// Windows x64 ABI: Local returned on the stack. +#[cfg(target_os = "windows")] +pub type ResolveSourceCallback<'a> = extern "C" fn( + *mut *const Object, + Local<'a, Context>, + Local<'a, String>, + Local<'a, FixedArray>, + Local<'a, Module>, +) -> *mut *const Object; + +impl<'a, F> MapFnFrom for ResolveSourceCallback<'a> +where + F: UnitType + + Fn( + Local<'a, Context>, + Local<'a, String>, + Local<'a, FixedArray>, + Local<'a, Module>, + ) -> Option>, +{ + #[cfg(not(target_os = "windows"))] + fn mapping() -> Self { + let f = |context, specifier, import_attributes, referrer| { + ResolveSourceCallbackRet( + (F::get())(context, specifier, import_attributes, referrer) + .map(|r| -> *const Object { &*r }) + .unwrap_or(null()), + ) + }; + f.to_c_fn() + } + + #[cfg(target_os = "windows")] + fn mapping() -> Self { + let f = |ret_ptr, context, specifier, import_attributes, referrer| { + let r = (F::get())(context, specifier, import_attributes, referrer) + .map(|r| -> *const Object { &*r }) + .unwrap_or(null()); + unsafe { std::ptr::write(ret_ptr, r) }; // Write result to stack. + ret_ptr // Return stack pointer to the return value. + }; + f.to_c_fn() + } +} + extern "C" { fn v8__Module__GetStatus(this: *const Module) -> ModuleStatus; fn v8__Module__GetException(this: *const Module) -> *const Value; @@ -162,6 +221,7 @@ extern "C" { this: *const Module, context: *const Context, cb: ResolveModuleCallback, + source_callback: Option, ) -> MaybeBool; fn v8__Module__Evaluate( this: *const Module, @@ -323,6 +383,31 @@ impl Module { self, &*scope.get_current_context(), callback.map_fn_to(), + None, + ) + } + .into() + } + + /// Instantiates the module and its dependencies. + /// + /// Returns an empty Maybe if an exception occurred during + /// instantiation. (In the case where the callback throws an exception, that + /// exception is propagated.) + #[must_use] + #[inline(always)] + pub fn instantiate_module2<'a>( + &self, + scope: &mut HandleScope, + callback: impl MapFnTo>, + source_callback: impl MapFnTo>, + ) -> Option { + unsafe { + v8__Module__InstantiateModule( + self, + &*scope.get_current_context(), + callback.map_fn_to(), + Some(source_callback.map_fn_to()), ) } .into() diff --git a/tests/test_api.rs b/tests/test_api.rs index f0df7457..1bd44b32 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -51,7 +51,7 @@ mod setup { )) .is_ok()); v8::V8::set_flags_from_string( - "--no_freeze_flags_after_init --expose_gc --harmony-shadow-realm --allow_natives_syntax --turbo_fast_api_calls", + "--no_freeze_flags_after_init --expose_gc --harmony-shadow-realm --allow_natives_syntax --turbo_fast_api_calls --js-source-phase-imports", ); v8::V8::initialize_platform( v8::new_unprotected_default_platform(0, false).make_shared(), @@ -7755,6 +7755,52 @@ fn synthetic_module() { check("b", 2.0); } +#[test] +fn static_source_phase_import() { + let _setup_guard = setup::parallel_test(); + let isolate = &mut v8::Isolate::new(Default::default()); + + let scope = &mut v8::HandleScope::new(isolate); + + let context = v8::Context::new(scope, Default::default()); + let scope = &mut v8::ContextScope::new(scope, context); + + let module = create_module( + scope, + "import source mod from 'z'; export default mod;", + None, + v8::script_compiler::CompileOptions::NoCompileOptions, + ); + + let obj = eval(scope, "new WebAssembly.Module(new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]))").unwrap().cast::(); + + context.set_slot(v8::Global::new(scope, obj)); + + fn resolve_source<'a>( + context: v8::Local<'a, v8::Context>, + _specifier: v8::Local<'a, v8::String>, + _import_attributes: v8::Local<'a, v8::FixedArray>, + _referrer: v8::Local<'a, v8::Module>, + ) -> Option> { + let scope = unsafe { &mut v8::CallbackScope::new(context) }; + let global = context.get_slot::>().unwrap(); + Some(v8::Local::new(scope, global)) + } + + module + .instantiate_module2( + scope, + unexpected_module_resolve_callback, + resolve_source, + ) + .unwrap(); + module.evaluate(scope).unwrap(); + + let ns = module.get_module_namespace().cast::(); + let default = v8::String::new(scope, "default").unwrap(); + assert_eq!(obj, ns.get(scope, default.into()).unwrap()); +} + #[allow(clippy::float_cmp)] #[test] fn date() {