Skip to main content

rquickjs_core/value/
module.rs

1//! Types for loading and handling JS modules.
2
3use alloc::{ffi::CString, vec::Vec};
4use core::{
5    ffi::CStr,
6    marker::PhantomData,
7    mem::MaybeUninit,
8    ptr::{self, null_mut, NonNull},
9    slice,
10};
11
12use crate::{
13    atom::PredefinedAtom, qjs, Atom, Ctx, Error, FromAtom, FromJs, IntoAtom, IntoJs, Object,
14    Promise, Result, Value,
15};
16
17#[derive(Default)]
18pub enum WriteOptionsEndianness {
19    /// Native endian.
20    #[default]
21    Native,
22    /// Little endian.
23    Little,
24    /// Big endian.
25    Big,
26    /// Swaps from native to other kind.
27    Swap,
28}
29
30/// Module write options.
31#[derive(Default)]
32pub struct WriteOptions {
33    /// Endianness of bytecode.
34    pub endianness: WriteOptionsEndianness,
35    /// Allow SharedArrayBuffer.
36    pub allow_shared_array_buffer: bool,
37    /// Allow object references to encode arbitrary object graph.
38    pub object_reference: bool,
39    /// Do not write source code information.
40    pub strip_source: bool,
41    /// Do not write debug information.
42    pub strip_debug: bool,
43}
44
45impl WriteOptions {
46    pub fn to_flag(&self) -> i32 {
47        let mut flag = qjs::JS_WRITE_OBJ_BYTECODE;
48
49        let should_swap = match &self.endianness {
50            WriteOptionsEndianness::Native => false,
51            WriteOptionsEndianness::Little => cfg!(target_endian = "big"),
52            WriteOptionsEndianness::Big => cfg!(target_endian = "little"),
53            WriteOptionsEndianness::Swap => true,
54        };
55        if should_swap {
56            flag |= qjs::JS_WRITE_OBJ_BSWAP;
57        }
58
59        if self.allow_shared_array_buffer {
60            flag |= qjs::JS_WRITE_OBJ_SAB;
61        }
62
63        if self.object_reference {
64            flag |= qjs::JS_WRITE_OBJ_REFERENCE;
65        }
66
67        if self.strip_source {
68            flag |= qjs::JS_WRITE_OBJ_STRIP_SOURCE;
69        }
70
71        if self.strip_debug {
72            flag |= qjs::JS_WRITE_OBJ_STRIP_DEBUG;
73        }
74
75        flag as i32
76    }
77}
78
79/// Helper macro to provide module init function.
80/// Use for exporting module definitions to be loaded as part of a dynamic library.
81/// ```
82/// use rquickjs::{module::ModuleDef, module_init};
83///
84/// struct MyModule;
85/// impl ModuleDef for MyModule {}
86///
87/// module_init!(MyModule);
88/// // or
89/// module_init!(js_init_my_module: MyModule);
90/// ```
91#[macro_export]
92macro_rules! module_init {
93    ($type:ty) => {
94        $crate::module_init!(js_init_module: $type);
95    };
96
97    ($name:ident: $type:ty) => {
98        #[no_mangle]
99        pub unsafe extern "C" fn $name(
100            ctx: *mut $crate::qjs::JSContext,
101            module_name: *const $crate::qjs::c_char,
102        ) -> *mut $crate::qjs::JSModuleDef {
103            $crate::Module::init_raw::<$type>(ctx, module_name)
104        }
105    };
106}
107
108/// The raw module load function (`js_module_init`)
109pub type ModuleLoadFn =
110    unsafe extern "C" fn(*mut qjs::JSContext, *const qjs::c_char) -> *mut qjs::JSModuleDef;
111
112/// A class which can be used to declare rust-native JavaScript modules.
113pub trait ModuleDef {
114    fn declare<'js>(decl: &Declarations<'js>) -> Result<()> {
115        let _ = decl;
116        Ok(())
117    }
118
119    fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
120        let _ = (exports, ctx);
121        Ok(())
122    }
123}
124
125/// A struct used for setting declarations on a module.
126pub struct Declarations<'js>(Module<'js, Declared>);
127
128impl<'js> Declarations<'js> {
129    /// Define a new export in a module.
130    pub fn declare<N>(&self, name: N) -> Result<&Self>
131    where
132        N: Into<Vec<u8>>,
133    {
134        let name = CString::new(name)?;
135        self.declare_c_str(name.as_c_str())
136    }
137
138    /// Define a new export in a module.
139    ///
140    /// This function avoids an extra allocation, having to convert from a rust string into a
141    /// null-terminated CStr.
142    pub fn declare_c_str(&self, name: &CStr) -> Result<&Self> {
143        unsafe { qjs::JS_AddModuleExport(self.0.ctx.as_ptr(), self.0.as_ptr(), name.as_ptr()) };
144        Ok(self)
145    }
146
147    /// Returns the module being declared.
148    pub fn module(&self) -> &Module<'js, Declared> {
149        &self.0
150    }
151}
152
153/// A struct used for setting the value of previously declared exporsts of a module.
154pub struct Exports<'js>(Module<'js, Declared>);
155
156impl<'js> Exports<'js> {
157    /// Set the value of an exported entry.
158    pub fn export<N: Into<Vec<u8>>, T: IntoJs<'js>>(&self, name: N, value: T) -> Result<&Self> {
159        let name = CString::new(name.into())?;
160        self.export_c_str(name.as_c_str(), value)
161    }
162
163    /// Set the value of an exported entry.
164    ///
165    /// This function avoids a possible conversion from a rust string into a CStr
166    pub fn export_c_str<T: IntoJs<'js>>(&self, name: &CStr, value: T) -> Result<&Self> {
167        let value = value.into_js(&self.0.ctx)?;
168        let res = unsafe {
169            qjs::JS_SetModuleExport(
170                self.0.ctx.as_ptr(),
171                self.0.as_ptr(),
172                name.as_ptr(),
173                value.into_js_value(),
174            )
175        };
176        if res < 0 {
177            return Err(Error::InvalidExport);
178        }
179
180        Ok(self)
181    }
182
183    /// Returns the module being exported.
184    pub fn module(&self) -> &Module<'js, Declared> {
185        &self.0
186    }
187}
188
189/// A marker struct used to indicate that a module is possibly not yet evaluated.
190#[derive(Clone, Copy, Debug)]
191pub struct Declared;
192/// A marker struct used to indicate that a module was evaluated.
193#[derive(Clone, Copy, Debug)]
194pub struct Evaluated;
195
196/// A JavaScript module.
197#[derive(Clone, Debug)]
198pub struct Module<'js, T = Declared> {
199    ptr: NonNull<qjs::JSModuleDef>,
200    ctx: Ctx<'js>,
201    _type_marker: PhantomData<T>,
202}
203
204impl<'js, T> Module<'js, T> {
205    pub(crate) fn as_ptr(&self) -> *mut qjs::JSModuleDef {
206        self.ptr.as_ptr()
207    }
208
209    pub(crate) unsafe fn from_ptr(ctx: Ctx<'js>, ptr: NonNull<qjs::JSModuleDef>) -> Module<'js, T> {
210        Module {
211            ptr,
212            ctx,
213            _type_marker: PhantomData,
214        }
215    }
216
217    unsafe extern "C" fn eval_fn<D>(
218        ctx: *mut qjs::JSContext,
219        ptr: *mut qjs::JSModuleDef,
220    ) -> qjs::c_int
221    where
222        D: ModuleDef,
223    {
224        let ctx = Ctx::from_ptr(ctx);
225        // Should never be null
226        let ptr = NonNull::new(ptr).unwrap();
227        let module = unsafe { Module::from_ptr(ctx.clone(), ptr) };
228        let exports = Exports(module);
229        match D::evaluate(&ctx, &exports) {
230            Ok(_) => 0,
231            Err(error) => {
232                error.throw(&ctx);
233                -1
234            }
235        }
236    }
237
238    /// Returns the name of the module
239    pub fn name<N>(&self) -> Result<N>
240    where
241        N: FromAtom<'js>,
242    {
243        let name = unsafe {
244            Atom::from_atom_val(
245                self.ctx.clone(),
246                qjs::JS_GetModuleName(self.ctx.as_ptr(), self.as_ptr()),
247            )
248        };
249        N::from_atom(name)
250    }
251
252    /// Returns the context the module is associated with.
253    pub fn ctx(&self) -> &Ctx<'js> {
254        &self.ctx
255    }
256}
257
258impl<'js> Module<'js, Declared> {
259    /// Declare a module but don't evaluate it.
260    pub fn declare<N, S>(ctx: Ctx<'js>, name: N, source: S) -> Result<Module<'js, Declared>>
261    where
262        N: Into<Vec<u8>>,
263        S: Into<Vec<u8>>,
264    {
265        let name = CString::new(name)?;
266        let flag =
267            qjs::JS_EVAL_TYPE_MODULE | qjs::JS_EVAL_FLAG_STRICT | qjs::JS_EVAL_FLAG_COMPILE_ONLY;
268
269        let module_val = unsafe { ctx.eval_raw(source, name.as_c_str(), flag as i32)? };
270        let module_val = unsafe { ctx.handle_exception(module_val)? };
271        debug_assert_eq!(qjs::JS_TAG_MODULE, unsafe {
272            qjs::JS_VALUE_GET_TAG(module_val)
273        });
274        let module_ptr = unsafe {
275            NonNull::new(qjs::JS_VALUE_GET_PTR(module_val).cast()).ok_or(Error::Unknown)?
276        };
277        unsafe { Ok(Module::from_ptr(ctx, module_ptr)) }
278    }
279
280    /// Declare a rust native module but don't evaluate it.
281    pub fn declare_def<D, N>(ctx: Ctx<'js>, name: N) -> Result<Module<'js, Declared>>
282    where
283        N: Into<Vec<u8>>,
284        D: ModuleDef,
285    {
286        let name = CString::new(name)?;
287        let ptr =
288            unsafe { qjs::JS_NewCModule(ctx.as_ptr(), name.as_ptr(), Some(Self::eval_fn::<D>)) };
289        let ptr = NonNull::new(ptr).ok_or(Error::Unknown)?;
290        let m = unsafe { Module::from_ptr(ctx, ptr) };
291
292        let decl = Declarations(m);
293        D::declare(&decl)?;
294
295        Ok(decl.0)
296        //Ok(())
297    }
298
299    /// Evaluate the source of a module.
300    ///
301    /// This function returns a promise which resolved when the modules was fully compiled and
302    /// returns undefined.
303    ///
304    /// Since QuickJS doesn't give us a way to retrieve the module if we immediately evaluate a
305    /// modules source this function doesn't return a module object.
306    /// If the module is required, you should first declare it with [`Module::declare`] and then call [`Module::eval`] on the
307    /// returned module.
308    pub fn evaluate<N, S>(ctx: Ctx<'js>, name: N, source: S) -> Result<Promise<'js>>
309    where
310        N: Into<Vec<u8>>,
311        S: Into<Vec<u8>>,
312    {
313        let name = CString::new(name)?;
314        let flag = qjs::JS_EVAL_TYPE_MODULE | qjs::JS_EVAL_FLAG_STRICT;
315
316        let module_val = unsafe { ctx.eval_raw(source, name.as_c_str(), flag as i32)? };
317        let module_val = unsafe { ctx.handle_exception(module_val)? };
318        let v = unsafe { Value::from_js_value(ctx, module_val) };
319        Ok(v.into_promise().expect("evaluate should return a promise"))
320    }
321
322    /// Declares a module in the runtime and evaluates it.
323    pub fn evaluate_def<D, N>(
324        ctx: Ctx<'js>,
325        name: N,
326    ) -> Result<(Module<'js, Evaluated>, Promise<'js>)>
327    where
328        N: Into<Vec<u8>>,
329        D: ModuleDef,
330    {
331        let module = Self::declare_def::<D, N>(ctx, name)?;
332        module.eval()
333    }
334
335    /// Load a module from quickjs bytecode.
336    ///
337    /// # Safety
338    /// User must ensure that bytes handed to this function contain valid bytecode.
339    pub unsafe fn load(ctx: Ctx<'js>, bytes: &[u8]) -> Result<Module<'js, Declared>> {
340        let module = unsafe {
341            qjs::JS_ReadObject(
342                ctx.as_ptr(),
343                bytes.as_ptr(),
344                bytes.len() as _,
345                (qjs::JS_READ_OBJ_BYTECODE | qjs::JS_READ_OBJ_ROM_DATA) as i32,
346            )
347        };
348        let module = ctx.handle_exception(module)?;
349        debug_assert_eq!(qjs::JS_TAG_MODULE, unsafe { qjs::JS_VALUE_GET_TAG(module) });
350        let module_ptr =
351            unsafe { NonNull::new(qjs::JS_VALUE_GET_PTR(module).cast()).ok_or(Error::Unknown)? };
352        unsafe { Ok(Module::from_ptr(ctx, module_ptr)) }
353    }
354
355    /// Load a module from a raw module loading function.
356    ///
357    /// # Safety
358    /// The soundness of this function depends completely on if load_fn is implemented correctly.
359    /// THe load_fn function must return a pointer to a valid module loaded with the given context.
360    pub unsafe fn from_load_fn<N>(
361        ctx: Ctx<'js>,
362        name: N,
363        load_fn: ModuleLoadFn,
364    ) -> Result<Module<'js, Declared>>
365    where
366        N: Into<Vec<u8>>,
367    {
368        let name = CString::new(name)?;
369        let ptr = (load_fn)(ctx.as_ptr(), name.as_ptr().cast());
370        let ptr = NonNull::new(ptr).ok_or(Error::Exception)?;
371        unsafe { Ok(Module::from_ptr(ctx, ptr)) }
372    }
373
374    /// Evaluate the module.
375    ///
376    /// Returns the module as being evaluated and a promise which resolves when the module has finished evaluating.
377    /// The return value of the promise is the JavaScript value undefined.
378    pub fn eval(self) -> Result<(Module<'js, Evaluated>, Promise<'js>)> {
379        let ret = unsafe {
380            #[cfg(feature = "parallel")]
381            qjs::JS_UpdateStackTop(qjs::JS_GetRuntime(self.ctx.as_ptr()));
382
383            // JS_EvalFunction `free's` the module so we should dup first
384            let v = qjs::JS_MKPTR(qjs::JS_TAG_MODULE, self.ptr.as_ptr().cast());
385            qjs::JS_DupValue(self.ctx.as_ptr(), v);
386            qjs::JS_EvalFunction(self.ctx.as_ptr(), v)
387        };
388        let ret = unsafe { self.ctx.handle_exception(ret)? };
389        let promise = unsafe { Promise::from_js_value(self.ctx.clone(), ret) };
390        Ok((
391            Module {
392                ptr: self.ptr,
393                ctx: self.ctx,
394                _type_marker: PhantomData,
395            },
396            promise,
397        ))
398    }
399
400    /// A function for loading a Rust module from C.
401    ///
402    /// # Safety
403    /// This function should only be called when the module is loaded as part of a dynamically
404    /// loaded library.
405    pub unsafe extern "C" fn init_raw<D>(
406        ctx: *mut qjs::JSContext,
407        name: *const qjs::c_char,
408    ) -> *mut qjs::JSModuleDef
409    where
410        D: ModuleDef,
411    {
412        let ctx = Ctx::from_ptr(ctx);
413        let name = CStr::from_ptr(name).to_bytes();
414        match Self::declare_def::<D, _>(ctx.clone(), name) {
415            Ok(module) => module.as_ptr(),
416            Err(error) => {
417                error.throw(&ctx);
418                ptr::null_mut()
419            }
420        }
421    }
422
423    /// Import and evaluate a module
424    ///
425    /// This will work similar to an `import(specifier)` statement in JavaScript returning a promise with the result of the imported module.
426    pub fn import<S: Into<Vec<u8>>>(ctx: &Ctx<'js>, specifier: S) -> Result<Promise<'js>> {
427        let specifier = CString::new(specifier)?;
428        unsafe {
429            let base_name = ctx
430                .script_or_module_name(1)
431                .unwrap_or_else(|| Atom::from_predefined(ctx.clone(), PredefinedAtom::Empty));
432
433            let base_name_c_str =
434                qjs::JS_AtomToCStringLen(ctx.as_ptr(), null_mut(), base_name.atom);
435
436            let res = qjs::JS_LoadModule(ctx.as_ptr(), base_name_c_str, specifier.as_ptr());
437
438            qjs::JS_FreeCString(ctx.as_ptr(), base_name_c_str);
439
440            let res = ctx.handle_exception(res)?;
441
442            Ok(Promise::from_js_value(ctx.clone(), res))
443        }
444    }
445}
446
447impl<'js, Evaluated> Module<'js, Evaluated> {
448    /// Write object bytecode for the module.
449    ///
450    /// # Examples
451    ///
452    /// ```
453    /// use rquickjs::{Context, Module, Result, Runtime, WriteOptions, WriteOptionsEndianness};
454    /// fn main() -> Result<()> {
455    ///     let rt = Runtime::new()?;
456    ///     let ctx = Context::full(&rt)?;
457    ///     let bytecode = ctx.with(|ctx| {
458    ///         let src = "console.log('hello world')";
459    ///         let module = Module::declare(ctx.clone(), "foo.js", src)?;
460    ///         module.write(WriteOptions {
461    ///             endianness: WriteOptionsEndianness::Little,
462    ///             ..Default::default()
463    ///         })
464    ///     })?;
465    ///     println!("bytecode: {bytecode:?}");
466    ///     Ok(())
467    /// }
468    /// ```
469    pub fn write(&self, options: WriteOptions) -> Result<Vec<u8>> {
470        let ctx = &self.ctx;
471        let mut len = MaybeUninit::uninit();
472        let buf = unsafe {
473            qjs::JS_WriteObject(
474                ctx.as_ptr(),
475                len.as_mut_ptr(),
476                qjs::JS_MKPTR(qjs::JS_TAG_MODULE, self.ptr.as_ptr().cast()),
477                options.to_flag(),
478            )
479        };
480        if buf.is_null() {
481            return Err(ctx.raise_exception());
482        }
483        let len = unsafe { len.assume_init() };
484        let obj = unsafe { slice::from_raw_parts(buf, len as _) };
485        let obj = Vec::from(obj);
486        unsafe { qjs::js_free(ctx.as_ptr(), buf as _) };
487        Ok(obj)
488    }
489
490    /// Return the `import.meta` object of a module
491    pub fn meta(&self) -> Result<Object<'js>> {
492        unsafe {
493            Ok(Object::from_js_value(
494                self.ctx.clone(),
495                self.ctx
496                    .handle_exception(qjs::JS_GetImportMeta(self.ctx.as_ptr(), self.as_ptr()))?,
497            ))
498        }
499    }
500
501    /// Returns the module namespace, an object containing all the module exported values.
502    pub fn namespace(&self) -> Result<Object<'js>> {
503        unsafe {
504            let v = qjs::JS_GetModuleNamespace(self.ctx.as_ptr(), self.as_ptr());
505            let v = self.ctx.handle_exception(v)?;
506            Ok(Object::from_js_value(self.ctx.clone(), v))
507        }
508    }
509
510    /// Return exported value by name
511    pub fn get<N, T>(&self, name: N) -> Result<T>
512    where
513        N: IntoAtom<'js>,
514        T: FromJs<'js>,
515    {
516        self.namespace()?.get(name)
517    }
518
519    /// Change the module back to being only declared.
520    ///
521    /// This is always safe to do since calling eval again on an already evaluated module is safe.
522    pub fn into_declared(self) -> Module<'js, Declared> {
523        Module {
524            ptr: self.ptr,
525            ctx: self.ctx,
526            _type_marker: PhantomData,
527        }
528    }
529}
530
531#[cfg(test)]
532mod test {
533
534    use super::*;
535    use crate::*;
536
537    pub struct RustModule;
538
539    impl ModuleDef for RustModule {
540        fn declare(define: &Declarations) -> Result<()> {
541            define.declare_c_str(c"hello")?;
542            Ok(())
543        }
544
545        fn evaluate<'js>(_ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
546            exports.export_c_str(c"hello", "world")?;
547            Ok(())
548        }
549    }
550
551    pub struct CrashingRustModule;
552
553    impl ModuleDef for CrashingRustModule {
554        fn declare(_: &Declarations) -> Result<()> {
555            Ok(())
556        }
557
558        fn evaluate<'js>(ctx: &Ctx<'js>, _exports: &Exports<'js>) -> Result<()> {
559            ctx.eval::<(), _>(r#"throw new Error("kaboom")"#)?;
560            Ok(())
561        }
562    }
563
564    #[test]
565    fn from_rust_def() {
566        test_with(|ctx| {
567            Module::declare_def::<RustModule, _>(ctx, "rust_mod").unwrap();
568        })
569    }
570
571    #[test]
572    fn from_rust_def_eval() {
573        test_with(|ctx| {
574            let _ = Module::evaluate_def::<RustModule, _>(ctx, "rust_mod").unwrap();
575        })
576    }
577
578    #[test]
579    fn import_native() {
580        test_with(|ctx| {
581            Module::declare_def::<RustModule, _>(ctx.clone(), "rust_mod").unwrap();
582            Module::evaluate(
583                ctx.clone(),
584                "test",
585                r#"
586                import { hello } from "rust_mod";
587
588                globalThis.hello = hello;
589            "#,
590            )
591            .unwrap()
592            .finish::<()>()
593            .unwrap();
594            let text = ctx
595                .globals()
596                .get::<_, String>("hello")
597                .unwrap()
598                .to_string()
599                .unwrap();
600            assert_eq!(text.as_str(), "world");
601        })
602    }
603
604    #[test]
605    fn import_async() {
606        test_with(|ctx| {
607            Module::declare(
608                ctx.clone(),
609                "rust_mod",
610                "
611                async function foo(){
612                    return 'world';
613                };
614                export let hello = await foo();
615            ",
616            )
617            .unwrap();
618            Module::evaluate(
619                ctx.clone(),
620                "test",
621                r#"
622                import { hello } from "rust_mod";
623                globalThis.hello = hello;
624            "#,
625            )
626            .unwrap()
627            .finish::<()>()
628            .unwrap();
629            let text = ctx
630                .globals()
631                .get::<_, String>("hello")
632                .unwrap()
633                .to_string()
634                .unwrap();
635            assert_eq!(text.as_str(), "world");
636        })
637    }
638
639    #[test]
640    fn import() {
641        test_with(|ctx| {
642            Module::declare_def::<RustModule, _>(ctx.clone(), "rust_mod").unwrap();
643            let val: Object = Module::import(&ctx, "rust_mod").unwrap().finish().unwrap();
644            let hello: StdString = val.get("hello").unwrap();
645
646            assert_eq!(&hello, "world");
647        })
648    }
649
650    #[test]
651    #[should_panic(expected = "kaboom")]
652    fn import_crashing() {
653        use crate::{CatchResultExt, Context, Runtime};
654
655        let runtime = Runtime::new().unwrap();
656        let ctx = Context::full(&runtime).unwrap();
657        ctx.with(|ctx| {
658            Module::declare_def::<CrashingRustModule, _>(ctx.clone(), "bad_rust_mod").unwrap();
659            let _: Value = Module::import(&ctx, "bad_rust_mod")
660                .catch(&ctx)
661                .unwrap()
662                .finish()
663                .catch(&ctx)
664                .unwrap();
665        });
666    }
667
668    #[test]
669    fn eval_crashing_module_inside_module() {
670        let runtime = Runtime::new().unwrap();
671        let ctx = Context::full(&runtime).unwrap();
672
673        ctx.with(|ctx| {
674            let globals = ctx.globals();
675            let eval_crashing = |ctx: Ctx| {
676                Module::evaluate(ctx, "test2", "throw new Error(1)").map(|x| x.finish::<()>())
677            };
678            let function = Function::new(ctx.clone(), eval_crashing).unwrap();
679            globals.set("eval_crashing", function).unwrap();
680
681            let res = Module::evaluate(ctx, "test", " eval_crashing(); ")
682                .unwrap()
683                .finish::<()>();
684            assert!(res.is_err())
685        });
686    }
687
688    #[test]
689    fn access_before_fully_evaluating_module() {
690        let runtime = Runtime::new().unwrap();
691        let ctx = Context::full(&runtime).unwrap();
692
693        ctx.with(|ctx| {
694            let decl = Module::declare(
695                ctx,
696                "test",
697                r#"
698                async function async_res(){
699                    return await (async () => {
700                        return "OK"
701                    })()
702                };
703
704                export let res = await async_res()
705            "#,
706            )
707            .unwrap();
708
709            let (decl, promise) = decl.eval().unwrap();
710
711            let ns = decl.namespace().unwrap();
712            ns.get::<_, ()>("res").unwrap_err();
713
714            promise.finish::<()>().unwrap();
715
716            assert_eq!(ns.get::<_, std::string::String>("res").unwrap(), "OK");
717        });
718    }
719
720    #[test]
721    fn from_javascript() {
722        test_with(|ctx| {
723            let (module, promise) = Module::declare(
724                ctx.clone(),
725                "Test",
726                r#"
727            export var a = 2;
728            export function foo(){ return "bar"}
729            export class Baz{
730                quel = 3;
731                constructor(){
732                }
733            }
734                "#,
735            )
736            .unwrap()
737            .eval()
738            .unwrap();
739
740            promise.finish::<()>().unwrap();
741
742            assert_eq!(module.name::<StdString>().unwrap(), "Test");
743            let _ = module.meta().unwrap();
744
745            let ns = module.namespace().unwrap();
746
747            assert!(ns.contains_key("a").unwrap());
748            assert!(ns.contains_key("foo").unwrap());
749            assert!(ns.contains_key("Baz").unwrap());
750
751            assert_eq!(ns.get::<_, u32>("a").unwrap(), 2u32);
752        });
753    }
754}