Skip to content

[Literal] Respect PrintLimiter when printing cyclic data - #9305

Merged
tlively merged 1 commit into
mainfrom
literal-print-limiter
Oct 27, 2026
Merged

tlively merged 1 commit into
mainfrom
literal-print-limiter

Conversation

@tlively

@tlively tlively commented Oct 24, 2026

Copy link
Copy Markdown
Member

operator<<(std::ostream&, Literal) instantiated a PrintLimiter (incrementing PrintLimiter::printed), but never checked limiter.stop(), so would infinitely recurse when printing cyclic data. This was a regression from #9051.

Check limiter.stop() at the entry of operator<<(std::ostream&, Literal) to print [..] and return early when the print limit is reached, and break out of the GCData element loop once limiter.stop() becomes true.

Add tests for wasm-ctor-eval and --fuzz-exec showing that we no longer infinitely recurse.

`operator<<(std::ostream&, Literal)` instantiated a `PrintLimiter` (incrementing `PrintLimiter::printed`), but never checked `limiter.stop()`, so would infinitely recurse when printing cyclic data. This was a regression from #9051.

Check `limiter.stop()` at the entry of `operator<<(std::ostream&, Literal)` to print `[..]` and return early when the print limit is reached, and break out of the `GCData` element loop once `limiter.stop()` becomes true.

Add tests for wasm-ctor-eval and --fuzz-exec showing that we no longer infinitely recurse.
@tlively
tlively requested a review from a team as a code owner October 24, 2026 07:05
@tlively
tlively requested review from kripken and removed request for a team October 24, 2026 07:05

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a print limiter check in src/wasm/literal.cpp to prevent infinite recursion and excessive output when formatting and printing cyclic or recursive Garbage Collection (GC) structures. It also adds test cases in test/lit/ctor-eval/gc-cycle.wast and test/lit/exec/gc-cycle-leak.wast to verify that printing cyclic structures terminates correctly and outputs the expected truncated format ([..]). No review comments were provided, and there is no feedback to address.

Comment thread src/wasm/literal.cpp
}
o << literal.getElement(i);
if (limiter.stop()) {
break;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we print [..], to indicate we stopped early?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good idea.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, actually this ends up printing very many [..], specifically one for each level of nesting that gets unwound after the limit is reached. A single [..] will already have been printed by https://github.com/WebAssembly/binaryen/pull/9305/changes#diff-d063145a0a2ebd6ba09876c06a240d79e15c376eeee0c56bf69d10453fffc9b9R733 after the limit is first reached, so I think that's enough.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concretely, here's the updated test output after this change:

+ ;; CHECK-NEXT: [LoggingExternalInterface log-execution [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref
 (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0)))))
 [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0
))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $str
uct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null
 $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref
 null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut
 (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field
 (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (
field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (str
uct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0
 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $str
uct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type
 $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref
(type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0)))))
[ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0)
)))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $stru
ct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null
$struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref
null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut
(ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field
(mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (f
ield (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (stru
ct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0
(struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $stru
ct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type
$struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (
type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [
ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))
))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struc
t.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $
struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref n
ull $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (
ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (
mut (ref null $struct.0))))) [..][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]]
[..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..
]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]]]

I don't think all the extra [..] at the end are helpful.

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm otherwise

@tlively
tlively enabled auto-merge (squash) October 26, 2026 20:38

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, sgtm

@tlively
tlively merged commit 5db8f59 into main Oct 27, 2026
17 checks passed
@tlively
tlively deleted the literal-print-limiter branch October 27, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants