Repository: denoland/deno_core
Pull request #328 (2023-11-14 21:39:20)

feat: Add JsRuntime::run_event_loop2, JsRuntime::poll_event_loop2
Needed to solve https://github.com/denoland/deno/issues/20528 where we need to skip pumping V8 message
loop for some time.

bartlomieju: Note to self: create a PR in `deno` and demonstrate that this fixes the issue.

Patch:
diff --git a/core/lib.rs b/core/lib.rs
index 43bc2a1..884f3a5 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -130,6 +130,7 @@ pub use crate::runtime::CrossIsolateStore;
 pub use crate::runtime::JsRealm;
 pub use crate::runtime::JsRuntime;
 pub use crate::runtime::JsRuntimeForSnapshot;
+pub use crate::runtime::PollEventLoopOptions;
 pub use crate::runtime::RuntimeOptions;
 pub use crate::runtime::SharedArrayBufferStore;
 pub use crate::runtime::Snapshot;
diff --git a/core/modules/map.rs b/core/modules/map.rs
index c9dbd03..a0a9820 100644
--- a/core/modules/map.rs
+++ b/core/modules/map.rs
@@ -2,6 +2,7 @@
 use crate::error::exception_to_err_result;
 use crate::error::generic_error;
 use crate::error::throw_type_error;
+use crate::error::to_v8_type_error;
 use crate::fast_string::FastString;
 use crate::modules::get_asserted_module_type_from_assertions;
 use crate::modules::parse_import_assertions;
@@ -26,10 +27,15 @@ use anyhow::Error;
 use futures::future::FutureExt;
 use futures::stream::FuturesUnordered;
 use futures::stream::StreamFuture;
+use futures::StreamExt;
+use std::cell::Cell;
 use std::cell::RefCell;
 use std::collections::HashMap;
+use std::ops::DerefMut;
 use std::pin::Pin;
 use std::rc::Rc;
+use std::task::Context;
+use std::task::Poll;
 
 use super::AssertedModuleType;
 
@@ -55,46 +61,80 @@ pub(crate) enum SymbolicModule {
   Mod(ModuleId),
 }
 
+struct DynImportModEvaluate {
+  load_id: ModuleLoadId,
+  module_id: ModuleId,
+  promise: v8::Global<v8::Promise>,
+  module: v8::Global<v8::Module>,
+}
+
 /// A collection of JS modules.
 pub(crate) struct ModuleMap {
-  // Handling of specifiers and v8 objects
-  pub handles: Vec<v8::Global<v8::Module>>,
-  pub info: Vec<ModuleInfo>,
-  pub(crate) by_name_js: HashMap<ModuleName, SymbolicModule>,
-  pub(crate) by_name_json: HashMap<ModuleName, SymbolicModule>,
-  pub(crate) next_load_id: ModuleLoadId,
-
   // Handling of futures for loading module sources
-  pub loader: Rc<dyn ModuleLoader>,
-  pub(crate) dynamic_import_map:
-    HashMap<ModuleLoadId, v8::Global<v8::PromiseResolver>>,
-  pub(crate) preparing_dynamic_imports:
-    FuturesUnordered<Pin<Box<PrepareLoadFuture>>>,
-  pub(crate) pending_dynamic_imports:
-    FuturesUnordered<StreamFuture<RecursiveModuleLoad>>,
-
-  // This store is used temporarily, to forward parsed JSON
-  // value from `new_json_module` to `json_module_evaluation_steps`
+  // TODO(mmastrac): we should not be swapping this loader out
+  pub(crate) loader: RefCell<Rc<dyn ModuleLoader>>,
+
+  dynamic_import_map:
+    RefCell<HashMap<ModuleLoadId, v8::Global<v8::PromiseResolver>>>,
+  preparing_dynamic_imports:
+    RefCell<FuturesUnordered<Pin<Box<PrepareLoadFuture>>>>,
+  pending_dynamic_imports:
+    RefCell<FuturesUnordered<StreamFuture<RecursiveModuleLoad>>>,
+  pending_dyn_mod_evaluations: RefCell<Vec<DynImportModEvaluate>>,
+  data: RefCell<ModuleMapData>,
+
+  /// A counter used to delay our dynamic import deadlock detection by one spin
+  /// of the event loop.
+  pub(crate) dyn_module_evaluate_idle_counter: Cell<u32>,
+}
+
+#[derive(Default)]
+pub(crate) struct ModuleMapData {
+  /// Inverted index from module to index in `info`.
+  handles_inverted: HashMap<v8::Global<v8::Module>, usize>,
+  /// The handles we have loaded so far, corresponding with the [`ModuleInfo`] in `info`.
+  handles: Vec<v8::Global<v8::Module>>,
+  /// The modules we have loaded so far.
+  info: Vec<ModuleInfo>,
+  /// [`ModuleName`] to [`SymbolicModule`] for JS/WASM modules.
+  by_name_js: HashMap<ModuleName, SymbolicModule>,
+  /// [`ModuleName`] to [`SymbolicModule`] for JSON modules.
+  by_name_json: HashMap<ModuleName, SymbolicModule>,
+  /// The next ID used for a load.
+  next_load_id: ModuleLoadId,
+  /// If a main module has been loaded, points to it by index.
+  main_module_id: Option<ModuleId>,
+  /// This store is used temporarily, to forward parsed JSON
+  /// value from `new_json_module` to `json_module_evaluation_steps`
   json_value_store: HashMap<v8::Global<v8::Module>, v8::Global<v8::Value>>,
 }
 
 impl ModuleMap {
-  pub fn collect_modules(
+  pub(crate) fn next_load_id(&self) -> i32 {
+    // TODO(mmastrac): move recursive module loading into here so we can avoid making this pub
+    let mut data = self.data.borrow_mut();
+    let id = data.next_load_id;
+    data.next_load_id += 1;
+    id + 1
+  }
+
+  /// Walk the currently registered modules (JS, WASM and JSON) along with their index and names.
+  fn walk_modules(
     &self,
-  ) -> Vec<(AssertedModuleType, &ModuleName, &SymbolicModule)> {
-    let mut output = vec![];
+    mut f: impl FnMut(usize, AssertedModuleType, &ModuleName, &SymbolicModule),
+  ) {
+    let mut i = 0;
     for module_type in [
       AssertedModuleType::JavaScriptOrWasm,
       AssertedModuleType::Json,
     ] {
-      output.extend(
-        self
-          .by_name(module_type)
-          .iter()
-          .map(|x| (module_type, x.0, x.1)),
-      )
+      self.by_name(module_type, |map| {
+        for (name, module) in map.iter() {
+          f(i, module_type, name, module);
+          i += 1;
+        }
+      });
     }
-    output
   }
 
   #[cfg(debug_assertions)]
@@ -103,11 +143,12 @@ impl ModuleMap {
     scope: &mut v8::HandleScope,
   ) {
     let mut not_evaluated = vec![];
+    let data = self.data.borrow();
 
-    for (i, handle) in self.handles.iter().enumerate() {
+    for (handle, i) in data.handles_inverted.iter() {
       let module = v8::Local::new(scope, handle);
       if !matches!(module.get_status(), v8::ModuleStatus::Evaluated) {
-        not_evaluated.push(self.info[i].name.as_str().to_string());
+        not_evaluated.push(data.info[*i].name.as_str().to_string());
       }
     }
 
@@ -125,22 +166,25 @@ impl ModuleMap {
     scope: &mut v8::HandleScope,
   ) -> SnapshottedData {
     let array = v8::Array::new(scope, 3);
+    let data = self.data.borrow();
 
-    let next_load_id = v8::Integer::new(scope, self.next_load_id);
+    let next_load_id = v8::Integer::new(scope, data.next_load_id);
     array.set_index(scope, 0, next_load_id.into());
 
-    let info_arr = v8::Array::new(scope, self.info.len() as i32);
-    for (i, info) in self.info.iter().enumerate() {
-      let module_info_arr = v8::Array::new(scope, 5);
+    if let Some(main_module_id) = data.main_module_id {
+      let main_module_id = v8::Integer::new(scope, main_module_id as i32);
+      array.set_index(scope, 1, main_module_id.into());
+    }
+
+    let info_arr = v8::Array::new(scope, data.info.len() as i32);
+    for (i, info) in data.info.iter().enumerate() {
+      let module_info_arr = v8::Array::new(scope, 4);
 
       let id = v8::Integer::new(scope, info.id as i32);
       module_info_arr.set_index(scope, 0, id.into());
 
-      let main = v8::Boolean::new(scope, info.main);
-      module_info_arr.set_index(scope, 1, main.into());
-
       let name = info.name.v8(scope);
-      module_info_arr.set_index(scope, 2, name.into());
+      module_info_arr.set_index(scope, 1, name.into());
 
       let array_len = 2 * info.requests.len() as i32;
       let requests_arr = v8::Array::new(scope, array_len);
@@ -161,52 +205,51 @@ impl ModuleMap {
           asserted_module_type.into(),
         );
       }
-      module_info_arr.set_index(scope, 3, requests_arr.into());
+      module_info_arr.set_index(scope, 2, requests_arr.into());
 
       let module_type = v8::Integer::new(scope, info.module_type as i32);
-      module_info_arr.set_index(scope, 4, module_type.into());
+      module_info_arr.set_index(scope, 3, module_type.into());
 
       info_arr.set_index(scope, i as u32, module_info_arr.into());
     }
-    array.set_index(scope, 1, info_arr.into());
-
-    let by_name = self.collect_modules();
-    let by_name_array = v8::Array::new(scope, by_name.len() as i32);
-    {
-      for (i, (module_type, name, module)) in by_name.into_iter().enumerate() {
-        let arr = v8::Array::new(scope, 3);
-
-        let specifier = name.v8(scope);
-        arr.set_index(scope, 0, specifier.into());
-
-        let asserted_module_type = v8::Integer::new(scope, module_type as i32);
-        arr.set_index(scope, 1, asserted_module_type.into());
-
-        let symbolic_module: v8::Local<v8::Value> = match module {
-          SymbolicModule::Alias(alias) => {
-            let alias = v8::String::new_from_one_byte(
-              scope,
-              alias.as_bytes(),
-              v8::NewStringType::Normal,
-            )
-            .unwrap();
-            alias.into()
-          }
-          SymbolicModule::Mod(id) => {
-            let id = v8::Integer::new(scope, *id as i32);
-            id.into()
-          }
-        };
-        arr.set_index(scope, 2, symbolic_module);
+    array.set_index(scope, 2, info_arr.into());
+
+    let length = self.data.borrow().by_name_js.len()
+      + self.data.borrow().by_name_json.len();
+    let by_name_array = v8::Array::new(scope, length.try_into().unwrap());
+    self.walk_modules(|i, module_type, name, module| {
+      let arr = v8::Array::new(scope, 3);
+
+      let specifier = name.v8(scope);
+      arr.set_index(scope, 0, specifier.into());
+
+      let asserted_module_type = v8::Integer::new(scope, module_type as i32);
+      arr.set_index(scope, 1, asserted_module_type.into());
+
+      let symbolic_module: v8::Local<v8::Value> = match module {
+        SymbolicModule::Alias(alias) => {
+          let alias = v8::String::new_from_one_byte(
+            scope,
+            alias.as_bytes(),
+            v8::NewStringType::Normal,
+          )
+          .unwrap();
+          alias.into()
+        }
+        SymbolicModule::Mod(id) => {
+          let id = v8::Integer::new(scope, *id as i32);
+          id.into()
+        }
+      };
+      arr.set_index(scope, 2, symbolic_module);
 
-        by_name_array.set_index(scope, i as u32, arr.into());
-      }
-    }
-    array.set_index(scope, 2, by_name_array.into());
+      by_name_array.set_index(scope, i as u32, arr.into());
+    });
+    array.set_index(scope, 3, by_name_array.into());
 
     let array_global = v8::Global::new(scope, array);
 
-    let handles = self.handles.clone();
+    let handles = data.handles.clone();
     SnapshottedData {
       module_map_data: array_global,
       module_handles: handles,
@@ -214,7 +257,7 @@ impl ModuleMap {
   }
 
   pub fn update_with_snapshotted_data(
-    &mut self,
+    &self,
     scope: &mut v8::HandleScope,
     snapshotted_data: SnapshottedData,
   ) {
@@ -226,11 +269,21 @@ impl ModuleMap {
       assert!(next_load_id.is_int32());
       let integer = next_load_id.to_integer(scope).unwrap();
       let val = integer.int32_value(scope).unwrap();
-      self.next_load_id = val;
+      self.data.borrow_mut().next_load_id = val;
+    }
+
+    {
+      let main_module_id = local_data.get_index(scope, 1).unwrap();
+      if !main_module_id.is_undefined() {
+        let integer = main_module_id
+          .to_integer(scope)
+          .map(|v| v.int32_value(scope).unwrap() as usize);
+        self.data.borrow_mut().main_module_id = integer;
+      }
     }
 
     {
-      let info_val = local_data.get_index(scope, 1).unwrap();
+      let info_val = local_data.get_index(scope, 2).unwrap();
 
       let info_arr: v8::Local<v8::Array> = info_val.try_into().unwrap();
       let len = info_arr.length() as usize;
@@ -250,20 +303,14 @@ impl ModuleMap {
           .unwrap()
           .value() as ModuleId;
 
-        let main = module_info_arr
-          .get_index(scope, 1)
-          .unwrap()
-          .to_boolean(scope)
-          .is_true();
-
         let name = module_info_arr
-          .get_index(scope, 2)
+          .get_index(scope, 1)
           .unwrap()
           .to_rust_string_lossy(scope)
           .into();
 
         let requests_arr: v8::Local<v8::Array> = module_info_arr
-          .get_index(scope, 3)
+          .get_index(scope, 2)
           .unwrap()
           .try_into()
           .unwrap();
@@ -292,7 +339,7 @@ impl ModuleMap {
         }
 
         let module_type_no = module_info_arr
-          .get_index(scope, 4)
+          .get_index(scope, 3)
           .unwrap()
           .to_integer(scope)
           .unwrap()
@@ -303,6 +350,7 @@ impl ModuleMap {
           _ => unreachable!(),
         };
 
+        let main = self.data.borrow().main_module_id == Some(id);
         let module_info = ModuleInfo {
           id,
           main,
@@ -313,17 +361,15 @@ impl ModuleMap {
         info.push(module_info);
       }
 
-      self.info = info;
+      self.data.borrow_mut().info = info;
     }
 
-    self
-      .by_name_mut(AssertedModuleType::JavaScriptOrWasm)
-      .clear();
-    self.by_name_mut(AssertedModuleType::Json).clear();
+    self.data.borrow_mut().by_name_js.clear();
+    self.data.borrow_mut().by_name_json.clear();
 
     {
       let by_name_arr: v8::Local<v8::Array> =
-        local_data.get_index(scope, 2).unwrap().try_into().unwrap();
+        local_data.get_index(scope, 3).unwrap().try_into().unwrap();
       let len = by_name_arr.length() as usize;
 
       for i in 0..len {
@@ -363,27 +409,24 @@ impl ModuleMap {
           )
         };
 
-        self
-          .by_name_mut(asserted_module_type)
-          .insert(specifier.into(), val);
+        self.by_name_mut(asserted_module_type, move |map| {
+          map.insert(specifier.into(), val)
+        });
       }
     }
 
-    self.handles = snapshotted_data.module_handles;
+    self.data.borrow_mut().handles = snapshotted_data.module_handles;
   }
 
   pub(crate) fn new(loader: Rc<dyn ModuleLoader>) -> ModuleMap {
     Self {
-      handles: vec![],
-      info: vec![],
-      by_name_js: HashMap::new(),
-      by_name_json: HashMap::new(),
-      next_load_id: 1,
-      loader,
-      dynamic_import_map: HashMap::new(),
-      preparing_dynamic_imports: FuturesUnordered::new(),
-      pending_dynamic_imports: FuturesUnordered::new(),
-      json_value_store: HashMap::new(),
+      loader: loader.into(),
+      dyn_module_evaluate_idle_counter: Default::default(),
+      dynamic_import_map: Default::default(),
+      preparing_dynamic_imports: Default::default(),
+      pending_dynamic_imports: Default::default(),
+      pending_dyn_mod_evaluations: Default::default(),
+      data: Default::default(),
     }
   }
 
@@ -394,26 +437,27 @@ impl ModuleMap {
     name: impl AsRef<str>,
     asserted_module_type: AssertedModuleType,
   ) -> Option<ModuleId> {
-    let map = self.by_name(asserted_module_type);
-    let first_symbolic_module = map.get(name.as_ref())?;
-    let mut mod_name = match first_symbolic_module {
-      SymbolicModule::Mod(mod_id) => return Some(*mod_id),
-      SymbolicModule::Alias(target) => target,
-    };
-    loop {
-      let symbolic_module = map.get(mod_name.as_ref())?;
-      match symbolic_module {
-        SymbolicModule::Alias(target) => {
-          debug_assert!(mod_name != target);
-          mod_name = target;
-        }
+    self.by_name(asserted_module_type, |map| {
+      let first_symbolic_module = map.get(name.as_ref())?;
+      let mut mod_name = match first_symbolic_module {
         SymbolicModule::Mod(mod_id) => return Some(*mod_id),
+        SymbolicModule::Alias(target) => target,
+      };
+      loop {
+        let symbolic_module = map.get(mod_name.as_ref())?;
+        match symbolic_module {
+          SymbolicModule::Alias(target) => {
+            debug_assert!(mod_name != target);
+            mod_name = target;
+          }
+          SymbolicModule::Mod(mod_id) => return Some(*mod_id),
+        }
       }
-    }
+    })
   }
 
   pub(crate) fn new_json_module(
-    &mut self,
+    &self,
     scope: &mut v8::HandleScope,
     name: ModuleName,
     source: ModuleCode,
@@ -448,7 +492,11 @@ impl ModuleMap {
 
     let handle = v8::Global::<v8::Module>::new(tc_scope, module);
     let value_handle = v8::Global::<v8::Value>::new(tc_scope, parsed_json);
-    self.json_value_store.insert(handle.clone(), value_handle);
+    self
+      .data
+      .borrow_mut()
+      .json_value_store
+      .insert(handle.clone(), value_handle);
 
     let id =
       self.create_module_info(name, ModuleType::Json, handle, false, vec![]);
@@ -458,7 +506,7 @@ impl ModuleMap {
 
   /// Create and compile an ES module.
   pub(crate) fn new_es_module(
-    &mut self,
+    &self,
     scope: &mut v8::HandleScope,
     main: bool,
     name: ModuleName,
@@ -517,7 +565,7 @@ impl ModuleMap {
         return Err(ModuleError::Exception(exception));
       }
 
-      let module_specifier = match self.loader.resolve(
+      let module_specifier = match self.loader.borrow().resolve(
         &import_specifier,
         name.as_ref(),
         if is_dynamic_import {
@@ -539,12 +587,13 @@ impl ModuleMap {
     }
 
     if main {
-      let maybe_main_module = self.info.iter().find(|module| module.main);
-      if let Some(main_module) = maybe_main_module {
+      let data = self.data.borrow();
+      if let Some(main_module) = data.main_module_id {
+        let main_name = self.get_name_by_id(main_module).unwrap();
         return Err(ModuleError::Other(generic_error(
           format!("Trying to create \"main\" module ({:?}), when one already exists ({:?})",
           name.as_ref(),
-          main_module.name,
+          main_name,
         ))));
       }
     }
@@ -562,7 +611,7 @@ impl ModuleMap {
   }
 
   pub(crate) fn instantiate_module(
-    &mut self,
+    &self,
     scope: &mut v8::HandleScope,
     id: ModuleId,
   ) -> Result<(), v8::Global<v8::Value>> {
@@ -606,10 +655,9 @@ impl ModuleMap {
 
     let referrer_global = v8::Global::new(scope, referrer);
 
-    let referrer_info = module_map
-      .get_info(&referrer_global)
+    let referrer_name = module_map
+      .get_name_by_module(&referrer_global)
       .expect("ModuleInfo not found");
-    let referrer_name = referrer_info.name.as_str();
 
     let specifier_str = specifier.to_rust_string_lossy(scope);
 
@@ -621,7 +669,7 @@ impl ModuleMap {
     let maybe_module = module_map.resolve_callback(
       scope,
       &specifier_str,
-      referrer_name,
+      &referrer_name,
       assertions,
     );
     if let Some(module) = maybe_module {
@@ -645,6 +693,7 @@ impl ModuleMap {
   ) -> Option<v8::Local<'s, v8::Module>> {
     let resolved_specifier = self
       .loader
+      .borrow()
       .resolve(specifier, referrer, ResolutionKind::Import)
       .expect("Module should have been already resolved");
 
@@ -660,10 +709,6 @@ impl ModuleMap {
     None
   }
 
-  pub(crate) fn clear(&mut self) {
-    *self = Self::new(self.loader.clone())
-  }
-
   pub(crate) fn get_handle_by_name(
     &self,
     name: impl AsRef<str>,
@@ -675,7 +720,7 @@ impl ModuleMap {
   }
 
   pub(crate) fn inject_handle(
-    &mut self,
+    &self,
     name: ModuleName,
     module_type: ModuleType,
     handle: v8::Global<v8::Module>,
@@ -684,26 +729,32 @@ impl ModuleMap {
   }
 
   fn create_module_info(
-    &mut self,
+    &self,
     name: FastString,
     module_type: ModuleType,
     handle: v8::Global<v8::Module>,
     main: bool,
     requests: Vec<ModuleRequest>,
   ) -> ModuleId {
-    let id = self.handles.len();
+    let mut data = self.data.borrow_mut();
+    let id = data.handles.len();
     let (name1, name2) = name.into_cheap_copy();
-    self
-      .by_name_mut(module_type.into())
-      .insert(name1, SymbolicModule::Mod(id));
-    self.handles.push(handle);
-    self.info.push(ModuleInfo {
+    data.handles_inverted.insert(handle.clone(), id);
+    data.handles.push(handle);
+    if main {
+      data.main_module_id = Some(id);
+    }
+    data.info.push(ModuleInfo {
       id,
       main,
       name: name2,
       requests,
       module_type,
     });
+    drop(data);
+    self.by_name_mut(module_type.into(), |map| {
+      map.insert(name1, SymbolicModule::Mod(id))
+    });
 
     id
   }
@@ -711,8 +762,9 @@ impl ModuleMap {
   pub(crate) fn get_requested_modules(
     &self,
     id: ModuleId,
-  ) -> Option<&Vec<ModuleRequest>> {
-    self.info.get(id).map(|i| &i.requests)
+  ) -> Option<Vec<ModuleRequest>> {
+    // TODO(mmastrac): Remove cloning. We were originally cloning this at the call sites but that's no excuse.
+    self.data.borrow().info.get(id).map(|i| i.requests.clone())
   }
 
   fn is_registered(
@@ -721,43 +773,47 @@ impl ModuleMap {
     asserted_module_type: AssertedModuleType,
   ) -> bool {
     if let Some(id) = self.get_id(specifier.as_ref(), asserted_module_type) {
-      let info = self.get_info_by_id(id).unwrap();
-      return asserted_module_type == info.module_type.into();
+      return asserted_module_type
+        == self.data.borrow().info.get(id).unwrap().module_type.into();
     }
 
     false
   }
 
-  pub(crate) fn by_name(
+  pub(crate) fn by_name<T>(
     &self,
     asserted_module_type: AssertedModuleType,
-  ) -> &HashMap<ModuleName, SymbolicModule> {
+    f: impl FnOnce(&HashMap<ModuleName, SymbolicModule>) -> T,
+  ) -> T {
     match asserted_module_type {
-      AssertedModuleType::Json => &self.by_name_json,
-      AssertedModuleType::JavaScriptOrWasm => &self.by_name_js,
+      AssertedModuleType::Json => f(&self.data.borrow().by_name_json),
+      AssertedModuleType::JavaScriptOrWasm => f(&self.data.borrow().by_name_js),
     }
   }
 
-  pub(crate) fn by_name_mut(
-    &mut self,
+  pub(crate) fn by_name_mut<T>(
+    &self,
     asserted_module_type: AssertedModuleType,
-  ) -> &mut HashMap<ModuleName, SymbolicModule> {
+    f: impl FnOnce(&mut HashMap<ModuleName, SymbolicModule>) -> T,
+  ) -> T {
     match asserted_module_type {
-      AssertedModuleType::Json => &mut self.by_name_json,
-      AssertedModuleType::JavaScriptOrWasm => &mut self.by_name_js,
+      AssertedModuleType::Json => f(&mut self.data.borrow_mut().by_name_json),
+      AssertedModuleType::JavaScriptOrWasm => {
+        f(&mut self.data.borrow_mut().by_name_js)
+      }
     }
   }
 
   pub(crate) fn alias(
-    &mut self,
+    &self,
     name: FastString,
     asserted_module_type: AssertedModuleType,
     target: FastString,
   ) {
     debug_assert_ne!(name, target);
-    self
-      .by_name_mut(asserted_module_type)
-      .insert(name, SymbolicModule::Alias(target));
+    self.by_name_mut(asserted_module_type, |map| {
+      map.insert(name, SymbolicModule::Alias(target))
+    });
   }
 
   #[cfg(test)]
@@ -766,34 +822,56 @@ impl ModuleMap {
     name: &str,
     asserted_module_type: AssertedModuleType,
   ) -> bool {
-    let cond = self.by_name(asserted_module_type).get(name);
-    matches!(cond, Some(SymbolicModule::Alias(_)))
+    self.by_name(asserted_module_type, |map| {
+      matches!(map.get(name), Some(SymbolicModule::Alias(_)))
+    })
   }
 
   pub(crate) fn get_handle(
     &self,
     id: ModuleId,
   ) -> Option<v8::Global<v8::Module>> {
-    self.handles.get(id).cloned()
+    self.data.borrow().handles.get(id).cloned()
   }
 
-  pub(crate) fn get_info(
+  pub(crate) fn get_name_by_module(
     &self,
     global: &v8::Global<v8::Module>,
-  ) -> Option<&ModuleInfo> {
-    if let Some(id) = self.handles.iter().position(|module| module == global) {
-      return self.info.get(id);
+  ) -> Option<String> {
+    if let Some(id) = self.data.borrow().handles_inverted.get(global) {
+      self.get_name_by_id(*id)
+    } else {
+      None
     }
+  }
 
-    None
+  pub(crate) fn is_main_module(&self, global: &v8::Global<v8::Module>) -> bool {
+    let data = self.data.borrow();
+    data
+      .main_module_id
+      .map(|id| data.handles_inverted.get(global) == Some(&id))
+      .unwrap_or_default()
   }
 
-  pub(crate) fn get_info_by_id(&self, id: ModuleId) -> Option<&ModuleInfo> {
-    self.info.get(id)
+  pub(crate) fn get_name_by_id(&self, id: ModuleId) -> Option<String> {
+    // TODO(mmastrac): Don't clone
+    self
+      .data
+      .borrow()
+      .info
+      .get(id)
+      .map(|info| info.name.as_str().to_owned())
+  }
+
+  pub(crate) fn get_module_type_by_id(
+    &self,
+    id: ModuleId,
+  ) -> Option<ModuleType> {
+    self.data.borrow().info.get(id).map(|info| info.module_type)
   }
 
   pub(crate) async fn load_main(
-    module_map_rc: Rc<RefCell<ModuleMap>>,
+    module_map_rc: Rc<ModuleMap>,
     specifier: impl AsRef<str>,
   ) -> Result<RecursiveModuleLoad, Error> {
     let load =
@@ -803,7 +881,7 @@ impl ModuleMap {
   }
 
   pub(crate) async fn load_side(
-    module_map_rc: Rc<RefCell<ModuleMap>>,
+    module_map_rc: Rc<ModuleMap>,
     specifier: impl AsRef<str>,
   ) -> Result<RecursiveModuleLoad, Error> {
     let load =
@@ -814,7 +892,7 @@ impl ModuleMap {
 
   // Initiate loading of a module graph imported using `import()`.
   pub(crate) fn load_dynamic_import(
-    module_map_rc: Rc<RefCell<ModuleMap>>,
+    module_map_rc: Rc<ModuleMap>,
     specifier: &str,
     referrer: &str,
     asserted_module_type: AssertedModuleType,
@@ -827,15 +905,20 @@ impl ModuleMap {
       module_map_rc.clone(),
     );
 
-    let this: &mut ModuleMap = &mut module_map_rc.borrow_mut();
-    this.dynamic_import_map.insert(load.id, resolver_handle);
+    module_map_rc
+      .dynamic_import_map
+      .borrow_mut()
+      .insert(load.id, resolver_handle);
 
-    let loader = this.loader.clone();
-    let resolve_result =
-      loader.resolve(specifier, referrer, ResolutionKind::DynamicImport);
+    let loader = module_map_rc.loader.clone();
+    let resolve_result = loader.borrow().resolve(
+      specifier,
+      referrer,
+      ResolutionKind::DynamicImport,
+    );
     let fut = match resolve_result {
       Ok(module_specifier) => {
-        if this.is_registered(module_specifier, asserted_module_type) {
+        if module_map_rc.is_registered(module_specifier, asserted_module_type) {
           async move { (load.id, Ok(load)) }.boxed_local()
         } else {
           async move { (load.id, load.prepare().await.map(|()| load)) }
@@ -844,12 +927,308 @@ impl ModuleMap {
       }
       Err(error) => async move { (load.id, Err(error)) }.boxed_local(),
     };
-    this.preparing_dynamic_imports.push(fut);
+    module_map_rc
+      .preparing_dynamic_imports
+      .borrow_mut()
+      .push(fut);
   }
 
   pub(crate) fn has_pending_dynamic_imports(&self) -> bool {
-    !(self.preparing_dynamic_imports.is_empty()
-      && self.pending_dynamic_imports.is_empty())
+    !(self.preparing_dynamic_imports.borrow().is_empty()
+      && self.pending_dynamic_imports.borrow().is_empty())
+  }
+
+  pub(crate) fn has_pending_dyn_module_evaluation(&self) -> bool {
+    !self.pending_dyn_mod_evaluations.borrow().is_empty()
+  }
+
+  fn dynamic_import_module_evaluate(
+    &self,
+    scope: &mut v8::HandleScope,
+    load_id: ModuleLoadId,
+    id: ModuleId,
+  ) -> Result<(), Error> {
+    let module_handle = self.get_handle(id).expect("ModuleInfo not found");
+
+    let status = {
+      let module = module_handle.open(scope);
+      module.get_status()
+    };
+
+    match status {
+      v8::ModuleStatus::Instantiated | v8::ModuleStatus::Evaluated => {}
+      _ => return Ok(()),
+    }
+
+    // IMPORTANT: Top-level-await is enabled, which means that return value
+    // of module evaluation is a promise.
+    //
+    // This promise is internal, and not the same one that gets returned to
+    // the user. We add an empty `.catch()` handler so that it does not result
+    // in an exception if it rejects. That will instead happen for the other
+    // promise if not handled by the user.
+    //
+    // For more details see:
+    // https://github.com/denoland/deno/issues/4908
+    // https://v8.dev/features/top-level-await#module-execution-order
+    let tc_scope = &mut v8::TryCatch::new(scope);
+    let module = v8::Local::new(tc_scope, &module_handle);
+    let maybe_value = module.evaluate(tc_scope);
+
+    // Update status after evaluating.
+    let status = module.get_status();
+
+    if let Some(value) = maybe_value {
+      assert!(
+        status == v8::ModuleStatus::Evaluated
+          || status == v8::ModuleStatus::Errored
+      );
+      let promise = v8::Local::<v8::Promise>::try_from(value)
+        .expect("Expected to get promise as module evaluation result");
+      let empty_fn =
+        crate::runtime::bindings::create_empty_fn(tc_scope).unwrap();
+      promise.catch(tc_scope, empty_fn);
+      let promise_global = v8::Global::new(tc_scope, promise);
+      let module_global = v8::Global::new(tc_scope, module);
+
+      let dyn_import_mod_evaluate = DynImportModEvaluate {
+        load_id,
+        module_id: id,
+        promise: promise_global,
+        module: module_global,
+      };
+
+      self
+        .pending_dyn_mod_evaluations
+        .borrow_mut()
+        .push(dyn_import_mod_evaluate);
+    } else if tc_scope.has_terminated() || tc_scope.is_execution_terminating() {
+      return Err(
+        generic_error("Cannot evaluate dynamically imported module, because JavaScript execution has been terminated.")
+      );
+    } else {
+      assert!(status == v8::ModuleStatus::Errored);
+    }
+
+    Ok(())
+  }
+
+  // Returns true if some dynamic import was resolved.
+  pub(crate) fn evaluate_dyn_imports(
+    &self,
+    scope: &mut v8::HandleScope,
+  ) -> bool {
+    let pending =
+      std::mem::take(self.pending_dyn_mod_evaluations.borrow_mut().deref_mut());
+    if pending.is_empty() {
+      return false;
+    }
+    let mut resolved_any = false;
+    let mut still_pending = vec![];
+    for pending_dyn_evaluate in pending {
+      let maybe_result = {
+        let module_id = pending_dyn_evaluate.module_id;
+        let promise = pending_dyn_evaluate.promise.open(scope);
+        let _module = pending_dyn_evaluate.module.open(scope);
+        let promise_state = promise.state();
+
+        match promise_state {
+          v8::PromiseState::Pending => {
+            still_pending.push(pending_dyn_evaluate);
+            None
+          }
+          v8::PromiseState::Fulfilled => {
+            Some(Ok((pending_dyn_evaluate.load_id, module_id)))
+          }
+          v8::PromiseState::Rejected => {
+            let exception = promise.result(scope);
+            let exception = v8::Global::new(scope, exception);
+            Some(Err((pending_dyn_evaluate.load_id, exception)))
+          }
+        }
+      };
+
+      if let Some(result) = maybe_result {
+        resolved_any = true;
+        match result {
+          Ok((dyn_import_id, module_id)) => {
+            self.dynamic_import_resolve(scope, dyn_import_id, module_id);
+          }
+          Err((dyn_import_id, exception)) => {
+            self.dynamic_import_reject(scope, dyn_import_id, exception);
+          }
+        }
+      }
+    }
+    *self.pending_dyn_mod_evaluations.borrow_mut() = still_pending;
+    resolved_any
+  }
+
+  pub(crate) fn dynamic_import_reject(
+    &self,
+    scope: &mut v8::HandleScope,
+    id: ModuleLoadId,
+    exception: v8::Global<v8::Value>,
+  ) {
+    let resolver_handle = self
+      .dynamic_import_map
+      .borrow_mut()
+      .remove(&id)
+      .expect("Invalid dynamic import id");
+    let resolver = resolver_handle.open(scope);
+
+    let exception = v8::Local::new(scope, exception);
+    resolver.reject(scope, exception).unwrap();
+    scope.perform_microtask_checkpoint();
+  }
+
+  pub(crate) fn dynamic_import_resolve(
+    &self,
+    scope: &mut v8::HandleScope,
+    id: ModuleLoadId,
+    mod_id: ModuleId,
+  ) {
+    let resolver_handle = self
+      .dynamic_import_map
+      .borrow_mut()
+      .remove(&id)
+      .expect("Invalid dynamic import id");
+    let resolver = resolver_handle.open(scope);
+
+    let module = self
+      .get_handle(mod_id)
+      .map(|handle| v8::Local::new(scope, handle))
+      .expect("Dyn import module info not found");
+    // Resolution success
+    assert_eq!(module.get_status(), v8::ModuleStatus::Evaluated);
+
+    // IMPORTANT: No borrows to `ModuleMap` can be held at this point because
+    // resolving the promise might initiate another `import()` which will
+    // in turn call `bindings::host_import_module_dynamically_callback` which
+    // will reach into `ModuleMap` from within the isolate.
+    let module_namespace = module.get_module_namespace();
+    resolver.resolve(scope, module_namespace).unwrap();
+    self.dyn_module_evaluate_idle_counter.set(0);
+    scope.perform_microtask_checkpoint();
+  }
+
+  pub(crate) fn poll_prepare_dyn_imports(
+    &self,
+    scope: &mut v8::HandleScope,
+    cx: &mut Context,
+  ) -> Poll<Result<(), Error>> {
+    if self.preparing_dynamic_imports.borrow().is_empty() {
+      return Poll::Ready(Ok(()));
+    }
+
+    loop {
+      let poll_result = self
+        .preparing_dynamic_imports
+        .borrow_mut()
+        .poll_next_unpin(cx);
+
+      if let Poll::Ready(Some(prepare_poll)) = poll_result {
+        let dyn_import_id = prepare_poll.0;
+        let prepare_result = prepare_poll.1;
+
+        match prepare_result {
+          Ok(load) => {
+            self
+              .pending_dynamic_imports
+              .borrow_mut()
+              .push(load.into_future());
+          }
+          Err(err) => {
+            let exception = to_v8_type_error(scope, err);
+            self.dynamic_import_reject(scope, dyn_import_id, exception);
+          }
+        }
+        // Continue polling for more prepared dynamic imports.
+        continue;
+      }
+
+      // There are no active dynamic import loads, or none are ready.
+      return Poll::Ready(Ok(()));
+    }
+  }
+
+  pub(crate) fn poll_dyn_imports(
+    &self,
+    scope: &mut v8::HandleScope,
+    cx: &mut Context,
+  ) -> Poll<Result<(), Error>> {
+    if self.pending_dynamic_imports.borrow().is_empty() {
+      return Poll::Ready(Ok(()));
+    }
+
+    loop {
+      let poll_result = self
+        .pending_dynamic_imports
+        .borrow_mut()
+        .poll_next_unpin(cx);
+
+      if let Poll::Ready(Some(load_stream_poll)) = poll_result {
+        let maybe_result = load_stream_poll.0;
+        let mut load = load_stream_poll.1;
+        let dyn_import_id = load.id;
+
+        if let Some(load_stream_result) = maybe_result {
+          match load_stream_result {
+            Ok((request, info)) => {
+              // A module (not necessarily the one dynamically imported) has been
+              // fetched. Create and register it, and if successful, poll for the
+              // next recursive-load event related to this dynamic import.
+              let register_result =
+                load.register_and_recurse(scope, &request, info);
+
+              match register_result {
+                Ok(()) => {
+                  // Keep importing until it's fully drained
+                  self
+                    .pending_dynamic_imports
+                    .borrow_mut()
+                    .push(load.into_future());
+                }
+                Err(err) => {
+                  let exception = match err {
+                    ModuleError::Exception(e) => e,
+                    ModuleError::Other(e) => to_v8_type_error(scope, e),
+                  };
+                  self.dynamic_import_reject(scope, dyn_import_id, exception)
+                }
+              }
+            }
+            Err(err) => {
+              // A non-javascript error occurred; this could be due to a an invalid
+              // module specifier, or a problem with the source map, or a failure
+              // to fetch the module source code.
+              let exception = to_v8_type_error(scope, err);
+              self.dynamic_import_reject(scope, dyn_import_id, exception);
+            }
+          }
+        } else {
+          // The top-level module from a dynamic import has been instantiated.
+          // Load is done.
+          let module_id =
+            load.root_module_id.expect("Root module should be loaded");
+          let result = self.instantiate_module(scope, module_id);
+          if let Err(exception) = result {
+            self.dynamic_import_reject(scope, dyn_import_id, exception);
+          }
+          self.dynamic_import_module_evaluate(
+            scope,
+            dyn_import_id,
+            module_id,
+          )?;
+        }
+
+        // Continue polling for more ready dynamic imports.
+        continue;
+      }
+
+      // There are no active dynamic import loads, or none are ready.
+      return Poll::Ready(Ok(()));
+    }
   }
 
   /// Returns the namespace object of a module.
@@ -886,12 +1265,12 @@ impl ModuleMap {
   /// Clear the module map, meant to be used after initializing extensions.
   /// Optionally pass a list of exceptions `(old_name, new_name)` representing
   /// specifiers which will be renamed and preserved in the module map.
-  pub fn clear_module_map(&mut self, exceptions: &'static [&'static str]) {
+  pub fn clear_module_map(&self, exceptions: &'static [&'static str]) {
     let handles = exceptions
       .iter()
       .map(|mod_name| (self.get_handle_by_name(mod_name).unwrap(), mod_name))
       .collect::<Vec<_>>();
-    self.clear();
+    *self.data.borrow_mut() = ModuleMapData::default();
     for (handle, new_name) in handles {
       self.inject_handle(
         ModuleName::from_static(new_name),
@@ -906,7 +1285,8 @@ impl ModuleMap {
     scope: &mut v8::HandleScope,
     module_id: ModuleId,
   ) -> Vec<v8::Global<v8::Message>> {
-    let module_handle = self.handles.get(module_id).unwrap();
+    let data = self.data.borrow();
+    let module_handle = data.handles.get(module_id).unwrap();
 
     let module = v8::Local::new(scope, module_handle);
     let stalled = module.get_stalled_top_level_await_message(scope);
@@ -922,8 +1302,14 @@ impl ModuleMap {
     scope: &mut v8::HandleScope,
   ) -> Vec<v8::Global<v8::Message>> {
     // First check if that's root module
-    let root_module_id =
-      self.info.iter().filter(|m| m.main).map(|m| m.id).next();
+    let root_module_id = self
+      .data
+      .borrow()
+      .info
+      .iter()
+      .filter(|m| m.main)
+      .map(|m| m.id)
+      .next();
 
     if let Some(root_module_id) = root_module_id {
       let messages = self
@@ -935,7 +1321,7 @@ impl ModuleMap {
 
     // It wasn't a top module, so iterate over all modules and try to find
     // any with stalled top level await
-    for module_id in 0..self.handles.len() {
+    for module_id in 0..self.data.borrow().handles.len() {
       let messages =
         self.get_stalled_top_level_await_message_for_module(scope, module_id);
       if !messages.is_empty() {
@@ -945,6 +1331,33 @@ impl ModuleMap {
 
     vec![]
   }
+
+  // TODO(mmastrac): this is better than giving the entire crate access to the internals.
+  #[cfg(test)]
+  pub fn assert_module_map(&self, modules: &Vec<ModuleInfo>) {
+    let data = self.data.borrow();
+    assert_eq!(data.handles.len(), modules.len());
+    assert_eq!(data.info.len(), modules.len());
+    assert_eq!(data.next_load_id as usize, modules.len());
+    drop(data);
+
+    assert_eq!(
+      self.by_name(AssertedModuleType::Json, |map| map.len())
+        + self.by_name(AssertedModuleType::JavaScriptOrWasm, |map| map.len()),
+      modules.len()
+    );
+
+    for info in modules {
+      let data = self.data.borrow();
+      assert!(data.handles.get(info.id).is_some());
+      assert_eq!(data.info.get(info.id).unwrap(), info);
+      drop(data);
+      assert!(self.by_name(AssertedModuleType::JavaScriptOrWasm, |map| map
+        .get(&info.name)
+        .unwrap()
+        == &SymbolicModule::Mod(info.id)));
+    }
+  }
 }
 
 impl Default for ModuleMap {
@@ -967,6 +1380,7 @@ fn json_module_evaluation_steps<'a>(
 
   let handle = v8::Global::<v8::Module>::new(tc_scope, module);
   let value_handle = module_map
+    .data
     .borrow_mut()
     .json_value_store
     .remove(&handle)
diff --git a/core/modules/mod.rs b/core/modules/mod.rs
index 64093f6..d077c4b 100644
--- a/core/modules/mod.rs
+++ b/core/modules/mod.rs
@@ -41,8 +41,6 @@ pub use loaders::ModuleLoader;
 pub use loaders::NoopModuleLoader;
 pub use loaders::StaticModuleLoader;
 pub(crate) use map::ModuleMap;
-#[cfg(test)]
-pub(crate) use map::SymbolicModule;
 
 pub type ModuleId = usize;
 pub(crate) type ModuleLoadId = i32;
@@ -307,7 +305,7 @@ pub(crate) struct RecursiveModuleLoad {
   root_asserted_module_type: Option<AssertedModuleType>,
   root_module_type: Option<ModuleType>,
   state: LoadState,
-  module_map_rc: Rc<RefCell<ModuleMap>>,
+  module_map_rc: Rc<ModuleMap>,
   pending: FuturesUnordered<Pin<Box<ModuleLoadFuture>>>,
   visited: HashSet<ModuleRequest>,
   visited_as_alias: Rc<RefCell<HashSet<String>>>,
@@ -321,12 +319,12 @@ impl RecursiveModuleLoad {
   ///
   /// The module corresponding for the given `specifier` will be marked as
   // "the main module" (`import.meta.main` will return `true` for this module).
-  fn main(specifier: &str, module_map_rc: Rc<RefCell<ModuleMap>>) -> Self {
+  fn main(specifier: &str, module_map_rc: Rc<ModuleMap>) -> Self {
     Self::new(LoadInit::Main(specifier.to_string()), module_map_rc)
   }
 
   /// Starts a new asynchronous load of the module graph for given specifier.
-  fn side(specifier: &str, module_map_rc: Rc<RefCell<ModuleMap>>) -> Self {
+  fn side(specifier: &str, module_map_rc: Rc<ModuleMap>) -> Self {
     Self::new(LoadInit::Side(specifier.to_string()), module_map_rc)
   }
 
@@ -336,7 +334,7 @@ impl RecursiveModuleLoad {
     specifier: &str,
     referrer: &str,
     asserted_module_type: AssertedModuleType,
-    module_map_rc: Rc<RefCell<ModuleMap>>,
+    module_map_rc: Rc<ModuleMap>,
   ) -> Self {
     Self::new(
       LoadInit::DynamicImport(
@@ -348,14 +346,9 @@ impl RecursiveModuleLoad {
     )
   }
 
-  fn new(init: LoadInit, module_map_rc: Rc<RefCell<ModuleMap>>) -> Self {
-    let id = {
-      let mut module_map = module_map_rc.borrow_mut();
-      let id = module_map.next_load_id;
-      module_map.next_load_id += 1;
-      id
-    };
-    let loader = module_map_rc.borrow().loader.clone();
+  fn new(init: LoadInit, module_map_rc: Rc<ModuleMap>) -> Self {
+    let id = module_map_rc.next_load_id();
+    let loader = module_map_rc.loader.borrow().clone();
     let asserted_module_type = match init {
       LoadInit::DynamicImport(_, _, module_type) => module_type,
       _ => AssertedModuleType::JavaScriptOrWasm,
@@ -376,19 +369,13 @@ impl RecursiveModuleLoad {
     // FIXME(bartlomieju): this seems fishy
     // Ignore the error here, let it be hit in `Stream::poll_next()`.
     if let Ok(root_specifier) = load.resolve_root() {
-      if let Some(module_id) = module_map_rc
-        .borrow()
-        .get_id(root_specifier, asserted_module_type)
+      if let Some(module_id) =
+        module_map_rc.get_id(root_specifier, asserted_module_type)
       {
         load.root_module_id = Some(module_id);
         load.root_asserted_module_type = Some(asserted_module_type);
-        load.root_module_type = Some(
-          module_map_rc
-            .borrow()
-            .get_info_by_id(module_id)
-            .unwrap()
-            .module_type,
-        );
+        load.root_module_type =
+          Some(module_map_rc.get_module_type_by_id(module_id).unwrap());
       }
     }
     load
@@ -474,7 +461,7 @@ impl RecursiveModuleLoad {
     let module_url_found = if let Some(module_url_found) = module_url_found {
       let (module_url_found1, module_url_found2) =
         module_url_found.into_cheap_copy();
-      self.module_map_rc.borrow_mut().alias(
+      self.module_map_rc.alias(
         module_url_specified,
         expected_asserted_module_type,
         module_url_found1,
@@ -486,7 +473,6 @@ impl RecursiveModuleLoad {
 
     let maybe_module_id = self
       .module_map_rc
-      .borrow()
       .get_id(&module_url_found, expected_asserted_module_type);
     let module_id = match maybe_module_id {
       Some(id) => {
@@ -497,16 +483,14 @@ impl RecursiveModuleLoad {
         id
       }
       None => match module_source.module_type {
-        ModuleType::JavaScript => {
-          self.module_map_rc.borrow_mut().new_es_module(
-            scope,
-            self.is_currently_loading_main_module(),
-            module_url_found,
-            module_source.code,
-            self.is_dynamic_import(),
-          )?
-        }
-        ModuleType::Json => self.module_map_rc.borrow_mut().new_json_module(
+        ModuleType::JavaScript => self.module_map_rc.new_es_module(
+          scope,
+          self.is_currently_loading_main_module(),
+          module_url_found,
+          module_source.code,
+          self.is_dynamic_import(),
+        )?,
+        ModuleType::Json => self.module_map_rc.new_json_module(
           scope,
           module_url_found,
           module_source.code,
@@ -530,7 +514,6 @@ impl RecursiveModuleLoad {
       let referrer = ModuleSpecifier::parse(&module_request.specifier).unwrap();
       let imports = self
         .module_map_rc
-        .borrow()
         .get_requested_modules(module_id)
         .unwrap()
         .clone();
@@ -541,7 +524,7 @@ impl RecursiveModuleLoad {
             .borrow()
             .contains(&module_request.specifier)
         {
-          if let Some(module_id) = self.module_map_rc.borrow().get_id(
+          if let Some(module_id) = self.module_map_rc.get_id(
             module_request.specifier.as_str(),
             module_request.asserted_module_type,
           ) {
@@ -725,7 +708,6 @@ pub(crate) struct ModuleRequest {
 pub(crate) struct ModuleInfo {
   #[allow(unused)]
   pub id: ModuleId,
-  // Used in "bindings.rs" for "import.meta.main" property value.
   pub main: bool,
   pub name: ModuleName,
   pub requests: Vec<ModuleRequest>,
diff --git a/core/runtime/bindings.rs b/core/runtime/bindings.rs
index 81822ef..68ea720 100644
--- a/core/runtime/bindings.rs
+++ b/core/runtime/bindings.rs
@@ -343,21 +343,21 @@ pub extern "C" fn host_initialize_import_meta_object_callback(
 ) {
   // SAFETY: `CallbackScope` can be safely constructed from `Local<Context>`
   let scope = &mut unsafe { v8::CallbackScope::new(context) };
-  let module_map_rc = JsRealm::module_map_from(scope);
-  let module_map = module_map_rc.borrow();
+  let module_map = JsRealm::module_map_from(scope);
 
   let module_global = v8::Global::new(scope, module);
-  let info = module_map
-    .get_info(&module_global)
+  let name = module_map
+    .get_name_by_module(&module_global)
     .expect("Module not found");
 
   let url_key = v8::String::new_external_onebyte_static(scope, b"url").unwrap();
-  let url_val = info.name.v8(scope);
+  let url_val = v8::String::new(scope, &name).unwrap();
   meta.create_data_property(scope, url_key.into(), url_val.into());
 
   let main_key =
     v8::String::new_external_onebyte_static(scope, b"main").unwrap();
-  let main_val = v8::Boolean::new(scope, info.main);
+  let main = module_map.is_main_module(&module_global);
+  let main_val = v8::Boolean::new(scope, main);
   meta.create_data_property(scope, main_key.into(), main_val.into());
 
   let builder =
@@ -387,7 +387,7 @@ fn import_meta_resolve(
     url_prop.to_rust_string_lossy(scope)
   };
   let module_map_rc = JsRealm::module_map_from(scope);
-  let loader = module_map_rc.borrow().loader.clone();
+  let loader = module_map_rc.loader.clone();
   let specifier_str = specifier.to_rust_string_lossy(scope);
 
   if specifier_str.starts_with("npm:") {
@@ -395,8 +395,11 @@ fn import_meta_resolve(
     return;
   }
 
-  match loader.resolve(&specifier_str, &referrer, ResolutionKind::DynamicImport)
-  {
+  match loader.borrow().resolve(
+    &specifier_str,
+    &referrer,
+    ResolutionKind::DynamicImport,
+  ) {
     Ok(resolved) => {
       let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap();
       rv.set(resolved_val);
diff --git a/core/runtime/jsrealm.rs b/core/runtime/jsrealm.rs
index d501873..10f9d95 100644
--- a/core/runtime/jsrealm.rs
+++ b/core/runtime/jsrealm.rs
@@ -2,12 +2,10 @@
 use super::bindings;
 use crate::error::exception_to_err_result;
 use crate::error::generic_error;
-use crate::error::to_v8_type_error;
 use crate::module_specifier::ModuleSpecifier;
 use crate::modules::ModuleCode;
 use crate::modules::ModuleError;
 use crate::modules::ModuleId;
-use crate::modules::ModuleLoadId;
 use crate::modules::ModuleMap;
 use crate::ops::OpCtx;
 use crate::ops::PendingOp;
@@ -24,8 +22,6 @@ use std::hash::BuildHasherDefault;
 use std::hash::Hasher;
 use std::option::Option;
 use std::rc::Rc;
-use std::task::Context;
-use std::task::Poll;
 use v8::HandleScope;
 use v8::Local;
 
@@ -48,13 +44,6 @@ impl Hasher for IdentityHasher {
   }
 }
 
-struct DynImportModEvaluate {
-  load_id: ModuleLoadId,
-  module_id: ModuleId,
-  promise: v8::Global<v8::Promise>,
-  module: v8::Global<v8::Module>,
-}
-
 pub(crate) struct ModEvaluate {
   promise: Option<v8::Global<v8::Promise>>,
   pub(crate) has_evaluated: bool,
@@ -71,7 +60,6 @@ pub(crate) struct ContextState {
   pub(crate) js_wasm_streaming_cb: Option<Rc<v8::Global<v8::Function>>>,
   pub(crate) pending_promise_rejections:
     VecDeque<(v8::Global<v8::Promise>, v8::Global<v8::Value>)>,
-  pending_dyn_mod_evaluate: Vec<DynImportModEvaluate>,
   pub(crate) pending_mod_evaluate: Option<ModEvaluate>,
   pub(crate) unrefed_ops: HashSet<i32, BuildHasherDefault<IdentityHasher>>,
   pub(crate) pending_ops: JoinSet<PendingOp>,
@@ -134,7 +122,7 @@ pub struct JsRealm(pub(crate) JsRealmInner);
 pub(crate) struct JsRealmInner {
   context_state: Rc<RefCell<ContextState>>,
   context: Rc<v8::Global<v8::Context>>,
-  module_map: Rc<RefCell<ModuleMap>>,
+  module_map: Rc<ModuleMap>,
   runtime_state: Rc<RefCell<JsRuntimeState>>,
   is_main_realm: bool,
 }
@@ -143,7 +131,7 @@ impl JsRealmInner {
   pub(crate) fn new(
     context_state: Rc<RefCell<ContextState>>,
     context: v8::Global<v8::Context>,
-    module_map: Rc<RefCell<ModuleMap>>,
+    module_map: Rc<ModuleMap>,
     runtime_state: Rc<RefCell<JsRuntimeState>>,
     is_main_realm: bool,
   ) -> Self {
@@ -156,26 +144,27 @@ impl JsRealmInner {
     }
   }
 
+  #[inline(always)]
   pub fn num_pending_ops(&self) -> usize {
     self.context_state.borrow().pending_ops.len()
   }
 
+  #[inline(always)]
   pub fn num_unrefed_ops(&self) -> usize {
     self.context_state.borrow().unrefed_ops.len()
   }
 
+  #[inline(always)]
   pub fn has_pending_dyn_imports(&self) -> bool {
-    self.module_map.borrow().has_pending_dynamic_imports()
+    self.module_map.has_pending_dynamic_imports()
   }
 
+  #[inline(always)]
   pub fn has_pending_dyn_module_evaluation(&self) -> bool {
-    !self
-      .context_state
-      .borrow()
-      .pending_dyn_mod_evaluate
-      .is_empty()
+    self.module_map.has_pending_dyn_module_evaluation()
   }
 
+  #[inline(always)]
   pub fn has_pending_module_evaluation(&self) -> bool {
     self.context_state.borrow().pending_mod_evaluate.is_some()
   }
@@ -191,7 +180,7 @@ impl JsRealmInner {
   }
 
   #[inline(always)]
-  pub(crate) fn module_map(&self) -> Rc<RefCell<ModuleMap>> {
+  pub(crate) fn module_map(&self) -> Rc<ModuleMap> {
     self.module_map.clone()
   }
 
@@ -270,14 +259,9 @@ impl JsRealm {
   }
 
   #[inline(always)]
-  pub(crate) fn module_map_from(
-    scope: &mut v8::HandleScope,
-  ) -> Rc<RefCell<ModuleMap>> {
+  pub(crate) fn module_map_from(scope: &mut v8::HandleScope) -> Rc<ModuleMap> {
     let context = scope.get_current_context();
-    context
-      .get_slot::<Rc<RefCell<ModuleMap>>>(scope)
-      .unwrap()
-      .clone()
+    context.get_slot::<Rc<ModuleMap>>(scope).unwrap().clone()
   }
 
   #[inline(always)]
@@ -402,13 +386,6 @@ impl JsRealm {
     }
   }
 
-  /// Clears all loaded modules
-  /// May not clear all associated memory and should not
-  /// be used in production
-  pub fn clear_modules(&mut self) {
-    self.0.module_map().borrow_mut().clear();
-  }
-
   /// Returns the namespace object of a module.
   ///
   /// This is only available after module evaluation has completed.
@@ -421,7 +398,6 @@ impl JsRealm {
     self
       .0
       .module_map()
-      .borrow()
       .get_module_namespace(&mut self.handle_scope(isolate), module_id)
   }
 
@@ -433,89 +409,9 @@ impl JsRealm {
     self
       .0
       .module_map()
-      .borrow_mut()
       .instantiate_module(&mut self.handle_scope(isolate), id)
   }
 
-  fn dynamic_import_module_evaluate(
-    &self,
-    isolate: &mut v8::Isolate,
-    load_id: ModuleLoadId,
-    id: ModuleId,
-  ) -> Result<(), Error> {
-    let module_handle = self
-      .0
-      .module_map()
-      .borrow()
-      .get_handle(id)
-      .expect("ModuleInfo not found");
-
-    let status = {
-      let scope = &mut self.handle_scope(isolate);
-      let module = module_handle.open(scope);
-      module.get_status()
-    };
-
-    match status {
-      v8::ModuleStatus::Instantiated | v8::ModuleStatus::Evaluated => {}
-      _ => return Ok(()),
-    }
-
-    // IMPORTANT: Top-level-await is enabled, which means that return value
-    // of module evaluation is a promise.
-    //
-    // This promise is internal, and not the same one that gets returned to
-    // the user. We add an empty `.catch()` handler so that it does not result
-    // in an exception if it rejects. That will instead happen for the other
-    // promise if not handled by the user.
-    //
-    // For more details see:
-    // https://github.com/denoland/deno/issues/4908
-    // https://v8.dev/features/top-level-await#module-execution-order
-    let scope = &mut self.handle_scope(isolate);
-    let tc_scope = &mut v8::TryCatch::new(scope);
-    let module = v8::Local::new(tc_scope, &module_handle);
-    let maybe_value = module.evaluate(tc_scope);
-
-    // Update status after evaluating.
-    let status = module.get_status();
-
-    if let Some(value) = maybe_value {
-      assert!(
-        status == v8::ModuleStatus::Evaluated
-          || status == v8::ModuleStatus::Errored
-      );
-      let promise = v8::Local::<v8::Promise>::try_from(value)
-        .expect("Expected to get promise as module evaluation result");
-      let empty_fn = bindings::create_empty_fn(tc_scope).unwrap();
-      promise.catch(tc_scope, empty_fn);
-      let promise_global = v8::Global::new(tc_scope, promise);
-      let module_global = v8::Global::new(tc_scope, module);
-
-      let dyn_import_mod_evaluate = DynImportModEvaluate {
-        load_id,
-        module_id: id,
-        promise: promise_global,
-        module: module_global,
-      };
-
-      self
-        .0
-        .context_state
-        .borrow_mut()
-        .pending_dyn_mod_evaluate
-        .push(dyn_import_mod_evaluate);
-    } else if tc_scope.has_terminated() || tc_scope.is_execution_terminating() {
-      return Err(
-        generic_error("Cannot evaluate dynamically imported module, because JavaScript execution has been terminated.")
-      );
-    } else {
-      assert!(status == v8::ModuleStatus::Errored);
-    }
-
-    Ok(())
-  }
-
   // TODO(bartlomieju): make it return `ModuleEvaluationFuture`?
   /// Evaluates an already instantiated ES module.
   ///
@@ -538,7 +434,6 @@ impl JsRealm {
     let tc_scope = &mut v8::TryCatch::new(scope);
 
     let module = module_map_rc
-      .borrow()
       .get_handle(id)
       .map(|handle| v8::Local::new(tc_scope, handle))
       .expect("ModuleInfo not found");
@@ -552,12 +447,7 @@ impl JsRealm {
       } else {
         "Module not instantiated"
       },
-      module_map_rc
-        .borrow()
-        .get_info_by_id(id)
-        .unwrap()
-        .name
-        .as_str(),
+      module_map_rc.get_name_by_id(id).unwrap(),
       id,
     );
 
@@ -659,212 +549,13 @@ impl JsRealm {
     receiver
   }
 
-  fn dynamic_import_reject(
-    &self,
-    isolate: &mut v8::Isolate,
-    id: ModuleLoadId,
-    exception: v8::Global<v8::Value>,
-  ) {
-    let module_map_rc = self.0.module_map();
-    let scope = &mut self.handle_scope(isolate);
-
-    let resolver_handle = module_map_rc
-      .borrow_mut()
-      .dynamic_import_map
-      .remove(&id)
-      .expect("Invalid dynamic import id");
-    let resolver = resolver_handle.open(scope);
-
-    // IMPORTANT: No borrows to `ModuleMap` can be held at this point because
-    // rejecting the promise might initiate another `import()` which will
-    // in turn call `bindings::host_import_module_dynamically_callback` which
-    // will reach into `ModuleMap` from within the isolate.
-    let exception = v8::Local::new(scope, exception);
-    resolver.reject(scope, exception).unwrap();
-    scope.perform_microtask_checkpoint();
-  }
-
-  fn dynamic_import_resolve(
-    &self,
-    isolate: &mut v8::Isolate,
-    id: ModuleLoadId,
-    mod_id: ModuleId,
-  ) {
-    let state_rc = self.0.runtime_state.clone();
-    let module_map_rc = self.0.module_map();
-    let scope = &mut self.handle_scope(isolate);
-
-    let resolver_handle = module_map_rc
-      .borrow_mut()
-      .dynamic_import_map
-      .remove(&id)
-      .expect("Invalid dynamic import id");
-    let resolver = resolver_handle.open(scope);
-
-    let module = {
-      module_map_rc
-        .borrow()
-        .get_handle(mod_id)
-        .map(|handle| v8::Local::new(scope, handle))
-        .expect("Dyn import module info not found")
-    };
-    // Resolution success
-    assert_eq!(module.get_status(), v8::ModuleStatus::Evaluated);
-
-    // IMPORTANT: No borrows to `ModuleMap` can be held at this point because
-    // resolving the promise might initiate another `import()` which will
-    // in turn call `bindings::host_import_module_dynamically_callback` which
-    // will reach into `ModuleMap` from within the isolate.
-    let module_namespace = module.get_module_namespace();
-    resolver.resolve(scope, module_namespace).unwrap();
-    state_rc.borrow_mut().dyn_module_evaluate_idle_counter = 0;
-    scope.perform_microtask_checkpoint();
-  }
-
-  pub(in crate::runtime) fn prepare_dyn_imports(
-    &self,
-    isolate: &mut v8::Isolate,
-    cx: &mut Context,
-  ) -> Poll<Result<(), Error>> {
-    if self
-      .0
-      .module_map()
-      .borrow()
-      .preparing_dynamic_imports
-      .is_empty()
-    {
-      return Poll::Ready(Ok(()));
-    }
-
-    loop {
-      let poll_result = self
-        .0
-        .module_map()
-        .borrow_mut()
-        .preparing_dynamic_imports
-        .poll_next_unpin(cx);
-
-      if let Poll::Ready(Some(prepare_poll)) = poll_result {
-        let dyn_import_id = prepare_poll.0;
-        let prepare_result = prepare_poll.1;
-
-        match prepare_result {
-          Ok(load) => {
-            self
-              .0
-              .module_map()
-              .borrow_mut()
-              .pending_dynamic_imports
-              .push(load.into_future());
-          }
-          Err(err) => {
-            let exception =
-              to_v8_type_error(&mut self.handle_scope(isolate), err);
-            self.dynamic_import_reject(isolate, dyn_import_id, exception);
-          }
-        }
-        // Continue polling for more prepared dynamic imports.
-        continue;
-      }
-
-      // There are no active dynamic import loads, or none are ready.
-      return Poll::Ready(Ok(()));
-    }
+  pub(crate) fn modules_idle(&self) -> bool {
+    self.0.module_map.dyn_module_evaluate_idle_counter.get() > 1
   }
 
-  pub(in crate::runtime) fn poll_dyn_imports(
-    &self,
-    isolate: &mut v8::Isolate,
-    cx: &mut Context,
-  ) -> Poll<Result<(), Error>> {
-    if self
-      .0
-      .module_map()
-      .borrow()
-      .pending_dynamic_imports
-      .is_empty()
-    {
-      return Poll::Ready(Ok(()));
-    }
-
-    loop {
-      let poll_result = self
-        .0
-        .module_map()
-        .borrow_mut()
-        .pending_dynamic_imports
-        .poll_next_unpin(cx);
-
-      if let Poll::Ready(Some(load_stream_poll)) = poll_result {
-        let maybe_result = load_stream_poll.0;
-        let mut load = load_stream_poll.1;
-        let dyn_import_id = load.id;
-
-        if let Some(load_stream_result) = maybe_result {
-          match load_stream_result {
-            Ok((request, info)) => {
-              // A module (not necessarily the one dynamically imported) has been
-              // fetched. Create and register it, and if successful, poll for the
-              // next recursive-load event related to this dynamic import.
-              let register_result = load.register_and_recurse(
-                &mut self.handle_scope(isolate),
-                &request,
-                info,
-              );
-
-              match register_result {
-                Ok(()) => {
-                  // Keep importing until it's fully drained
-                  self
-                    .0
-                    .module_map()
-                    .borrow_mut()
-                    .pending_dynamic_imports
-                    .push(load.into_future());
-                }
-                Err(err) => {
-                  let exception = match err {
-                    ModuleError::Exception(e) => e,
-                    ModuleError::Other(e) => {
-                      to_v8_type_error(&mut self.handle_scope(isolate), e)
-                    }
-                  };
-                  self.dynamic_import_reject(isolate, dyn_import_id, exception)
-                }
-              }
-            }
-            Err(err) => {
-              // A non-javascript error occurred; this could be due to a an invalid
-              // module specifier, or a problem with the source map, or a failure
-              // to fetch the module source code.
-              let exception =
-                to_v8_type_error(&mut self.handle_scope(isolate), err);
-              self.dynamic_import_reject(isolate, dyn_import_id, exception);
-            }
-          }
-        } else {
-          // The top-level module from a dynamic import has been instantiated.
-          // Load is done.
-          let module_id =
-            load.root_module_id.expect("Root module should be loaded");
-          let result = self.instantiate_module(isolate, module_id);
-          if let Err(exception) = result {
-            self.dynamic_import_reject(isolate, dyn_import_id, exception);
-          }
-          self.dynamic_import_module_evaluate(
-            isolate,
-            dyn_import_id,
-            module_id,
-          )?;
-        }
-
-        // Continue polling for more ready dynamic imports.
-        continue;
-      }
-
-      // There are no active dynamic import loads, or none are ready.
-      return Poll::Ready(Ok(()));
-    }
+  pub(crate) fn increment_modules_idle(&self) {
+    let count = &self.0.module_map.dyn_module_evaluate_idle_counter;
+    count.set(count.get() + 1)
   }
 
   /// "deno_core" runs V8 with Top Level Await enabled. It means that each
@@ -933,60 +624,6 @@ impl JsRealm {
     }
   }
 
-  // Returns true if some dynamic import was resolved.
-  pub(in crate::runtime) fn evaluate_dyn_imports(
-    &self,
-    isolate: &mut v8::Isolate,
-  ) -> bool {
-    let pending = std::mem::take(
-      &mut self.0.context_state.borrow_mut().pending_dyn_mod_evaluate,
-    );
-    if pending.is_empty() {
-      return false;
-    }
-    let mut resolved_any = false;
-    let mut still_pending = vec![];
-    for pending_dyn_evaluate in pending {
-      let maybe_result = {
-        let scope = &mut self.handle_scope(isolate);
-
-        let module_id = pending_dyn_evaluate.module_id;
-        let promise = pending_dyn_evaluate.promise.open(scope);
-        let _module = pending_dyn_evaluate.module.open(scope);
-        let promise_state = promise.state();
-
-        match promise_state {
-          v8::PromiseState::Pending => {
-            still_pending.push(pending_dyn_evaluate);
-            None
-          }
-          v8::PromiseState::Fulfilled => {
-            Some(Ok((pending_dyn_evaluate.load_id, module_id)))
-          }
-          v8::PromiseState::Rejected => {
-            let exception = promise.result(scope);
-            let exception = v8::Global::new(scope, exception);
-            Some(Err((pending_dyn_evaluate.load_id, exception)))
-          }
-        }
-      };
-
-      if let Some(result) = maybe_result {
-        resolved_any = true;
-        match result {
-          Ok((dyn_import_id, module_id)) => {
-            self.dynamic_import_resolve(isolate, dyn_import_id, module_id);
-          }
-          Err((dyn_import_id, exception)) => {
-            self.dynamic_import_reject(isolate, dyn_import_id, exception);
-          }
-        }
-      }
-    }
-    self.0.context_state.borrow_mut().pending_dyn_mod_evaluate = still_pending;
-    resolved_any
-  }
-
   /// Asynchronously load specified module and all of its dependencies.
   ///
   /// The module will be marked as "main", and because of that
@@ -1006,7 +643,6 @@ impl JsRealm {
       let scope = &mut self.handle_scope(isolate);
       // true for main module
       module_map_rc
-        .borrow_mut()
         .new_es_module(scope, true, specifier, code, false)
         .map_err(|e| match e {
           ModuleError::Exception(exception) => {
@@ -1062,7 +698,6 @@ impl JsRealm {
       let scope = &mut self.handle_scope(isolate);
       // false for side module (not main module)
       module_map_rc
-        .borrow_mut()
         .new_es_module(scope, false, specifier, code, false)
         .map_err(|e| match e {
           ModuleError::Exception(exception) => {
diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs
index d0f2fbe..68ce19c 100644
--- a/core/runtime/jsruntime.rs
+++ b/core/runtime/jsruntime.rs
@@ -42,6 +42,7 @@ use anyhow::Context as AnyhowContext;
 use anyhow::Error;
 use futures::channel::oneshot;
 use futures::future::poll_fn;
+use futures::Future;
 use smallvec::SmallVec;
 use std::any::Any;
 use std::cell::RefCell;
@@ -51,6 +52,7 @@ use std::mem::ManuallyDrop;
 use std::ops::Deref;
 use std::ops::DerefMut;
 use std::option::Option;
+use std::pin::Pin;
 use std::rc::Rc;
 use std::sync::atomic::AtomicBool;
 use std::sync::atomic::Ordering;
@@ -305,9 +307,6 @@ pub type CompiledWasmModuleStore = CrossIsolateStore<v8::CompiledWasmModule>;
 pub struct JsRuntimeState {
   known_realms: Vec<JsRealmInner>,
   pub(crate) has_tick_scheduled: bool,
-  /// A counter used to delay our dynamic import deadlock detection by one spin
-  /// of the event loop.
-  pub(crate) dyn_module_evaluate_idle_counter: u32,
   pub(crate) source_map_getter: Option<Rc<Box<dyn SourceMapGetter>>>,
   pub(crate) source_map_cache: Rc<RefCell<SourceMapCache>>,
   pub(crate) op_state: Rc<RefCell<OpState>>,
@@ -480,6 +479,21 @@ impl RuntimeOptions {
   }
 }
 
+#[derive(Copy, Clone, Debug)]
+pub struct PollEventLoopOptions {
+  pub wait_for_inspector: bool,
+  pub pump_v8_message_loop: bool,
+}
+
+impl Default for PollEventLoopOptions {
+  fn default() -> Self {
+    Self {
+      wait_for_inspector: true,
+      pump_v8_message_loop: true,
+    }
+  }
+}
+
 #[derive(Default)]
 pub struct CreateRealmOptions {
   /// Implementation of `ModuleLoader` which will be
@@ -618,7 +632,6 @@ impl JsRuntime {
       .unwrap_or_else(|| Box::new(crate::modules::validate_import_attributes));
 
     let state_rc = Rc::new(RefCell::new(JsRuntimeState {
-      dyn_module_evaluate_idle_counter: 0,
       has_tick_scheduled: false,
       source_map_getter: options.source_map_getter.map(Rc::new),
       source_map_cache: Default::default(),
@@ -755,18 +768,17 @@ impl JsRuntime {
       .module_loader
       .unwrap_or_else(|| Rc::new(NoopModuleLoader));
 
-    let module_map_rc = Rc::new(RefCell::new(ModuleMap::new(loader)));
+    let module_map = Rc::new(ModuleMap::new(loader));
     if let Some(snapshotted_data) = snapshotted_data {
-      let mut module_map = module_map_rc.borrow_mut();
       module_map.update_with_snapshotted_data(scope, snapshotted_data);
     }
-    context.set_slot(scope, module_map_rc.clone());
+    context.set_slot(scope, module_map.clone());
 
     {
       let main_realm = JsRealmInner::new(
         context_state,
         main_context,
-        module_map_rc.clone(),
+        module_map.clone(),
         state_rc.clone(),
         true,
       );
@@ -810,9 +822,7 @@ impl JsRuntime {
     if let Some(preserve_snapshotted_modules) =
       options.preserve_snapshotted_modules
     {
-      module_map_rc
-        .borrow_mut()
-        .clear_module_map(preserve_snapshotted_modules);
+      module_map.clear_module_map(preserve_snapshotted_modules);
     }
 
     js_runtime
@@ -820,7 +830,7 @@ impl JsRuntime {
 
   #[cfg(test)]
   #[inline]
-  pub(crate) fn module_map(&mut self) -> Rc<RefCell<ModuleMap>> {
+  pub(crate) fn module_map(&mut self) -> Rc<ModuleMap> {
     self.main_realm().0.module_map()
   }
 
@@ -918,20 +928,18 @@ impl JsRuntime {
       let loader = options
         .module_loader
         .unwrap_or_else(|| Rc::new(NoopModuleLoader));
-      let module_map_rc = Rc::new(RefCell::new(ModuleMap::new(loader)));
+      let module_map = Rc::new(ModuleMap::new(loader));
       if self.init_mode.is_from_snapshot() {
         let snapshotted_data =
           snapshot_util::get_snapshotted_data(scope, context);
-        module_map_rc
-          .borrow_mut()
-          .update_with_snapshotted_data(scope, snapshotted_data);
+        module_map.update_with_snapshotted_data(scope, snapshotted_data);
       }
-      context.set_slot(scope, module_map_rc.clone());
+      context.set_slot(scope, module_map.clone());
 
       let realm = JsRealmInner::new(
         context_state,
         v8::Global::new(scope, context),
-        module_map_rc,
+        module_map,
         self.inner.state.clone(),
         false,
       );
@@ -953,7 +961,6 @@ impl JsRuntime {
       realm
         .0
         .module_map()
-        .borrow_mut()
         .clear_module_map(preserve_snapshotted_modules);
     }
 
@@ -974,14 +981,14 @@ impl JsRuntime {
     // 2. Iterate through all extensions:
     //  a. If an extension has a `esm_entry_point`, execute it.
 
-    let module_map_rc = realm.0.module_map();
+    let module_map = realm.0.module_map();
 
     // Take extensions temporarily so we can avoid have a mutable reference to self
     let extensions = std::mem::take(&mut self.extensions);
 
-    let loader = module_map_rc.borrow().loader.clone();
+    let loader = module_map.loader.borrow().clone();
     let ext_loader = Rc::new(ExtModuleLoader::new(&extensions));
-    module_map_rc.borrow_mut().loader = ext_loader;
+    *module_map.loader.borrow_mut() = ext_loader;
 
     let mut esm_entrypoints = vec![];
 
@@ -1025,8 +1032,7 @@ impl JsRuntime {
 
       for specifier in esm_entrypoints {
         let mod_id = {
-          module_map_rc
-            .borrow()
+          module_map
             .get_id(specifier, AssertedModuleType::JavaScriptOrWasm)
             .unwrap_or_else(|| {
               panic!("{} not present in the module map", specifier)
@@ -1047,7 +1053,6 @@ impl JsRuntime {
             // Find the TLA location to display it on the panic.
             let location = {
               let scope = &mut realm.handle_scope(self.v8_isolate());
-              let module_map = module_map_rc.borrow();
               let messages = module_map.find_stalled_top_level_await(scope);
               assert!(!messages.is_empty());
               let msg = v8::Local::new(scope, &messages[0]);
@@ -1067,7 +1072,6 @@ impl JsRuntime {
       #[cfg(debug_assertions)]
       {
         let mut scope = realm.handle_scope(self.v8_isolate());
-        let module_map = module_map_rc.borrow();
         module_map.assert_all_modules_evaluated(&mut scope);
       }
 
@@ -1075,7 +1079,8 @@ impl JsRuntime {
     })?;
 
     self.extensions = extensions;
-    module_map_rc.borrow_mut().loader = loader;
+    let module_map = realm.0.module_map();
+    *module_map.loader.borrow_mut() = loader;
 
     Ok(())
   }
@@ -1488,6 +1493,41 @@ impl JsRuntime {
     poll_fn(|cx| self.poll_event_loop(cx, wait_for_inspector)).await
   }
 
+  /// Runs event loop to completion
+  ///
+  /// This future resolves when:
+  ///  - there are no more pending dynamic imports
+  ///  - there are no more pending ops
+  ///  - there are no more active inspector sessions (only if
+  ///     `PollEventLoopOptions.wait_for_inspector` is set to true)
+  pub async fn run_event_loop2(
+    &mut self,
+    poll_options: PollEventLoopOptions,
+  ) -> Result<(), Error> {
+    poll_fn(|cx| self.poll_event_loop2(cx, poll_options)).await
+  }
+
+  /// A utility function that run provided future concurrently with the event loop.
+  ///
+  /// Useful for interacting with local inspector session.
+  pub async fn with_event_loop<'fut, T>(
+    &mut self,
+    mut fut: Pin<Box<dyn Future<Output = T> + 'fut>>,
+    poll_options: PollEventLoopOptions,
+  ) -> T {
+    loop {
+      tokio::select! {
+        biased;
+
+        result = &mut fut => {
+          return result;
+        },
+
+        _ = self.run_event_loop2(poll_options) => {}
+      }
+    }
+  }
+
   /// Runs a single tick of event loop
   ///
   /// If `wait_for_inspector` is set to true event loop
@@ -1496,6 +1536,32 @@ impl JsRuntime {
     &mut self,
     cx: &mut Context,
     wait_for_inspector: bool,
+  ) -> Poll<Result<(), Error>> {
+    self.poll_event_loop_inner(
+      cx,
+      PollEventLoopOptions {
+        wait_for_inspector,
+        ..Default::default()
+      },
+    )
+  }
+
+  /// Runs a single tick of event loop
+  ///
+  /// If `PollEventLoopOptions.wait_for_inspector` is set to true, the event
+  /// loop will return `Poll::Pending` if there are active inspector sessions.
+  pub fn poll_event_loop2(
+    &mut self,
+    cx: &mut Context,
+    poll_options: PollEventLoopOptions,
+  ) -> Poll<Result<(), Error>> {
+    self.poll_event_loop_inner(cx, poll_options)
+  }
+
+  fn poll_event_loop_inner(
+    &mut self,
+    cx: &mut Context,
+    poll_options: PollEventLoopOptions,
   ) -> Poll<Result<(), Error>> {
     let has_inspector: bool;
     {
@@ -1509,7 +1575,9 @@ impl JsRuntime {
       let _ = self.inspector().borrow().poll_sessions(Some(cx)).unwrap();
     }
 
-    self.pump_v8_message_loop()?;
+    if poll_options.pump_v8_message_loop {
+      self.pump_v8_message_loop()?;
+    }
 
     // Dynamic module loading - ie. modules loaded using "import()"
     {
@@ -1533,20 +1601,23 @@ impl JsRuntime {
         let mut has_evaluated = false;
 
         // Fast main realm poll
-        let main_realm = self.inner.main_realm.as_ref().unwrap();
-        loop {
-          let poll_imports =
-            main_realm.prepare_dyn_imports(&mut self.inner.v8_isolate, cx)?;
-          assert!(poll_imports.is_ready());
-
-          let poll_imports =
-            main_realm.poll_dyn_imports(&mut self.inner.v8_isolate, cx)?;
-          assert!(poll_imports.is_ready());
-
-          if main_realm.evaluate_dyn_imports(&mut self.inner.v8_isolate) {
-            has_evaluated = true;
-          } else {
-            break;
+        {
+          let main_realm = self.inner.main_realm.as_ref().unwrap();
+          let mut scope = main_realm.handle_scope(&mut self.inner.v8_isolate);
+          let modules = main_realm.0.module_map();
+          loop {
+            let poll_imports =
+              modules.poll_prepare_dyn_imports(&mut scope, cx)?;
+            assert!(poll_imports.is_ready());
+
+            let poll_imports = modules.poll_dyn_imports(&mut scope, cx)?;
+            assert!(poll_imports.is_ready());
+
+            if modules.evaluate_dyn_imports(&mut scope) {
+              has_evaluated = true;
+            } else {
+              break;
+            }
           }
         }
 
@@ -1557,19 +1628,20 @@ impl JsRuntime {
           drop(state);
           for inner_realm in realms {
             let realm = JsRealm::new(inner_realm);
+            let mut scope = realm.handle_scope(&mut self.inner.v8_isolate);
+            let modules = realm.0.module_map();
 
             // Try and resolve as many dynamic imports in each realm as possible
             // before moving to the next.
             loop {
               let poll_imports =
-                realm.prepare_dyn_imports(&mut self.inner.v8_isolate, cx)?;
+                modules.poll_prepare_dyn_imports(&mut scope, cx)?;
               assert!(poll_imports.is_ready());
 
-              let poll_imports =
-                realm.poll_dyn_imports(&mut self.inner.v8_isolate, cx)?;
+              let poll_imports = modules.poll_dyn_imports(&mut scope, cx)?;
               assert!(poll_imports.is_ready());
 
-              if realm.evaluate_dyn_imports(&mut self.inner.v8_isolate) {
+              if modules.evaluate_dyn_imports(&mut scope) {
                 has_evaluated = true;
               } else {
                 break;
@@ -1641,7 +1713,7 @@ impl JsRuntime {
         let has_active_sessions = inspector.borrow().has_active_sessions();
         let has_blocking_sessions = inspector.borrow().has_blocking_sessions();
 
-        if wait_for_inspector && has_active_sessions {
+        if poll_options.wait_for_inspector && has_active_sessions {
           // If there are no blocking sessions (eg. REPL) we can now notify
           // debugger that the program has finished running and we're ready
           // to exit the process once debugger disconnects.
@@ -1712,8 +1784,7 @@ impl JsRuntime {
         || pending_state.has_tick_scheduled
       {
         // pass, will be polled again
-      } else if self.inner.state.borrow().dyn_module_evaluate_idle_counter >= 1
-      {
+      } else if self.inner.main_realm.as_ref().unwrap().modules_idle() {
         let known_realms = &self.inner.state.borrow().known_realms;
         return Poll::Ready(Err(
           find_and_report_stalled_level_await_in_any_realm(
@@ -1722,11 +1793,16 @@ impl JsRuntime {
           ),
         ));
       } else {
-        let mut state = self.inner.state.borrow_mut();
+        let state = self.inner.state.borrow_mut();
         // Delay the above error by one spin of the event loop. A dynamic import
         // evaluation may complete during this, in which case the counter will
         // reset.
-        state.dyn_module_evaluate_idle_counter += 1;
+        self
+          .inner
+          .main_realm
+          .as_ref()
+          .unwrap()
+          .increment_modules_idle();
         state.op_state.borrow().waker.wake();
       }
     }
@@ -1742,7 +1818,7 @@ fn find_and_report_stalled_level_await_in_any_realm(
   for inner_realm in known_realms {
     let scope = &mut inner_realm.handle_scope(v8_isolate);
     let module_map = inner_realm.module_map();
-    let messages = module_map.borrow().find_stalled_top_level_await(scope);
+    let messages = module_map.find_stalled_top_level_await(scope);
 
     if !messages.is_empty() {
       // We are gonna print only a single message to provide a nice formatting
@@ -1814,8 +1890,7 @@ impl JsRuntimeForSnapshot {
     // Serialize the module map and store its data in the snapshot.
     {
       let snapshotted_data = {
-        let module_map_rc = realm.0.module_map();
-        let module_map = module_map_rc.borrow();
+        let module_map = realm.0.module_map();
         module_map.serialize_for_snapshotting(
           &mut realm.handle_scope(self.v8_isolate()),
         )
@@ -1970,13 +2045,6 @@ impl JsRuntime {
     self.main_realm().mod_evaluate(self.v8_isolate(), id)
   }
 
-  /// Clears all loaded modules
-  /// May not free all associated memory, and should not be used
-  /// in production environments
-  pub fn clear_modules(&mut self) {
-    self.main_realm().clear_modules()
-  }
-
   /// Asynchronously load specified module and all of its dependencies.
   ///
   /// The module will be marked as "main", and because of that
diff --git a/core/runtime/mod.rs b/core/runtime/mod.rs
index 43fdd2f..9b700f7 100644
--- a/core/runtime/mod.rs
+++ b/core/runtime/mod.rs
@@ -1,5 +1,5 @@
 // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-mod bindings;
+pub(crate) mod bindings;
 mod jsrealm;
 mod jsruntime;
 #[doc(hidden)]
@@ -22,6 +22,7 @@ pub(crate) use jsruntime::InitMode;
 pub use jsruntime::JsRuntime;
 pub use jsruntime::JsRuntimeForSnapshot;
 pub use jsruntime::JsRuntimeState;
+pub use jsruntime::PollEventLoopOptions;
 pub use jsruntime::RuntimeOptions;
 pub use jsruntime::SharedArrayBufferStore;
 pub use jsruntime::Snapshot;
diff --git a/core/runtime/snapshot_util.rs b/core/runtime/snapshot_util.rs
index c0df226..76dcdaa 100644
--- a/core/runtime/snapshot_util.rs
+++ b/core/runtime/snapshot_util.rs
@@ -187,7 +187,7 @@ pub(crate) fn get_snapshotted_data(
 
   let next_module_id = {
     let info_data: v8::Local<v8::Array> =
-      val.get_index(&mut scope, 1).unwrap().try_into().unwrap();
+      val.get_index(&mut scope, 2).unwrap().try_into().unwrap();
     info_data.length()
   };
 


Test patch:
diff --git a/core/modules/tests.rs b/core/modules/tests.rs
index 531f8eb..3a4a917 100644
--- a/core/modules/tests.rs
+++ b/core/modules/tests.rs
@@ -251,7 +251,7 @@ fn test_recursive_load() {
   );
 
   let module_map_rc = runtime.module_map();
-  let modules = module_map_rc.borrow();
+  let modules = module_map_rc;
 
   assert_eq!(
     modules.get_id("file:///a.js", AssertedModuleType::JavaScriptOrWasm),
@@ -268,7 +268,7 @@ fn test_recursive_load() {
     .unwrap();
   assert_eq!(
     modules.get_requested_modules(a_id),
-    Some(&vec![
+    Some(vec![
       ModuleRequest {
         specifier: "file:///b.js".to_string(),
         asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
@@ -281,19 +281,19 @@ fn test_recursive_load() {
   );
   assert_eq!(
     modules.get_requested_modules(b_id),
-    Some(&vec![ModuleRequest {
+    Some(vec![ModuleRequest {
       specifier: "file:///c.js".to_string(),
       asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
     },])
   );
   assert_eq!(
     modules.get_requested_modules(c_id),
-    Some(&vec![ModuleRequest {
+    Some(vec![ModuleRequest {
       specifier: "file:///d.js".to_string(),
       asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
     },])
   );
-  assert_eq!(modules.get_requested_modules(d_id), Some(&vec![]));
+  assert_eq!(modules.get_requested_modules(d_id), Some(vec![]));
 }
 
 #[test]
@@ -331,11 +331,10 @@ fn test_mods() {
 
   assert_eq!(DISPATCH_COUNT.load(Ordering::Relaxed), 0);
 
-  let module_map_rc = runtime.module_map().clone();
+  let module_map = runtime.module_map().clone();
 
   let (mod_a, mod_b) = {
     let scope = &mut runtime.handle_scope();
-    let mut module_map = module_map_rc.borrow_mut();
     let specifier_a = ascii_str!("file:///a.js");
     let mod_a = module_map
       .new_es_module(
@@ -358,7 +357,7 @@ fn test_mods() {
     let imports = module_map.get_requested_modules(mod_a);
     assert_eq!(
       imports,
-      Some(&vec![ModuleRequest {
+      Some(vec![ModuleRequest {
         specifier: "file:///b.js".to_string(),
         asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
       },])
@@ -411,11 +410,10 @@ fn test_json_module() {
     )
     .unwrap();
 
-  let module_map_rc = runtime.module_map().clone();
+  let module_map = runtime.module_map().clone();
 
   let (mod_a, mod_b, mod_c) = {
     let scope = &mut runtime.handle_scope();
-    let mut module_map = module_map_rc.borrow_mut();
     let specifier_a = ascii_str!("file:///a.js");
     let specifier_b = ascii_str!("file:///b.js");
     let mod_a = module_map
@@ -437,7 +435,7 @@ fn test_json_module() {
     let imports = module_map.get_requested_modules(mod_a);
     assert_eq!(
       imports,
-      Some(&vec![ModuleRequest {
+      Some(vec![ModuleRequest {
         specifier: "file:///c.json".to_string(),
         asserted_module_type: AssertedModuleType::Json,
       },])
@@ -462,7 +460,7 @@ fn test_json_module() {
     let imports = module_map.get_requested_modules(mod_b);
     assert_eq!(
       imports,
-      Some(&vec![ModuleRequest {
+      Some(vec![ModuleRequest {
         specifier: "file:///c.json".to_string(),
         asserted_module_type: AssertedModuleType::Json,
       },])
@@ -514,11 +512,10 @@ fn test_validate_import_attributes() {
     ..Default::default()
   });
 
-  let module_map_rc = runtime.module_map().clone();
+  let module_map = runtime.module_map().clone();
 
   {
     let scope = &mut runtime.handle_scope();
-    let mut module_map = module_map_rc.borrow_mut();
     let specifier_a = ascii_str!("file:///a.js");
     let module_err = module_map
       .new_es_module(
@@ -556,9 +553,8 @@ fn test_validate_import_attributes_default() {
 
   {
     let scope = &mut runtime.handle_scope();
-    let mut module_map = module_map_rc.borrow_mut();
     let specifier_a = ascii_str!("file:///a.js");
-    let module_err = module_map
+    let module_err = module_map_rc
       .new_es_module(
         scope,
         true,
@@ -586,9 +582,8 @@ fn test_validate_import_attributes_default() {
 
   {
     let scope = &mut runtime.handle_scope();
-    let mut module_map = module_map_rc.borrow_mut();
     let specifier_a = ascii_str!("file:///a.js");
-    let module_err = module_map
+    let module_err = module_map_rc
       .new_es_module(
         scope,
         true,
@@ -691,32 +686,6 @@ async fn dyn_import_ok() {
   .await;
 }
 
-#[tokio::test]
-async fn clear_modules_works() {
-  fn count_modules(runtime: &mut JsRuntime) -> usize {
-    runtime
-      .main_realm()
-      .0
-      .module_map()
-      .borrow_mut()
-      .handles
-      .len()
-  }
-
-  let mut runtime = JsRuntime::new(Default::default());
-  futures::executor::block_on(runtime.load_main_module(
-    &Url::parse("file:///a.js").unwrap(),
-    Some(ascii_str!("")),
-  ))
-  .unwrap();
-
-  let result = futures::executor::block_on(runtime.run_event_loop(false));
-  assert!(result.is_ok());
-
-  runtime.clear_modules();
-  assert_eq!(0, count_modules(&mut runtime));
-}
-
 #[tokio::test]
 async fn dyn_import_borrow_mut_error() {
   // https://github.com/denoland/deno/issues/6054
@@ -822,7 +791,7 @@ fn test_circular_load() {
     );
 
     let module_map_rc = runtime.module_map();
-    let modules = module_map_rc.borrow();
+    let modules = module_map_rc;
 
     assert_eq!(
       modules
@@ -835,7 +804,7 @@ fn test_circular_load() {
 
     assert_eq!(
       modules.get_requested_modules(circular1_id),
-      Some(&vec![ModuleRequest {
+      Some(vec![ModuleRequest {
         specifier: "file:///circular2.js".to_string(),
         asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
       }])
@@ -843,7 +812,7 @@ fn test_circular_load() {
 
     assert_eq!(
       modules.get_requested_modules(circular2_id),
-      Some(&vec![ModuleRequest {
+      Some(vec![ModuleRequest {
         specifier: "file:///circular3.js".to_string(),
         asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
       }])
@@ -857,7 +826,7 @@ fn test_circular_load() {
       .unwrap();
     assert_eq!(
       modules.get_requested_modules(circular3_id),
-      Some(&vec![
+      Some(vec![
         ModuleRequest {
           specifier: "file:///circular1.js".to_string(),
           asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
@@ -902,7 +871,7 @@ fn test_redirect_load() {
     );
 
     let module_map_rc = runtime.module_map();
-    let modules = module_map_rc.borrow();
+    let modules = module_map_rc;
 
     assert_eq!(
       modules
@@ -1053,7 +1022,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
   );
 
   let module_map_rc = runtime.module_map();
-  let modules = module_map_rc.borrow();
+  let modules = module_map_rc;
 
   assert_eq!(
     modules.get_id(
@@ -1074,7 +1043,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
 
   assert_eq!(
     modules.get_requested_modules(main_id),
-    Some(&vec![
+    Some(vec![
       ModuleRequest {
         specifier: "file:///b.js".to_string(),
         asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
@@ -1087,19 +1056,19 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
   );
   assert_eq!(
     modules.get_requested_modules(b_id),
-    Some(&vec![ModuleRequest {
+    Some(vec![ModuleRequest {
       specifier: "file:///c.js".to_string(),
       asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
     }])
   );
   assert_eq!(
     modules.get_requested_modules(c_id),
-    Some(&vec![ModuleRequest {
+    Some(vec![ModuleRequest {
       specifier: "file:///d.js".to_string(),
       asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
     }])
   );
-  assert_eq!(modules.get_requested_modules(d_id), Some(&vec![]));
+  assert_eq!(modules.get_requested_modules(d_id), Some(vec![]));
 }
 
 #[test]
diff --git a/core/runtime/tests/jsrealm.rs b/core/runtime/tests/jsrealm.rs
index 479ae29..db6f83d 100644
--- a/core/runtime/tests/jsrealm.rs
+++ b/core/runtime/tests/jsrealm.rs
@@ -732,101 +732,6 @@ async fn test_realms_concurrent_module_evaluations() {
   assert!(matches!(other_realm_receiver.await, Ok(Ok(()))));
 }
 
-// Make sure that loading and evaluating dynamic imported modules in different
-// realms at the same time works.
-#[tokio::test]
-async fn test_realm_concurrent_dynamic_imports() {
-  let loader = Rc::new(StaticModuleLoader::with(
-    Url::parse("file:///test.js").unwrap(),
-    ascii_str!(r#"await Deno.core.opAsync("op_wait");"#),
-  ));
-
-  #[op2(async)]
-  async fn op_wait(op_state: Rc<RefCell<OpState>>) {
-    let (sender, receiver) = oneshot::channel::<()>();
-    op_state
-      .borrow_mut()
-      .borrow_mut::<Vec<oneshot::Sender<()>>>()
-      .push(sender);
-    receiver.await.unwrap();
-  }
-  deno_core::extension!(test_ext, ops = [op_wait]);
-
-  let mut runtime = JsRuntime::new(RuntimeOptions {
-    extensions: vec![test_ext::init_ops()],
-    module_loader: Some(loader.clone()),
-    ..Default::default()
-  });
-  runtime
-    .op_state()
-    .borrow_mut()
-    .put::<Vec<oneshot::Sender<()>>>(vec![]);
-
-  let main_realm = runtime.main_realm();
-  let other_realm = runtime
-    .create_realm(CreateRealmOptions {
-      module_loader: Some(loader.clone()),
-    })
-    .unwrap();
-
-  let main_realm_promise = {
-    let global = main_realm
-      .execute_script_static(
-        runtime.v8_isolate(),
-        "",
-        r#"import("file:///test.js")"#,
-      )
-      .unwrap();
-    let scope = &mut main_realm.handle_scope(runtime.v8_isolate());
-    let local = v8::Local::new(scope, global);
-    let promise = v8::Local::<v8::Promise>::try_from(local).unwrap();
-    assert_eq!(promise.state(), v8::PromiseState::Pending);
-    v8::Global::new(scope, promise)
-  };
-  let other_realm_promise = {
-    let global = other_realm
-      .execute_script_static(
-        runtime.v8_isolate(),
-        "",
-        r#"import("file:///test.js")"#,
-      )
-      .unwrap();
-    let scope = &mut other_realm.handle_scope(runtime.v8_isolate());
-    let local = v8::Local::new(scope, global);
-    let promise = v8::Local::<v8::Promise>::try_from(local).unwrap();
-    assert_eq!(promise.state(), v8::PromiseState::Pending);
-    v8::Global::new(scope, promise)
-  };
-
-  poll_fn(|cx| {
-    let res = runtime.poll_event_loop(cx, false);
-    assert!(matches!(res, Poll::Pending));
-    Poll::Ready(())
-  })
-  .await;
-
-  // Resolve the promises
-  {
-    let senders = runtime
-      .op_state()
-      .borrow_mut()
-      .take::<Vec<oneshot::Sender<()>>>();
-    for sender in senders {
-      sender.send(()).unwrap();
-    }
-  }
-
-  runtime.run_event_loop(false).await.unwrap();
-  assert_eq!(
-    main_realm_promise.open(runtime.v8_isolate()).state(),
-    v8::PromiseState::Fulfilled
-  );
-  assert_eq!(
-    other_realm_promise.open(runtime.v8_isolate()).state(),
-    v8::PromiseState::Fulfilled
-  );
-}
-
 #[test]
 fn es_snapshot() {
   let startup_data = {
diff --git a/core/runtime/tests/snapshot.rs b/core/runtime/tests/snapshot.rs
index 1664ce0..c087f53 100644
--- a/core/runtime/tests/snapshot.rs
+++ b/core/runtime/tests/snapshot.rs
@@ -3,9 +3,7 @@ use crate::extensions::Op;
 use crate::modules::AssertedModuleType;
 use crate::modules::LoggingModuleLoader;
 use crate::modules::ModuleInfo;
-use crate::modules::ModuleLoadId;
 use crate::modules::ModuleType;
-use crate::modules::SymbolicModule;
 use crate::*;
 use anyhow::Error;
 use std::borrow::Cow;
@@ -168,34 +166,6 @@ fn es_snapshot() {
     }
   }
 
-  fn assert_module_map(runtime: &mut JsRuntime, modules: &Vec<ModuleInfo>) {
-    let module_map_rc = runtime.module_map();
-    let module_map = module_map_rc.borrow();
-    assert_eq!(module_map.handles.len(), modules.len());
-    assert_eq!(module_map.info.len(), modules.len());
-    assert_eq!(
-      module_map.by_name(AssertedModuleType::Json).len()
-        + module_map
-          .by_name(AssertedModuleType::JavaScriptOrWasm)
-          .len(),
-      modules.len()
-    );
-
-    assert_eq!(module_map.next_load_id, (modules.len() + 1) as ModuleLoadId);
-
-    for info in modules {
-      assert!(module_map.handles.get(info.id).is_some());
-      assert_eq!(module_map.info.get(info.id).unwrap(), info);
-      assert_eq!(
-        module_map
-          .by_name(AssertedModuleType::JavaScriptOrWasm)
-          .get(&info.name)
-          .unwrap(),
-        &SymbolicModule::Mod(info.id)
-      );
-    }
-  }
-
   #[op2]
   #[string]
   fn op_test() -> Result<String, Error> {
@@ -234,7 +204,7 @@ fn es_snapshot() {
 
   modules.extend((1..200).map(|i| create_module(&mut runtime, i, false)));
 
-  assert_module_map(&mut runtime, &modules);
+  runtime.module_map().assert_module_map(&modules);
 
   let snapshot = runtime.snapshot();
 
@@ -248,12 +218,12 @@ fn es_snapshot() {
     ..Default::default()
   });
 
-  assert_module_map(&mut runtime2, &modules);
+  runtime2.module_map().assert_module_map(&modules);
 
   modules.extend((200..400).map(|i| create_module(&mut runtime2, i, false)));
   modules.push(create_module(&mut runtime2, 400, true));
 
-  assert_module_map(&mut runtime2, &modules);
+  runtime2.module_map().assert_module_map(&modules);
 
   let snapshot2 = runtime2.snapshot();
 
@@ -267,7 +237,7 @@ fn es_snapshot() {
     ..Default::default()
   });
 
-  assert_module_map(&mut runtime3, &modules);
+  runtime3.module_map().assert_module_map(&modules);
 
   let source_code = r#"(async () => {
     const mod = await import("file:///400.js");