This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new 8d2e9acea1 GROOVY-11840: SC: implicit-this reference to outer class 
static field
8d2e9acea1 is described below

commit 8d2e9acea1e58e37527d901df12b7f7ed765bbdc
Author: Eric Milles <[email protected]>
AuthorDate: Wed Jan 14 10:11:18 2026 -0600

    GROOVY-11840: SC: implicit-this reference to outer class static field
    
    3_0_X backport
---
 .../classgen/asm/sc/StaticInvocationWriter.java    | 39 +++++-----
 .../transformers/BooleanExpressionTransformer.java | 83 +++++++++-------------
 src/test/gls/innerClass/InnerClassTest.groovy      |  4 +-
 .../groovy/transform/stc/ClosuresSTCTest.groovy    | 20 ++++++
 ...StaticCompileNullCompareOptimizationTest.groovy | 62 ++++++++++++----
 5 files changed, 130 insertions(+), 78 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
index ca02908926..fcdcc0e75a 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
@@ -44,7 +44,6 @@ import org.codehaus.groovy.ast.stmt.ForStatement;
 import org.codehaus.groovy.classgen.AsmClassGenerator;
 import org.codehaus.groovy.classgen.Verifier;
 import org.codehaus.groovy.classgen.asm.BytecodeHelper;
-import org.codehaus.groovy.classgen.asm.CallSiteWriter;
 import org.codehaus.groovy.classgen.asm.CompileStack;
 import org.codehaus.groovy.classgen.asm.ExpressionAsVariableSlot;
 import org.codehaus.groovy.classgen.asm.InvocationWriter;
@@ -527,6 +526,11 @@ public class StaticInvocationWriter extends 
InvocationWriter {
         }
     }
 
+    @Override
+    protected boolean makeCachedCall(final Expression origin, final 
ClassExpression sender, final Expression receiver, final Expression message, 
final Expression arguments, final MethodCallerMultiAdapter adapter, final 
boolean safe, final boolean spreadSafe, final boolean implicitThis, final 
boolean containsSpreadExpression) {
+        return false;
+    }
+
     @Override
     public void makeCall(final Expression origin, final Expression receiver, 
final Expression message, final Expression arguments, final 
MethodCallerMultiAdapter adapter, final boolean safe, final boolean spreadSafe, 
final boolean implicitThis) {
         if (origin.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION) != 
null) {
@@ -646,17 +650,11 @@ public class StaticInvocationWriter extends 
InvocationWriter {
             mv.visitInsn(ACONST_NULL);
             mv.visitLabel(endof);
         } else {
-            if (origin instanceof AttributeExpression && (adapter == 
AsmClassGenerator.getField || adapter == 
AsmClassGenerator.getGroovyObjectField)) {
-                CallSiteWriter callSiteWriter = controller.getCallSiteWriter();
-                String fieldName = ((AttributeExpression) 
origin).getPropertyAsString();
-                if (fieldName != null && callSiteWriter instanceof 
StaticTypesCallSiteWriter) {
-                    ClassNode receiverType = 
controller.getTypeChooser().resolveType(receiver, controller.getClassNode());
-                    if (((StaticTypesCallSiteWriter) 
callSiteWriter).makeGetField(receiver, receiverType, fieldName, safe, false)) {
-                        return;
-                    }
-                }
+            boolean tryGetField = (adapter == AsmClassGenerator.getField
+                    || adapter == AsmClassGenerator.getGroovyObjectField);
+            if (!tryGetField || !makeGetField(origin, receiver, safe)) { // 
GETFIELD or GETSTATIC
+                super.makeCall(origin, receiver, message, arguments, adapter, 
safe, spreadSafe, implicitThis);
             }
-            super.makeCall(origin, receiver, message, arguments, adapter, 
safe, spreadSafe, implicitThis);
         }
     }
 
@@ -690,6 +688,20 @@ public class StaticInvocationWriter extends 
InvocationWriter {
         return false;
     }
 
+    private boolean makeGetField(final Expression origin, final Expression 
receiver, final boolean safe) {
+        if (origin instanceof AttributeExpression && 
controller.getCallSiteWriter() instanceof StaticTypesCallSiteWriter) {
+            String fieldName = ((AttributeExpression) 
origin).getPropertyAsString();
+            if (fieldName != null) {
+                ClassNode receiverType = receiver.getType();
+                if (!(receiver instanceof ClassExpression)) { // GROOVY-11840
+                    receiverType = 
controller.getTypeChooser().resolveType(receiver, controller.getClassNode());
+                }
+                return ((StaticTypesCallSiteWriter) 
controller.getCallSiteWriter()).makeGetField(receiver, receiverType, fieldName, 
safe, /*implicitThis:*/false);
+            }
+        }
+        return false;
+    }
+
     private class CheckcastReceiverExpression extends Expression {
         private final Expression receiver;
         private final MethodNode target;
@@ -763,9 +775,4 @@ public class StaticInvocationWriter extends 
InvocationWriter {
             return type;
         }
     }
-
-    @Override
-    protected boolean makeCachedCall(final Expression origin, final 
ClassExpression sender, final Expression receiver, final Expression message, 
final Expression arguments, final MethodCallerMultiAdapter adapter, final 
boolean safe, final boolean spreadSafe, final boolean implicitThis, final 
boolean containsSpreadExpression) {
-        return false;
-    }
 }
diff --git 
a/src/main/java/org/codehaus/groovy/transform/sc/transformers/BooleanExpressionTransformer.java
 
b/src/main/java/org/codehaus/groovy/transform/sc/transformers/BooleanExpressionTransformer.java
index 9471939690..f0eb7190ce 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/sc/transformers/BooleanExpressionTransformer.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/sc/transformers/BooleanExpressionTransformer.java
@@ -44,6 +44,7 @@ import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDG
 import static org.objectweb.asm.Opcodes.DUP;
 import static org.objectweb.asm.Opcodes.GOTO;
 import static org.objectweb.asm.Opcodes.ICONST_0;
+import static org.objectweb.asm.Opcodes.ICONST_1;
 import static org.objectweb.asm.Opcodes.IFNONNULL;
 import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
 import static org.objectweb.asm.Opcodes.POP;
@@ -65,12 +66,10 @@ class BooleanExpressionTransformer {
         } while (expr instanceof BooleanExpression);
 
         if (!(expr instanceof BinaryExpression)) {
-            expr = transformer.transform(expr);
-            ClassNode type = transformer.getTypeChooser().resolveType(expr, 
transformer.getClassNode());
-            Expression opt = new OptimizingBooleanExpression(expr, type);
-            if (reverse) opt = new NotExpression(opt);
-            opt.setSourcePosition(boolX);
-            return opt;
+            expr = new 
OptimizingBooleanExpression(transformer.transform(expr));
+            if (reverse) expr = new NotExpression(expr);
+            expr.setSourcePosition(boolX);
+            return expr;
         }
 
         return transformer.superTransform(boolX);
@@ -80,17 +79,13 @@ class BooleanExpressionTransformer {
 
     private static class OptimizingBooleanExpression extends BooleanExpression 
{
 
-        private final ClassNode type;
-
-        OptimizingBooleanExpression(final Expression expression, final 
ClassNode type) {
+        OptimizingBooleanExpression(final Expression expression) {
             super(expression);
-            // we must use the redirect node, otherwise InnerClassNode would 
not have the "correct" type
-            this.type = type.redirect();
         }
 
         @Override
         public Expression transformExpression(final ExpressionTransformer 
transformer) {
-            Expression opt = new 
OptimizingBooleanExpression(transformer.transform(getExpression()), type);
+            Expression opt = new 
OptimizingBooleanExpression(transformer.transform(getExpression()));
             opt.setSourcePosition(this);
             opt.copyNodeMetaData(this);
             return opt;
@@ -103,48 +98,40 @@ class BooleanExpressionTransformer {
                 MethodVisitor mv = controller.getMethodVisitor();
                 OperandStack os = controller.getOperandStack();
 
-                if (ClassHelper.Boolean_TYPE.equals(type)) {
-                    getExpression().visit(visitor);
-                    Label unbox = new Label();
-                    Label exit = new Label();
-                    // check for null
+                int mark = os.getStackLength();
+                getExpression().visit(visitor);
+
+                ClassNode type = os.getTopOperand(); // GROOVY-6270, 
GROOVY-9501, GROOVY-9569, GROOVY-10920, GROOVY-11840
+
+                if (ClassHelper.isPrimitiveType(type)) {
+                    BytecodeHelper.convertPrimitiveToBoolean(mv, type);
+                } else {
                     mv.visitInsn(DUP);
-                    mv.visitJumpInsn(IFNONNULL, unbox);
+                    Label end = new Label();
+                    Label asBoolean = new Label();
+                    mv.visitJumpInsn(IFNONNULL, asBoolean);
+
+                    // null => false
                     mv.visitInsn(POP);
                     mv.visitInsn(ICONST_0);
-                    mv.visitJumpInsn(GOTO, exit);
-                    mv.visitLabel(unbox);
-                    if (!os.getTopOperand().equals(type)) 
BytecodeHelper.doCast(mv, type); // GROOVY-6270
-                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", 
"booleanValue", "()Z", false);
-                    mv.visitLabel(exit);
-                    os.replace(ClassHelper.boolean_TYPE);
-                    return;
-                }
-
-                if (ClassHelper.isPrimitiveType(type) && 
!ClassHelper.VOID_TYPE.equals(type)) { // GROOVY-10920
-                    getExpression().visit(visitor);
-                    if (ClassHelper.boolean_TYPE.equals(type)) {
-                        os.doGroovyCast(ClassHelper.boolean_TYPE);
-                        return;
+                    mv.visitJumpInsn(GOTO, end);
+
+                    mv.visitLabel(asBoolean);
+                    if (ClassHelper.Boolean_TYPE.equals(type)) {
+                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", 
"booleanValue", "()Z", false);
+                    } else if (replaceAsBooleanWithCompareToNull(type, 
controller.getSourceUnit().getClassLoader())) {
+                        // value => true
+                        mv.visitInsn(POP);
+                        mv.visitInsn(ICONST_1);
                     } else {
-                        // in case of null safe invocation, it is possible 
that what was supposed to be a primitive type
-                        // becomes the "null" constant, so we need to recheck
-                        ClassNode top = 
controller.getOperandStack().getTopOperand();
-                        if (ClassHelper.isPrimitiveType(top)) {
-                            BytecodeHelper.convertPrimitiveToBoolean(mv, top);
-                            os.replace(ClassHelper.boolean_TYPE);
-                            return;
-                        }
+                        os.castToBool(mark, true);
                     }
+                    mv.visitLabel(end);
                 }
-
-                if (replaceAsBooleanWithCompareToNull(type, 
controller.getSourceUnit().getClassLoader())) {
-                    new CompareToNullExpression(getExpression(), 
false).visit(visitor);
-                    return;
-                }
+                os.replace(ClassHelper.boolean_TYPE);
+            } else {
+                super.visit(visitor);
             }
-
-            super.visit(visitor);
         }
 
         /**
@@ -158,7 +145,7 @@ class BooleanExpressionTransformer {
         private static boolean replaceAsBooleanWithCompareToNull(final 
ClassNode type, final ClassLoader dgmProvider) {
             if (type.getMethod("asBoolean", Parameter.EMPTY_ARRAY) != null) {
                 // GROOVY-10711
-            } else if (Modifier.isFinal(type.getModifiers()) || 
isEffectivelyFinal(type)) {
+            } else if (Modifier.isFinal(type.getModifiers()) || 
isEffectivelyFinal(type.redirect())) {
                 List<MethodNode> asBoolean = 
findDGMMethodsByNameAndArguments(dgmProvider, type, "asBoolean", 
ClassNode.EMPTY_ARRAY);
                 if (asBoolean.size() == 1) {
                     MethodNode theAsBoolean = asBoolean.get(0);
diff --git a/src/test/gls/innerClass/InnerClassTest.groovy 
b/src/test/gls/innerClass/InnerClassTest.groovy
index 42d2cf60a0..778e442a14 100644
--- a/src/test/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/gls/innerClass/InnerClassTest.groovy
@@ -597,8 +597,8 @@ final class InnerClassTest {
                     @Override
                     void run() {
                         try {
-                            if (!flag) {
-                                // do work
+                            if (flag) {
+                                assert false : 'boolean conversion'
                             }
                         } catch (e) {
                             error = e
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy 
b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index 49b2121018..91f5c1ed4f 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -660,6 +660,26 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-11840
+    void testAccessStaticFieldFromNestedClosure2() {
+        assertScript '''
+            class C {
+                private static final boolean FLAG = false
+
+                static main(args) {
+                    (1..100).each {
+                        if (FLAG) {
+                            assert false : 'boolean conversion'
+                        }
+                        if (FLAG == true) {
+                            assert false : 'boolean comparison'
+                        }
+                    }
+                }
+            }
+        '''
+    }
+
     // GROOVY-11360
     void testLexicalScopeVersusGetDynamicProperty() {
         assertScript '''
diff --git 
a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileNullCompareOptimizationTest.groovy
 
b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileNullCompareOptimizationTest.groovy
index 3e2e54c758..c466a903c1 100644
--- 
a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileNullCompareOptimizationTest.groovy
+++ 
b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileNullCompareOptimizationTest.groovy
@@ -195,10 +195,36 @@ final class StaticCompileNullCompareOptimizationTest 
extends AbstractBytecodeTes
         ''')
         assert bytecode.hasSequence([
             'ALOAD 1',
+            'DUP',
+            'IFNONNULL L1',
+            'POP',
+            'ICONST_0',
+            'GOTO L2',
+            'L1',
             config.indyEnabled ? 'INVOKEDYNAMIC cast(Ljava/lang/Object;)Z' : 
'INVOKESTATIC 
org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox 
(Ljava/lang/Object;)Z'
         ])
     }
 
+    void testNoGroovyTruthOptimizationForString() {
+        def bytecode = compile(method:'m', '''
+            @groovy.transform.CompileStatic
+            void m(String x) {
+                if (x) {
+                }
+            }
+        ''')
+        assert bytecode.hasSequence([
+            'ALOAD 1',
+            'DUP',
+            'IFNONNULL L1',
+            'POP',
+            'ICONST_0',
+            'GOTO L2',
+            'L1',
+            config.indyEnabled ? 'INVOKEDYNAMIC cast(Ljava/lang/String;)Z' : 
'INVOKESTATIC 
org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox 
(Ljava/lang/Object;)Z'
+        ])
+    }
+
     void testGroovyTruthOptimizationForFinalClass() {
         def bytecode = compile(method:'m', '''
             final class A {
@@ -211,13 +237,13 @@ final class StaticCompileNullCompareOptimizationTest 
extends AbstractBytecodeTes
         ''')
         assert bytecode.hasSequence([
             'ALOAD 1',
-            'IFNULL L1',
-            'ICONST_1',
-            'GOTO L2',
-            'L1',
+            'DUP',
+            'IFNONNULL L1',
+            'POP',
             'ICONST_0',
-            'L2',
-            'IFEQ L3'
+            'GOTO L2',
+            'POP',
+            'ICONST_1'
         ])
         if (config.indyEnabled)
             assert !bytecode.hasSequence(['INVOKEDYNAMIC cast(LA$B;)Z'])
@@ -237,13 +263,13 @@ final class StaticCompileNullCompareOptimizationTest 
extends AbstractBytecodeTes
         ''')
         assert bytecode.hasSequence([
             'ALOAD 1',
-            'IFNULL L1',
-            'ICONST_1',
-            'GOTO L2',
-            'L1',
+            'DUP',
+            'IFNONNULL L1',
+            'POP',
             'ICONST_0',
-            'L2',
-            'IFEQ L3'
+            'GOTO L2',
+            'POP',
+            'ICONST_1'
         ])
         if (config.indyEnabled)
             assert !bytecode.hasSequence(['INVOKEDYNAMIC cast(LA$B;)Z'])
@@ -263,6 +289,12 @@ final class StaticCompileNullCompareOptimizationTest 
extends AbstractBytecodeTes
         ''')
         assert bytecode.hasSequence([
             'ALOAD 1',
+            'DUP',
+            'IFNONNULL L1',
+            'POP',
+            'ICONST_0',
+            'GOTO L2',
+            'L1',
             config.indyEnabled ? 'INVOKEDYNAMIC cast(LA$B;)Z' : 'INVOKESTATIC 
org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox 
(Ljava/lang/Object;)Z'
         ])
     }
@@ -285,6 +317,12 @@ final class StaticCompileNullCompareOptimizationTest 
extends AbstractBytecodeTes
         ''')
         assert bytecode.hasSequence([
             'ALOAD 1',
+            'DUP',
+            'IFNONNULL L1',
+            'POP',
+            'ICONST_0',
+            'GOTO L2',
+            'L1',
             config.indyEnabled ? 'INVOKEDYNAMIC cast(LC;)Z' : 'INVOKESTATIC 
org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox 
(Ljava/lang/Object;)Z'
         ])
     }

Reply via email to