Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.codehaus.groovy.transform.trait;

import org.apache.groovy.ast.tools.MethodNodeUtils;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassHelper;
Expand All @@ -34,7 +35,6 @@
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
import org.codehaus.groovy.ast.stmt.EmptyStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.ast.tools.GeneralUtils;
Expand Down Expand Up @@ -177,6 +177,7 @@ private static void applyTrait(final ClassNode trait, final ClassNode cNode, fin
Traits.STATIC_INIT_METHOD, Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC, ClassHelper.VOID_TYPE,
new Parameter[] {new Parameter(ClassHelper.CLASS_Type,"clazz")}, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE);
staticInitMethod.setDeclaringClass(helperClassNode);
staticInitCall.setImplicitThis(false);
staticInitCall.setMethodTarget(staticInitMethod);
cNode.addStaticInitializerStatements(Collections.singletonList(stmt(staticInitCall)), false);
if (fieldHelperClassNode != null && !cNode.declaresInterface(fieldHelperClassNode)) {
Expand Down Expand Up @@ -265,10 +266,17 @@ private static void applyTrait(final ClassNode trait, final ClassNode cNode, fin
// getInitialExpression above will be null if not in same source unit
// so instead set within (static) initializer
if (fieldNode.isFinal()) {
String baseName = fieldNode.isStatic() ? Traits.STATIC_INIT_METHOD : Traits.INIT_METHOD;
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));
// a final field with an initial value has its own initializer in the helper,
// which takes the trait, or the trait's class for a static field
String fieldInitName = (fieldNode.isStatic() ? Traits.STATIC_INIT_METHOD : Traits.INIT_METHOD) + fieldNode.getName();
ClassNode selfType = fieldNode.isStatic() ? ClassHelper.CLASS_Type : trait;
MethodNode fieldInitMethod = helperClassNode.getDeclaredMethod(fieldInitName, new Parameter[]{new Parameter(selfType, "self")});
if (fieldInitMethod != null) {
MethodCallExpression fieldInitCall = callX(classX(helperClassNode), fieldInitName, varX("this"));
// call it directly instead of through the meta class of the helper
fieldInitCall.setImplicitThis(false);
fieldInitCall.setMethodTarget(fieldInitMethod);
Statement stmt = stmt(assignX(varX(fieldNode.getName(), fieldNode.getType()), fieldInitCall));
if (isStatic == 0) {
cNode.addObjectInitializerStatements(stmt);
} else {
Expand Down Expand Up @@ -316,9 +324,20 @@ private static void applyTrait(final ClassNode trait, final ClassNode cNode, fin
}
}
}
cNode.addObjectInitializerStatements(stmt(
callX(classX(helperClassNode), Traits.INIT_METHOD, varX("this"))
));
// TraitASTTransformation generates $init$(TraitType) for every trait, so a helper without it is a bug
MethodNode initMethod = helperClassNode.getDeclaredMethod(Traits.INIT_METHOD, new Parameter[]{new Parameter(trait, Traits.THIS_OBJECT)});
if (initMethod == null) {
throw new GroovyBugError("Trait helper " + helperClassNode.getName() + " does not declare "
+ Traits.INIT_METHOD + "(" + trait.getName() + ")");
}
MethodCallExpression initCall = callX(
classX(helperClassNode),
Traits.INIT_METHOD,
varX("this"));
// call the helper's $init$(TraitType) directly instead of through its meta class
initCall.setImplicitThis(false);
initCall.setMethodTarget(initMethod);
cNode.addObjectInitializerStatements(stmt(initCall));
}

private static void createForwarderMethod(
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/codehaus/groovy/transform/trait/Traits.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ public abstract class Traits {

/**
* Name of the synthetic instance initialization method.
* <p>
* Since GROOVY-12491, implementing classes call it, like {@link #STATIC_INIT_METHOD} and the
* initializers of final trait fields, with a direct {@code invokestatic}, so a method added to
* the meta class of the helper does not replace it. The classes that {@code withTraits} and
* {@code as} generate at run time are composed the same way, so this holds for them too.
*/
static final String INIT_METHOD = "$init$";

Expand Down
Loading
Loading