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/01/31 18:00:20 UTC

[groovy] 01/05: GROOVY-5728: anon. inner can use private constructor when super is outer

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

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

commit 70c74d9b948dbea9238809277b47a35699c3889a
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Mar 20 10:51:07 2021 -0500

    GROOVY-5728: anon. inner can use private constructor when super is outer
    
    Conflicts:
    	src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
---
 .../classgen/InnerClassCompletionVisitor.java      | 76 +++++++++++++++-------
 .../stc/AnonymousInnerClassSTCTest.groovy          | 17 +++++
 2 files changed, 69 insertions(+), 24 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index 1e52435..445ed0a 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -42,7 +42,11 @@ import org.objectweb.asm.Opcodes;
 import java.util.List;
 
 import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedConstructor;
-import static org.codehaus.groovy.ast.ClassHelper.CLOSURE_TYPE;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
 
 public class InnerClassCompletionVisitor extends InnerClassVisitorHelper implements Opcodes {
 
@@ -51,8 +55,8 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
     private FieldNode thisField = null;
 
     private static final String
-            CLOSURE_INTERNAL_NAME   = BytecodeHelper.getClassInternalName(CLOSURE_TYPE),
-            CLOSURE_DESCRIPTOR      = BytecodeHelper.getTypeDescription(CLOSURE_TYPE);
+            CLOSURE_INTERNAL_NAME = BytecodeHelper.getClassInternalName(ClassHelper.CLOSURE_TYPE),
+            CLOSURE_DESCRIPTOR    = BytecodeHelper.getTypeDescription(ClassHelper.CLOSURE_TYPE);
 
     public InnerClassCompletionVisitor(CompilationUnit cu, SourceUnit su) {
         sourceUnit = su;
@@ -65,7 +69,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
 
     @Override
     public void visitClass(ClassNode node) {
-        this.classNode = node;
+        classNode = node;
         thisField = null;
         InnerClassNode innerClass = null;
         if (!node.isEnum() && !node.isInterface() && node instanceof InnerClassNode) {
@@ -88,6 +92,30 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
     public void visitConstructor(ConstructorNode node) {
         addThisReference(node);
         super.visitConstructor(node);
+        // an anonymous inner class may use a private constructor (via a bridge) if its super class is also an outer class
+        if (((InnerClassNode) classNode).isAnonymous() && classNode.getOuterClasses().contains(classNode.getSuperClass())) {
+            ConstructorNode superCtor = classNode.getSuperClass().getDeclaredConstructor(node.getParameters());
+            if (superCtor != null && superCtor.isPrivate()) {
+                ClassNode superClass = classNode.getUnresolvedSuperClass();
+                makeBridgeConstructor(superClass, node.getParameters()); // GROOVY-5728
+                ConstructorCallExpression superCtorCall = getFirstIfSpecialConstructorCall(node.getCode());
+                ((TupleExpression) superCtorCall.getArguments()).addExpression(castX(superClass, nullX()));
+            }
+        }
+    }
+
+    private static void makeBridgeConstructor(ClassNode c, Parameter[] p) {
+        Parameter[] newP = new Parameter[p.length + 1];
+        for (int i = 0; i < p.length; i += 1) {
+            newP[i] = new Parameter(p[i].getType(), "p" + i);
+        }
+        newP[p.length] = new Parameter(c, "$anonymous");
+
+        if (c.getDeclaredConstructor(newP) == null) {
+            TupleExpression args = new TupleExpression();
+            for (int i = 0; i < p.length; i += 1) args.addExpression(varX(newP[i]));
+            addGeneratedConstructor(c, ACC_SYNTHETIC, newP, ClassNode.EMPTY_ARRAY, stmt(ctorThisX(args)));
+        }
     }
 
     private static String getTypeDescriptor(ClassNode node, boolean isStatic) {
@@ -160,7 +188,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
 
     private void getThis(MethodVisitor mv, String classInternalName, String outerClassDescriptor, String innerClassInternalName) {
         mv.visitVarInsn(ALOAD, 0);
-        if (thisField != null && CLOSURE_TYPE.equals(thisField.getType())) {
+        if (thisField != null && ClassHelper.CLOSURE_TYPE.equals(thisField.getType())) {
             mv.visitFieldInsn(GETFIELD, classInternalName, "this$0", CLOSURE_DESCRIPTOR);
             mv.visitMethodInsn(INVOKEVIRTUAL, CLOSURE_INTERNAL_NAME, "getThisObject", "()Ljava/lang/Object;", false);
             mv.visitTypeInsn(CHECKCAST, innerClassInternalName);
@@ -168,18 +196,17 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
             mv.visitFieldInsn(GETFIELD, classInternalName, "this$0", outerClassDescriptor);
         }
     }
-    
-    private void addDefaultMethods(InnerClassNode node) {
-        final boolean isStatic = isStatic(node);
 
+    private void addDefaultMethods(InnerClassNode node) {
+        boolean isStatic = isStatic(node);
         ClassNode outerClass = node.getOuterClass();
-        final String classInternalName = org.codehaus.groovy.classgen.asm.BytecodeHelper.getClassInternalName(node);
+        final int objectDistance = getObjectDistance(outerClass);
+        final String classInternalName = BytecodeHelper.getClassInternalName(node);
         final String outerClassInternalName = getInternalName(outerClass, isStatic);
         final String outerClassDescriptor = getTypeDescriptor(outerClass, isStatic);
-        final int objectDistance = getObjectDistance(outerClass);
 
         // add missing method dispatcher
-        Parameter[] parameters = new Parameter[]{
+        Parameter[] parameters = {
                 new Parameter(ClassHelper.STRING_TYPE, "name"),
                 new Parameter(ClassHelper.OBJECT_TYPE, "args")
         };
@@ -446,19 +473,20 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
         return namePrefix;
     }
 
-    private static ConstructorCallExpression getFirstIfSpecialConstructorCall(BlockStatement code) {
-        if (code == null) return null;
-
-        final List<Statement> statementList = code.getStatements();
-        if (statementList.isEmpty()) return null;
-
-        final Statement statement = statementList.get(0);
-        if (!(statement instanceof ExpressionStatement)) return null;
-
-        Expression expression = ((ExpressionStatement) statement).getExpression();
-        if (!(expression instanceof ConstructorCallExpression)) return null;
-        ConstructorCallExpression cce = (ConstructorCallExpression) expression;
-        if (cce.isSpecialCall()) return cce;
+    private static ConstructorCallExpression getFirstIfSpecialConstructorCall(Statement code) {
+        if (code != null && !code.isEmpty()) {
+            if (code instanceof BlockStatement) {
+                Statement stmt = ((BlockStatement) code).getStatements().get(0);
+                return getFirstIfSpecialConstructorCall(stmt); // blocks of blocks
+            }
+            if (code instanceof ExpressionStatement) {
+                Expression expr = ((ExpressionStatement) code).getExpression();
+                if (expr instanceof ConstructorCallExpression) {
+                    ConstructorCallExpression call = (ConstructorCallExpression) expr;
+                    if (call.isSpecialCall()) return call;
+                }
+            }
+        }
         return null;
     }
 }
diff --git a/src/test/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy b/src/test/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy
index eb52a3a..ab1bb2e 100644
--- a/src/test/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy
+++ b/src/test/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy
@@ -206,4 +206,21 @@ class AnonymousInnerClassSTCTest extends StaticTypeCheckingTestCase {
             }
         '''
     }
+
+    // GROOVY-5728
+    void testPrivateConstructorAndPublicStaticFactory() {
+        assertScript '''
+            abstract class A {
+                private A() { }
+                abstract answer()
+                static A create() {
+                    return new A() { // IllegalAccessError when A$1 calls private constructor
+                        def answer() { 42 }
+                    }
+                }
+            }
+
+            assert A.create().answer() == 42
+        '''
+    }
 }