From commits-return-7680-archive-asf-public=cust-asf.ponee.io@groovy.apache.org Fri Nov 16 22:43:07 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id EF636180670 for ; Fri, 16 Nov 2018 22:43:06 +0100 (CET) Received: (qmail 67115 invoked by uid 500); 16 Nov 2018 21:43:06 -0000 Mailing-List: contact commits-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list commits@groovy.apache.org Received: (qmail 67106 invoked by uid 99); 16 Nov 2018 21:43:06 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 16 Nov 2018 21:43:06 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id A8BFFE0483; Fri, 16 Nov 2018 21:43:05 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: paulk@apache.org To: commits@groovy.apache.org Message-Id: <671fc93460d04fdfb9e36b183aa971c8@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: groovy git commit: GROOVY-8880: Traits - static/instance init blocks (closes #823) Date: Fri, 16 Nov 2018 21:43:05 +0000 (UTC) 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 Authored: Fri Nov 16 00:20:01 2018 +1000 Committer: Paul King 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 methods = new ArrayList(cNode.getMethods()); List nonPublicAPIMethods = new LinkedList(); + List 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("")) { + 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 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 getStatements(Statement stmt) { + if (stmt instanceof BlockStatement) { + return ((BlockStatement) stmt).getStatements(); + } + List result = new ArrayList(); + 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.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 staticStatements = new ArrayList(); + 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' + ''' + } }