Skip to content

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

Open
tlively wants to merge 1 commit into
mainfrom
literal-print-limiter
Open

tlively wants to merge 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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants