summaryrefslogtreecommitdiff
path: root/core/runtime.rs
diff options
context:
space:
mode:
Diffstat (limited to 'core/runtime.rs')
-rw-r--r--core/runtime.rs93
1 files changed, 78 insertions, 15 deletions
diff --git a/core/runtime.rs b/core/runtime.rs
index 527226626..4647160c9 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -1168,7 +1168,7 @@ impl JsRuntime {
return Poll::Ready(Ok(()));
}
- let mut state = self.state.borrow_mut();
+ let state = self.state.borrow();
// Check if more async ops have been dispatched
// during this turn of event loop.
@@ -1185,6 +1185,8 @@ impl JsRuntime {
state.waker.wake();
}
+ drop(state);
+
if pending_state.has_pending_module_evaluation {
if pending_state.has_pending_refed_ops
|| pending_state.has_pending_dyn_imports
@@ -1195,8 +1197,16 @@ impl JsRuntime {
{
// pass, will be polled again
} else {
- let msg = "Module evaluation is still pending but there are no pending ops or dynamic imports. This situation is often caused by unresolved promises.";
- return Poll::Ready(Err(generic_error(msg)));
+ let scope = &mut self.handle_scope();
+ let messages = find_stalled_top_level_await(scope);
+ // We are gonna print only a single message to provide a nice formatting
+ // with source line of offending promise shown. Once user fixed it, then
+ // they will get another error message for the next promise (but this
+ // situation is gonna be very rare, if ever happening).
+ assert!(!messages.is_empty());
+ let msg = v8::Local::new(scope, messages[0].clone());
+ let js_error = JsError::from_v8_message(scope, msg);
+ return Poll::Ready(Err(js_error.into()));
}
}
@@ -1207,19 +1217,19 @@ impl JsRuntime {
|| pending_state.has_tick_scheduled
{
// pass, will be polled again
- } else if state.dyn_module_evaluate_idle_counter >= 1 {
- let mut msg = "Dynamically imported module evaluation is still pending but there are no pending ops. This situation is often caused by unresolved promises.
-Pending dynamic modules:\n".to_string();
- let module_map = self.module_map.as_mut().unwrap().borrow_mut();
- for pending_evaluate in &state.pending_dyn_mod_evaluate {
- let module_info = module_map
- .get_info_by_id(&pending_evaluate.module_id)
- .unwrap();
- msg.push_str("- ");
- msg.push_str(module_info.name.as_str());
- }
- return Poll::Ready(Err(generic_error(msg)));
+ } else if self.state.borrow().dyn_module_evaluate_idle_counter >= 1 {
+ let scope = &mut self.handle_scope();
+ let messages = find_stalled_top_level_await(scope);
+ // We are gonna print only a single message to provide a nice formatting
+ // with source line of offending promise shown. Once user fixed it, then
+ // they will get another error message for the next promise (but this
+ // situation is gonna be very rare, if ever happening).
+ assert!(!messages.is_empty());
+ let msg = v8::Local::new(scope, messages[0].clone());
+ let js_error = JsError::from_v8_message(scope, msg);
+ return Poll::Ready(Err(js_error.into()));
} else {
+ let mut state = self.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.
@@ -1269,6 +1279,59 @@ Pending dynamic modules:\n".to_string();
}
}
+fn get_stalled_top_level_await_message_for_module<'s>(
+ scope: &'s mut v8::HandleScope,
+ module_id: ModuleId,
+) -> Vec<v8::Global<v8::Message>> {
+ let module_map = JsRuntime::module_map(scope);
+ let module_map = module_map.borrow();
+ let module_handle = module_map.handles_by_id.get(&module_id).unwrap();
+
+ let module = v8::Local::new(scope, module_handle);
+ let stalled = module.get_stalled_top_level_await_message(scope);
+ let mut messages = vec![];
+ for (_, message) in stalled {
+ messages.push(v8::Global::new(scope, message));
+ }
+ messages
+}
+
+fn find_stalled_top_level_await<'s>(
+ scope: &'s mut v8::HandleScope,
+) -> Vec<v8::Global<v8::Message>> {
+ let module_map = JsRuntime::module_map(scope);
+ let module_map = module_map.borrow();
+
+ // First check if that's root module
+ let root_module_id = module_map
+ .info
+ .values()
+ .filter(|m| m.main)
+ .map(|m| m.id)
+ .next();
+
+ if let Some(root_module_id) = root_module_id {
+ let messages =
+ get_stalled_top_level_await_message_for_module(scope, root_module_id);
+ if !messages.is_empty() {
+ return messages;
+ }
+ }
+
+ // It wasn't a top module, so iterate over all modules and try to find
+ // any with stalled top level await
+ let module_ids = module_map.handles_by_id.keys().copied().collect::<Vec<_>>();
+ for module_id in module_ids {
+ let messages =
+ get_stalled_top_level_await_message_for_module(scope, module_id);
+ if !messages.is_empty() {
+ return messages;
+ }
+ }
+
+ unreachable!()
+}
+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) struct EventLoopPendingState {
has_pending_refed_ops: bool,