mirror of
https://github.com/lexmount/moli.git
synced 2026-09-28 08:01:37 +00:00
fix(modules): preserve original exceptions through error reporting
This commit is contained in:
+7
-4
@@ -6,10 +6,13 @@
|
||||
window.addEventListener("error", (event) => {
|
||||
window.dynamicModuleTlaPayloadOrder.push("window-error");
|
||||
window.dynamicModuleTlaPayloadErrorMessageMatches = Boolean(
|
||||
event.error &&
|
||||
typeof event.error.message === "string" &&
|
||||
event.error.message.length > 0,
|
||||
typeof event.message === "string" && event.message.length > 0,
|
||||
);
|
||||
window.dynamicModuleTlaPayloadErrorIdentityMatches =
|
||||
event.error === window.dynamicModuleTlaPayloadReason &&
|
||||
Object.getPrototypeOf(event.error) === null &&
|
||||
!Object.hasOwn(event.error, "message") &&
|
||||
!Object.hasOwn(event.error, "fileName");
|
||||
window.dynamicModuleTlaPayloadFilenameMatches =
|
||||
typeof event.filename === "string" &&
|
||||
event.filename.includes(
|
||||
@@ -23,7 +26,7 @@
|
||||
window.dynamicModuleTlaPayloadOrder.push("script-error");
|
||||
};
|
||||
broken.text =
|
||||
'const reason = Object.create(null); await Promise.reject(reason);';
|
||||
'const reason = window.dynamicModuleTlaPayloadReason = Object.create(null); await Promise.reject(reason);';
|
||||
document.body.appendChild(broken);
|
||||
|
||||
const later = document.createElement("script");
|
||||
|
||||
+7
-4
@@ -6,10 +6,13 @@
|
||||
window.addEventListener("error", (event) => {
|
||||
window.parserOwnedModuleTlaPayloadOrder.push("window-error");
|
||||
window.parserOwnedModuleTlaPayloadErrorMessageMatches = Boolean(
|
||||
event.error &&
|
||||
typeof event.error.message === "string" &&
|
||||
event.error.message.length > 0,
|
||||
typeof event.message === "string" && event.message.length > 0,
|
||||
);
|
||||
window.parserOwnedModuleTlaPayloadErrorIdentityMatches =
|
||||
event.error === window.parserOwnedModuleTlaPayloadReason &&
|
||||
Object.getPrototypeOf(event.error) === null &&
|
||||
!Object.hasOwn(event.error, "message") &&
|
||||
!Object.hasOwn(event.error, "fileName");
|
||||
window.parserOwnedModuleTlaPayloadFilenameMatches =
|
||||
typeof event.filename === "string" &&
|
||||
event.filename.includes(
|
||||
@@ -21,7 +24,7 @@
|
||||
type="module"
|
||||
onerror="window.parserOwnedModuleTlaPayloadOrder.push('script-error');"
|
||||
>
|
||||
const reason = Object.create(null);
|
||||
const reason = window.parserOwnedModuleTlaPayloadReason = Object.create(null);
|
||||
await Promise.reject(reason);
|
||||
</script>
|
||||
<script type="module">
|
||||
|
||||
@@ -3151,6 +3151,10 @@ async fn dynamic_module_tla_exotic_rejection_reports_window_error_payload_withou
|
||||
diagnostic_global(&page, "dynamicModuleTlaPayloadErrorMessageMatches"),
|
||||
Some(&JsValueSnapshot::Bool(true))
|
||||
);
|
||||
assert_eq!(
|
||||
diagnostic_global(&page, "dynamicModuleTlaPayloadErrorIdentityMatches"),
|
||||
Some(&JsValueSnapshot::Bool(true))
|
||||
);
|
||||
assert_eq!(
|
||||
diagnostic_global(&page, "dynamicModuleTlaPayloadFilenameMatches"),
|
||||
Some(&JsValueSnapshot::Bool(true))
|
||||
@@ -3839,6 +3843,10 @@ async fn parser_owned_module_tla_exotic_rejection_reports_window_error_payload_w
|
||||
diagnostic_global(&page, "parserOwnedModuleTlaPayloadErrorMessageMatches"),
|
||||
Some(&JsValueSnapshot::Bool(true))
|
||||
);
|
||||
assert_eq!(
|
||||
diagnostic_global(&page, "parserOwnedModuleTlaPayloadErrorIdentityMatches"),
|
||||
Some(&JsValueSnapshot::Bool(true))
|
||||
);
|
||||
assert_eq!(
|
||||
diagnostic_global(&page, "parserOwnedModuleTlaPayloadFilenameMatches"),
|
||||
Some(&JsValueSnapshot::Bool(true))
|
||||
@@ -5377,7 +5385,7 @@ async fn module_wasm_js_cycle_reports_guard_without_crashing() -> Result<()> {
|
||||
.await?;
|
||||
assert_eq!(
|
||||
diagnostic_global(&page, "moduleWasmJsCycleConstructor"),
|
||||
Some(&JsValueSnapshot::String("Error".to_owned())),
|
||||
Some(&JsValueSnapshot::String("TypeError".to_owned())),
|
||||
"message: {:?}",
|
||||
diagnostic_global(&page, "moduleWasmJsCycleMessage")
|
||||
);
|
||||
|
||||
@@ -698,6 +698,30 @@ struct DependencyCandidate {
|
||||
}
|
||||
|
||||
fn is_parse_error(error: &ModuleLoadError) -> bool {
|
||||
error.exception_id.is_some()
|
||||
(error.exception_id.is_some()
|
||||
&& matches!(
|
||||
error.stage,
|
||||
ModuleLoadStage::Compile | ModuleLoadStage::Resolve
|
||||
))
|
||||
|| error.error_constructor == Some(ModuleErrorConstructorKind::SyntaxError)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn retained_values_do_not_turn_link_or_evaluation_failures_into_parse_errors() {
|
||||
for (stage, expected) in [
|
||||
(ModuleLoadStage::Compile, true),
|
||||
(ModuleLoadStage::Resolve, true),
|
||||
(ModuleLoadStage::Fetch, false),
|
||||
(ModuleLoadStage::Instantiate, false),
|
||||
(ModuleLoadStage::Evaluate, false),
|
||||
] {
|
||||
let error = ModuleLoadError::new(stage, "retained error")
|
||||
.with_exception_id(crate::ModuleExceptionId(1));
|
||||
assert_eq!(is_parse_error(&error), expected, "{stage:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::types::ScriptErrorConstructorKind;
|
||||
use crate::types::{ScriptErrorConstructorKind, ScriptErrorValue};
|
||||
use moli_module_script_tree::ModuleExceptionId;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -60,6 +60,17 @@ impl ModuleLoadError {
|
||||
&self.message
|
||||
}
|
||||
|
||||
pub(crate) fn with_message(mut self, message: String) -> Self {
|
||||
self.message = message;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn error_value(&self) -> Option<ScriptErrorValue> {
|
||||
self.exception_id
|
||||
.map(ScriptErrorValue::Retained)
|
||||
.or_else(|| self.error_constructor.map(ScriptErrorValue::Constructor))
|
||||
}
|
||||
|
||||
pub(crate) fn error_constructor(&self) -> Option<ScriptErrorConstructorKind> {
|
||||
self.error_constructor
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::host::{
|
||||
};
|
||||
use crate::page_task_queue::PostParseLifecycleWork;
|
||||
use crate::planning::PreparedScript;
|
||||
use crate::types::{ScriptErrorConstructorKind, ScriptKind};
|
||||
use crate::types::{ScriptErrorValue, ScriptKind};
|
||||
use tracing::warn;
|
||||
|
||||
impl DocumentRuntime {
|
||||
@@ -156,7 +156,7 @@ impl DocumentRuntime {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Vec<PostParseLifecycleWork> {
|
||||
if script.host_script_handle.is_none()
|
||||
&& matches!(script.kind, ScriptKind::Module | ScriptKind::ImportMap)
|
||||
@@ -186,7 +186,7 @@ impl DocumentRuntime {
|
||||
message,
|
||||
Some(script.url.as_str()),
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -196,17 +196,12 @@ impl DocumentRuntime {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Vec<PageTask> {
|
||||
self.plan_script_failure_lifecycle_work(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
)
|
||||
.into_iter()
|
||||
.map(PostParseLifecycleWork::into_page_task)
|
||||
.collect()
|
||||
self.plan_script_failure_lifecycle_work(script, message, module_failure_policy, error_value)
|
||||
.into_iter()
|
||||
.map(PostParseLifecycleWork::into_page_task)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn enqueue_parser_boundary_lifecycle_work(&mut self, work: PostParseLifecycleWork) {
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
PreparedScript, PreparedScriptSourceLoadOutcome, SharedScriptSourceLoad,
|
||||
prepared_script_with_loaded_source,
|
||||
},
|
||||
types::{ScriptErrorConstructorKind, SharedNavigationResponseResult},
|
||||
types::{ScriptErrorValue, SharedNavigationResponseResult},
|
||||
};
|
||||
|
||||
use super::{DocumentScriptExecutionLane, DocumentScriptSourceFailureLane};
|
||||
@@ -21,7 +21,7 @@ use super::{DocumentScriptExecutionLane, DocumentScriptSourceFailureLane};
|
||||
pub(crate) struct PageOwnedDocumentScriptSourceFailure {
|
||||
message: String,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
}
|
||||
|
||||
impl PageOwnedDocumentScriptSourceFailure {
|
||||
@@ -29,19 +29,19 @@ impl PageOwnedDocumentScriptSourceFailure {
|
||||
Self {
|
||||
message,
|
||||
module_failure_policy: None,
|
||||
error_constructor: None,
|
||||
error_value: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn runtime_terminal(
|
||||
message: String,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Self {
|
||||
Self {
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,13 +55,9 @@ impl PageOwnedDocumentScriptSourceFailure {
|
||||
) -> (
|
||||
String,
|
||||
Option<ModuleFailurePolicy>,
|
||||
Option<ScriptErrorConstructorKind>,
|
||||
Option<ScriptErrorValue>,
|
||||
) {
|
||||
(
|
||||
self.message,
|
||||
self.module_failure_policy,
|
||||
self.error_constructor,
|
||||
)
|
||||
(self.message, self.module_failure_policy, self.error_value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::parser_module_evaluation::{
|
||||
use crate::parser_script::action::ParserClassicScriptNextOwnerAction;
|
||||
use crate::planning::{PreparedScript, SharedScriptSourceLoad};
|
||||
use crate::stylesheet_blocking::DocumentBlockingStylesheetSignature;
|
||||
use crate::types::ScriptErrorConstructorKind;
|
||||
use crate::types::ScriptErrorValue;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct ParserModuleEvaluationReactionUpdate {
|
||||
@@ -620,7 +620,7 @@ where
|
||||
&mut self,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
mut convert: Convert,
|
||||
) -> Option<ParserModuleEvaluationReactionUpdate>
|
||||
where
|
||||
@@ -631,7 +631,7 @@ where
|
||||
{
|
||||
let root_entry =
|
||||
self.parser_module_evaluations
|
||||
.mark_rejected(reaction_id, reason, error_constructor)?;
|
||||
.mark_rejected(reaction_id, reason, error_value)?;
|
||||
let queued_ready_action_count = self.promote_ready_parser_module_evaluations(&mut convert);
|
||||
Some(ParserModuleEvaluationReactionUpdate {
|
||||
root_entry,
|
||||
|
||||
@@ -24,8 +24,7 @@ use super::{
|
||||
prepared_script_with_loaded_source,
|
||||
},
|
||||
types::{
|
||||
ScriptErrorConstructorKind, ScriptKind, ScriptMode, ScriptSourceKind,
|
||||
SharedNavigationResponseResult,
|
||||
ScriptErrorValue, ScriptKind, ScriptMode, ScriptSourceKind, SharedNavigationResponseResult,
|
||||
},
|
||||
};
|
||||
use crate::frame_owner_model::MainDocumentScriptLoadDelayLease;
|
||||
@@ -165,7 +164,7 @@ struct DynamicScriptFailure {
|
||||
kind: DynamicScriptFailureKind,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
source_network_result: Option<SharedNavigationResponseResult>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -187,7 +186,7 @@ impl DynamicScriptFailure {
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result: None,
|
||||
error_constructor: None,
|
||||
error_value: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,11 +198,8 @@ impl DynamicScriptFailure {
|
||||
self
|
||||
}
|
||||
|
||||
fn with_error_constructor(
|
||||
mut self,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
) -> Self {
|
||||
self.error_constructor = error_constructor;
|
||||
fn with_error_value(mut self, error_value: Option<ScriptErrorValue>) -> Self {
|
||||
self.error_value = error_value;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -335,7 +331,7 @@ pub(super) enum DynamicScriptRunnable {
|
||||
kind: DynamicScriptFailureKind,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
source_network_result: Option<SharedNavigationResponseResult>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -354,7 +350,7 @@ pub(super) struct DynamicScriptFailureTerminal {
|
||||
pub(super) kind: DynamicScriptFailureKind,
|
||||
pub(super) module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
pub(super) source_network_result: Option<SharedNavigationResponseResult>,
|
||||
pub(super) error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
pub(super) error_value: Option<ScriptErrorValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -582,7 +578,7 @@ impl DynamicScriptOwner {
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result,
|
||||
error_constructor,
|
||||
error_value,
|
||||
}) = self.followup_work.pop_front()
|
||||
else {
|
||||
unreachable!("owned error terminal changed after an immutable front check")
|
||||
@@ -594,7 +590,7 @@ impl DynamicScriptOwner {
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result,
|
||||
error_constructor,
|
||||
error_value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -642,33 +638,33 @@ impl DynamicScriptOwner {
|
||||
message: String,
|
||||
kind: DynamicScriptFailureKind,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) {
|
||||
self.note_script_failed_with_kind_and_error_constructor(
|
||||
self.note_script_failed_with_kind_and_error_value(
|
||||
id,
|
||||
script,
|
||||
message,
|
||||
kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn note_script_failed_with_kind_and_error_constructor(
|
||||
pub(super) fn note_script_failed_with_kind_and_error_value(
|
||||
&mut self,
|
||||
id: DynamicScriptOwnerId,
|
||||
script: &PreparedScript,
|
||||
message: String,
|
||||
kind: DynamicScriptFailureKind,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) {
|
||||
assert!(
|
||||
script.host_script_handle.is_some(),
|
||||
"runtime dynamic script should carry host handle before failure dispatch planning"
|
||||
);
|
||||
let failure = DynamicScriptFailure::with_kind(message, kind, module_failure_policy)
|
||||
.with_error_constructor(error_constructor);
|
||||
.with_error_value(error_value);
|
||||
if failure.is_deferrable_module() {
|
||||
self.note_script_failed_in_queue_or_enqueue(id, script.clone(), failure);
|
||||
self.refresh_followup_work();
|
||||
@@ -1189,13 +1185,13 @@ impl DynamicScriptOwner {
|
||||
&mut self,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Option<ModuleScriptEvaluationUpdate> {
|
||||
let update = self.mark_module_script_evaluation_reaction(
|
||||
reaction_id,
|
||||
ModuleScriptEvaluationReactionState::Rejected {
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
},
|
||||
);
|
||||
if update.is_some() {
|
||||
@@ -1279,21 +1275,21 @@ impl DynamicScriptOwner {
|
||||
message: String,
|
||||
kind: DynamicScriptFailureKind,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
source_network_result: Option<SharedNavigationResponseResult>,
|
||||
) {
|
||||
self.requeue_failed_script_front_with_error_constructor(
|
||||
self.requeue_failed_script_front_with_error_value(
|
||||
id,
|
||||
script,
|
||||
message,
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn requeue_failed_script_front_with_error_constructor(
|
||||
pub(super) fn requeue_failed_script_front_with_error_value(
|
||||
&mut self,
|
||||
id: DynamicScriptOwnerId,
|
||||
script: PreparedScript,
|
||||
@@ -1301,11 +1297,11 @@ impl DynamicScriptOwner {
|
||||
kind: DynamicScriptFailureKind,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
source_network_result: Option<SharedNavigationResponseResult>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) {
|
||||
let failure = DynamicScriptFailure::with_kind(message, kind, module_failure_policy)
|
||||
.with_source_network_result(source_network_result)
|
||||
.with_error_constructor(error_constructor);
|
||||
.with_error_value(error_value);
|
||||
self.requeue_script_failure_front(id, script, failure);
|
||||
}
|
||||
|
||||
@@ -1952,11 +1948,11 @@ impl DynamicScriptOwner {
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result,
|
||||
error_constructor,
|
||||
error_value,
|
||||
} => {
|
||||
let failure = DynamicScriptFailure::with_kind(message, kind, module_failure_policy)
|
||||
.with_source_network_result(source_network_result)
|
||||
.with_error_constructor(error_constructor);
|
||||
.with_error_value(error_value);
|
||||
if failure.is_deferrable_module() {
|
||||
self.enqueue_script_failure_with_id(id, script, failure);
|
||||
return;
|
||||
@@ -2166,7 +2162,7 @@ impl DynamicScriptOwner {
|
||||
kind: failure.kind,
|
||||
module_failure_policy: failure.module_failure_policy,
|
||||
source_network_result: failure.source_network_result,
|
||||
error_constructor: failure.error_constructor,
|
||||
error_value: failure.error_value,
|
||||
})
|
||||
}
|
||||
DynamicScriptReadyState::Loading
|
||||
@@ -3482,7 +3478,7 @@ mod tests {
|
||||
"opaque typed module graph failure".to_owned(),
|
||||
DynamicScriptFailureKind::ModuleResolve,
|
||||
Some(ModuleFailurePolicy::GraphFailure),
|
||||
Some(ScriptErrorConstructorKind::SyntaxError),
|
||||
Some(crate::types::ScriptErrorConstructorKind::SyntaxError.into()),
|
||||
);
|
||||
|
||||
let second = owner
|
||||
@@ -3499,7 +3495,7 @@ mod tests {
|
||||
let DynamicScriptRunnable::DispatchError {
|
||||
id: failure_id,
|
||||
message,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
} = failure
|
||||
else {
|
||||
@@ -3508,8 +3504,8 @@ mod tests {
|
||||
assert_eq!(failure_id, first_id);
|
||||
assert_eq!(message, "opaque typed module graph failure");
|
||||
assert_eq!(
|
||||
error_constructor,
|
||||
Some(ScriptErrorConstructorKind::SyntaxError),
|
||||
error_value,
|
||||
Some(crate::types::ScriptErrorConstructorKind::SyntaxError.into()),
|
||||
"deferred dynamic module failures must retain their original error constructor"
|
||||
);
|
||||
}
|
||||
@@ -3815,7 +3811,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn owner_terminal_failure_preserves_error_constructor() {
|
||||
fn owner_terminal_failure_preserves_error_value() {
|
||||
let mut owner = DynamicScriptOwner::default();
|
||||
owner.enqueue_batch(DynamicScriptBatch {
|
||||
async_scripts: VecDeque::from([prepared_script(0, ScriptMode::Async)]),
|
||||
@@ -3828,26 +3824,24 @@ mod tests {
|
||||
panic!("expected executable dynamic script");
|
||||
};
|
||||
|
||||
owner.note_script_failed_with_kind_and_error_constructor(
|
||||
owner.note_script_failed_with_kind_and_error_value(
|
||||
id,
|
||||
&script,
|
||||
"typed failure".to_owned(),
|
||||
DynamicScriptFailureKind::Immediate,
|
||||
None,
|
||||
Some(ScriptErrorConstructorKind::SyntaxError),
|
||||
Some(crate::types::ScriptErrorConstructorKind::SyntaxError.into()),
|
||||
);
|
||||
|
||||
let DynamicScriptRunnable::DispatchError {
|
||||
error_constructor, ..
|
||||
} = owner
|
||||
let DynamicScriptRunnable::DispatchError { error_value, .. } = owner
|
||||
.next_runnable_script()
|
||||
.expect("typed failure should become owner terminal work")
|
||||
else {
|
||||
panic!("expected dynamic script error dispatch");
|
||||
};
|
||||
assert_eq!(
|
||||
error_constructor,
|
||||
Some(ScriptErrorConstructorKind::SyntaxError)
|
||||
error_value,
|
||||
Some(crate::types::ScriptErrorConstructorKind::SyntaxError.into())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
{
|
||||
module_runtime::ModuleOwnerState,
|
||||
planning::{PreparedScript, ScriptSource},
|
||||
types::{ScriptErrorConstructorKind, ScriptKind, ScriptMode, ScriptSourceKind},
|
||||
types::{ScriptErrorValue, ScriptKind, ScriptMode, ScriptSourceKind},
|
||||
},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
@@ -1077,13 +1077,13 @@ impl HostScriptScheduler {
|
||||
&self,
|
||||
message: &str,
|
||||
filename: Option<&str>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> PostParseLifecycleWork {
|
||||
PostParseLifecycleWork::ReportWindowScriptFailure(
|
||||
WindowScriptFailureReportTask::new_with_error_constructor(
|
||||
WindowScriptFailureReportTask::new_with_error_value(
|
||||
message,
|
||||
filename.map(std::borrow::ToOwned::to_owned),
|
||||
error_constructor,
|
||||
error_value,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -1096,7 +1096,7 @@ impl HostScriptScheduler {
|
||||
message: &str,
|
||||
filename: Option<&str>,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Vec<PostParseLifecycleWork> {
|
||||
let mut tasks = Vec::new();
|
||||
let message = normalize_module_link_failure_message(message, filename);
|
||||
@@ -1130,7 +1130,7 @@ impl HostScriptScheduler {
|
||||
tasks.push(self.plan_window_script_failure_report_lifecycle_work(
|
||||
&message,
|
||||
filename,
|
||||
error_constructor,
|
||||
error_value,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1166,7 +1166,7 @@ impl HostScriptScheduler {
|
||||
message: &str,
|
||||
filename: Option<&str>,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Vec<PageTask> {
|
||||
self.plan_script_failure_lifecycle_work(
|
||||
kind,
|
||||
@@ -1175,7 +1175,7 @@ impl HostScriptScheduler {
|
||||
message,
|
||||
filename,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
)
|
||||
.into_iter()
|
||||
.map(PostParseLifecycleWork::into_page_task)
|
||||
|
||||
@@ -131,22 +131,18 @@ impl ScriptFailurePageTaskPolicy {
|
||||
report_window_failure: false,
|
||||
load_event_after_window_failure: false,
|
||||
},
|
||||
ModuleFailurePolicy::GraphFailure => Self {
|
||||
load_event: event_policy.dispatch_policy(ScriptEventKind::Load),
|
||||
error_event: ScriptEventDispatchPolicy::Skip(
|
||||
ScriptEventSkipReason::ModuleGraphFailure,
|
||||
),
|
||||
report_window_failure: true,
|
||||
load_event_after_window_failure: true,
|
||||
},
|
||||
ModuleFailurePolicy::EvaluationFailure => Self {
|
||||
load_event: event_policy.dispatch_policy(ScriptEventKind::Load),
|
||||
error_event: ScriptEventDispatchPolicy::Skip(
|
||||
ScriptEventSkipReason::ModuleGraphFailure,
|
||||
),
|
||||
report_window_failure: true,
|
||||
load_event_after_window_failure: false,
|
||||
},
|
||||
// A script with a fetched module graph still completes its
|
||||
// external-script load step when running that graph rejects.
|
||||
ModuleFailurePolicy::GraphFailure | ModuleFailurePolicy::EvaluationFailure => {
|
||||
Self {
|
||||
load_event: event_policy.dispatch_policy(ScriptEventKind::Load),
|
||||
error_event: ScriptEventDispatchPolicy::Skip(
|
||||
ScriptEventSkipReason::ModuleGraphFailure,
|
||||
),
|
||||
report_window_failure: true,
|
||||
load_event_after_window_failure: true,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Self {
|
||||
|
||||
@@ -2473,7 +2473,7 @@ fn explicit_module_failure_policy_overrides_opaque_message_text() {
|
||||
load_event: ScriptEventDispatchPolicy::Dispatch,
|
||||
error_event: ScriptEventDispatchPolicy::Skip(ScriptEventSkipReason::ModuleGraphFailure),
|
||||
report_window_failure: true,
|
||||
load_event_after_window_failure: false,
|
||||
load_event_after_window_failure: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::dom::NodeId;
|
||||
use crate::dynamic_script_owner::DynamicScriptOwnerId;
|
||||
use crate::frame_owner_model::{FrameDocumentTaskOwner, MainDocumentScriptLoadDelayLease};
|
||||
use crate::planning::PreparedScript;
|
||||
use crate::types::ScriptErrorConstructorKind;
|
||||
use crate::types::ScriptErrorValue;
|
||||
|
||||
pub(crate) struct ModuleScriptContinuation {
|
||||
pub(crate) script: PreparedScript,
|
||||
@@ -830,7 +830,7 @@ pub(crate) enum ModuleScriptEvaluationReactionState {
|
||||
Fulfilled,
|
||||
Rejected {
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1132,14 +1132,14 @@ impl JsContextHost {
|
||||
document_owner: FrameDocumentTaskOwner,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) {
|
||||
self.queue_page_module_reaction(
|
||||
RendererPageModuleReactionEvent::DocumentModuleScriptEvaluationRejected {
|
||||
document_owner,
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1165,7 +1165,7 @@ impl JsContextHost {
|
||||
realm_id: crate::frame_owner_model::FrameRealmId,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) {
|
||||
self.queue_page_module_reaction(
|
||||
RendererPageModuleReactionEvent::ChildParserModuleEvaluationRejected {
|
||||
@@ -1173,7 +1173,7 @@ impl JsContextHost {
|
||||
realm_id,
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ use crate::{
|
||||
InFlightWorkerSubresourceFetchState, MessagePortId, NetworkBodySourceId,
|
||||
PendingSubresourceAuthState, PendingSubresourceFetchState,
|
||||
PendingSubresourceResponseState, PendingWebSocketResponseState,
|
||||
RunningSubresourceFetchState, ScriptErrorConstructorKind, ScriptNetworkOutputItem,
|
||||
StreamingSubresourceFetchState, SubresourceResourceType,
|
||||
RunningSubresourceFetchState, ScriptNetworkOutputItem, StreamingSubresourceFetchState,
|
||||
SubresourceResourceType,
|
||||
},
|
||||
util::{
|
||||
string_from_utf16_units_lossy, utf16_units, utf16_units_contain_unpaired_surrogate,
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
module_runtime::DynamicModuleImportOwner,
|
||||
resource_ready::{ReadyPageTask, RendererPageTaskReadyMetadata},
|
||||
runtime::{PageOwnerTurnOutcome, RendererDocumentToken},
|
||||
types::ScriptErrorConstructorKind,
|
||||
types::ScriptErrorValue,
|
||||
};
|
||||
|
||||
use super::{RendererOwnerWakeSender, RendererOwnerWakeSource, RendererPageTaskReadySignal};
|
||||
@@ -43,7 +43,7 @@ pub(crate) enum RendererPageModuleReactionEvent {
|
||||
document_owner: FrameDocumentTaskOwner,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
},
|
||||
ChildParserModuleEvaluationFulfilled {
|
||||
document_owner: FrameDocumentTaskOwner,
|
||||
@@ -55,7 +55,7 @@ pub(crate) enum RendererPageModuleReactionEvent {
|
||||
realm_id: FrameRealmId,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
},
|
||||
DynamicModuleEvaluationFulfilled {
|
||||
import_owner: DynamicModuleImportOwner,
|
||||
@@ -120,17 +120,17 @@ impl fmt::Debug for RendererPageModuleReactionEvent {
|
||||
match self {
|
||||
Self::DocumentModuleScriptEvaluationRejected {
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
}
|
||||
| Self::ChildParserModuleEvaluationRejected {
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
} => {
|
||||
debug
|
||||
.field("reason", reason)
|
||||
.field("error_constructor", error_constructor);
|
||||
.field("error_value", error_value);
|
||||
}
|
||||
Self::DynamicModuleEvaluationRejected { .. } => {
|
||||
debug.field("reason", &"<v8::Global<Value>>");
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
host::ScriptEventTask,
|
||||
planning::PreparedScript,
|
||||
stylesheet_blocking::DocumentOwnedBlockingStylesheetDiscoveryInput,
|
||||
types::ScriptErrorConstructorKind,
|
||||
types::ScriptErrorValue,
|
||||
types::ScriptRun,
|
||||
};
|
||||
use url::Url;
|
||||
@@ -70,23 +70,23 @@ pub(crate) enum PageOwnedInternalLoadingTaskEffect {
|
||||
pub(crate) struct WindowScriptFailureReportTask {
|
||||
pub(crate) message: String,
|
||||
pub(crate) filename: Option<String>,
|
||||
pub(crate) error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
pub(crate) error_value: Option<ScriptErrorValue>,
|
||||
}
|
||||
|
||||
impl WindowScriptFailureReportTask {
|
||||
pub(crate) fn new(message: impl Into<String>, filename: Option<String>) -> Self {
|
||||
Self::new_with_error_constructor(message, filename, None)
|
||||
Self::new_with_error_value(message, filename, None)
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_error_constructor(
|
||||
pub(crate) fn new_with_error_value(
|
||||
message: impl Into<String>,
|
||||
filename: Option<String>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Self {
|
||||
Self {
|
||||
message: message.into(),
|
||||
filename,
|
||||
error_constructor,
|
||||
error_value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::document_script_scheduler::{
|
||||
use crate::document_task_lane::DocumentTaskQueue;
|
||||
use crate::module_runtime::ModuleEntryId;
|
||||
use crate::module_script_continuation::ModuleScriptEvaluationReactionState;
|
||||
use crate::types::ScriptErrorConstructorKind;
|
||||
use crate::types::ScriptErrorValue;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ParserModuleEvaluationContinuation<Work> {
|
||||
@@ -150,7 +150,7 @@ impl<Work> ParserModuleEvaluationStore<Work> {
|
||||
&mut self,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Option<ModuleEntryId> {
|
||||
let evaluation = self
|
||||
.evaluations
|
||||
@@ -158,7 +158,7 @@ impl<Work> ParserModuleEvaluationStore<Work> {
|
||||
.find(|evaluation| evaluation.reaction_id == reaction_id)?;
|
||||
evaluation.reaction_state = ModuleScriptEvaluationReactionState::Rejected {
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
};
|
||||
Some(evaluation.root_entry)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ use crate::script_vm::{
|
||||
PostParseLifecycleAdvance, PostParsePageOwnedTask, RendererDocumentIsolateHandle,
|
||||
};
|
||||
use crate::script_vm::{PreparedScriptExecutionOutcome, RendererDocumentIsolateBootstrap};
|
||||
use crate::types::ScriptErrorConstructorKind;
|
||||
use crate::types::ScriptErrorValue;
|
||||
use crate::types::ScriptSkipReason;
|
||||
use moli_page_types::{
|
||||
ContentSecurityPolicyIssueSnapshot, ContentSecurityPolicyViolationType, InspectorIssueSnapshot,
|
||||
@@ -424,14 +424,8 @@ fn wrap_native_esm_module_load_error(
|
||||
prefix: &str,
|
||||
error: crate::module_runtime::ModuleLoadError,
|
||||
) -> crate::module_runtime::ModuleLoadError {
|
||||
let wrapped = crate::module_runtime::ModuleLoadError::new(
|
||||
error.stage(),
|
||||
format!("{prefix}: {}", error.message()),
|
||||
);
|
||||
match error.error_constructor() {
|
||||
Some(error_constructor) => wrapped.with_error_constructor(error_constructor),
|
||||
None => wrapped,
|
||||
}
|
||||
let message = format!("{prefix}: {}", error.message());
|
||||
error.with_message(message)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -484,7 +478,7 @@ enum PageOwnedScriptFailureClassification {
|
||||
Typed {
|
||||
dynamic_kind: crate::dynamic_script_owner::DynamicScriptFailureKind,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -501,7 +495,7 @@ impl PageOwnedScriptFailureClassification {
|
||||
script, stage,
|
||||
),
|
||||
module_failure_policy: error.module_failure_policy(),
|
||||
error_constructor: error.error_constructor(),
|
||||
error_value: error.error_value(),
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -517,7 +511,7 @@ impl PageOwnedScriptFailureClassification {
|
||||
error.stage(),
|
||||
),
|
||||
module_failure_policy: Some(module_failure_policy),
|
||||
error_constructor: error.error_constructor(),
|
||||
error_value: error.error_value(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -595,14 +589,14 @@ fn complete_prepared_script_execution_failure(
|
||||
PageOwnedScriptFailureClassification::Typed {
|
||||
dynamic_kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
} => vm.finish_runtime_owned_script_failure_with_kind(
|
||||
dynamic_script_owner_id,
|
||||
&script,
|
||||
&error,
|
||||
dynamic_kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
),
|
||||
PageOwnedScriptFailureClassification::LegacyMessageText => {
|
||||
vm.finish_runtime_owned_script_failure(dynamic_script_owner_id, &script, &error);
|
||||
@@ -614,34 +608,34 @@ fn complete_prepared_script_execution_failure(
|
||||
Some(script.url.as_str()),
|
||||
);
|
||||
} else if vm.parser_owned_module_reports_failure_immediately(&script) {
|
||||
let (module_failure_policy, error_constructor) = match failure_classification {
|
||||
let (module_failure_policy, error_value) = match failure_classification {
|
||||
PageOwnedScriptFailureClassification::Typed {
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
} => (module_failure_policy, error_constructor),
|
||||
} => (module_failure_policy, error_value),
|
||||
PageOwnedScriptFailureClassification::LegacyMessageText => (None, None),
|
||||
};
|
||||
vm.dispatch_parser_owned_module_failure_and_finish_settlement_best_effort(
|
||||
&script,
|
||||
&error,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
} else {
|
||||
let (module_failure_policy, error_constructor) = match failure_classification {
|
||||
let (module_failure_policy, error_value) = match failure_classification {
|
||||
PageOwnedScriptFailureClassification::Typed {
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
} => (module_failure_policy, error_constructor),
|
||||
} => (module_failure_policy, error_value),
|
||||
PageOwnedScriptFailureClassification::LegacyMessageText => (None, None),
|
||||
};
|
||||
vm.enqueue_script_failure_lifecycle_work_best_effort(
|
||||
&script,
|
||||
&error,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
}
|
||||
complete_prepared_script_execution_failure_report(script, error)
|
||||
@@ -657,13 +651,12 @@ fn complete_page_owned_prepared_script_execution_failure_body(
|
||||
prepared_script_activity: crate::script_vm::PreparedScriptBodyActivity,
|
||||
) -> PageOwnedScriptExecutionOutcome {
|
||||
let terminal_activity = if completion_owner.is_runtime_owned() {
|
||||
let (dynamic_kind, module_failure_policy, error_constructor) = match failure_classification
|
||||
{
|
||||
let (dynamic_kind, module_failure_policy, error_value) = match failure_classification {
|
||||
PageOwnedScriptFailureClassification::Typed {
|
||||
dynamic_kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
} => (dynamic_kind, module_failure_policy, error_constructor),
|
||||
error_value,
|
||||
} => (dynamic_kind, module_failure_policy, error_value),
|
||||
PageOwnedScriptFailureClassification::LegacyMessageText => (
|
||||
crate::dynamic_script_owner::DynamicScriptOwner::legacy_message_failure_kind(
|
||||
&script, &error,
|
||||
@@ -678,40 +671,40 @@ fn complete_page_owned_prepared_script_execution_failure_body(
|
||||
&error,
|
||||
dynamic_kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
)
|
||||
} else if vm.parser_owned_inline_importmap_reports_window_error_immediately(&script) {
|
||||
vm.report_window_error_body_best_effort(&error, Some(script.url.as_str()), None);
|
||||
crate::script_vm::ScriptTerminalBodyActivity::EventDispatchAttempted
|
||||
} else if vm.parser_owned_module_reports_failure_immediately(&script) {
|
||||
let (module_failure_policy, error_constructor) = match failure_classification {
|
||||
let (module_failure_policy, error_value) = match failure_classification {
|
||||
PageOwnedScriptFailureClassification::Typed {
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
} => (module_failure_policy, error_constructor),
|
||||
} => (module_failure_policy, error_value),
|
||||
PageOwnedScriptFailureClassification::LegacyMessageText => (None, None),
|
||||
};
|
||||
vm.dispatch_current_prepared_script_error_body_best_effort(
|
||||
&script,
|
||||
&error,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
)
|
||||
} else {
|
||||
let (module_failure_policy, error_constructor) = match failure_classification {
|
||||
let (module_failure_policy, error_value) = match failure_classification {
|
||||
PageOwnedScriptFailureClassification::Typed {
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
} => (module_failure_policy, error_constructor),
|
||||
} => (module_failure_policy, error_value),
|
||||
PageOwnedScriptFailureClassification::LegacyMessageText => (None, None),
|
||||
};
|
||||
vm.enqueue_script_failure_lifecycle_work_best_effort(
|
||||
&script,
|
||||
&error,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
crate::script_vm::ScriptTerminalBodyActivity::NoEventDispatch
|
||||
};
|
||||
@@ -800,13 +793,13 @@ async fn execute_prepared_script_on_script_execution_lane(
|
||||
let failure_classification =
|
||||
PageOwnedScriptFailureClassification::from_prepared_script_error(&script, &error);
|
||||
if let Some(claim) = runtime_script_claim.take() {
|
||||
let (module_failure_policy, error_constructor) = match failure_classification {
|
||||
let (module_failure_policy, error_value) = match failure_classification {
|
||||
PageOwnedScriptFailureClassification::LegacyMessageText => (None, None),
|
||||
PageOwnedScriptFailureClassification::Typed {
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
} => (module_failure_policy, error_constructor),
|
||||
} => (module_failure_policy, error_value),
|
||||
};
|
||||
let message = error.into_message();
|
||||
let terminal_activity = vm.finish_claimed_runtime_owned_script_failure_body(
|
||||
@@ -814,7 +807,7 @@ async fn execute_prepared_script_on_script_execution_lane(
|
||||
&script,
|
||||
&message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
return complete_prepared_script_execution_failure_report_with_activity(
|
||||
script,
|
||||
@@ -2563,7 +2556,7 @@ impl PageVm {
|
||||
PageOwnedScriptFailureClassification::Typed {
|
||||
dynamic_kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
},
|
||||
) if dynamic_kind.is_deferrable_module() => {
|
||||
self.vm_mut()
|
||||
@@ -2573,7 +2566,7 @@ impl PageVm {
|
||||
message.clone(),
|
||||
dynamic_kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
(
|
||||
complete_prepared_script_execution_failure_report(
|
||||
|
||||
@@ -112,7 +112,7 @@ impl PageOwnedDocumentScriptHooks for MainPageOwnedDocumentScriptHooks<'_, '_> {
|
||||
failure: PageOwnedDocumentScriptSourceFailure,
|
||||
runtime_script_claim: Option<DynamicScriptPageTaskClaim>,
|
||||
) -> PageOwnedDocumentScriptBodyExecution {
|
||||
let (error, module_failure_policy, error_constructor) = failure.into_parts();
|
||||
let (error, module_failure_policy, error_value) = failure.into_parts();
|
||||
if let Some(claim) = runtime_script_claim {
|
||||
let terminal_activity = self
|
||||
.page_vm
|
||||
@@ -122,7 +122,7 @@ impl PageOwnedDocumentScriptHooks for MainPageOwnedDocumentScriptHooks<'_, '_> {
|
||||
&script,
|
||||
&error,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
return complete_prepared_script_execution_failure_report_with_activity(
|
||||
script,
|
||||
|
||||
@@ -415,7 +415,7 @@ impl PageVm {
|
||||
}
|
||||
ModuleScriptEvaluationReactionState::Rejected {
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
} => {
|
||||
let message = format!(
|
||||
"NativeEsmEvaluateFailed: native module graph evaluation rejected: {reason}"
|
||||
@@ -441,7 +441,7 @@ impl PageVm {
|
||||
self.vm_mut().report_window_error_body_best_effort(
|
||||
&message,
|
||||
Some(evaluation.script_continuation.script.url.as_str()),
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
MainParserContinuationTaskEffect::applied(
|
||||
owner,
|
||||
@@ -452,7 +452,7 @@ impl PageVm {
|
||||
.report_module_tla_rejection_and_finish_reaction_best_effort(
|
||||
&message,
|
||||
Some(evaluation.script_continuation.script.url.as_str()),
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
MainParserContinuationTaskEffect::NotApplied
|
||||
};
|
||||
@@ -487,7 +487,7 @@ impl PageVm {
|
||||
}
|
||||
ModuleScriptEvaluationReactionState::Rejected {
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
} => Some(
|
||||
self.complete_module_script_failure_for_terminal_disposition(
|
||||
evaluation.script_continuation,
|
||||
@@ -500,7 +500,7 @@ impl PageVm {
|
||||
module_failure_policy: Some(
|
||||
crate::host::ModuleFailurePolicy::EvaluationFailure,
|
||||
),
|
||||
error_constructor,
|
||||
error_value,
|
||||
},
|
||||
PreparedScriptBodyActivity::NotEntered,
|
||||
terminal_disposition,
|
||||
|
||||
@@ -148,6 +148,7 @@ mod main_runtime_script_completion;
|
||||
mod media_element_event;
|
||||
mod message_port_delivery;
|
||||
mod misc_platform_api;
|
||||
mod module_error_reporting;
|
||||
mod module_reaction;
|
||||
mod modulepreload_start_completion;
|
||||
mod navigation_api_task;
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
use super::*;
|
||||
|
||||
fn bound_parser_module(page_vm: &mut PageVm, position: u32, url: Url) -> PreparedScript {
|
||||
let mut script = prepared_external_module_for_page_vm_test_with_node(page_vm, position, url);
|
||||
let runtime = &mut page_vm.vm_mut().document_runtime;
|
||||
let body = runtime.snapshot_document().document_body_handle().unwrap();
|
||||
let node = runtime
|
||||
.dom_host_mut()
|
||||
.create_parser_element_without_attributes(
|
||||
"script".to_owned(),
|
||||
"http://www.w3.org/1999/xhtml".to_owned(),
|
||||
None,
|
||||
);
|
||||
assert!(runtime.dom_host_mut().append_child(body, node));
|
||||
script.node_id = node;
|
||||
script.host_script_handle = Some(runtime.bind_parser_owned_script_handle_for_node(node));
|
||||
script
|
||||
}
|
||||
|
||||
async fn assert_parser_module_reports_original_value(value: &str, reject_later: bool) {
|
||||
let loader =
|
||||
crate::network::ResourceRequestClient::new(&FetchConfig::default()).expect("loader");
|
||||
let mut page_vm = test_page_vm_with_loader_and_document_url(
|
||||
&loader,
|
||||
Vec::new(),
|
||||
Url::parse("https://example.com/module-reporting.html").unwrap(),
|
||||
);
|
||||
page_vm
|
||||
.vm_mut()
|
||||
.eval(&format!(
|
||||
r#"
|
||||
globalThis.__thrown = {value};
|
||||
globalThis.__hadFileName = Object.hasOwn(Object(__thrown), 'fileName');
|
||||
globalThis.__errors = [];
|
||||
globalThis.__onerrors = [];
|
||||
globalThis.__coercions = 0;
|
||||
globalThis.__unhandled = 0;
|
||||
globalThis.__loads = 0;
|
||||
addEventListener('unhandledrejection', event => {{
|
||||
++__unhandled;
|
||||
event.preventDefault();
|
||||
}});
|
||||
addEventListener('error', event => {{
|
||||
__errors.push(event.error);
|
||||
event.preventDefault();
|
||||
}});
|
||||
onerror = (message, filename, line, column, error) => {{
|
||||
__onerrors.push(error);
|
||||
return true;
|
||||
}};
|
||||
"#,
|
||||
))
|
||||
.expect("install original exception and both observers");
|
||||
let module_url = Url::parse("https://example.com/report-original.mjs").unwrap();
|
||||
let script = bound_parser_module(&mut page_vm, 9101, module_url.clone());
|
||||
page_vm
|
||||
.vm_mut()
|
||||
.eval("document.querySelector('script').addEventListener('load', () => ++__loads);")
|
||||
.unwrap();
|
||||
let work = install_parser_module_defer_work(&mut page_vm, script);
|
||||
page_vm
|
||||
.execute_post_parse_page_owned_task_on_named_owner_lane(&loader, work)
|
||||
.await
|
||||
.expect("module waits for its graph");
|
||||
let source = if reject_later {
|
||||
"await new Promise((_, reject) => { globalThis.__rejectModule = reject; });"
|
||||
} else {
|
||||
"throw globalThis.__thrown;"
|
||||
};
|
||||
enqueue_parser_owned_module_script_fetch_completion_for_test(
|
||||
&mut page_vm,
|
||||
0,
|
||||
&module_url,
|
||||
source,
|
||||
);
|
||||
assert!(
|
||||
run_next_main_module_fetch_terminal_for_test(&mut page_vm)
|
||||
.unwrap()
|
||||
.is_some()
|
||||
);
|
||||
run_ready_parser_deferred_body_for_test(&mut page_vm, &loader, "original module exception")
|
||||
.await;
|
||||
if reject_later {
|
||||
assert_eq!(page_vm.vm_mut().eval("__errors.length").unwrap(), "0");
|
||||
page_vm
|
||||
.vm_mut()
|
||||
.eval("__rejectModule(__thrown); 'rejected'")
|
||||
.unwrap();
|
||||
}
|
||||
run_parser_module_completion_turns_for_test(
|
||||
&mut page_vm,
|
||||
&loader,
|
||||
usize::from(reject_later),
|
||||
"original module exception",
|
||||
)
|
||||
.await;
|
||||
assert_eq!(
|
||||
page_vm.vm_mut().eval(
|
||||
"JSON.stringify([__errors.length, __onerrors.length, Object.is(__errors[0], __thrown), Object.is(__onerrors[0], __thrown), __coercions, Object.hasOwn(Object(__thrown), 'fileName') === __hadFileName])"
|
||||
).unwrap(),
|
||||
"[1,1,true,true,0,true]",
|
||||
"module error reporting must preserve {value}, reject_later={reject_later}",
|
||||
);
|
||||
assert_eq!(
|
||||
page_vm
|
||||
.vm_mut()
|
||||
.eval("[__loads, __unhandled].join('|')")
|
||||
.unwrap(),
|
||||
"1|0",
|
||||
"module scripts must load once without leaking their internal rejection"
|
||||
);
|
||||
assert_eq!(
|
||||
page_vm
|
||||
.report
|
||||
.runs
|
||||
.iter()
|
||||
.filter(|run| run.url() == &module_url)
|
||||
.count(),
|
||||
1,
|
||||
"reporting must not complete a module script twice",
|
||||
);
|
||||
}
|
||||
|
||||
const THROWN_VALUES: &[&str] = &[
|
||||
"Object.freeze({ marker: 42 })",
|
||||
"Object.freeze(new TypeError('original'))",
|
||||
"undefined",
|
||||
"null",
|
||||
"false",
|
||||
"-0",
|
||||
"NaN",
|
||||
"17n",
|
||||
"Symbol('original')",
|
||||
"'original string'",
|
||||
"({ toString() { ++__coercions; throw new Error('must not coerce'); } })",
|
||||
"({ get fileName() { ++__coercions; throw new Error('must not read'); }, set fileName(value) { ++__coercions; throw new Error('must not write'); } })",
|
||||
];
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn parser_module_error_reporting_preserves_synchronous_thrown_values() {
|
||||
run_page_vm_async_test(async {
|
||||
for value in THROWN_VALUES {
|
||||
assert_parser_module_reports_original_value(value, false).await;
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn parser_module_error_reporting_preserves_deferred_rejection_values() {
|
||||
run_page_vm_async_test(async {
|
||||
for value in THROWN_VALUES {
|
||||
assert_parser_module_reports_original_value(value, true).await;
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn parser_module_error_reporting_reuses_the_cached_parse_exception() {
|
||||
run_page_vm_async_test(async {
|
||||
let loader = crate::network::ResourceRequestClient::new(&FetchConfig::default()).unwrap();
|
||||
let mut page_vm = test_page_vm_with_loader_and_document_url(
|
||||
&loader, Vec::new(), Url::parse("https://example.com/parse-error.html").unwrap(),
|
||||
);
|
||||
page_vm.vm_mut().eval(r#"
|
||||
globalThis.__errors = [];
|
||||
addEventListener('error', event => {
|
||||
__errors.push(event.error);
|
||||
event.preventDefault();
|
||||
});
|
||||
"#).unwrap();
|
||||
let url = Url::parse("https://example.com/shared-syntax-error.mjs").unwrap();
|
||||
let owner = page_vm.vm().current_main_document_task_owner().unwrap();
|
||||
for position in [9101, 9102] {
|
||||
let script = bound_parser_module(&mut page_vm, position, url.clone());
|
||||
assert!(page_vm.vm_mut().claim_main_parser_deferred_script(
|
||||
owner, script, None, None, Default::default(),
|
||||
).unwrap());
|
||||
}
|
||||
let work = page_vm.seal_main_parser_deferred_scripts(owner).unwrap();
|
||||
page_vm.execute_post_parse_page_owned_task_on_named_owner_lane(&loader, work).await.unwrap();
|
||||
enqueue_parser_owned_module_script_fetch_completion_for_test(
|
||||
&mut page_vm, 0, &url, "export const broken = ;",
|
||||
);
|
||||
run_next_main_module_fetch_terminal_for_test(&mut page_vm).unwrap();
|
||||
while page_vm.vm_mut().has_ready_native_module_owner_actions() {
|
||||
run_next_native_module_owner_event_for_test(&mut page_vm, &loader, "shared syntax error").await;
|
||||
}
|
||||
for _ in 0..2 {
|
||||
run_ready_parser_deferred_body_for_test(&mut page_vm, &loader, "shared syntax error").await;
|
||||
}
|
||||
assert_eq!(page_vm.vm_mut().eval(
|
||||
"[__errors.length, __errors[0] instanceof SyntaxError, __errors[0] === __errors[1]].join('|')",
|
||||
).unwrap(), "2|true|true");
|
||||
}).await;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
FrameDocumentModuleScriptGraphNotification, FrameDocumentModuleScriptTerminalFollowup,
|
||||
},
|
||||
module_runtime::ModuleEntryId,
|
||||
types::ScriptErrorConstructorKind,
|
||||
types::ScriptErrorValue,
|
||||
};
|
||||
|
||||
pub(super) struct ChildDocumentScriptSchedulerOwner<'vm> {
|
||||
@@ -231,7 +231,7 @@ impl<'vm> ChildDocumentScriptSchedulerOwner<'vm> {
|
||||
&mut self,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> usize {
|
||||
let mut host = self.vm._context_host.borrow_mut();
|
||||
let queued_ready_action_count = host
|
||||
@@ -239,7 +239,7 @@ impl<'vm> ChildDocumentScriptSchedulerOwner<'vm> {
|
||||
.mark_parser_module_evaluation_rejected(
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
|evaluation| evaluation,
|
||||
)
|
||||
.map(|update| update.queued_ready_action_count())
|
||||
|
||||
@@ -1033,7 +1033,7 @@ impl ScriptVm {
|
||||
)
|
||||
},
|
||||
);
|
||||
let error_constructor = error.error_constructor();
|
||||
let error_value = error.error_value();
|
||||
let error = error.into_message();
|
||||
if failure_kind
|
||||
== crate::dynamic_script_owner::DynamicScriptFailureKind::Immediate
|
||||
@@ -1051,7 +1051,7 @@ impl ScriptVm {
|
||||
self.apply_runtime_script_failure_terminal(&script,
|
||||
&error,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
lease,
|
||||
);
|
||||
}
|
||||
@@ -1059,13 +1059,13 @@ impl ScriptVm {
|
||||
self.document_runtime
|
||||
.runtime_script_work_mut()
|
||||
.dynamic_scripts
|
||||
.note_script_failed_with_kind_and_error_constructor(
|
||||
.note_script_failed_with_kind_and_error_value(
|
||||
id,
|
||||
&script,
|
||||
error,
|
||||
failure_kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
}
|
||||
processed_runnable_this_turn = true;
|
||||
@@ -1093,20 +1093,20 @@ impl ScriptVm {
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result,
|
||||
error_constructor,
|
||||
error_value,
|
||||
} => {
|
||||
if yield_after_one_runnable && processed_runnable_this_turn {
|
||||
self.document_runtime
|
||||
.runtime_script_work_mut()
|
||||
.dynamic_scripts
|
||||
.requeue_failed_script_front_with_error_constructor(
|
||||
.requeue_failed_script_front_with_error_value(
|
||||
id,
|
||||
script,
|
||||
message,
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
return Ok(RuntimePendingWorkFlushOutcome::Complete);
|
||||
}
|
||||
@@ -1121,14 +1121,14 @@ impl ScriptVm {
|
||||
self.document_runtime
|
||||
.runtime_script_work_mut()
|
||||
.dynamic_scripts
|
||||
.requeue_failed_script_front_with_error_constructor(
|
||||
.requeue_failed_script_front_with_error_value(
|
||||
id,
|
||||
script,
|
||||
message,
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
return Ok(RuntimePendingWorkFlushOutcome::Complete);
|
||||
}
|
||||
@@ -1156,7 +1156,7 @@ impl ScriptVm {
|
||||
&script,
|
||||
&message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
lease,
|
||||
);
|
||||
}
|
||||
@@ -1224,7 +1224,7 @@ impl ScriptVm {
|
||||
self.document_runtime
|
||||
.runtime_script_work_mut()
|
||||
.dynamic_scripts
|
||||
.requeue_failed_script_front_with_error_constructor(
|
||||
.requeue_failed_script_front_with_error_value(
|
||||
owner_id,
|
||||
continuation.script,
|
||||
error.message().to_owned(),
|
||||
@@ -1233,7 +1233,7 @@ impl ScriptVm {
|
||||
&error,
|
||||
)),
|
||||
None,
|
||||
error.error_constructor(),
|
||||
error.error_value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::ScriptVm;
|
||||
use crate::document_runtime::DomHandle;
|
||||
use crate::host::ModuleFailurePolicy;
|
||||
use crate::module_runtime::{ModuleGraphHandle, ModuleLoadError, ModuleLoadStage};
|
||||
use crate::types::ScriptErrorConstructorKind;
|
||||
use crate::types::ScriptErrorValue;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(super) struct PendingMousePress {
|
||||
@@ -85,7 +85,7 @@ pub(crate) struct PreparedScriptExecutionError {
|
||||
message: String,
|
||||
module_load_stage: Option<ModuleLoadStage>,
|
||||
module_failure_policy: Option<ModuleFailurePolicy>,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
body_activity: PreparedScriptBodyActivity,
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ impl PreparedScriptExecutionError {
|
||||
message: message.into(),
|
||||
module_load_stage: None,
|
||||
module_failure_policy: None,
|
||||
error_constructor: None,
|
||||
error_value: None,
|
||||
body_activity: PreparedScriptBodyActivity::NotEntered,
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ impl PreparedScriptExecutionError {
|
||||
message: message.into(),
|
||||
module_load_stage: None,
|
||||
module_failure_policy: None,
|
||||
error_constructor: None,
|
||||
error_value: None,
|
||||
body_activity: PreparedScriptBodyActivity::Entered,
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ impl PreparedScriptExecutionError {
|
||||
message: error.message().to_owned(),
|
||||
module_load_stage: Some(error.stage()),
|
||||
module_failure_policy: Some(module_failure_policy),
|
||||
error_constructor: error.error_constructor(),
|
||||
error_value: error.error_value(),
|
||||
body_activity: PreparedScriptBodyActivity::NotEntered,
|
||||
}
|
||||
}
|
||||
@@ -131,7 +131,7 @@ impl PreparedScriptExecutionError {
|
||||
message: message.into(),
|
||||
module_load_stage: Some(ModuleLoadStage::Fetch),
|
||||
module_failure_policy: Some(ModuleFailurePolicy::TopLevelLoadFailure),
|
||||
error_constructor: None,
|
||||
error_value: None,
|
||||
body_activity: PreparedScriptBodyActivity::NotEntered,
|
||||
}
|
||||
}
|
||||
@@ -148,8 +148,8 @@ impl PreparedScriptExecutionError {
|
||||
self.module_failure_policy
|
||||
}
|
||||
|
||||
pub(crate) fn error_constructor(&self) -> Option<ScriptErrorConstructorKind> {
|
||||
self.error_constructor
|
||||
pub(crate) fn error_value(&self) -> Option<ScriptErrorValue> {
|
||||
self.error_value
|
||||
}
|
||||
|
||||
pub(crate) fn body_activity(&self) -> PreparedScriptBodyActivity {
|
||||
|
||||
@@ -128,7 +128,7 @@ impl ScriptVm {
|
||||
let settlement = match self.report_window_error_body(
|
||||
&task.message,
|
||||
task.filename.as_deref(),
|
||||
task.error_constructor,
|
||||
task.error_value,
|
||||
) {
|
||||
Ok(()) => MainDocumentPostParseCallbackSettlement::Completed,
|
||||
Err(error) => {
|
||||
|
||||
@@ -68,8 +68,8 @@ use crate::page_task_queue::{
|
||||
};
|
||||
use crate::planning::PreparedScript;
|
||||
use crate::types::{
|
||||
ChildDynamicImportFetchCompletion, ScriptErrorConstructorKind, SubresourceRequestInitiatorType,
|
||||
SubresourceResourceType,
|
||||
ChildDynamicImportFetchCompletion, ScriptErrorConstructorKind, ScriptErrorValue,
|
||||
SubresourceRequestInitiatorType, SubresourceResourceType,
|
||||
};
|
||||
#[cfg(test)]
|
||||
use crate::types::{
|
||||
@@ -86,6 +86,7 @@ mod child_ready_document_script;
|
||||
mod dynamic_import_selected_task_body;
|
||||
mod load_error;
|
||||
mod main_selected_task;
|
||||
pub(super) use load_error::retained_module_exception;
|
||||
use load_error::{module_load_error_value, retain_module_exception};
|
||||
pub(crate) use main_selected_task::{
|
||||
MainDynamicImportGraphFetchBodySettlement, MainNativeModuleSelectedTaskApplication,
|
||||
@@ -2634,7 +2635,7 @@ impl ScriptVm {
|
||||
&mut self,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Option<DocumentModuleReactionUpdate> {
|
||||
let parser_update = self
|
||||
.document_runtime
|
||||
@@ -2642,7 +2643,7 @@ impl ScriptVm {
|
||||
.mark_parser_module_evaluation_rejected(
|
||||
reaction_id,
|
||||
reason.clone(),
|
||||
error_constructor,
|
||||
error_value,
|
||||
parser_module_evaluation_continuation_into_ready_action,
|
||||
)
|
||||
.map(DocumentModuleReactionUpdate::ParserOwned);
|
||||
@@ -2650,7 +2651,7 @@ impl ScriptVm {
|
||||
self.mark_runtime_owned_module_script_evaluation_rejected(
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
)
|
||||
.map(DocumentModuleReactionUpdate::RuntimeOwned)
|
||||
})
|
||||
@@ -3775,6 +3776,13 @@ impl ScriptVm {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Script-element evaluation consumes rejection itself, either
|
||||
// below or through its retained TLA continuation. Claim that
|
||||
// responsibility before cleanup can notify rejected promises.
|
||||
// Dynamic import returned above and owns its own reaction.
|
||||
if let Some(promise) = promise {
|
||||
promise.mark_as_handled();
|
||||
}
|
||||
if let Err(error) = Self::perform_microtask_checkpoints(&mut scope, None) {
|
||||
return Ok(Err(ModuleLoadError::new(
|
||||
ModuleLoadStage::Evaluate,
|
||||
@@ -4185,14 +4193,10 @@ impl ScriptVm {
|
||||
RendererPageModuleReactionEvent::DocumentModuleScriptEvaluationRejected {
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
} => self
|
||||
.apply_native_module_script_evaluation_rejected(
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
)
|
||||
.apply_native_module_script_evaluation_rejected(reaction_id, reason, error_value)
|
||||
.map(PageModuleReactionApplication::module_state_updated),
|
||||
RendererPageModuleReactionEvent::ChildParserModuleEvaluationFulfilled {
|
||||
reaction_id,
|
||||
@@ -4205,12 +4209,12 @@ impl ScriptVm {
|
||||
RendererPageModuleReactionEvent::ChildParserModuleEvaluationRejected {
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
} => (self.apply_child_parser_module_evaluation_rejected(
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
) > 0)
|
||||
.then_some(PageModuleReactionApplication::module_state_updated(
|
||||
PageModuleReactionFollowup::None,
|
||||
@@ -4351,12 +4355,12 @@ impl ScriptVm {
|
||||
&mut self,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Option<PageModuleReactionFollowup> {
|
||||
let update = self.mark_module_evaluation_reaction_rejected_for_owner(
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
error_value,
|
||||
)?;
|
||||
Some(match update {
|
||||
DocumentModuleReactionUpdate::ParserOwned(update) => {
|
||||
@@ -4498,17 +4502,12 @@ fn native_module_script_reaction_rejected_callback<'s>(
|
||||
let Some(host_ptr) = context_host_ptr_from_global_bridge(scope) else {
|
||||
return;
|
||||
};
|
||||
let reason = args.get(0);
|
||||
let error_constructor = script_error_constructor_kind_from_value(scope, reason);
|
||||
let reason = reason
|
||||
.to_string(scope)
|
||||
.map(|value| value.to_rust_string_lossy(scope))
|
||||
.unwrap_or_else(|| "unknown promise rejection".to_owned());
|
||||
let error = native_module_evaluation_exception_error(scope, args.get(0), "");
|
||||
unsafe { &mut *host_ptr }.queue_document_module_script_evaluation_rejected(
|
||||
document_owner,
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
error.message().to_owned(),
|
||||
error.error_value(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4545,18 +4544,13 @@ fn child_parser_module_reaction_rejected_callback<'s>(
|
||||
let Some(host_ptr) = context_host_ptr_from_global_bridge(scope) else {
|
||||
return;
|
||||
};
|
||||
let reason = args.get(0);
|
||||
let error_constructor = script_error_constructor_kind_from_value(scope, reason);
|
||||
let reason = reason
|
||||
.to_string(scope)
|
||||
.map(|value| value.to_rust_string_lossy(scope))
|
||||
.unwrap_or_else(|| "unknown promise rejection".to_owned());
|
||||
let error = native_module_evaluation_exception_error(scope, args.get(0), "");
|
||||
unsafe { &mut *host_ptr }.queue_child_parser_module_script_evaluation_rejected(
|
||||
document_owner,
|
||||
realm_id,
|
||||
reaction_id,
|
||||
reason,
|
||||
error_constructor,
|
||||
error.message().to_owned(),
|
||||
error.error_value(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4866,11 +4860,26 @@ fn native_module_evaluation_exception_error(
|
||||
exception: v8::Local<'_, v8::Value>,
|
||||
prefix: &str,
|
||||
) -> ModuleLoadError {
|
||||
let message = exception
|
||||
.to_string(scope)
|
||||
.map(|message| message.to_rust_string_lossy(scope))
|
||||
.unwrap_or_else(|| "unknown module evaluation exception".to_owned());
|
||||
let error = ModuleLoadError::new(ModuleLoadStage::Evaluate, format!("{prefix}: {message}"));
|
||||
let id = match retain_module_exception(scope, exception) {
|
||||
Ok(id) => id,
|
||||
Err(error) => {
|
||||
return ModuleLoadError::new(
|
||||
ModuleLoadStage::Evaluate,
|
||||
format!("failed to retain module evaluation exception: {error}"),
|
||||
);
|
||||
}
|
||||
};
|
||||
// V8's internal diagnostic does not invoke author-defined toString or
|
||||
// location getters. The diagnostic text never substitutes for the value.
|
||||
let message = v8::Exception::create_message(scope, exception)
|
||||
.get(scope)
|
||||
.to_rust_string_lossy(scope);
|
||||
let message = if prefix.is_empty() {
|
||||
message
|
||||
} else {
|
||||
format!("{prefix}: {message}")
|
||||
};
|
||||
let error = ModuleLoadError::new(ModuleLoadStage::Evaluate, message).with_exception_id(id);
|
||||
match script_error_constructor_kind_from_value(scope, exception) {
|
||||
Some(error_constructor) => error.with_error_constructor(error_constructor),
|
||||
None => error,
|
||||
|
||||
@@ -1084,6 +1084,12 @@ impl ScriptVm {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Child script elements own their evaluation rejection, just
|
||||
// like main-document scripts. Do not publish the internal
|
||||
// evaluation promise as an unhandled author promise.
|
||||
if let Some(promise) = promise {
|
||||
promise.mark_as_handled();
|
||||
}
|
||||
if let Err(error) = Self::perform_microtask_checkpoints(&mut scope, None) {
|
||||
return Ok(Err(ModuleLoadError::new(
|
||||
ModuleLoadStage::Evaluate,
|
||||
|
||||
@@ -183,9 +183,9 @@ impl ScriptVm {
|
||||
&mut self,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> usize {
|
||||
super::super::child_document_script_scheduler::ChildDocumentScriptSchedulerOwner::new(self)
|
||||
.mark_parser_module_evaluation_rejected(reaction_id, reason, error_constructor)
|
||||
.mark_parser_module_evaluation_rejected(reaction_id, reason, error_value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,20 +46,7 @@ pub(super) fn module_load_error_value<'s>(
|
||||
error: &ModuleLoadError,
|
||||
) -> Result<v8::Local<'s, v8::Value>> {
|
||||
if let Some(id) = error.exception_id() {
|
||||
let global = scope.get_current_context().global(scope);
|
||||
let map = get_private_value(scope, global, MODULE_EXCEPTIONS_SLOT)
|
||||
.and_then(|value| v8::Local::<v8::Map>::try_from(value).ok())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("module exception registry missing from request realm")
|
||||
})?;
|
||||
let key = v8::BigInt::new_from_u64(scope, id.0);
|
||||
anyhow::ensure!(
|
||||
map.has(scope, key.into()) == Some(true),
|
||||
"module exception {id:?} does not belong to request realm"
|
||||
);
|
||||
return map
|
||||
.get(scope, key.into())
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to read retained module exception"));
|
||||
return retained_module_exception(scope, id);
|
||||
}
|
||||
let message = v8_string(scope, error.message())
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to allocate module error message"))?;
|
||||
@@ -73,6 +60,23 @@ pub(super) fn module_load_error_value<'s>(
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to create module error"))
|
||||
}
|
||||
|
||||
pub(in crate::script_vm) fn retained_module_exception<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
id: ModuleExceptionId,
|
||||
) -> Result<v8::Local<'s, v8::Value>> {
|
||||
let global = scope.get_current_context().global(scope);
|
||||
let map = get_private_value(scope, global, MODULE_EXCEPTIONS_SLOT)
|
||||
.and_then(|value| v8::Local::<v8::Map>::try_from(value).ok())
|
||||
.ok_or_else(|| anyhow::anyhow!("module exception registry missing from request realm"))?;
|
||||
let key = v8::BigInt::new_from_u64(scope, id.0);
|
||||
anyhow::ensure!(
|
||||
map.has(scope, key.into()) == Some(true),
|
||||
"module exception {id:?} does not belong to request realm"
|
||||
);
|
||||
map.get(scope, key.into())
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to read retained module exception"))
|
||||
}
|
||||
|
||||
impl ScriptVm {
|
||||
pub(crate) fn preserve_native_module_load_error(
|
||||
&mut self,
|
||||
|
||||
@@ -640,13 +640,13 @@ impl ScriptVm {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) -> Result<FollowupPageTaskDisposition> {
|
||||
self.enqueue_script_failure_lifecycle_work_with_load_delay_binding(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
None,
|
||||
)
|
||||
}
|
||||
@@ -656,14 +656,14 @@ impl ScriptVm {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
load_delay_binding: Option<MainDocumentScriptLoadDelayLease>,
|
||||
) -> Result<FollowupPageTaskDisposition> {
|
||||
let mut planned_failure_work = self.document_runtime.plan_script_failure_lifecycle_work(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
if let Some(binding) = load_delay_binding {
|
||||
planned_failure_work.push(PostParseLifecycleWork::SettleMainDocumentScriptLoadDelay(
|
||||
@@ -681,13 +681,13 @@ impl ScriptVm {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) {
|
||||
match self.enqueue_script_failure_lifecycle_work_for_prepared_script(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
) {
|
||||
Ok(FollowupPageTaskDisposition::Skipped) => {}
|
||||
Ok(FollowupPageTaskDisposition::Deferred | FollowupPageTaskDisposition::Enqueued) => {}
|
||||
|
||||
@@ -367,7 +367,7 @@ impl ScriptVm {
|
||||
message,
|
||||
module_failure_policy,
|
||||
source_network_result,
|
||||
error_constructor,
|
||||
error_value,
|
||||
..
|
||||
}) if self.prepared_script_uses_runtime_owned_page_task_execution(&script) => {
|
||||
self.record_runtime_warning(format_args!(
|
||||
@@ -389,7 +389,7 @@ impl ScriptVm {
|
||||
failure: crate::document_script_scheduler::PageOwnedDocumentScriptSourceFailure::runtime_terminal(
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
),
|
||||
source_network_result,
|
||||
runtime_script_claim: Some(runtime_script_claim),
|
||||
@@ -405,7 +405,7 @@ impl ScriptVm {
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result,
|
||||
error_constructor,
|
||||
error_value,
|
||||
}) => {
|
||||
let _ = (
|
||||
id,
|
||||
@@ -413,7 +413,7 @@ impl ScriptVm {
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
panic!(
|
||||
"production DynamicScriptOwner failures must carry runtime-owned Page execution identity: {}",
|
||||
@@ -552,12 +552,12 @@ impl ScriptVm {
|
||||
&mut self,
|
||||
reaction_id: u64,
|
||||
reason: String,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) -> Option<ModuleScriptEvaluationUpdate> {
|
||||
self.document_runtime
|
||||
.runtime_script_work_mut()
|
||||
.dynamic_scripts
|
||||
.mark_module_script_evaluation_rejected(reaction_id, reason, error_constructor)
|
||||
.mark_module_script_evaluation_rejected(reaction_id, reason, error_value)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1181,14 +1181,14 @@ impl ScriptVm {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) -> crate::script_vm::ScriptTerminalBodyActivity {
|
||||
let (dynamic_script_owner_id, lease) = claim.into_parts();
|
||||
let settlement = self.apply_runtime_script_failure_terminal_body(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
lease,
|
||||
);
|
||||
debug!(
|
||||
@@ -1232,14 +1232,14 @@ impl ScriptVm {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
lease: MainDocumentScriptLoadDelayLease,
|
||||
) {
|
||||
let work = self.document_runtime.plan_script_failure_lifecycle_work(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
for terminal in work {
|
||||
match terminal {
|
||||
@@ -1283,14 +1283,14 @@ impl ScriptVm {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
lease: MainDocumentScriptLoadDelayLease,
|
||||
) -> RuntimeScriptFailureTerminalBodySettlement {
|
||||
let work = self.document_runtime.plan_script_failure_lifecycle_work(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
let mut activity = crate::script_vm::ScriptTerminalBodyActivity::NoEventDispatch;
|
||||
for terminal in work {
|
||||
@@ -1303,7 +1303,7 @@ impl ScriptVm {
|
||||
self.report_window_error_body_best_effort(
|
||||
&task.message,
|
||||
task.filename.as_deref(),
|
||||
task.error_constructor,
|
||||
task.error_value,
|
||||
);
|
||||
activity = crate::script_vm::ScriptTerminalBodyActivity::EventDispatchAttempted;
|
||||
}
|
||||
@@ -1435,7 +1435,7 @@ impl ScriptVm {
|
||||
message: &str,
|
||||
kind: crate::dynamic_script_owner::DynamicScriptFailureKind,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) -> crate::script_vm::ScriptTerminalBodyActivity {
|
||||
if let Some(id) = dynamic_script_owner_id {
|
||||
if kind == crate::dynamic_script_owner::DynamicScriptFailureKind::Immediate {
|
||||
@@ -1454,7 +1454,7 @@ impl ScriptVm {
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
lease,
|
||||
)
|
||||
.activity;
|
||||
@@ -1462,13 +1462,13 @@ impl ScriptVm {
|
||||
self.document_runtime
|
||||
.runtime_script_work_mut()
|
||||
.dynamic_scripts
|
||||
.note_script_failed_with_kind_and_error_constructor(
|
||||
.note_script_failed_with_kind_and_error_value(
|
||||
id,
|
||||
script,
|
||||
message.to_owned(),
|
||||
kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
self.enqueue_immediate_runtime_script_work_if_needed();
|
||||
return crate::script_vm::ScriptTerminalBodyActivity::NoEventDispatch;
|
||||
@@ -1483,7 +1483,7 @@ impl ScriptVm {
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
self.enqueue_immediate_runtime_script_work_if_needed();
|
||||
return activity;
|
||||
@@ -1492,7 +1492,7 @@ impl ScriptVm {
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
) {
|
||||
Ok(FollowupPageTaskDisposition::Skipped) => {}
|
||||
Ok(
|
||||
@@ -1531,7 +1531,7 @@ impl ScriptVm {
|
||||
message: String,
|
||||
kind: crate::dynamic_script_owner::DynamicScriptFailureKind,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) {
|
||||
debug_assert!(
|
||||
kind.is_deferrable_module(),
|
||||
@@ -1540,13 +1540,13 @@ impl ScriptVm {
|
||||
self.document_runtime
|
||||
.runtime_script_work_mut()
|
||||
.dynamic_scripts
|
||||
.note_script_failed_with_kind_and_error_constructor(
|
||||
.note_script_failed_with_kind_and_error_value(
|
||||
dynamic_script_owner_id,
|
||||
script,
|
||||
message,
|
||||
kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1604,14 +1604,14 @@ impl ScriptVm {
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result: _,
|
||||
error_constructor,
|
||||
error_value,
|
||||
} = terminal;
|
||||
if let Some(lease) = lease {
|
||||
self.apply_runtime_script_failure_terminal(
|
||||
&script,
|
||||
&message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
lease,
|
||||
);
|
||||
}
|
||||
@@ -1648,7 +1648,7 @@ impl ScriptVm {
|
||||
kind,
|
||||
module_failure_policy,
|
||||
source_network_result: _,
|
||||
error_constructor,
|
||||
error_value,
|
||||
} = terminal;
|
||||
if let Some(lease) = lease {
|
||||
let lease_owner = lease.owner();
|
||||
@@ -1656,7 +1656,7 @@ impl ScriptVm {
|
||||
&script,
|
||||
&message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
lease,
|
||||
);
|
||||
if terminal.activity
|
||||
@@ -1695,7 +1695,7 @@ impl ScriptVm {
|
||||
message: &str,
|
||||
kind: crate::dynamic_script_owner::DynamicScriptFailureKind,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) {
|
||||
if let Some(id) = dynamic_script_owner_id {
|
||||
if kind == crate::dynamic_script_owner::DynamicScriptFailureKind::Immediate {
|
||||
@@ -1711,7 +1711,7 @@ impl ScriptVm {
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
lease,
|
||||
);
|
||||
}
|
||||
@@ -1720,13 +1720,13 @@ impl ScriptVm {
|
||||
self.document_runtime
|
||||
.runtime_script_work_mut()
|
||||
.dynamic_scripts
|
||||
.note_script_failed_with_kind_and_error_constructor(
|
||||
.note_script_failed_with_kind_and_error_value(
|
||||
id,
|
||||
script,
|
||||
message.to_owned(),
|
||||
kind,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
debug!(
|
||||
dynamic_script_owner_id = ?id,
|
||||
@@ -1746,7 +1746,7 @@ impl ScriptVm {
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
self.enqueue_immediate_runtime_script_work_if_needed();
|
||||
return;
|
||||
@@ -1755,7 +1755,7 @@ impl ScriptVm {
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
) {
|
||||
Ok(FollowupPageTaskDisposition::Skipped) => {}
|
||||
Ok(
|
||||
|
||||
@@ -117,6 +117,12 @@ pub(super) fn flush_pending_promise_rejections(scope: &mut v8::PinScope<'_, '_>)
|
||||
.realm
|
||||
.with_current_scope(scope, host_ptr, |scope, dispatch_scope| {
|
||||
let promise = v8::Local::new(scope, &rejection.promise);
|
||||
// HTML rechecks PromiseIsHandled immediately before notifying.
|
||||
// The host, or an earlier notification in this detached batch,
|
||||
// may have handled it since it was first queued.
|
||||
if promise.has_handler() {
|
||||
return;
|
||||
}
|
||||
let reason = rejection
|
||||
.reason
|
||||
.as_ref()
|
||||
|
||||
@@ -16,6 +16,7 @@ use crate::context_bootstrap::{
|
||||
};
|
||||
use crate::host::ScriptEventTask;
|
||||
use crate::native_bridge::JsContextHost;
|
||||
use crate::types::ScriptErrorValue;
|
||||
use crate::util::{get_private_value, v8_string, v8str};
|
||||
|
||||
impl ScriptVm {
|
||||
@@ -51,9 +52,9 @@ impl ScriptVm {
|
||||
&mut self,
|
||||
message: &str,
|
||||
filename: Option<&str>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) {
|
||||
if let Err(error) = self.report_window_error_body(message, filename, error_constructor) {
|
||||
if let Err(error) = self.report_window_error_body(message, filename, error_value) {
|
||||
self.record_runtime_warning(format_args!(
|
||||
"window script failure body dispatch failed for `{}`: {error}",
|
||||
filename.unwrap_or("")
|
||||
@@ -65,7 +66,7 @@ impl ScriptVm {
|
||||
&mut self,
|
||||
message: &str,
|
||||
filename: Option<&str>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<ScriptErrorValue>,
|
||||
) -> Result<()> {
|
||||
let context_ptr: *const v8::Global<v8::Context> = &self.page_default_context;
|
||||
let context_host = self._context_host.clone();
|
||||
@@ -78,13 +79,20 @@ impl ScriptVm {
|
||||
let global = scope.get_current_context().global(scope);
|
||||
let message_value = v8_string(scope, message)
|
||||
.ok_or_else(|| anyhow!("failed to allocate reportError message"))?;
|
||||
let error_value = window_script_failure_error_value(
|
||||
scope,
|
||||
global,
|
||||
error_constructor,
|
||||
message_value,
|
||||
);
|
||||
if let Some(filename) = filename
|
||||
let retained = matches!(error_value, Some(ScriptErrorValue::Retained(_)));
|
||||
let error_value = match error_value {
|
||||
Some(ScriptErrorValue::Retained(id)) => {
|
||||
super::native_module::retained_module_exception(scope, id)?
|
||||
}
|
||||
Some(ScriptErrorValue::Constructor(kind)) => {
|
||||
window_script_failure_error_value(scope, global, Some(kind), message_value)
|
||||
}
|
||||
None => window_script_failure_error_value(scope, global, None, message_value),
|
||||
};
|
||||
// Location metadata belongs to the ErrorEvent. Never mutate
|
||||
// the original exception (or invoke an author's setter).
|
||||
if !retained
|
||||
&& let Some(filename) = filename
|
||||
&& let Some(filename_value) = v8_string(scope, filename)
|
||||
&& let Ok(error_object) = v8::Local::<v8::Object>::try_from(error_value)
|
||||
{
|
||||
|
||||
@@ -29,10 +29,10 @@ impl ScriptVm {
|
||||
&mut self,
|
||||
message: &str,
|
||||
filename: Option<&str>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
boundary: &'static str,
|
||||
) -> Result<()> {
|
||||
self.report_window_error_body(message, filename, error_constructor)?;
|
||||
self.report_window_error_body(message, filename, error_value)?;
|
||||
self.perform_owner_lane_task_microtask_checkpoints()
|
||||
.with_context(|| format!("{boundary} checkpoint failed"))
|
||||
}
|
||||
@@ -42,14 +42,14 @@ impl ScriptVm {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
boundary: &'static str,
|
||||
) {
|
||||
let planned_failure_work = self.document_runtime.plan_script_failure_lifecycle_work(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
for work in planned_failure_work {
|
||||
let result = match work {
|
||||
@@ -60,7 +60,7 @@ impl ScriptVm {
|
||||
.report_window_error_and_finish_synchronous_checkpoint(
|
||||
&task.message,
|
||||
task.filename.as_deref(),
|
||||
task.error_constructor,
|
||||
task.error_value,
|
||||
boundary,
|
||||
),
|
||||
_ => continue,
|
||||
@@ -101,13 +101,13 @@ impl ScriptVm {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) {
|
||||
self.dispatch_planned_script_failure_and_finish_each_synchronous_checkpoint(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
"parser-owned module failure settlement",
|
||||
);
|
||||
}
|
||||
@@ -120,13 +120,13 @@ impl ScriptVm {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) {
|
||||
self.dispatch_planned_script_failure_and_finish_each_synchronous_checkpoint(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
"unclaimed runtime-script terminal",
|
||||
);
|
||||
}
|
||||
@@ -137,12 +137,12 @@ impl ScriptVm {
|
||||
&mut self,
|
||||
message: &str,
|
||||
filename: Option<&str>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) {
|
||||
if let Err(error) = self.report_window_error_and_finish_synchronous_checkpoint(
|
||||
message,
|
||||
filename,
|
||||
error_constructor,
|
||||
error_value,
|
||||
"module TLA rejection reaction",
|
||||
) {
|
||||
self.record_runtime_warning(format_args!(
|
||||
@@ -179,7 +179,7 @@ impl ScriptVm {
|
||||
if let Err(error) = self.report_window_error_and_finish_synchronous_checkpoint(
|
||||
&task.message,
|
||||
task.filename.as_deref(),
|
||||
task.error_constructor,
|
||||
task.error_value,
|
||||
"runtime script failure terminal",
|
||||
) {
|
||||
self.record_runtime_warning(format_args!(
|
||||
@@ -195,13 +195,13 @@ impl ScriptVm {
|
||||
script: &PreparedScript,
|
||||
message: &str,
|
||||
module_failure_policy: Option<crate::host::ModuleFailurePolicy>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) -> ScriptTerminalBodyActivity {
|
||||
let planned_failure_work = self.document_runtime.plan_script_failure_lifecycle_work(
|
||||
script,
|
||||
message,
|
||||
module_failure_policy,
|
||||
error_constructor,
|
||||
error_value,
|
||||
);
|
||||
let mut activity = ScriptTerminalBodyActivity::NoEventDispatch;
|
||||
for work in planned_failure_work {
|
||||
@@ -214,7 +214,7 @@ impl ScriptVm {
|
||||
self.report_window_error_body_best_effort(
|
||||
&task.message,
|
||||
task.filename.as_deref(),
|
||||
task.error_constructor,
|
||||
task.error_value,
|
||||
);
|
||||
activity = ScriptTerminalBodyActivity::EventDispatchAttempted;
|
||||
}
|
||||
@@ -245,7 +245,7 @@ impl ScriptVm {
|
||||
self.report_window_script_failure_and_checkpoint_for_test(
|
||||
&task.message,
|
||||
task.filename.as_deref(),
|
||||
task.error_constructor,
|
||||
task.error_value,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -254,12 +254,12 @@ impl ScriptVm {
|
||||
&mut self,
|
||||
message: &str,
|
||||
filename: Option<&str>,
|
||||
error_constructor: Option<crate::types::ScriptErrorConstructorKind>,
|
||||
error_value: Option<crate::types::ScriptErrorValue>,
|
||||
) {
|
||||
if let Err(error) = self.report_window_error_and_finish_synchronous_checkpoint(
|
||||
message,
|
||||
filename,
|
||||
error_constructor,
|
||||
error_value,
|
||||
"test-only Window error",
|
||||
) {
|
||||
self.record_runtime_warning(format_args!(
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn rejection_notification_rechecks_handlers_added_by_an_earlier_notification() {
|
||||
let mut vm = new_storage_test_vm("https://promise-notification-batch.test/");
|
||||
vm.eval(
|
||||
r#"
|
||||
globalThis.__notifications = [];
|
||||
const first = Promise.reject('first');
|
||||
const second = Promise.reject('second');
|
||||
onunhandledrejection = event => {
|
||||
__notifications.push(event.reason);
|
||||
event.preventDefault();
|
||||
if (event.promise === first) second.catch(() => {});
|
||||
};
|
||||
onrejectionhandled = () => __notifications.push('rejectionhandled');
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
vm.eval("0").unwrap();
|
||||
assert_eq!(
|
||||
vm.eval("JSON.stringify(__notifications)").unwrap(),
|
||||
r#"["first"]"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn main_window_unhandled_rejection_dispatches_to_main_window() {
|
||||
let mut vm = new_storage_test_vm("https://main-promise-rejection.test/");
|
||||
|
||||
@@ -1,6 +1,47 @@
|
||||
use super::*;
|
||||
use crate::parser::{PreparedImportMap, PreparedImportMapSource};
|
||||
|
||||
#[test]
|
||||
fn retained_window_error_body_preserves_identity_without_owning_the_checkpoint() {
|
||||
let mut vm = new_storage_test_vm("https://script-terminal.test/retained.html");
|
||||
vm.eval(
|
||||
r#"
|
||||
globalThis.__errors = [];
|
||||
globalThis.__order = [];
|
||||
addEventListener('error', event => {
|
||||
__errors.push(event.error);
|
||||
__order.push('error');
|
||||
queueMicrotask(() => __order.push('microtask'));
|
||||
event.preventDefault();
|
||||
});
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let error = vm
|
||||
.preserve_native_module_load_error(
|
||||
None,
|
||||
crate::module_runtime::ModuleLoadError::new(
|
||||
crate::module_runtime::ModuleLoadStage::Compile,
|
||||
"remembered syntax error",
|
||||
)
|
||||
.with_error_constructor(crate::types::ScriptErrorConstructorKind::SyntaxError),
|
||||
)
|
||||
.unwrap();
|
||||
for _ in 0..2 {
|
||||
vm.report_window_error_body(error.message(), Some("module.js"), error.error_value())
|
||||
.unwrap();
|
||||
}
|
||||
assert_eq!(vm.eval_without_microtask_checkpoint_for_test(
|
||||
"[__order.join('|'), __errors[0] === __errors[1], __errors[0] instanceof SyntaxError, Object.hasOwn(__errors[0], 'fileName')].join(',')",
|
||||
).unwrap(), "error|error,true,true,false");
|
||||
vm.perform_script_task_checkpoint(None).unwrap();
|
||||
assert_eq!(
|
||||
vm.eval_without_microtask_checkpoint_for_test("__order.join('|')")
|
||||
.unwrap(),
|
||||
"error|error|microtask|microtask"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_error_body_leaves_checkpoint_to_its_carrier() {
|
||||
let mut vm = new_storage_test_vm("https://script-terminal.test/body.html");
|
||||
|
||||
@@ -37,6 +37,21 @@ pub(crate) enum ScriptErrorConstructorKind {
|
||||
WebAssemblyLinkError,
|
||||
}
|
||||
|
||||
/// The value carried by script-failure reporting tasks. Constructor metadata
|
||||
/// remains a fallback for failures originating in the host; a JavaScript
|
||||
/// exception must instead retain its original value in the reporting realm.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum ScriptErrorValue {
|
||||
Constructor(ScriptErrorConstructorKind),
|
||||
Retained(moli_module_script_tree::ModuleExceptionId),
|
||||
}
|
||||
|
||||
impl From<ScriptErrorConstructorKind> for ScriptErrorValue {
|
||||
fn from(constructor: ScriptErrorConstructorKind) -> Self {
|
||||
Self::Constructor(constructor)
|
||||
}
|
||||
}
|
||||
|
||||
pub use moli_script::{
|
||||
ScriptElementClassificationInput, ScriptPreparationClassificationInput,
|
||||
classify_script_preparation,
|
||||
|
||||
Reference in New Issue
Block a user