Skip to content

Commit 271a718

Browse files
authored
Add PromiseHook bindings (#453)
* Add PromiseHook bindings * squash! back to quickjs-ng * squash! update x86_64-apple-darwin.rs * squash! update x86_64-pc-windows-gnu.rs * squash! update aarch64-apple-darwin.rs
1 parent 3394aa3 commit 271a718

File tree

13 files changed

+1164
-420
lines changed

13 files changed

+1164
-420
lines changed

core/src/runtime.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,18 @@ pub(crate) use r#async::InnerRuntime;
2020
#[cfg(feature = "futures")]
2121
pub use r#async::{AsyncRuntime, AsyncWeakRuntime};
2222

23+
use crate::value::promise::PromiseHookType;
2324
use crate::{Ctx, Value};
2425

26+
/// The type of the promise hook.
27+
#[cfg(not(feature = "parallel"))]
28+
pub type PromiseHook =
29+
Box<dyn for<'a> Fn(Ctx<'a>, PromiseHookType, Value<'a>, Value<'a>) + 'static>;
30+
/// The type of the promise hook.
31+
#[cfg(feature = "parallel")]
32+
pub type PromiseHook =
33+
Box<dyn for<'a> Fn(Ctx<'a>, PromiseHookType, Value<'a>, Value<'a>) + Send + 'static>;
34+
2535
/// The type of the promise rejection tracker.
2636
#[cfg(not(feature = "parallel"))]
2737
pub type RejectionTracker = Box<dyn for<'a> Fn(Ctx<'a>, Value<'a>, Value<'a>, bool) + 'static>;

core/src/runtime/async.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use async_lock::Mutex;
1313

1414
use super::{
1515
opaque::Opaque, raw::RawRuntime, schedular::SchedularPoll, spawner::DriveFuture,
16-
InterruptHandler, MemoryUsage,
16+
InterruptHandler, MemoryUsage, PromiseHook,
1717
};
1818
use crate::allocator::Allocator;
1919
#[cfg(feature = "loader")]
@@ -160,6 +160,14 @@ impl AsyncRuntime {
160160
}
161161
}
162162

163+
/// Set a closure which is called when a promise is created, resolved, or chained.
164+
#[inline]
165+
pub async fn set_promise_hook(&self, tracker: Option<PromiseHook>) {
166+
unsafe {
167+
self.inner.lock().await.runtime.set_promise_hook(tracker);
168+
}
169+
}
170+
163171
/// Set a closure which is regularly called by the engine when it is executing code.
164172
/// If the provided closure returns `true` the interpreter will raise and uncatchable
165173
/// exception and return control flow to the caller.

core/src/runtime/base.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! QuickJS runtime related types.
22
3-
use super::{opaque::Opaque, raw::RawRuntime, InterruptHandler, MemoryUsage, RejectionTracker};
3+
use super::{
4+
opaque::Opaque, raw::RawRuntime, InterruptHandler, MemoryUsage, PromiseHook, RejectionTracker,
5+
};
46
use crate::allocator::Allocator;
57
#[cfg(feature = "loader")]
68
use crate::loader::{Loader, Resolver};
@@ -61,6 +63,14 @@ impl Runtime {
6163
WeakRuntime(Ref::downgrade(&self.inner))
6264
}
6365

66+
/// Set a closure which is called when a promise is created, resolved, or chained.
67+
#[inline]
68+
pub fn set_promise_hook(&self, tracker: Option<PromiseHook>) {
69+
unsafe {
70+
self.inner.lock().set_promise_hook(tracker);
71+
}
72+
}
73+
6474
/// Set a closure which is called when a Promise is rejected.
6575
#[inline]
6676
pub fn set_host_promise_rejection_tracker(&self, tracker: Option<RejectionTracker>) {

core/src/runtime/opaque.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55

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

32+
/// The user provided promise hook, if any.
33+
promise_hook: UnsafeCell<Option<PromiseHook>>,
34+
3235
/// The user provided rejection tracker, if any.
3336
rejection_tracker: UnsafeCell<Option<RejectionTracker>>,
3437

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

61+
promise_hook: UnsafeCell::new(None),
62+
5863
rejection_tracker: UnsafeCell::new(None),
5964

6065
interrupt_handler: UnsafeCell::new(None),
@@ -169,6 +174,20 @@ impl<'js> Opaque<'js> {
169174
self.userdata.get()
170175
}
171176

177+
pub fn set_promise_hook(&self, promise_hook: Option<PromiseHook>) {
178+
unsafe { (*self.promise_hook.get()) = promise_hook }
179+
}
180+
181+
pub fn run_promise_hook<'a>(
182+
&self,
183+
ctx: Ctx<'a>,
184+
type_: PromiseHookType,
185+
promise: Value<'a>,
186+
parent: Value<'a>,
187+
) {
188+
unsafe { (*self.promise_hook.get()).as_mut().unwrap()(ctx, type_, promise, parent) }
189+
}
190+
172191
pub fn set_rejection_tracker(&self, tracker: Option<RejectionTracker>) {
173192
unsafe { (*self.rejection_tracker.get()) = tracker }
174193
}

core/src/runtime/raw.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{
1515
Ctx, Error, Result, Value,
1616
};
1717

18-
use super::{opaque::Opaque, InterruptHandler, RejectionTracker};
18+
use super::{opaque::Opaque, InterruptHandler, PromiseHook, PromiseHookType, RejectionTracker};
1919

2020
const DUMP_BYTECODE_FINAL: u64 = 0x01;
2121
const DUMP_BYTECODE_PASS2: u64 = 0x02;
@@ -286,6 +286,49 @@ impl RawRuntime {
286286
stats.assume_init()
287287
}
288288

289+
pub unsafe fn set_promise_hook(&mut self, hook: Option<PromiseHook>) {
290+
unsafe extern "C" fn promise_hook_wrapper(
291+
ctx: *mut rquickjs_sys::JSContext,
292+
type_: u32,
293+
promise: rquickjs_sys::JSValue,
294+
parent: rquickjs_sys::JSValue,
295+
opaque: *mut ::std::os::raw::c_void,
296+
) {
297+
let opaque = NonNull::new_unchecked(opaque).cast::<Opaque>();
298+
299+
let catch_unwind = panic::catch_unwind(AssertUnwindSafe(move || {
300+
let ctx = Ctx::from_ptr(ctx);
301+
302+
let rtype = match type_ {
303+
qjs::JSPromiseHookType_JS_PROMISE_HOOK_INIT => PromiseHookType::Init,
304+
qjs::JSPromiseHookType_JS_PROMISE_HOOK_BEFORE => PromiseHookType::Before,
305+
qjs::JSPromiseHookType_JS_PROMISE_HOOK_AFTER => PromiseHookType::After,
306+
qjs::JSPromiseHookType_JS_PROMISE_HOOK_RESOLVE => PromiseHookType::Resolve,
307+
_ => unreachable!(),
308+
};
309+
310+
opaque.as_ref().run_promise_hook(
311+
ctx.clone(),
312+
rtype,
313+
Value::from_js_value_const(ctx.clone(), promise),
314+
Value::from_js_value_const(ctx, parent),
315+
);
316+
}));
317+
match catch_unwind {
318+
Ok(_) => {}
319+
Err(panic) => {
320+
opaque.as_ref().set_panic(panic);
321+
}
322+
}
323+
}
324+
qjs::JS_SetPromiseHook(
325+
self.rt.as_ptr(),
326+
hook.as_ref().map(|_| promise_hook_wrapper as _),
327+
qjs::JS_GetRuntimeOpaque(self.rt.as_ptr()),
328+
);
329+
self.get_opaque().set_promise_hook(hook);
330+
}
331+
289332
pub unsafe fn set_host_promise_rejection_tracker(&mut self, tracker: Option<RejectionTracker>) {
290333
unsafe extern "C" fn rejection_tracker_wrapper(
291334
ctx: *mut rquickjs_sys::JSContext,

core/src/value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ impl<'js> Value<'js> {
342342
/// Check if the value is an array
343343
#[inline]
344344
pub fn is_array(&self) -> bool {
345-
0 != unsafe { qjs::JS_IsArray(self.ctx.as_ptr(), self.value) }
345+
unsafe { qjs::JS_IsArray(self.value) }
346346
}
347347

348348
/// Check if the value is a function

core/src/value/promise.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ pub enum PromiseState {
2525
Rejected,
2626
}
2727

28+
/// The type of promise event.
29+
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
30+
pub enum PromiseHookType {
31+
Init,
32+
Before,
33+
After,
34+
Resolve,
35+
}
36+
2837
/// A JavaScript promise.
2938
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
3039
#[repr(transparent)]

sys/build.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ fn main() {
121121
let out_dir = Path::new(&out_dir);
122122

123123
let header_files = [
124-
"libbf.h",
124+
"builtin-array-fromasync.h",
125+
"xsum.h",
125126
"libregexp-opcode.h",
126127
"libregexp.h",
127128
"libunicode-table.h",
@@ -139,7 +140,7 @@ fn main() {
139140
"libunicode.c",
140141
"cutils.c",
141142
"quickjs.c",
142-
"libbf.c",
143+
"xsum.c",
143144
];
144145

145146
let mut defines: Vec<(String, Option<&str>)> = vec![("_GNU_SOURCE".into(), None)];

0 commit comments

Comments
 (0)