/******************************************************************************* * Copyright (c) 2005, 2015 QNX Software Systems 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: * Doug Schaefer (QNX) - Initial API and implementation * Markus Schorn (Wind River Systems) * IBM Corporation * Andrew Ferguson (Symbian) * Anton Leherbauer (Wind River Systems) * Sergey Prigogin (Google) * Jens Elmenthaler - http://bugs.eclipse.org/173458 (camel case completion) *******************************************************************************/ package org.eclipse.cdt.internal.core.pdom; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.dom.ILinkage; import org.eclipse.cdt.core.dom.IPDOMNode; import org.eclipse.cdt.core.dom.IPDOMVisitor; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.ICompositeType; import org.eclipse.cdt.core.dom.ast.IEnumeration; import org.eclipse.cdt.core.dom.ast.IEnumerator; import org.eclipse.cdt.core.dom.ast.IField; import org.eclipse.cdt.core.dom.ast.IFunction; import org.eclipse.cdt.core.dom.ast.IMacroBinding; import org.eclipse.cdt.core.dom.ast.IParameter; import org.eclipse.cdt.core.dom.ast.ITypedef; import org.eclipse.cdt.core.dom.ast.IVariable; import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPField; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace; import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable; import org.eclipse.cdt.core.index.IIndexBinding; import org.eclipse.cdt.core.index.IIndexFileLocation; import org.eclipse.cdt.core.index.IIndexLinkage; import org.eclipse.cdt.core.index.IIndexLocationConverter; import org.eclipse.cdt.core.index.IIndexMacro; import org.eclipse.cdt.core.index.IIndexMacroContainer; import org.eclipse.cdt.core.index.IndexFilter; import org.eclipse.cdt.core.parser.ISignificantMacros; import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.Linkage; import org.eclipse.cdt.internal.core.index.IIndexCBindingConstants; import org.eclipse.cdt.internal.core.index.IIndexFragment; import org.eclipse.cdt.internal.core.index.IIndexFragmentBinding; import org.eclipse.cdt.internal.core.index.IIndexFragmentFile; import org.eclipse.cdt.internal.core.index.IIndexFragmentFileSet; import org.eclipse.cdt.internal.core.index.IIndexFragmentInclude; import org.eclipse.cdt.internal.core.index.IIndexFragmentName; import org.eclipse.cdt.internal.core.index.IIndexScope; import org.eclipse.cdt.internal.core.pdom.db.BTree; import org.eclipse.cdt.internal.core.pdom.db.ChunkCache; import org.eclipse.cdt.internal.core.pdom.db.DBProperties; import org.eclipse.cdt.internal.core.pdom.db.Database; import org.eclipse.cdt.internal.core.pdom.db.IBTreeComparator; import org.eclipse.cdt.internal.core.pdom.db.IBTreeVisitor; import org.eclipse.cdt.internal.core.pdom.dom.BindingCollector; import org.eclipse.cdt.internal.core.pdom.dom.FindBinding; import org.eclipse.cdt.internal.core.pdom.dom.IPDOMIterator; import org.eclipse.cdt.internal.core.pdom.dom.IPDOMLinkageFactory; import org.eclipse.cdt.internal.core.pdom.dom.MacroContainerCollector; import org.eclipse.cdt.internal.core.pdom.dom.MacroContainerPatternCollector; import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding; import org.eclipse.cdt.internal.core.pdom.dom.PDOMFile; import org.eclipse.cdt.internal.core.pdom.dom.PDOMInclude; import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage; import org.eclipse.cdt.internal.core.pdom.dom.PDOMMacro; import org.eclipse.cdt.internal.core.pdom.dom.PDOMMacroContainer; import org.eclipse.cdt.internal.core.pdom.dom.PDOMMacroReferenceName; import org.eclipse.cdt.internal.core.pdom.dom.PDOMName; import org.eclipse.cdt.internal.core.pdom.dom.PDOMNamedNode; import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode; import org.eclipse.cdt.internal.core.pdom.tag.PDOMTagIndex; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.Status; /** * Database for storing semantic information for one project. */ public class PDOM extends PlatformObject implements IPDOM { private static final int CANCELLATION_CHECK_INTERVAL = 500; private static final int BLOCKED_WRITE_LOCK_OUTPUT_INTERVAL = 30000; private static final int LONG_WRITE_LOCK_REPORT_THRESHOLD = 1000; private static final int LONG_READ_LOCK_WAIT_REPORT_THRESHOLD = 1000; static boolean sDEBUG_LOCKS= false; // Initialized in the PDOMManager, because IBM needs PDOM independent of runtime plugin. /** * Identifier for PDOM format * @see IIndexFragment#PROPERTY_FRAGMENT_FORMAT_ID */ public static final String FRAGMENT_PROPERTY_VALUE_FORMAT_ID= "org.eclipse.cdt.internal.core.pdom.PDOM"; //$NON-NLS-1$ /* * PDOM internal format history * * #x# = the version was used in an official release * * 0 - the beginning of it all * 1 - first change to kick off upgrades * 2 - added file inclusions * 3 - added macros and change string implementation * 4 - added parameters in C++ * 5 - added types and restructured nodes a bit * 6 - function style macros * # 7#- class key - <<CDT 3.1>> * 8 - enumerators * 9 - base classes * 10 - typedefs, types on C++ variables * #11#- changed how members work - <<CDT 3.1.1>>, <<CDT 3.1.2>> * 12 - one more change for members (is-a list -> has-a list) * 13 - CV-qualifiers, storage class specifiers, function/method annotations * 14 - added timestamps for files (bug 149571) * 15 - fixed offsets for pointer types and qualifier types and PDOMCPPVariable (bug 160540). * 16 - have PDOMCPPField store type information, and PDOMCPPNamespaceAlias store what it is aliasing * 17 - use single linked list for names in file, adds a link to enclosing definition name. * 18 - distinction between c-unions and c-structs. * 19 - alter representation of paths in the PDOM (162172) * 20 - add pointer to member types, array types, return types for functions * 21 - change representation of paths in the PDOM (167549) * 22 - fix inheritance relations (167396) * 23 - types on c-variables, return types on c-functions * 24 - file local scopes (161216) * 25 - change ordering of bindings (175275) * 26 - add properties storage * 27 - templates: classes, functions, limited nesting support, only template type parameters * 28 - templates: class instance/specialization base classes * 29 - includes: fixed modeling of unresolved includes (180159) * 30 - templates: method/constructor templates, typedef specializations * 31 - macros: added file locations * 32 - support stand-alone function types (181936) * 33 - templates: constructor instances * 34 - fix for base classes represented by qualified names (183843) * 35 - add scanner configuration hash-code (62366) * #36#- changed chunk size back to 4K (184892) - <<CDT 4.0>> * #37#- added index for nested bindings (189811), compatible with version 36 - <<CDT 4.0.1>> * 38 - added b-tree for macros (193056), compatible with version 36 and 37 * #39#- added flag for function-style macros (208558), compatible with version 36,37,38 - <<CDT 4.0.2>> * * 50 - support for complex, imaginary and long long (bug 209049). * 51 - modeling extern "C" (bug 191989) * 52 - files per linkage (bug 191989) * 53 - polymorphic method calls (bug 156691) * 54 - optimization of database size (bug 210392) * 55 - generalization of local bindings (bug 215783) * 56 - using directives (bug 216527) * 57.0 - macro references (bug 156561) * 58.0 - non-type parameters (bug 207840) * 59.0 - changed modeling of deferred class instances (bug 229218) * 60.0 - store integral values with basic types (bug 207871) * #61.0# - properly insert macro undef statements into macro-containers (bug 234591) - <<CDT 5.0>> * * CDT 6.0 development * 70.0 - cleaned up templates, fixes bug 236197 * 71.0 - proper support for anonymous unions, bug 206450 * 72.0 - store project-relative paths for resources that belong to the project, bug 239472 * 72.1 - store flag for pure virtual methods. * 73.0 - add values for variables and enumerations, bug 250788 * 74.0 - changes for proper template argument support, bug 242668 * 75.0 - support for friends, bug 250167 * 76.0 - support for exception specification, bug 252697 * 77.0 - support for parameter annotations, bug 254520 * 78.0 - support for updating class templates, bug 254520 * 79.0 - instantiation of values, bug 245027 * 80.0 - support for specializations of partial specializations, bug 259872 * 81.0 - change to c++ function types, bug 264479 * 82.0 - offsets for using directives, bug 270806 * #83.0# - unconditionally store name in PDOMInclude, bug 272815 - <<CDT 6.0>> * #84.0# - storing free record pointers as (ptr>>3), bug 279620 - <<CDT 6.0.1>> * * CDT 7.0 development (versions not supported on the 6.0.x branch) * 90.0 - support for array sizes, bug 269926 * 91.0 - storing unknown bindings other than unknown class types, bug 284686. * 92.0 - simplification of basic types, bug 231859. * 93.0 - further simplification of basic types, bug 231859. * 94.0 - new model for storing types, bug 294306. * 95.0 - parameter packs, bug 294730. * 96.0 - storing pack expansions in the template parameter map, bug 294730. * 97.0 - storing file contents hash in PDOMFile, bug 302083. * #98.0# - strongly typed enums, bug 305975. <<CDT 7.0.0>> * #99.0# - correct marshalling of basic types, bug 319186. <<CDT 7.0.1>> * * CDT 8.0 development (versions not supported on the 7.0.x branch) * 110.0 - update index on encoding change, bug 317435. * 111.0 - correct marshalling of basic types, bug 319186. * 111.1 - defaulted and deleted functions, bug 305978 * 112.0 - inline namespaces, bug 305980 * 113.0 - Changed marshaling of values, bug 327878 * #114.0# - Partial specializations for class template specializations, bug 332884. * - Corrected signatures for function templates, bug 335062. <<CDT 8.0>> * * CDT 8.1 development (versions not supported on the 8.0.x branch) * 120.0 - Enumerators in global index, bug 356235 * 120.1 - Specializations of using declarations, bug 357293. * 121.0 - Multiple variants of included header file, bug 197989. * 122.0 - Compacting strings * 123.0 - Combined file size and encoding hash code. * 124.0 - GCC attributes and NO_RETURN flag for functions. * #125.0# - Indexes for unresolved includes and files indexed with I/O errors. <<CDT 8.1>> * * CDT 8.2 development (versions not supported on the 8.1.x branch) * 130.0 - Dependent expressions, bug 299911. * 131.0 - Dependent expressions part 2, bug 299911. * 132.0 - Explicit virtual overrides, bug 380623. * 133.0 - Storing template arguments via direct marshalling, bug 299911. * 134.0 - Storing unknown bindings via direct marshalling, bug 381824. * 135.0 - Changed marshalling of EvalUnary, bug 391001. * 136.0 - Extended CPPTemplateTypeArgument to include the original type, bug 392278. * 137.0 - Fixed serialization of very large types and template arguments, bug 392278. * 138.0 - Constexpr functions, bug 395238. * 139.0 - More efficient and robust storage of types and template arguments, bug 395243. * 140.0 - Enumerators with dependent values, bug 389009. * 140.1 - Mechanism for tagging nodes with extended data, bug 400020 * 141.0 - Storing enclosing template bindings for evaluations, bug 399829. * 142.0 - Changed marshalling of evaluations to allow more than 15 evaluation kinds, bug 401479. * 143.0 - Store implied object type in EvalFunctionSet, bug 402409. * 144.0 - Add support for storing function sets with zero functions in EvalFunctionSet, bug 402498. * 145.0 - Changed marshalling of CPPBasicType to store the associated numerical value, bug 407808. * 146.0 - Added visibility support on class type level, bug 402878. * #147.0# - Store whether function name is qualified in EvalFunctionSet, bug 408296. <<CDT 8.2>> * * CDT 8.3 development (versions not supported on the 8.2.x branch) * 160.0 - Store specialized template parameters of class/function template specializations, bug 407497. * 161.0 - Allow reference to PDOMBinding from other PDOMLinkages, bug 422681. * 162.0 - PDOMNode now stores the factoryId for loading, bug 422681. * #163.0# - QtLinkage changed storage format of QObject to accommodate QGadget. <<CDT 8.3>> * * CDT 8.4 development (versions not supported on the 8.3.x branch) * 170.0 - Unconditionally store arguments of EvalTypeId, bug 430230. * 171.0 - Replacement headers for Organize Includes, bug 414692. * #172.0# - Store default values for function parameters, bug 432701. <<CDT 8.4>> * * CDT 8.6 development (versions not supported on the 8.5.x branch) * 180.0 - Internal types of enumerators, bug 446711. * 180.1 - Storing types of unknown members, bug 447728. * 180.2 - Do not apply significant macros to source files, bug 450888. <<CDT 8.6>> * * CDT 8.7 development (versions not supported on the 8.6.x branch) * 181.0 - C function type with varargs, bug 452416. * 182.0 - A flag added to PDOMCPPClassSpecialization, bug 466362. <<CDT 8.7>> * * CDT 8.8 development (versions not supported on the 8.7.x branch) * 190.0 - Signature change for methods with ref-qualifiers, bug 470014. * 191.0 - Added EvalID.fIsPointerDeref, bug 472436. <<CDT 8.8>> * * CDT 9.0 development (versions not supported on the 8.8.x branch) * 200.0 - Added PDOMCPPAliasTemplateInstance, bug 486915. * 201.0 - PDOMCPPBase stores a CPPParameterPackType for pack expansions, bug 487703. <<CDT 9.0>> * * CDT 9.2 development (versions not supported on the 9.0.x branch) * 202.0 - C++14 constexpr evaluation, bug 490475. * 203.0 - Use 16 bits to store field position, bug 501616. * 204.0 - Do not store return expression in index, follow-up to bug 490475. * 205.0 - Reworked storage of annotations, bug 505832. * 206.0 - DependentValue split out from IntegralValue. * * CDT 9.3 development (versions not supported on the 9.2.x branch) * 207.0 - Store a caller record for macro reference names. */ private static final int MIN_SUPPORTED_VERSION= version(207, 0); private static final int MAX_SUPPORTED_VERSION= version(207, Short.MAX_VALUE); private static final int DEFAULT_VERSION = version(207, 0); private static int version(int major, int minor) { return (major << 16) + minor; } /** * Returns the version that shall be used when creating new databases. */ public static int getDefaultVersion() { return DEFAULT_VERSION; } public static boolean isSupportedVersion(int vers) { return vers >= MIN_SUPPORTED_VERSION && vers <= MAX_SUPPORTED_VERSION; } public static int getMinSupportedVersion() { return MIN_SUPPORTED_VERSION; } public static int getMaxSupportedVersion() { return MAX_SUPPORTED_VERSION; } public static String versionString(int version) { final int major= version >> 16; final int minor= version & 0xffff; return "" + major + '.' + minor; //$NON-NLS-1$ } public static final int LINKAGES = Database.DATA_AREA; public static final int FILE_INDEX = Database.DATA_AREA + 4; public static final int INDEX_OF_DEFECTIVE_FILES = Database.DATA_AREA + 8; public static final int INDEX_OF_FILES_WITH_UNRESOLVED_INCLUDES = Database.DATA_AREA + 12; public static final int PROPERTIES = Database.DATA_AREA + 16; public static final int TAG_INDEX = Database.DATA_AREA + 20; public static final int END= Database.DATA_AREA + 24; static { assert END <= Database.CHUNK_SIZE; } public static class ChangeEvent { public Set<IIndexFileLocation> fClearedFiles= new HashSet<>(); public Set<IIndexFileLocation> fFilesWritten= new HashSet<>(); private boolean fCleared; private boolean fReloaded; private boolean fNewFiles; private void setCleared() { fCleared= true; fReloaded= false; fNewFiles= false; fClearedFiles.clear(); fFilesWritten.clear(); } public boolean isCleared() { return fCleared; } public void setReloaded() { fReloaded= true; } public boolean isReloaded() { return fReloaded; } public void setHasNewFiles() { fNewFiles = true; } public boolean hasNewFiles() { return fNewFiles; } public boolean isTrivial() { return !fCleared && !fReloaded && !fNewFiles && fClearedFiles.isEmpty() && fFilesWritten.isEmpty(); } } public static interface IListener { public void handleChange(PDOM pdom, ChangeEvent event); } // Primitive comparator that compares database offsets of two records. private static final IBTreeComparator offsetComparator = new IBTreeComparator() { @Override public int compare(long record1, long record2) throws CoreException { return record1 < record2 ? -1 : record1 == record2 ? 0 : 1; } }; // Local caches protected Database db; private BTree fileIndex; private PDOMTagIndex tagIndex; private BTree indexOfDefectiveFiles; private BTree indexOfFiledWithUnresolvedIncludes; private final Map<Integer, PDOMLinkage> fLinkageIDCache = new HashMap<>(); private File fPath; private final IIndexLocationConverter locationConverter; private final Map<String, IPDOMLinkageFactory> fPDOMLinkageFactoryCache; private final HashMap<Object, Object> fResultCache= new HashMap<>(); private List<IListener> listeners; protected ChangeEvent fEvent= new ChangeEvent(); public PDOM(File dbPath, IIndexLocationConverter locationConverter, Map<String, IPDOMLinkageFactory> linkageFactoryMappings) throws CoreException { this(dbPath, locationConverter, ChunkCache.getSharedInstance(), linkageFactoryMappings); } public PDOM(File dbPath, IIndexLocationConverter locationConverter, ChunkCache cache, Map<String, IPDOMLinkageFactory> linkageFactoryMappings) throws CoreException { fPDOMLinkageFactoryCache = linkageFactoryMappings; loadDatabase(dbPath, cache); this.locationConverter = locationConverter; if (sDEBUG_LOCKS) { fLockDebugging= new HashMap<>(); System.out.println("Debugging PDOM Locks"); //$NON-NLS-1$ } } /** * Returns whether this PDOM can never be written to. Writable subclasses should return false. */ protected boolean isPermanentlyReadOnly() { return true; } private void loadDatabase(File dbPath, ChunkCache cache) throws CoreException { fPath= dbPath; final boolean lockDB= db == null || lockCount != 0; clearCaches(); db = new Database(fPath, cache, getDefaultVersion(), isPermanentlyReadOnly()); db.setLocked(lockDB); if (isSupportedVersion()) { readLinkages(); } db.setLocked(lockCount != 0); } public IIndexLocationConverter getLocationConverter() { return locationConverter; } public boolean isSupportedVersion() throws CoreException { final int version = db.getVersion(); return version >= MIN_SUPPORTED_VERSION && version <= MAX_SUPPORTED_VERSION; } private void readLinkages() throws CoreException { long record= getFirstLinkageRecord(); while (record != 0) { String linkageID= PDOMLinkage.getLinkageID(this, record).getString(); IPDOMLinkageFactory factory= fPDOMLinkageFactoryCache.get(linkageID); if (factory != null) { PDOMLinkage linkage= factory.getLinkage(this, record); fLinkageIDCache.put(linkage.getLinkageID(), linkage); } record= PDOMLinkage.getNextLinkageRecord(this, record); } } protected PDOMLinkage createLinkage(int linkageID) throws CoreException { PDOMLinkage pdomLinkage= fLinkageIDCache.get(linkageID); if (pdomLinkage == null) { final String linkageName= Linkage.getLinkageName(linkageID); IPDOMLinkageFactory factory= fPDOMLinkageFactoryCache.get(linkageName); if (factory != null) { return factory.createLinkage(this); } } return pdomLinkage; } public PDOMLinkage getLinkage(int linkageID) throws CoreException { return fLinkageIDCache.get(linkageID); } private Collection<PDOMLinkage> getLinkageList() { return fLinkageIDCache.values(); } public void accept(IPDOMVisitor visitor) throws CoreException { for (PDOMLinkage linkage : getLinkageList()) { linkage.accept(visitor); } } @Override public void addListener(IListener listener) { if (listeners == null) listeners = new LinkedList<>(); listeners.add(listener); } @Override public void removeListener(IListener listener) { if (listeners == null) return; listeners.remove(listener); } private void fireChange(ChangeEvent event) { if (listeners == null || event.isTrivial()) return; Iterator<IListener> i = listeners.iterator(); while (i.hasNext()) i.next().handleChange(this, event); } public Database getDB() { return db; } public BTree getFileIndex() throws CoreException { if (fileIndex == null) fileIndex = new BTree(getDB(), FILE_INDEX, new PDOMFile.Comparator(getDB())); return fileIndex; } public PDOMTagIndex getTagIndex() throws CoreException { if (tagIndex == null) { tagIndex = new PDOMTagIndex(db, TAG_INDEX); } return tagIndex; } /** * Returns the index of files that were read with I/O errors. */ public BTree getIndexOfDefectiveFiles() throws CoreException { if (indexOfDefectiveFiles == null) indexOfDefectiveFiles = new BTree(getDB(), INDEX_OF_DEFECTIVE_FILES, offsetComparator); return indexOfDefectiveFiles; } /** * Returns the index of files containing unresolved includes. */ public BTree getIndexOfFilesWithUnresolvedIncludes() throws CoreException { if (indexOfFiledWithUnresolvedIncludes == null) { indexOfFiledWithUnresolvedIncludes = new BTree(getDB(), INDEX_OF_FILES_WITH_UNRESOLVED_INCLUDES, offsetComparator); } return indexOfFiledWithUnresolvedIncludes; } @Deprecated @Override public PDOMFile getFile(int linkageID, IIndexFileLocation location) throws CoreException { PDOMLinkage linkage= getLinkage(linkageID); if (linkage == null) return null; return PDOMFile.findFile(linkage, getFileIndex(), location, locationConverter); } @Override public PDOMFile getFile(int linkageID, IIndexFileLocation location, ISignificantMacros macroDictionary) throws CoreException { PDOMLinkage linkage= getLinkage(linkageID); if (linkage == null) return null; return PDOMFile.findFile(linkage, getFileIndex(), location, locationConverter, macroDictionary); } public PDOMFile getFile(PDOMLinkage linkage, IIndexFileLocation location, ISignificantMacros macroDictionary) throws CoreException { return PDOMFile.findFile(linkage, getFileIndex(), location, locationConverter, macroDictionary); } @Override public IIndexFragmentFile[] getFiles(int linkageID, IIndexFileLocation location) throws CoreException { PDOMLinkage linkage= getLinkage(linkageID); if (linkage == null) return IIndexFragmentFile.EMPTY_ARRAY; return PDOMFile.findFiles(linkage, getFileIndex(), location, locationConverter); } @Override public IIndexFragmentFile[] getFiles(IIndexFileLocation location) throws CoreException { return PDOMFile.findFiles(this, getFileIndex(), location, locationConverter); } @Override public IIndexFragmentFile[] getAllFiles() throws CoreException { return getFiles(getFileIndex()); } @Override public IIndexFragmentFile[] getDefectiveFiles() throws CoreException { return getFiles(getIndexOfDefectiveFiles()); } @Override public IIndexFragmentFile[] getFilesWithUnresolvedIncludes() throws CoreException { return getFiles(getIndexOfFilesWithUnresolvedIncludes()); } private IIndexFragmentFile[] getFiles(BTree index) throws CoreException { final List<PDOMFile> files = new ArrayList<>(); index.accept(new IBTreeVisitor() { @Override public int compare(long record) throws CoreException { return 0; } @Override public boolean visit(long record) throws CoreException { PDOMFile file = PDOMFile.recreateFile(PDOM.this, record); files.add(file); return true; } }); return files.toArray(new IIndexFragmentFile[files.size()]); } protected IIndexFragmentFile addFile(int linkageID, IIndexFileLocation location, ISignificantMacros sigMacros) throws CoreException { PDOMLinkage linkage= createLinkage(linkageID); IIndexFragmentFile file = getFile(linkage, location, sigMacros); if (file == null) { PDOMFile pdomFile = new PDOMFile(linkage, location, linkageID, sigMacros); getFileIndex().insert(pdomFile.getRecord()); file= pdomFile; fEvent.setHasNewFiles(); } return file; } protected void clearFileIndex() throws CoreException { db.putRecPtr(FILE_INDEX, 0); fileIndex = null; } protected void clear() throws CoreException { assert lockCount < 0; // needs write-lock. // Clear out the database, everything is set to zero. int vers = getDefaultVersion(); db.clear(vers); clearCaches(); fEvent.setCleared(); } void reloadFromFile(File file) throws CoreException { assert lockCount < 0; // must have write lock. File oldFile= fPath; clearCaches(); try { db.close(); } catch (CoreException e) { CCorePlugin.log(e); } loadDatabase(file, db.getChunkCache()); db.setExclusiveLock(); oldFile.delete(); fEvent.fReloaded= true; } public boolean isEmpty() throws CoreException { return getFirstLinkageRecord() == 0; } @Override public IIndexFragmentBinding findBinding(IASTName name) throws CoreException { IBinding binding= name.resolveBinding(); if (binding != null) { PDOMLinkage linkage= adaptLinkage(name.getLinkage()); if (linkage != null) { return findBindingInLinkage(linkage, binding, true); } } else if (name.getPropertyInParent() == IASTPreprocessorStatement.MACRO_NAME) { PDOMLinkage linkage= adaptLinkage(name.getLinkage()); if (linkage != null) { return linkage.findMacroContainer(name.getSimpleID()); } } return null; } private static class BindingFinder implements IPDOMVisitor { private final Pattern[] pattern; private final IProgressMonitor monitor; private final ArrayList<PDOMNamedNode> currentPath= new ArrayList<>(); private final ArrayList<BitSet> matchStack= new ArrayList<>(); private final List<PDOMNamedNode> bindings = new ArrayList<>(); private final boolean isFullyQualified; private BitSet matchesUpToLevel; private final IndexFilter filter; public BindingFinder(Pattern[] pattern, boolean isFullyQualified, IndexFilter filter, IProgressMonitor monitor) { this.pattern = pattern; this.monitor = monitor; this.isFullyQualified= isFullyQualified; this.filter= filter; matchesUpToLevel= new BitSet(); matchesUpToLevel.set(0); matchStack.add(matchesUpToLevel); } @Override public boolean visit(IPDOMNode node) throws CoreException { if (monitor.isCanceled()) throw new CoreException(Status.OK_STATUS); if (node instanceof PDOMNamedNode) { PDOMNamedNode nnode = (PDOMNamedNode) node; String name = new String(nnode.getNameCharArray()); // check if we have a complete match. final int lastIdx = pattern.length-1; if (matchesUpToLevel.get(lastIdx) && pattern[lastIdx].matcher(name).matches()) { if (nnode instanceof IBinding && filter.acceptBinding((IBinding) nnode)) { bindings.add(nnode); } } // check if we have a partial match if (nnode.mayHaveChildren()) { // Avoid visiting unscoped enumerator items twice if (pattern.length == 1 && nnode instanceof ICPPEnumeration && !((ICPPEnumeration) nnode).isScoped()) { return false; } boolean visitNextLevel= false; BitSet updatedMatchesUpToLevel= new BitSet(); if (!isFullyQualified) { updatedMatchesUpToLevel.set(0); visitNextLevel= true; } for (int i = 0; i < lastIdx; i++) { if (matchesUpToLevel.get(i) && pattern[i].matcher(name).matches()) { updatedMatchesUpToLevel.set(i+1); visitNextLevel= true; } } if (visitNextLevel) { matchStack.add(matchesUpToLevel); matchesUpToLevel= updatedMatchesUpToLevel; currentPath.add(nnode); return true; } } return false; } return false; } @Override public void leave(IPDOMNode node) throws CoreException { final int idx= currentPath.size()-1; if (idx >= 0 && currentPath.get(idx) == node) { currentPath.remove(idx); matchesUpToLevel= matchStack.remove(matchStack.size()-1); } } public IIndexFragmentBinding[] getBindings() { return bindings.toArray(new IIndexFragmentBinding[bindings.size()]); } } public IIndexBinding[] findBindings(Pattern pattern, boolean isFullyQualified, IndexFilter filter, IProgressMonitor monitor) throws CoreException { return findBindings(new Pattern[] { pattern }, isFullyQualified, filter, monitor); } @Override public IIndexFragmentBinding[] findBindings(Pattern[] patterns, boolean isFullyQualified, IndexFilter filter, IProgressMonitor monitor) throws CoreException { if (monitor == null) { monitor= new NullProgressMonitor(); } // check for some easy cases Boolean caseSensitive= getCaseSensitive(patterns); if (caseSensitive != null) { char[][] simpleNames= extractSimpleNames(patterns); if (simpleNames != null) { if (simpleNames.length == 1) { return findBindings(simpleNames[0], isFullyQualified, caseSensitive, filter, monitor); } else if (isFullyQualified) { return findBindings(simpleNames, caseSensitive, filter, monitor); } } char[] prefix= extractPrefix(patterns); if (prefix != null) { return findBindingsForPrefix(prefix, isFullyQualified, caseSensitive, filter, monitor); } } BindingFinder finder = new BindingFinder(patterns, isFullyQualified, filter, monitor); for (PDOMLinkage linkage : getLinkageList()) { if (filter.acceptLinkage(linkage)) { try { linkage.accept(finder); } catch (CoreException e) { if (e.getStatus() != Status.OK_STATUS) throw e; return IIndexFragmentBinding.EMPTY_INDEX_BINDING_ARRAY; } } } return finder.getBindings(); } private Boolean getCaseSensitive(Pattern[] patterns) { Boolean caseSensitive= null; for (Pattern p : patterns) { switch (p.flags()) { case 0: if (caseSensitive == Boolean.FALSE) { return null; } caseSensitive= Boolean.TRUE; break; case Pattern.CASE_INSENSITIVE: if (caseSensitive == Boolean.TRUE) { return null; } caseSensitive= Boolean.FALSE; break; default: return null; } } return caseSensitive; } private char[][] extractSimpleNames(Pattern[] pattern) { char[][] result= new char[pattern.length][]; int i= 0; for (Pattern p : pattern) { char[] input= p.pattern().toCharArray(); for (char c : input) { if (!Character.isLetterOrDigit(c) && c != '_') { return null; } } result[i++]= input; } return result; } private char[] extractPrefix(Pattern[] pattern) { if (pattern.length != 1) return null; String p= pattern[0].pattern(); if (p.endsWith(".*")) { //$NON-NLS-1$ char[] input= p.substring(0, p.length()-2).toCharArray(); for (char c : input) { if (!Character.isLetterOrDigit(c) && c != '_') { return null; } } return input; } return null; } @Override public IIndexFragmentBinding[] findMacroContainers(Pattern pattern, IndexFilter filter, IProgressMonitor monitor) throws CoreException { if (monitor == null) { monitor= new NullProgressMonitor(); } Pattern[] patterns= new Pattern[] { pattern }; Boolean caseSensitive= getCaseSensitive(patterns); if (caseSensitive != null) { char[][] simpleNames= extractSimpleNames(patterns); if (simpleNames != null && simpleNames.length == 1) { return findMacroContainers(simpleNames[0], false, caseSensitive, filter, monitor); } char[] prefix= extractPrefix(patterns); if (prefix != null) { return findMacroContainers(prefix, true, caseSensitive, filter, monitor); } } List<IIndexFragmentBinding> result= new ArrayList<>(); for (PDOMLinkage linkage : getLinkageList()) { if (filter.acceptLinkage(linkage)) { try { MacroContainerPatternCollector finder = new MacroContainerPatternCollector(linkage, pattern, monitor); linkage.getMacroIndex().accept(finder); result.addAll(Arrays.asList(finder.getMacroContainers())); } catch (CoreException e) { if (e.getStatus() != Status.OK_STATUS) throw e; return IIndexFragmentBinding.EMPTY_INDEX_BINDING_ARRAY; } } } return result.toArray(new IIndexFragmentBinding[result.size()]); } @Override public IIndexFragmentBinding[] findBindings(char[][] names, IndexFilter filter, IProgressMonitor monitor) throws CoreException { return findBindings(names, true, filter, monitor); } public IIndexFragmentBinding[] findBindings(char[][] names, boolean caseSensitive, IndexFilter filter, IProgressMonitor monitor) throws CoreException { if (names.length == 0) { return IIndexFragmentBinding.EMPTY_INDEX_BINDING_ARRAY; } if (names.length == 1) { return findBindings(names[0], true, caseSensitive, filter, monitor); } IIndexFragmentBinding[] candidates = findBindings(names[names.length - 1], false, caseSensitive, filter, monitor); int j= 0; for (int i = 0; i < candidates.length; i++) { IIndexFragmentBinding cand = candidates[i]; if (matches(cand, names, caseSensitive)) { candidates[j++]= cand; } } return ArrayUtil.trimAt(IIndexFragmentBinding.class, candidates, j - 1); } private boolean matches(IIndexFragmentBinding cand, char[][] names, boolean caseSensitive) { for (int i= names.length; --i >= 0; cand= cand.getOwner()) { if (cand == null) return false; char[] name= cand.getNameCharArray(); if (!CharArrayUtils.equals(name, 0, name.length, names[i], !caseSensitive)) { if (cand instanceof IEnumeration) { if (cand instanceof ICPPEnumeration && ((ICPPEnumeration) cand).isScoped()) return false; // Unscoped enumerations are not part of the qualified name. i++; } else if (cand instanceof ICPPNamespace && name.length == 0) { // Anonymous namespaces are not part of the qualified name. i++; } else { return false; } } } return cand == null; } private long getFirstLinkageRecord() throws CoreException { return db.getRecPtr(LINKAGES); } @Override public IIndexLinkage[] getLinkages() { Collection<PDOMLinkage> values = getLinkageList(); return values.toArray(new IIndexLinkage[values.size()]); } @Override public PDOMLinkage[] getLinkageImpls() { Collection<PDOMLinkage> values = getLinkageList(); return values.toArray(new PDOMLinkage[values.size()]); } public void insertLinkage(PDOMLinkage linkage) throws CoreException { linkage.setNext(db.getRecPtr(LINKAGES)); db.putRecPtr(LINKAGES, linkage.getRecord()); fLinkageIDCache.put(linkage.getLinkageID(), linkage); } // Read-write lock rules. Readers don't conflict with other readers, // Writers conflict with readers, and everyone conflicts with writers. private final Object mutex = new Object(); private int lockCount; private int waitingReaders; private long lastWriteAccess= 0; private long lastReadAccess= 0; private long timeWriteLockAcquired; @Override public void acquireReadLock() throws InterruptedException { long t = sDEBUG_LOCKS ? System.nanoTime() : 0; synchronized (mutex) { ++waitingReaders; try { while (lockCount < 0) mutex.wait(); } finally { --waitingReaders; } ++lockCount; db.setLocked(true); if (sDEBUG_LOCKS) { t = (System.nanoTime() - t) / 1000000; if (t >= LONG_READ_LOCK_WAIT_REPORT_THRESHOLD) { System.out.println("Acquired index read lock after " + t + " ms wait."); //$NON-NLS-1$//$NON-NLS-2$ } incReadLock(fLockDebugging); } } } @Override public void releaseReadLock() { synchronized (mutex) { assert lockCount > 0: "No lock to release"; //$NON-NLS-1$ if (sDEBUG_LOCKS) { decReadLock(fLockDebugging); } lastReadAccess= System.currentTimeMillis(); if (lockCount > 0) --lockCount; mutex.notifyAll(); db.setLocked(lockCount != 0); } // A lock release probably means that some AST is going away. The result cache has to be // cleared since it may contain objects belonging to the AST that is going away. A failure // to release an AST object would cause a memory leak since the whole AST would remain // pinned to memory. // TODO(sprigogin): It would be more efficient to replace the global result cache with // separate caches for each AST. clearResultCache(); } /** * Acquire a write lock on this PDOM. Blocks until any existing read/write locks are released. * @throws InterruptedException * @throws IllegalStateException if this PDOM is not writable */ public void acquireWriteLock(IProgressMonitor monitor) throws InterruptedException { acquireWriteLock(0, monitor); } /** * Acquire a write lock on this PDOM, giving up the specified number of read locks first. Blocks * until any existing read/write locks are released. * @throws InterruptedException * @throws IllegalStateException if this PDOM is not writable */ public void acquireWriteLock(int giveupReadLocks, IProgressMonitor monitor) throws InterruptedException { assert !isPermanentlyReadOnly(); synchronized (mutex) { if (sDEBUG_LOCKS) { incWriteLock(giveupReadLocks); } if (giveupReadLocks > 0) { // give up on read locks assert lockCount >= giveupReadLocks: "Not enough locks to release"; //$NON-NLS-1$ if (lockCount < giveupReadLocks) { giveupReadLocks= lockCount; } } else { giveupReadLocks= 0; } // Let the readers go first long start= sDEBUG_LOCKS ? System.currentTimeMillis() : 0; int count = 0; while (lockCount > giveupReadLocks || waitingReaders > 0) { mutex.wait(CANCELLATION_CHECK_INTERVAL); if (monitor != null && monitor.isCanceled()) { throw new OperationCanceledException(); } count++; if (monitor != null && count == LONG_WRITE_LOCK_REPORT_THRESHOLD / CANCELLATION_CHECK_INTERVAL) { monitor.subTask(Messages.PDOM_waitingForWriteLock); } if (sDEBUG_LOCKS) { start = reportBlockedWriteLock(start, giveupReadLocks); } } lockCount= -1; if (sDEBUG_LOCKS) timeWriteLockAcquired = System.currentTimeMillis(); db.setExclusiveLock(); } if (monitor != null) monitor.subTask(""); //$NON-NLS-1$ } final public void releaseWriteLock() { releaseWriteLock(0, true); } @SuppressWarnings("nls") public void releaseWriteLock(int establishReadLocks, boolean flush) { // When all locks are released we can clear the result cache. if (establishReadLocks == 0) { clearResultCache(); } try { db.giveUpExclusiveLock(flush); } catch (CoreException e) { CCorePlugin.log(e); } assert lockCount == -1; if (!fEvent.isTrivial()) lastWriteAccess= System.currentTimeMillis(); final ChangeEvent event= fEvent; fEvent= new ChangeEvent(); synchronized (mutex) { if (sDEBUG_LOCKS) { long timeHeld = lastWriteAccess - timeWriteLockAcquired; if (timeHeld >= LONG_WRITE_LOCK_REPORT_THRESHOLD) { System.out.println("Index write lock held for " + timeHeld + " ms"); } decWriteLock(establishReadLocks); } if (lockCount < 0) lockCount= establishReadLocks; mutex.notifyAll(); db.setLocked(lockCount != 0); } fireChange(event); } @Override public boolean hasWaitingReaders() { synchronized (mutex) { return waitingReaders > 0; } } @Override public long getLastWriteAccess() { return lastWriteAccess; } public long getLastReadAccess() { return lastReadAccess; } protected PDOMLinkage adaptLinkage(ILinkage linkage) throws CoreException { return fLinkageIDCache.get(linkage.getLinkageID()); } private ThreadLocal<IBinding> inProgress = new ThreadLocal<>(); @Override public IIndexFragmentBinding adaptBinding(IBinding binding) throws CoreException { if (inProgress.get() == binding) { // Detect if we're recursing during the adapt. That shouldn't happen and // leads to stack overflow when it does. return null; } inProgress.set(binding); try { return adaptBinding(binding, true); } finally { inProgress.set(null); } } private IIndexFragmentBinding adaptBinding(IBinding binding, boolean includeLocal) throws CoreException { if (binding == null) { return null; } PDOMNode pdomNode= binding.getAdapter(PDOMNode.class); if (pdomNode instanceof IIndexFragmentBinding && pdomNode.getPDOM() == this) { return (IIndexFragmentBinding) pdomNode; } PDOMLinkage linkage= adaptLinkage(binding.getLinkage()); if (linkage != null) { return findBindingInLinkage(linkage, binding, includeLocal); } if (binding instanceof IMacroBinding) { for (PDOMLinkage linkage2 : fLinkageIDCache.values()) { IIndexFragmentBinding pdomBinding = findBindingInLinkage(linkage2, binding, includeLocal); if (pdomBinding != null) return pdomBinding; } } return null; } private IIndexFragmentBinding findBindingInLinkage(PDOMLinkage linkage, IBinding binding, boolean includeLocal) throws CoreException { if (binding instanceof IMacroBinding || binding instanceof IIndexMacroContainer) { return linkage.findMacroContainer(binding.getNameCharArray()); } return linkage.adaptBinding(binding, includeLocal); } public IIndexFragmentBinding findBinding(IIndexFragmentName indexName) throws CoreException { if (indexName instanceof PDOMName) { PDOMName pdomName= (PDOMName) indexName; return pdomName.getBinding(); } return null; } @Override public IIndexFragmentName[] findNames(IBinding binding, int options) throws CoreException { ArrayList<IIndexFragmentName> names= new ArrayList<>(); IIndexFragmentBinding myBinding= adaptBinding(binding); if (myBinding instanceof PDOMBinding) { PDOMBinding pdomBinding = (PDOMBinding) myBinding; findNamesForMyBinding(pdomBinding, options, names); if ((options & SEARCH_ACROSS_LANGUAGE_BOUNDARIES) != 0) { PDOMBinding[] xlangBindings= getCrossLanguageBindings(binding); for (PDOMBinding xlangBinding : xlangBindings) { findNamesForMyBinding(xlangBinding, options, names); } } } else if (myBinding instanceof PDOMMacroContainer) { final PDOMMacroContainer macroContainer = (PDOMMacroContainer) myBinding; findNamesForMyBinding(macroContainer, options, names); if ((options & SEARCH_ACROSS_LANGUAGE_BOUNDARIES) != 0) { PDOMMacroContainer[] xlangBindings= getCrossLanguageBindings(macroContainer); for (PDOMMacroContainer xlangBinding : xlangBindings) { findNamesForMyBinding(xlangBinding, options, names); } } } return names.toArray(new IIndexFragmentName[names.size()]); } private void findNamesForMyBinding(PDOMBinding pdomBinding, int options, ArrayList<IIndexFragmentName> names) throws CoreException { PDOMName name; if ((options & FIND_DECLARATIONS) != 0) { for (name= pdomBinding.getFirstDeclaration(); name != null; name= name.getNextInBinding()) { if (isCommitted(name)) { names.add(name); } } } if ((options & FIND_DEFINITIONS) != 0) { for (name = pdomBinding.getFirstDefinition(); name != null; name= name.getNextInBinding()) { if (isCommitted(name)) { names.add(name); } } } if ((options & FIND_REFERENCES) != 0) { for (name = pdomBinding.getFirstReference(); name != null; name= name.getNextInBinding()) { if (isCommitted(name)) { names.add(name); } } for (IPDOMIterator<PDOMName> iterator = pdomBinding.getExternalReferences(); iterator.hasNext();) { name = iterator.next(); if (isCommitted(name)) names.add(name); } } } private void findNamesForMyBinding(PDOMMacroContainer container, int options, ArrayList<IIndexFragmentName> names) throws CoreException { if ((options & FIND_DEFINITIONS) != 0) { for (PDOMMacro macro= container.getFirstDefinition(); macro != null; macro= macro.getNextInContainer()) { final IIndexFragmentName name = macro.getDefinition(); if (name != null && isCommitted(macro)) { names.add(name); } } } if ((options & FIND_REFERENCES) != 0) { for (PDOMMacroReferenceName name = container.getFirstReference(); name != null; name= name.getNextInContainer()) { if (isCommitted(name)) { names.add(name); } } } } protected boolean isCommitted(PDOMName name) throws CoreException { return true; } protected boolean isCommitted(PDOMMacro name) throws CoreException { return true; } protected boolean isCommitted(PDOMMacroReferenceName name) throws CoreException { return true; } @Override public IIndexFragmentInclude[] findIncludedBy(IIndexFragmentFile file) throws CoreException { PDOMFile pdomFile= adaptFile(file); if (pdomFile != null) { List<PDOMInclude> result = new ArrayList<>(); for (PDOMInclude i= pdomFile.getFirstIncludedBy(); i != null; i= i.getNextInIncludedBy()) { if (i.getIncludedBy().getTimestamp() > 0) { result.add(i); } } return result.toArray(new PDOMInclude[result.size()]); } return new PDOMInclude[0]; } private PDOMFile adaptFile(IIndexFragmentFile file) throws CoreException { if (file.getIndexFragment() == this && file instanceof PDOMFile) { return (PDOMFile) file; } return getFile(file.getLinkageID(), file.getLocation(), file.getSignificantMacros()); } public File getPath() { return fPath; } @Override public IIndexFragmentBinding[] findBindingsForPrefix(char[] prefix, boolean filescope, IndexFilter filter, IProgressMonitor monitor) throws CoreException { return findBindingsForPrefix(prefix, filescope, false, filter, monitor); } public IIndexFragmentBinding[] findBindingsForPrefix(char[] prefix, boolean filescope, boolean caseSensitive, IndexFilter filter, IProgressMonitor monitor) throws CoreException { return findBindingsForPrefixOrContentAssist(prefix, filescope, false, caseSensitive, filter, monitor); } @Override public IIndexFragmentBinding[] findBindingsForContentAssist(char[] prefix, boolean filescope, IndexFilter filter, IProgressMonitor monitor) throws CoreException { return findBindingsForPrefixOrContentAssist(prefix, filescope, true, false, filter, monitor); } private IIndexFragmentBinding[] findBindingsForPrefixOrContentAssist(char[] prefix, boolean filescope, boolean isContentAssist, boolean caseSensitive, IndexFilter filter, IProgressMonitor monitor) throws CoreException { ArrayList<IIndexFragmentBinding> result= new ArrayList<>(); for (PDOMLinkage linkage : getLinkageList()) { if (filter.acceptLinkage(linkage)) { PDOMBinding[] bindings; BindingCollector visitor = new BindingCollector(linkage, prefix, filter, !isContentAssist, isContentAssist, caseSensitive); visitor.setMonitor(monitor); try { linkage.accept(visitor); if (!filescope) { // Avoid adding unscoped enumerator items twice visitor.setSkipGlobalEnumerators(true); linkage.getNestedBindingsIndex().accept(visitor); } } catch (OperationCanceledException e) { } bindings= visitor.getBindings(); for (PDOMBinding binding : bindings) { result.add(binding); } } } return result.toArray(new IIndexFragmentBinding[result.size()]); } @Override public IIndexFragmentBinding[] findBindings(char[] name, boolean filescope, IndexFilter filter, IProgressMonitor monitor) throws CoreException { return findBindings(name, filescope, true, filter, monitor); } public IIndexFragmentBinding[] findBindings(char[] name, boolean filescope, boolean isCaseSensitive, IndexFilter filter, IProgressMonitor monitor) throws CoreException { ArrayList<IIndexFragmentBinding> result= new ArrayList<>(); try { for (PDOMLinkage linkage : getLinkageList()) { if (filter.acceptLinkage(linkage)) { if (isCaseSensitive) { PDOMBinding[] bindings= linkage.getBindingsViaCache(name, monitor); for (PDOMBinding binding : bindings) { if (filter.acceptBinding(binding)) { result.add(binding); } } } if (!isCaseSensitive || !filescope) { BindingCollector visitor= new BindingCollector(linkage, name, filter, false, false, isCaseSensitive); visitor.setMonitor(monitor); if (!isCaseSensitive) linkage.accept(visitor); if (!filescope) { // Avoid adding unscoped enumerator items twice visitor.setSkipGlobalEnumerators(true); linkage.getNestedBindingsIndex().accept(visitor); } PDOMBinding[] bindings = visitor.getBindings(); for (PDOMBinding binding : bindings) { result.add(binding); } } } } } catch (OperationCanceledException e) { } return result.toArray(new IIndexFragmentBinding[result.size()]); } public IIndexFragmentBinding[] findMacroContainers(char[] prefix, boolean isPrefix, boolean isCaseSensitive, IndexFilter filter, IProgressMonitor monitor) throws CoreException { ArrayList<IIndexFragmentBinding> result= new ArrayList<>(); try { for (PDOMLinkage linkage : getLinkageList()) { if (filter.acceptLinkage(linkage)) { MacroContainerCollector visitor = new MacroContainerCollector(linkage, prefix, isPrefix, false, isCaseSensitive); visitor.setMonitor(monitor); linkage.getMacroIndex().accept(visitor); result.addAll(visitor.getMacroList()); } } } catch (OperationCanceledException e) { } return result.toArray(new IIndexFragmentBinding[result.size()]); } @Override public IIndexMacro[] findMacros(char[] prefix, boolean isPrefix, boolean isCaseSensitive, IndexFilter filter, IProgressMonitor monitor) throws CoreException { ArrayList<IIndexMacro> result= new ArrayList<>(); try { for (PDOMLinkage linkage : getLinkageList()) { if (filter.acceptLinkage(linkage)) { MacroContainerCollector visitor = new MacroContainerCollector(linkage, prefix, isPrefix, false, isCaseSensitive); visitor.setMonitor(monitor); linkage.getMacroIndex().accept(visitor); for (PDOMMacroContainer mcont : visitor.getMacroList()) { result.addAll(Arrays.asList(mcont.getDefinitions())); } } } } catch (OperationCanceledException e) { } return result.toArray(new IIndexMacro[result.size()]); } @Override public String getProperty(String propertyName) throws CoreException { if (IIndexFragment.PROPERTY_FRAGMENT_FORMAT_ID.equals(propertyName)) { return FRAGMENT_PROPERTY_VALUE_FORMAT_ID; } int version = db.getVersion(); if (IIndexFragment.PROPERTY_FRAGMENT_FORMAT_VERSION.equals(propertyName)) { return PDOM.versionString(version); } // play it safe, properties are accessed before version checks. if (PDOM.isSupportedVersion(version)) { return new DBProperties(db, PROPERTIES).getProperty(propertyName); } if (IIndexFragment.PROPERTY_FRAGMENT_ID.equals(propertyName)) { return "Unknown"; //$NON-NLS-1$ } return null; } public void close() throws CoreException { db.close(); clearCaches(); } private void clearCaches() { fileIndex= null; tagIndex = null; indexOfDefectiveFiles= null; indexOfFiledWithUnresolvedIncludes= null; fLinkageIDCache.clear(); clearResultCache(); } @Override public void clearResultCache() { synchronized (fResultCache) { fResultCache.clear(); } } @Override public long getCacheHits() { return db.getCacheHits(); } @Override public long getCacheMisses() { return db.getCacheMisses(); } @Override public void resetCacheCounters() { db.resetCacheCounters(); } protected void flush() throws CoreException { db.flush(); } @Override public Object getCachedResult(Object key) { synchronized (fResultCache) { return fResultCache.get(key); } } public void putCachedResult(Object key, Object result) { putCachedResult(key, result, true); } @Override public Object putCachedResult(Object key, Object result, boolean replace) { synchronized (fResultCache) { Object old= fResultCache.put(key, result); if (old != null && !replace) { fResultCache.put(key, old); return old; } return result; } } public void removeCachedResult(Object key) { synchronized (fResultCache) { fResultCache.remove(key); } } public String createKeyForCache(long record, char[] name) { return new StringBuilder(name.length + 2).append((char) (record >> 16)).append((char) record).append(name).toString(); } public boolean hasLastingDefinition(PDOMBinding binding) throws CoreException { return binding.hasDefinition(); } private PDOMBinding[] getCrossLanguageBindings(IBinding binding) throws CoreException { switch (binding.getLinkage().getLinkageID()) { case ILinkage.C_LINKAGE_ID: return getCPPBindingForC(binding); case ILinkage.CPP_LINKAGE_ID: return getCBindingForCPP(binding); } return PDOMBinding.EMPTY_PDOMBINDING_ARRAY; } private PDOMMacroContainer[] getCrossLanguageBindings(PDOMMacroContainer binding) throws CoreException { final int inputLinkage= binding.getLinkage().getLinkageID(); if (inputLinkage == ILinkage.C_LINKAGE_ID || inputLinkage == ILinkage.CPP_LINKAGE_ID) { final char[] name= binding.getNameCharArray(); for (PDOMLinkage linkage : getLinkageList()) { final int linkageID = linkage.getLinkageID(); if (linkageID != inputLinkage) { if (linkageID == ILinkage.C_LINKAGE_ID || linkageID == ILinkage.CPP_LINKAGE_ID) { PDOMMacroContainer container= linkage.findMacroContainer(name); if (container != null) { return new PDOMMacroContainer[] { container }; } } } } } return new PDOMMacroContainer[0]; } private PDOMBinding[] getCBindingForCPP(IBinding binding) throws CoreException { PDOMBinding result= null; PDOMLinkage c= getLinkage(ILinkage.C_LINKAGE_ID); if (c == null) { return PDOMBinding.EMPTY_PDOMBINDING_ARRAY; } if (binding instanceof ICPPFunction) { ICPPFunction func = (ICPPFunction) binding; if (func.isExternC()) { result = FindBinding.findBinding(c.getIndex(), c, func.getNameCharArray(), new int[] { IIndexCBindingConstants.CFUNCTION }, 0); } } else if (binding instanceof ICPPField) { ICPPField field = (ICPPField) binding; IBinding parent = field.getCompositeTypeOwner(); PDOMBinding[] cOwners = getCBindingForCPP(parent); List<PDOMBinding> results = new ArrayList<>(); for (PDOMBinding cOwner : cOwners) { result = FindBinding.findBinding(cOwner, c, field.getNameCharArray(), new int[] { IIndexCBindingConstants.CFIELD }, 0); if (result != null) { results.add(result); } } return results.isEmpty() ? PDOMBinding.EMPTY_PDOMBINDING_ARRAY : results.toArray(new PDOMBinding[results.size()]); } else if (binding instanceof ICPPVariable) { ICPPVariable var = (ICPPVariable) binding; if (var.isExternC()) { result = FindBinding.findBinding(c.getIndex(), c, var.getNameCharArray(), new int[] { IIndexCBindingConstants.CVARIABLE }, 0); } } else if (binding instanceof IEnumeration) { result= FindBinding.findBinding(c.getIndex(), c, binding.getNameCharArray(), new int[] { IIndexCBindingConstants.CENUMERATION }, 0); } else if (binding instanceof IEnumerator) { result= FindBinding.findBinding(c.getIndex(), c, binding.getNameCharArray(), new int[] { IIndexCBindingConstants.CENUMERATOR }, 0); } else if (binding instanceof ITypedef) { result= FindBinding.findBinding(c.getIndex(), c, binding.getNameCharArray(), new int[] { IIndexCBindingConstants.CTYPEDEF }, 0); } else if (binding instanceof ICompositeType) { final int key= ((ICompositeType) binding).getKey(); if (key == ICompositeType.k_struct || key == ICompositeType.k_union) { result= FindBinding.findBinding(c.getIndex(), c, binding.getNameCharArray(), new int[] { IIndexCBindingConstants.CSTRUCTURE }, 0); if (result instanceof ICompositeType && ((ICompositeType) result).getKey() != key) { result= null; } } } return result == null ? PDOMBinding.EMPTY_PDOMBINDING_ARRAY : new PDOMBinding[] { result }; } private PDOMBinding[] getCPPBindingForC(IBinding binding) throws CoreException { PDOMLinkage cpp= getLinkage(ILinkage.CPP_LINKAGE_ID); if (cpp == null) { return PDOMBinding.EMPTY_PDOMBINDING_ARRAY; } PDOMBinding[] cppOwners = null; IndexFilter filter= null; if (binding instanceof IFunction) { filter= new IndexFilter() { @Override public boolean acceptBinding(IBinding binding) { if (binding instanceof ICPPFunction) { return ((ICPPFunction) binding).isExternC(); } return false; } }; } else if (binding instanceof IField) { IBinding compOwner = ((IField) binding).getCompositeTypeOwner(); cppOwners = getCPPBindingForC(compOwner); if (cppOwners.length > 0) { filter = IndexFilter.ALL; } } else if (binding instanceof IVariable) { if (!(binding instanceof IField) && !(binding instanceof IParameter)) { filter= new IndexFilter() { @Override public boolean acceptBinding(IBinding binding) { if (binding instanceof ICPPVariable) { return ((ICPPVariable) binding).isExternC(); } return false; } }; } } else if (binding instanceof IEnumeration) { filter= new IndexFilter() { @Override public boolean acceptBinding(IBinding binding) { return binding instanceof IEnumeration; } }; } else if (binding instanceof ITypedef) { filter= new IndexFilter() { @Override public boolean acceptBinding(IBinding binding) { return binding instanceof ITypedef; } }; } else if (binding instanceof IEnumerator) { filter= new IndexFilter() { @Override public boolean acceptBinding(IBinding binding) { return binding instanceof IEnumerator; } }; } else if (binding instanceof ICompositeType) { final int key = ((ICompositeType) binding).getKey(); filter= new IndexFilter() { @Override public boolean acceptBinding(IBinding binding) { if (binding instanceof ICompositeType) { return ((ICompositeType) binding).getKey() == key; } return false; } }; } if (filter != null) { BindingCollector collector= new BindingCollector(cpp, binding.getNameCharArray(), filter, false, false, true); if (cppOwners != null) { for (PDOMBinding owner : cppOwners) { owner.accept(collector); } } else { cpp.accept(collector); } return collector.getBindings(); } return PDOMBinding.EMPTY_PDOMBINDING_ARRAY; } @Override public IIndexFragmentFileSet createFileSet() { return new PDOMFileSet(); } // For debugging lock issues static class DebugLockInfo { int fReadLocks; int fWriteLocks; List<StackTraceElement[]> fTraces= new ArrayList<>(); public int addTrace() { fTraces.add(Thread.currentThread().getStackTrace()); return fTraces.size(); } @SuppressWarnings("nls") public void write(String threadName) { System.out.println("Thread: '" + threadName + "': " + fReadLocks + " readlocks, " + fWriteLocks + " writelocks"); for (StackTraceElement[] trace : fTraces) { System.out.println(" Stacktrace:"); for (StackTraceElement ste : trace) { System.out.println(" " + ste); } } } public void inc(DebugLockInfo val) { fReadLocks+= val.fReadLocks; fWriteLocks+= val.fWriteLocks; fTraces.addAll(val.fTraces); } } // For debugging lock issues private Map<Thread, DebugLockInfo> fLockDebugging; // For debugging lock issues private static DebugLockInfo getLockInfo(Map<Thread, DebugLockInfo> lockDebugging) { assert sDEBUG_LOCKS; Thread key = Thread.currentThread(); DebugLockInfo result= lockDebugging.get(key); if (result == null) { result= new DebugLockInfo(); lockDebugging.put(key, result); } return result; } // For debugging lock issues static void incReadLock(Map<Thread, DebugLockInfo> lockDebugging) { DebugLockInfo info = getLockInfo(lockDebugging); info.fReadLocks++; if (info.addTrace() > 10) { outputReadLocks(lockDebugging); } } // For debugging lock issues @SuppressWarnings("nls") static void decReadLock(Map<Thread, DebugLockInfo> lockDebugging) throws AssertionError { DebugLockInfo info = getLockInfo(lockDebugging); if (info.fReadLocks <= 0) { outputReadLocks(lockDebugging); throw new AssertionError("Superfluous releaseReadLock"); } if (info.fWriteLocks != 0) { outputReadLocks(lockDebugging); throw new AssertionError("Releasing readlock while holding write lock"); } if (--info.fReadLocks == 0) { lockDebugging.remove(Thread.currentThread()); } else { info.addTrace(); } } // For debugging lock issues @SuppressWarnings("nls") private void incWriteLock(int giveupReadLocks) throws AssertionError { DebugLockInfo info = getLockInfo(fLockDebugging); if (info.fReadLocks != giveupReadLocks) { outputReadLocks(fLockDebugging); throw new AssertionError("write lock with " + giveupReadLocks + " readlocks, expected " + info.fReadLocks); } if (info.fWriteLocks != 0) throw new AssertionError("Duplicate write lock"); info.fWriteLocks++; } // For debugging lock issues private void decWriteLock(int establishReadLocks) throws AssertionError { DebugLockInfo info = getLockInfo(fLockDebugging); if (info.fReadLocks != establishReadLocks) throw new AssertionError("release write lock with " + establishReadLocks + " readlocks, expected " + info.fReadLocks); //$NON-NLS-1$ //$NON-NLS-2$ if (info.fWriteLocks != 1) throw new AssertionError("Wrong release write lock"); //$NON-NLS-1$ info.fWriteLocks= 0; if (info.fReadLocks == 0) { fLockDebugging.remove(Thread.currentThread()); } } // For debugging lock issues @SuppressWarnings("nls") private long reportBlockedWriteLock(long start, int giveupReadLocks) { long now= System.currentTimeMillis(); if (now >= start + BLOCKED_WRITE_LOCK_OUTPUT_INTERVAL) { System.out.println(); System.out.println("Blocked writeLock"); System.out.println(" lockcount= " + lockCount + ", giveupReadLocks=" + giveupReadLocks + ", waitingReaders=" + waitingReaders); outputReadLocks(fLockDebugging); start= now; } return start; } // For debugging lock issues @SuppressWarnings("nls") private static void outputReadLocks(Map<Thread, DebugLockInfo> lockDebugging) { System.out.println("--------------------- Lock Debugging -------------------------"); for (Thread th: lockDebugging.keySet()) { DebugLockInfo info = lockDebugging.get(th); info.write(th.getName()); } System.out.println("---------------------------------------------------------------"); } // For debugging lock issues public void adjustThreadForReadLock(Map<Thread, DebugLockInfo> lockDebugging) { for (Thread th : lockDebugging.keySet()) { DebugLockInfo val= lockDebugging.get(th); if (val.fReadLocks > 0) { DebugLockInfo myval= fLockDebugging.get(th); if (myval == null) { myval= new DebugLockInfo(); fLockDebugging.put(th, myval); } myval.inc(val); for (int i = 0; i < val.fReadLocks; i++) { decReadLock(fLockDebugging); } } } } @Override public IIndexScope[] getInlineNamespaces() throws CoreException { PDOMLinkage linkage = getLinkage(ILinkage.CPP_LINKAGE_ID); if (linkage == null) { return IIndexScope.EMPTY_INDEX_SCOPE_ARRAY; } return linkage.getInlineNamespaces(); } @Override public boolean isFullyInitialized() { return true; } }