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 2020/08/08 13:40:36 UTC

[groovy] branch master updated: GROOVY-9255: transform "TraitType.super.name" to getName()/isName() call

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 ffb8322  GROOVY-9255: transform "TraitType.super.name" to getName()/isName() call
ffb8322 is described below

commit ffb83225b5be7c7f567b6fed91b23612b2bcc185
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Aug 8 08:40:27 2020 -0500

    GROOVY-9255: transform "TraitType.super.name" to getName()/isName() call
    
    "TraitType.super.name = ..." was already handled; binary expression is
    transformed before subbing "TraitType$Helper.setName(this, ...)", so be
    careful not to transform LHS (assign target) to accessor call.
    
    closes #1340
---
 .../java/org/codehaus/groovy/syntax/Types.java     |  3 +
 .../transform/trait/SuperCallTraitTransformer.java | 55 +++++++++++++
 .../traitx/TraitASTTransformationTest.groovy       | 94 ++++++++++++++++++++++
 3 files changed, 152 insertions(+)

diff --git a/src/main/java/org/codehaus/groovy/syntax/Types.java b/src/main/java/org/codehaus/groovy/syntax/Types.java
index 0464dc6..4cf90c4 100644
--- a/src/main/java/org/codehaus/groovy/syntax/Types.java
+++ b/src/main/java/org/codehaus/groovy/syntax/Types.java
@@ -368,6 +368,9 @@ public class Types {
     //---------------------------------------------------------------------------
     // TYPE HIERARCHIES
 
+    /**
+     * @since 3.0.0
+     */
     public static boolean isAssignment(int type) {
         return ofType(type, ASSIGNMENT_OPERATOR);
     }
diff --git a/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java b/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
index 4eccccb..ba57ecc 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
@@ -36,6 +36,8 @@ import org.codehaus.groovy.ast.expr.VariableExpression;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.syntax.Types;
 
+import java.util.function.Function;
+
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 import static org.objectweb.asm.Opcodes.ACC_STATIC;
@@ -66,6 +68,9 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
         if (exp instanceof BinaryExpression) {
             return transformBinaryExpression((BinaryExpression) exp);
         }
+        if (exp instanceof PropertyExpression) {
+            return transformPropertyExpression((PropertyExpression) exp);
+        }
         if (exp instanceof MethodCallExpression) {
             return transformMethodCallExpression((MethodCallExpression) exp);
         }
@@ -73,6 +78,10 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
     }
 
     private Expression transformBinaryExpression(final BinaryExpression exp) {
+        if (Types.isAssignment(exp.getOperation().getType()))
+            // prevent transform of assignment target to accessor method call
+            exp.getLeftExpression().putNodeMetaData("assign.target", exp.getOperation());
+
         Expression trn = super.transform(exp);
         if (trn instanceof BinaryExpression) {
             BinaryExpression bin = (BinaryExpression) trn;
@@ -109,6 +118,52 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
         return trn;
     }
 
+    private Expression transformPropertyExpression(final PropertyExpression exp) {
+        if (exp.getNodeMetaData("assign.target") == null) {
+            ClassNode traitType = getTraitSuperTarget(exp.getObjectExpression());
+            if (traitType != null) {
+                ClassNode helperType = getHelper(traitType);
+                // TraitType.super.foo -> TraitType$Helper.getFoo(this)
+
+                Function<MethodNode, MethodCallExpression> xform = (methodNode) -> {
+                    MethodCallExpression methodCall = new MethodCallExpression(
+                            new ClassExpression(helperType),
+                            methodNode.getName(),
+                            new ArgumentListExpression(
+                                    new VariableExpression("this")
+                            )
+                    );
+                    methodCall.getObjectExpression().setSourcePosition(((PropertyExpression) exp.getObjectExpression()).getObjectExpression());
+                    methodCall.getMethod().setSourcePosition(exp.getProperty());
+                    methodCall.setSpreadSafe(exp.isSpreadSafe());
+                    methodCall.setMethodTarget(methodNode);
+                    methodCall.setImplicitThis(false);
+                    return methodCall;
+                };
+
+                String getterName = MetaProperty.getGetterName(exp.getPropertyAsString(), null);
+                for (MethodNode method : helperType.getMethods(getterName)) {
+                    if (method.isStatic() && method.getParameters().length == 1
+                            && method.getParameters()[0].getType().equals(traitType)
+                            && !method.getReturnType().equals(ClassHelper.VOID_TYPE)) {
+                        return xform.apply(method);
+                    }
+                }
+
+                String isserName = "is" + getterName.substring(3);
+                for (MethodNode method : helperType.getMethods(isserName)) {
+                    if (method.isStatic() && method.getParameters().length == 1
+                            && method.getParameters()[0].getType().equals(traitType)
+                            && method.getReturnType().equals(ClassHelper.boolean_TYPE)) {
+                        return xform.apply(method);
+                    }
+                }
+            }
+        }
+        exp.removeNodeMetaData("assign.target");
+        return super.transform(exp);
+    }
+
     private Expression transformMethodCallExpression(final MethodCallExpression exp) {
         ClassNode traitType = getTraitSuperTarget(exp.getObjectExpression());
         if (traitType != null) {
diff --git a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
index 81d6fd0..5a57531 100644
--- a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
@@ -1014,6 +1014,100 @@ final class TraitASTTransformationTest {
         '''
     }
 
+    @Test // GROOVY-9255
+    void testTraitSuperPropertyGet() {
+        assertScript '''
+            trait T {
+              def x = 'value'
+            }
+            class C implements T {
+              def test() {
+                T.super.x
+              }
+            }
+            assert new C().test() == 'value'
+        '''
+
+        assertScript '''
+            trait T {
+              boolean x = true
+            }
+            class C implements T {
+              def test() {
+                T.super.x
+              }
+            }
+            assert new C().test() == true
+        '''
+
+        assertScript '''
+            trait T {
+              def getX() { 'value' }
+            }
+            class C implements T {
+              def test() {
+                T.super.x
+              }
+            }
+            assert new C().test() == 'value'
+        '''
+
+        assertScript '''
+            trait T {
+              boolean isX() { true }
+            }
+            class C implements T {
+              def test() {
+                T.super.x
+              }
+            }
+            assert new C().test() == true
+        '''
+    }
+
+    @Test
+    void testTraitSuperPropertySet() {
+        assertScript '''
+            trait T {
+              def x
+            }
+            class C implements T {
+              def test() {
+                T.super.x = 'value'
+                return x
+              }
+            }
+            assert new C().test() == 'value'
+        '''
+
+        // TODO: add support for compound assignment
+        shouldFail MissingPropertyException, '''
+            trait T {
+              def x = 'value'
+            }
+            class C implements T {
+              def test() {
+                T.super.x -= ~/e\b/
+                T.super.x += 'able'
+                return x
+              }
+            }
+            assert new C().test() == 'valuable'
+        '''
+
+        assertScript '''
+            trait T {
+              def setX(value) { 'retval' }
+            }
+            class C implements T {
+              def test() {
+                T.super.x = 'value'
+              }
+            }
+            assert new C().test() == 'retval'
+        '''
+    }
+
     @Test
     void testSuperCallInTraitExtendingAnotherTrait() {
         assertScript '''