diff options
Diffstat (limited to 'src/lombok/javac')
-rw-r--r-- | src/lombok/javac/HandlerLibrary.java | 227 | ||||
-rw-r--r-- | src/lombok/javac/JavacAST.java | 329 | ||||
-rw-r--r-- | src/lombok/javac/JavacASTAdapter.java | 35 | ||||
-rw-r--r-- | src/lombok/javac/JavacASTVisitor.java | 182 | ||||
-rw-r--r-- | src/lombok/javac/JavacAnnotationHandler.java | 6 | ||||
-rw-r--r-- | src/lombok/javac/JavacNode.java | 63 | ||||
-rw-r--r-- | src/lombok/javac/apt/Processor.java | 66 | ||||
-rw-r--r-- | src/lombok/javac/handlers/HandleGetter_javac.java | 27 | ||||
-rw-r--r-- | src/lombok/javac/handlers/PKG.java | 9 |
9 files changed, 703 insertions, 241 deletions
diff --git a/src/lombok/javac/HandlerLibrary.java b/src/lombok/javac/HandlerLibrary.java index 6f33003f..f8fd0d75 100644 --- a/src/lombok/javac/HandlerLibrary.java +++ b/src/lombok/javac/HandlerLibrary.java @@ -1,13 +1,9 @@ package lombok.javac; import java.lang.annotation.Annotation; -import java.lang.reflect.Array; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -16,19 +12,28 @@ import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import javax.annotation.processing.Messager; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; +import lombok.core.AnnotationValues; import lombok.core.SpiLoadUtil; +import lombok.core.TypeLibrary; +import lombok.core.TypeResolver; +import lombok.core.AnnotationValues.AnnotationValue; +import lombok.core.AnnotationValues.AnnotationValueDecodeFail; + +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCAssign; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCLiteral; +import com.sun.tools.javac.tree.JCTree.JCNewArray; public class HandlerLibrary { + private final TypeLibrary typeLibrary = new TypeLibrary(); private final Map<String, AnnotationHandlerContainer<?>> annotationHandlers = new HashMap<String, AnnotationHandlerContainer<?>>(); // private final Collection<JavacASTVisitor> visitorHandlers = new ArrayList<JavacASTVisitor>(); @@ -41,9 +46,56 @@ public class HandlerLibrary { this.annotationClass = annotationClass; } - @SuppressWarnings("unchecked") - public void handle(JavacNode node, Object annInstance) { - handler.handle(node, (T) annInstance); + private Object calculateGuess(JCExpression expr) { + if ( expr instanceof JCLiteral ) { + return ((JCLiteral)expr).value; + } else if ( expr instanceof JCIdent || expr instanceof JCFieldAccess ) { + String x = expr.toString(); + if ( x.endsWith(".class") ) x = x.substring(0, x.length() - 6); + else { + int idx = x.lastIndexOf('.'); + if ( idx > -1 ) x = x.substring(idx + 1); + } + return x; + } else return null; + } + + public void handle(JavacAST.Node node) { + Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>(); + JCAnnotation anno = (JCAnnotation) node.get(); + List<JCExpression> arguments = anno.getArguments(); + for ( Method m : annotationClass.getDeclaredMethods() ) { + if ( !Modifier.isPublic(m.getModifiers()) ) continue; + String name = m.getName(); + List<String> raws = new ArrayList<String>(); + List<Object> guesses = new ArrayList<Object>(); + + for ( JCExpression arg : arguments ) { + JCAssign assign = (JCAssign) arg; + String mName = assign.lhs.toString(); + if ( !mName.equals(name) ) continue; + JCExpression rhs = assign.rhs; + if ( rhs instanceof JCNewArray ) { + List<JCExpression> elems = ((JCNewArray)rhs).elems; + for ( JCExpression inner : elems ) { + raws.add(inner.toString()); + guesses.add(calculateGuess(inner)); + } + } else { + raws.add(rhs.toString()); + guesses.add(calculateGuess(rhs)); + } + } + + values.put(name, new AnnotationValue(node, raws, guesses) { + @Override public void setError(String message, int valueIdx) { + //TODO + super.setError(message, valueIdx); + } + }); + } + + handler.handle(new AnnotationValues<T>(annotationClass, values, node), (JCAnnotation)node.get(), node); } } @@ -68,6 +120,7 @@ public class HandlerLibrary { messager.printMessage(Diagnostic.Kind.WARNING, "Duplicate handlers for annotation type: " + container.annotationClass.getName()); } + lib.typeLibrary.addType(container.annotationClass.getName()); } catch ( ServiceConfigurationError e ) { messager.printMessage(Diagnostic.Kind.WARNING, "Can't load Lombok annotation handler for javac: " + e); @@ -75,144 +128,26 @@ public class HandlerLibrary { } } - public void handleAnnotation(JavacNode node, TypeElement annotationType) { - AnnotationHandlerContainer<?> container = annotationHandlers.get(annotationType.getQualifiedName().toString()); - if ( container == null ) return; - try { - container.handle(node, createAnnotation(container.annotationClass, annotationType.getQualifiedName(), node)); - } catch ( AnnotationValueDecodeFail e ) { - node.addError(e.getMessage(), e.mirror, e.value); - } - } - - private Object createAnnotation(Class<? extends Annotation> target, Name annotationName, JavacNode node) - throws AnnotationValueDecodeFail { - AnnotationMirror mirror = fetchMirror(annotationName, node); - if ( mirror == null ) throw new AssertionError("This can't be."); - - InvocationHandler invocations = new AnnotationMirrorInvocationHandler(target, mirror); - return Proxy.newProxyInstance(target.getClassLoader(), new Class[] { target }, invocations); - } - - private static class AnnotationValueDecodeFail extends Exception { - private static final long serialVersionUID = 1L; - - AnnotationMirror mirror; - AnnotationValue value; - - AnnotationValueDecodeFail(String msg, AnnotationMirror mirror, AnnotationValue value) { - super(msg); - this.mirror = mirror; - this.value = value; - } - } - - private static class AnnotationMirrorInvocationHandler implements InvocationHandler { - private final AnnotationMirror mirror; - private final Map<String, Object> values = new HashMap<String, Object>(); - - AnnotationMirrorInvocationHandler(Class<?> target, AnnotationMirror mirror) throws AnnotationValueDecodeFail { - this.mirror = mirror; - - for ( Method m : target.getDeclaredMethods() ) { - if ( !Modifier.isPublic(m.getModifiers()) ) continue; - values.put(m.getName(), decode(m)); - } - } - - private Object decode(Method m) throws AnnotationValueDecodeFail { - for ( Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : - mirror.getElementValues().entrySet() ) { - - if ( entry.getKey().getSimpleName().contentEquals(m.getName()) ) { - AnnotationValue value = entry.getValue(); - return convert(m.getReturnType(), mirror, value, value.getValue()); - } - } + public void handleAnnotation(JCCompilationUnit unit, JavacAST.Node node, JCAnnotation annotation) { + TypeResolver resolver = new TypeResolver(typeLibrary, node.getPackageDeclaration(), node.getImportStatements()); + String rawType = annotation.annotationType.toString(); + for ( String fqn : resolver.findTypeMatches(node, rawType) ) { + AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn); + if ( container == null ) continue; - return m.getDefaultValue(); - } - - @Override public Object invoke(Object proxy, Method method, Object[] args) { - return values.get(method.getName()); - } - - private Object convert(Class<?> expected, AnnotationMirror mirror, AnnotationValue value, Object v) throws AnnotationValueDecodeFail { - if ( expected == int.class ) { - if ( v instanceof Number ) return ((Number)v).intValue(); - else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value); - } else if ( expected == long.class ) { - if ( v instanceof Number ) return ((Number)v).longValue(); - else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value); - } else if ( expected == short.class ) { - if ( v instanceof Number ) return ((Number)v).shortValue(); - else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value); - } else if ( expected == byte.class ) { - if ( v instanceof Number ) return ((Number)v).byteValue(); - else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value); - } else if ( expected == double.class ) { - if ( v instanceof Number ) return ((Number)v).doubleValue(); - else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value); - } else if ( expected == float.class ) { - if ( v instanceof Number ) return ((Number)v).floatValue(); - else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value); - } else if ( expected == char.class ) { - if ( v instanceof Character ) return v; - else throw new AnnotationValueDecodeFail("Expected a character here", mirror, value); - } else if ( expected == boolean.class ) { - if ( v instanceof Boolean ) return v; - else throw new AnnotationValueDecodeFail("Expected a boolean here", mirror, value); - } else if ( expected == String.class ) { - if ( v instanceof String ) return v; - else throw new AnnotationValueDecodeFail("Expected a String here", mirror, value); - } else if ( expected == Class.class ) { - if ( v instanceof TypeMirror ) { - try { - return Class.forName(v.toString()); - } catch ( ClassNotFoundException e ) { - throw new AnnotationValueDecodeFail( - "I can't find this class. Lombok only works well with types in the core java libraries.", - mirror, value); - } - } else throw new AnnotationValueDecodeFail("Expected a class literal here", mirror, value); - } else if ( Enum.class.isAssignableFrom(expected) ) { - if ( v instanceof VariableElement ) { - String n = ((VariableElement)v).getSimpleName().toString(); - @SuppressWarnings("unchecked") - Object enumVal = Enum.valueOf((Class<? extends Enum>)expected, n); - return enumVal; - } else throw new AnnotationValueDecodeFail("Expected an enum value here", mirror, value); - } else if ( expected.isArray() ) { - if ( v instanceof Collection<?> ) { - List<Object> convertedValues = new ArrayList<Object>(); - Class<?> componentType = expected.getComponentType(); - for ( Object innerV : (Collection<?>)v ) { - convertedValues.add(convert(componentType, mirror, value, innerV)); - } - - Object array = Array.newInstance(componentType, convertedValues.size()); - int pos = 0; - for ( Object converted : convertedValues ) Array.set(array, pos++, converted); - return array; - } else throw new AnnotationValueDecodeFail("Expected an array value here", mirror, value); -// Collection<AnnotationValue> result = (Collection<AnnotationValue>)entry.getValue().getValue(); -// return result; - } else { - throw new AssertionError("We didn't know this is even a legal annotation type: " + expected); + try { + container.handle(node); + } catch ( AnnotationValueDecodeFail fail ) { + fail.owner.setError(fail.getMessage(), fail.idx); + } catch ( Throwable t ) { + t.printStackTrace(); +// Eclipse.error(String.format("Lombok annotation handler %s failed", container.handler.getClass()), t); + //TODO } } } - private AnnotationMirror fetchMirror(Name lookingFor, JavacNode node) { - for ( AnnotationMirror mirror : node.getJavacAST().getAnnotationMirrors() ) { - if ( !lookingFor.contentEquals( - ((TypeElement)(mirror.getAnnotationType()).asElement()).getQualifiedName()) ) continue; - return mirror; - } - return null; - } - - public void handleType(TypeElement typeElement) { + public void handleAST(JavacAST ast) { //Later! } diff --git a/src/lombok/javac/JavacAST.java b/src/lombok/javac/JavacAST.java new file mode 100644 index 00000000..f56477fb --- /dev/null +++ b/src/lombok/javac/JavacAST.java @@ -0,0 +1,329 @@ +package lombok.javac; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.annotation.processing.Messager; + +import lombok.core.AST; + +import com.sun.source.util.Trees; +import com.sun.tools.javac.processing.JavacProcessingEnvironment; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCImport; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.util.Name; + +public class JavacAST extends AST<JCTree> { + private final Trees trees; + private final JavacProcessingEnvironment env; + private final Messager messager; + private final Name.Table nameTable; + private final TreeMaker treeMaker; + + public JavacAST(Trees trees, JavacProcessingEnvironment env, JCCompilationUnit top) { + super(top.sourcefile == null ? null : top.sourcefile.toString()); + setTop(buildCompilationUnit(top)); + this.trees = trees; + this.env = env; + this.messager = env.getMessager(); + this.nameTable = Name.Table.instance(env.getContext()); + this.treeMaker = TreeMaker.instance(env.getContext()); + } + + @Override public String getPackageDeclaration() { + JCCompilationUnit unit = (JCCompilationUnit)top().get(); + return unit.pid instanceof JCFieldAccess ? unit.pid.toString() : null; + } + + @Override public Collection<String> getImportStatements() { + List<String> imports = new ArrayList<String>(); + JCCompilationUnit unit = (JCCompilationUnit)top().get(); + for ( JCTree def : unit.defs ) { + if ( def instanceof JCImport ) { + imports.add(((JCImport)def).qualid.toString()); + } + } + + return imports; + } + + public void traverse(JavacASTVisitor visitor) { + Node current = top(); + visitor.visitCompilationUnit(current, (JCCompilationUnit)current.get()); + traverseChildren(visitor, current); + visitor.endVisitCompilationUnit(current, (JCCompilationUnit)current.get()); + } + + private void traverseChildren(JavacASTVisitor visitor, Node node) { + for ( Node child : node.down() ) { + JCTree n = child.get(); + + switch ( child.getKind() ) { + case TYPE: + visitor.visitType(child, (JCClassDecl)n); + traverseChildren(visitor, child); + visitor.endVisitType(child, (JCClassDecl)n); + break; + case FIELD: + visitor.visitField(child, (JCVariableDecl)n); + traverseChildren(visitor, child); + visitor.endVisitField(child, (JCVariableDecl)n); + break; + case METHOD: + visitor.visitMethod(child, (JCMethodDecl)n); + traverseChildren(visitor, child); + visitor.endVisitMethod(child, (JCMethodDecl)n); + break; + case INITIALIZER: + visitor.visitInitializer(child, (JCBlock)n); + traverseChildren(visitor, child); + visitor.endVisitInitializer(child, (JCBlock)n); + break; + case ARGUMENT: + JCMethodDecl parent = (JCMethodDecl) child.up().get(); + visitor.visitMethodArgument(child, (JCVariableDecl)n, parent); + traverseChildren(visitor, child); + visitor.endVisitMethodArgument(child, (JCVariableDecl)n, parent); + break; + case LOCAL: + visitor.visitLocal(child, (JCVariableDecl)n); + traverseChildren(visitor, child); + visitor.endVisitLocal(child, (JCVariableDecl)n); + break; + case STATEMENT: + visitor.visitStatement(child, (JCTree)n); + traverseChildren(visitor, child); + visitor.endVisitStatement(node, (JCTree)n); + break; + case ANNOTATION: + switch ( child.up().getKind() ) { + case TYPE: + visitor.visitAnnotationOnType((JCClassDecl)child.up().get(), child, (JCAnnotation)n); + break; + case FIELD: + visitor.visitAnnotationOnField((JCVariableDecl)child.up().get(), child, (JCAnnotation)n); + break; + case METHOD: + visitor.visitAnnotationOnMethod((JCMethodDecl)child.up().get(), child, (JCAnnotation)n); + break; + case ARGUMENT: + JCVariableDecl argument = (JCVariableDecl)child.up().get(); + JCMethodDecl method = (JCMethodDecl)child.up().up().get(); + visitor.visitAnnotationOnMethodArgument(argument, method, child, (JCAnnotation)n); + break; + case LOCAL: + visitor.visitAnnotationOnLocal((JCVariableDecl)child.up().get(), child, (JCAnnotation)n); + break; + default: + throw new AssertionError("Can't be reached"); + } + break; + default: + throw new AssertionError("Can't be reached: " + child.getKind()); + } + } + } + + @Override public Node top() { + return (Node) super.top(); + } + + @Override public Node get(JCTree astNode) { + return (Node) super.get(astNode); + } + + public Name toName(String name) { + return nameTable.fromString(name); + } + + public TreeMaker getTreeMaker() { + return treeMaker; + } + + private Node buildCompilationUnit(JCCompilationUnit top) { + List<Node> childNodes = new ArrayList<Node>(); + for ( JCTree s : top.defs ) { + if ( s instanceof JCClassDecl ) { + addIfNotNull(childNodes, buildType((JCClassDecl)s)); + } // else they are import statements, which we don't care about. Or Skip objects, whatever those are. + } + + return new Node(top, childNodes, Kind.COMPILATION_UNIT); + } + + private Node buildType(JCClassDecl type) { + if ( alreadyHandled(type) ) return null; + List<Node> childNodes = new ArrayList<Node>(); + + for ( JCTree def : type.defs ) { + for ( JCAnnotation annotation : type.mods.annotations ) addIfNotNull(childNodes, buildAnnotation(annotation)); + /* A def can be: + * JCClassDecl for inner types + * JCMethodDecl for constructors and methods + * JCVariableDecl for fields + * JCBlock for (static) initializers + */ + if ( def instanceof JCMethodDecl ) addIfNotNull(childNodes, buildMethod((JCMethodDecl)def)); + else if ( def instanceof JCClassDecl ) addIfNotNull(childNodes, buildType((JCClassDecl)def)); + else if ( def instanceof JCVariableDecl ) addIfNotNull(childNodes, buildField((JCVariableDecl)def)); + else if ( def instanceof JCBlock ) addIfNotNull(childNodes, buildInitializer((JCBlock)def)); + } + + return putInMap(new Node(type, childNodes, Kind.TYPE)); + } + + private Node buildField(JCVariableDecl field) { + if ( alreadyHandled(field) ) return null; + List<Node> childNodes = new ArrayList<Node>(); + for ( JCAnnotation annotation : field.mods.annotations ) addIfNotNull(childNodes, buildAnnotation(annotation)); + addIfNotNull(childNodes, buildExpression(field.init)); + return putInMap(new Node(field, childNodes, Kind.FIELD)); + } + + private Node buildLocalVar(JCVariableDecl local) { + if ( alreadyHandled(local) ) return null; + List<Node> childNodes = new ArrayList<Node>(); + for ( JCAnnotation annotation : local.mods.annotations ) addIfNotNull(childNodes, buildAnnotation(annotation)); + addIfNotNull(childNodes, buildExpression(local.init)); + return putInMap(new Node(local, childNodes, Kind.LOCAL)); + } + + private Node buildInitializer(JCBlock initializer) { + if ( alreadyHandled(initializer) ) return null; + List<Node> childNodes = new ArrayList<Node>(); + for ( JCStatement statement: initializer.stats ) addIfNotNull(childNodes, buildStatement(statement)); + return putInMap(new Node(initializer, childNodes, Kind.INITIALIZER)); + } + + private Node buildMethod(JCMethodDecl method) { + if ( alreadyHandled(method) ) return null; + List<Node> childNodes = new ArrayList<Node>(); + for ( JCAnnotation annotation : method.mods.annotations ) addIfNotNull(childNodes, buildAnnotation(annotation)); + for ( JCVariableDecl param : method.params ) addIfNotNull(childNodes, buildLocalVar(param)); + if ( method.body != null && method.body.stats != null ) + for ( JCStatement statement : method.body.stats ) addIfNotNull(childNodes, buildStatement(statement)); + return putInMap(new Node(method, childNodes, Kind.METHOD)); + } + + private Node buildAnnotation(JCAnnotation annotation) { + if ( alreadyHandled(annotation) ) return null; + return putInMap(new Node(annotation, null, Kind.ANNOTATION)); + } + + private Node buildExpression(JCExpression expression) { + return buildStatementOrExpression(expression); + } + + private Node buildStatement(JCStatement statement) { + return buildStatementOrExpression(statement); + } + + private Node buildStatementOrExpression(JCTree statement) { + if ( statement == null || alreadyHandled(statement) ) return null; + if ( statement instanceof JCAnnotation ) return null; + if ( statement instanceof JCClassDecl ) return buildType((JCClassDecl)statement); + if ( statement instanceof JCVariableDecl ) return buildLocalVar((JCVariableDecl)statement); + + //We drill down because LocalDeclarations and TypeDeclarations can occur anywhere, even in, say, + //an if block, or even the expression on an assert statement! + + setAsHandled(statement); + return drill(statement); + } + + private Node drill(JCTree statement) { + List<Node> childNodes = new ArrayList<Node>(); + for ( FieldAccess fa : fieldsOf(statement.getClass()) ) childNodes.addAll(buildWithField(Node.class, statement, fa)); + return putInMap(new Node(statement, childNodes, Kind.STATEMENT)); + } + + protected Collection<Class<? extends JCTree>> getStatementTypes() { + Collection<Class<? extends JCTree>> collection = new ArrayList<Class<? extends JCTree>>(2); + collection.add(JCStatement.class); + collection.add(JCExpression.class); + return collection; + } + + private static void addIfNotNull(Collection<Node> nodes, Node node) { + if ( node != null ) nodes.add(node); + } + + public class Node extends AST<JCTree>.Node { + public Node(JCTree node, Collection<Node> children, Kind kind) { + super(node, children, kind); + } + + @Override public String getName() { + final Name n; + + if ( node instanceof JCClassDecl ) n = ((JCClassDecl)node).name; + else if ( node instanceof JCMethodDecl ) n = ((JCMethodDecl)node).name; + else if ( node instanceof JCVariableDecl ) n = ((JCVariableDecl)node).name; + else n = null; + + return n == null ? null : n.toString(); + } + + @Override protected boolean calculateIsStructurallySignificant() { + if ( node instanceof JCClassDecl ) return true; + if ( node instanceof JCMethodDecl ) return true; + if ( node instanceof JCVariableDecl ) return true; + if ( node instanceof JCCompilationUnit ) return true; + return false; + } + + public TreeMaker getTreeMaker() { + return treeMaker; + } + + public Name toName(String name) { + return JavacAST.this.toName(name); + } + + /** {@inheritDoc} */ + @Override public Node directUp() { + return (Node) super.directUp(); + } + + /** {@inheritDoc} */ + @Override public Node up() { + return (Node) super.up(); + } + + /** {@inheritDoc} */ + @Override public Node top() { + return (Node) super.top(); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Override public Collection<Node> down() { + return (Collection<Node>) children; + } + + public void addError(String message) { + System.err.println("ERR: " + message); + //TODO + } + + public void addWarning(String message) { + System.err.println("WARN: " + message); + //TODO + } + } + + @Override protected Node buildStatement(Object statement) { + return buildStatementOrExpression((JCTree) statement); + } +} diff --git a/src/lombok/javac/JavacASTAdapter.java b/src/lombok/javac/JavacASTAdapter.java new file mode 100644 index 00000000..5d678f44 --- /dev/null +++ b/src/lombok/javac/JavacASTAdapter.java @@ -0,0 +1,35 @@ +package lombok.javac; + +import lombok.javac.JavacAST.Node; + +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; + +public class JavacASTAdapter implements JavacASTVisitor { + @Override public void visitCompilationUnit(Node top, JCCompilationUnit unit) {} + @Override public void endVisitCompilationUnit(Node top, JCCompilationUnit unit) {} + @Override public void visitType(Node typeNode, JCClassDecl type) {} + @Override public void visitAnnotationOnType(JCClassDecl type, Node annotationNode, JCAnnotation annotation) {} + @Override public void endVisitType(Node typeNode, JCClassDecl type) {} + @Override public void visitField(Node fieldNode, JCVariableDecl field) {} + @Override public void visitAnnotationOnField(JCVariableDecl field, Node annotationNode, JCAnnotation annotation) {} + @Override public void endVisitField(Node fieldNode, JCVariableDecl field) {} + @Override public void visitInitializer(Node initializerNode, JCBlock initializer) {} + @Override public void endVisitInitializer(Node initializerNode, JCBlock initializer) {} + @Override public void visitMethod(Node methodNode, JCMethodDecl method) {} + @Override public void visitAnnotationOnMethod(JCMethodDecl method, Node annotationNode, JCAnnotation annotation) {} + @Override public void endVisitMethod(Node methodNode, JCMethodDecl method) {} + @Override public void visitMethodArgument(Node argumentNode, JCVariableDecl argument, JCMethodDecl method) {} + @Override public void visitAnnotationOnMethodArgument(JCVariableDecl argument, JCMethodDecl method, Node annotationNode, JCAnnotation annotation) {} + @Override public void endVisitMethodArgument(Node argumentNode, JCVariableDecl argument, JCMethodDecl method) {} + @Override public void visitLocal(Node localNode, JCVariableDecl local) {} + @Override public void visitAnnotationOnLocal(JCVariableDecl local, Node annotationNode, JCAnnotation annotation) {} + @Override public void endVisitLocal(Node localNode, JCVariableDecl local) {} + @Override public void visitStatement(Node statementNode, JCTree statement) {} + @Override public void endVisitStatement(Node statementNode, JCTree statement) {} +} diff --git a/src/lombok/javac/JavacASTVisitor.java b/src/lombok/javac/JavacASTVisitor.java new file mode 100644 index 00000000..daeb6b80 --- /dev/null +++ b/src/lombok/javac/JavacASTVisitor.java @@ -0,0 +1,182 @@ +package lombok.javac; + +import lombok.javac.JavacAST.Node; + +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; + +public interface JavacASTVisitor { + /** + * Called at the very beginning and end. + */ + void visitCompilationUnit(Node top, JCCompilationUnit unit); + void endVisitCompilationUnit(Node top, JCCompilationUnit unit); + + /** + * Called when visiting a type (a class, interface, annotation, enum, etcetera). + */ + void visitType(Node typeNode, JCClassDecl type); + void visitAnnotationOnType(JCClassDecl type, Node annotationNode, JCAnnotation annotation); + void endVisitType(Node typeNode, JCClassDecl type); + + /** + * Called when visiting a field of a class. + */ + void visitField(Node fieldNode, JCVariableDecl field); + void visitAnnotationOnField(JCVariableDecl field, Node annotationNode, JCAnnotation annotation); + void endVisitField(Node fieldNode, JCVariableDecl field); + + /** + * Called for static and instance initializers. You can tell the difference via the isStatic() method. + */ + void visitInitializer(Node initializerNode, JCBlock initializer); + void endVisitInitializer(Node initializerNode, JCBlock initializer); + + /** + * Called for both methods and constructors. + */ + void visitMethod(Node methodNode, JCMethodDecl method); + void visitAnnotationOnMethod(JCMethodDecl method, Node annotationNode, JCAnnotation annotation); + void endVisitMethod(Node methodNode, JCMethodDecl method); + + /** + * Visits a method argument. + */ + void visitMethodArgument(Node argumentNode, JCVariableDecl argument, JCMethodDecl method); + void visitAnnotationOnMethodArgument(JCVariableDecl argument, JCMethodDecl method, Node annotationNode, JCAnnotation annotation); + void endVisitMethodArgument(Node argumentNode, JCVariableDecl argument, JCMethodDecl method); + + /** + * Visits a local declaration - that is, something like 'int x = 10;' on the method level. Also called + * for method parameters. + */ + void visitLocal(Node localNode, JCVariableDecl local); + void visitAnnotationOnLocal(JCVariableDecl local, Node annotationNode, JCAnnotation annotation); + void endVisitLocal(Node localNode, JCVariableDecl local); + + /** + * Visits a statement that isn't any of the other visit methods (e.g. JCClassDecl). + * The statement object is guaranteed to be either a JCStatement or a JCExpression. + */ + void visitStatement(Node statementNode, JCTree statement); + void endVisitStatement(Node statementNode, JCTree statement); + + public static class JavacASTPrinter implements JavacASTVisitor { + int indent = 0; + private void print(String text, Object... params) { + StringBuilder sb = new StringBuilder(); + for ( int i = 0 ; i < indent ; i++ ) sb.append(" "); + System.out.printf(sb.append(text).append('\n').toString(), params); + } + + @Override public void visitCompilationUnit(Node Node, JCCompilationUnit unit) { + System.out.println("---------------------------------------------------------"); + + print("<CU %s>", Node.getFileName()); + indent++; + } + + @Override public void endVisitCompilationUnit(Node node, JCCompilationUnit unit) { + indent--; + print("</CUD>"); + } + + @Override public void visitType(Node node, JCClassDecl type) { + print("<TYPE %s>", type.name); + indent++; + } + + @Override public void visitAnnotationOnType(JCClassDecl type, Node node, JCAnnotation annotation) { + print("<ANNOTATION: %s />", annotation); + } + + @Override public void endVisitType(Node node, JCClassDecl type) { + indent--; + print("</TYPE %s>", type.name); + } + + @Override public void visitInitializer(Node node, JCBlock initializer) { + print("<%s INITIALIZER>", + initializer.isStatic() ? "static" : "instance"); + indent++; + } + + @Override public void endVisitInitializer(Node node, JCBlock initializer) { + indent--; + print("</%s INITIALIZER>", initializer.isStatic() ? "static" : "instance"); + } + + @Override public void visitField(Node node, JCVariableDecl field) { + print("<FIELD %s %s>", field.type, field.name); + indent++; + } + + @Override public void visitAnnotationOnField(JCVariableDecl field, Node node, JCAnnotation annotation) { + print("<ANNOTATION: %s />", annotation); + } + + @Override public void endVisitField(Node node, JCVariableDecl field) { + indent--; + print("</FIELD %s %s>", field.type, field.name); + } + + @Override public void visitMethod(Node node, JCMethodDecl method) { + String type = method.name.contentEquals("<init>") ? "CONSTRUCTOR" : "METHOD"; + print("<%s %s>", type, method.name); + indent++; + } + + @Override public void visitAnnotationOnMethod(JCMethodDecl method, Node node, JCAnnotation annotation) { + print("<ANNOTATION: %s />", annotation); + } + + @Override public void endVisitMethod(Node node, JCMethodDecl method) { + String type = method.name.contentEquals("<init>") ? "CONSTRUCTOR" : "METHOD"; + indent--; + print("</%s %s>", type, method.name); + } + + @Override public void visitMethodArgument(Node node, JCVariableDecl arg, JCMethodDecl method) { + print("<METHOARG %s %s = %s>", arg.type, arg.name); + indent++; + } + + @Override public void visitAnnotationOnMethodArgument(JCVariableDecl arg, JCMethodDecl method, Node nodeAnnotation, JCAnnotation annotation) { + print("<ANNOTATION: %s />", annotation); + } + + @Override public void endVisitMethodArgument(Node node, JCVariableDecl arg, JCMethodDecl method) { + indent--; + print("</METHODARG %s %s>", arg.type, arg.name); + } + + @Override public void visitLocal(Node node, JCVariableDecl local) { + print("<LOCAL %s %s>", local.type, local.name); + indent++; + } + + @Override public void visitAnnotationOnLocal(JCVariableDecl local, Node node, JCAnnotation annotation) { + print("<ANNOTATION: %s />", annotation); + } + + @Override public void endVisitLocal(Node node, JCVariableDecl local) { + indent--; + print("</LOCAL %s %s>", local.type, local.name); + } + + @Override public void visitStatement(Node node, JCTree statement) { + print("<%s>", statement.getClass()); + indent++; + } + + @Override public void endVisitStatement(Node node, JCTree statement) { + indent--; + print("</%s>", statement.getClass()); + } + } +} diff --git a/src/lombok/javac/JavacAnnotationHandler.java b/src/lombok/javac/JavacAnnotationHandler.java index 67542a12..ce3c5fd7 100644 --- a/src/lombok/javac/JavacAnnotationHandler.java +++ b/src/lombok/javac/JavacAnnotationHandler.java @@ -2,6 +2,10 @@ package lombok.javac; import java.lang.annotation.Annotation; +import lombok.core.AnnotationValues; + +import com.sun.tools.javac.tree.JCTree.JCAnnotation; + public interface JavacAnnotationHandler<T extends Annotation> { - void handle(JavacNode annotedElement, T annotation); + void handle(AnnotationValues<T> annotation, JCAnnotation ast, JavacAST.Node annotedElement); } diff --git a/src/lombok/javac/JavacNode.java b/src/lombok/javac/JavacNode.java deleted file mode 100644 index 2e65e1d1..00000000 --- a/src/lombok/javac/JavacNode.java +++ /dev/null @@ -1,63 +0,0 @@ -package lombok.javac; - -import javax.annotation.processing.Messager; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.tools.Diagnostic; - -import com.sun.source.util.Trees; -import com.sun.tools.javac.processing.JavacProcessingEnvironment; -import com.sun.tools.javac.tree.TreeMaker; -import com.sun.tools.javac.tree.JCTree.JCClassDecl; -import com.sun.tools.javac.util.Name; - -public class JavacNode { - private final Element node; - private final Messager messager; - private final JavacProcessingEnvironment env; - private final Trees trees; - - public JavacNode(Trees trees, JavacProcessingEnvironment env, Element node) { - this.trees = trees; - this.env = env; - this.node = node; - this.messager = env.getMessager(); - } - - public Element getJavacAST() { - return node; - } - - public JCClassDecl getEnclosingType() { - Element parent = node; - while ( !(parent instanceof TypeElement) ) parent = node.getEnclosingElement(); - TypeElement classElement = (TypeElement)parent; - return (JCClassDecl)trees.getTree(classElement); - } - - public Name.Table createNameTable() { - return Name.Table.instance(env.getContext()); - } - - public TreeMaker createTreeMaker() { - return TreeMaker.instance(env.getContext()); - } - - public void addError(String message) { - this.messager.printMessage(Diagnostic.Kind.ERROR, message, node); - } - - public void addError(String message, AnnotationMirror mirror, AnnotationValue value) { - this.messager.printMessage(Diagnostic.Kind.ERROR, message, node, mirror, value); - } - - public void addWarning(String message) { - this.messager.printMessage(Diagnostic.Kind.WARNING, message, node); - } - - public void addWarning(String message, AnnotationMirror mirror, AnnotationValue value) { - this.messager.printMessage(Diagnostic.Kind.WARNING, message, node, mirror, value); - } -} diff --git a/src/lombok/javac/apt/Processor.java b/src/lombok/javac/apt/Processor.java index 864ef52c..2780ce16 100644 --- a/src/lombok/javac/apt/Processor.java +++ b/src/lombok/javac/apt/Processor.java @@ -1,5 +1,8 @@ package lombok.javac.apt; +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; import java.util.Set; import javax.annotation.processing.AbstractProcessor; @@ -12,10 +15,17 @@ import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import lombok.javac.HandlerLibrary; -import lombok.javac.JavacNode; +import lombok.javac.JavacAST; +import lombok.javac.JavacASTAdapter; +import lombok.javac.JavacAST.Node; import com.sun.source.util.Trees; import com.sun.tools.javac.processing.JavacProcessingEnvironment; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; @SupportedAnnotationTypes("*") @@ -39,23 +49,53 @@ public class Processor extends AbstractProcessor { if ( processingEnv == null ) return false; - for ( TypeElement annotationType : annotations ) { - if ( !handlers.hasHandlerFor(annotationType) ) continue; - for ( Element element : roundEnv.getElementsAnnotatedWith(annotationType) ) { - handlers.handleAnnotation(createNode(element), annotationType); - } + IdentityHashMap<JCCompilationUnit, Void> units = new IdentityHashMap<JCCompilationUnit, Void>(); + for ( Element element : roundEnv.getRootElements() ) units.put(toUnit(element), null); + + List<JavacAST> asts = new ArrayList<JavacAST>(); + + for ( JCCompilationUnit unit : units.keySet() ) asts.add(new JavacAST(trees, processingEnv, unit)); + + for ( JavacAST ast : asts ) { + ast.traverse(new AnnotationVisitor()); + handlers.handleAST(ast); + } + return false; + } + + private class AnnotationVisitor extends JavacASTAdapter { + @Override public void visitAnnotationOnType(JCClassDecl type, Node annotationNode, JCAnnotation annotation) { + if ( annotationNode.isHandled() ) return; + handlers.handleAnnotation((JCCompilationUnit) annotationNode.top().get(), annotationNode, annotation); + annotationNode.setHandled(); } - for ( Element element : roundEnv.getRootElements() ) { - if ( element instanceof TypeElement ) { - handlers.handleType((TypeElement)element); - } + @Override public void visitAnnotationOnField(JCVariableDecl field, Node annotationNode, JCAnnotation annotation) { + if ( annotationNode.isHandled() ) return; + handlers.handleAnnotation((JCCompilationUnit) annotationNode.top().get(), annotationNode, annotation); + annotationNode.setHandled(); } - return false; + @Override public void visitAnnotationOnMethod(JCMethodDecl method, Node annotationNode, JCAnnotation annotation) { + if ( annotationNode.isHandled() ) return; + handlers.handleAnnotation((JCCompilationUnit) annotationNode.top().get(), annotationNode, annotation); + annotationNode.setHandled(); + } + + @Override public void visitAnnotationOnMethodArgument(JCVariableDecl argument, JCMethodDecl method, Node annotationNode, JCAnnotation annotation) { + if ( annotationNode.isHandled() ) return; + handlers.handleAnnotation((JCCompilationUnit) annotationNode.top().get(), annotationNode, annotation); + annotationNode.setHandled(); + } + + @Override public void visitAnnotationOnLocal(JCVariableDecl local, Node annotationNode, JCAnnotation annotation) { + if ( annotationNode.isHandled() ) return; + handlers.handleAnnotation((JCCompilationUnit) annotationNode.top().get(), annotationNode, annotation); + annotationNode.setHandled(); + } } - private JavacNode createNode(Element element) { - return new JavacNode(trees, processingEnv, element); + private JCCompilationUnit toUnit(Element element) { + return (JCCompilationUnit) trees.getPath(element).getCompilationUnit(); } } diff --git a/src/lombok/javac/handlers/HandleGetter_javac.java b/src/lombok/javac/handlers/HandleGetter_javac.java index 7863a097..8501b65f 100644 --- a/src/lombok/javac/handlers/HandleGetter_javac.java +++ b/src/lombok/javac/handlers/HandleGetter_javac.java @@ -2,17 +2,15 @@ package lombok.javac.handlers; import static lombok.javac.handlers.PKG.*; -import javax.lang.model.element.Element; - import lombok.Getter; +import lombok.core.AnnotationValues; +import lombok.core.AST.Kind; import lombok.javac.JavacAnnotationHandler; -import lombok.javac.JavacNode; +import lombok.javac.JavacAST; import org.mangosdk.spi.ProviderFor; import com.sun.source.tree.MethodTree; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.JCTree.JCAnnotation; @@ -27,26 +25,29 @@ import com.sun.tools.javac.util.Name; @ProviderFor(JavacAnnotationHandler.class) public class HandleGetter_javac implements JavacAnnotationHandler<Getter> { - @Override public void handle(JavacNode node, Getter getter) { - if ( !node.getJavacAST().getKind().isField() ) { + @Override public void handle(AnnotationValues<Getter> annotation, JCAnnotation ast, JavacAST.Node node) { + if ( node.up().getKind() != Kind.FIELD ) { node.addError("@Getter is only supported on a field."); return; } - JCClassDecl javacClassTree = node.getEnclosingType(); + Getter getter = annotation.getInstance(); + + JCClassDecl javacClassTree = (JCClassDecl) node.up().up().get(); int access = toJavacModifier(getter.value()); - MethodTree getterMethod = createGetter(access, node.getJavacAST(), node.createTreeMaker(), node.createNameTable()); + MethodTree getterMethod = createGetter(access, node.up(), node.getTreeMaker()); javacClassTree.defs = javacClassTree.defs.append((JCTree)getterMethod); } - private MethodTree createGetter(int access, Element field, TreeMaker treeMaker, Name.Table nameTable) { - JCStatement returnStatement = treeMaker.Return(treeMaker.Ident((Symbol)field)); + private MethodTree createGetter(int access, JavacAST.Node field, TreeMaker treeMaker) { + JCVariableDecl fieldNode = (JCVariableDecl) field.get(); + JCStatement returnStatement = treeMaker.Return(treeMaker.Ident(fieldNode.getName())); JCBlock methodBody = treeMaker.Block(0, List.of(returnStatement)); - Name methodName = Name.fromString(nameTable, toGetterName(field)); - JCExpression methodType = treeMaker.Type((Type)field.asType()); + Name methodName = field.toName(toGetterName((JCVariableDecl)field.get())); + JCExpression methodType = fieldNode.type != null ? treeMaker.Type(fieldNode.type) : fieldNode.vartype; List<JCTypeParameter> methodGenericParams = List.nil(); List<JCVariableDecl> parameters = List.nil(); diff --git a/src/lombok/javac/handlers/PKG.java b/src/lombok/javac/handlers/PKG.java index 4622c3ee..9ba3765a 100644 --- a/src/lombok/javac/handlers/PKG.java +++ b/src/lombok/javac/handlers/PKG.java @@ -2,17 +2,16 @@ package lombok.javac.handlers; import java.lang.reflect.Modifier; -import javax.lang.model.element.Element; -import javax.lang.model.type.TypeKind; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import lombok.AccessLevel; import lombok.core.TransformationsUtil; class PKG { - static String toGetterName(Element field) { - CharSequence fieldName = field.getSimpleName(); + static String toGetterName(JCVariableDecl field) { + CharSequence fieldName = field.name; - boolean isBoolean = field.asType().getKind() == TypeKind.BOOLEAN; + boolean isBoolean = field.vartype.toString().equals("boolean"); return TransformationsUtil.toGetterName(fieldName, isBoolean); } |