Skip to content

Add PromiseHook bindings #453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions core/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,18 @@ pub(crate) use r#async::InnerRuntime;
#[cfg(feature = "futures")]
pub use r#async::{AsyncRuntime, AsyncWeakRuntime};

use crate::value::promise::PromiseHookType;
use crate::{Ctx, Value};

/// The type of the promise hook.
#[cfg(not(feature = "parallel"))]
pub type PromiseHook =
Box<dyn for<'a> Fn(Ctx<'a>, PromiseHookType, Value<'a>, Value<'a>) + 'static>;
/// The type of the promise hook.
#[cfg(feature = "parallel")]
pub type PromiseHook =
Box<dyn for<'a> Fn(Ctx<'a>, PromiseHookType, Value<'a>, Value<'a>) + Send + 'static>;

/// The type of the promise rejection tracker.
#[cfg(not(feature = "parallel"))]
pub type RejectionTracker = Box<dyn for<'a> Fn(Ctx<'a>, Value<'a>, Value<'a>, bool) + 'static>;
Expand Down
10 changes: 9 additions & 1 deletion core/src/runtime/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use async_lock::Mutex;

use super::{
opaque::Opaque, raw::RawRuntime, schedular::SchedularPoll, spawner::DriveFuture,
InterruptHandler, MemoryUsage,
InterruptHandler, MemoryUsage, PromiseHook,
};
use crate::allocator::Allocator;
#[cfg(feature = "loader")]
Expand Down Expand Up @@ -160,6 +160,14 @@ impl AsyncRuntime {
}
}

/// Set a closure which is called when a promise is created, resolved, or chained.
#[inline]
pub async fn set_promise_hook(&self, tracker: Option<PromiseHook>) {
unsafe {
self.inner.lock().await.runtime.set_promise_hook(tracker);
}
}

/// Set a closure which is regularly called by the engine when it is executing code.
/// If the provided closure returns `true` the interpreter will raise and uncatchable
/// exception and return control flow to the caller.
Expand Down
12 changes: 11 additions & 1 deletion core/src/runtime/base.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! QuickJS runtime related types.

use super::{opaque::Opaque, raw::RawRuntime, InterruptHandler, MemoryUsage, RejectionTracker};
use super::{
opaque::Opaque, raw::RawRuntime, InterruptHandler, MemoryUsage, PromiseHook, RejectionTracker,
};
use crate::allocator::Allocator;
#[cfg(feature = "loader")]
use crate::loader::{Loader, Resolver};
Expand Down Expand Up @@ -61,6 +63,14 @@ impl Runtime {
WeakRuntime(Ref::downgrade(&self.inner))
}

/// Set a closure which is called when a promise is created, resolved, or chained.
#[inline]
pub fn set_promise_hook(&self, tracker: Option<PromiseHook>) {
unsafe {
self.inner.lock().set_promise_hook(tracker);
}
}

/// Set a closure which is called when a Promise is rejected.
#[inline]
pub fn set_host_promise_rejection_tracker(&self, tracker: Option<RejectionTracker>) {
Expand Down
21 changes: 20 additions & 1 deletion core/src/runtime/opaque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{

use super::{
userdata::{UserDataGuard, UserDataMap},
InterruptHandler, RejectionTracker, UserDataError,
InterruptHandler, PromiseHook, PromiseHookType, RejectionTracker, UserDataError,
};
use std::{
any::{Any, TypeId},
Expand All @@ -29,6 +29,9 @@ pub(crate) struct Opaque<'js> {
/// Used to carry a panic if a callback triggered one.
panic: Cell<Option<Box<dyn Any + Send + 'static>>>,

/// The user provided promise hook, if any.
promise_hook: UnsafeCell<Option<PromiseHook>>,

/// The user provided rejection tracker, if any.
rejection_tracker: UnsafeCell<Option<RejectionTracker>>,

Expand All @@ -55,6 +58,8 @@ impl<'js> Opaque<'js> {
Opaque {
panic: Cell::new(None),

promise_hook: UnsafeCell::new(None),

rejection_tracker: UnsafeCell::new(None),

interrupt_handler: UnsafeCell::new(None),
Expand Down Expand Up @@ -169,6 +174,20 @@ impl<'js> Opaque<'js> {
self.userdata.get()
}

pub fn set_promise_hook(&self, promise_hook: Option<PromiseHook>) {
unsafe { (*self.promise_hook.get()) = promise_hook }
}

pub fn run_promise_hook<'a>(
&self,
ctx: Ctx<'a>,
type_: PromiseHookType,
promise: Value<'a>,
parent: Value<'a>,
) {
unsafe { (*self.promise_hook.get()).as_mut().unwrap()(ctx, type_, promise, parent) }
}

pub fn set_rejection_tracker(&self, tracker: Option<RejectionTracker>) {
unsafe { (*self.rejection_tracker.get()) = tracker }
}
Expand Down
45 changes: 44 additions & 1 deletion core/src/runtime/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
Ctx, Error, Result, Value,
};

use super::{opaque::Opaque, InterruptHandler, RejectionTracker};
use super::{opaque::Opaque, InterruptHandler, PromiseHook, PromiseHookType, RejectionTracker};

const DUMP_BYTECODE_FINAL: u64 = 0x01;
const DUMP_BYTECODE_PASS2: u64 = 0x02;
Expand Down Expand Up @@ -286,6 +286,49 @@ impl RawRuntime {
stats.assume_init()
}

pub unsafe fn set_promise_hook(&mut self, hook: Option<PromiseHook>) {
unsafe extern "C" fn promise_hook_wrapper(
ctx: *mut rquickjs_sys::JSContext,
type_: u32,
promise: rquickjs_sys::JSValue,
parent: rquickjs_sys::JSValue,
opaque: *mut ::std::os::raw::c_void,
) {
let opaque = NonNull::new_unchecked(opaque).cast::<Opaque>();

let catch_unwind = panic::catch_unwind(AssertUnwindSafe(move || {
let ctx = Ctx::from_ptr(ctx);

let rtype = match type_ {
qjs::JSPromiseHookType_JS_PROMISE_HOOK_INIT => PromiseHookType::Init,
qjs::JSPromiseHookType_JS_PROMISE_HOOK_BEFORE => PromiseHookType::Before,
qjs::JSPromiseHookType_JS_PROMISE_HOOK_AFTER => PromiseHookType::After,
qjs::JSPromiseHookType_JS_PROMISE_HOOK_RESOLVE => PromiseHookType::Resolve,
_ => unreachable!(),
};

opaque.as_ref().run_promise_hook(
ctx.clone(),
rtype,
Value::from_js_value_const(ctx.clone(), promise),
Value::from_js_value_const(ctx, parent),
);
}));
match catch_unwind {
Ok(_) => {}
Err(panic) => {
opaque.as_ref().set_panic(panic);
}
}
}
qjs::JS_SetPromiseHook(
self.rt.as_ptr(),
hook.as_ref().map(|_| promise_hook_wrapper as _),
qjs::JS_GetRuntimeOpaque(self.rt.as_ptr()),
);
self.get_opaque().set_promise_hook(hook);
}

pub unsafe fn set_host_promise_rejection_tracker(&mut self, tracker: Option<RejectionTracker>) {
unsafe extern "C" fn rejection_tracker_wrapper(
ctx: *mut rquickjs_sys::JSContext,
Expand Down
2 changes: 1 addition & 1 deletion core/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ impl<'js> Value<'js> {
/// Check if the value is an array
#[inline]
pub fn is_array(&self) -> bool {
0 != unsafe { qjs::JS_IsArray(self.ctx.as_ptr(), self.value) }
unsafe { qjs::JS_IsArray(self.value) }
}

/// Check if the value is a function
Expand Down
9 changes: 9 additions & 0 deletions core/src/value/promise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ pub enum PromiseState {
Rejected,
}

/// The type of promise event.
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum PromiseHookType {
Init,
Before,
After,
Resolve,
}

/// A JavaScript promise.
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
#[repr(transparent)]
Expand Down
5 changes: 3 additions & 2 deletions sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ fn main() {
let out_dir = Path::new(&out_dir);

let header_files = [
"libbf.h",
"builtin-array-fromasync.h",
"xsum.h",
"libregexp-opcode.h",
"libregexp.h",
"libunicode-table.h",
Expand All @@ -139,7 +140,7 @@ fn main() {
"libunicode.c",
"cutils.c",
"quickjs.c",
"libbf.c",
"xsum.c",
];

let mut defines: Vec<(String, Option<&str>)> = vec![("_GNU_SOURCE".into(), None)];
Expand Down
2 changes: 1 addition & 1 deletion sys/quickjs
Submodule quickjs updated 59 files
+302 −85 .github/workflows/ci.yml
+12 −6 .github/workflows/release.yml
+1 −1 .github/workflows/tsan.yml
+2 −1 .gitignore
+70 −36 CMakeLists.txt
+4 −4 LICENSE
+32 −5 Makefile
+49 −0 amalgam.js
+489 −0 api-test.c
+113 −0 builtin-array-fromasync.h
+36 −0 builtin-array-fromasync.js
+178 −4 cutils.c
+52 −1 cutils.h
+10 −0 docs/docs/building.md
+4 −0 docs/docs/projects.md
+16 −0 docs/docs/stdlib.md
+3 −1 docs/docs/supported_platforms.md
+19 −0 examples/meson.build
+2 −2 fuzz.c
+27 −26 gen/function_source.c
+6 −6 gen/hello.c
+24 −24 gen/hello_module.c
+2,036 −2,036 gen/repl.c
+258 −253 gen/standalone.c
+19 −19 gen/test_fib.c
+0 −653 getopt_compat.h
+0 −134 interrupt-test.c
+0 −8,424 libbf.c
+0 −545 libbf.h
+30 −6 libregexp.c
+5 −0 libregexp.h
+521 −0 meson.build
+6 −0 meson_options.txt
+45 −39 qjs.c
+149 −56 qjsc.c
+1 −0 quickjs-atom.h
+491 −341 quickjs-libc.c
+3 −3 quickjs-libc.h
+1 −5 quickjs-opcode.h
+4,538 −3,034 quickjs.c
+311 −168 quickjs.h
+90 −124 run-test262.c
+2 −2 standalone.js
+1 −1 test262
+7 −2 test262.conf
+94 −1 test262_errors.txt
+26 −0 tests/bug858.js
+6 −0 tests/bug904.js
+7 −0 tests/bug988.js
+3 −0 tests/bug999.js
+8 −0 tests/destructured-export.js
+64 −5 tests/microbench.js
+5 −0 tests/str-pad-leak.js
+5 −4 tests/test_bjson.js
+17 −2 tests/test_builtin.js
+7 −6 tests/test_std.js
+1 −3 tests/test_worker.js
+1,122 −0 xsum.c
+133 −0 xsum.h
Loading
Loading