[
https://issues.apache.org/jira/browse/GROOVY-11907?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18071391#comment-18071391
]
Paul King edited comment on GROOVY-11907 at 4/6/26 10:29 AM:
-------------------------------------------------------------
Claude's analysis:
h2. Summary
*Root Cause:* GROOVY-11817 moved `DYNAMIC_RESOLUTION` checking from
method-level (in `StaticTypesWriterController.getCallSiteWriter()`) to
per-expression (in `getCallSiteWriterFor(Expression)`). This works correctly
for property expressions, which were wired up to use the new per-expression
check. However, trait static field helpers generate a compound expression like:
{code}
(($static$self instanceof Class) ? $static$self : $static$self.getClass())
.traitName__fieldName$get()
{code}
The outer `MethodCallExpression` (the field getter) is correctly marked with
`DYNAMIC_RESOLUTION` by `TraitTypeCheckingExtension.makeDynamic()`, which also
sets `DYNAMIC_RESOLUTION` on the enclosing method. The inner
`$static$self.getClass()` sub-expression is NOT marked. Before GROOVY-11817,
the method-level flag caused the dynamic call site writer to be used for all
expressions in the method, including this sub-expression. After GROOVY-11817,
the method-level check was removed, and the unmarked sub-expression falls
through to the static call site writer. The mix of dynamic (outer) and static
(inner) writers within the ternary produces bytecode with inconsistent stack
frame types, causing `AssertionError` in ASM's `Frame.putAbstractType` during
frame computation.
*Trigger conditions:* The bug manifests when compilation order causes
`TraitTypeCheckingExtension.handleMissingMethod` to be invoked for the trait
static field helper (triggering `makeDynamic`). This occurs when a compiler
configuration script with an inline `CONVERSION` phase customizer is active
together with another global AST transform (e.g. Spock's `SpockTransform`) that
alters class processing order. Without these, the trait helper method resolves
normally during type checking, `makeDynamic` is never called, all expressions
use the static writer consistently, and no frame issue occurs.
*Fix:* In `TraitReceiverTransformer.asClass()`, mark the `getClass()`
sub-expression with `DYNAMIC_RESOLUTION` directly when generating receiver
expressions for static field access. This ensures
`StaticInvocationWriter.makeCall()` delegates it to the dynamic writer,
consistent with the outer field getter expression, regardless of whether the
method-level flag is checked.
*Bisected to:* commit `920970323e` (GROOVY-11817) on the `GROOVY_5_0_X` branch.
was (Author: paulk):
h2. Summary
*Root Cause:* GROOVY-11817 moved `DYNAMIC_RESOLUTION` checking from
method-level (in `StaticTypesWriterController.getCallSiteWriter()`) to
per-expression (in `getCallSiteWriterFor(Expression)`). This works correctly
for property expressions, which were wired up to use the new per-expression
check. However, trait static field helpers generate a compound expression like:
{code}
(($static$self instanceof Class) ? $static$self : $static$self.getClass())
.traitName__fieldName$get()
{code}
The outer `MethodCallExpression` (the field getter) is correctly marked with
`DYNAMIC_RESOLUTION` by `TraitTypeCheckingExtension.makeDynamic()`, which also
sets `DYNAMIC_RESOLUTION` on the enclosing method. The inner
`$static$self.getClass()` sub-expression is NOT marked. Before GROOVY-11817,
the method-level flag caused the dynamic call site writer to be used for all
expressions in the method, including this sub-expression. After GROOVY-11817,
the method-level check was removed, and the unmarked sub-expression falls
through to the static call site writer. The mix of dynamic (outer) and static
(inner) writers within the ternary produces bytecode with inconsistent stack
frame types, causing `AssertionError` in ASM's `Frame.putAbstractType` during
frame computation.
*Trigger conditions:* The bug manifests when compilation order causes
`TraitTypeCheckingExtension.handleMissingMethod` to be invoked for the trait
static field helper (triggering `makeDynamic`). This occurs when a compiler
configuration script with an inline `CONVERSION` phase customizer is active
together with another global AST transform (e.g. Spock's `SpockTransform`) that
alters class processing order. Without these, the trait helper method resolves
normally during type checking, `makeDynamic` is never called, all expressions
use the static writer consistently, and no frame issue occurs.
*Fix:* In `TraitReceiverTransformer.asClass()`, mark the `getClass()`
sub-expression with `DYNAMIC_RESOLUTION` directly when generating receiver
expressions for static field access. This ensures
`StaticInvocationWriter.makeCall()` delegates it to the dynamic writer,
consistent with the outer field getter expression, regardless of whether the
method-level flag is checked.
*Bisected to:* commit `920970323e` (GROOVY-11817) on the `GROOVY_5_0_X` branch.
> SC: trait static field helper generates invalid bytecode when method-level
> DYNAMIC_RESOLUTION is present (GROOVY-11817 regression)
> -----------------------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-11907
> URL: https://issues.apache.org/jira/browse/GROOVY-11907
> Project: Groovy
> Issue Type: Bug
> Reporter: Paul King
> Priority: Major
>
> This is to track the *_grails-geb testFixtures_* error discussed here:
> [Canary] Grails 8 on Groovy 5 + Spring Boot 4 integration branch
> https://github.com/apache/grails-core/pull/15557
--
This message was sent by Atlassian Jira
(v8.20.10#820010)