GROOVY-8127: Access to Trait$Trait$Helper#$self is forbidden (closes #529)
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/aff9fff5 Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/aff9fff5 Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/aff9fff5 Branch: refs/heads/parrot Commit: aff9fff589e6d3eb8d9f405cf8dfdf258128b488 Parents: 7cfaa27 Author: paulk <pa...@asert.com.au> Authored: Mon Apr 24 18:02:34 2017 +1000 Committer: paulk <pa...@asert.com.au> Committed: Thu May 11 08:03:38 2017 +1000 ---------------------------------------------------------------------- .../transform/trait/TraitASTTransformation.java | 22 ++++---- .../groovy/transform/trait/TraitComposer.java | 7 +-- src/test/groovy/bugs/Groovy8127Bug.groovy | 53 ++++++++++++++++++++ 3 files changed, 70 insertions(+), 12 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/aff9fff5/src/main/org/codehaus/groovy/transform/trait/TraitASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/transform/trait/TraitASTTransformation.java b/src/main/org/codehaus/groovy/transform/trait/TraitASTTransformation.java index 8222056..4ac7753 100644 --- a/src/main/org/codehaus/groovy/transform/trait/TraitASTTransformation.java +++ b/src/main/org/codehaus/groovy/transform/trait/TraitASTTransformation.java @@ -73,9 +73,13 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS; import static org.codehaus.groovy.transform.trait.SuperCallTraitTransformer.UNRESOLVED_HELPER_CLASS; /** - * Handles generation of code for the @Trait annotation. A class annotated with @Trait will generate, instead: <ul> - * <li>an <i>interface</i> with the same name</li> <li>an utility inner class that will be used by the compiler to - * handle the trait</li> </ul> + * Handles generation of code for the traits (trait keyword is equivalent to using the @Trait annotation). + * A class annotated with @Trait will generate, instead: + * <ul> + * <li>an <i>interface</i> with the same name</li> + * <li>a utility inner class that will be used by the compiler to implement the trait</li> + * <li>potentially a utility inner class to assist with implementing trait fields</li> + * </ul> * * @author Cedric Champeau */ @@ -415,21 +419,21 @@ public class TraitASTTransformation extends AbstractASTTransformation implements Expression initialExpression = field.getInitialExpression(); MethodNode selectedMethod = field.isStatic()?staticInitializer:initializer; if (initialExpression != null) { + VariableExpression thisObject = new VariableExpression(selectedMethod.getParameters()[0]); + ExpressionStatement initCode = new ExpressionStatement(initialExpression); + processBody(thisObject, initCode, trait, helper, fieldHelper, knownFields); if (field.isFinal()) { String baseName = field.isStatic() ? Traits.STATIC_INIT_METHOD : Traits.INIT_METHOD; MethodNode fieldInitializer = new MethodNode( baseName + Traits.remappedFieldName(trait, field.getName()), ACC_STATIC | ACC_PUBLIC | ACC_SYNTHETIC, field.getOriginType(), - Parameter.EMPTY_ARRAY, + new Parameter[]{createSelfParameter(trait, field.isStatic())}, ClassNode.EMPTY_ARRAY, - returnS(initialExpression) + returnS(initCode.getExpression()) ); helper.addMethod(fieldInitializer); } - VariableExpression thisObject = new VariableExpression(selectedMethod.getParameters()[0]); - ExpressionStatement initCode = new ExpressionStatement(initialExpression); - processBody(thisObject, initCode, trait, helper, fieldHelper, knownFields); BlockStatement code = (BlockStatement) selectedMethod.getCode(); MethodCallExpression mce; if (field.isStatic()) { @@ -480,7 +484,7 @@ public class TraitASTTransformation extends AbstractASTTransformation implements ACC_STATIC | ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC, field.getOriginType(), fieldHelper, - field.isFinal() ? initialExpression : null + null ); // copy annotations from field to dummy field List<AnnotationNode> copied = new LinkedList<AnnotationNode>(); http://git-wip-us.apache.org/repos/asf/groovy/blob/aff9fff5/src/main/org/codehaus/groovy/transform/trait/TraitComposer.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/transform/trait/TraitComposer.java b/src/main/org/codehaus/groovy/transform/trait/TraitComposer.java index 1025e61..b97f480 100644 --- a/src/main/org/codehaus/groovy/transform/trait/TraitComposer.java +++ b/src/main/org/codehaus/groovy/transform/trait/TraitComposer.java @@ -71,6 +71,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import static org.codehaus.groovy.ast.tools.GeneralUtils.args; import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX; import static org.codehaus.groovy.ast.tools.GeneralUtils.callX; import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS; @@ -257,13 +258,13 @@ public abstract class TraitComposer { List<AnnotationNode> copied = new LinkedList<AnnotationNode>(); List<AnnotationNode> notCopied = new LinkedList<AnnotationNode>(); GeneralUtils.copyAnnotatedNodeAnnotations(helperField, copied, notCopied); - FieldNode fieldNode = cNode.addField(fieldName, fieldMods, returnType, (fieldMods & Opcodes.ACC_FINAL) == 0 ? null : helperField.getInitialExpression()); + FieldNode fieldNode = cNode.addField(fieldName, fieldMods, returnType, null); fieldNode.addAnnotations(copied); // getInitialExpression above will be null if not in same source unit // so instead set within (static) initializer - if (fieldNode.isFinal() && !(helperClassNode instanceof InnerClassNode)) { + if (fieldNode.isFinal()) { String baseName = fieldNode.isStatic() ? Traits.STATIC_INIT_METHOD : Traits.INIT_METHOD; - Expression mce = callX(helperClassNode, baseName + fieldNode.getName()); + 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); http://git-wip-us.apache.org/repos/asf/groovy/blob/aff9fff5/src/test/groovy/bugs/Groovy8127Bug.groovy ---------------------------------------------------------------------- diff --git a/src/test/groovy/bugs/Groovy8127Bug.groovy b/src/test/groovy/bugs/Groovy8127Bug.groovy new file mode 100644 index 0000000..842390e --- /dev/null +++ b/src/test/groovy/bugs/Groovy8127Bug.groovy @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.bugs + +import gls.CompilableTestSupport + +class Groovy8127Bug extends CompilableTestSupport { + void testTraitWithClosureReferencingField() { + assertScript """ + trait BarTrait { + String result = '' + public final Runnable bar = { result = 'changeme' } as Runnable + void doRun() { bar.run() } + } + + class Bar implements BarTrait {} + + def b = new Bar() + b.doRun() + assert b.result == 'changeme' + """ + } + + void testTraitWithCompileStaticAndCoercedClosure() { + shouldCompile """ + @groovy.transform.CompileStatic + trait FooTrait { + public final Runnable foo = { println new Date() } as Runnable + void doRun() { foo.run() } + } + + class Foo implements FooTrait { } + + new Foo().doRun() + """ + } +}