You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Paul King (Jira)" <ji...@apache.org> on 2026/04/06 10:30:00 UTC

[jira] [Comment Edited] (GROOVY-11907) SC: trait static field helper generates invalid bytecode when method-level DYNAMIC_RESOLUTION is present (GROOVY-11817 regression)

    [ 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)