diff options
-rw-r--r-- | src/lombok/eclipse/EclipseAST.java | 337 | ||||
-rw-r--r-- | src/lombok/eclipse/EclipseASTAdapter.java | 25 | ||||
-rw-r--r-- | src/lombok/eclipse/EclipseASTVisitor.java | 159 | ||||
-rw-r--r-- | src/lombok/eclipse/HandleGetter_ecj.java | 26 | ||||
-rw-r--r-- | src/lombok/eclipse/TransformEclipseAST.java | 46 | ||||
-rw-r--r-- | src_eclipseagent/java/lombok/ClassLoaderWorkaround.java | 49 | ||||
-rw-r--r-- | src_eclipseagent/lombok/agent/eclipse/EclipseParserTransformer.java | 59 |
7 files changed, 606 insertions, 95 deletions
diff --git a/src/lombok/eclipse/EclipseAST.java b/src/lombok/eclipse/EclipseAST.java new file mode 100644 index 00000000..63815a99 --- /dev/null +++ b/src/lombok/eclipse/EclipseAST.java @@ -0,0 +1,337 @@ +package lombok.eclipse; + +import static lombok.Lombok.sneakyThrow; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Initializer; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; + +public class EclipseAST { + public void traverse(EclipseASTVisitor visitor) { + Node current = top(); + visitor.visitCompilationUnit(current, (CompilationUnitDeclaration)current.node); + traverseChildren(visitor, current); + visitor.endVisitCompilationUnit(current, (CompilationUnitDeclaration)current.node); + } + + private void traverseChildren(EclipseASTVisitor visitor, Node node) { + for ( Node child : node.children ) { + ASTNode n = child.node; + if ( n instanceof TypeDeclaration ) { + visitor.visitType(child, (TypeDeclaration)n); + traverseChildren(visitor, child); + visitor.endVisitType(child, (TypeDeclaration)n); + } else if ( n instanceof Initializer ) { + visitor.visitInitializer(child, (Initializer)n); + traverseChildren(visitor, child); + visitor.endVisitInitializer(child, (Initializer)n); + } else if ( n instanceof FieldDeclaration ) { + visitor.visitField(child, (FieldDeclaration)n); + traverseChildren(visitor, child); + visitor.endVisitField(child, (FieldDeclaration)n); + } else if ( n instanceof AbstractMethodDeclaration ) { + visitor.visitMethod(child, (AbstractMethodDeclaration)n); + traverseChildren(visitor, child); + visitor.endVisitMethod(child, (AbstractMethodDeclaration)n); + } else if ( n instanceof LocalDeclaration ) { + visitor.visitLocal(child, (LocalDeclaration)n); + traverseChildren(visitor, child); + visitor.endVisitLocal(child, (LocalDeclaration)n); + } else throw new AssertionError("Can't be reached"); + } + } + + public boolean isCompleteParse() { + return completeParse; + } + + public String getFileName() { + return fileName; + } + + public Node top() { + return top; + } + + public Node get(ASTNode node) { + return nodeMap.get(node); + } + + public final class Node { + final ASTNode node; + Node parent; + final Collection<Node> children; + boolean handled; + + Node(ASTNode node, Collection<Node> children) { + this.node = node; + this.children = children == null ? Collections.<Node>emptyList() : children; + } + + public ASTNode getEclipseNode() { + return node; + } + + public Node up() { + return parent; + } + + public Collection<Node> down() { + return children; + } + + public boolean isHandled() { + return handled; + } + + public Node setHandled() { + this.handled = true; + return this; + } + + public Node top() { + return top; + } + + public String getFileName() { + return fileName; + } + + public boolean isCompleteParse() { + return completeParse; + } + } + + private final Map<ASTNode, Void> identityDetector = new IdentityHashMap<ASTNode, Void>(); + private Map<ASTNode, Node> nodeMap = new HashMap<ASTNode, Node>(); + private final CompilationUnitDeclaration compilationUnitDeclaration; + private final String fileName; + private Node top; + private boolean completeParse; + + public EclipseAST(CompilationUnitDeclaration ast) { + this.compilationUnitDeclaration = ast; + this.fileName = ast.compilationResult.fileName == null ? "(unknown).java" : new String(ast.compilationResult.fileName); + this.top = buildTree(ast); + this.completeParse = isComplete(ast); + } + + public void reparse() { + if ( completeParse ) return; + boolean newCompleteParse = isComplete(compilationUnitDeclaration); + if ( !newCompleteParse ) return; + Map<ASTNode, Node> oldMap = nodeMap; + nodeMap = new HashMap<ASTNode, Node>(); + this.top = buildTree(compilationUnitDeclaration); + + //Retain 'handled' flags. + for ( Map.Entry<ASTNode, Node> e : nodeMap.entrySet() ) { + Node oldEntry = oldMap.get(e.getKey()); + if ( oldEntry != null && oldEntry.handled ) e.getValue().handled = true; + } + + this.completeParse = true; + } + + private static boolean isComplete(CompilationUnitDeclaration unit) { + return (unit.bits & ASTNode.HasAllMethodBodies) > 0; + } + + private Node putInMap(Node parent) { + for ( Node child : parent.children ) child.parent = parent; + nodeMap.put(parent.node, parent); + identityDetector.put(parent.node, null); + return parent; + } + + private Node buildTree(CompilationUnitDeclaration top) { + identityDetector.clear(); + Collection<Node> children = buildTree(top.types); + return putInMap(new Node(top, children)); + } + + private void addIfNotNull(Collection<Node> collection, Node n) { + if ( n != null ) collection.add(n); + } + + private Collection<Node> buildTree(TypeDeclaration[] children) { + if ( children == null ) return Collections.emptyList(); + List<Node> childNodes = new ArrayList<Node>(); + for ( TypeDeclaration type : children ) addIfNotNull(childNodes, buildTree(type)); + return childNodes; + } + + private Node buildTree(TypeDeclaration type) { + if ( identityDetector.containsKey(type) ) return null; + List<Node> childNodes = new ArrayList<Node>(); + childNodes.addAll(buildTree(type.fields)); + childNodes.addAll(buildTree(type.memberTypes)); + childNodes.addAll(buildTree(type.methods)); + return putInMap(new Node(type, childNodes)); + } + + private Collection<Node> buildTree(FieldDeclaration[] children) { + if ( children == null ) return Collections.emptyList(); + List<Node> childNodes = new ArrayList<Node>(); + for ( FieldDeclaration child : children ) addIfNotNull(childNodes, buildTree(child)); + return childNodes; + } + + private Node buildTree(FieldDeclaration field) { + if ( field instanceof Initializer ) return buildTree((Initializer)field); + if ( identityDetector.containsKey(field) ) return null; + return putInMap(new Node(field, buildWithStatement(field.initialization))); + } + + private Node buildTree(Initializer initializer) { + if ( identityDetector.containsKey(initializer) ) return null; + return putInMap(new Node(initializer, buildWithStatement(initializer.block))); + } + + private Collection<Node> buildTree(AbstractMethodDeclaration[] children) { + if ( children == null ) return Collections.emptyList(); + List<Node> childNodes = new ArrayList<Node>(); + for (AbstractMethodDeclaration method : children ) addIfNotNull(childNodes, buildTree(method)); + return childNodes; + } + + private Node buildTree(AbstractMethodDeclaration method) { + if ( identityDetector.containsKey(method) ) return null; + List<Node> childNodes = new ArrayList<Node>(); + childNodes.addAll(buildTree(method.arguments)); + childNodes.addAll(buildTree(method.statements)); + return putInMap(new Node(method, childNodes)); + } + + //Arguments are a kind of LocalDeclaration. They can definitely contain lombok annotations, so we care about them. + private Collection<Node> buildTree(Argument[] children) { + if ( children == null ) return Collections.emptyList(); + List<Node> childNodes = new ArrayList<Node>(); + for ( LocalDeclaration local : children ) { + if ( !identityDetector.containsKey(local) ) { + addIfNotNull(childNodes, buildTree(local)); + childNodes.addAll(buildWithStatement(local.initialization)); + } + } + return childNodes; + } + + private Node buildTree(LocalDeclaration local) { + if ( identityDetector.containsKey(local) ) return null; + return putInMap(new Node(local, null)); + } + + private Collection<Node> buildTree(Statement[] children) { + if ( children == null ) return Collections.emptyList(); + List<Node> childNodes = new ArrayList<Node>(); + for ( Statement child : children ) childNodes.addAll(buildWithStatement(child)); + return childNodes; + } + + //Almost anything is a statement, so this method has a different name to avoid overloading confusion + private Collection<Node> buildWithStatement(Statement child) { + if ( child == null || identityDetector.containsKey(child) ) return Collections.emptyList(); + if ( child instanceof TypeDeclaration ) { + Node n = buildTree((TypeDeclaration)child); + return n == null ? Collections.<Node>emptyList() : Collections.singleton(n); + } + + if ( child instanceof LocalDeclaration ) { + List<Node> childNodes = new ArrayList<Node>(); + addIfNotNull(childNodes, buildTree((LocalDeclaration)child)); + identityDetector.put(child, null); + childNodes.addAll(buildWithStatement(((LocalDeclaration)child).initialization)); + return childNodes; + } + //We drill down because LocalDeclarations and TypeDeclarations can occur anywhere, even in, say, + //an if block, or even the expression on an assert statement! + + identityDetector.put(child, null); + return drill(child); + } + + private Collection<Node> drill(Statement child) { + List<Node> childNodes = new ArrayList<Node>(); + for ( FieldAccess fa : fieldsOf(child.getClass()) ) childNodes.addAll(buildWithField(child, fa)); + return childNodes; + } + + private static class FieldAccess { + final Field field; + final int dim; + + FieldAccess(Field field, int dim) { + this.field = field; + this.dim = dim; + } + } + private Map<Class<?>, Collection<FieldAccess>> fieldsOfASTClasses = new HashMap<Class<?>, Collection<FieldAccess>>(); + private Collection<FieldAccess> fieldsOf(Class<?> c) { + Collection<FieldAccess> fields = fieldsOfASTClasses.get(c); + if ( fields != null ) return fields; + + fields = new ArrayList<FieldAccess>(); + getFields(c, fields); + fieldsOfASTClasses.put(c, fields); + return fields; + } + + private void getFields(Class<?> c, Collection<FieldAccess> fields) { + if ( c == ASTNode.class || c == null ) return; + for ( Field f : c.getDeclaredFields() ) { + Class<?> t = f.getType(); + int dim = 0; + while ( t.isArray() ) { + dim++; + t = t.getComponentType(); + } + if ( Statement.class.isAssignableFrom(t) ) { + f.setAccessible(true); + fields.add(new FieldAccess(f, dim)); + } + } + getFields(c.getSuperclass(), fields); + } + + private Collection<Node> buildWithField(Statement child, FieldAccess fa) { + if ( Modifier.isStatic(fa.field.getModifiers()) ) return Collections.emptyList(); + List<Node> list = new ArrayList<Node>(); + buildWithField(child, fa, list); + return list; + } + + private void buildWithField(Statement child, FieldAccess fa, Collection<Node> list) { + try { + Object o = fa.field.get(child); + if ( fa.dim == 0 ) list.addAll(buildWithStatement((Statement)o)); + else buildWithArray(o, list, fa.dim); + } catch ( IllegalAccessException e ) { + sneakyThrow(e); + } + } + + private void buildWithArray(Object array, Collection<Node> list, int dim) { + if ( array == null ) return; + if ( dim == 1 ) for ( Object v : (Object[])array ) { + list.addAll(buildWithStatement((Statement)v)); + } else for ( Object v : (Object[])array ) { + buildWithArray(v, list, dim-1); + } + } +} diff --git a/src/lombok/eclipse/EclipseASTAdapter.java b/src/lombok/eclipse/EclipseASTAdapter.java new file mode 100644 index 00000000..fa87872f --- /dev/null +++ b/src/lombok/eclipse/EclipseASTAdapter.java @@ -0,0 +1,25 @@ +package lombok.eclipse; + +import lombok.eclipse.EclipseAST.Node; + +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Initializer; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; + +public abstract class EclipseASTAdapter implements EclipseASTVisitor { + @Override public void visitCompilationUnit(Node node, CompilationUnitDeclaration unit) {} + @Override public void endVisitCompilationUnit(Node node, CompilationUnitDeclaration unit) {} + @Override public void visitType(Node node, TypeDeclaration type) {} + @Override public void endVisitType(Node node, TypeDeclaration type) {} + @Override public void visitInitializer(Node node, Initializer initializer) {} + @Override public void endVisitInitializer(Node node, Initializer initializer) {} + @Override public void visitField(Node node, FieldDeclaration field) {} + @Override public void endVisitField(Node node, FieldDeclaration field) {} + @Override public void visitMethod(Node node, AbstractMethodDeclaration declaration) {} + @Override public void endVisitMethod(Node node, AbstractMethodDeclaration declaration) {} + @Override public void visitLocal(Node node, LocalDeclaration declaration) {} + @Override public void endVisitLocal(Node node, LocalDeclaration declaration) {} +} diff --git a/src/lombok/eclipse/EclipseASTVisitor.java b/src/lombok/eclipse/EclipseASTVisitor.java new file mode 100644 index 00000000..aff1cc38 --- /dev/null +++ b/src/lombok/eclipse/EclipseASTVisitor.java @@ -0,0 +1,159 @@ +package lombok.eclipse; + +import java.lang.reflect.Modifier; + +import lombok.eclipse.EclipseAST.Node; + +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.Block; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Initializer; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; + +public interface EclipseASTVisitor { + /** + * Called at the very beginning and end. + */ + void visitCompilationUnit(Node node, CompilationUnitDeclaration unit); + void endVisitCompilationUnit(Node node, CompilationUnitDeclaration unit); + + /** + * Called when visiting a type (a class, interface, annotation, enum, etcetera). + */ + void visitType(Node node, TypeDeclaration type); + void endVisitType(Node node, TypeDeclaration type); + + /** + * Called when visiting a field of a class. + * Even though in eclipse initializers (both instance and static) are represented as Initializer objects, + * which are a subclass of FieldDeclaration, those do NOT result in a call to this method. They result + * in a call to the visitInitializer method. + */ + void visitField(Node node, FieldDeclaration field); + void endVisitField(Node node, FieldDeclaration field); + + /** + * Called for static and instance initializers. You can tell the difference via the modifier flag on the + * ASTNode (8 for static, 0 for not static). The content is in the 'block', not in the 'initialization', + * which would always be null for an initializer instance. + */ + void visitInitializer(Node node, Initializer initializer); + void endVisitInitializer(Node node, Initializer initializer); + + /** + * Called for both methods (MethodDeclaration) and constructors (ConstructorDeclaration), but not for + * Clinit objects, which are a vestigial eclipse thing that never contain anything. Static initializers + * show up as 'Initializer', in the visitInitializer method, with modifier bit STATIC set. + */ + void visitMethod(Node node, AbstractMethodDeclaration declaration); + void endVisitMethod(Node node, AbstractMethodDeclaration declaration); + + /** + * Visits a local declaration - that is, something like 'int x = 10;' on the method level. Also called + * for method parameter (those would be Arguments, a subclass of LocalDeclaration). + */ + void visitLocal(Node node, LocalDeclaration declaration); + void endVisitLocal(Node node, LocalDeclaration declaration); + + public static class EclipseASTPrinter implements EclipseASTVisitor { + 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); + } + + private String str(char[] c) { + if ( c == null ) return "(NULL)"; + else return new String(c); + } + + private String str(TypeReference type) { + if ( type == null ) return "(NULL)"; + char[][] c = type.getTypeName(); + StringBuilder sb = new StringBuilder(); + boolean first = true; + for ( char[] d : c ) { + sb.append(first ? "" : ".").append(new String(d)); + first = false; + } + return sb.toString(); + } + + @Override public void visitCompilationUnit(Node node, CompilationUnitDeclaration unit) { + System.out.println("---------------------------------------------------------"); + System.out.println(node.isCompleteParse() ? "COMPLETE" : "incomplete"); + + print("<CUD %s>", node.getFileName()); + indent++; + } + + @Override public void endVisitCompilationUnit(Node node, CompilationUnitDeclaration unit) { + indent--; + print("</CUD>"); + } + + @Override public void visitType(Node node, TypeDeclaration type) { + print("<TYPE %s>", str(type.name)); + indent++; + } + + @Override public void endVisitType(Node node, TypeDeclaration type) { + indent--; + print("</TYPE %s>", str(type.name)); + } + + @Override public void visitInitializer(Node node, Initializer initializer) { + Block block = initializer.block; + boolean s = (block != null && block.statements != null); + print("<%s INITIALIZER: %s>", + (initializer.modifiers & Modifier.STATIC) > 0 ? "static" : "instance", + s ? "filled" : "blank"); + indent++; + } + + @Override public void endVisitInitializer(Node node, Initializer initializer) { + indent--; + print("</%s INITIALIZER>", (initializer.modifiers & Modifier.STATIC) > 0 ? "static" : "instance"); + } + + @Override public void visitField(Node node, FieldDeclaration field) { + print("<FIELD %s %s = %s>", str(field.type), str(field.name), field.initialization); + indent++; + } + + @Override public void endVisitField(Node node, FieldDeclaration field) { + indent--; + print("</FIELD %s %s>", str(field.type), str(field.name)); + } + + @Override public void visitMethod(Node node, AbstractMethodDeclaration method) { + String type = method instanceof ConstructorDeclaration ? "CONSTRUCTOR" : "METHOD"; + print("<%s %s: %s>", type, str(method.selector), method.statements != null ? "filled" : "blank"); + indent++; + } + + @Override public void endVisitMethod(Node node, AbstractMethodDeclaration method) { + String type = method instanceof ConstructorDeclaration ? "CONSTRUCTOR" : "METHOD"; + indent--; + print("</%s %s>", type, str(method.selector)); + } + + @Override public void visitLocal(Node node, LocalDeclaration local) { + String type = local instanceof Argument ? "ARGUMENT" : "LOCAL"; + print("<%s %s %s = %s>", type, str(local.type), str(local.name), local.initialization); + indent++; + } + + @Override public void endVisitLocal(Node node, LocalDeclaration local) { + String type = local instanceof Argument ? "ARGUMENT" : "LOCAL"; + indent--; + print("</%s %s %s>", type, str(local.type), str(local.name)); + } + } +} diff --git a/src/lombok/eclipse/HandleGetter_ecj.java b/src/lombok/eclipse/HandleGetter_ecj.java index f71376a3..fdfbd7be 100644 --- a/src/lombok/eclipse/HandleGetter_ecj.java +++ b/src/lombok/eclipse/HandleGetter_ecj.java @@ -2,6 +2,7 @@ package lombok.eclipse; import java.lang.reflect.Modifier; +import lombok.eclipse.EclipseAST.Node; import lombok.transformations.TransformationsUtil; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -18,15 +19,17 @@ import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; public class HandleGetter_ecj { - public void apply(Annotation annotation, TypeDeclaration type, FieldDeclaration field) { + public void apply(Annotation annotation, Node node, FieldDeclaration field) { TypeReference fieldType = field.type; - String getterName = TransformationsUtil.toGetterName(new String(field.name), nameEquals(fieldType.getTypeName(), "boolean")); + String getterName = TransformationsUtil.toGetterName( + new String(field.name), nameEquals(fieldType.getTypeName(), "boolean")); - for ( AbstractMethodDeclaration method : type.methods ) { + TypeDeclaration parent = (TypeDeclaration) node.up().getEclipseNode(); + if ( parent.methods != null ) for ( AbstractMethodDeclaration method : parent.methods ) { if ( method.selector != null && new String(method.selector).equals(getterName) ) return; } - MethodDeclaration method = new MethodDeclaration(type.compilationResult); + MethodDeclaration method = new MethodDeclaration(parent.compilationResult); method.modifiers = Modifier.PUBLIC; method.returnType = field.type; method.annotations = null; @@ -35,17 +38,22 @@ public class HandleGetter_ecj { method.binding = null; method.thrownExceptions = null; method.typeParameters = null; - method.scope = new MethodScope(type.scope, method, false); + method.scope = parent.scope == null ? null : new MethodScope(parent.scope, method, false); method.bits |= ASTNode.Bit24; Expression fieldExpression = new SingleNameReference(field.name, (field.declarationSourceStart << 32) | field.declarationSourceEnd); Statement returnStatement = new ReturnStatement(fieldExpression, field.sourceStart, field.sourceEnd); method.bodyStart = method.declarationSourceStart = method.sourceStart = annotation.sourceStart; method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = annotation.sourceEnd; method.statements = new Statement[] { returnStatement }; - AbstractMethodDeclaration[] newArray = new AbstractMethodDeclaration[type.methods.length + 1]; - System.arraycopy(type.methods, 0, newArray, 0, type.methods.length); - newArray[type.methods.length] = method; - type.methods = newArray; + if ( parent.methods == null ) { + parent.methods = new AbstractMethodDeclaration[1]; + parent.methods[0] = method; + } else { + AbstractMethodDeclaration[] newArray = new AbstractMethodDeclaration[parent.methods.length + 1]; + System.arraycopy(parent.methods, 0, newArray, 0, parent.methods.length); + newArray[parent.methods.length] = method; + parent.methods = newArray; + } } private boolean nameEquals(char[][] typeName, String string) { diff --git a/src/lombok/eclipse/TransformEclipseAST.java b/src/lombok/eclipse/TransformEclipseAST.java index 9212a0d4..498b403f 100644 --- a/src/lombok/eclipse/TransformEclipseAST.java +++ b/src/lombok/eclipse/TransformEclipseAST.java @@ -1,12 +1,14 @@ package lombok.eclipse; +import java.util.Map; +import java.util.WeakHashMap; + +import lombok.eclipse.EclipseAST.Node; + import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; -import org.eclipse.jdt.internal.compiler.ast.Initializer; -import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; -import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.parser.Parser; /** @@ -23,6 +25,9 @@ import org.eclipse.jdt.internal.compiler.parser.Parser; * @author rspilker */ public class TransformEclipseAST { + private static final Map<CompilationUnitDeclaration, EclipseAST> astCache = + new WeakHashMap<CompilationUnitDeclaration, EclipseAST>(); + /** * This method is called immediately after eclipse finishes building a CompilationUnitDeclaration, which is * the top-level AST node when eclipse parses a source file. The signature is 'magic' - you should not @@ -35,26 +40,25 @@ public class TransformEclipseAST { * @param ast The AST node belonging to the compilation unit (java speak for a single source file). */ public static void transform(Parser parser, CompilationUnitDeclaration ast) { - if ( ast.types != null ) for ( TypeDeclaration type : ast.types ) { - if ( type.fields != null ) for ( FieldDeclaration field : type.fields ) { - if ( field.annotations != null ) for ( Annotation annotation : field.annotations ) { - if ( annotation.type.toString().equals("Getter") ) { - new HandleGetter_ecj().apply(annotation, type, field); - } - } - } - } - } - - public static void transform(Parser parser, MethodDeclaration ast) { - - } - - public static void transform(Parser parser, ConstructorDeclaration ast) { + EclipseAST existing = astCache.get(ast); + if ( existing == null ) { + existing = new EclipseAST(ast); + astCache.put(ast, existing); + } else existing.reparse(); + existing.traverse(new AnnotationVisitor()); } - public static void transform(Parser parser, Initializer ast) { + private static class AnnotationVisitor extends EclipseASTAdapter { + @Override public void visitField(Node node, FieldDeclaration field) { + if ( field.annotations == null ) return; + for ( Annotation annotation : field.annotations ) { + TypeReference type = annotation.type; + if ( type != null && new String(type.getLastToken()).equals("Getter") ) { + new HandleGetter_ecj().apply(annotation, node, field); + } + } + } } } diff --git a/src_eclipseagent/java/lombok/ClassLoaderWorkaround.java b/src_eclipseagent/java/lombok/ClassLoaderWorkaround.java index 0238b592..a3fc1521 100644 --- a/src_eclipseagent/java/lombok/ClassLoaderWorkaround.java +++ b/src_eclipseagent/java/lombok/ClassLoaderWorkaround.java @@ -3,8 +3,10 @@ package java.lombok; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; + /** * Allows you to inject the lombok classes into any classloader, even if that classloader does not * know how to find the lombok classes. @@ -14,35 +16,32 @@ import java.lang.reflect.Method; * @author rzwitserloot */ public class ClassLoaderWorkaround { - private static boolean initialized; - private static Method transformCompilationUnitDeclaration; - private static Method transformMethodDeclaration; - private static Method transformConstructorDeclaration; - private static Method transformInitializer; - - public static void transformCompilationUnitDeclaration(Object parser, Object cud) throws Exception { - initialize(cud); - transformCompilationUnitDeclaration.invoke(null, parser, cud); + static RuntimeException sneakyThrow(Throwable t) { + if ( t == null ) throw new NullPointerException("t"); + ClassLoaderWorkaround.<RuntimeException>sneakyThrow0(t); + return null; } - public static void transformMethodDeclaration(Object parser, Object methodDeclaration) throws Exception { - initialize(methodDeclaration); - transformMethodDeclaration.invoke(null, parser, methodDeclaration); + @SuppressWarnings("unchecked") + private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T { + throw (T)t; } - public static void transformConstructorDeclaration(Object parser, Object constructorDeclaration) throws Exception { - initialize(constructorDeclaration); - transformConstructorDeclaration.invoke(null, parser, constructorDeclaration); - } + private static boolean initialized; + private static Method transform; - public static void transformInitializer(Object parser, Object initializer) throws Exception { - initialize(initializer); - transformInitializer.invoke(null, parser, initializer); + public static void transformCompilationUnitDeclaration(Object parser, Object cud) throws Exception { + initialize(cud); + try { + transform.invoke(null, parser, cud); + } catch ( InvocationTargetException e ) { + throw sneakyThrow(e.getCause()); + } } private static void initialize(Object cud) throws ClassNotFoundException { if ( initialized ) { - if ( transformInitializer == null ) throw new ClassNotFoundException("lombok.eclipse.TransformEclipseAST"); + if ( transform == null ) throw new ClassNotFoundException("lombok.eclipse.TransformEclipseAST"); return; } @@ -87,15 +86,7 @@ public class ClassLoaderWorkaround { Class<?> c = loader.loadClass("lombok.eclipse.TransformEclipseAST"); for ( Method m : c.getMethods() ) { if ( m.getName().equals("transform") ) { - if ( m.getParameterTypes().length >= 2 ) { - Class<?> astType = m.getParameterTypes()[1]; - String astName = astType.getName(); - astName = astName.substring(astName.lastIndexOf('.') + 1); - if ( astName.equals("CompilationUnitDeclaration") ) transformCompilationUnitDeclaration = m; - else if ( astName.equals("MethodDeclaration") ) transformMethodDeclaration = m; - else if ( astName.equals("ConstructorDeclaration") ) transformConstructorDeclaration = m; - else if ( astName.equals("Initializer") ) transformInitializer = m; - } + transform = m; } } } catch ( ClassNotFoundException ignore ) {} diff --git a/src_eclipseagent/lombok/agent/eclipse/EclipseParserTransformer.java b/src_eclipseagent/lombok/agent/eclipse/EclipseParserTransformer.java index c0e64061..4a1b4a01 100644 --- a/src_eclipseagent/lombok/agent/eclipse/EclipseParserTransformer.java +++ b/src_eclipseagent/lombok/agent/eclipse/EclipseParserTransformer.java @@ -19,6 +19,7 @@ class EclipseParserTransformer { private static final String COMPILER_PKG = "Lorg/eclipse/jdt/internal/compiler/ast/"; private static final String TARGET_STATIC_CLASS = "java/lombok/ClassLoaderWorkaround"; + private static final String TARGET_STATIC_METHOD_NAME = "transformCompilationUnitDeclaration"; private static final String TARGET_STATIC_METHOD_DESC = "(Ljava/lang/Object;Ljava/lang/Object;)V"; private static final Map<String, Class<? extends MethodVisitor>> rewriters; @@ -26,9 +27,10 @@ class EclipseParserTransformer { static { Map<String, Class<? extends MethodVisitor>> map = new HashMap<String, Class<? extends MethodVisitor>>(); map.put(String.format("endParse(I)%sCompilationUnitDeclaration;", COMPILER_PKG), EndParsePatcher.class); - map.put(String.format("parse(%1$sMethodDeclaration;%1$sCompilationUnitDeclaration;)V", COMPILER_PKG), ParseMethodPatcher.class); - map.put(String.format("parse(%1$sConstructorDeclaration;%1$sCompilationUnitDeclaration;Z)V", COMPILER_PKG), ParseConstructorPatcher.class); - map.put(String.format("parse(%1$sInitializer;%1$sTypeDeclaration;%1$sCompilationUnitDeclaration;)V", COMPILER_PKG), ParseInitializerPatcher.class); + map.put(String.format("getMethodBodies(%sCompilationUnitDeclaration;)V", COMPILER_PKG), GetMethodBodiesPatcher.class); + map.put(String.format("parse(%1$sMethodDeclaration;%1$sCompilationUnitDeclaration;)V", COMPILER_PKG), ParseBlockContainerPatcher.class); + map.put(String.format("parse(%1$sConstructorDeclaration;%1$sCompilationUnitDeclaration;Z)V", COMPILER_PKG), ParseBlockContainerPatcher.class); + map.put(String.format("parse(%1$sInitializer;%1$sTypeDeclaration;%1$sCompilationUnitDeclaration;)V", COMPILER_PKG), ParseBlockContainerPatcher.class); rewriters = Collections.unmodifiableMap(map); } @@ -85,12 +87,26 @@ class EclipseParserTransformer { private static final int BIT24 = 0x800000; - static class ParseBlockContainerPatcher extends MethodAdapter { - private final String staticMethodName; + static class GetMethodBodiesPatcher extends MethodAdapter { + GetMethodBodiesPatcher(MethodVisitor mv) { + super(mv); + } - ParseBlockContainerPatcher(MethodVisitor mv, String staticMethodName) { + @Override public void visitInsn(int opcode) { + if ( opcode == Opcodes.RETURN ) { + //injects: ClassLoaderWorkaround.transformCUD(parser, compilationUnitDeclaration); + super.visitVarInsn(Opcodes.ALOAD, 0); + super.visitVarInsn(Opcodes.ALOAD, 1); + super.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET_STATIC_CLASS, + TARGET_STATIC_METHOD_NAME, TARGET_STATIC_METHOD_DESC); + } + super.visitInsn(opcode); + } + } + + static class ParseBlockContainerPatcher extends MethodAdapter { + ParseBlockContainerPatcher(MethodVisitor mv) { super(mv); - this.staticMethodName = staticMethodName; } @Override public void visitCode() { @@ -106,35 +122,6 @@ class EclipseParserTransformer { mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); super.visitCode(); } - - @Override public void visitInsn(int opcode) { - if ( opcode == Opcodes.RETURN ) { - //injects: ClassLoaderWorkaround.transformConstructorDeclaration(parser, constructorDeclaration); - super.visitVarInsn(Opcodes.ALOAD, 0); - super.visitVarInsn(Opcodes.ALOAD, 1); - super.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET_STATIC_CLASS, - staticMethodName, TARGET_STATIC_METHOD_DESC); - } - super.visitInsn(opcode); - } - } - - static class ParseConstructorPatcher extends ParseBlockContainerPatcher { - public ParseConstructorPatcher(MethodVisitor mv) { - super(mv, "transformConstructorDeclaration"); - } - } - - static class ParseMethodPatcher extends ParseBlockContainerPatcher { - public ParseMethodPatcher(MethodVisitor mv) { - super(mv, "transformMethodDeclaration"); - } - } - - static class ParseInitializerPatcher extends ParseBlockContainerPatcher { - public ParseInitializerPatcher(MethodVisitor mv) { - super(mv, "transformInitializer"); - } } static class EndParsePatcher extends MethodAdapter { |