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 754d81986a GROOVY-11301, GROOVY-11365: SC: method reference to private 
or protected
754d81986a is described below

commit 754d81986ac36b9fc701f7ef621b5cb2da81285b
Author: Eric Milles <[email protected]>
AuthorDate: Wed Sep 18 12:39:07 2024 -0500

    GROOVY-11301, GROOVY-11365: SC: method reference to private or protected
    
    of outer or upper class (using access bridge)
---
 ...StaticTypesMethodReferenceExpressionWriter.java | 19 +++++++++++-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 35 ++++++++++-----------
 .../transform/stc/MethodReferenceTest.groovy       | 36 +++++++++++++++++-----
 3 files changed, 63 insertions(+), 27 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
index dee145e84b..fa7c4d8263 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
@@ -31,6 +31,7 @@ import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
 import org.codehaus.groovy.ast.expr.MethodReferenceExpression;
 import org.codehaus.groovy.ast.tools.GeneralUtils;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
 import org.codehaus.groovy.classgen.asm.BytecodeHelper;
 import org.codehaus.groovy.classgen.asm.MethodReferenceExpressionWriter;
 import org.codehaus.groovy.classgen.asm.WriterController;
@@ -111,12 +112,21 @@ public class StaticTypesMethodReferenceExpressionWriter 
extends MethodReferenceE
         } else {
             // TODO: move the findMethodRefMethod and checking to 
StaticTypeCheckingVisitor
             methodRefMethod = findMethodRefMethod(methodRefName, 
parametersWithExactType, typeOrTargetRef, typeOrTargetRefType);
+            if 
(methodReferenceExpression.getNodeMetaData(StaticTypesMarker.PV_METHODS_ACCESS) 
!= null) { // GROOVY-11301, GROOVY-11365: access bridge indicated
+                Map<MethodNode,MethodNode> bridgeMethods = 
typeOrTargetRefType.redirect().getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_BRIDGE_METHODS);
+                if (bridgeMethods != null) methodRefMethod = 
bridgeMethods.getOrDefault(methodRefMethod, methodRefMethod); // bridge may not 
have been generated
+            }
         }
 
         validate(methodReferenceExpression, typeOrTargetRefType, 
methodRefName, methodRefMethod, parametersWithExactType,
                 resolveClassNodeGenerics(extractPlaceholders(functionalType), 
null, abstractMethod.getReturnType()));
 
-        if (isExtensionMethod(methodRefMethod)) {
+        if (isBridgeMethod(methodRefMethod)) {
+            targetIsArgument = true; // GROOVY-11301, GROOVY-11365
+            if (isClassExpression) { // method expects an instance argument
+                methodRefMethod = addSyntheticMethodForDGSM(methodRefMethod);
+            }
+        } else if (isExtensionMethod(methodRefMethod)) {
             ExtensionMethodNode extensionMethodNode = (ExtensionMethodNode) 
methodRefMethod;
             methodRefMethod  = extensionMethodNode.getExtensionMethodNode();
             boolean isStatic = extensionMethodNode.isStaticExtension();
@@ -208,6 +218,8 @@ public class StaticTypesMethodReferenceExpressionWriter 
extends MethodReferenceE
             addFatalError(error, methodReference);
         } else if (methodNode.isVoidMethod() && 
!ClassHelper.isPrimitiveVoid(samReturnType)) {
             addFatalError("Invalid return type: void is not convertible to " + 
samReturnType.getText(), methodReference);
+        } else if 
(!AsmClassGenerator.isMemberDirectlyAccessible(methodNode.getModifiers(), 
methodNode.getDeclaringClass(), controller.getClassNode())) {
+            addFatalError("Cannot access method: " + methodName + " of class: 
" + methodNode.getDeclaringClass().getText(), methodReference); // GROOVY-11365
         } else if (samParameters.length > 0 && 
isTypeReferringInstanceMethod(methodReference.getExpression(), methodNode) && 
!isAssignableTo(samParameters[0].getType(), targetType)) {
             throw new RuntimeParserException("Invalid receiver type: " + 
samParameters[0].getType().getText() + " is not compatible with " + 
targetType.getText(), methodReference.getExpression());
         }
@@ -422,6 +434,11 @@ public class StaticTypesMethodReferenceExpressionWriter 
extends MethodReferenceE
 
     
//--------------------------------------------------------------------------
 
+    private static boolean isBridgeMethod(final MethodNode mn) {
+        int staticSynthetic = Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
+        return ((mn.getModifiers() & staticSynthetic) == staticSynthetic) && 
mn.getName().startsWith("access$");
+    }
+
     private static boolean isConstructorReference(final String name) {
         return "new".equals(name);
     }
diff --git 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 3a6d96d9f3..f2a9c0032c 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -591,14 +591,13 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
     }
 
     /**
-     * Checks for private field access from inner or outer class.
+     * Checks for private field access from closure or nestmate.
      */
     private void checkOrMarkPrivateAccess(final Expression source, final 
FieldNode fn, final boolean lhsOfAssignment) {
         if (fn != null && fn.isPrivate() && !fn.isSynthetic()) {
             ClassNode declaringClass = fn.getDeclaringClass();
             ClassNode enclosingClass = 
typeCheckingContext.getEnclosingClassNode();
-            if (declaringClass == enclosingClass && 
typeCheckingContext.getEnclosingClosure() == null) return;
-            if (declaringClass == enclosingClass || 
getOutermost(declaringClass) == getOutermost(enclosingClass)) {
+            if (declaringClass == enclosingClass ? 
typeCheckingContext.getEnclosingClosure() != null : 
getOutermost(declaringClass) == getOutermost(enclosingClass)) {
                 StaticTypesMarker accessKind = lhsOfAssignment ? 
PV_FIELDS_MUTATION : PV_FIELDS_ACCESS;
                 addPrivateFieldOrMethodAccess(source, declaringClass, 
accessKind, fn);
             }
@@ -606,29 +605,26 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
     }
 
     /**
-     * Checks for private method call from inner or outer class.
+     * Checks for private or protected method access from closure or nestmate.
      */
     private void checkOrMarkPrivateAccess(final Expression source, final 
MethodNode mn) {
         ClassNode declaringClass = mn.getDeclaringClass();
-        ClassNode enclosingClassNode = 
typeCheckingContext.getEnclosingClassNode();
-        if (declaringClass != enclosingClassNode || 
typeCheckingContext.getEnclosingClosure() != null) {
-            int mods = mn.getModifiers();
-            boolean sameModule = declaringClass.getModule() == 
enclosingClassNode.getModule();
-            String packageName = declaringClass.getPackageName();
-            if (packageName == null) {
-                packageName = "";
-            }
-            if (Modifier.isPrivate(mods) && sameModule) {
+        ClassNode enclosingClass = typeCheckingContext.getEnclosingClassNode();
+        if (declaringClass != enclosingClass || 
typeCheckingContext.getEnclosingClosure() != null) {
+            if (mn.isPrivate()
+                    && declaringClass.getModule() == 
enclosingClass.getModule()) {
                 addPrivateFieldOrMethodAccess(source, declaringClass, 
PV_METHODS_ACCESS, mn);
-            } else if (Modifier.isProtected(mods) && 
!packageName.equals(enclosingClassNode.getPackageName())
-                    && !implementsInterfaceOrIsSubclassOf(enclosingClassNode, 
declaringClass)) {
-                ClassNode cn = enclosingClassNode;
-                while ((cn = cn.getOuterClass()) != null) {
+            } else if (mn.isProtected()
+                    && !inSamePackage(enclosingClass, declaringClass)
+                    && (!implementsInterfaceOrIsSubclassOf(enclosingClass, 
declaringClass)
+                                    || 
typeCheckingContext.getEnclosingClosure() != null)) {
+                ClassNode cn = enclosingClass;
+                do {
                     if (implementsInterfaceOrIsSubclassOf(cn, declaringClass)) 
{
                         addPrivateFieldOrMethodAccess(source, cn, 
PV_METHODS_ACCESS, mn);
                         break;
                     }
-                }
+                } while ((cn = cn.getOuterClass()) != null);
             }
         }
     }
@@ -2651,7 +2647,8 @@ out:    if ((samParameterTypes.length == 1 && 
isOrImplements(samParameterTypes[0
 
                 ClassNode ownerType = receiverType;
                 candidates.stream()
-                        .map(candidate -> {
+                        .peek(candidate -> 
checkOrMarkPrivateAccess(expression, candidate)) // GROOVY-11365
+                        .map (candidate -> {
                             ClassNode returnType = candidate.getReturnType();
                             if (!candidate.isStatic() && 
GenericsUtils.hasUnresolvedGenerics(returnType)) {
                                 Map<GenericsTypeName, GenericsType> spec = new 
HashMap<>(); // GROOVY-11364
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy 
b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index cc2c8f61c6..8c1bdc7b64 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -1526,22 +1526,44 @@ final class MethodReferenceTest {
 
     @Test // GROOVY-11301
     void testInnerClassPrivateMethodReference() {
-        def script = '''
+        assertScript shell, '''
+            @CompileStatic
             class C {
                 static class D {
                     private static String m() { 'D' }
                 }
-                @CompileStatic
                 static main(args) {
                     Supplier<String> str = D::m
                     assert str.get() == 'D'
                 }
             }
         '''
-        if (Runtime.version().feature() < 15) {
-            shouldFail(shell, IllegalAccessError, script)
-        } else {
-            assertScript(shell, script)
-        }
+    }
+
+    @Test // GROOVY-11365
+    void testInnerClassProtectedMethodReference() {
+        assertScript shell, '''package p
+            abstract class A<E> {
+                protected E op(E e) { result = e }
+                protected E result
+            }
+
+            true
+        '''
+        assertScript shell, '''
+            @CompileStatic
+            class C extends p.A<Integer> {
+                void test() {
+                    def runnable = { ->
+                        Consumer<Integer> consumer = this::op
+                        consumer.accept(42) // IllegalAccessError
+                    }
+                    runnable.run()
+                    assert result == Integer.valueOf(42)
+                }
+            }
+
+            new C().test()
+        '''
     }
 }

Reply via email to