1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
package lombok.javac.handlers;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import lombok.core.AST;
import lombok.core.AnnotationValues;
import lombok.experimental.Memoize;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.spi.Provides;
import static lombok.javac.handlers.JavacHandlerUtil.*;
@Provides
public class HandleMemoize extends JavacAnnotationHandler<Memoize> {
@Override
public void handle(AnnotationValues<Memoize> annotation, JCTree.JCAnnotation ast, JavacNode annotationNode) {
// TODO: handle experimental flag usage
deleteAnnotationIfNeccessary(annotationNode, Memoize.class);
JavacNode annotatedNode = annotationNode.up();
if (annotatedNode.getKind() != AST.Kind.METHOD) {
annotationNode.addError("@Memoize is only valid on a method.");
return;
}
JCTree.JCMethodDecl md = (JCTree.JCMethodDecl) annotatedNode.get();
JavacNode containerNode = annotatedNode.up();
if (containerNode.getKind() != AST.Kind.TYPE) {
annotationNode.addError("@Memoize is only valid on methods in a class.");
return;
}
JCTree.JCClassDecl cd = (JCTree.JCClassDecl) containerNode.get();
if ((md.mods.flags & Flags.ABSTRACT) != 0) {
annotationNode.addError("@Memoize is not valid on an abstract method.");
return;
}
if (md.name.charAt(0) == '<') {
annotationNode.addError("@Memoize is not valid on a constructor.");
}
boolean isStatic = (md.mods.flags & Flags.STATIC) != 0;
JavacTreeMaker maker = containerNode.getTreeMaker();
Name cacheName = annotationNode.toName("$lombok$memoizeCache$" + cd.name.toString() + "$" + md.name.toString());
JCTree.JCVariableDecl cacheVariable = maker.VarDef(
maker.Modifiers(Flags.PRIVATE | Flags.TRANSIENT | (isStatic ? Flags.STATIC : 0)),
cacheName,
chainDots(annotatedNode, "java", "util", "Map"),
maker.NewClass(null, List.<JCTree.JCExpression>nil(), chainDots(annotatedNode, "java", "util", "HashMap"), List.<JCTree.JCExpression>nil(), null)
);
injectFieldAndMarkGenerated(containerNode, cacheVariable);
recursiveSetGeneratedBy(cacheVariable, annotationNode);
Name newName = annotatedNode.toName(md.name.toString() + "$uncached");
Name keyName = annotatedNode.toName("key");
Name resultName = annotatedNode.toName("result");
ListBuffer<JCTree.JCExpression> paramNames = new ListBuffer<JCTree.JCExpression>();
for (JCTree.JCVariableDecl param : md.params) {
paramNames.add(maker.Ident(param.name));
}
ListBuffer<JCTree.JCExpression> tyParamNames = new ListBuffer<JCTree.JCExpression>();
for (JCTree.JCTypeParameter param : md.typarams) {
tyParamNames.add(maker.Ident(param.name));
}
List<JCTree.JCStatement> newStatements = List.<JCTree.JCStatement>of(
maker.VarDef(maker.Modifiers(0), keyName, genJavaLangTypeRef(annotatedNode, ast.pos, "Object"), maker.Apply(
List.<JCTree.JCExpression>nil(), chainDots(annotatedNode, "java", "util", "Arrays", "asList"), List.<JCTree.JCExpression>of(
maker.NewArray(
genJavaLangTypeRef(annotatedNode, ast.pos, "Object"),
List.<JCTree.JCExpression>nil(),
paramNames.toList()
)
)
)),
maker.If(
maker.Apply(
List.<JCTree.JCExpression>nil(), chainDots(annotatedNode, cacheName.toString(), "containsKey"), List.<JCTree.JCExpression>of(maker.Ident(keyName))
),
maker.Return(maker.TypeCast(
md.restype,
maker.Apply(
List.<JCTree.JCExpression>nil(), chainDots(annotatedNode, cacheName.toString(), "get"), List.<JCTree.JCExpression>of(maker.Ident(keyName))
)
)),
maker.Block(0, List.<JCTree.JCStatement>of(
maker.VarDef(maker.Modifiers(0), resultName, md.restype,
maker.Apply(tyParamNames.toList(), maker.Select(isStatic ? maker.Ident(cd.name) : maker.Ident(annotatedNode.toName("this")), newName), paramNames.toList())
),
maker.Exec(maker.Apply(List.<JCTree.JCExpression>nil(), chainDots(annotatedNode, cacheName.toString(), "put"), List.<JCTree.JCExpression>of(maker.Ident(keyName), maker.Ident(resultName)))),
maker.Return(maker.Ident(resultName))
))
)
);
ListBuffer<JCTree.JCAnnotation> newAnnotations = new ListBuffer<JCTree.JCAnnotation>();
for (JCTree.JCAnnotation jcAnnotation : md.mods.annotations) {
if (jcAnnotation != annotationNode.get())
newAnnotations.add(jcAnnotation);
}
md.mods.annotations = newAnnotations.toList();
JCTree.JCMethodDecl newMethod = maker.MethodDef(maker.Modifiers((md.mods.flags & ~Flags.PUBLIC & ~Flags.PROTECTED) | Flags.PRIVATE, List.<JCTree.JCAnnotation>nil()), newName, md.restype, md.typarams, md.params, md.thrown, md.body, null);
injectMethod(containerNode, newMethod);
md.body = maker.Block(0, newStatements);
}
}
|