/** * This file is licensed under the University of Illinois/NCSA Open Source License. See LICENSE.TXT for details. */ package edu.illinois.keshmesh.transformer.core; import java.util.List; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.filebuffers.ITextFileBufferManager; import org.eclipse.core.filebuffers.LocationKind; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.SynchronizedStatement; import org.eclipse.jdt.core.dom.TryStatement; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.Refactoring; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.UndoEdit; import edu.illinois.keshmesh.detector.bugs.BugInstance; import edu.illinois.keshmesh.detector.bugs.CodePosition; import edu.illinois.keshmesh.detector.bugs.LCK03JBugPattern; import edu.illinois.keshmesh.detector.bugs.LCK03JFixInformation; import edu.illinois.keshmesh.util.Logger; /** * * @author Samira Tasharofi * */ public class LCK03JFixer extends Refactoring { final static String SYNC_COMMAND = "synchronized"; CodePosition bugPosition; BugInstance bugInstance; LCK03JFixInformation fixInformation; public LCK03JFixer(CodePosition bugPosition) { super(); this.bugPosition = bugPosition; } public LCK03JFixer(BugInstance bugInstance) { super(); this.bugInstance = bugInstance; this.bugPosition = bugInstance.getBugPosition(); this.fixInformation = (LCK03JFixInformation) bugInstance.getFixInformation(); } @Override public RefactoringStatus checkFinalConditions(IProgressMonitor progressMonitor) throws CoreException, OperationCanceledException { return null; } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor progressMonitor) throws CoreException, OperationCanceledException { if (fixInformation.getTypeNames().size() == 1) { if (fixInformation.isLock()) return RefactoringStatus.create(Status.OK_STATUS); else return RefactoringStatus.createFatalErrorStatus("Cannot fix the synchronized statement on Condition: " + fixInformation.toString()); } else { return RefactoringStatus.createFatalErrorStatus("Multiple types are found: " + fixInformation.toString()); } } /** * FIXME: * * Break the method into smaller ones. * * Rethrow the exception as CoreException * * Preserve comments. * * Preserve formatting. * */ @Override public Change createChange(IProgressMonitor progressMonitor) throws CoreException, OperationCanceledException { ITextFileBufferManager textFileBufferManager = null; try { //Retrieving the Document out of IPath textFileBufferManager = FileBuffers.getTextFileBufferManager(); textFileBufferManager.connect(bugPosition.getSourcePath(), LocationKind.LOCATION, progressMonitor); ITextFileBuffer textFileBuffer = textFileBufferManager.getTextFileBuffer(bugPosition.getSourcePath(), LocationKind.IFILE); IDocument document = textFileBuffer.getDocument(); try { Logger.log(document.get(bugPosition.getFirstOffset(), bugPosition.getLength())); } catch (BadLocationException e1) { e1.printStackTrace(); } // Parsing the Document ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setKind(ASTParser.K_COMPILATION_UNIT); parser.setSource(document.get().toCharArray()); parser.setResolveBindings(true); CompilationUnit compilationUnit = (CompilationUnit) parser.createAST(progressMonitor); // Retrieving the begin and end indexes of synchronized command int bugLineOffset = document.getLineInformation(bugPosition.getFirstLine() - 1).getOffset(); int bugLineLength = document.getLineInformation(bugPosition.getFirstLine() - 1).getLength(); String bugLine = document.get(bugLineOffset, bugLineLength); int syncCommandBeginIndex = getSynchronizedCommandBeginIndex(bugLine); int syncCommandLastIndex = getSynchronizedCommandLastIndex(bugLine, syncCommandBeginIndex); // Extracting the synchronized command expression String bugLineAfterSync = bugLine.substring(syncCommandBeginIndex + SYNC_COMMAND.length()); int openParenthesisIndex = bugLineAfterSync.indexOf('(') + syncCommandBeginIndex + SYNC_COMMAND.length(); String synchExpression = bugLine.substring(openParenthesisIndex + 1, syncCommandLastIndex); // Computing the begin and end offset of the synchronized command int syncCommandBeginOffset = bugLineOffset + syncCommandBeginIndex; int syncCommandLastOffset = bugLineOffset + syncCommandLastIndex; // Getting the synchronized command AST node ASTNode monitorNode = NodeFinder.perform(compilationUnit, syncCommandBeginOffset, syncCommandLastOffset - syncCommandBeginOffset + 1); SynchronizedStatement synchronizedStatement = (SynchronizedStatement) monitorNode; List synchBodyStatements = synchronizedStatement.getBody().statements(); // Creating a "local variable assignment" statement and a try/catch/final block to be replaced at monitor node place //AST ast = synchronizedStatement.getAST(); ASTRewrite rewriter = ASTRewrite.create(compilationUnit.getAST()); String localVarNameForLock = "tempLock"; String localVarAssignment = LCK03JBugPattern.LOCK + " " + localVarNameForLock + " = " + synchExpression + ";\n"; String tryFinalBlockStatements = "try {\n" + localVarNameForLock + ".lock();\n"; for (Object statement : synchBodyStatements) { tryFinalBlockStatements += statement; } tryFinalBlockStatements += "} finally {\n" + localVarNameForLock + ".unlock();\n}"; // Rewriting the monitor node ASTNode astNode = rewriter.createStringPlaceholder(localVarAssignment + tryFinalBlockStatements, TryStatement.TRY_STATEMENT); rewriter.replace(synchronizedStatement, astNode, null); TextEdit textEdit = rewriter.rewriteAST(document, null); UndoEdit undoEdit = textEdit.apply(document); //Committing changes to the source file textFileBuffer.commit(progressMonitor, true); } catch (BadLocationException e) { throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to fix LCK03J.", e)); } finally { textFileBufferManager.disconnect(bugPosition.getSourcePath(), LocationKind.LOCATION, progressMonitor); } return null; } private int getSynchronizedCommandBeginIndex(String syncCommandLine) { int syncBeginIndex = syncCommandLine.indexOf(SYNC_COMMAND); // There is a possibility of having synchronized word within comments // Skipping the comments before the synchronized command int startCommentIndex = syncCommandLine.indexOf("/*"); int endCommentIndex = syncCommandLine.indexOf("*/"); String temp_synchCommandLine = syncCommandLine; int temp_syncIndex = syncBeginIndex; int temp_beginIndex = 0; while (startCommentIndex >= 0 && endCommentIndex > 0 && temp_synchCommandLine.length() > 0 && temp_syncIndex > startCommentIndex) { temp_beginIndex += (endCommentIndex + 2); temp_synchCommandLine = temp_synchCommandLine.substring(endCommentIndex + 2); temp_syncIndex = temp_synchCommandLine.indexOf(SYNC_COMMAND); startCommentIndex = temp_synchCommandLine.indexOf("/*"); endCommentIndex = temp_synchCommandLine.indexOf("*/"); syncBeginIndex = temp_beginIndex + temp_syncIndex; Logger.log(Integer.toString(syncBeginIndex)); } return syncBeginIndex; } private int getSynchronizedCommandLastIndex(String syncCommandLine, int beginIndex) { String syncCommandLineAfterSyncWord = syncCommandLine.substring(beginIndex + SYNC_COMMAND.length()); int openParenthesisIndex = syncCommandLineAfterSyncWord.indexOf('(') + beginIndex + SYNC_COMMAND.length(); int index = openParenthesisIndex; int pcounter = 1; while (pcounter != 0 && index < syncCommandLine.length()) { index++; if (syncCommandLine.charAt(index) == ')') { pcounter--; } else if (syncCommandLine.charAt(index) == '(') { pcounter++; } } return index; } @Override public String getName() { return "LCK03J Fixer"; } }