You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2023/10/03 10:32:36 UTC

[groovy] 05/07: GROOVY-8299: implement call to interface method using super with static compilation

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

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

commit cb33dc6c8d54b89f738baf95a48121273e269bad
Author: Jochen Theodorou <bl...@gmx.org>
AuthorDate: Mon Sep 25 21:38:01 2023 +0200

    GROOVY-8299: implement call to interface method using super with static compilation
---
 .../groovy/classgen/asm/InvocationWriter.java      | 23 ++++++++++++++++++++--
 .../classgen/asm/sc/StaticInvocationWriter.java    | 10 +++++++++-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 12 +++++++++++
 3 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
index ce89bc4092..ed66155ed7 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
@@ -157,7 +157,10 @@ public class InvocationWriter {
         if (!target.isStatic()) {
             if (receiver != null) {
                 Expression objectExpression = receiver;
-                if (implicitThis
+                if (!implicitThis && callSuperDefault(enclosingClass, target, receiver)) {
+                    compileStack.pushImplicitThis(true);
+                    objectExpression = new VariableExpression("this", declaringClass);
+                } else if (implicitThis
                         && enclosingClass.getOuterClass() != null
                         && !enclosingClass.isDerivedFrom(declaringClass)
                         && !enclosingClass.implementsInterface(declaringClass)) {
@@ -181,7 +184,7 @@ public class InvocationWriter {
         int opcode;
         if (target.isStatic()) {
             opcode = INVOKESTATIC;
-        } else if (isSuperExpression(receiver)) {
+        } else if (isSuperExpression(receiver) || isClassWithSuper(receiver)) {
             opcode = INVOKESPECIAL;
         } else if (declaringClass.isInterface()) {
             opcode = INVOKEINTERFACE;
@@ -227,6 +230,22 @@ public class InvocationWriter {
         return true;
     }
 
+    private boolean callSuperDefault(ClassNode enclosingClass, MethodNode target, Expression receiver) {
+        ClassNode declaringClass = target.getDeclaringClass();
+        if (declaringClass.isInterface() && enclosingClass.implementsInterface(declaringClass)) {
+            return isClassWithSuper(receiver);
+        }
+        return false;
+    }
+
+    private boolean isClassWithSuper(Expression exp) {
+        if (exp instanceof PropertyExpression) {
+            PropertyExpression pexp = (PropertyExpression) exp;
+            return pexp.getObjectExpression() instanceof ClassExpression && "super".equals(pexp.getPropertyAsString());
+        }
+        return false;
+    }
+
     /**
      * Supplements {@link org.apache.groovy.ast.tools.ExpressionUtils#isThisExpression isThisExpression}
      * with the ability to see into {@code CheckcastReceiverExpression}.
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
index 4631287a4d..0aa0e28738 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
@@ -381,7 +381,7 @@ public class StaticInvocationWriter extends InvocationWriter {
                 }
             }
         }
-        if (receiver != null && !isSuperExpression(receiver)) {
+        if (receiver != null && !isSuperExpression(receiver) && !isClassWithSuper(receiver)) {
             // in order to avoid calls to castToType, which is the dynamic behaviour, make sure that we call CHECKCAST instead then replace the top operand type
             if (currentCall.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER) == null) fixedReceiver = new CheckcastReceiverExpression(fixedReceiver, target);
             return super.writeDirectMethodCall(target, fixedImplicitThis, fixedReceiver, args);
@@ -389,6 +389,14 @@ public class StaticInvocationWriter extends InvocationWriter {
         return super.writeDirectMethodCall(target, implicitThis, receiver, args);
     }
 
+    private boolean isClassWithSuper(Expression receiver) {
+        if (receiver instanceof PropertyExpression) {
+            PropertyExpression pexp = (PropertyExpression) receiver;
+            return pexp.getObjectExpression() instanceof ClassExpression && "super".equals(pexp.getPropertyAsString());
+        }
+        return false;
+    }
+
     private boolean tryPrivateMethod(final MethodNode target, final boolean implicitThis, final Expression receiver, final TupleExpression args, final ClassNode classNode) {
         ClassNode declaringClass = target.getDeclaringClass();
         if ((isPrivateBridgeMethodsCallAllowed(declaringClass, classNode) || isPrivateBridgeMethodsCallAllowed(classNode, declaringClass))
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 0afd3f2765..f702ae6a15 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1533,6 +1533,18 @@ out:    if ((samParameterTypes.length == 1 && isOrImplements(samParameterTypes[0
             }
         }
 
+        if (staticOnlyAccess && "super".equals(propertyName)) {
+            // handle "I.super" for default interface logic
+            ClassNode enclosingType = typeCheckingContext.getEnclosingClassNode();
+            ClassNode accessor = objectExpressionType.getGenericsTypes()[0].getType();
+            if (accessor.isInterface() && enclosingType.implementsInterface(accessor)) {
+                storeType(pexp, accessor);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
         boolean foundGetterOrSetter = false;
         String capName = capitalize(propertyName);
         Set<ClassNode> handledNodes = new HashSet<>();