diff options
Diffstat (limited to 'src/lombok/eclipse/handlers')
-rw-r--r-- | src/lombok/eclipse/handlers/HandleCleanup.java | 133 |
1 files changed, 27 insertions, 106 deletions
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; } } |