address review: each resetData call counts before batching, skipped calls' names are not prepared, and each skip cause is reported apart

This commit is contained in:
Dylan M. Taylor committed 2026-11-11 16:40:15 +00:00
1 parent ed5acca9cf
commit 58caad48e9
1 file changed
+30 -19
+30 -19
View File
@@ -464,6 +464,8 @@ struct Runner<'a, 'b> {
/// the event's time budget, which is kept as it was then: the clock is swapped before the end).
resets: usize,
resets_skipped: usize,
/// Of those, the ones left undone because the clock was spent, and its budget then.
resets_timed_out: usize,
reset_skip_budget_ms: Option<u64>,
relayouts: usize,
relayouts_skipped: bool,
@@ -520,6 +522,7 @@ impl<'a, 'b> Runner<'a, 'b> {
skipped: 0,
resets: 0,
resets_skipped: 0,
resets_timed_out: 0,
reset_skip_budget_ms: None,
relayouts: 0,
relayouts_skipped: false,
@@ -787,18 +790,6 @@ impl<'a, 'b> Runner<'a, 'b> {
/// `xfa.host.resetData`: the fields back to their defaults, in the form and the data.
fn reset(&mut self, names: &[String]) -> Result<(), String> {
// A script may ask for thousands of resets, each a scan of the form: past a count (which
// holds where the clock can't, in the browser) or the event's time budget they are
// skipped, and the event says so.
let late = self.clock.spent();
if late || self.resets >= MAX_RESETS_PER_EVENT {
self.resets_skipped += 1;
if late {
self.reset_skip_budget_ms = Some(self.clock.budget_ms);
}
return Ok(());
}
self.resets += 1;
self.flush()?;
let by_som = self.by_som().clone();
let targets = (!names.is_empty()).then(|| ResetTargets::new(names));
@@ -963,7 +954,21 @@ impl<'a, 'b> Runner<'a, 'b> {
XfaEffect::ResetData(names) => {
// A script resetting several subforms in a row (`resetData` once for each)
// resets them together: the same fields end up at their defaults.
pending_reset.get_or_insert_with(ResetRun::default).add(names);
// A script may ask for thousands of resets, each a scan of the form: each call
// counts, and past [`MAX_RESETS_PER_EVENT`] (which holds where the clock can't,
// in the browser) or the event's time budget it is skipped, and the event says
// so. A skipped call's names are not prepared.
let late = self.clock.spent();
if late || self.resets >= MAX_RESETS_PER_EVENT {
self.resets_skipped += 1;
if late {
self.resets_timed_out += 1;
self.reset_skip_budget_ms = Some(self.clock.budget_ms);
}
} else {
self.resets += 1;
pending_reset.get_or_insert_with(ResetRun::default).add(names);
}
let next_is_reset = effects.get(at.saturating_add(1)).is_some_and(|e| matches!(e, XfaEffect::ResetData(_)));
if !next_is_reset && let Some(run) = pending_reset.take() {
self.reset(run.names())?;
@@ -1107,12 +1112,18 @@ impl<'a, 'b> Runner<'a, 'b> {
};
self.run.out.errors.push(format!("The form's scripts were stopped: {why}; {} more were skipped", self.skipped));
}
if self.resets_skipped > 0 {
let why = match self.reset_skip_budget_ms {
Some(ms) => format!("the scripts took more than {} seconds", ms / 1000),
None => format!("more than {MAX_RESETS_PER_EVENT} resets were asked for"),
};
self.run.out.errors.push(format!("The form's scripts were stopped: {why}; {} more resets were skipped", self.resets_skipped));
let over_the_count = self.resets_skipped - self.resets_timed_out;
if over_the_count > 0 {
self.run.out.errors.push(format!(
"The form's scripts were stopped: more than {MAX_RESETS_PER_EVENT} resets were asked for; {over_the_count} more resets were skipped"
));
}
if let (true, Some(ms)) = (self.resets_timed_out > 0, self.reset_skip_budget_ms) {
self.run.out.errors.push(format!(
"The form's scripts were stopped: the scripts took more than {} seconds; {} more resets were skipped",
ms / 1000,
self.resets_timed_out
));
}
if self.dropped_output > 0 {
self.run.out.errors.push(format!("The form's scripts produced too much output; {} more messages were left out", self.dropped_output));