0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-12-24 08:09:16 -05:00

Add Isolate::set_promise_hook() (#496)

This commit is contained in:
Ben Noordhuis 2020-10-12 22:33:46 +02:00 committed by GitHub
parent ea0c7c9383
commit 57390ec4ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 0 deletions

View file

@ -188,6 +188,10 @@ void v8__Isolate__RequestInterrupt(v8::Isolate* isolate,
isolate->RequestInterrupt(callback, data);
}
void v8__Isolate__SetPromiseHook(v8::Isolate* isolate, v8::PromiseHook hook) {
isolate->SetPromiseHook(hook);
}
void v8__Isolate__SetPromiseRejectCallback(v8::Isolate* isolate,
v8::PromiseRejectCallback callback) {
isolate->SetPromiseRejectCallback(callback);

View file

@ -42,8 +42,34 @@ pub enum MicrotasksPolicy {
Auto = 2,
}
/// PromiseHook with type Init is called when a new promise is
/// created. When a new promise is created as part of the chain in the
/// case of Promise.then or in the intermediate promises created by
/// Promise.{race, all}/AsyncFunctionAwait, we pass the parent promise
/// otherwise we pass undefined.
///
/// PromiseHook with type Resolve is called at the beginning of
/// resolve or reject function defined by CreateResolvingFunctions.
///
/// PromiseHook with type Before is called at the beginning of the
/// PromiseReactionJob.
///
/// PromiseHook with type After is called right at the end of the
/// PromiseReactionJob.
#[derive(Debug)]
#[repr(C)]
pub enum PromiseHookType {
Init,
Resolve,
Before,
After,
}
pub type MessageCallback = extern "C" fn(Local<Message>, Local<Value>);
pub type PromiseHook =
extern "C" fn(PromiseHookType, Local<Promise>, Local<Value>);
pub type PromiseRejectCallback = extern "C" fn(PromiseRejectMessage);
/// HostInitializeImportMetaObjectCallback is called the first time import.meta
@ -127,6 +153,7 @@ extern "C" {
callback: NearHeapLimitCallback,
heap_limit: usize,
);
fn v8__Isolate__SetPromiseHook(isolate: *mut Isolate, hook: PromiseHook);
fn v8__Isolate__SetPromiseRejectCallback(
isolate: *mut Isolate,
callback: PromiseRejectCallback,
@ -398,6 +425,12 @@ impl Isolate {
unsafe { v8__Isolate__AddMessageListener(self, callback) }
}
/// Set the PromiseHook callback for various promise lifecycle
/// events.
pub fn set_promise_hook(&mut self, hook: PromiseHook) {
unsafe { v8__Isolate__SetPromiseHook(self, hook) }
}
/// Set callback to notify about promise reject with no handler, or
/// revocation of such a previous notification once the handler is added.
pub fn set_promise_reject_callback(

View file

@ -98,6 +98,8 @@ pub use isolate::MessageCallback;
pub use isolate::MicrotasksPolicy;
pub use isolate::NearHeapLimitCallback;
pub use isolate::OwnedIsolate;
pub use isolate::PromiseHook;
pub use isolate::PromiseHookType;
pub use isolate::PromiseRejectCallback;
pub use isolate_create_params::CreateParams;
pub use module::*;

View file

@ -1726,6 +1726,57 @@ fn promise_reject_callback_no_value() {
}
}
#[test]
fn promise_hook() {
extern "C" fn hook(
type_: v8::PromiseHookType,
promise: v8::Local<v8::Promise>,
_parent: v8::Local<v8::Value>,
) {
let scope = &mut unsafe { v8::CallbackScope::new(promise) };
let context = promise.creation_context(scope);
let scope = &mut v8::ContextScope::new(scope, context);
let global = context.global(scope);
let name = v8::String::new(scope, "hook").unwrap();
let func = global.get(scope, name.into()).unwrap();
let func = v8::Local::<v8::Function>::try_from(func).unwrap();
let args = &[v8::Integer::new(scope, type_ as i32).into(), promise.into()];
func.call(scope, global.into(), args).unwrap();
}
let _setup_guard = setup();
let isolate = &mut v8::Isolate::new(Default::default());
isolate.set_promise_hook(hook);
{
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
let source = r#"
var promises = new Set();
function hook(type, promise) {
if (type === /* Init */ 0) promises.add(promise);
if (type === /* Resolve */ 1) promises.delete(promise);
}
function expect(expected, actual = promises.size) {
if (actual !== expected) throw `expected ${expected}, actual ${actual}`;
}
expect(0);
new Promise(resolve => {
expect(1);
resolve();
expect(0);
});
expect(0);
new Promise(() => {});
expect(1);
promises.values().next().value
"#;
let promise = eval(scope, source).unwrap();
let promise = v8::Local::<v8::Promise>::try_from(promise).unwrap();
assert!(!promise.has_handler());
assert_eq!(promise.state(), v8::PromiseState::Pending);
}
}
fn mock_script_origin<'s>(
scope: &mut v8::HandleScope<'s>,
resource_name_: &str,