Repository: groovy
Updated Branches:
  refs/heads/master 0eb3d7bd6 -> 55fd0ddaa


GROOVY-8880: Traits - static/instance init blocks (closes #823)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/55fd0dda
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/55fd0dda
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/55fd0dda

Branch: refs/heads/master
Commit: 55fd0ddaa0396dbf84117c6a08a86982ce5fc0d6
Parents: 0eb3d7b
Author: Paul King <pa...@asert.com.au>
Authored: Fri Nov 16 00:20:01 2018 +1000
Committer: Paul King <pa...@asert.com.au>
Committed: Sat Nov 17 07:41:58 2018 +1000

----------------------------------------------------------------------
 .../transform/trait/TraitASTTransformation.java | 47 +++++++++++++++++++-
 .../groovy/transform/trait/TraitComposer.java   | 28 +++++++-----
 .../traitx/TraitASTTransformationTest.groovy    | 37 +++++++++++++++
 3 files changed, 98 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/55fd0dda/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java 
b/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
index 02c0f08..cd81da6 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
@@ -217,6 +217,7 @@ public class TraitASTTransformation extends 
AbstractASTTransformation implements
         // add methods
         List<MethodNode> methods = new 
ArrayList<MethodNode>(cNode.getMethods());
         List<MethodNode> nonPublicAPIMethods = new LinkedList<MethodNode>();
+        List<Statement> staticInitStatements = null;
         for (final MethodNode methodNode : methods) {
             boolean declared = methodNode.getDeclaringClass() == cNode;
             if (declared) {
@@ -226,8 +227,13 @@ public class TraitASTTransformation extends 
AbstractASTTransformation implements
                     return null;
                 }
                 if (!methodNode.isAbstract()) {
-                    // add non-abstract methods; abstract methods covered from 
trait interface
-                    helper.addMethod(processMethod(cNode, helper, methodNode, 
fieldHelper, fieldNames));
+                    MethodNode newMethod = processMethod(cNode, helper, 
methodNode, fieldHelper, fieldNames);
+                    if (methodNode.getName().equals("<clinit>")) {
+                        staticInitStatements = 
getStatements(newMethod.getCode());
+                    } else {
+                        // add non-abstract methods; abstract methods covered 
from trait interface
+                        helper.addMethod(newMethod);
+                    }
                 }
                 if (methodNode.isPrivate() || methodNode.isStatic()) {
                     nonPublicAPIMethods.add(methodNode);
@@ -245,6 +251,22 @@ public class TraitASTTransformation extends 
AbstractASTTransformation implements
             processField(field, initializer, staticInitializer, fieldHelper, 
helper, staticFieldHelper, cNode, fieldNames);
         }
 
+        // copy statements from static and instance init blocks
+        if (staticInitStatements != null) {
+            BlockStatement toBlock = getBlockStatement(staticInitializer, 
staticInitializer.getCode());
+            for (Statement next : staticInitStatements) {
+                toBlock.addStatement(next);
+            }
+        }
+        List<Statement> initStatements = 
cNode.getObjectInitializerStatements();
+        Statement toCode = initializer.getCode();
+        BlockStatement toBlock = getBlockStatement(initializer, toCode);
+        for (Statement next : initStatements) {
+            Parameter selfParam = createSelfParameter(cNode, false);
+            toBlock.addStatement(processBody(new 
VariableExpression(selfParam), next, cNode, helper, fieldHelper, fieldNames));
+        }
+        initStatements.clear();
+
         // clear properties to avoid generation of methods
         cNode.getProperties().clear();
 
@@ -279,6 +301,27 @@ public class TraitASTTransformation extends 
AbstractASTTransformation implements
         return helper;
     }
 
+    private BlockStatement getBlockStatement(MethodNode targetMethod, 
Statement code) {
+        BlockStatement toBlock;
+        if (code instanceof BlockStatement) {
+            toBlock = (BlockStatement) code;
+        } else {
+            toBlock = new BlockStatement();
+            toBlock.addStatement(code);
+            targetMethod.setCode(toBlock);
+        }
+        return toBlock;
+    }
+
+    private List<Statement> getStatements(Statement stmt) {
+        if (stmt instanceof BlockStatement) {
+            return ((BlockStatement) stmt).getStatements();
+        }
+        List<Statement> result = new ArrayList<Statement>();
+        result.add(stmt);
+        return result;
+    }
+
     private static MethodNode createInitMethod(final boolean isStatic, final 
ClassNode cNode, final ClassNode helper) {
         MethodNode initializer = new MethodNode(
                 isStatic?Traits.STATIC_INIT_METHOD:Traits.INIT_METHOD,

http://git-wip-us.apache.org/repos/asf/groovy/blob/55fd0dda/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java 
b/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
index f7a57f8..70eaba9 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
@@ -178,12 +178,6 @@ public abstract class TraitComposer {
                 createForwarderMethod(trait, cNode, methodNode, 
originalMethod, helperClassNode, methodGenericsSpec, helperMethodParams, 
origParams, params, argList, unit);
             }
         }
-        cNode.addObjectInitializerStatements(new ExpressionStatement(
-                new MethodCallExpression(
-                        new ClassExpression(helperClassNode),
-                        Traits.INIT_METHOD,
-                        new ArgumentListExpression(new 
VariableExpression("this")))
-        ));
         MethodCallExpression staticInitCall = new MethodCallExpression(
                 new ClassExpression(helperClassNode),
                 Traits.STATIC_INIT_METHOD,
@@ -276,12 +270,16 @@ public abstract class TraitComposer {
                             // so instead set within (static) initializer
                             if (fieldNode.isFinal()) {
                                 String baseName = fieldNode.isStatic() ? 
Traits.STATIC_INIT_METHOD : Traits.INIT_METHOD;
-                                Expression mce = callX(helperClassNode, 
baseName + fieldNode.getName(), args(varX("this")));
-                                Statement stmt = 
stmt(assignX(varX(fieldNode.getName(), fieldNode.getType()), mce));
-                                if (isStatic == 0) {
-                                    cNode.addObjectInitializerStatements(stmt);
-                                } else {
-                                    
cNode.addStaticInitializerStatements(Collections.<Statement>singletonList(stmt),
 false);
+                                StaticMethodCallExpression mce = 
callX(helperClassNode, baseName + fieldNode.getName(), args(varX("this")));
+                                if 
(helperClassNode.hasPossibleStaticMethod(mce.getMethod(), mce.getArguments())) {
+                                    Statement stmt = 
stmt(assignX(varX(fieldNode.getName(), fieldNode.getType()), mce));
+                                    if (isStatic == 0) {
+                                        
cNode.addObjectInitializerStatements(stmt);
+                                    } else {
+                                        List<Statement> staticStatements = new 
ArrayList<Statement>();
+                                        staticStatements.add(stmt);
+                                        
cNode.addStaticInitializerStatements(staticStatements, true);
+                                    }
                                 }
                             }
                         }
@@ -322,6 +320,12 @@ public abstract class TraitComposer {
                     cNode.addMethod(impl);
                 }
             }
+            cNode.addObjectInitializerStatements(new ExpressionStatement(
+                    new MethodCallExpression(
+                            new ClassExpression(helperClassNode),
+                            Traits.INIT_METHOD,
+                            new ArgumentListExpression(new 
VariableExpression("this")))
+            ));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/groovy/blob/55fd0dda/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
----------------------------------------------------------------------
diff --git 
a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
 
b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
index 4c3b95e..b20c4c6 100644
--- 
a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
+++ 
b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
@@ -2607,4 +2607,41 @@ assert c.b() == 2
         '''
     }
 
+    //GROOVY-8880
+    void testTraitWithInitBlock() {
+        assertScript '''
+            trait MyTrait {
+                final String first = 'FOO'
+                final String last = 'BAR'
+                String full
+
+                {
+                    full = "$first$last"
+                }
+            }
+
+            class MyClass implements MyTrait { }
+
+            def mc = new MyClass()
+            assert mc.full == 'FOOBAR'
+        '''
+    }
+
+    //GROOVY-8880
+    void testTraitWithStaticInitBlock() {
+        assertScript '''
+            trait MyTrait {
+                static final String first = 'FOO'
+                static final String last = 'BAR'
+                static String full
+                static {
+                    full = "$first$last"
+                }
+            }
+
+            class MyClass implements MyTrait { }
+
+            assert MyClass.full == 'FOOBAR'
+        '''
+    }
 }

Reply via email to