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()
+ '''
+ }
}