1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Bug 337486 - AbstractIndexAstChecker allows AST to outlive index read lock.

This commit is contained in:
Sergey Prigogin 2011-04-12 22:13:46 +00:00
parent c24ce74075
commit 87ae09ee72
11 changed files with 477 additions and 327 deletions

View file

@ -6,7 +6,7 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alena Laskavaia - initial API and implementation
* Alena Laskavaia - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.codan.internal.checkers;
@ -14,7 +14,6 @@ import java.util.Iterator;
import org.eclipse.cdt.codan.core.cxx.CxxAstUtils;
import org.eclipse.cdt.codan.core.cxx.model.AbstractAstFunctionChecker;
import org.eclipse.cdt.codan.core.cxx.model.CxxModelsCache;
import org.eclipse.cdt.codan.core.model.IProblem;
import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy;
import org.eclipse.cdt.codan.core.model.cfg.ICfgData;
@ -156,7 +155,7 @@ public class ReturnChecker extends AbstractAstFunctionChecker {
* @return
*/
protected boolean endsWithNoExitNode(IASTFunctionDefinition func) {
IControlFlowGraph graph = CxxModelsCache.getInstance().getControlFlowGraph(func);
IControlFlowGraph graph = getModelCache().getControlFlowGraph(func);
Iterator<IExitNode> exitNodeIterator = graph.getExitNodeIterator();
boolean noexitop = false;
for (; exitNodeIterator.hasNext();) {

View file

@ -6,13 +6,14 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alena Laskavaia - initial API and implementation
* Alena Laskavaia - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.codan.core.cxx.model;
import org.eclipse.cdt.codan.core.CodanCorePlugin;
import org.eclipse.cdt.codan.core.cxx.Activator;
import org.eclipse.cdt.codan.core.model.AbstractCheckerWithProblemPreferences;
import org.eclipse.cdt.codan.core.model.ICheckerInvocationContext;
import org.eclipse.cdt.codan.core.model.IProblem;
import org.eclipse.cdt.codan.core.model.IProblemLocation;
import org.eclipse.cdt.codan.core.model.IProblemLocationFactory;
@ -23,114 +24,87 @@ import org.eclipse.cdt.core.dom.ast.IASTMacroExpansionLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.OperationCanceledException;
/**
* Convenience implementation of checker that work on index based ast of a c/c++
* Convenience implementation of checker that works on index-based AST of a C/C++
* program.
*
* Clients may extend this class.
*/
public abstract class AbstractIndexAstChecker extends AbstractCheckerWithProblemPreferences implements ICAstChecker,
IRunnableInEditorChecker {
private IFile file;
private ICodanCommentMap commentmap;
public abstract class AbstractIndexAstChecker extends AbstractCheckerWithProblemPreferences
implements ICAstChecker, IRunnableInEditorChecker {
private CxxModelsCache modelCache;
protected IFile getFile() {
return file;
}
protected IProject getProject() {
return file == null ? null : file.getProject();
}
void processFile(IFile file) throws CoreException, InterruptedException {
commentmap = null;
IASTTranslationUnit ast = CxxModelsCache.getInstance().getAst(file);
if (ast == null)
return;
// lock the index for read access
IIndex index = CxxModelsCache.getInstance().getIndex(file);
index.acquireReadLock();
try {
// traverse the ast using the visitor pattern.
this.file = file;
processAst(ast);
} finally {
this.file = null;
index.releaseReadLock();
}
}
public synchronized boolean processResource(IResource resource) {
@Override
public synchronized boolean processResource(IResource resource) throws OperationCanceledException {
if (!shouldProduceProblems(resource))
return false;
if (resource instanceof IFile) {
IFile file = (IFile) resource;
if (!(resource instanceof IFile))
return true;
processFile((IFile) resource);
return false;
}
private void processFile(IFile file) throws OperationCanceledException {
ICheckerInvocationContext context = getContext();
synchronized (context) {
modelCache = context.get(CxxModelsCache.class);
if (modelCache == null) {
ICElement celement = CoreModel.getDefault().create(file);
if (!(celement instanceof ITranslationUnit)) {
return;
}
modelCache = new CxxModelsCache((ITranslationUnit) celement);
context.add(modelCache);
}
}
try {
IASTTranslationUnit ast = modelCache.getAST();
if (ast != null) {
synchronized (ast) {
processAst(ast);
}
}
} catch (CoreException e) {
Activator.log(e);
} finally {
modelCache = null;
}
}
/*
* (non-Javadoc)
*
* @see IRunnableInEditorChecker#processModel(Object, ICheckerInvocationContext)
*/
public synchronized void processModel(Object model, ICheckerInvocationContext context) {
if (model instanceof IASTTranslationUnit) {
setContext(context);
IASTTranslationUnit ast = (IASTTranslationUnit) model;
synchronized (context) {
modelCache = context.get(CxxModelsCache.class);
if (modelCache == null) {
modelCache = new CxxModelsCache(ast);
context.add(modelCache);
}
}
try {
processFile(file);
} catch (CoreException e) {
CodanCorePlugin.log(e);
} catch (InterruptedException e) {
// ignore
}
return false;
}
return true;
}
public void reportProblem(String id, IASTNode astNode, Object... args) {
IProblemLocation loc = getProblemLocation(astNode);
if (loc!=null) reportProblem(id, loc, args);
}
public void reportProblem(IProblem problem, IASTNode astNode, Object... args) {
IProblemLocation loc = getProblemLocation(astNode);
if (loc!=null) reportProblem(problem, loc, args);
}
@SuppressWarnings("restriction")
protected IProblemLocation getProblemLocation(IASTNode astNode) {
IASTFileLocation astLocation = astNode.getFileLocation();
IPath location = new Path(astLocation.getFileName());
IFile astFile = ResourceLookup.selectFileForLocation(location, getProject());
if (astFile == null) {
astFile = file;
}
if (astFile == null) {
Activator.log("Cannot resolve location: " + location); //$NON-NLS-1$
return null;
}
return getProblemLocation(astNode, astLocation, astFile);
}
private IProblemLocation getProblemLocation(IASTNode astNode, IASTFileLocation astLocation, IFile astFile) {
int line = astLocation.getStartingLineNumber();
IProblemLocationFactory locFactory = getRuntime().getProblemLocationFactory();
if (hasMacroLocation(astNode) && astNode instanceof IASTName) {
IASTImageLocation imageLocation = ((IASTName) astNode).getImageLocation();
if (imageLocation != null) {
int start = imageLocation.getNodeOffset();
int end = start + imageLocation.getNodeLength();
return locFactory.createProblemLocation(astFile, start, end, line);
processAst(ast);
} finally {
modelCache = null;
setContext(null);
}
}
if (line == astLocation.getEndingLineNumber()) {
return locFactory.createProblemLocation(astFile, astLocation.getNodeOffset(),
astLocation.getNodeOffset() + astLocation.getNodeLength(), line);
}
return locFactory.createProblemLocation(astFile, line);
}
private boolean hasMacroLocation(IASTNode astNode) {
return astNode.getNodeLocations().length == 1 && astNode.getNodeLocations()[0] instanceof IASTMacroExpansionLocation;
}
@Override
@ -138,40 +112,60 @@ public abstract class AbstractIndexAstChecker extends AbstractCheckerWithProblem
return true;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.cdt.codan.core.model.IRunnableInEditorChecker#processModel
* (java.lang.Object)
*/
@SuppressWarnings("restriction")
public synchronized void processModel(Object model) {
if (model instanceof IASTTranslationUnit) {
CxxModelsCache.getInstance().clearCash();
IASTTranslationUnit ast = (IASTTranslationUnit) model;
IPath location = new Path(ast.getFilePath());
IFile astFile = ResourceLookup.selectFileForLocation(location, getProject());
file = astFile;
commentmap = null;
processAst(ast);
public void reportProblem(String id, IASTNode astNode, Object... args) {
IProblemLocation loc = getProblemLocation(astNode);
if (loc != null)
reportProblem(id, loc, args);
}
public void reportProblem(IProblem problem, IASTNode astNode, Object... args) {
IProblemLocation loc = getProblemLocation(astNode);
if (loc != null)
reportProblem(problem, loc, args);
}
protected IProblemLocation getProblemLocation(IASTNode astNode) {
IASTFileLocation astLocation = astNode.getFileLocation();
return getProblemLocation(astNode, astLocation);
}
private IProblemLocation getProblemLocation(IASTNode astNode, IASTFileLocation astLocation) {
int line = astLocation.getStartingLineNumber();
IProblemLocationFactory locFactory = getRuntime().getProblemLocationFactory();
if (hasMacroLocation(astNode) && astNode instanceof IASTName) {
IASTImageLocation imageLocation = ((IASTName) astNode).getImageLocation();
if (imageLocation != null) {
int start = imageLocation.getNodeOffset();
int end = start + imageLocation.getNodeLength();
return locFactory.createProblemLocation(getFile(), start, end, line);
}
}
if (line == astLocation.getEndingLineNumber()) {
return locFactory.createProblemLocation(getFile(), astLocation.getNodeOffset(),
astLocation.getNodeOffset() + astLocation.getNodeLength(), line);
}
return locFactory.createProblemLocation(getFile(), line);
}
private boolean hasMacroLocation(IASTNode astNode) {
return astNode.getNodeLocations().length == 1 &&
astNode.getNodeLocations()[0] instanceof IASTMacroExpansionLocation;
}
protected IFile getFile() {
return modelCache.getFile();
}
protected IProject getProject() {
IFile file = getFile();
return file == null ? null : file.getProject();
}
protected CxxModelsCache getModelCache() {
return modelCache;
}
protected ICodanCommentMap getCommentMap() {
if (commentmap == null) {
try {
CxxModelsCache cxxcache = CxxModelsCache.getInstance();
synchronized (cxxcache) {
IASTTranslationUnit ast = cxxcache.getAst(getFile());
commentmap = cxxcache.getCommentedNodeMap(ast);
return commentmap;
}
} catch (Exception e) {
Activator.log(e);
}
}
return commentmap;
return modelCache.getCommentedNodeMap();
}
}

View file

@ -6,7 +6,8 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alena Laskavaia - initial API and implementation
* Alena Laskavaia - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.codan.core.cxx.model;
@ -15,32 +16,71 @@ import java.util.WeakHashMap;
import org.eclipse.cdt.codan.core.cxx.Activator;
import org.eclipse.cdt.codan.core.cxx.internal.model.CodanCommentMap;
import org.eclipse.cdt.codan.core.cxx.internal.model.cfg.CxxControlFlowGraph;
import org.eclipse.cdt.codan.core.model.ICodanDisposable;
import org.eclipse.cdt.codan.core.model.cfg.IControlFlowGraph;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.OperationCanceledException;
/**
* Cache data models for resource so checkers can share it
*/
public class CxxModelsCache {
private IFile file;
private IASTTranslationUnit ast;
private ITranslationUnit tu;
private IIndex index;
private WeakHashMap<IASTFunctionDefinition, IControlFlowGraph> cfgmap = new WeakHashMap<IASTFunctionDefinition, IControlFlowGraph>(0);
private ICodanCommentMap commentMap;
private static CxxModelsCache instance = new CxxModelsCache();
public class CxxModelsCache implements ICodanDisposable {
private static final int PARSE_MODE = ITranslationUnit.AST_SKIP_ALL_HEADERS
| ITranslationUnit.AST_CONFIGURE_USING_SOURCE_CONTEXT
| ITranslationUnit.AST_SKIP_TRIVIAL_EXPRESSIONS_IN_AGGREGATE_INITIALIZERS
| ITranslationUnit.AST_PARSE_INACTIVE_CODE;
public static CxxModelsCache getInstance() {
return instance;
private final IFile file;
private final ITranslationUnit tu;
private IASTTranslationUnit ast;
private IIndex index;
private final WeakHashMap<IASTFunctionDefinition, IControlFlowGraph> cfgmap;
private ICodanCommentMap commentMap;
private boolean disposed;
CxxModelsCache(ITranslationUnit tu) {
this.tu = tu;
this.file = tu != null ? (IFile) tu.getResource() : null;
cfgmap = new WeakHashMap<IASTFunctionDefinition, IControlFlowGraph>(0);
}
CxxModelsCache(IASTTranslationUnit ast) {
this(ast.getOriginatingTranslationUnit());
this.ast = ast;
}
public IASTTranslationUnit getAST() throws OperationCanceledException, CoreException {
return getAST(tu);
}
public IASTTranslationUnit getAST(ITranslationUnit tu)
throws OperationCanceledException, CoreException {
if (!this.tu.equals(tu)) {
throw new IllegalArgumentException();
}
if (ast == null) {
getIndex();
ast= tu.getAST(index, PARSE_MODE);
}
return ast;
}
public ITranslationUnit getTranslationUnit() {
return tu;
}
public IFile getFile() {
return file;
}
public synchronized IControlFlowGraph getControlFlowGraph(IASTFunctionDefinition func) {
@ -48,85 +88,67 @@ public class CxxModelsCache {
if (cfg != null)
return cfg;
cfg = CxxControlFlowGraph.build(func);
if (cfgmap.size() > 20) { // if too many function better drop the cash XXX should be LRU
// TODO(Alena Laskavaia): Change to LRU.
if (cfgmap.size() > 20) { // if too many function better drop the cash
cfgmap.clear();
}
cfgmap.put(func, cfg);
return cfg;
}
public synchronized IASTTranslationUnit getAst(IFile file) throws CoreException, InterruptedException {
if (file.equals(this.file)) {
return ast;
public synchronized ICodanCommentMap getCommentedNodeMap() {
return getCommentedNodeMap(tu);
}
public synchronized ICodanCommentMap getCommentedNodeMap(ITranslationUnit tu) {
if (!this.tu.equals(tu)) {
throw new IllegalArgumentException();
}
// create translation unit and access index
ICElement celement = CoreModel.getDefault().create(file);
if (!(celement instanceof ITranslationUnit))
return null; // not a C/C++ file
clearCash();
this.file = file;
//System.err.println("Making ast for "+file);
tu = (ITranslationUnit) celement;
index = CCorePlugin.getIndexManager().getIndex(tu.getCProject());
// lock the index for read access
index.acquireReadLock();
try {
// create index based ast
ast = tu.getAST(index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS);
if (ast == null)
return null;//
return ast;
} finally {
if (commentMap == null) {
if (ast == null) {
throw new IllegalStateException("getCommentedNodeMap called before getAST"); //$NON-NLS-1$
}
commentMap = new CodanCommentMap(ASTCommenter.getCommentedNodeMap(ast));
}
return commentMap;
}
/**
* Returns the index that can be safely used for reading until the cache is disposed.
*
* @return The index.
*/
public synchronized IIndex getIndex() throws CoreException, OperationCanceledException {
Assert.isTrue(!disposed, "CxxASTCache is already disposed."); //$NON-NLS-1$
if (this.index == null) {
ICProject[] projects = CoreModel.getDefault().getCModel().getCProjects();
IIndex index = CCorePlugin.getIndexManager().getIndex(projects);
try {
index.acquireReadLock();
} catch (InterruptedException e) {
throw new OperationCanceledException();
}
this.index = index;
}
return this.index;
}
/**
* @see IDisposable#dispose()
* This method should not be called concurrently with any other method.
*/
public void dispose() {
Assert.isTrue(!disposed, "CxxASTCache.dispose() called more than once."); //$NON-NLS-1$
disposed = true;
if (index != null) {
index.releaseReadLock();
}
}
public synchronized ICodanCommentMap getCommentedNodeMap(IASTTranslationUnit ast) {
if (this.ast == ast) {
try {
index.acquireReadLock();
try {
commentMap = new CodanCommentMap(ASTCommenter.getCommentedNodeMap(ast));
} finally {
index.releaseReadLock();
}
return commentMap;
} catch (InterruptedException e) {
return null;
}
}
throw new IllegalArgumentException("Not cached");
}
public ICodanCommentMap getCommentedNodeMap(IFile file) {
try {
IASTTranslationUnit ast = getAst(file);
return getCommentedNodeMap(ast);
} catch (InterruptedException e) {
return null;
} catch (CoreException e) {
Activator.log(e);
return null;
}
}
/**
* Clear cash for current file
*/
public void clearCash() {
cfgmap.clear();
ast = null;
tu = null;
index = null;
commentMap = null;
}
public synchronized IIndex getIndex(IFile file) throws CoreException, InterruptedException {
if (file.equals(this.file)) {
return index;
}
getAst(file); // to init variables
return index;
@Override
protected void finalize() throws Throwable {
if (!disposed)
Activator.log("CxxASTCache was not disposed."); //$NON-NLS-1$
super.finalize();
}
}

View file

@ -17,9 +17,11 @@ import junit.framework.TestCase;
import org.eclipse.cdt.codan.core.CodanRuntime;
import org.eclipse.cdt.codan.core.model.IChecker;
import org.eclipse.cdt.codan.core.model.ICheckerInvocationContext;
import org.eclipse.cdt.codan.core.model.IProblemLocation;
import org.eclipse.cdt.codan.core.model.IProblemReporter;
import org.eclipse.cdt.codan.core.model.IRunnableInEditorChecker;
import org.eclipse.cdt.codan.internal.core.CheckerInvocationContext;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.parser.ISourceCodeParser;
@ -71,7 +73,7 @@ public abstract class CodanFastCxxAstTestCase extends TestCase {
/**
* @return
*
*
*/
public IASTTranslationUnit parse(String code) {
return parse(code, isCpp() ? ParserLanguage.CPP : ParserLanguage.C, true);
@ -116,7 +118,7 @@ public abstract class CodanFastCxxAstTestCase extends TestCase {
/**
* Override if any of code that test tried to parse has errors, otherwise
* parse method would assert
*
*
* @return
*/
protected boolean hasCodeErrors() {
@ -153,11 +155,13 @@ public abstract class CodanFastCxxAstTestCase extends TestCase {
codanproblems.add(new ProblemInstance(problemId, loc, args));
}
});
ICheckerInvocationContext context = new CheckerInvocationContext(null);
try {
IChecker checker = getChecker();
((IRunnableInEditorChecker) checker).processModel(tu);
((IRunnableInEditorChecker) checker).processModel(tu, context);
} finally {
CodanRuntime.getInstance().setProblemReporter(problemReporter);
context.dispose();
}
}

View file

@ -1,12 +1,13 @@
/*******************************************************************************
* Copyright (c) 2009, 2010 Alena Laskavaia
* Copyright (c) 2009, 2010 Alena Laskavaia
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alena Laskavaia - initial API and implementation
* Alena Laskavaia - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.codan.core.model;
@ -14,22 +15,21 @@ import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.codan.core.CodanRuntime;
import org.eclipse.cdt.codan.internal.core.CheckerInvocationContext;
import org.eclipse.cdt.codan.internal.core.CheckersRegistry;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.OperationCanceledException;
/**
* Convenience implementation of IChecker interface. Has a default
* implementation for common methods.
*
*/
public abstract class AbstractChecker implements IChecker {
protected String name;
/**
* @since 2.0
*/
protected ICheckerInvocationContext context;
private ICheckerInvocationContext context;
private IProblemReporter problemReporter;
/**
* Default constructor
@ -47,7 +47,7 @@ public abstract class AbstractChecker implements IChecker {
/**
* Reports a simple problem for given file and line
*
*
* @param id
* - problem id
* @param file
@ -66,7 +66,7 @@ public abstract class AbstractChecker implements IChecker {
/**
* Finds an instance of problem by given id, in user profile registered for
* specific file
*
*
* @param id
* - problem id
* @param file
@ -104,7 +104,7 @@ public abstract class AbstractChecker implements IChecker {
/**
* Reports a simple problem for given file and line, error message comes
* from problem definition
*
*
* @param id
* - problem id
* @param file
@ -121,16 +121,12 @@ public abstract class AbstractChecker implements IChecker {
* @since 2.0
*/
public IProblemReporter getProblemReporter() {
try {
return getContext().getProblemReporter();
} catch (Exception e) {
return CodanRuntime.getInstance().getProblemReporter();
}
return problemReporter;
}
/**
* Convenience method to return codan runtime
*
*
* @return
*/
protected CodanRuntime getRuntime() {
@ -139,7 +135,7 @@ public abstract class AbstractChecker implements IChecker {
/**
* Convenience method to create and return instance of IProblemLocation
*
*
* @param file
* - file where problem is found
* @param line
@ -152,7 +148,7 @@ public abstract class AbstractChecker implements IChecker {
/**
* Convenience method to create and return instance of IProblemLocation
*
*
* @param file
* - file where problem is found
* @param startChar
@ -176,7 +172,7 @@ public abstract class AbstractChecker implements IChecker {
/**
* report a problem
*
*
* @param problemId - id of a problem
* @param loc - problem location
* @param args - extra problem arguments
@ -186,56 +182,72 @@ public abstract class AbstractChecker implements IChecker {
}
/**
* Get invocation context.
*
* Returns the invocation context.
*
* @return checker invocation context
*
*
* @since 2.0
*/
public ICheckerInvocationContext getContext() {
protected ICheckerInvocationContext getContext() {
return context;
}
/**
* Set the invocation context. Usually called by codan builder.
* Object that calls this should also synchronize of checker object
* to prevent multi-thread access to a running context
*
* @since 2.0
*/
public void setContext(ICheckerInvocationContext context) {
protected void setContext(ICheckerInvocationContext context) {
this.context = context;
}
/**
* @since 2.0
*/
public boolean before(IResource resource) {
IChecker checker = this;
public void before(IResource resource) {
IProblemReporter problemReporter = CodanRuntime.getInstance().getProblemReporter();
IProblemReporter sessionReporter = problemReporter;
this.problemReporter = problemReporter;
if (problemReporter instanceof IProblemReporterSessionPersistent) {
// create session problem reporter
sessionReporter = ((IProblemReporterSessionPersistent) problemReporter).createReporter(resource, checker);
((IProblemReporterSessionPersistent) sessionReporter).start();
this.problemReporter = ((IProblemReporterSessionPersistent) problemReporter).createReporter(resource, this);
((IProblemReporterSessionPersistent) this.problemReporter).start();
} else if (problemReporter instanceof IProblemReporterPersistent) {
// delete markers if checker can possibly run on this
// resource this way if checker is not enabled markers would be
// deleted too
((IProblemReporterPersistent) problemReporter).deleteProblems(resource, checker);
((IProblemReporterPersistent) problemReporter).deleteProblems(resource, this);
}
((AbstractChecker) checker).setContext(new CheckerInvocationContext(resource, sessionReporter));
return true;
}
/**
* @since 2.0
*/
public boolean after(IResource resource) {
if (getContext().getProblemReporter() instanceof IProblemReporterSessionPersistent) {
// delete general markers
((IProblemReporterSessionPersistent) getContext().getProblemReporter()).done();
public void after(IResource resource) {
if (problemReporter instanceof IProblemReporterSessionPersistent) {
// Delete general markers
((IProblemReporterSessionPersistent) problemReporter).done();
}
problemReporter = null;
}
/**
* @param resource the resource to process.
* @return true if framework should traverse children of the resource and
* run this checkers on them again.
* @throws OperationCanceledException if the checker was interrupted.
* @since 2.0
*/
public abstract boolean processResource(IResource resource) throws OperationCanceledException;
/**
* @see IChecker#processResource(IResource, ICheckerInvocationContext)
* @since 2.0
*/
public synchronized boolean processResource(IResource resource, ICheckerInvocationContext context)
throws OperationCanceledException {
this.setContext(context);
try {
return processResource(resource);
} finally {
this.setContext(null);
}
return true;
}
}

View file

@ -1,27 +1,29 @@
/*******************************************************************************
* Copyright (c) 2009, 2010 Alena Laskavaia
* Copyright (c) 2009, 2010 Alena Laskavaia
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alena Laskavaia - initial API and implementation
* Alena Laskavaia - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.codan.core.model;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.OperationCanceledException;
/**
* Interface that checker must implement (through extending directly or
* indirectly {@link AbstractChecker}.
*
*
* <p>
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as part
* of a work in progress. There is no guarantee that this API will work or that
* it will remain the same.
* </p>
*
*
* @noextend This interface is not intended to be extended by clients.
* @noimplement This interface is not intended to be implemented by clients.
* Extend {@link AbstractChecker} class instead.
@ -29,25 +31,36 @@ import org.eclipse.core.resources.IResource;
public interface IChecker {
/**
* Main method that checker should implement that actually detects errors
*
* @param resource
* - resource to run on
*
* @param resource the resource to run on.
* @param context container object for sharing data between different checkers
* operating on the resource.
* @return true if framework should traverse children of the resource and
* run this checkers on them again
*/
boolean processResource(IResource resource);
/**
* run this checkers on them again.
* @throws OperationCanceledException if the checker was interrupted.
* @since 2.0
*/
boolean before(IResource resource);
boolean processResource(IResource resource, ICheckerInvocationContext context)
throws OperationCanceledException;
/**
* Called before processing a resource.
*
* @param resource the resource that is about to be processed.
* @since 2.0
*/
boolean after(IResource resource);
void before(IResource resource);
/**
* Called before processing a resource.
*
* @param resource the resource that has been processed.
* @since 2.0
*/
void after(IResource resource);
/**
* @return the problem reporter.
* @since 2.0
*/
IProblemReporter getProblemReporter();
@ -56,10 +69,9 @@ public interface IChecker {
* Implement this method to trim down type of resource you are interested
* in, usually it will be c/c++ files only. This method should be
* independent from current user preferences.
*
* @param resource
* - resource to run on
* @return - true if checker should be run on this resource
*
* @param resource the resource to run on.
* @return true if checker should be run on this resource.
*/
boolean enabledInContext(IResource resource);
@ -70,7 +82,7 @@ public interface IChecker {
* {@link IRunnableInEditorChecker}.
* Checker should return false if check is non-trivial and takes a long
* time.
*
*
* @return true if need to be run in editor as user types, and false
* otherwise
*/

View file

@ -1,34 +1,60 @@
/*******************************************************************************
* Copyright (c) 2009,2010 QNX Software Systems
* Copyright (c) 2009, 2011 QNX Software Systems
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.codan.core.model;
import org.eclipse.core.resources.IResource;
/**
* Since there is only one instance of checker available this object keeps
* track of invocation context - which would usually contain resource and some
* other object that checker require
* Context object that can be used to store data shared between different
* checkers operating on the same resource. The context and all objects stored
* in it are disposed of at the end of processing of a resource. May store
* objects of arbitrary types but only a single instance per type.
* <p>
* Implementations of this interface are guaranteed to be thread-safe.
* <p>
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as part
* of a work in progress. There is no guarantee that this API will work or that
* it will remain the same.
* </p>
*
*
* @noextend This interface is not intended to be extended by clients.
* @noimplement This interface is not intended to be implemented by clients.
*
*
* @since 2.0
*/
public interface ICheckerInvocationContext {
public interface ICheckerInvocationContext extends ICodanDisposable {
/**
* @return the resource this context is associated with.
*/
IResource getResource();
IProblemReporter getProblemReporter();
/**
* Returns the object of the given type. Lookup by an interface or a superclass
* is also possible, but in case when there are multiple objects implementing
* the interface, an arbitrary one will be returned.
*
* @param <T> the type of the object.
* @param objectClass the class of the object to retrieve.
* @return the object of the given type, or <code>null</code> if not present in the context.
*/
public <T> T get(Class<T> objectClass);
/**
* Adds an object to the context. The context accepts only a single
* instance of each class.
*
* @param <T> the type of the object.
* @param object the object to add to the context.
* @throws IllegalArgumentException if an attempt is made to add second instance
* of the same class.
*/
public <T extends ICodanDisposable> void add(T object);
}

View file

@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2011 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.codan.core.model;
/**
* <p>
* An interface for objects that own resources that have to be explicitly
* released. A disposable object is guaranteed to receive a {@link #dispose()} call
* when it is not longer needed. At this point, the object must release all resources
* and detach all listeners. A disposable object can only be disposed once; it cannot
* be reused.
* </p>
* <p>
* This interface can be extended or implemented by clients.
* </p>
* @since 2.0
*/
public interface ICodanDisposable {
/**
* Disposes of the cache. This method has to be called exactly once during
* the life cycle of the cache.
*/
public void dispose();
}

View file

@ -1,12 +1,13 @@
/*******************************************************************************
* Copyright (c) 2009, 2010 Alena Laskavaia
* Copyright (c) 2009, 2010 Alena Laskavaia
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alena Laskavaia - initial API and implementation
* Alena Laskavaia - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.codan.core.model;
@ -18,12 +19,15 @@ package org.eclipse.cdt.codan.core.model;
* of a work in progress. There is no guarantee that this API will work or that
* it will remain the same.
* </p>
*
*
* @noextend This interface is not intended to be extended by clients.
*/
public interface IRunnableInEditorChecker {
/**
* @param model
* @param model the model to check.
* @param context container object for sharing data between different checkers
* operating on the model.
* @since 2.0
*/
void processModel(Object model);
void processModel(Object model, ICheckerInvocationContext context);
}

View file

@ -1,40 +1,78 @@
/*******************************************************************************
* Copyright (c) 2009,2010 QNX Software Systems
* Copyright (c) 2009, 2011 QNX Software Systems
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.codan.internal.core;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.codan.core.model.ICheckerInvocationContext;
import org.eclipse.cdt.codan.core.model.IProblemReporter;
import org.eclipse.cdt.codan.core.model.ICodanDisposable;
import org.eclipse.core.resources.IResource;
/**
* Implementation of ICheckerInvocationContext
* Implementation of ICheckerInvocationContext.
* This class is thread-safe.
*/
public class CheckerInvocationContext implements ICheckerInvocationContext {
private IResource resource;
private IProblemReporter sessionReporter;
private final IResource resource;
private final Map<Class<?>, Object> objectStorage;
/**
* @param resource
* @param sessionReporter
* @param resource the resource this context is associated with.
*/
public CheckerInvocationContext(IResource resource, IProblemReporter sessionReporter) {
public CheckerInvocationContext(IResource resource) {
this.resource = resource;
this.sessionReporter = sessionReporter;
objectStorage = new HashMap<Class<?>, Object>();
}
public IResource getResource() {
return resource;
}
public IProblemReporter getProblemReporter() {
return sessionReporter;
@SuppressWarnings("unchecked")
public synchronized <T> T get(Class<T> objectClass) {
T object = (T) objectStorage.get(objectClass);
if (object != null)
return object;
for (Map.Entry<Class<?>, Object> entry : objectStorage.entrySet()) {
if (objectClass.isAssignableFrom(entry.getKey()))
return (T) entry.getValue();
}
return null;
}
/*
* (non-Javadoc)
* @see ICheckerInvocationContext#add(Object)
*/
public synchronized <T extends ICodanDisposable> void add(T object) {
Object old = objectStorage.put(object.getClass(), object);
if (old != null && object != old) {
objectStorage.put(old.getClass(), old); // Restore old value.
throw new IllegalArgumentException();
}
}
/*
* (non-Javadoc)
* @see IDisposableCache#dispose()
*/
public void dispose() {
for (Map.Entry<Class<?>, Object> entry : objectStorage.entrySet()) {
Object obj = entry.getValue();
if (obj instanceof ICodanDisposable) {
((ICodanDisposable) obj).dispose();
}
}
objectStorage.clear();
}
}

View file

@ -6,7 +6,8 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alena Laskavaia - initial API and implementation
* Alena Laskavaia - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.codan.internal.core;
@ -17,6 +18,7 @@ import org.eclipse.cdt.codan.core.Messages;
import org.eclipse.cdt.codan.core.model.CheckerLaunchMode;
import org.eclipse.cdt.codan.core.model.Checkers;
import org.eclipse.cdt.codan.core.model.IChecker;
import org.eclipse.cdt.codan.core.model.ICheckerInvocationContext;
import org.eclipse.cdt.codan.core.model.ICodanBuilder;
import org.eclipse.cdt.codan.core.model.IRunnableInEditorChecker;
import org.eclipse.core.resources.IContainer;
@ -27,6 +29,7 @@ import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
/**
@ -34,7 +37,7 @@ import org.eclipse.core.runtime.SubProgressMonitor;
*/
public class CodanBuilder extends IncrementalProjectBuilder implements ICodanBuilder {
/**
* codan builder id
* Codan builder id
*/
public static final String BUILDER_ID = "org.eclipse.cdt.codan.core.codanBuilder"; //$NON-NLS-1$
@ -126,39 +129,43 @@ public class CodanBuilder extends IncrementalProjectBuilder implements ICodanBui
// System.err.println("processing " + resource);
monitor.beginTask(Messages.CodanBuilder_Code_Analysis_On + resource, checkers + memsize * tick);
try {
for (IChecker checker : chegistry) {
try {
if (monitor.isCanceled())
return;
if (doesCheckerSupportLaunchMode(checker, checkerLaunchMode)
&& checker.enabledInContext(resource)
&& chegistry.isCheckerEnabledForLaunchMode(checker, resource, checkerLaunchMode)) {
synchronized (checker) {
try {
checker.before(resource);
if (chegistry.isCheckerEnabled(checker, resource)) {
//long time = System.currentTimeMillis();
if (checkerLaunchMode == CheckerLaunchMode.RUN_AS_YOU_TYPE) {
((IRunnableInEditorChecker) checker).processModel(model);
} else {
checker.processResource(resource);
ICheckerInvocationContext context = new CheckerInvocationContext(resource);
try {
for (IChecker checker : chegistry) {
try {
if (monitor.isCanceled())
return;
if (doesCheckerSupportLaunchMode(checker, checkerLaunchMode)
&& checker.enabledInContext(resource)
&& chegistry.isCheckerEnabledForLaunchMode(checker, resource, checkerLaunchMode)) {
synchronized (checker) {
try {
checker.before(resource);
if (chegistry.isCheckerEnabled(checker, resource)) {
//long time = System.currentTimeMillis();
if (checkerLaunchMode == CheckerLaunchMode.RUN_AS_YOU_TYPE) {
((IRunnableInEditorChecker) checker).processModel(model, context);
} else {
checker.processResource(resource, context);
}
// System.err.println("Checker "
// + checker.getClass() + " worked "
// + (System.currentTimeMillis() - time));
}
// System.err
// .println("Checker "
// + checker.getClass()
// + " worked "
// + (System
// .currentTimeMillis() - time));
} finally {
checker.after(resource);
}
} finally {
checker.after(resource);
}
}
monitor.worked(1);
} catch (OperationCanceledException e) {
return;
} catch (Throwable e) {
CodanCorePlugin.log(e);
}
monitor.worked(1);
} catch (Throwable e) {
CodanCorePlugin.log(e);
}
} finally {
context.dispose();
}
if (resource instanceof IContainer
&& (checkerLaunchMode == CheckerLaunchMode.RUN_ON_FULL_BUILD || checkerLaunchMode == CheckerLaunchMode.RUN_ON_DEMAND)) {