deft_quick_js/bindings/
mod.rs

1mod compile;
2//TODO no pub?
3pub mod convert;
4mod droppable_value;
5//TODO no pub?
6pub mod value;
7
8use std::{ffi::CString, os::raw::{c_int, c_void}, sync::Mutex};
9use std::any::Any;
10use std::cell::{Cell};
11use std::ptr::{null_mut};
12use std::rc::Rc;
13use anyhow::Context;
14use libquickjs_sys as q;
15use libquickjs_sys::{JS_EVAL_TYPE_MODULE, JSClassID, JSContext, JSValue};
16
17use crate::{callback::{Arguments, Callback}, console::ConsoleBackend, ContextError, ExecutionError, JsValue, ResourceValue, ValueError};
18
19use value::{JsFunction, OwnedJsObject};
20
21pub use value::{JsCompiledFunction, OwnedJsValue};
22use crate::bindings::convert::deserialize_value;
23use crate::exception::{HostPromiseRejectionTracker, HostPromiseRejectionTrackerWrapper};
24use crate::loader::{quickjs_rs_module_loader, JsModuleLoader};
25
26// JS_TAG_* constants from quickjs.
27// For some reason bindgen does not pick them up.
28#[cfg(feature = "bigint")]
29const TAG_BIG_INT: i64 = -10;
30const TAG_STRING: i64 = -7;
31const TAG_FUNCTION_BYTECODE: i64 = -2;
32const TAG_OBJECT: i64 = -1;
33const TAG_INT: i64 = 0;
34const TAG_BOOL: i64 = 1;
35const TAG_NULL: i64 = 2;
36const TAG_UNDEFINED: i64 = 3;
37pub const TAG_EXCEPTION: i64 = 6;
38const TAG_FLOAT64: i64 = 7;
39
40extern "C" fn host_promise_rejection_tracker(
41    ctx: *mut JSContext,
42    promise: JSValue,
43    reason: JSValue,
44    is_handled: bool,
45    opaque: *mut ::std::os::raw::c_void,
46) {
47    let promise =  deserialize_value(ctx, &promise).unwrap();
48    let reason = deserialize_value(ctx, &reason).unwrap();
49    let mut opaque = opaque as *mut HostPromiseRejectionTrackerWrapper;
50    unsafe {
51        (*opaque).tracker.track_promise_rejection(promise, reason, is_handled);
52    }
53}
54
55/// Helper for creating CStrings.
56pub fn make_cstring(value: impl Into<Vec<u8>>) -> Result<CString, ValueError> {
57    CString::new(value).map_err(ValueError::StringWithZeroBytes)
58}
59
60pub struct ClassId {
61    id: Cell<JSClassID>,
62}
63
64pub struct ResourceObject {
65    pub data: ResourceValue,
66}
67
68unsafe impl Send for ClassId {}
69unsafe impl Sync for ClassId {}
70
71impl ClassId {
72    pub const fn new() -> Self {
73        ClassId {
74            id: Cell::new(0)
75        }
76    }
77}
78
79trait JsClass {
80    const NAME: &'static str;
81
82    fn class_id() -> Rc<ClassId>;
83
84}
85
86thread_local! {
87    static CLASS_ID: Rc<ClassId> = Rc::new(ClassId::new());
88}
89
90struct Resource;
91
92impl JsClass for Resource {
93    const NAME: &'static str = "Resource";
94
95    fn class_id() -> Rc<ClassId> {
96        CLASS_ID.with(|c| c.clone())
97    }
98}
99
100type WrappedCallback = dyn Fn(c_int, *mut q::JSValue) -> q::JSValue;
101
102/// Taken from: https://s3.amazonaws.com/temp.michaelfbryan.com/callbacks/index.html
103///
104/// Create a C wrapper function for a Rust closure to enable using it as a
105/// callback function in the Quickjs runtime.
106///
107/// Both the boxed closure and the boxed data are returned and must be stored
108/// by the caller to guarantee they stay alive.
109unsafe fn build_closure_trampoline<F>(
110    closure: F,
111) -> ((Box<WrappedCallback>, Box<q::JSValue>), q::JSCFunctionData)
112where
113    F: Fn(c_int, *mut q::JSValue) -> q::JSValue + 'static,
114{
115    unsafe extern "C" fn trampoline<F>(
116        _ctx: *mut q::JSContext,
117        _this: q::JSValue,
118        argc: c_int,
119        argv: *mut q::JSValue,
120        _magic: c_int,
121        data: *mut q::JSValue,
122    ) -> q::JSValue
123    where
124        F: Fn(c_int, *mut q::JSValue) -> q::JSValue,
125    {
126        let closure_ptr = (*data).u.ptr;
127        let closure: &mut F = &mut *(closure_ptr as *mut F);
128        (*closure)(argc, argv)
129    }
130
131    let boxed_f = Box::new(closure);
132
133    let data = Box::new(q::JSValue {
134        u: q::JSValueUnion {
135            ptr: (&*boxed_f) as *const F as *mut c_void,
136        },
137        tag: TAG_NULL,
138    });
139
140    ((boxed_f, data), Some(trampoline::<F>))
141}
142
143/// OwnedValueRef wraps a Javascript value from the quickjs runtime.
144/// It prevents leaks by ensuring that the inner value is deallocated on drop.
145pub struct OwnedValueRef<'a> {
146    context: &'a ContextWrapper,
147    value: q::JSValue,
148}
149
150impl<'a> Drop for OwnedValueRef<'a> {
151    fn drop(&mut self) {
152        unsafe {
153            q::JS_FreeValue(self.context.context, self.value);
154        }
155    }
156}
157
158impl<'a> Clone for OwnedValueRef<'a> {
159    fn clone(&self) -> Self {
160        Self::new_dup(self.context, self.value)
161    }
162}
163
164impl<'a> std::fmt::Debug for OwnedValueRef<'a> {
165    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
166        match self.value.tag {
167            TAG_EXCEPTION => write!(f, "Exception(?)"),
168            TAG_NULL => write!(f, "NULL"),
169            TAG_UNDEFINED => write!(f, "UNDEFINED"),
170            TAG_BOOL => write!(f, "Bool(?)",),
171            TAG_INT => write!(f, "Int(?)"),
172            TAG_FLOAT64 => write!(f, "Float(?)"),
173            TAG_STRING => write!(f, "String(?)"),
174            TAG_OBJECT => write!(f, "Object(?)"),
175            TAG_FUNCTION_BYTECODE => write!(f, "Bytecode(?)"),
176            _ => write!(f, "?"),
177        }
178    }
179}
180
181impl<'a> OwnedValueRef<'a> {
182    pub fn new(context: &'a ContextWrapper, value: q::JSValue) -> Self {
183        Self { context, value }
184    }
185    pub fn new_dup(context: &'a ContextWrapper, value: q::JSValue) -> Self {
186        let ret = Self::new(context, value);
187        unsafe { q::JS_DupValue(ret.context.context, ret.value) };
188        ret
189    }
190
191    /// Get the inner JSValue without freeing in drop.
192    ///
193    /// Unsafe because the caller is responsible for freeing the returned value.
194    unsafe fn into_inner(self) -> q::JSValue {
195        let v = self.value;
196        std::mem::forget(self);
197        v
198    }
199
200    /// Get the inner JSValue without increasing ref count
201    pub(crate) fn as_inner(&self) -> &q::JSValue {
202        &self.value
203    }
204
205    /// Get the inner JSValue while increasing ref count, this is handy when you pass a JSValue to a new owner like e.g. setProperty
206    #[allow(dead_code)]
207    pub(crate) fn as_inner_dup(&self) -> &q::JSValue {
208        unsafe { q::JS_DupValue(self.context.context, self.value) };
209        &self.value
210    }
211
212    pub fn is_null(&self) -> bool {
213        self.value.tag == TAG_NULL
214    }
215
216    pub fn is_bool(&self) -> bool {
217        self.value.tag == TAG_BOOL
218    }
219
220    pub fn is_exception(&self) -> bool {
221        self.value.tag == TAG_EXCEPTION
222    }
223
224    pub fn is_object(&self) -> bool {
225        self.value.tag == TAG_OBJECT
226    }
227
228    pub fn is_string(&self) -> bool {
229        self.value.tag == TAG_STRING
230    }
231
232    pub fn is_compiled_function(&self) -> bool {
233        self.value.tag == TAG_FUNCTION_BYTECODE
234    }
235
236    pub fn to_string(&self) -> Result<String, ExecutionError> {
237        let value = if self.is_string() {
238            self.to_value()?
239        } else {
240            let raw = unsafe { q::JS_ToString(self.context.context, self.value) };
241            let value = OwnedValueRef::new(self.context, raw);
242
243            if value.value.tag != TAG_STRING {
244                return Err(ExecutionError::Exception(
245                    "Could not convert value to string".into(),
246                ));
247            }
248            value.to_value()?
249        };
250
251        Ok(value.as_str().unwrap().to_string())
252    }
253
254    pub fn to_value(&self) -> Result<JsValue, ValueError> {
255        self.context.to_value(&self.value)
256    }
257
258    pub fn to_bool(&self) -> Result<bool, ValueError> {
259        match self.to_value()? {
260            JsValue::Bool(b) => Ok(b),
261            _ => Err(ValueError::UnexpectedType),
262        }
263    }
264
265    #[cfg(test)]
266    pub fn get_ref_count(&self) -> i32 {
267        if self.value.tag < 0 {
268            // This transmute is OK since if tag < 0, the union will be a refcount
269            // pointer.
270            let ptr = unsafe { self.value.u.ptr as *mut q::JSRefCountHeader };
271            let pref: &mut q::JSRefCountHeader = &mut unsafe { *ptr };
272            pref.ref_count
273        } else {
274            -1
275        }
276    }
277}
278
279/// Wraps an object from the quickjs runtime.
280/// Provides convenience property accessors.
281pub struct OwnedObjectRef<'a> {
282    value: OwnedValueRef<'a>,
283}
284
285impl<'a> OwnedObjectRef<'a> {
286    pub fn new(value: OwnedValueRef<'a>) -> Result<Self, ValueError> {
287        if value.value.tag != TAG_OBJECT {
288            Err(ValueError::Internal("Expected an object".into()))
289        } else {
290            Ok(Self { value })
291        }
292    }
293
294    fn into_value(self) -> OwnedValueRef<'a> {
295        self.value
296    }
297
298    /// Get the tag of a property.
299    fn property_tag(&self, name: &str) -> Result<i64, ValueError> {
300        let cname = make_cstring(name)?;
301        let raw = unsafe {
302            q::JS_GetPropertyStr(self.value.context.context, self.value.value, cname.as_ptr())
303        };
304        let t = raw.tag;
305        unsafe {
306            q::JS_FreeValue(self.value.context.context, raw);
307        }
308        Ok(t)
309    }
310
311    /// Determine if the object is a promise by checking the presence of
312    /// a 'then' and a 'catch' property.
313    fn is_promise(&self) -> Result<bool, ValueError> {
314        if self.property_tag("then")? == TAG_OBJECT && self.property_tag("catch")? == TAG_OBJECT {
315            Ok(true)
316        } else {
317            Ok(false)
318        }
319    }
320
321    pub fn property(&self, name: &str) -> Result<OwnedValueRef<'a>, ExecutionError> {
322        let cname = make_cstring(name)?;
323        let raw = unsafe {
324            q::JS_GetPropertyStr(self.value.context.context, self.value.value, cname.as_ptr())
325        };
326
327        if raw.tag == TAG_EXCEPTION {
328            Err(ExecutionError::Internal(format!(
329                "Exception while getting property '{}'",
330                name
331            )))
332        } else if raw.tag == TAG_UNDEFINED {
333            Err(ExecutionError::Internal(format!(
334                "Property '{}' not found",
335                name
336            )))
337        } else {
338            Ok(OwnedValueRef::new(self.value.context, raw))
339        }
340    }
341
342    // Set a property on an object.
343    // NOTE: this method takes ownership of the `JSValue`, so it must not be
344    // freed later.
345    unsafe fn set_property_raw(&self, name: &str, value: q::JSValue) -> Result<(), ExecutionError> {
346        let cname = make_cstring(name)?;
347        let ret = q::JS_SetPropertyStr(
348            self.value.context.context,
349            self.value.value,
350            cname.as_ptr(),
351            value,
352        );
353        if ret < 0 {
354            Err(ExecutionError::Exception("Could not set property".into()))
355        } else {
356            Ok(())
357        }
358    }
359
360    pub fn set_property(&self, name: &str, value: JsValue) -> Result<(), ExecutionError> {
361        let qval = self.value.context.serialize_value(value)?;
362        unsafe {
363            // set_property_raw takes ownership, so we must prevent a free.
364            self.set_property_raw(name, qval.extract())?;
365        }
366        Ok(())
367    }
368}
369
370/*
371type ModuleInit = dyn Fn(*mut q::JSContext, *mut q::JSModuleDef);
372
373thread_local! {
374    static NATIVE_MODULE_INIT: RefCell<Option<Box<ModuleInit>>> = RefCell::new(None);
375}
376
377unsafe extern "C" fn native_module_init(
378    ctx: *mut q::JSContext,
379    m: *mut q::JSModuleDef,
380) -> ::std::os::raw::c_int {
381    NATIVE_MODULE_INIT.with(|init| {
382        let init = init.replace(None).unwrap();
383        init(ctx, m);
384    });
385    0
386}
387*/
388
389/// Wraps a quickjs context.
390///
391/// Cleanup of the context happens in drop.
392pub struct ContextWrapper {
393    runtime: *mut q::JSRuntime,
394    pub(crate) context: *mut q::JSContext,
395    /// Stores callback closures and quickjs data pointers.
396    /// This array is write-only and only exists to ensure the lifetime of
397    /// the closure.
398    // A Mutex is used over a RefCell because it needs to be unwind-safe.
399    callbacks: Mutex<Vec<(Box<WrappedCallback>, Box<q::JSValue>)>>,
400    module_loader: Option<*mut Box<dyn JsModuleLoader>>,
401    host_promise_rejection_tracker_wrapper: Option<*mut HostPromiseRejectionTrackerWrapper>,
402}
403
404impl Drop for ContextWrapper {
405    fn drop(&mut self) {
406        unsafe {
407            {
408                if let Some(p) = self.host_promise_rejection_tracker_wrapper {
409                    let _ = Box::from_raw(p);
410                }
411            }
412            q::JS_FreeContext(self.context);
413            q::JS_FreeRuntime(self.runtime);
414        }
415    }
416}
417
418impl ContextWrapper {
419    /// Initialize a wrapper by creating a JSRuntime and JSContext.
420    pub fn new(memory_limit: Option<usize>) -> Result<Self, ContextError> {
421        let runtime = unsafe { q::JS_NewRuntime() };
422        if runtime.is_null() {
423            return Err(ContextError::RuntimeCreationFailed);
424        }
425
426        // Configure memory limit if specified.
427        if let Some(limit) = memory_limit {
428            unsafe {
429                q::JS_SetMemoryLimit(runtime, limit as _);
430            }
431        }
432
433        unsafe  {
434            //js_std_set_worker_new_context_func(JS_NewCustomContext);
435            //js_std_init_handlers(runtime);
436        }
437
438        let context = unsafe { q::JS_NewContext(runtime) };
439        if context.is_null() {
440            unsafe {
441                q::JS_FreeRuntime(runtime);
442            }
443            return Err(ContextError::ContextCreationFailed);
444        }
445
446        // Initialize the promise resolver helper code.
447        // This code is needed by Self::resolve_value
448        let wrapper = Self {
449            runtime,
450            context,
451            callbacks: Mutex::new(Vec::new()),
452            module_loader: None,
453            host_promise_rejection_tracker_wrapper: None,
454        };
455
456        Ok(wrapper)
457    }
458
459    pub fn set_host_promise_rejection_tracker<F: HostPromiseRejectionTracker + 'static>(&mut self, tracker: F) {
460        let tracker = HostPromiseRejectionTrackerWrapper::new(Box::new(tracker));
461        let ptr = Box::into_raw(Box::new(tracker));
462        self.host_promise_rejection_tracker_wrapper = Some(ptr);
463        unsafe {
464            q::JS_SetHostPromiseRejectionTracker(self.runtime, Some(host_promise_rejection_tracker), ptr as _);
465        }
466    }
467
468    pub fn set_module_loader(&mut self, module_loader: Box<dyn JsModuleLoader>) {
469        let module_loader= Box::new(module_loader);
470        unsafe {
471            let module_loader = Box::into_raw(module_loader);
472            self.module_loader = Some(module_loader);
473            q::JS_SetModuleLoaderFunc(self.runtime, None, Some(quickjs_rs_module_loader), module_loader as *mut c_void);
474        }
475    }
476
477    // See console standard: https://console.spec.whatwg.org
478    pub fn set_console(&self, backend: Box<dyn ConsoleBackend>) -> Result<(), ExecutionError> {
479        use crate::console::Level;
480
481        self.add_callback("__console_write", move |args: Arguments| {
482            let mut args = args.into_vec();
483
484            if args.len() > 1 {
485                let level_raw = args.remove(0);
486
487                let level_opt = level_raw.as_str().and_then(|v| match v {
488                    "trace" => Some(Level::Trace),
489                    "debug" => Some(Level::Debug),
490                    "log" => Some(Level::Log),
491                    "info" => Some(Level::Info),
492                    "warn" => Some(Level::Warn),
493                    "error" => Some(Level::Error),
494                    _ => None,
495                });
496
497                if let Some(level) = level_opt {
498                    backend.log(level, args);
499                }
500            }
501        })?;
502
503        Ok(())
504    }
505
506    /// Reset the wrapper by creating a new context.
507    pub fn reset(self) -> Result<Self, ContextError> {
508        unsafe {
509            q::JS_FreeContext(self.context);
510        };
511        self.callbacks.lock().unwrap().clear();
512        let context = unsafe { q::JS_NewContext(self.runtime) };
513        if context.is_null() {
514            return Err(ContextError::ContextCreationFailed);
515        }
516
517        let mut s = self;
518        s.context = context;
519        Ok(s)
520    }
521
522    pub fn serialize_value(&self, value: JsValue) -> Result<OwnedJsValue<'_>, ExecutionError> {
523        let serialized = convert::serialize_value(self.context, value)?;
524        Ok(OwnedJsValue::new(self, serialized))
525    }
526
527    // Deserialize a quickjs runtime value into a Rust value.
528    pub(crate) fn to_value(&self, value: &q::JSValue) -> Result<JsValue, ValueError> {
529        convert::deserialize_value(self.context, value)
530    }
531
532    /// Get the global object.
533    pub fn global(&self) -> Result<OwnedJsObject<'_>, ExecutionError> {
534        let global_raw = unsafe { q::JS_GetGlobalObject(self.context) };
535        let global_ref = OwnedJsValue::new(self, global_raw);
536        let global = global_ref.try_into_object()?;
537        Ok(global)
538    }
539
540    /// Get the last exception from the runtime, and if present, convert it to a ExceptionError.
541    pub(crate) fn get_exception(&self) -> Option<ExecutionError> {
542        let value = unsafe {
543            let raw = q::JS_GetException(self.context);
544            OwnedJsValue::new(self, raw)
545        };
546
547        if value.is_null() {
548            None
549        } else if value.is_exception() {
550            Some(ExecutionError::Internal(
551                "Could get exception from runtime".into(),
552            ))
553        } else {
554            match value.js_to_string() {
555                Ok(strval) => {
556                    if strval.contains("out of memory") {
557                        Some(ExecutionError::OutOfMemory)
558                    } else {
559                        Some(ExecutionError::Exception(JsValue::String(strval)))
560                    }
561                }
562                Err(e) => Some(e),
563            }
564        }
565    }
566
567    /// Returns `Result::Err` when an error ocurred.
568    pub(crate) fn ensure_no_excpetion(&self) -> Result<(), ExecutionError> {
569        if let Some(e) = self.get_exception() {
570            Err(e)
571        } else {
572            Ok(())
573        }
574    }
575
576    /// If the given value is a promise, run the event loop until it is
577    /// resolved, and return the final value.
578    fn resolve_value<'a>(
579        &'a self,
580        value: OwnedJsValue<'a>,
581    ) -> Result<OwnedJsValue<'a>, ExecutionError> {
582        if value.is_exception() {
583            unsafe {
584                //TODO remove
585                // js_std_dump_error(self.context);
586            }
587            let err = self
588                .get_exception()
589                .unwrap_or_else(|| ExecutionError::Exception("Unknown exception".into()));
590            Err(err)
591        } else if value.is_object() {
592            let obj = value.try_into_object()?;
593            Ok(obj.into_value())
594        } else {
595            Ok(value)
596        }
597    }
598
599    /// Evaluate javascript code.
600    pub fn eval<'a>(&'a self, code: &str, eval_type: u32, filename: &str) -> Result<OwnedJsValue<'a>, ExecutionError> {
601        let filename_c = make_cstring(filename)?;
602        let code_c = make_cstring(code)?;
603
604        let value_raw = unsafe {
605            q::JS_Eval(
606                self.context,
607                code_c.as_ptr(),
608                code.len() as _,
609                filename_c.as_ptr(),
610                eval_type as i32,
611            )
612        };
613        let value = OwnedJsValue::new(self, value_raw);
614        self.resolve_value(value)
615    }
616
617    /*
618    /// Call a constructor function.
619    fn call_constructor<'a>(
620        &'a self,
621        function: OwnedJsValue<'a>,
622        args: Vec<OwnedJsValue<'a>>,
623    ) -> Result<OwnedJsValue<'a>, ExecutionError> {
624        let mut qargs = args.iter().map(|arg| arg.value).collect::<Vec<_>>();
625
626        let value_raw = unsafe {
627            q::JS_CallConstructor(
628                self.context,
629                function.value,
630                qargs.len() as i32,
631                qargs.as_mut_ptr(),
632            )
633        };
634        let value = OwnedJsValue::new(self, value_raw);
635        if value.is_exception() {
636            let err = self
637                .get_exception()
638                .unwrap_or_else(|| ExecutionError::Exception("Unknown exception".into()));
639            Err(err)
640        } else {
641            Ok(value)
642        }
643    }
644    */
645
646    /// Call a JS function with the given arguments.
647    pub fn call_function<'a>(
648        &'a self,
649        function: JsFunction<'a>,
650        args: Vec<OwnedJsValue<'a>>,
651    ) -> Result<OwnedJsValue<'a>, ExecutionError> {
652        let ret = function.call(args)?;
653        self.resolve_value(ret)
654    }
655
656    /// Helper for executing a callback closure.
657    fn exec_callback<F>(
658        context: *mut q::JSContext,
659        argc: c_int,
660        argv: *mut q::JSValue,
661        callback: &impl Callback<F>,
662    ) -> Result<q::JSValue, ExecutionError> {
663        let result = std::panic::catch_unwind(|| {
664            let arg_slice = unsafe { std::slice::from_raw_parts(argv, argc as usize) };
665
666            let mut args = Vec::with_capacity(arg_slice.len());
667            for a in arg_slice {
668                let a = deserialize_value(context, a)
669                    .map_err(|e| {
670                        ExecutionError::Internal(
671                            format!("failed to deserialize arguments {} (zero-based) to JS value, {}", args.len(), e)
672                        )
673                    })?;
674                args.push(a);
675            }
676
677            match callback.call(args) {
678                Ok(Ok(result)) => {
679                    let serialized = convert::serialize_value(context, result)
680                        .map_err(|e| {
681                            ExecutionError::Internal(format!("failed to serialize rust value to js value, {}", e))
682                        })?;
683                    Ok(serialized)
684                }
685                // TODO: better error reporting.
686                Ok(Err(e)) => Err(ExecutionError::Exception(JsValue::String(e))),
687                Err(e) => Err(e.into()),
688            }
689        });
690
691        match result {
692            Ok(r) => r,
693            Err(_e) => Err(ExecutionError::Internal("Callback panicked!".to_string())),
694        }
695    }
696
697    /// Add a global JS function that is backed by a Rust function or closure.
698    pub fn create_callback<'a, F>(
699        &'a self,
700        name: &str,
701        callback: impl Callback<F> + 'static,
702    ) -> Result<JsFunction<'a>, ExecutionError> {
703        let argcount = callback.argument_count() as i32;
704
705        let context = self.context;
706        let name = name.to_string();
707        let wrapper = move |argc: c_int, argv: *mut q::JSValue| -> q::JSValue {
708            match Self::exec_callback(context, argc, argv, &callback) {
709                Ok(value) => value,
710                // TODO: better error reporting.
711                Err(e) => {
712                    let js_exception_value = match e {
713                        ExecutionError::Exception(e) => e,
714                        other => format!("Failed to call [{}], {}", &name,  other.to_string()).into(),
715                    };
716                    let js_exception =
717                        convert::serialize_value(context, js_exception_value).unwrap();
718                    unsafe {
719                        q::JS_Throw(context, js_exception);
720                    }
721
722                    q::JSValue {
723                        u: q::JSValueUnion { int32: 0 },
724                        tag: TAG_EXCEPTION,
725                    }
726                }
727            }
728        };
729
730        let (pair, trampoline) = unsafe { build_closure_trampoline(wrapper) };
731        let data = (&*pair.1) as *const q::JSValue as *mut q::JSValue;
732        self.callbacks.lock().unwrap().push(pair);
733
734        let obj = unsafe {
735            let f = q::JS_NewCFunctionData(self.context, trampoline, argcount, 0, 1, data);
736            OwnedJsValue::new(self, f)
737        };
738
739        let f = obj.try_into_function()?;
740        Ok(f)
741    }
742
743    pub fn add_callback<'a, F>(
744        &'a self,
745        name: &str,
746        callback: impl Callback<F> + 'static,
747    ) -> Result<(), ExecutionError> {
748        let cfunc = self.create_callback(name, callback)?;
749        let global = self.global()?;
750        global.set_property(name, cfunc.into_value())?;
751        Ok(())
752    }
753
754    /// return Ok(false) if no job pending, Ok(true) if a job was executed successfully.
755    pub fn execute_pending_job(&self) -> Result<bool, ExecutionError> {
756        let mut job_ctx = null_mut();
757        let flag = unsafe {
758            q::JS_ExecutePendingJob(self.runtime, &mut job_ctx)
759        };
760        if flag < 0 {
761            //FIXME should get exception from job_ctx
762            let e = self.get_exception().unwrap_or_else(|| {
763                ExecutionError::Exception("Unknown exception".into())
764            });
765            return Err(e);
766        }
767        Ok(flag != 0)
768    }
769
770    pub fn execute_module(&self, module_name: &str) -> Result<(), ExecutionError> {
771        if let Some(ml) = self.module_loader {
772            unsafe {
773                let loader = &mut *ml;
774                let module = loader.load(module_name).map_err(|e| ExecutionError::Internal(format!("Fail to load module:{}", e)))?;
775                self.eval(&module, JS_EVAL_TYPE_MODULE, module_name)?;
776                Ok(())
777            }
778        } else {
779            Err(ExecutionError::Internal("Module loader is not set".to_string()))
780        }
781    }
782
783}