| /******************************************************************************* |
| * Copyright (c) 2008, 2016 Wind River Systems, 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: |
| * Markus Schorn - initial API and implementation |
| * Sergey Prigogin (Google) |
| * Thomas Corbat (IFS) |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.core.dom.parser; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.WeakHashMap; |
| import java.util.concurrent.Semaphore; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.eclipse.cdt.core.CCorePlugin; |
| import org.eclipse.cdt.core.dom.IName; |
| import org.eclipse.cdt.core.dom.ast.ASTGenericVisitor; |
| import org.eclipse.cdt.core.dom.ast.ASTVisitor; |
| import org.eclipse.cdt.core.dom.ast.IASTComment; |
| import org.eclipse.cdt.core.dom.ast.IASTDeclaration; |
| import org.eclipse.cdt.core.dom.ast.IASTFileLocation; |
| import org.eclipse.cdt.core.dom.ast.IASTName; |
| import org.eclipse.cdt.core.dom.ast.IASTNode; |
| import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; |
| import org.eclipse.cdt.core.dom.ast.IASTNodeSelector; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; |
| import org.eclipse.cdt.core.dom.ast.IASTProblem; |
| import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; |
| import org.eclipse.cdt.core.dom.ast.IASTTypeId; |
| import org.eclipse.cdt.core.dom.ast.IBinding; |
| import org.eclipse.cdt.core.dom.ast.IMacroBinding; |
| import org.eclipse.cdt.core.dom.ast.INodeFactory; |
| import org.eclipse.cdt.core.dom.ast.IScope; |
| import org.eclipse.cdt.core.dom.ast.IType; |
| import org.eclipse.cdt.core.dom.parser.IBuiltinBindingsProvider; |
| import org.eclipse.cdt.core.index.IIndex; |
| import org.eclipse.cdt.core.index.IIndexFile; |
| import org.eclipse.cdt.core.index.IIndexFileSet; |
| import org.eclipse.cdt.core.model.ITranslationUnit; |
| import org.eclipse.cdt.core.parser.ISignificantMacros; |
| import org.eclipse.cdt.core.parser.util.ArrayUtil; |
| import org.eclipse.cdt.internal.core.index.IndexBasedFileContentProvider; |
| import org.eclipse.cdt.internal.core.index.IndexFileSet; |
| import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver; |
| import org.eclipse.cdt.internal.core.parser.scanner.ISkippedIndexedFilesListener; |
| import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent; |
| import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContentProvider; |
| import org.eclipse.core.runtime.CoreException; |
| |
| /** |
| * Abstract base class for all translation units. |
| * |
| * This class and other ASTNode subclasses are not thread safe. |
| * Even 'get' methods may cause changes to the underlying object. |
| */ |
| public abstract class ASTTranslationUnit extends ASTNode implements IASTTranslationUnit, ISkippedIndexedFilesListener { |
| private static final IASTPreprocessorStatement[] EMPTY_PREPROCESSOR_STATEMENT_ARRAY = {}; |
| private static final IASTPreprocessorMacroDefinition[] EMPTY_PREPROCESSOR_MACRODEF_ARRAY = {}; |
| private static final IASTPreprocessorIncludeStatement[] EMPTY_PREPROCESSOR_INCLUSION_ARRAY = {}; |
| private static final IASTProblem[] EMPTY_PROBLEM_ARRAY = {}; |
| private static final String EMPTY_STRING = ""; //$NON-NLS-1$ |
| |
| private IASTDeclaration[] fAllDeclarations; |
| private IASTDeclaration[] fActiveDeclarations; |
| private int fLastDeclaration= -1; |
| |
| protected ILocationResolver fLocationResolver; |
| private IIndex fIndex; |
| private boolean fIsHeader= true; |
| private IIndexFileSet fIndexFileSet; |
| private IIndexFileSet fASTFileSet; |
| private INodeFactory fNodeFactory; |
| private boolean fForContentAssist; |
| private ITranslationUnit fOriginatingTranslationUnit; |
| private ISignificantMacros fSignificantMacros= ISignificantMacros.NONE; |
| private boolean fPragmaOnceSemantics; |
| private SizeofCalculator fSizeofCalculator; |
| /** The semaphore controlling exclusive access to the AST. */ |
| private final Semaphore fSemaphore= new Semaphore(1); |
| private boolean fBasedOnIncompleteIndex; |
| private boolean fNodesOmitted; |
| private IBuiltinBindingsProvider fBuiltinBindingsProvider; |
| |
| // Caches |
| private final WeakHashMap<IType, String> fUnnormalizedTypeStringCache = new WeakHashMap<>(); |
| private final WeakHashMap<IType, String> fNormalizedTypeStringCache = new WeakHashMap<>(); |
| |
| @Override |
| public final IASTTranslationUnit getTranslationUnit() { |
| return this; |
| } |
| |
| @Override |
| public final void addDeclaration(IASTDeclaration d) { |
| if (d != null) { |
| d.setParent(this); |
| d.setPropertyInParent(OWNED_DECLARATION); |
| fAllDeclarations = ArrayUtil.appendAt(IASTDeclaration.class, |
| fAllDeclarations, ++fLastDeclaration, d); |
| fActiveDeclarations= null; |
| } |
| } |
| |
| @Override |
| public final IASTDeclaration[] getDeclarations() { |
| IASTDeclaration[] active= fActiveDeclarations; |
| if (active == null) { |
| active = ASTQueries.extractActiveDeclarations(fAllDeclarations, fLastDeclaration+1); |
| fActiveDeclarations= active; |
| } |
| return active; |
| } |
| |
| @Override |
| public final IASTDeclaration[] getDeclarations(boolean includeInactive) { |
| if (includeInactive) { |
| fAllDeclarations= ArrayUtil.trimAt(IASTDeclaration.class, |
| fAllDeclarations, fLastDeclaration); |
| return fAllDeclarations; |
| } |
| return getDeclarations(); |
| } |
| |
| public final void replace(IASTNode child, IASTNode other) { |
| assert child.isActive() == other.isActive(); |
| for (int i = 0; i <= fLastDeclaration; ++i) { |
| if (fAllDeclarations[i] == child) { |
| other.setParent(child.getParent()); |
| other.setPropertyInParent(child.getPropertyInParent()); |
| fAllDeclarations[i] = (IASTDeclaration) other; |
| fActiveDeclarations= null; |
| return; |
| } |
| } |
| } |
| |
| @Override |
| public final IName[] getDeclarations(IBinding binding) { |
| IName[] names= getDeclarationsInAST(binding); |
| if (names.length == 0 && fIndex != null) { |
| try { |
| names = fIndex.findDeclarations(binding); |
| } catch (CoreException e) { |
| CCorePlugin.log(e); |
| return names; |
| } |
| } |
| |
| return names; |
| } |
| |
| protected final IASTName[] getMacroDefinitionsInAST(IMacroBinding binding) { |
| if (fLocationResolver == null) |
| return IASTName.EMPTY_NAME_ARRAY; |
| |
| IASTName[] declarations = fLocationResolver.getDeclarations(binding); |
| int j = 0; |
| for (int i = 0; i < declarations.length; i++) { |
| IASTName name = declarations[i]; |
| if (name.isPartOfTranslationUnitFile()) { |
| declarations[j++] = name; |
| } |
| } |
| if (j < declarations.length) |
| return j > 0 ? Arrays.copyOf(declarations, j) : IASTName.EMPTY_NAME_ARRAY; |
| return declarations; |
| } |
| |
| protected final IASTName[] getMacroReferencesInAST(IMacroBinding binding) { |
| if (fLocationResolver == null) |
| return IASTName.EMPTY_NAME_ARRAY; |
| return fLocationResolver.getReferences(binding); |
| } |
| |
| @Override |
| public final IName[] getDefinitions(IBinding binding) { |
| IName[] names= getDefinitionsInAST(binding); |
| if (names.length == 0 && fIndex != null) { |
| try { |
| names= fIndex.findDefinitions(binding); |
| } catch (CoreException e) { |
| CCorePlugin.log(e); |
| return names; |
| } |
| } |
| return names; |
| } |
| |
| @Override |
| public final IASTPreprocessorMacroDefinition[] getMacroDefinitions() { |
| if (fLocationResolver == null) |
| return EMPTY_PREPROCESSOR_MACRODEF_ARRAY; |
| return fLocationResolver.getMacroDefinitions(); |
| } |
| |
| @Override |
| public IASTPreprocessorMacroExpansion[] getMacroExpansions() { |
| if (fLocationResolver == null) |
| return IASTPreprocessorMacroExpansion.EMPTY_ARRAY; |
| return fLocationResolver.getMacroExpansions(getFileLocation()); |
| } |
| |
| @Override |
| public final IASTPreprocessorMacroDefinition[] getBuiltinMacroDefinitions() { |
| if (fLocationResolver == null) |
| return EMPTY_PREPROCESSOR_MACRODEF_ARRAY; |
| return fLocationResolver.getBuiltinMacroDefinitions(); |
| } |
| |
| @Override |
| public final IASTPreprocessorIncludeStatement[] getIncludeDirectives() { |
| if (fLocationResolver == null) |
| return EMPTY_PREPROCESSOR_INCLUSION_ARRAY; |
| return fLocationResolver.getIncludeDirectives(); |
| } |
| |
| @Override |
| public final IASTPreprocessorStatement[] getAllPreprocessorStatements() { |
| if (fLocationResolver == null) |
| return EMPTY_PREPROCESSOR_STATEMENT_ARRAY; |
| return fLocationResolver.getAllPreprocessorStatements(); |
| } |
| |
| public final void setLocationResolver(ILocationResolver resolver) { |
| fLocationResolver= resolver; |
| resolver.setRootNode(this); |
| } |
| |
| @Override |
| public final IASTProblem[] getPreprocessorProblems() { |
| if (fLocationResolver == null) |
| return EMPTY_PROBLEM_ARRAY; |
| IASTProblem[] result = fLocationResolver.getScannerProblems(); |
| for (int i = 0; i < result.length; ++i) { |
| IASTProblem p = result[i]; |
| p.setParent(this); |
| p.setPropertyInParent(IASTTranslationUnit.SCANNER_PROBLEM); |
| } |
| return result; |
| } |
| |
| @Override |
| public final int getPreprocessorProblemsCount() { |
| return fLocationResolver == null ? 0 : fLocationResolver.getScannerProblemsCount(); |
| } |
| |
| @Override |
| public final String getFilePath() { |
| if (fLocationResolver == null) |
| return EMPTY_STRING; |
| return fLocationResolver.getTranslationUnitPath(); |
| } |
| |
| @Override |
| public final boolean accept(ASTVisitor action) { |
| if (action.shouldVisitTranslationUnit) { |
| switch (action.visit(this)) { |
| case ASTVisitor.PROCESS_ABORT: return false; |
| case ASTVisitor.PROCESS_SKIP: return true; |
| default: break; |
| } |
| } |
| IASTDeclaration[] decls = getDeclarations(action.includeInactiveNodes); |
| for (IASTDeclaration decl : decls) { |
| if (!decl.accept(action)) return false; |
| } |
| |
| if (action.shouldVisitTranslationUnit && action.leave(this) == ASTVisitor.PROCESS_ABORT) |
| return false; |
| |
| return true; |
| } |
| |
| @Override |
| public final IASTFileLocation flattenLocationsToFile(IASTNodeLocation[] nodeLocations) { |
| if (fLocationResolver == null) |
| return null; |
| return fLocationResolver.flattenLocations(nodeLocations); |
| } |
| |
| @Override |
| public final IDependencyTree getDependencyTree() { |
| if (fLocationResolver == null) |
| return null; |
| return fLocationResolver.getDependencyTree(); |
| } |
| |
| @Override |
| public final String getContainingFilename(int offset) { |
| if (fLocationResolver == null) |
| return EMPTY_STRING; |
| return fLocationResolver.getContainingFilePath(offset); |
| } |
| |
| @Override |
| public final IIndex getIndex() { |
| return fIndex; |
| } |
| |
| @Override |
| public final void setIndex(IIndex index) { |
| this.fIndex = index; |
| if (index != null) { |
| fIndexFileSet= index.createFileSet(); |
| fASTFileSet= index.createFileSet(); |
| } |
| } |
| |
| @Override |
| public final INodeFactory getASTNodeFactory() { |
| return fNodeFactory; |
| } |
| |
| public final void setASTNodeFactory(INodeFactory nodeFactory) { |
| this.fNodeFactory = nodeFactory; |
| } |
| |
| @Override |
| public final IASTComment[] getComments() { |
| if (fLocationResolver != null) { |
| return fLocationResolver.getComments(); |
| } |
| return IASTComment.EMPTY_COMMENT_ARRAY; |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public final <T> T getAdapter(Class<T> adapter) { |
| if (adapter.isInstance(fLocationResolver)) { |
| return (T) fLocationResolver; |
| } |
| if (adapter.isInstance(fIndexFileSet)) { |
| return (T) fIndexFileSet; |
| } |
| if (fLocationResolver != null && adapter.isInstance(fLocationResolver.getLexerOptions())) { |
| return (T) fLocationResolver.getLexerOptions(); |
| } |
| return null; |
| } |
| |
| @Override |
| public final boolean isHeaderUnit() { |
| return fIsHeader; |
| } |
| |
| @Override |
| public final void setIsHeaderUnit(boolean headerUnit) { |
| fIsHeader= headerUnit; |
| } |
| |
| public boolean isForContentAssist() { |
| return fForContentAssist; |
| } |
| |
| public final void setIsForContentAssist(boolean forContentAssist) { |
| fForContentAssist= forContentAssist; |
| } |
| |
| @Override |
| public boolean isBasedOnIncompleteIndex() { |
| return fBasedOnIncompleteIndex; |
| } |
| |
| public void setBasedOnIncompleteIndex(boolean basedOnIncompleteIndex) { |
| fBasedOnIncompleteIndex = basedOnIncompleteIndex; |
| } |
| |
| @Override |
| public void skippedFile(int offset, InternalFileContent fileContent) { |
| if (fIndexFileSet != null) { |
| List<IIndexFile> files= fileContent.getFilesIncluded(); |
| for (IIndexFile indexFile : files) { |
| fASTFileSet.remove(indexFile); |
| fIndexFileSet.add(indexFile); |
| } |
| } |
| } |
| |
| @Override |
| public final IIndexFileSet getIndexFileSet() { |
| return fIndexFileSet; |
| } |
| |
| @Override |
| public void parsingFile(InternalFileContentProvider provider, InternalFileContent fc) { |
| if (fASTFileSet != null) { |
| if (provider instanceof IndexBasedFileContentProvider) { |
| try { |
| for (IIndexFile file : ((IndexBasedFileContentProvider) provider).findIndexFiles(fc)) { |
| if (!fIndexFileSet.contains(file)) { |
| fASTFileSet.add(file); |
| } |
| } |
| } catch (CoreException e) { |
| // Ignore, tracking of replaced files fails. |
| } |
| } |
| } |
| } |
| |
| @Override |
| public final IIndexFileSet getASTFileSet() { |
| return fASTFileSet; |
| } |
| |
| @Override |
| public final IASTNode selectNodeForLocation(String path, int realOffset, int realLength) { |
| return getNodeSelector(path).findNode(realOffset, realLength); |
| } |
| |
| @Override |
| public final IASTNodeSelector getNodeSelector(String filePath) { |
| return new ASTNodeSelector(this, fLocationResolver, filePath); |
| } |
| |
| /** |
| * Must be called by the parser, before the ast is passed to the clients. |
| */ |
| public abstract void resolveAmbiguities(); |
| |
| /** |
| * Can be called to create a type for a type-id. |
| */ |
| protected abstract IType createType(IASTTypeId typeid); |
| |
| /** |
| * Maps an index scope to the AST. |
| * |
| * @param scope a scope, possibly from index |
| * @return the corresponding scope in the AST, or the original scope if it doesn't have |
| * a counterpart in the AST. |
| */ |
| public abstract IScope mapToASTScope(IScope scope); |
| |
| protected <T extends ASTTranslationUnit> T copy(T copy, CopyStyle style) { |
| copy.setIndex(fIndex); |
| ASTTranslationUnit target = copy; |
| target.fIsHeader = fIsHeader; |
| target.fNodeFactory = fNodeFactory; |
| target.setLocationResolver(fLocationResolver); |
| target.fForContentAssist = fForContentAssist; |
| target.fOriginatingTranslationUnit = fOriginatingTranslationUnit; |
| target.fNodesOmitted = fNodesOmitted; |
| |
| for (IASTDeclaration declaration : getDeclarations()) { |
| copy.addDeclaration(declaration == null ? null : declaration.copy(style)); |
| } |
| |
| return super.copy(copy, style); |
| } |
| |
| @Override |
| public final void freeze() { |
| accept(new ASTGenericVisitor(true) { |
| @Override |
| protected int genericVisit(IASTNode node) { |
| ((ASTNode) node).setIsFrozen(); |
| return PROCESS_CONTINUE; |
| } |
| }); |
| |
| if (IndexFileSet.sDEBUG && fIndexFileSet != null && fASTFileSet != null) { |
| long t = ((IndexFileSet) fIndexFileSet).getTimingContainsDeclarationNanos() + |
| ((IndexFileSet) fASTFileSet).getTimingContainsDeclarationNanos(); |
| String forName = fOriginatingTranslationUnit == null ? |
| "" : " for " + fOriginatingTranslationUnit.getElementName(); //$NON-NLS-1$ //$NON-NLS-2$ |
| System.out.println(String.format("IndexFileSet.containsDeclaration%s took %.2g ms", forName, t / 1.e6)); //$NON-NLS-1$ |
| } |
| } |
| |
| @Override |
| public ITranslationUnit getOriginatingTranslationUnit() { |
| return fOriginatingTranslationUnit; |
| } |
| |
| public void setOriginatingTranslationUnit(ITranslationUnit tu) { |
| this.fOriginatingTranslationUnit = tu; |
| } |
| |
| @Override |
| public ISignificantMacros getSignificantMacros() { |
| return fSignificantMacros; |
| } |
| |
| @Override |
| public void setSignificantMacros(ISignificantMacros sigMacros) { |
| assertNotFrozen(); |
| if (sigMacros != null) |
| fSignificantMacros= sigMacros; |
| } |
| |
| @Override |
| public boolean hasPragmaOnceSemantics() { |
| return fPragmaOnceSemantics; |
| } |
| |
| @Override |
| public void setPragmaOnceSemantics(boolean value) { |
| assertNotFrozen(); |
| fPragmaOnceSemantics= value; |
| } |
| |
| /** |
| * Starts exclusive access. |
| * |
| * @throws InterruptedException if the current thread is interrupted |
| */ |
| public void beginExclusiveAccess() throws InterruptedException { |
| fSemaphore.acquire(); |
| } |
| |
| /** |
| * Starts exclusive access. |
| * |
| * @param timeoutMillis the maximum time to wait in milliseconds |
| * @return {@code true} if exclusive access was acquired, or {@code false} if it |
| * was not possible to acquire exclusive access before the timeout expired |
| * @throws InterruptedException if the current thread is interrupted |
| */ |
| public boolean tryBeginExclusiveAccess(long timeoutMillis) throws InterruptedException { |
| return fSemaphore.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS); |
| } |
| |
| public void endExclusiveAccess() { |
| fSemaphore.release(); |
| } |
| |
| public SizeofCalculator getSizeofCalculator() { |
| if (fSizeofCalculator == null) { |
| fSizeofCalculator = new SizeofCalculator(this); |
| } |
| return fSizeofCalculator; |
| } |
| |
| @Override |
| public boolean hasNodesOmitted() { |
| return fNodesOmitted; |
| } |
| |
| @Override |
| public void setHasNodesOmitted(boolean hasNodesOmitted) { |
| assertNotFrozen(); |
| fNodesOmitted = hasNodesOmitted; |
| } |
| |
| /** |
| * If ambiguity resolution is in progress, and processing of 'node' has been deferred, |
| * process it now. Has no effect if ambiguity resolution is not in progress. |
| */ |
| public void resolvePendingAmbiguities(IASTNode node) {} |
| |
| public void setupBuiltinBindings(IBuiltinBindingsProvider builtinBindingsProvider) { |
| IScope tuScope = getScope(); |
| |
| IBinding[] bindings = builtinBindingsProvider.getBuiltinBindings(tuScope); |
| for (IBinding binding : bindings) { |
| ASTInternal.addBinding(tuScope, binding); |
| } |
| |
| // Save the builtin bindings provider for later use by isKnownBuiltin(). |
| fBuiltinBindingsProvider = builtinBindingsProvider; |
| } |
| |
| public boolean isKnownBuiltin(char[] builtinName) { |
| if (fBuiltinBindingsProvider != null) { |
| return fBuiltinBindingsProvider.isKnownBuiltin(builtinName); |
| } |
| return false; |
| } |
| |
| public Map<IType, String> getTypeStringCache(boolean normalized) { |
| return normalized ? fNormalizedTypeStringCache : fUnnormalizedTypeStringCache; |
| } |
| } |