Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GroovyAST add generic field at compile time

I want to add a generic field to some classes at compile time. To this objective, I was implement my own AST annotation and transformation classes by following the official documentation and annotate desired classes with AST annotation.

But I am getting this error at compile time:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: /home/.../groovy/Sample.groovy: -1: A transform used a generics containing ClassNode java.util.HashSet for the field x directly. You are not supposed to do this. Please create a new ClassNode referring to the old ClassNode and use the new ClassNode instead of the old one. Otherwise the compiler will create wrong descriptors and a potential NullPointerException in TypeResolver in the OpenJDK. If this is not your own doing, please report this bug to the writer of the transform. @ line -1, column -1.

Have I made a mistake?

Sample codes

For example, suppose I want to add a HashSet<Long> field, named x, to every class annotated by MyAST annotation.

My AST annotation class:

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
@GroovyASTTransformationClass(classes = [MyASTTransformation.class])
public @interface MyAST {
}

My AST transformation class:

@CompileStatic
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
public class MyASTTransformation implements ASTTransformation {

@Override
public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
     ClassNode clazz = (ClassNode) nodes[1];
     ClassNode longHashSetClass = new ClassNode(HashSet.class);
     longHashSetClass.setGenericsTypes([new GenericsType(new ClassNode(Long.class))] as GenericsType[]);
     FieldNode field = new FieldNode("x", FieldNode.ACC_PRIVATE, longHashSetClass, clazz, new ConstantExpression(null));
     clazz.addField(field);
 }
}

Sample annotated class:

@MyAST
public class Sample {
}

Note

When I eliminate the line longHashSetClass.setGenericsTypes([new GenericsType(new ClassNode(Long.class))] as GenericsType[]);, everything is OK but type of x is HashSet instead of HashSet<Long> at runtime.

like image 619
vahidreza Avatar asked Oct 19 '22 04:10

vahidreza


1 Answers

You should use ClassHelper or GenericUtils in order to create a ClassNode :

import static org.codehaus.groovy.ast.ClassHelper.make
import static org.codehaus.groovy.ast.tools.GenericsUtils.makeClassSafeWithGenerics

...

ClassNode hashSet = makeClassSafeWithGenerics(HashSet, make(Long))
like image 121
Jérémie B Avatar answered Oct 21 '22 04:10

Jérémie B