aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lombok/core/AST.java103
-rw-r--r--src/lombok/eclipse/EclipseAST.java18
-rw-r--r--src/lombok/eclipse/handlers/HandleCleanup.java133
-rw-r--r--src/lombok/javac/JavacAST.java33
4 files changed, 166 insertions, 121 deletions
diff --git a/src/lombok/core/AST.java b/src/lombok/core/AST.java
index 871f24be..f4d4d628 100644
--- a/src/lombok/core/AST.java
+++ b/src/lombok/core/AST.java
@@ -2,6 +2,7 @@ package lombok.core;
import static lombok.Lombok.sneakyThrow;
+import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
@@ -93,12 +94,12 @@ public abstract class AST<N> {
public abstract class Node {
protected final Kind kind;
protected final N node;
- protected final Collection<? extends Node> children;
+ protected final List<? extends Node> children;
protected Node parent;
protected boolean handled;
protected boolean isStructurallySignificant;
- protected Node(N node, Collection<? extends Node> children, Kind kind) {
+ protected Node(N node, List<? extends Node> children, Kind kind) {
this.kind = kind;
this.node = node;
this.children = children == null ? Collections.<Node>emptyList() : children;
@@ -129,6 +130,22 @@ public abstract class AST<N> {
return node;
}
+ @SuppressWarnings("unchecked")
+ public Node replaceWith(N newN, Kind kind) {
+ Node newNode = buildTree(newN, kind);
+ newNode.parent = parent;
+ for ( int i = 0 ; i < parent.children.size() ; i++ ) {
+ if ( parent.children.get(i) == this ) ((List)parent.children).set(i, newNode);
+ }
+
+ parent.replaceChildNode(get(), newN);
+ return newNode;
+ }
+
+ public void replaceChildNode(N oldN, N newN) {
+ replaceStatementInNode(get(), oldN, newN);
+ }
+
public Kind getKind() {
return kind;
}
@@ -204,7 +221,6 @@ public abstract class AST<N> {
private void gatherAndRemoveChildren(Map<N, Node> map) {
for ( Node child : children ) child.gatherAndRemoveChildren(map);
map.put(get(), this);
- children.clear();
identityDetector.remove(get());
nodeMap.remove(get());
}
@@ -301,6 +317,87 @@ public abstract class AST<N> {
return list;
}
+ protected boolean replaceStatementInNode(N statement, N oldN, N newN) {
+ for ( FieldAccess fa : fieldsOf(statement.getClass()) ) {
+ if ( replaceStatementInField(fa, statement, oldN, newN) ) return true;
+ }
+
+ return false;
+ }
+
+ private boolean replaceStatementInField(FieldAccess fa, N statement, N oldN, N newN) {
+ try {
+ Object o = fa.field.get(statement);
+ if ( o == null ) return false;
+
+ if ( o == oldN ) {
+ fa.field.set(statement, newN);
+ return true;
+ }
+
+ if ( fa.dim > 0 ) {
+ if ( o.getClass().isArray() ) {
+ return replaceStatementInArray(o, oldN, newN);
+ } else if ( Collection.class.isInstance(o) ) {
+ return replaceStatementInCollection(fa.field, statement, new ArrayList<Collection<?>>(), (Collection<?>)o, oldN, newN);
+ }
+ }
+
+ return false;
+ } catch ( IllegalAccessException e ) {
+ throw sneakyThrow(e);
+ }
+
+ }
+
+ private boolean replaceStatementInCollection(Field field, Object fieldRef, List<Collection<?>> chain, Collection<?> collection, N oldN, N newN) throws IllegalAccessException {
+ if ( collection == null ) return false;
+
+ int idx = -1;
+ for ( Object o : collection ) {
+ idx++;
+ if ( o == null ) continue;
+ if ( Collection.class.isInstance(o) ) {
+ Collection<?> newC = (Collection<?>)o;
+ List<Collection<?>> newChain = new ArrayList<Collection<?>>(chain);
+ newChain.add(newC);
+ if ( replaceStatementInCollection(field, fieldRef, newChain, newC, oldN, newN) ) return true;
+ }
+ if ( o == oldN ) {
+ setElementInASTCollection(field, fieldRef, chain, collection, idx, newN);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** Override if your AST collection does not support the set method. Javac's for example, does not. */
+ @SuppressWarnings("unchecked")
+ protected void setElementInASTCollection(Field field, Object fieldRef, List<Collection<?>> chain, Collection<?> collection, int idx, N newN) throws IllegalAccessException {
+ if ( collection instanceof List<?> ) {
+ ((List)collection).set(idx, newN);
+ }
+ }
+
+ private boolean replaceStatementInArray(Object array, N oldN, N newN) {
+ if ( array == null ) return false;
+
+ int len = Array.getLength(array);
+ for ( int i = 0 ; i < len ; i++ ) {
+ Object o = Array.get(array, i);
+ if ( o == null ) continue;
+ if ( o.getClass().isArray() ) {
+ if ( replaceStatementInArray(o, oldN, newN) ) return true;
+ } else if ( o == oldN ) {
+ Array.set(array, i, newN);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
@SuppressWarnings("unchecked")
private <T extends Node> void buildWithField0(Class<T> nodeType, N child, FieldAccess fa, Collection<T> list) {
try {
diff --git a/src/lombok/eclipse/EclipseAST.java b/src/lombok/eclipse/EclipseAST.java
index c5bdd2ff..5b2b153d 100644
--- a/src/lombok/eclipse/EclipseAST.java
+++ b/src/lombok/eclipse/EclipseAST.java
@@ -143,7 +143,7 @@ public class EclipseAST extends AST<ASTNode> {
}
public final class Node extends AST<ASTNode>.Node {
- Node(ASTNode node, Collection<Node> children, Kind kind) {
+ Node(ASTNode node, List<Node> children, Kind kind) {
super(node, children, kind);
}
@@ -300,7 +300,7 @@ public class EclipseAST extends AST<ASTNode> {
private static String toFileName(CompilationUnitDeclaration ast) {
return ast.compilationResult.fileName == null ? null : new String(ast.compilationResult.fileName);
}
-
+
public void reparse() {
propagateProblems();
if ( completeParse ) return;
@@ -349,7 +349,7 @@ public class EclipseAST extends AST<ASTNode> {
}
private Node buildCompilationUnit(CompilationUnitDeclaration top) {
- Collection<Node> children = buildTypes(top.types);
+ List<Node> children = buildTypes(top.types);
return putInMap(new Node(top, children, Kind.COMPILATION_UNIT));
}
@@ -357,7 +357,7 @@ public class EclipseAST extends AST<ASTNode> {
if ( n != null ) collection.add(n);
}
- private Collection<Node> buildTypes(TypeDeclaration[] children) {
+ private List<Node> buildTypes(TypeDeclaration[] children) {
if ( children == null ) return Collections.emptyList();
List<Node> childNodes = new ArrayList<Node>();
for ( TypeDeclaration type : children ) addIfNotNull(childNodes, buildType(type));
@@ -381,9 +381,9 @@ public class EclipseAST extends AST<ASTNode> {
return childNodes;
}
- private static <T> Collection<T> singleton(T item) {
+ private static <T> List<T> singleton(T item) {
if ( item == null ) return Collections.emptyList();
- else return Collections.singleton(item);
+ else return Collections.singletonList(item);
}
private Node buildField(FieldDeclaration field) {
@@ -454,21 +454,17 @@ public class EclipseAST extends AST<ASTNode> {
return childNodes;
}
- //Almost anything is a statement, so this method has a different name to avoid overloading confusion
private Node buildStatement(Statement child) {
if ( child == null || alreadyHandled(child) ) return null;
if ( child instanceof TypeDeclaration ) return buildType((TypeDeclaration)child);
if ( child instanceof LocalDeclaration ) return buildLocal((LocalDeclaration)child, Kind.LOCAL);
- //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(child);
return drill(child);
}
- protected Node drill(Statement statement) {
+ private Node drill(Statement 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));
diff --git a/src/lombok/eclipse/handlers/HandleCleanup.java b/src/lombok/eclipse/handlers/HandleCleanup.java
index f381f25e..867bd0e5 100644
--- a/src/lombok/eclipse/handlers/HandleCleanup.java
+++ b/src/lombok/eclipse/handlers/HandleCleanup.java
@@ -1,9 +1,5 @@
package lombok.eclipse.handlers;
-import static lombok.eclipse.Eclipse.copyType;
-
-import java.util.Arrays;
-
import lombok.Cleanup;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
@@ -13,24 +9,14 @@ import lombok.eclipse.EclipseAST.Node;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
-import org.eclipse.jdt.internal.compiler.ast.Argument;
-import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CaseStatement;
-import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
-import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
-import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
-import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
-import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
import org.eclipse.jdt.internal.compiler.ast.TryStatement;
-import org.eclipse.jdt.internal.compiler.ast.TypeReference;
-import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
-import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.mangosdk.spi.ProviderFor;
@ProviderFor(EclipseAnnotationHandler.class)
@@ -69,7 +55,7 @@ public class HandleCleanup implements EclipseAnnotationHandler<Cleanup> {
}
if ( statements == null ) {
- annotationNode.addError("Parent block does not contain any statements. This is a lombok bug.");
+ annotationNode.addError("LOMBOK BUG: Parent block does not contain any statements.");
return true;
}
@@ -79,119 +65,53 @@ public class HandleCleanup implements EclipseAnnotationHandler<Cleanup> {
}
if ( start == statements.length ) {
- annotationNode.addError("Can't find this local variable declaration inside its parent. This is a lombok bug.");
+ annotationNode.addError("LOMBOK BUG: Can't find this local variable declaration inside its parent.");
return true;
}
start++;
- int end = start + 1;
- for ( ; end < statements.length ; end++ ) {
- if ( isSwitch && statements[end] instanceof CaseStatement ) {
- annotationNode.addError("The cleanup method must be called before the next case/default statement.");
- return true;
- }
- if ( statements[end] instanceof MessageSend ) {
- MessageSend ms = (MessageSend)statements[end];
- //The method name is the same as the 'cleanupName = ' field of the @Cleanup annotation...
- if ( ms.selector == null || !cleanupName.equals(new String(ms.selector)) ) continue;
- //The call is of the form 'foo.cleanup(anything)', where foo is a simple reference and not an expression...
- if ( !(ms.receiver instanceof SingleNameReference) ) continue;
- //And the reference is the same name as the local variable annotated with @Cleanup...
- if ( !Arrays.equals(((SingleNameReference)ms.receiver).token, decl.name) ) continue;
- //Then we found it!
- if ( ms.arguments != null && ms.arguments.length > 0 ) {
- //As we'll be moving the close() call around, any references to local vars may not be valid in the new scope.
- //Technically we could throw scope markers around the whole shebang and split local var declarations into a separate
- //declaration (in the newly created top scope) and an initialization, but, then there's 'final' and 'definite assignment'
- //rules to worry about. So, let's make this easy on ourselves and allow no arguments, for now.
- annotationNode.addError("The cleanup method cannot have any arguments.");
- return true;
+ int end;
+ if ( isSwitch ) {
+ end = start + 1;
+ for ( ; end < statements.length ; end++ ) {
+ if ( statements[end] instanceof CaseStatement ) {
+ break;
}
- break;
}
- }
+ } else end = statements.length;
- if ( end == statements.length ) {
- annotationNode.addError("You need to include a " + new String(decl.name) + "." + cleanupName + "() call at the same scope level.");
- return true;
- }
-
- //At this point, at start-1, there's the local declaration, and at end, there's the close call.
- //Thus, we need to move [start, end) into a try block, and move the close call to its own scope.
+ //At this point:
+ // start-1 = Local Declaration marked with @Cleanup
+ // start = first instruction that needs to be wrapped into a try block
+ // end = last intruction of the scope -OR- last instruction before the next case label in switch statements.
+ // hence:
+ // [start, end) = statements for the try block.
Statement[] tryBlock = new Statement[end - start];
System.arraycopy(statements, start, tryBlock, 0, end-start);
- //Remove the stuff we just dumped into the tryBlock, AND the close() call, and then leave room for the try node and the unique name.
- Statement[] newStatements = new Statement[statements.length - (end-start) +1];
- System.arraycopy(statements, 0, newStatements, 0, start);
- System.arraycopy(statements, end+1, newStatements, start+2, statements.length - end -1);
+ //Remove the stuff we just dumped into the tryBlock, and then leave room for the try node.
+ int newStatementsLength = statements.length - (end-start); //Remove room for every statement moved into try block...
+ newStatementsLength += 1; //But add room for the TryStatement node itself.
+ Statement[] newStatements = new Statement[newStatementsLength];
+ System.arraycopy(statements, 0, newStatements, 0, start); //copy all statements before the try block verbatim.
+ System.arraycopy(statements, end, newStatements, start+1, statements.length - end); //For switch statements.
+
TryStatement tryStatement = new TryStatement();
- newStatements[start+1] = tryStatement;
- LocalDeclaration tempVar = new LocalDeclaration(("$lombok$cleanup$" + new String(decl.name)).toCharArray(), 0, 0);
- tempVar.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
- tempVar.initialization = new FalseLiteral(0, 0);
- newStatements[start] = tempVar;
tryStatement.tryBlock = new Block(0);
tryStatement.tryBlock.statements = tryBlock;
+ newStatements[start] = tryStatement;
- char[] exName = ("$lombok$cleanup$ex$" + new String(decl.name)).toCharArray();
Statement[] finallyBlock = new Statement[1];
- TryStatement safeClose = new TryStatement();
- safeClose.tryBlock = new Block(0);
- safeClose.tryBlock.statements = new Statement[1];
- MessageSend newCloseCall = new MessageSend();
- newCloseCall.receiver = new SingleNameReference(decl.name, 0);
- newCloseCall.selector = cleanupName.toCharArray();
- safeClose.tryBlock.statements[0] = newCloseCall;
- safeClose.catchArguments = new Argument[1];
- safeClose.catchArguments[0] = new Argument(exName, 0,
- new QualifiedTypeReference(TypeConstants.JAVA_LANG_THROWABLE, new long[] { 0, 0, 0}), 0);
- safeClose.catchBlocks = new Block[1];
- safeClose.catchBlocks[0] = new Block(0);
- safeClose.catchBlocks[0].sourceEnd = safeClose.catchBlocks[0].sourceStart = -2;
MessageSend unsafeClose = new MessageSend();
unsafeClose.receiver = new SingleNameReference(decl.name, 0);
unsafeClose.selector = cleanupName.toCharArray();
- finallyBlock[0] = new IfStatement(new SingleNameReference(tempVar.name, 0), safeClose, unsafeClose, 0, 0);
+ finallyBlock[0] = unsafeClose;
tryStatement.finallyBlock = new Block(0);
tryStatement.finallyBlock.statements = finallyBlock;
- Node containingMethodNode = annotationNode;
- TypeReference[] thrownExceptions = null;
- findThrownExceptions:
- while ( containingMethodNode != null ) {
- switch ( containingMethodNode.getKind() ) {
- case INITIALIZER:
- break findThrownExceptions;
- case METHOD:
- thrownExceptions = ((AbstractMethodDeclaration)containingMethodNode.get()).thrownExceptions;
- break findThrownExceptions;
- default:
- containingMethodNode = containingMethodNode.up();
- }
- }
-
- if ( thrownExceptions == null ) thrownExceptions = new TypeReference[0];
- tryStatement.catchArguments = new Argument[thrownExceptions.length + 2];
- tryStatement.catchBlocks = new Block[thrownExceptions.length + 2];
- int idx = 0;
- tryStatement.catchArguments[idx++] = new Argument(exName, 0,
- new QualifiedTypeReference(TypeConstants.JAVA_LANG_RUNTIMEEXCEPTION, new long[] { 0, 0, 0 }), 0);
- tryStatement.catchArguments[idx++] = new Argument(exName, 0,
- new QualifiedTypeReference(TypeConstants.JAVA_LANG_ERROR, new long[] { 0, 0, 0 }), 0);
- for ( ; idx < tryStatement.catchArguments.length ; idx++ ) {
- tryStatement.catchArguments[idx] = new Argument(exName, 0, copyType(thrownExceptions[idx-2]), 0);
- }
-
- for ( idx = 0 ; idx < tryStatement.catchBlocks.length ; idx++ ) {
- Block b = new Block(0);
- tryStatement.catchBlocks[idx] = b;
- b.statements = new Statement[2];
- b.statements[0] = new Assignment(new SingleNameReference(tempVar.name, 0), new TrueLiteral(0, 0), 0);
- b.statements[1] = new ThrowStatement(new SingleNameReference(exName, 0), 0, 0);
- b.sourceEnd = b.sourceStart = -2;
- }
+ tryStatement.catchArguments = null;
+ tryStatement.catchBlocks = null;
if ( blockNode instanceof AbstractMethodDeclaration ) {
((AbstractMethodDeclaration)blockNode).statements = newStatements;
@@ -203,6 +123,7 @@ public class HandleCleanup implements EclipseAnnotationHandler<Cleanup> {
ancestor.rebuild();
+
return true;
}
}
diff --git a/src/lombok/javac/JavacAST.java b/src/lombok/javac/JavacAST.java
index a253f8cd..9e10f320 100644
--- a/src/lombok/javac/JavacAST.java
+++ b/src/lombok/javac/JavacAST.java
@@ -228,7 +228,7 @@ public class JavacAST extends AST<JCTree> {
}
public class Node extends AST<JCTree>.Node {
- public Node(JCTree node, Collection<Node> children, Kind kind) {
+ public Node(JCTree node, List<Node> children, Kind kind) {
super(node, children, kind);
}
@@ -411,6 +411,37 @@ public class JavacAST extends AST<JCTree> {
}
}
+ @SuppressWarnings("unchecked")
+ @Override protected void setElementInASTCollection(Field field, Object refField, List<Collection<?>> chain, Collection<?> collection, int idx, JCTree newN) throws IllegalAccessException {
+ com.sun.tools.javac.util.List<?> list = setElementInConsList(chain, collection, ((List)collection).get(idx), newN);
+ field.set(refField, list);
+ }
+
+ private com.sun.tools.javac.util.List<?> setElementInConsList(List<Collection<?>> chain, Collection<?> current, Object oldO, Object newO) {
+ com.sun.tools.javac.util.List<?> oldL = (com.sun.tools.javac.util.List<?>) current;
+ com.sun.tools.javac.util.List<?> newL = replaceInConsList(oldL, oldO, newO);
+ if ( chain.isEmpty() ) return newL;
+ else {
+ List<Collection<?>> reducedChain = new ArrayList<Collection<?>>(chain);
+ Collection<?> newCurrent = reducedChain.remove(reducedChain.size() -1);
+ return setElementInConsList(reducedChain, newCurrent, oldL, newL);
+ }
+ }
+
+ private com.sun.tools.javac.util.List<?> replaceInConsList(com.sun.tools.javac.util.List<?> oldL, Object oldO, Object newO) {
+ boolean repl = false;
+ Object[] a = oldL.toArray();
+ for ( int i = 0 ; i < a.length ; i++ ) {
+ if ( a[i] == oldO ) {
+ a[i] = newO;
+ repl = true;
+ }
+ }
+
+ if ( repl ) return com.sun.tools.javac.util.List.<Object>from(a);
+ else return oldL;
+ }
+
private void increaseErrorCount(Messager messager) {
try {
Field f = messager.getClass().getDeclaredField("errorCount");