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, JS_VALUE_GET_PTR};
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 = JS_VALUE_GET_PTR(*data);
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(
134        q::JS_MKPTR(q::JS_TAG_NULL, (&*boxed_f) as *const F as *mut c_void)
135    );
136
137    ((boxed_f, data), Some(trampoline::<F>))
138}
139
140/// OwnedValueRef wraps a Javascript value from the quickjs runtime.
141/// It prevents leaks by ensuring that the inner value is deallocated on drop.
142pub struct OwnedValueRef<'a> {
143    context: &'a ContextWrapper,
144    value: q::JSValue,
145}
146
147impl<'a> Drop for OwnedValueRef<'a> {
148    fn drop(&mut self) {
149        unsafe {
150            q::JS_FreeValue(self.context.context, self.value);
151        }
152    }
153}
154
155impl<'a> Clone for OwnedValueRef<'a> {
156    fn clone(&self) -> Self {
157        Self::new_dup(self.context, self.value)
158    }
159}
160
161impl<'a> std::fmt::Debug for OwnedValueRef<'a> {
162    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
163        unsafe {
164            match q::JS_VALUE_GET_TAG(self.value) {
165                q::JS_TAG_EXCEPTION => write!(f, "Exception(?)"),
166                q::JS_TAG_NULL => write!(f, "NULL"),
167                q::JS_TAG_UNDEFINED => write!(f, "UNDEFINED"),
168                q::JS_TAG_BOOL => write!(f, "Bool(?)",),
169                q::JS_TAG_INT => write!(f, "Int(?)"),
170                q::JS_TAG_FLOAT64 => write!(f, "Float(?)"),
171                q::JS_TAG_STRING => write!(f, "String(?)"),
172                q::JS_TAG_OBJECT => write!(f, "Object(?)"),
173                q::JS_TAG_FUNCTION_BYTECODE => write!(f, "Bytecode(?)"),
174                _ => write!(f, "?"),
175            }
176        }
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        q::JS_IsNull(self.value)
214    }
215
216    pub fn is_bool(&self) -> bool {
217        q::JS_IsBool(self.value)
218    }
219
220    pub fn is_exception(&self) -> bool {
221        q::JS_IsException(self.value)
222    }
223
224    pub fn is_object(&self) -> bool {
225        q::JS_IsObject(self.value)
226    }
227
228    pub fn is_string(&self) -> bool {
229        q::JS_IsString(self.value)
230    }
231
232    pub fn is_compiled_function(&self) -> bool {
233        q::JS_VALUE_GET_TAG(self.value) == q::JS_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.is_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 q::JS_VALUE_GET_TAG(self.value) < 0 {
268            // This transmute is OK since if tag < 0, the union will be a refcount
269            // pointer.
270            let ptr = unsafe { q::JS_VALUE_GET_PTR(self.value) 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.is_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 = unsafe {
305            q::JS_VALUE_GET_TAG(raw)
306        };
307        unsafe {
308            q::JS_FreeValue(self.value.context.context, raw);
309        }
310        Ok(t as i64)
311    }
312
313    /// Determine if the object is a promise by checking the presence of
314    /// a 'then' and a 'catch' property.
315    fn is_promise(&self) -> Result<bool, ValueError> {
316        if self.property_tag("then")? == TAG_OBJECT && self.property_tag("catch")? == TAG_OBJECT {
317            Ok(true)
318        } else {
319            Ok(false)
320        }
321    }
322
323    pub fn property(&self, name: &str) -> Result<OwnedValueRef<'a>, ExecutionError> {
324        let cname = make_cstring(name)?;
325        let raw = unsafe {
326            q::JS_GetPropertyStr(self.value.context.context, self.value.value, cname.as_ptr())
327        };
328
329        if q::JS_IsException(raw) {
330            Err(ExecutionError::Internal(format!(
331                "Exception while getting property '{}'",
332                name
333            )))
334        } else if q::JS_IsUndefined(raw) {
335            Err(ExecutionError::Internal(format!(
336                "Property '{}' not found",
337                name
338            )))
339        } else {
340            Ok(OwnedValueRef::new(self.value.context, raw))
341        }
342    }
343
344    // Set a property on an object.
345    // NOTE: this method takes ownership of the `JSValue`, so it must not be
346    // freed later.
347    unsafe fn set_property_raw(&self, name: &str, value: q::JSValue) -> Result<(), ExecutionError> {
348        let cname = make_cstring(name)?;
349        let ret = q::JS_SetPropertyStr(
350            self.value.context.context,
351            self.value.value,
352            cname.as_ptr(),
353            value,
354        );
355        if ret < 0 {
356            Err(ExecutionError::Exception("Could not set property".into()))
357        } else {
358            Ok(())
359        }
360    }
361
362    pub fn set_property(&self, name: &str, value: JsValue) -> Result<(), ExecutionError> {
363        let qval = self.value.context.serialize_value(value)?;
364        unsafe {
365            // set_property_raw takes ownership, so we must prevent a free.
366            self.set_property_raw(name, qval.extract())?;
367        }
368        Ok(())
369    }
370}
371
372/*
373type ModuleInit = dyn Fn(*mut q::JSContext, *mut q::JSModuleDef);
374
375thread_local! {
376    static NATIVE_MODULE_INIT: RefCell<Option<Box<ModuleInit>>> = RefCell::new(None);
377}
378
379unsafe extern "C" fn native_module_init(
380    ctx: *mut q::JSContext,
381    m: *mut q::JSModuleDef,
382) -> ::std::os::raw::c_int {
383    NATIVE_MODULE_INIT.with(|init| {
384        let init = init.replace(None).unwrap();
385        init(ctx, m);
386    });
387    0
388}
389*/
390
391/// Wraps a quickjs context.
392///
393/// Cleanup of the context happens in drop.
394pub struct ContextWrapper {
395    runtime: *mut q::JSRuntime,
396    pub(crate) context: *mut q::JSContext,
397    /// Stores callback closures and quickjs data pointers.
398    /// This array is write-only and only exists to ensure the lifetime of
399    /// the closure.
400    // A Mutex is used over a RefCell because it needs to be unwind-safe.
401    callbacks: Mutex<Vec<(Box<WrappedCallback>, Box<q::JSValue>)>>,
402    module_loader: Option<*mut Box<dyn JsModuleLoader>>,
403    host_promise_rejection_tracker_wrapper: Option<*mut HostPromiseRejectionTrackerWrapper>,
404}
405
406impl Drop for ContextWrapper {
407    fn drop(&mut self) {
408        unsafe {
409            {
410                if let Some(p) = self.host_promise_rejection_tracker_wrapper {
411                    let _ = Box::from_raw(p);
412                }
413            }
414            q::JS_FreeContext(self.context);
415            q::JS_FreeRuntime(self.runtime);
416        }
417    }
418}
419
420impl ContextWrapper {
421    /// Initialize a wrapper by creating a JSRuntime and JSContext.
422    pub fn new(memory_limit: Option<usize>) -> Result<Self, ContextError> {
423        let runtime = unsafe { q::JS_NewRuntime() };
424        if runtime.is_null() {
425            return Err(ContextError::RuntimeCreationFailed);
426        }
427
428        // Configure memory limit if specified.
429        if let Some(limit) = memory_limit {
430            unsafe {
431                q::JS_SetMemoryLimit(runtime, limit as _);
432            }
433        }
434
435        unsafe  {
436            //js_std_set_worker_new_context_func(JS_NewCustomContext);
437            //js_std_init_handlers(runtime);
438        }
439
440        let context = unsafe { q::JS_NewContext(runtime) };
441        if context.is_null() {
442            unsafe {
443                q::JS_FreeRuntime(runtime);
444            }
445            return Err(ContextError::ContextCreationFailed);
446        }
447
448        // Initialize the promise resolver helper code.
449        // This code is needed by Self::resolve_value
450        let wrapper = Self {
451            runtime,
452            context,
453            callbacks: Mutex::new(Vec::new()),
454            module_loader: None,
455            host_promise_rejection_tracker_wrapper: None,
456        };
457
458        Ok(wrapper)
459    }
460
461    pub fn set_host_promise_rejection_tracker<F: HostPromiseRejectionTracker + 'static>(&mut self, tracker: F) {
462        let tracker = HostPromiseRejectionTrackerWrapper::new(Box::new(tracker));
463        let ptr = Box::into_raw(Box::new(tracker));
464        self.host_promise_rejection_tracker_wrapper = Some(ptr);
465        unsafe {
466            q::JS_SetHostPromiseRejectionTracker(self.runtime, Some(host_promise_rejection_tracker), ptr as _);
467        }
468    }
469
470    pub fn set_module_loader(&mut self, module_loader: Box<dyn JsModuleLoader>) {
471        let module_loader= Box::new(module_loader);
472        unsafe {
473            let module_loader = Box::into_raw(module_loader);
474            self.module_loader = Some(module_loader);
475            q::JS_SetModuleLoaderFunc(self.runtime, None, Some(quickjs_rs_module_loader), module_loader as *mut c_void);
476        }
477    }
478
479    // See console standard: https://console.spec.whatwg.org
480    pub fn set_console(&self, backend: Box<dyn ConsoleBackend>) -> Result<(), ExecutionError> {
481        use crate::console::Level;
482
483        self.add_callback("__console_write", move |args: Arguments| {
484            let mut args = args.into_vec();
485
486            if args.len() > 1 {
487                let level_raw = args.remove(0);
488
489                let level_opt = level_raw.as_str().and_then(|v| match v {
490                    "trace" => Some(Level::Trace),
491                    "debug" => Some(Level::Debug),
492                    "log" => Some(Level::Log),
493                    "info" => Some(Level::Info),
494                    "warn" => Some(Level::Warn),
495                    "error" => Some(Level::Error),
496                    _ => None,
497                });
498
499                if let Some(level) = level_opt {
500                    backend.log(level, args);
501                }
502            }
503        })?;
504
505        Ok(())
506    }
507
508    /// Reset the wrapper by creating a new context.
509    pub fn reset(self) -> Result<Self, ContextError> {
510        unsafe {
511            q::JS_FreeContext(self.context);
512        };
513        self.callbacks.lock().unwrap().clear();
514        let context = unsafe { q::JS_NewContext(self.runtime) };
515        if context.is_null() {
516            return Err(ContextError::ContextCreationFailed);
517        }
518
519        let mut s = self;
520        s.context = context;
521        Ok(s)
522    }
523
524    pub fn serialize_value(&self, value: JsValue) -> Result<OwnedJsValue<'_>, ExecutionError> {
525        let serialized = convert::serialize_value(self.context, value)?;
526        Ok(OwnedJsValue::new(self, serialized))
527    }
528
529    // Deserialize a quickjs runtime value into a Rust value.
530    pub(crate) fn to_value(&self, value: &q::JSValue) -> Result<JsValue, ValueError> {
531        convert::deserialize_value(self.context, value)
532    }
533
534    /// Get the global object.
535    pub fn global(&self) -> Result<OwnedJsObject<'_>, ExecutionError> {
536        let global_raw = unsafe { q::JS_GetGlobalObject(self.context) };
537        let global_ref = OwnedJsValue::new(self, global_raw);
538        let global = global_ref.try_into_object()?;
539        Ok(global)
540    }
541
542    /// Get the last exception from the runtime, and if present, convert it to a ExceptionError.
543    pub(crate) fn get_exception(&self) -> Option<ExecutionError> {
544        let value = unsafe {
545            let raw = q::JS_GetException(self.context);
546            OwnedJsValue::new(self, raw)
547        };
548
549        if value.is_null() {
550            None
551        } else if value.is_exception() {
552            Some(ExecutionError::Internal(
553                "Could get exception from runtime".into(),
554            ))
555        } else {
556            match value.js_to_string() {
557                Ok(strval) => {
558                    if strval.contains("out of memory") {
559                        Some(ExecutionError::OutOfMemory)
560                    } else {
561                        Some(ExecutionError::Exception(JsValue::String(strval)))
562                    }
563                }
564                Err(e) => Some(e),
565            }
566        }
567    }
568
569    /// Returns `Result::Err` when an error ocurred.
570    pub(crate) fn ensure_no_excpetion(&self) -> Result<(), ExecutionError> {
571        if let Some(e) = self.get_exception() {
572            Err(e)
573        } else {
574            Ok(())
575        }
576    }
577
578    /// If the given value is a promise, run the event loop until it is
579    /// resolved, and return the final value.
580    fn resolve_value<'a>(
581        &'a self,
582        value: OwnedJsValue<'a>,
583    ) -> Result<OwnedJsValue<'a>, ExecutionError> {
584        if value.is_exception() {
585            unsafe {
586                //TODO remove
587                // js_std_dump_error(self.context);
588            }
589            let err = self
590                .get_exception()
591                .unwrap_or_else(|| ExecutionError::Exception("Unknown exception".into()));
592            Err(err)
593        } else if value.is_object() {
594            let obj = value.try_into_object()?;
595            Ok(obj.into_value())
596        } else {
597            Ok(value)
598        }
599    }
600
601    /// Evaluate javascript code.
602    pub fn eval<'a>(&'a self, code: &str, eval_type: u32, filename: &str) -> Result<OwnedJsValue<'a>, ExecutionError> {
603        let filename_c = make_cstring(filename)?;
604        let code_c = make_cstring(code)?;
605
606        let value_raw = unsafe {
607            q::JS_Eval(
608                self.context,
609                code_c.as_ptr(),
610                code.len() as _,
611                filename_c.as_ptr(),
612                eval_type as i32,
613            )
614        };
615        let value = OwnedJsValue::new(self, value_raw);
616        self.resolve_value(value)
617    }
618
619    /*
620    /// Call a constructor function.
621    fn call_constructor<'a>(
622        &'a self,
623        function: OwnedJsValue<'a>,
624        args: Vec<OwnedJsValue<'a>>,
625    ) -> Result<OwnedJsValue<'a>, ExecutionError> {
626        let mut qargs = args.iter().map(|arg| arg.value).collect::<Vec<_>>();
627
628        let value_raw = unsafe {
629            q::JS_CallConstructor(
630                self.context,
631                function.value,
632                qargs.len() as i32,
633                qargs.as_mut_ptr(),
634            )
635        };
636        let value = OwnedJsValue::new(self, value_raw);
637        if value.is_exception() {
638            let err = self
639                .get_exception()
640                .unwrap_or_else(|| ExecutionError::Exception("Unknown exception".into()));
641            Err(err)
642        } else {
643            Ok(value)
644        }
645    }
646    */
647
648    /// Call a JS function with the given arguments.
649    pub fn call_function<'a>(
650        &'a self,
651        function: JsFunction<'a>,
652        args: Vec<OwnedJsValue<'a>>,
653    ) -> Result<OwnedJsValue<'a>, ExecutionError> {
654        let ret = function.call(args)?;
655        self.resolve_value(ret)
656    }
657
658    /// Helper for executing a callback closure.
659    fn exec_callback<F>(
660        context: *mut q::JSContext,
661        argc: c_int,
662        argv: *mut q::JSValue,
663        callback: &impl Callback<F>,
664    ) -> Result<q::JSValue, ExecutionError> {
665        let result = std::panic::catch_unwind(|| {
666            let arg_slice = unsafe { std::slice::from_raw_parts(argv, argc as usize) };
667
668            let mut args = Vec::with_capacity(arg_slice.len());
669            for a in arg_slice {
670                let a = deserialize_value(context, a)
671                    .map_err(|e| {
672                        ExecutionError::Internal(
673                            format!("failed to deserialize arguments {} (zero-based) to JS value, {}", args.len(), e)
674                        )
675                    })?;
676                args.push(a);
677            }
678
679            match callback.call(args) {
680                Ok(Ok(result)) => {
681                    let serialized = convert::serialize_value(context, result)
682                        .map_err(|e| {
683                            ExecutionError::Internal(format!("failed to serialize rust value to js value, {}", e))
684                        })?;
685                    Ok(serialized)
686                }
687                // TODO: better error reporting.
688                Ok(Err(e)) => Err(ExecutionError::Exception(JsValue::String(e))),
689                Err(e) => Err(e.into()),
690            }
691        });
692
693        match result {
694            Ok(r) => r,
695            Err(_e) => Err(ExecutionError::Internal("Callback panicked!".to_string())),
696        }
697    }
698
699    /// Add a global JS function that is backed by a Rust function or closure.
700    pub fn create_callback<'a, F>(
701        &'a self,
702        name: &str,
703        callback: impl Callback<F> + 'static,
704    ) -> Result<JsFunction<'a>, ExecutionError> {
705        let argcount = callback.argument_count() as i32;
706
707        let context = self.context;
708        let name = name.to_string();
709        let wrapper = move |argc: c_int, argv: *mut q::JSValue| -> q::JSValue {
710            match Self::exec_callback(context, argc, argv, &callback) {
711                Ok(value) => value,
712                // TODO: better error reporting.
713                Err(e) => {
714                    let js_exception_value = match e {
715                        ExecutionError::Exception(e) => e,
716                        other => format!("Failed to call [{}], {}", &name,  other.to_string()).into(),
717                    };
718                    let js_exception =
719                        convert::serialize_value(context, js_exception_value).unwrap();
720                    unsafe {
721                        q::JS_Throw(context, js_exception);
722                    }
723
724                    q::JS_MKVAL(q::JS_TAG_EXCEPTION, 0)
725                }
726            }
727        };
728
729        let (pair, trampoline) = unsafe { build_closure_trampoline(wrapper) };
730        let data = (&*pair.1) as *const q::JSValue as *mut q::JSValue;
731        self.callbacks.lock().unwrap().push(pair);
732
733        let obj = unsafe {
734            let f = q::JS_NewCFunctionData(self.context, trampoline, argcount, 0, 1, data);
735            OwnedJsValue::new(self, f)
736        };
737
738        let f = obj.try_into_function()?;
739        Ok(f)
740    }
741
742    pub fn add_callback<'a, F>(
743        &'a self,
744        name: &str,
745        callback: impl Callback<F> + 'static,
746    ) -> Result<(), ExecutionError> {
747        let cfunc = self.create_callback(name, callback)?;
748        let global = self.global()?;
749        global.set_property(name, cfunc.into_value())?;
750        Ok(())
751    }
752
753    /// return Ok(false) if no job pending, Ok(true) if a job was executed successfully.
754    pub fn execute_pending_job(&self) -> Result<bool, ExecutionError> {
755        let mut job_ctx = null_mut();
756        let flag = unsafe {
757            q::JS_ExecutePendingJob(self.runtime, &mut job_ctx)
758        };
759        if flag < 0 {
760            //FIXME should get exception from job_ctx
761            let e = self.get_exception().unwrap_or_else(|| {
762                ExecutionError::Exception("Unknown exception".into())
763            });
764            return Err(e);
765        }
766        Ok(flag != 0)
767    }
768
769    pub fn execute_module(&self, module_name: &str) -> Result<(), ExecutionError> {
770        if let Some(ml) = self.module_loader {
771            unsafe {
772                let loader = &mut *ml;
773                let module = loader.load(module_name).map_err(|e| ExecutionError::Internal(format!("Fail to load module:{}", e)))?;
774                self.eval(&module, JS_EVAL_TYPE_MODULE, module_name)?;
775                Ok(())
776            }
777        } else {
778            Err(ExecutionError::Internal("Module loader is not set".to_string()))
779        }
780    }
781
782}