mirror of
https://github.com/denoland/rusty_v8.git
synced 2025-01-11 08:34:01 -05:00
feat: v8::Module::get_stalled_top_level_await_message (#1123)
This commit adds "v8::Module::get_stalled_top_level_await_message" API that allows to retrieve a vector of tuples with handles to v8::Module and v8::Message. This information can be used to display a nice error when event loop runs out of work to do but there are still unresolved promises.
This commit is contained in:
parent
cc7183d09f
commit
3d30e7cc82
3 changed files with 132 additions and 0 deletions
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include "support.h"
|
||||
|
@ -2690,6 +2691,26 @@ const v8::UnboundModuleScript* v8__Module__GetUnboundModuleScript(
|
|||
return local_to_ptr(ptr_to_local(&self)->GetUnboundModuleScript());
|
||||
}
|
||||
|
||||
struct StalledTopLevelAwaitMessage {
|
||||
const v8::Module* module;
|
||||
const v8::Message* message;
|
||||
};
|
||||
|
||||
|
||||
size_t v8__Module__GetStalledTopLevelAwaitMessage(
|
||||
const v8::Module& self, v8::Isolate* isolate,
|
||||
StalledTopLevelAwaitMessage* out_vec, size_t out_len) {
|
||||
auto messages = ptr_to_local(&self)->GetStalledTopLevelAwaitMessage(isolate);
|
||||
auto len = std::min(messages.size(), out_len);
|
||||
for (size_t i = 0; i < len; i += 1) {
|
||||
StalledTopLevelAwaitMessage stalled_message;
|
||||
stalled_message.module = local_to_ptr(std::get<0>(messages[i]));
|
||||
stalled_message.message = local_to_ptr(std::get<1>(messages[i]));
|
||||
out_vec[i] = stalled_message;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
const v8::String* v8__ModuleRequest__GetSpecifier(
|
||||
const v8::ModuleRequest& self) {
|
||||
return local_to_ptr(self.GetSpecifier());
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::FixedArray;
|
|||
use crate::HandleScope;
|
||||
use crate::Isolate;
|
||||
use crate::Local;
|
||||
use crate::Message;
|
||||
use crate::Module;
|
||||
use crate::ModuleRequest;
|
||||
use crate::String;
|
||||
|
@ -195,6 +196,18 @@ extern "C" {
|
|||
fn v8__ModuleRequest__GetImportAssertions(
|
||||
this: *const ModuleRequest,
|
||||
) -> *const FixedArray;
|
||||
fn v8__Module__GetStalledTopLevelAwaitMessage(
|
||||
this: *const Module,
|
||||
isolate: *const Isolate,
|
||||
out_vec: *mut StalledTopLevelAwaitMessage,
|
||||
vec_len: usize,
|
||||
) -> usize;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct StalledTopLevelAwaitMessage {
|
||||
pub module: *const Module,
|
||||
pub message: *const Message,
|
||||
}
|
||||
|
||||
/// A location in JavaScript source.
|
||||
|
@ -413,6 +426,44 @@ impl Module {
|
|||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Search the modules requested directly or indirectly by the module for
|
||||
/// any top-level await that has not yet resolved. If there is any, the
|
||||
/// returned vector contains a tuple of the unresolved module and a message
|
||||
/// with the pending top-level await.
|
||||
/// An embedder may call this before exiting to improve error messages.
|
||||
pub fn get_stalled_top_level_await_message(
|
||||
&self,
|
||||
scope: &mut HandleScope,
|
||||
) -> Vec<(Local<Module>, Local<Message>)> {
|
||||
let mut out_vec: Vec<StalledTopLevelAwaitMessage> = Vec::with_capacity(16);
|
||||
for _i in 0..16 {
|
||||
out_vec.push(StalledTopLevelAwaitMessage {
|
||||
module: std::ptr::null(),
|
||||
message: std::ptr::null(),
|
||||
});
|
||||
}
|
||||
|
||||
let returned_len = unsafe {
|
||||
v8__Module__GetStalledTopLevelAwaitMessage(
|
||||
&*self,
|
||||
scope.get_isolate_ptr(),
|
||||
out_vec.as_mut_ptr(),
|
||||
out_vec.len(),
|
||||
)
|
||||
};
|
||||
|
||||
let mut ret_vec = Vec::with_capacity(returned_len);
|
||||
for item in out_vec.iter().take(returned_len) {
|
||||
unsafe {
|
||||
ret_vec.push((
|
||||
Local::from_raw(item.module).unwrap(),
|
||||
Local::from_raw(item.message).unwrap(),
|
||||
));
|
||||
}
|
||||
}
|
||||
ret_vec
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleRequest {
|
||||
|
|
|
@ -3506,6 +3506,66 @@ fn module_evaluation() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_stalled_top_level_await() {
|
||||
let _setup_guard = setup();
|
||||
let isolate = &mut v8::Isolate::new(Default::default());
|
||||
{
|
||||
let scope = &mut v8::HandleScope::new(isolate);
|
||||
let context = v8::Context::new(scope);
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
|
||||
let source_text =
|
||||
v8::String::new(scope, "await new Promise((_resolve, _reject) => {});")
|
||||
.unwrap();
|
||||
let origin = mock_script_origin(scope, "foo.js");
|
||||
let source = v8::script_compiler::Source::new(source_text, Some(&origin));
|
||||
|
||||
let module = v8::script_compiler::compile_module(scope, source).unwrap();
|
||||
assert!(module.script_id().is_some());
|
||||
assert!(module.is_source_text_module());
|
||||
assert!(!module.is_synthetic_module());
|
||||
assert_eq!(v8::ModuleStatus::Uninstantiated, module.get_status());
|
||||
module.hash(&mut DefaultHasher::new()); // Should not crash.
|
||||
|
||||
let result = module
|
||||
.instantiate_module(scope, compile_specifier_as_module_resolve_callback);
|
||||
assert!(result.unwrap());
|
||||
assert_eq!(v8::ModuleStatus::Instantiated, module.get_status());
|
||||
|
||||
let result = module.evaluate(scope);
|
||||
assert!(result.is_some());
|
||||
assert_eq!(v8::ModuleStatus::Evaluated, module.get_status());
|
||||
|
||||
let promise: v8::Local<v8::Promise> = result.unwrap().try_into().unwrap();
|
||||
scope.perform_microtask_checkpoint();
|
||||
assert_eq!(promise.state(), v8::PromiseState::Pending);
|
||||
let stalled = module.get_stalled_top_level_await_message(scope);
|
||||
assert_eq!(stalled.len(), 1);
|
||||
let (_module, message) = stalled[0];
|
||||
let message_str = message.get(scope);
|
||||
assert_eq!(
|
||||
message_str.to_rust_string_lossy(scope),
|
||||
"Top-level await promise never resolved"
|
||||
);
|
||||
assert_eq!(Some(1), message.get_line_number(scope));
|
||||
assert_eq!(
|
||||
message
|
||||
.get_script_resource_name(scope)
|
||||
.unwrap()
|
||||
.to_rust_string_lossy(scope),
|
||||
"foo.js"
|
||||
);
|
||||
assert_eq!(
|
||||
message
|
||||
.get_source_line(scope)
|
||||
.unwrap()
|
||||
.to_rust_string_lossy(scope),
|
||||
"await new Promise((_resolve, _reject) => {});"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_assertions() {
|
||||
let _setup_guard = setup();
|
||||
|
|
Loading…
Reference in a new issue