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

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


The following commit(s) were added to refs/heads/master by this push:
     new ae55de4  GROOVY-10424: SC: prevent infinite recursion for 
`isExtended(ClassNode)`
ae55de4 is described below

commit ae55de4f76c48d0189a1c9491b772e3aafaacc2f
Author: Eric Milles <[email protected]>
AuthorDate: Sat Dec 18 10:56:39 2021 -0600

    GROOVY-10424: SC: prevent infinite recursion for `isExtended(ClassNode)`
---
 .../transformers/BooleanExpressionTransformer.java | 150 ++++++++++-----------
 src/test/groovy/transform/stc/BugsSTCTest.groovy   |  36 +++++
 2 files changed, 106 insertions(+), 80 deletions(-)

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 82e8261..671a261 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,7 +21,6 @@ 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.expr.BinaryExpression;
 import org.codehaus.groovy.ast.expr.BooleanExpression;
@@ -32,7 +31,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;
@@ -41,9 +39,6 @@ import java.lang.reflect.Modifier;
 import java.util.Iterator;
 import java.util.List;
 
-import static org.codehaus.groovy.ast.ClassHelper.isObjectType;
-import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveBoolean;
-import static org.codehaus.groovy.ast.ClassHelper.isWrapperBoolean;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments;
 import static org.objectweb.asm.Opcodes.DUP;
 import static org.objectweb.asm.Opcodes.GOTO;
@@ -52,75 +47,61 @@ import static org.objectweb.asm.Opcodes.IFNONNULL;
 import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
 import static org.objectweb.asm.Opcodes.POP;
 
-public class BooleanExpressionTransformer {
-    private final StaticCompilationTransformer transformer;
+class BooleanExpressionTransformer {
 
-    public BooleanExpressionTransformer(StaticCompilationTransformer 
staticCompilationTransformer) {
-        transformer = staticCompilationTransformer;
+    private final StaticCompilationTransformer scTransformer;
+
+    BooleanExpressionTransformer(final StaticCompilationTransformer 
scTransformer) {
+        this.scTransformer = scTransformer;
     }
 
-    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;
+    Expression transformBooleanExpression(final BooleanExpression be) {
+        if (!(be instanceof NotExpression || be.getExpression() instanceof 
BinaryExpression)) {
+            ClassNode type = 
scTransformer.getTypeChooser().resolveType(be.getExpression(), 
scTransformer.getClassNode());
+            return optimizeBooleanExpression(be, type, scTransformer);
         }
-        return transformer.superTransform(booleanExpression);
+        return scTransformer.superTransform(be);
     }
 
-    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 (owner.getInnerClasses().hasNext()) {
-            return isExtended(owner, owner.getInnerClasses());
-        }
-        return false;
+    private static Expression optimizeBooleanExpression(final 
BooleanExpression be, final ClassNode targetType, final ExpressionTransformer 
transformer) {
+        Expression opt = new 
OptimizingBooleanExpression(transformer.transform(be.getExpression()), 
targetType);
+        opt.setSourcePosition(be);
+        opt.copyNodeMetaData(be);
+        return opt;
     }
 
+    
//--------------------------------------------------------------------------
+
     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;
+            return optimizeBooleanExpression(this, type, transformer);
         }
 
         @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 (isPrimitiveBoolean(type)) {
-                    expression.visit(visitor);
+                if (ClassHelper.isPrimitiveBoolean(type)) {
+                    getExpression().visit(visitor);
                     os.doGroovyCast(ClassHelper.boolean_TYPE);
                     return;
                 }
-                if (isWrapperBoolean(type)) {
-                    MethodVisitor mv = controller.getMethodVisitor();
-                    expression.visit(visitor);
+
+                if (ClassHelper.isWrapperBoolean(type)) {
+                    getExpression().visit(visitor);
                     Label unbox = new Label();
                     Label exit = new Label();
                     // check for null
@@ -130,57 +111,66 @@ 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();
+                    getExpression().visit(visitor);
+                    // in case of null-safe invocation, it is possible that 
what
+                    // was supposed to be a primitive type becomes "null" 
value,
+                    // so we need to recheck
+                    top = os.getTopOperand();
                     if (ClassHelper.isPrimitiveType(top)) {
-                        
BytecodeHelper.convertPrimitiveToBoolean(controller.getMethodVisitor(), top);
-                        
controller.getOperandStack().replace(ClassHelper.boolean_TYPE);
+                        BytecodeHelper.convertPrimitiveToBoolean(mv, top);
+                        os.replace(ClassHelper.boolean_TYPE);
                         return;
                     }
                 }
+
                 List<MethodNode> asBoolean = 
findDGMMethodsByNameAndArguments(controller.getSourceUnit().getClassLoader(), 
top, "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 (isObjectType(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 method = asBoolean.get(0);
+                    if (method instanceof ExtensionMethodNode) {
+                        ClassNode selfType = (((ExtensionMethodNode) 
method).getExtensionMethodNode()).getParameters()[0].getType();
+                        // 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), check that we are in one of these cases:
+                        // (a) a final class
+                        // (b) an effectively-final inner class
+                        if (ClassHelper.isObjectType(selfType) && 
(Modifier.isFinal(top.getModifiers()) || isEffectivelyFinal(top))) {
+                            Expression opt = new 
CompareToNullExpression(getExpression(), false);
+                            opt.visit(visitor);
+                            return;
                         }
                     }
                 }
-                super.visit(visitor);
-            } else {
-                super.visit(visitor);
             }
+
+            super.visit(visitor);
+        }
+
+        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;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/test/groovy/transform/stc/BugsSTCTest.groovy 
b/src/test/groovy/transform/stc/BugsSTCTest.groovy
index c405b65..488c279 100644
--- a/src/test/groovy/transform/stc/BugsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/BugsSTCTest.groovy
@@ -1101,4 +1101,40 @@ Printer
             assert n == 2
         '''
     }
+
+    // GROOVY-10424
+    void testPrivateInnerClassOptimizedBooleanExpr1() {
+        assertScript '''
+            class Outer {
+                private static class Inner {
+                    private Inner() {} // triggers creation of Inner$1 in 
StaticCompilationVisitor$addPrivateBridgeMethods
+                }
+                void test() {
+                    def inner = new Inner()
+                    if (inner) { // optimized boolean expression; 
StackOverflowError
+                        assert true
+                    }
+                }
+            }
+            new Outer().test()
+        '''
+    }
+
+    // GROOVY-10424
+    void testPrivateInnerClassOptimizedBooleanExpr2() {
+        assertScript '''
+            class Outer {
+                private static class Inner {
+                    static class Three {}
+                }
+                void test() {
+                    def inner = new Inner()
+                    if (inner) { // optimized boolean expression; 
StackOverflowError
+                        assert true
+                    }
+                }
+            }
+            new Outer().test()
+        '''
+    }
 }

Reply via email to