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