0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2025-01-21 21:50:20 -05:00

ShadowRealm integration callback (#959)

This commit is contained in:
Andreu Botella 2022-05-24 16:26:12 +02:00 committed by GitHub
parent 5f90045ad0
commit d43535c48b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 159 additions and 1 deletions

View file

@ -4,6 +4,7 @@
#include <iostream>
#include "support.h"
#include "v8-callbacks.h"
#include "v8/include/libplatform/libplatform.h"
#include "v8/include/v8-fast-api-calls.h"
#include "v8/include/v8-inspector.h"
@ -263,6 +264,11 @@ void v8__Isolate__SetHostImportModuleDynamicallyCallback(
HostImportModuleDynamicallyCallback);
}
void v8__Isolate__SetHostCreateShadowRealmContextCallback(
v8::Isolate* isolate, v8::HostCreateShadowRealmContextCallback callback) {
isolate->SetHostCreateShadowRealmContextCallback(callback);
}
bool v8__Isolate__AddMessageListener(v8::Isolate* isolate,
v8::MessageCallback callback) {
return isolate->AddMessageListener(callback);

View file

@ -129,6 +129,19 @@ pub type HostImportModuleDynamicallyCallback = extern "C" fn(
Local<FixedArray>,
) -> *mut Promise;
/// `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.
///
/// The method combines [`Context`] creation and the implementation-defined
/// abstract operation `HostInitializeShadowRealm` into one.
///
/// The embedder should use [`Context::new`] to create a new context. If the
/// creation fails, the embedder must propagate that exception by returning
/// [`None`].
pub type HostCreateShadowRealmContextCallback =
for<'s> fn(scope: &mut HandleScope<'s>) -> Option<Local<'s, Context>>;
pub type InterruptCallback =
extern "C" fn(isolate: &mut Isolate, data: *mut c_void);
@ -222,6 +235,19 @@ extern "C" {
isolate: *mut Isolate,
callback: HostImportModuleDynamicallyCallback,
);
#[cfg(not(target_os = "windows"))]
fn v8__Isolate__SetHostCreateShadowRealmContextCallback(
isolate: *mut Isolate,
callback: extern "C" fn(initiator_context: Local<Context>) -> *mut Context,
);
#[cfg(target_os = "windows")]
fn v8__Isolate__SetHostCreateShadowRealmContextCallback(
isolate: *mut Isolate,
callback: extern "C" fn(
rv: *mut *mut Context,
initiator_context: Local<Context>,
) -> *mut *mut Context,
);
fn v8__Isolate__RequestInterrupt(
isolate: *const Isolate,
callback: InterruptCallback,
@ -609,6 +635,56 @@ impl Isolate {
}
}
/// 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(
&mut self,
callback: HostCreateShadowRealmContextCallback,
) {
#[inline]
extern "C" fn rust_shadow_realm_callback(
initiator_context: Local<Context>,
) -> *mut Context {
let mut scope = unsafe { CallbackScope::new(initiator_context) };
let callback = scope
.get_slot::<HostCreateShadowRealmContextCallback>()
.unwrap();
let context = callback(&mut scope);
context
.map(|l| l.as_non_null().as_ptr())
.unwrap_or_else(null_mut)
}
// Windows x64 ABI: MaybeLocal<Context> must be returned on the stack.
#[cfg(target_os = "windows")]
extern "C" fn rust_shadow_realm_callback_windows(
rv: *mut *mut Context,
initiator_context: Local<Context>,
) -> *mut *mut Context {
let ret = rust_shadow_realm_callback(initiator_context);
unsafe {
rv.write(ret);
}
rv
}
let slot_didnt_exist_before = self.set_slot(callback);
if slot_didnt_exist_before {
unsafe {
#[cfg(target_os = "windows")]
v8__Isolate__SetHostCreateShadowRealmContextCallback(
self,
rust_shadow_realm_callback_windows,
);
#[cfg(not(target_os = "windows"))]
v8__Isolate__SetHostCreateShadowRealmContextCallback(
self,
rust_shadow_realm_callback,
);
}
}
}
/// Add a callback to invoke in case the heap size is close to the heap limit.
/// If multiple callbacks are added, only the most recently added callback is
/// invoked.

View file

@ -92,6 +92,7 @@ pub use handle::Handle;
pub use handle::Local;
pub use handle::Weak;
pub use isolate::HeapStatistics;
pub use isolate::HostCreateShadowRealmContextCallback;
pub use isolate::HostImportModuleDynamicallyCallback;
pub use isolate::HostInitializeImportMetaObjectCallback;
pub use isolate::Isolate;

View file

@ -35,7 +35,9 @@ fn setup() -> SetupGuard {
"../third_party/icu/common/icudtl.dat"
))
.is_ok());
v8::V8::set_flags_from_string("--expose_gc --harmony-import-assertions");
v8::V8::set_flags_from_string(
"--expose_gc --harmony-import-assertions --harmony-shadow-realm",
);
v8::V8::initialize_platform(
v8::new_default_platform(0, false).make_shared(),
);
@ -6728,3 +6730,76 @@ fn finalizer_on_kept_global() {
drop(weak);
drop(global);
}
#[test]
fn host_create_shadow_realm_context_callback() {
let _setup_guard = setup();
let isolate = &mut v8::Isolate::new(Default::default());
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
{
let tc_scope = &mut v8::TryCatch::new(scope);
assert!(eval(tc_scope, "new ShadowRealm()").is_none());
assert!(tc_scope.has_caught());
}
struct CheckData {
callback_called: bool,
main_context: v8::Global<v8::Context>,
}
let main_context = v8::Global::new(scope, context);
scope.set_slot(CheckData {
callback_called: false,
main_context,
});
scope.set_host_create_shadow_realm_context_callback(|scope| {
let main_context = {
let data = scope.get_slot_mut::<CheckData>().unwrap();
data.callback_called = true;
data.main_context.clone()
};
assert_eq!(scope.get_current_context(), main_context);
// Can't return None without throwing.
let message = v8::String::new(scope, "Unsupported").unwrap();
let exception = v8::Exception::type_error(scope, message);
scope.throw_exception(exception);
None
});
{
let tc_scope = &mut v8::TryCatch::new(scope);
assert!(eval(tc_scope, "new ShadowRealm()").is_none());
assert!(tc_scope.has_caught());
assert!(tc_scope.get_slot::<CheckData>().unwrap().callback_called);
}
scope.set_host_create_shadow_realm_context_callback(|scope| {
let main_context = {
let data = scope.get_slot_mut::<CheckData>().unwrap();
data.callback_called = true;
data.main_context.clone()
};
assert_eq!(scope.get_current_context(), main_context);
let new_context = v8::Context::new(scope);
{
let scope = &mut v8::ContextScope::new(scope, new_context);
let global = new_context.global(scope);
let key = v8::String::new(scope, "test").unwrap();
let value = v8::Integer::new(scope, 42);
global.set(scope, key.into(), value.into()).unwrap();
}
Some(new_context)
});
let value =
eval(scope, "new ShadowRealm().evaluate(`globalThis.test`)").unwrap();
assert_eq!(value.uint32_value(scope), Some(42));
assert!(scope.get_slot::<CheckData>().unwrap().callback_called);
}