diff options
author | Filip Kieres <filip@linux.com> | 2022-03-22 14:32:32 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-22 14:32:32 +0100 |
commit | d2b7192c1b02d77911831f0c2ae12287229bc148 (patch) | |
tree | 7ed08f44fdeb0232c3db4536a5a43a56026bd16e /core/runtime.rs | |
parent | 49012cbc337589089697c2c6ce4708538441d968 (diff) |
feat(core): Add JsRuntime::get_module_namespace to access the namespace object of a module (#14026)
Diffstat (limited to 'core/runtime.rs')
-rw-r--r-- | core/runtime.rs | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/core/runtime.rs b/core/runtime.rs index dc279de88..8c781bf24 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -675,6 +675,44 @@ impl JsRuntime { snapshot } + /// Returns the namespace object of a module. + /// + /// This is only available after module evaluation has completed. + /// This function panics if module has not been instantiated. + pub fn get_module_namespace( + &mut self, + module_id: ModuleId, + ) -> Result<v8::Global<v8::Object>, Error> { + let module_map_rc = Self::module_map(self.v8_isolate()); + + let module_handle = module_map_rc + .borrow() + .get_handle(module_id) + .expect("ModuleInfo not found"); + + let scope = &mut self.handle_scope(); + + let module = module_handle.open(scope); + + if module.get_status() == v8::ModuleStatus::Errored { + let exception = module.get_exception(); + let err = exception_to_err_result(scope, exception, false) + .map_err(|err| attach_handle_to_error(scope, err, exception)); + return err; + } + + assert!(matches!( + module.get_status(), + v8::ModuleStatus::Instantiated | v8::ModuleStatus::Evaluated + )); + + let module_namespace: v8::Local<v8::Object> = + v8::Local::try_from(module.get_module_namespace()) + .map_err(|err: v8::DataError| generic_error(err.to_string()))?; + + Ok(v8::Global::new(scope, module_namespace)) + } + /// Registers a callback on the isolate when the memory limits are approached. /// Use this to prevent V8 from crashing the process when reaching the limit. /// @@ -2187,6 +2225,89 @@ pub mod tests { } #[test] + fn test_get_module_namespace() { + #[derive(Default)] + struct ModsLoader; + + impl ModuleLoader for ModsLoader { + fn resolve( + &self, + specifier: &str, + referrer: &str, + _is_main: bool, + ) -> Result<ModuleSpecifier, Error> { + assert_eq!(specifier, "file:///main.js"); + assert_eq!(referrer, "."); + let s = crate::resolve_import(specifier, referrer).unwrap(); + Ok(s) + } + + fn load( + &self, + _module_specifier: &ModuleSpecifier, + _maybe_referrer: Option<ModuleSpecifier>, + _is_dyn_import: bool, + ) -> Pin<Box<ModuleSourceFuture>> { + async { Err(generic_error("Module loading is not supported")) } + .boxed_local() + } + } + + let loader = std::rc::Rc::new(ModsLoader::default()); + let mut runtime = JsRuntime::new(RuntimeOptions { + module_loader: Some(loader), + ..Default::default() + }); + + let specifier = crate::resolve_url("file:///main.js").unwrap(); + let source_code = r#" + export const a = "b"; + export default 1 + 2; + "# + .to_string(); + + let module_id = futures::executor::block_on( + runtime.load_main_module(&specifier, Some(source_code)), + ) + .unwrap(); + + let _ = runtime.mod_evaluate(module_id); + + let module_namespace = runtime.get_module_namespace(module_id).unwrap(); + + let scope = &mut runtime.handle_scope(); + + let module_namespace = + v8::Local::<v8::Object>::new(scope, module_namespace); + + assert!(module_namespace.is_module_namespace_object()); + + let unknown_export_name = v8::String::new(scope, "none").unwrap(); + let binding = module_namespace.get(scope, unknown_export_name.into()); + + assert!(binding.is_some()); + assert!(binding.unwrap().is_undefined()); + + let empty_export_name = v8::String::new(scope, "").unwrap(); + let binding = module_namespace.get(scope, empty_export_name.into()); + + assert!(binding.is_some()); + assert!(binding.unwrap().is_undefined()); + + let a_export_name = v8::String::new(scope, "a").unwrap(); + let binding = module_namespace.get(scope, a_export_name.into()); + + assert!(binding.unwrap().is_string()); + assert_eq!(binding.unwrap(), v8::String::new(scope, "b").unwrap()); + + let default_export_name = v8::String::new(scope, "default").unwrap(); + let binding = module_namespace.get(scope, default_export_name.into()); + + assert!(binding.unwrap().is_number()); + assert_eq!(binding.unwrap(), v8::Number::new(scope, 3_f64)); + } + + #[test] fn test_heap_limits() { let create_params = v8::Isolate::create_params().heap_limits(0, 3 * 1024 * 1024); |