From 1af8428debd05ad57403e0d60059a8e582dfdd82 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Wed, 18 Jan 2017 19:57:44 -0800 Subject: [PATCH] Bug 509898 - CPPSemantics.isReachableFromAst is slow and is causing UI freezes Optimized IndexFileSet.containsDeclaration method for performance and to reduce its memory footprint. Change-Id: I0e867b96c6d6ab102561bc999127980d1be26a7b --- .../core/index/IIndexFragmentFileSet.java | 9 +++- .../cdt/internal/core/index/IndexFileSet.java | 31 ++++++++----- .../eclipse/cdt/internal/core/pdom/PDOM.java | 12 ++++++ .../cdt/internal/core/pdom/PDOMFileSet.java | 14 +++++- .../core/pdom/dom/CompoundRecordIterator.java | 43 +++++++++++++++++++ .../core/pdom/dom/IRecordIterator.java | 27 ++++++++++++ .../internal/core/pdom/dom/PDOMBinding.java | 10 +++++ .../cdt/internal/core/pdom/dom/PDOMName.java | 37 +++++++++++++++- 8 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/CompoundRecordIterator.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/IRecordIterator.java diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IIndexFragmentFileSet.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IIndexFragmentFileSet.java index 1152714a2f4..59c24fd01dd 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IIndexFragmentFileSet.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IIndexFragmentFileSet.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2012 Wind River Systems, Inc. and others. + * Copyright (c) 2008, 2017 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 @@ -7,6 +7,7 @@ * * Contributors: * Markus Schorn - initial API and implementation + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.index; @@ -15,7 +16,6 @@ import org.eclipse.core.runtime.CoreException; public interface IIndexFragmentFileSet { /** * Returns whether the file-set contains the file of the local binding. - * @throws CoreException */ boolean containsFileOfLocalBinding(IIndexFragmentBinding binding) throws CoreException; @@ -33,4 +33,9 @@ public interface IIndexFragmentFileSet { * Returns whether the file set contains the given file. */ boolean contains(IIndexFragmentFile file) throws CoreException; + + /** + * Returns {@code true} if this file set is empty. + */ + boolean isEmpty(); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexFileSet.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexFileSet.java index 4b4ec4de15c..64a4b098363 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexFileSet.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexFileSet.java @@ -21,6 +21,11 @@ import org.eclipse.cdt.core.index.IIndexBinding; import org.eclipse.cdt.core.index.IIndexFile; import org.eclipse.cdt.core.index.IIndexFileLocation; import org.eclipse.cdt.core.index.IIndexFileSet; +import org.eclipse.cdt.internal.core.pdom.PDOM; +import org.eclipse.cdt.internal.core.pdom.PDOMFileSet; +import org.eclipse.cdt.internal.core.pdom.db.Database; +import org.eclipse.cdt.internal.core.pdom.dom.IRecordIterator; +import org.eclipse.cdt.internal.core.pdom.dom.PDOMName; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; @@ -45,26 +50,30 @@ public class IndexFileSet implements IIndexFileSet { @Override public void remove(IIndexFile indexFile) { - final IIndexFragmentFile fragFile = (IIndexFragmentFile) indexFile; - final IIndexFragment frag= fragFile.getIndexFragment(); - IIndexFragmentFileSet subSet= fSubSets.get(frag); + final IIndexFragmentFile fragmentFile = (IIndexFragmentFile) indexFile; + final IIndexFragment fragment = fragmentFile.getIndexFragment(); + IIndexFragmentFileSet subSet = fSubSets.get(fragment); if (subSet != null) { - subSet.remove(fragFile); + subSet.remove(fragmentFile); } } @Override public boolean containsDeclaration(IIndexBinding binding) { for (Map.Entry entry : fSubSets.entrySet()) { + IIndexFragment fragment = entry.getKey(); + IIndexFragmentFileSet fragmentFileSet = entry.getValue(); try { - IIndexFragmentName[] names = - entry.getKey().findNames(binding, IIndexFragment.FIND_DECLARATIONS_DEFINITIONS); - for (IIndexFragmentName name : names) { - try { - if (entry.getValue().contains((IIndexFragmentFile) name.getFile())) + if (!fragmentFileSet.isEmpty() && fragmentFileSet instanceof PDOMFileSet && fragment instanceof PDOM) { + PDOM pdom = (PDOM) fragment; + PDOMFileSet pdomFileSet = (PDOMFileSet) fragmentFileSet; + Database db = pdom.getDB(); + IRecordIterator nameIterator = pdom.getDeclarationsDefintitionsRecordIterator(binding); + long nameRecord; + while ((nameRecord = nameIterator.next()) != 0) { + long fileRecord = PDOMName.getFileRecord(db, nameRecord); + if (pdomFileSet.containsFile(fileRecord)) return true; - } catch (CoreException e) { - CCorePlugin.log(e); } } } catch (CoreException e) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java index 3d9680f6d68..3808bc5bfbc 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java @@ -77,9 +77,11 @@ 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.CompoundRecordIterator; 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.IRecordIterator; 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; @@ -1270,6 +1272,16 @@ public class PDOM extends PlatformObject implements IPDOM { } } + public IRecordIterator getDeclarationsDefintitionsRecordIterator(IIndexBinding binding) throws CoreException { + IIndexFragmentBinding myBinding= adaptBinding(binding); + if (myBinding instanceof PDOMBinding) { + PDOMBinding pdomBinding = (PDOMBinding) myBinding; + return new CompoundRecordIterator(pdomBinding.getDeclarationRecordIterator(), + pdomBinding.getDefinitionRecordIterator()); + } + return IRecordIterator.EMPTY; + } + protected boolean isCommitted(PDOMName name) throws CoreException { return true; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMFileSet.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMFileSet.java index ebfeace556f..1299b24a7cc 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMFileSet.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMFileSet.java @@ -19,7 +19,7 @@ import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding; import org.eclipse.cdt.internal.core.pdom.dom.PDOMFile; import org.eclipse.core.runtime.CoreException; -public class PDOMFileSet implements IIndexFragmentFileSet { +public final class PDOMFileSet implements IIndexFragmentFileSet { private final HashSet fFileIDs= new HashSet<>(); @Override @@ -50,4 +50,16 @@ public class PDOMFileSet implements IIndexFragmentFileSet { } return false; } + + /** + * Returns whether the file set contains the file corresponding to the given record. + */ + public boolean containsFile(long fileRecord) { + return fFileIDs.contains(fileRecord); + } + + @Override + public boolean isEmpty() { + return fFileIDs.isEmpty(); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/CompoundRecordIterator.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/CompoundRecordIterator.java new file mode 100644 index 00000000000..db53b92f35c --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/CompoundRecordIterator.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2017 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.internal.core.pdom.dom; + +import org.eclipse.core.runtime.CoreException; + +/** + * A record iterator that is a concatenation of multiple record iterators. + */ +public final class CompoundRecordIterator implements IRecordIterator { + private final IRecordIterator[] iterators; + private int currentOffset; + + /** + * Initializes the compound iterator. + * + * @param iterators the iterators to concatenate + */ + public CompoundRecordIterator(IRecordIterator... iterators) { + if (iterators == null) + throw new NullPointerException(); + this.iterators = iterators; + } + + @Override + public long next() throws CoreException { + for (; currentOffset < iterators.length; currentOffset++) { + IRecordIterator iterator = iterators[currentOffset]; + long record = iterator.next(); + if (record != 0) + return record; + } + return 0; + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/IRecordIterator.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/IRecordIterator.java new file mode 100644 index 00000000000..15ed13aa358 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/IRecordIterator.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2017 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.internal.core.pdom.dom; + +import org.eclipse.core.runtime.CoreException; + +/** + * An interface for iterating through lists that are stored in the PDOM without instantiating objects. + */ +@FunctionalInterface +public interface IRecordIterator { + public static final IRecordIterator EMPTY = () -> 0; + + /** + * Returns the record of next element in the iteration, or zero if there are no elements left in + * the iteration. + */ + public long next() throws CoreException; +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMBinding.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMBinding.java index 9c66d986f60..7237dc9099b 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMBinding.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMBinding.java @@ -175,6 +175,16 @@ public abstract class PDOMBinding extends PDOMNamedNode implements IPDOMBinding return namerec != 0 ? new PDOMName(getLinkage(), namerec) : null; } + public IRecordIterator getDeclarationRecordIterator() throws CoreException { + Database db = getDB(); + return PDOMName.getNameInBindingRecordIterator(db, db.getRecPtr(record + FIRST_DECL)); + } + + public IRecordIterator getDefinitionRecordIterator() throws CoreException { + Database db = getDB(); + return PDOMName.getNameInBindingRecordIterator(db, db.getRecPtr(record + FIRST_DEF)); + } + /** * Returns an iterator over the names in other linkages that reference this binding. Does * not return null. diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMName.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMName.java index 157266fcb71..86812964a88 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMName.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMName.java @@ -195,6 +195,10 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { linkage.getDB().putRecPtr(record + FILE_REC_OFFSET, file != null ? file.getRecord() : 0); } + public static long getFileRecord(Database db, long record) throws CoreException { + return db.getRecPtr(record + FILE_REC_OFFSET); + } + @Override public IIndexName getEnclosingDefinition() throws CoreException { long namerec = getEnclosingDefinitionRecord(); @@ -419,7 +423,7 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { @Override public IIndexName[] getEnclosedNames() throws CoreException { - ArrayList result= new ArrayList(); + ArrayList result= new ArrayList<>(); PDOMName name= getNextInFile(); while (name != null) { if (name.getEnclosingDefinitionRecord() == record) { @@ -429,4 +433,35 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { } return result.toArray(new PDOMName[result.size()]); } + + /** + * Returns an iterator over names in binding. This is a lighter weight alternative to + * the {@link #getNextInBinding()} method. + */ + public static IRecordIterator getNameInBindingRecordIterator(Database db, long nameRecord) { + if (nameRecord == 0) + return IRecordIterator.EMPTY; + return new NameInBindingRecordIterator(db, nameRecord); + } + + /** + * Iterator over PDOMName records in a binding. + */ + private static class NameInBindingRecordIterator implements IRecordIterator { + final Database db; + long nameRecord; + + public NameInBindingRecordIterator(Database db, long nameRecord) { + this.db = db; + this.nameRecord = nameRecord; + } + + @Override + public long next() throws CoreException { + long record = nameRecord; + if (record != 0) + nameRecord = db.getRecPtr(record + BINDING_NEXT_OFFSET); + return record; + } + } }