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