You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/07/29 22:17:43 UTC

[groovy] branch GROOVY_3_0_X updated: GROOVY-10618, GROOVY-10711: SC: `BooleanExpression` and `NotExpression`

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 a01446dbe2 GROOVY-10618, GROOVY-10711: SC: `BooleanExpression` and `NotExpression`
a01446dbe2 is described below

commit a01446dbe28824390d1bb6df1a6e54267644d719
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Jul 29 16:05:01 2022 -0500

    GROOVY-10618, GROOVY-10711: SC: `BooleanExpression` and `NotExpression`
    
    3_0_X backport
---
 .../groovy/ast/expr/BooleanExpression.java         |   7 +-
 .../codehaus/groovy/ast/expr/NotExpression.java    |   2 +
 .../transformers/BooleanExpressionTransformer.java | 175 +++++++------
 .../sc/transformers/CompareToNullExpression.java   |  52 ++--
 .../transformers/StaticCompilationTransformer.java |  32 +--
 ...StaticCompileNullCompareOptimizationTest.groovy | 291 ++++++++++++---------
 6 files changed, 306 insertions(+), 253 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/expr/BooleanExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/BooleanExpression.java
index bde832082c..744b485f2b 100644
--- a/src/main/java/org/codehaus/groovy/ast/expr/BooleanExpression.java
+++ b/src/main/java/org/codehaus/groovy/ast/expr/BooleanExpression.java
@@ -31,22 +31,25 @@ public class BooleanExpression extends Expression {
         this.expression = expression;
         setType(ClassHelper.boolean_TYPE); // for consistency with AsmClassGenerator. see AsmClassGenerator.visitBooleanExpression.
     }
-    
+
     public Expression getExpression() {
         return expression;
     }
 
+    @Override
     public void visit(GroovyCodeVisitor visitor) {
         visitor.visitBooleanExpression(this);
     }
 
+    @Override
     public Expression transformExpression(ExpressionTransformer transformer) {
         Expression ret = new BooleanExpression(transformer.transform(expression));
         ret.setSourcePosition(this);
         ret.copyNodeMetaData(this);
         return ret;
     }
-    
+
+    @Override
     public String getText() {
         return expression.getText();
     }
diff --git a/src/main/java/org/codehaus/groovy/ast/expr/NotExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/NotExpression.java
index f404765862..6f990e59da 100644
--- a/src/main/java/org/codehaus/groovy/ast/expr/NotExpression.java
+++ b/src/main/java/org/codehaus/groovy/ast/expr/NotExpression.java
@@ -26,6 +26,7 @@ public class NotExpression extends BooleanExpression {
         super(expression);
     }
 
+    @Override
     public void visit(GroovyCodeVisitor visitor) {
         visitor.visitNotExpression(this);
     }
@@ -34,6 +35,7 @@ public class NotExpression extends BooleanExpression {
         return false;
     }
 
+    @Override
     public Expression transformExpression(ExpressionTransformer transformer) {
         Expression ret = new NotExpression(transformer.transform(getExpression()));
         ret.setSourcePosition(this);
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 f15b75caf6..882a2dc86d 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
@@ -21,8 +21,8 @@ package org.codehaus.groovy.transform.sc.transformers;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.GroovyCodeVisitor;
-import org.codehaus.groovy.ast.InnerClassNode;
 import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.expr.BinaryExpression;
 import org.codehaus.groovy.ast.expr.BooleanExpression;
 import org.codehaus.groovy.ast.expr.Expression;
@@ -32,7 +32,6 @@ import org.codehaus.groovy.classgen.AsmClassGenerator;
 import org.codehaus.groovy.classgen.asm.BytecodeHelper;
 import org.codehaus.groovy.classgen.asm.OperandStack;
 import org.codehaus.groovy.classgen.asm.WriterController;
-import org.codehaus.groovy.classgen.asm.sc.StaticTypesTypeChooser;
 import org.codehaus.groovy.transform.stc.ExtensionMethodNode;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
@@ -49,73 +48,63 @@ import static org.objectweb.asm.Opcodes.IFNONNULL;
 import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
 import static org.objectweb.asm.Opcodes.POP;
 
-public class BooleanExpressionTransformer {
+class BooleanExpressionTransformer {
+
     private final StaticCompilationTransformer transformer;
 
-    public BooleanExpressionTransformer(StaticCompilationTransformer staticCompilationTransformer) {
-        transformer = staticCompilationTransformer;
+    BooleanExpressionTransformer(final StaticCompilationTransformer transformer) {
+        this.transformer = transformer;
     }
 
-    Expression transformBooleanExpression(final BooleanExpression booleanExpression) {
-        if (booleanExpression instanceof NotExpression) {
-            return transformer.superTransform(booleanExpression);
-        }
-        final Expression expression = booleanExpression.getExpression();
-        if (!(expression instanceof BinaryExpression)) {
-            StaticTypesTypeChooser typeChooser = transformer.getTypeChooser();
-            final ClassNode type = typeChooser.resolveType(expression, transformer.getClassNode());
-            BooleanExpression transformed = new OptimizingBooleanExpression(transformer.transform(expression),type);
-            transformed.setSourcePosition(booleanExpression);
-            transformed.copyNodeMetaData(booleanExpression);
-            return transformed;
-        }
-        return transformer.superTransform(booleanExpression);
-    }
+    Expression transformBooleanExpression(final BooleanExpression boolX) {
+        Expression expr = boolX;
+        boolean reverse = false;
+        do { // undo arbitrary nesting of (Boolean|Not)Expressions
+            if (expr instanceof NotExpression) reverse = !reverse;
+            expr = ((BooleanExpression) expr).getExpression();
+        } while (expr instanceof BooleanExpression);
 
-    private static boolean isExtended(ClassNode owner, Iterator<InnerClassNode> classes) {
-        while (classes.hasNext()) {
-            InnerClassNode next = classes.next();
-            if (next!=owner && next.isDerivedFrom(owner)) return true;
-            if (isExtended(owner,next.getInnerClasses())) return true;
+        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;
         }
-        return false;
+
+        return transformer.superTransform(boolX);
     }
 
+    //--------------------------------------------------------------------------
+
     private static class OptimizingBooleanExpression extends BooleanExpression {
 
-        private final Expression expression;
         private final ClassNode type;
 
-        public OptimizingBooleanExpression(final Expression expression, final ClassNode type) {
+        OptimizingBooleanExpression(final Expression expression, final ClassNode type) {
             super(expression);
-            this.expression = 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 ret = new OptimizingBooleanExpression(transformer.transform(expression), type);
-            ret.setSourcePosition(this);
-            ret.copyNodeMetaData(this);
-            return ret;
+            Expression opt = new OptimizingBooleanExpression(transformer.transform(getExpression()), type);
+            opt.setSourcePosition(this);
+            opt.copyNodeMetaData(this);
+            return opt;
         }
 
         @Override
         public void visit(final GroovyCodeVisitor visitor) {
             if (visitor instanceof AsmClassGenerator) {
-                AsmClassGenerator acg = (AsmClassGenerator) visitor;
-                WriterController controller = acg.getController();
+                WriterController controller = ((AsmClassGenerator) visitor).getController();
+                MethodVisitor mv = controller.getMethodVisitor();
                 OperandStack os = controller.getOperandStack();
 
-                if (type.equals(ClassHelper.boolean_TYPE)) {
-                    expression.visit(visitor);
-                    os.doGroovyCast(ClassHelper.boolean_TYPE);
-                    return;
-                }
-                if (type.equals(ClassHelper.Boolean_TYPE)) {
-                    MethodVisitor mv = controller.getMethodVisitor();
-                    expression.visit(visitor);
+                if (ClassHelper.Boolean_TYPE.equals(type)) {
+                    getExpression().visit(visitor);
                     Label unbox = new Label();
                     Label exit = new Label();
                     // check for null
@@ -125,57 +114,81 @@ public class BooleanExpressionTransformer {
                     mv.visitInsn(ICONST_0);
                     mv.visitJumpInsn(GOTO, exit);
                     mv.visitLabel(unbox);
-                    // unbox
-                    // GROOVY-6270
-                    if (!os.getTopOperand().equals(type)) BytecodeHelper.doCast(mv, type);
+                    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;
                 }
-                ClassNode top = type;
-                if (ClassHelper.isPrimitiveType(top)) {
-                    expression.visit(visitor);
-                    // 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
-                    top = controller.getOperandStack().getTopOperand();
-                    if (ClassHelper.isPrimitiveType(top)) {
-                        BytecodeHelper.convertPrimitiveToBoolean(controller.getMethodVisitor(), top);
-                        controller.getOperandStack().replace(ClassHelper.boolean_TYPE);
+
+                if (ClassHelper.isPrimitiveType(type)) {
+                    getExpression().visit(visitor);
+                    if (ClassHelper.boolean_TYPE.equals(type)) {
+                        os.doGroovyCast(ClassHelper.boolean_TYPE);
                         return;
+                    } 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;
+                        }
                     }
                 }
-                List<MethodNode> asBoolean = findDGMMethodsByNameAndArguments(controller.getSourceUnit().getClassLoader(), top, "asBoolean", ClassNode.EMPTY_ARRAY);
+
+                if (replaceAsBooleanWithCompareToNull(type, controller.getSourceUnit().getClassLoader())) {
+                    new CompareToNullExpression(getExpression(), false).visit(visitor);
+                    return;
+                }
+            }
+
+            super.visit(visitor);
+        }
+
+        /**
+         * Inline an "expr != null" check instead of boolean conversion iff:
+         * (1) the class doesn't define an {@code asBoolean()} method
+         * (2) no subclass defines an {@code asBoolean()} method
+         * For (2), check that we are in one of these cases:
+         *   (a) a final class
+         *   (b) an effectively-final inner class
+         */
+        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)) {
+                List<MethodNode> asBoolean = findDGMMethodsByNameAndArguments(dgmProvider, type, "asBoolean", ClassNode.EMPTY_ARRAY);
                 if (asBoolean.size() == 1) {
-                    MethodNode node = asBoolean.get(0);
-                    if (node instanceof ExtensionMethodNode) {
-                        MethodNode dgmNode = ((ExtensionMethodNode) node).getExtensionMethodNode();
-                        ClassNode owner = dgmNode.getParameters()[0].getType();
-                        if (ClassHelper.OBJECT_TYPE.equals(owner)) {
-                            // we may inline a var!=null check instead of calling a helper method iff
-                            // (1) the class doesn't define an asBoolean method (already tested)
-                            // (2) no subclass defines an asBoolean method
-                            // For (2), we check that we are in one of those cases
-                            // (a) a final class
-                            // (b) a private inner class without subclass
-                            if (Modifier.isFinal(top.getModifiers())
-                                    || (top instanceof InnerClassNode
-                                    && Modifier.isPrivate(top.getModifiers())
-                                    && !isExtended(top, top.getOuterClass().getInnerClasses()))
-                                    ) {
-                                CompareToNullExpression expr = new CompareToNullExpression(
-                                        expression, false
-                                );
-                                expr.visit(acg);
-                                return;
-                            }
+                    MethodNode theAsBoolean = asBoolean.get(0);
+                    if (theAsBoolean instanceof ExtensionMethodNode) {
+                        ClassNode selfType = (((ExtensionMethodNode) theAsBoolean).getExtensionMethodNode()).getParameters()[0].getType();
+                        if (ClassHelper.OBJECT_TYPE.equals(selfType)) {
+                            return true;
                         }
                     }
                 }
-                super.visit(visitor);
-            } else {
-                super.visit(visitor);
             }
+            return false;
+        }
+
+        private static boolean isEffectivelyFinal(final ClassNode type) {
+            if (!Modifier.isPrivate(type.getModifiers())) return false;
+
+            List<ClassNode> outers = type.getOuterClasses();
+            ClassNode outer = outers.get(outers.size() - 1);
+            return !isExtended(type, outer.getInnerClasses());
+        }
+
+        private static boolean isExtended(final ClassNode type, final Iterator<? extends ClassNode> inners) {
+            while (inners.hasNext()) { ClassNode next = inners.next();
+                if (next != type && next.isDerivedFrom(type))
+                    return true;
+                if (isExtended(type, next.getInnerClasses()))
+                    return true;
+            }
+            return false;
         }
     }
 }
diff --git a/src/main/java/org/codehaus/groovy/transform/sc/transformers/CompareToNullExpression.java b/src/main/java/org/codehaus/groovy/transform/sc/transformers/CompareToNullExpression.java
index 8651154dfa..36e6436752 100644
--- a/src/main/java/org/codehaus/groovy/transform/sc/transformers/CompareToNullExpression.java
+++ b/src/main/java/org/codehaus/groovy/transform/sc/transformers/CompareToNullExpression.java
@@ -29,9 +29,14 @@ import org.codehaus.groovy.classgen.asm.WriterController;
 import org.codehaus.groovy.syntax.Token;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
 
-public class CompareToNullExpression extends BinaryExpression implements Opcodes {
+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.IFNULL;
+
+public class CompareToNullExpression extends BinaryExpression {
     private final boolean equalsNull;
 
     public CompareToNullExpression(final Expression expression, final boolean equalsNull) {
@@ -69,32 +74,31 @@ public class CompareToNullExpression extends BinaryExpression implements Opcodes
 
     @Override
     public void visit(final GroovyCodeVisitor visitor) {
-        if (visitor instanceof AsmClassGenerator) {
-            AsmClassGenerator acg = (AsmClassGenerator) visitor;
-            WriterController controller = acg.getController();
-            MethodVisitor mv = controller.getMethodVisitor();
-
-            getObjectExpression().visit(acg);
+        if (!(visitor instanceof AsmClassGenerator)) {
+            super.visit(visitor);
+            return;
+        }
 
-            if (ClassHelper.isPrimitiveType(controller.getOperandStack().getTopOperand())) {
-                controller.getOperandStack().pop();
-                mv.visitInsn(equalsNull ? ICONST_0 : ICONST_1);
+        WriterController controller = ((AsmClassGenerator) visitor).getController();
+        MethodVisitor mv = controller.getMethodVisitor();
 
-                controller.getOperandStack().push(ClassHelper.boolean_TYPE);
-            } else {
-                Label zero = new Label();
-                mv.visitJumpInsn(equalsNull ? IFNONNULL : IFNULL, zero);
-                mv.visitInsn(ICONST_1);
-                Label end = new Label();
-                mv.visitJumpInsn(GOTO, end);
-                mv.visitLabel(zero);
-                mv.visitInsn(ICONST_0);
-                mv.visitLabel(end);
+        getObjectExpression().visit(visitor);
 
-                controller.getOperandStack().replace(ClassHelper.boolean_TYPE);
-            }
+        if (ClassHelper.isPrimitiveType(controller.getOperandStack().getTopOperand())) {
+            controller.getOperandStack().pop();
+            mv.visitInsn(equalsNull ? ICONST_0 : ICONST_1);
+            controller.getOperandStack().push(ClassHelper.boolean_TYPE);
         } else {
-            super.visit(visitor);
+            Label no = new Label(), yes = new Label();
+
+            mv.visitJumpInsn(equalsNull ? IFNONNULL : IFNULL, no);
+            mv.visitInsn(ICONST_1);
+            mv.visitJumpInsn(GOTO, yes);
+            mv.visitLabel(no);
+            mv.visitInsn(ICONST_0);
+            mv.visitLabel(yes);
+
+            controller.getOperandStack().replace(ClassHelper.boolean_TYPE);
         }
     }
 }
diff --git a/src/main/java/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java b/src/main/java/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java
index e2a5a7902e..b51ca7cd7b 100644
--- a/src/main/java/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java
+++ b/src/main/java/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java
@@ -71,12 +71,12 @@ public class StaticCompilationTransformer extends ClassCodeExpressionTransformer
 
     // various helpers in order to avoid a potential very big class
     private final StaticMethodCallExpressionTransformer staticMethodCallExpressionTransformer = new StaticMethodCallExpressionTransformer(this);
-    private final ConstructorCallTransformer constructorCallTransformer = new ConstructorCallTransformer(this);
     private final MethodCallExpressionTransformer methodCallExpressionTransformer = new MethodCallExpressionTransformer(this);
-    private final BinaryExpressionTransformer binaryExpressionTransformer = new BinaryExpressionTransformer(this);
+    private final ConstructorCallTransformer constructorCallTransformer = new ConstructorCallTransformer(this);
+    private final VariableExpressionTransformer variableExpressionTransformer = new VariableExpressionTransformer();
     private final ClosureExpressionTransformer closureExpressionTransformer = new ClosureExpressionTransformer(this);
     private final BooleanExpressionTransformer booleanExpressionTransformer = new BooleanExpressionTransformer(this);
-    private final VariableExpressionTransformer variableExpressionTransformer = new VariableExpressionTransformer();
+    private final BinaryExpressionTransformer binaryExpressionTransformer = new BinaryExpressionTransformer(this);
     private final RangeExpressionTransformer rangeExpressionTransformer = new RangeExpressionTransformer(this);
     private final ListExpressionTransformer listExpressionTransformer = new ListExpressionTransformer(this);
     private final CastExpressionOptimizer castExpressionTransformer = new CastExpressionOptimizer(this);
@@ -105,36 +105,36 @@ public class StaticCompilationTransformer extends ClassCodeExpressionTransformer
     }
 
     @Override
-    public Expression transform(Expression expr) {
+    public Expression transform(final Expression expr) {
         if (expr instanceof StaticMethodCallExpression) {
             return staticMethodCallExpressionTransformer.transformStaticMethodCallExpression((StaticMethodCallExpression) expr);
         }
-        if (expr instanceof BinaryExpression) {
-            return binaryExpressionTransformer.transformBinaryExpression((BinaryExpression)expr);
-        }
         if (expr instanceof MethodCallExpression) {
             return methodCallExpressionTransformer.transformMethodCallExpression((MethodCallExpression) expr);
         }
-        if (expr instanceof ClosureExpression) {
-            return closureExpressionTransformer.transformClosureExpression((ClosureExpression) expr);
-        }
         if (expr instanceof ConstructorCallExpression) {
             return constructorCallTransformer.transformConstructorCall((ConstructorCallExpression) expr);
         }
+        if (expr instanceof VariableExpression) {
+            return variableExpressionTransformer.transformVariableExpression((VariableExpression) expr);
+        }
+        if (expr instanceof ClosureExpression) {
+            return closureExpressionTransformer.transformClosureExpression((ClosureExpression) expr);
+        }
         if (expr instanceof BooleanExpression) {
-            return booleanExpressionTransformer.transformBooleanExpression((BooleanExpression)expr);
+            return booleanExpressionTransformer.transformBooleanExpression((BooleanExpression) expr);
         }
-        if (expr instanceof VariableExpression) {
-            return variableExpressionTransformer.transformVariableExpression((VariableExpression)expr);
+        if (expr instanceof BinaryExpression) {
+            return binaryExpressionTransformer.transformBinaryExpression((BinaryExpression) expr);
         }
         if (expr instanceof RangeExpression) {
-            return rangeExpressionTransformer.transformRangeExpression(((RangeExpression)expr));
+            return rangeExpressionTransformer.transformRangeExpression((RangeExpression) expr);
         }
         if (expr instanceof ListExpression) {
             return listExpressionTransformer.transformListExpression((ListExpression) expr);
         }
         if (expr instanceof CastExpression) {
-            return castExpressionTransformer.transformCastExpression(((CastExpression)expr));
+            return castExpressionTransformer.transformCastExpression((CastExpression) expr);
         }
         return super.transform(expr);
     }
@@ -142,7 +142,7 @@ public class StaticCompilationTransformer extends ClassCodeExpressionTransformer
     /**
      * Called by helpers when super.transform() is needed.
      */
-    final Expression superTransform(Expression expr) {
+    final Expression superTransform(final Expression expr) {
         return super.transform(expr);
     }
 
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 3adf4760ea..3e2e54c758 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileNullCompareOptimizationTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileNullCompareOptimizationTest.groovy
@@ -19,71 +19,62 @@
 package org.codehaus.groovy.classgen.asm.sc
 
 import org.codehaus.groovy.classgen.asm.AbstractBytecodeTestCase
+
 import static org.codehaus.groovy.control.CompilerConfiguration.DEFAULT as config
 
 /**
  * Unit tests for static compilation: null test optimizations.
  */
-class StaticCompileNullCompareOptimizationTest extends AbstractBytecodeTestCase {
-    void testShouldUseIfNonNull() {
+final class StaticCompileNullCompareOptimizationTest extends AbstractBytecodeTestCase {
+
+    void testShouldUseIfNull1() {
         def bytecode = compile(method:'m', '''
             @groovy.transform.CompileStatic
             void m(Object o) {
-                o == null
+                o != null
             }
         ''')
-        assert bytecode.hasStrictSequence([
-                'IFNONNULL'
-        ])
+        assert bytecode.hasStrictSequence(['IFNULL'])
     }
-    void testShouldUseIfNull() {
+
+    void testShouldUseIfNull2() {
         def bytecode = compile(method:'m', '''
             @groovy.transform.CompileStatic
             void m(Object o) {
-                o != null
+                null != o
             }
         ''')
-        assert bytecode.hasStrictSequence([
-                'IFNULL'
-        ])
+        assert bytecode.hasStrictSequence(['IFNULL'])
     }
 
-    void testShouldUseIfNonNull2() {
+    void testShouldUseIfNonNull1() {
         def bytecode = compile(method:'m', '''
             @groovy.transform.CompileStatic
             void m(Object o) {
-                null == o
+                o == null
             }
         ''')
-        assert bytecode.hasStrictSequence([
-                'IFNONNULL'
-        ])
+        assert bytecode.hasStrictSequence(['IFNONNULL'])
     }
 
-    void testShouldUseIfNull2() {
+    void testShouldUseIfNonNull2() {
         def bytecode = compile(method:'m', '''
             @groovy.transform.CompileStatic
             void m(Object o) {
-                null != o
+                null == o
             }
         ''')
-        assert bytecode.hasStrictSequence([
-                'IFNULL'
-        ])
+        assert bytecode.hasStrictSequence(['IFNONNULL'])
     }
 
-    void testPrimitiveWithNullShouldBeOptimized() {
+    void testPrimitiveWithNullShouldBeOptimized1() {
         def bytecode = compile(method:'m', '''
             @groovy.transform.CompileStatic
             void m(int x) {
                 null == x
             }
         ''')
-        assert bytecode.hasStrictSequence([
-                'ICONST_0',
-                'POP'
-        ])
-
+        assert bytecode.hasStrictSequence(['ICONST_0', 'POP'])
     }
 
     void testPrimitiveWithNullShouldBeOptimized2() {
@@ -93,185 +84,225 @@ class StaticCompileNullCompareOptimizationTest extends AbstractBytecodeTestCase
                 x == null
             }
         ''')
-        assert bytecode.hasStrictSequence([
-                'ICONST_0',
-                'POP'
+        assert bytecode.hasStrictSequence(['ICONST_0', 'POP'])
+    }
+
+    void testOptimizeGroovyTruthForPrimitiveBoolean1() {
+        def bytecode = compile(method:'m', '''
+            @groovy.transform.CompileStatic
+            void m(boolean x) {
+                if (x) {
+                }
+            }
+        ''')
+        assert bytecode.hasSequence([
+            'ILOAD 1',
+            'IFEQ L1',
+            'L1',
+            'RETURN'
         ])
     }
 
+    void testOptimizeGroovyTruthForPrimitiveBoolean2() {
+        def bytecode = compile(method:'m', '''
+            @groovy.transform.CompileStatic
+            void m(boolean x) {
+                if (!x) {
+                }
+            }
+        ''')
+        assert bytecode.hasSequence([
+            'ILOAD 1',
+            'IFNE L1',
+            'ICONST_1',
+            'GOTO L2',
+            'L1',
+            'ICONST_0',
+            'L2',
+            'IFEQ L3',
+            'L3',
+            'RETURN'
+        ])
+    }
 
-    void testOptimizeGroovyTruthForPrimitiveBoolean() {
+    void testOptimizeGroovyTruthForPrimitiveBoolean3() {
         def bytecode = compile(method:'m', '''
             @groovy.transform.CompileStatic
             void m(boolean x) {
-                if (x) {
-                    println 'ok'
+                if (!!x) {
                 }
             }
         ''')
-        assert bytecode.hasStrictSequence(['ILOAD 1', 'IFEQ'])
+        assert bytecode.hasSequence([
+            'ILOAD 1',
+            'IFEQ L1',
+            'L1',
+            'RETURN'
+        ])
     }
 
-    void testOptimizeGroovyTruthForBoxedBoolean() {
+    void testOptimizeGroovyTruthForNonPrimitiveBoolean() {
         def bytecode = compile(method:'m', '''
             @groovy.transform.CompileStatic
             void m(Boolean x) {
                 if (x) {
-                    println 'ok'
                 }
             }
         ''')
-        if (config.indyEnabled) {
-            return
-        }
-        assert  bytecode.hasStrictSequence(['ALOAD 1', 'DUP', 'IFNONNULL', 'POP', 'ICONST_0', 'GOTO', 'L1',                'INVOKEVIRTUAL', 'L2',                'IFEQ']) ||
-                bytecode.hasStrictSequence(['ALOAD 1', 'DUP', 'IFNONNULL', 'POP', 'ICONST_0', 'GOTO', 'L1', 'FRAME SAME1', 'INVOKEVIRTUAL', 'L2', 'FRAME SAME1', 'IFEQ']) // bytecode with stack map frame
+        assert bytecode.hasSequence([
+            'ALOAD 1',
+            'DUP',
+            'IFNONNULL',
+            'POP',
+            'ICONST_0',
+            'GOTO',
+            'L1',
+            'INVOKEVIRTUAL',
+            'L2',
+            'IFEQ'
+        ])
     }
 
-    void testOptimizeGroovyTruthWithStringShouldNotBeTriggered() {
+    void testOptimizeGroovyTruthForPrimitiveNumberType() {
         def bytecode = compile(method:'m', '''
             @groovy.transform.CompileStatic
-            void m(String x) {
+            void m(int x) {
                 if (x) {
-                    println 'ok'
                 }
             }
         ''')
-        if (config.indyEnabled) {
-            assert bytecode.hasStrictSequence([
-                'ALOAD 1',
-                'INVOKEDYNAMIC cast(Ljava/lang/String;)Z',
-                '',
-                '',
-                '',
-                '',
-                '',
-                ']',
-                'IFEQ'
-            ])
-        } else {
-            assert bytecode.hasStrictSequence([
-                    'ALOAD 1',
-                    'INVOKESTATIC org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox (Ljava/lang/Object;)Z',
-                    'IFEQ'
-            ])
-        }
+        assert bytecode.hasSequence([
+            'ILOAD 1',
+            'IFEQ L1',
+            'ICONST_1',
+            'GOTO L2',
+            'L1',
+            'ICONST_0',
+            'L2',
+            'IFEQ L3',
+            'L3',
+            'RETURN'
+        ])
     }
 
-    void testGroovyTruthOptimizationWithObjectShouldNotBeTriggered() {
+    void testNoGroovyTruthOptimizationForObject() {
         def bytecode = compile(method:'m', '''
             @groovy.transform.CompileStatic
             void m(Object x) {
                 if (x) {
-                    println 'ok'
                 }
             }
         ''')
-        if (config.indyEnabled) {
-            assert bytecode.hasStrictSequence([
-                'ALOAD 1',
-                'INVOKEDYNAMIC cast(Ljava/lang/Object;)Z',
-                '',
-                '',
-                '',
-                '',
-                '',
-                ']',
-                'IFEQ'
-            ])
-        } else {
-            assert bytecode.hasStrictSequence([
-                    'ALOAD 1',
-                    'INVOKESTATIC org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox (Ljava/lang/Object;)Z',
-                    'IFEQ'
-            ])
-        }
+        assert bytecode.hasSequence([
+            'ALOAD 1',
+            config.indyEnabled ? 'INVOKEDYNAMIC cast(Ljava/lang/Object;)Z' : 'INVOKESTATIC org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox (Ljava/lang/Object;)Z'
+        ])
     }
 
-    void testGroovyTruthOptimizationWithFinalClass() {
+    void testGroovyTruthOptimizationForFinalClass() {
         def bytecode = compile(method:'m', '''
-            final class A {}
+            final class A {
+            }
             @groovy.transform.CompileStatic
             void m(A x) {
                 if (x) {
-                    println 'ok'
                 }
             }
         ''')
-        assert bytecode.hasStrictSequence([
-                'ALOAD 1',
-                'IFNULL',
+        assert bytecode.hasSequence([
+            'ALOAD 1',
+            'IFNULL L1',
+            'ICONST_1',
+            'GOTO L2',
+            'L1',
+            'ICONST_0',
+            'L2',
+            'IFEQ L3'
         ])
+        if (config.indyEnabled)
+            assert !bytecode.hasSequence(['INVOKEDYNAMIC cast(LA$B;)Z'])
     }
 
-    void testGroovyTruthOptimizationWithPrivateInnerClass() {
+    void testGroovyTruthOptimizationForPrivateInnerClass() {
         def bytecode = compile(method:'m', '''
             class A {
-                private static class B {}
+                private static class B {
+                }
                 @groovy.transform.CompileStatic
                 void m(B x) {
                     if (x) {
-                        println 'ok'
                     }
                 }
             }
         ''')
-        assert bytecode.hasStrictSequence([
-                'ALOAD 1',
-                'IFNULL',
+        assert bytecode.hasSequence([
+            'ALOAD 1',
+            'IFNULL L1',
+            'ICONST_1',
+            'GOTO L2',
+            'L1',
+            'ICONST_0',
+            'L2',
+            'IFEQ L3'
         ])
+        if (config.indyEnabled)
+            assert !bytecode.hasSequence(['INVOKEDYNAMIC cast(LA$B;)Z'])
     }
 
-    void testGroovyTruthOptimizationWithPublicInnerClass() {
+    void testNoGroovyTruthOptimizationForPublicInnerClass() {
         def bytecode = compile(method:'m', '''
             class A {
-                public static class B {}
+                public static class B {
+                }
                 @groovy.transform.CompileStatic
                 void m(B x) {
                     if (x) {
-                        println 'ok'
                     }
                 }
             }
         ''')
-        if (config.indyEnabled) {
-            assert bytecode.hasStrictSequence([
-                'ALOAD 1',
-                'INVOKEDYNAMIC cast(LA$B;)Z',
-                '',
-                '',
-                '',
-                '',
-                '',
-                ']',
-                'IFEQ'
-            ])
-        } else {
-            assert bytecode.hasStrictSequence([
-                    'ALOAD 1',
-                    'INVOKESTATIC org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox (Ljava/lang/Object;)Z',
-                    'IFEQ'
-            ])
-        }
+        assert bytecode.hasSequence([
+            'ALOAD 1',
+            config.indyEnabled ? 'INVOKEDYNAMIC cast(LA$B;)Z' : 'INVOKESTATIC org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox (Ljava/lang/Object;)Z'
+        ])
     }
 
-    void testCompare() {
-        def bytecode=compile(method:'stat', '''
-            class Doc {}
-
+    // GROOVY-10711
+    void testNoGroovyTruthOptimizationIfProvidesAsBoolean() {
+        def bytecode = compile(method:'m', '''
             @groovy.transform.CompileStatic
-            class A {
-                static void foo() {
-                    Doc doc = null
-                    def cl = { if (doc) { 1 } else { 0 } }
-                    assert cl() == 0
+            @groovy.transform.Immutable
+            class C {
+                boolean asBoolean() {
                 }
             }
 
-            A.foo()
-
+            @groovy.transform.CompileStatic
+            void m(C x) {
+                if (!x) {
+                }
+            }
         ''')
-        clazz.main()
+        assert bytecode.hasSequence([
+            'ALOAD 1',
+            config.indyEnabled ? 'INVOKEDYNAMIC cast(LC;)Z' : 'INVOKESTATIC org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox (Ljava/lang/Object;)Z'
+        ])
     }
 
+    void testCompare() {
+        assertScript '''
+            class Pogo {
+            }
+            @groovy.transform.CompileStatic
+            class C {
+                static test() {
+                    Pogo pogo = null
+                    def check = { -> if (pogo) { 1 } else { 0 } }
+                    assert check() == 0
+                }
+            }
+
+            C.test()
+        '''
+    }
 }