0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2025-02-15 01:55:56 -05:00

feat: dynamic import phase callback

This commit is contained in:
snek 2025-02-12 16:51:43 +01:00
parent 7d1ebb20a7
commit 69bc7be3c8
No known key found for this signature in database
6 changed files with 256 additions and 0 deletions

View file

@ -154,6 +154,7 @@ fn build_binding() {
.clang_args(args)
.generate_cstr(true)
.rustified_enum(".*UseCounterFeature")
.rustified_enum(".*ModuleImportPhase")
.allowlist_item("v8__.*")
.allowlist_item("cppgc__.*")
.allowlist_item("RustObj")

View file

@ -272,6 +272,12 @@ void v8__Isolate__SetHostImportModuleDynamicallyCallback(
isolate->SetHostImportModuleDynamicallyCallback(callback);
}
void v8__Isolate__SetHostImportModuleWithPhaseDynamicallyCallback(
v8::Isolate* isolate,
v8::HostImportModuleWithPhaseDynamicallyCallback callback) {
isolate->SetHostImportModuleWithPhaseDynamicallyCallback(callback);
}
void v8__Isolate__SetHostCreateShadowRealmContextCallback(
v8::Isolate* isolate, v8::HostCreateShadowRealmContextCallback callback) {
isolate->SetHostCreateShadowRealmContextCallback(callback);

View file

@ -39,6 +39,7 @@ using v8__CFunctionInfo = v8::CFunctionInfo;
using v8__FastOneByteString = v8::FastOneByteString;
using v8__Isolate__UseCounterFeature = v8::Isolate::UseCounterFeature;
using v8__String__WriteFlags = v8::String::WriteFlags;
using v8__ModuleImportPhase = v8::ModuleImportPhase;
static uint32_t v8__MAJOR_VERSION = V8_MAJOR_VERSION;
static uint32_t v8__MINOR_VERSION = V8_MINOR_VERSION;

View file

@ -1,5 +1,6 @@
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
use crate::binding::v8__Isolate__UseCounterFeature;
pub use crate::binding::v8__ModuleImportPhase as ModuleImportPhase;
use crate::cppgc::Heap;
use crate::function::FunctionCallbackInfo;
use crate::gc::GCCallbackFlags;
@ -340,6 +341,171 @@ where
}
}
/// HostImportModuleWithPhaseDynamicallyCallback is called when we
/// require the embedder to load a module with a specific phase. This is used
/// as part of the dynamic import syntax.
///
/// The referrer contains metadata about the script/module that calls
/// import.
///
/// The specifier is the name of the module that should be imported.
///
/// The phase is the phase of the import requested.
///
/// The import_attributes are import attributes for this request in the form:
/// [key1, value1, key2, value2, ...] where the keys and values are of type
/// v8::String. Note, unlike the FixedArray passed to ResolveModuleCallback and
/// returned from ModuleRequest::GetImportAttributes(), this array does not
/// contain the source Locations of the attributes.
///
/// The Promise returned from this function is forwarded to userland
/// JavaScript. The embedder must resolve this promise according to the phase
/// requested:
/// - For ModuleImportPhase::kSource, the promise must be resolved with a
/// compiled ModuleSource object, or rejected with a SyntaxError if the
/// module does not support source representation.
/// - For ModuleImportPhase::kEvaluation, the promise must be resolved with a
/// ModuleNamespace object of a module that has been compiled, instantiated,
/// and evaluated.
///
/// In case of an exception, the embedder must reject this promise with the
/// exception. If the promise creation itself fails (e.g. due to stack
/// overflow), the embedder must propagate that exception by returning an empty
/// MaybeLocal.
///
/// This callback is still experimental and is only invoked for source phase
/// imports.
pub trait HostImportModuleWithPhaseDynamicallyCallback:
UnitType
+ for<'s> FnOnce(
&mut HandleScope<'s>,
Local<'s, Data>,
Local<'s, Value>,
Local<'s, String>,
ModuleImportPhase,
Local<'s, FixedArray>,
) -> Option<Local<'s, Promise>>
{
fn to_c_fn(self) -> RawHostImportModuleWithPhaseDynamicallyCallback;
}
#[cfg(target_family = "unix")]
pub(crate) type RawHostImportModuleWithPhaseDynamicallyCallback =
for<'s> extern "C" fn(
Local<'s, Context>,
Local<'s, Data>,
Local<'s, Value>,
Local<'s, String>,
ModuleImportPhase,
Local<'s, FixedArray>,
) -> *mut Promise;
#[cfg(all(target_family = "windows", target_arch = "x86_64"))]
pub type RawHostImportModuleWithPhaseDynamicallyCallback =
for<'s> extern "C" fn(
*mut *mut Promise,
Local<'s, Context>,
Local<'s, Data>,
Local<'s, Value>,
Local<'s, String>,
ModuleImportPhase,
Local<'s, FixedArray>,
) -> *mut *mut Promise;
impl<F> HostImportModuleWithPhaseDynamicallyCallback for F
where
F: UnitType
+ for<'s> FnOnce(
&mut HandleScope<'s>,
Local<'s, Data>,
Local<'s, Value>,
Local<'s, String>,
ModuleImportPhase,
Local<'s, FixedArray>,
) -> Option<Local<'s, Promise>>,
{
#[inline(always)]
fn to_c_fn(self) -> RawHostImportModuleWithPhaseDynamicallyCallback {
#[inline(always)]
fn scope_adapter<'s, F: HostImportModuleWithPhaseDynamicallyCallback>(
context: Local<'s, Context>,
host_defined_options: Local<'s, Data>,
resource_name: Local<'s, Value>,
specifier: Local<'s, String>,
import_phase: ModuleImportPhase,
import_attributes: Local<'s, FixedArray>,
) -> Option<Local<'s, Promise>> {
let scope = &mut unsafe { CallbackScope::new(context) };
(F::get())(
scope,
host_defined_options,
resource_name,
specifier,
import_phase,
import_attributes,
)
}
#[cfg(target_family = "unix")]
#[inline(always)]
extern "C" fn abi_adapter<
's,
F: HostImportModuleWithPhaseDynamicallyCallback,
>(
context: Local<'s, Context>,
host_defined_options: Local<'s, Data>,
resource_name: Local<'s, Value>,
specifier: Local<'s, String>,
import_phase: ModuleImportPhase,
import_attributes: Local<'s, FixedArray>,
) -> *mut Promise {
scope_adapter::<F>(
context,
host_defined_options,
resource_name,
specifier,
import_phase,
import_attributes,
)
.map_or_else(null_mut, |return_value| return_value.as_non_null().as_ptr())
}
#[cfg(all(target_family = "windows", target_arch = "x86_64"))]
#[inline(always)]
extern "C" fn abi_adapter<
's,
F: HostImportModuleWithPhaseDynamicallyCallback,
>(
return_value: *mut *mut Promise,
context: Local<'s, Context>,
host_defined_options: Local<'s, Data>,
resource_name: Local<'s, Value>,
specifier: Local<'s, String>,
import_phase: ModuleImportPhase,
import_attributes: Local<'s, FixedArray>,
) -> *mut *mut Promise {
unsafe {
std::ptr::write(
return_value,
scope_adapter::<F>(
context,
host_defined_options,
resource_name,
specifier,
import_phase,
import_attributes,
)
.map(|return_value| return_value.as_non_null().as_ptr())
.unwrap_or_else(null_mut),
);
return_value
}
}
abi_adapter::<F>
}
}
/// `HostCreateShadowRealmContextCallback` is called each time a `ShadowRealm`
/// is being constructed. You can use [`HandleScope::get_current_context`] to
/// get the [`Context`] in which the constructor is being run.
@ -497,6 +663,10 @@ extern "C" {
isolate: *mut Isolate,
callback: RawHostImportModuleDynamicallyCallback,
);
fn v8__Isolate__SetHostImportModuleWithPhaseDynamicallyCallback(
isolate: *mut Isolate,
callback: RawHostImportModuleWithPhaseDynamicallyCallback,
);
#[cfg(not(target_os = "windows"))]
fn v8__Isolate__SetHostCreateShadowRealmContextCallback(
isolate: *mut Isolate,
@ -1088,6 +1258,26 @@ impl Isolate {
}
}
/// This specifies the callback called by the upcoming dynamic
/// import() and import.source() language feature to load modules.
///
/// This API is experimental and is expected to be changed or removed in the
/// future. The callback is currently only called when for source-phase
/// imports. Evaluation-phase imports use the existing
/// HostImportModuleDynamicallyCallback callback.
#[inline(always)]
pub fn set_host_import_module_with_phase_dynamically_callback(
&mut self,
callback: impl HostImportModuleWithPhaseDynamicallyCallback,
) {
unsafe {
v8__Isolate__SetHostImportModuleWithPhaseDynamicallyCallback(
self,
callback.to_c_fn(),
);
}
}
/// This specifies the callback called by the upcoming `ShadowRealm`
/// construction language feature to retrieve host created globals.
pub fn set_host_create_shadow_realm_context_callback(

View file

@ -107,6 +107,7 @@ pub use isolate::GarbageCollectionType;
pub use isolate::HeapStatistics;
pub use isolate::HostCreateShadowRealmContextCallback;
pub use isolate::HostImportModuleDynamicallyCallback;
pub use isolate::HostImportModuleWithPhaseDynamicallyCallback;
pub use isolate::HostInitializeImportMetaObjectCallback;
pub use isolate::Isolate;
pub use isolate::IsolateHandle;
@ -114,6 +115,7 @@ pub use isolate::MemoryPressureLevel;
pub use isolate::MessageCallback;
pub use isolate::MessageErrorLevel;
pub use isolate::MicrotasksPolicy;
pub use isolate::ModuleImportPhase;
pub use isolate::NearHeapLimitCallback;
pub use isolate::OomDetails;
pub use isolate::OomErrorCallback;

View file

@ -7801,6 +7801,62 @@ fn static_source_phase_import() {
assert_eq!(obj, ns.get(scope, default.into()).unwrap());
}
#[test]
fn dynamic_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 obj = eval(scope, "new WebAssembly.Module(new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]))").unwrap().cast::<v8::Object>();
context.set_slot(v8::Global::new(scope, obj));
fn dynamic_import_cb<'s>(
_scope: &mut v8::HandleScope<'s>,
_host_defined_options: v8::Local<'s, v8::Data>,
_resource_name: v8::Local<'s, v8::Value>,
_specifier: v8::Local<'s, v8::String>,
_import_attributes: v8::Local<'s, v8::FixedArray>,
) -> Option<v8::Local<'s, v8::Promise>> {
unreachable!()
}
fn dynamic_import_phase_cb<'s>(
scope: &mut v8::HandleScope<'s>,
_host_defined_options: v8::Local<'s, v8::Data>,
_resource_name: v8::Local<'s, v8::Value>,
_specifier: v8::Local<'s, v8::String>,
phase: v8::ModuleImportPhase,
_import_attributes: v8::Local<'s, v8::FixedArray>,
) -> Option<v8::Local<'s, v8::Promise>> {
assert_eq!(phase, v8::ModuleImportPhase::kSource);
let resolver = v8::PromiseResolver::new(scope).unwrap();
let context = scope.get_current_context();
let global = context.get_slot::<v8::Global<v8::Object>>().unwrap();
let obj = v8::Local::new(scope, global);
resolver.resolve(scope, obj.into());
Some(resolver.get_promise(scope))
}
scope.set_host_import_module_dynamically_callback(dynamic_import_cb);
scope.set_host_import_module_with_phase_dynamically_callback(
dynamic_import_phase_cb,
);
let promise = eval(scope, "import.source('a')")
.unwrap()
.cast::<v8::Promise>();
eprintln!("{}", promise.result(scope).to_rust_string_lossy(scope));
assert_eq!(obj, promise.result(scope));
}
#[allow(clippy::float_cmp)]
#[test]
fn date() {