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