Thanks again for your thoughtful response. Comments inline.

On Sun, Sep 6, 2026 at 7:59 AM Matt M <[email protected]> wrote:
>
>  > [...] Just saying, that there is no 100% solution, but there should
>  > be one for this specific case.
>
> Yeah, fair enough. If I am understanding things correctly, it seems
> there's a bit more nuance involved in this as well, which makes sense.
> I haven't checked Groovy pre-6 just yet though to verify the results
> there; IIRC, the callstack is correct/works as expected for Groovy
> v5.X (though I could just be misremembering).

Things like these:

    log."$level"(msg)
    log.invokeMethod('info', [msg] as Object[])

Have the same problem in Groovy 5. They show as
org.codehaus.groovy.reflection.CachedMethod.invoke instead of the real
call.

[SNIP]

> For the various solutions proposed, my gut instinct says changing the
> flag's default is the pragmatic answer. However, the most robust
> solution of the ones proposed seems to be the third one "change indy
> to invoke on cold path bypassing invokeReflective and use
> method/accessor directly", but I am not a ranking expert to be the
> judge of that being the reasonable solution overall. I'll defer and
> trust others' judgment on this.

If you look at GROOVY-12354, it now has a three way setting for the
existing flag.
The true/false values set it on or off. Unset, it becomes opt-in for
normal Groovy
but turned on by default for AOT-linked call sites (native images). So
your gut was right!
We know the tier is a good option for native images and there were more cases
where it also improved performance but we need to do better reporting
on such cases
and it makes sense for us to take more time to do that without the
forced opt-in.

There are some good workarounds for RC-1 users (points 2-6 closely
matching what is now in the docs for folks who do want to opt-in):
1. Turn the tier off. `-Dgroovy.indy.cold.reflection=false` as a JVM
argument restores the 5.x behaviour, with no measurable cost. It must
be a command-line property because it is read once when IndyInterface
initialises. Lead with this; the rest is for people who cannot change
JVM arguments, or who still see runtime frames afterwards.
2. `@CompileStatic` on the classes whose log locations matter. Direct
calls bypass the tier, for ordinary calls; a dynamic method name still
goes through the metaclass.
3. JUL: `-Djdk.logger.packages=org.codehaus.groovy,groovy.lang` at run
time, or the `logp` methods with an explicit source. Also covers
dynamic method names and explicit `invokeMethod`, which show runtime
frames on every Groovy version.
4. Logback: add `jdk.internal.reflect`, `java.lang.reflect`,
`sun.reflect`, `org.codehaus.groovy.` and `groovy.lang.` to the
context's framework packages programmatically before logging starts.
Same coverage as item 3.
5. Log4j2: nothing short of option 1 or 2.
6. Stack traces: `StackTraceUtils.sanitize` strips every frame the
runtime adds. The logger settings above do not change exception
traces.

I weighed up re-rolling against what RC-1 also carries:
* roughly two dozen security hardening fixes, covering things like
bounded JSON and XML parsing depth, regex cost limits, file-permission
preservation in tooling, escaping in generated source, and
SecureASTCustomizer coverage gaps.
* MOP trampolines
* metaclass race and mutation fixes
* the parser cache bound behind the 5.1.1 OOM report
* compiler error tolerance and machine-readable diagnostics
* the concurrency channel-select API
All of which need feedback from real projects that a re-roll would postpone.

And we have been working on more hardening, so we might prepare RC-2
even as early as later this week.
The hardening work relates to the ASF's Responsible AI Initiative
which you can read more about here:

https://www.apache.org/foundation/initiatives/#responsible-ai-initiative


Cheers, Paul.

Reply via email to