From f887c8e671725af7d3f18ed55d65d2235036fa5b Mon Sep 17 00:00:00 2001 From: Andrew Eidsness Date: Wed, 27 Nov 2013 09:44:16 -0500 Subject: [PATCH] Bug 422681: Allow references to bindings in other linkages The existing PDOMBinding and PDOMName implementations do not allow references between linkages. This feature is needed so that the new Qt linkage can reference elements in the C++ linkage. It will also allow the C++ implementation for extern "C" to be cleaned up (see PDOM.getCrossLanguageBindings). Prior to this change, a PDOMBinding held three lists of names. One for each of declarations, definitions, and references. This change adds a fourth list for external references. External references are stored as a linked list of nodes. Each node holds: - The linkage id. - A pointer to the next node (or 0 for end-of-list). - A pointer to the record of first name in the list. The linkage id is held separately because a PDOMName does not have any field of it's own for linkage. By grouping elements in this way, we can reuse most of the existing list-related code. External references are accessed through a new PDOMIterator class. This is needed so that we can advance to the next linkage node when we get to the end of one node's list of names. This also adds a unit test for the new API. Change-Id: Ie2b14848db7409905beda0cec752080d5f42eec8 Signed-off-by: Andrew Eidsness Reviewed-on: https://git.eclipse.org/r/18979 Tested-by: Hudson CI Reviewed-by: Doug Schaefer IP-Clean: Doug Schaefer --- .../internal/pdom/tests/PDOMCPPBugsTest.java | 50 ++--- .../internal/pdom/tests/PDOMNameTests.java | 105 ++++++++++ .../cdt/internal/pdom/tests/PDOMTests.java | 1 + .../eclipse/cdt/internal/core/pdom/PDOM.java | 14 +- .../pdom/db/PDOMExternalReferencesList.java | 184 ++++++++++++++++++ .../internal/core/pdom/dom/IPDOMIterator.java | 36 ++++ .../internal/core/pdom/dom/PDOMBinding.java | 35 +++- .../cdt/internal/core/pdom/dom/PDOMName.java | 35 ++-- .../ui/indexview/CountNodeAction.java | 11 +- 9 files changed, 422 insertions(+), 49 deletions(-) create mode 100644 core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMNameTests.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/PDOMExternalReferencesList.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/IPDOMIterator.java diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMCPPBugsTest.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMCPPBugsTest.java index 6248a4fb90c..97a13d904e9 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMCPPBugsTest.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMCPPBugsTest.java @@ -12,6 +12,7 @@ package org.eclipse.cdt.internal.pdom.tests; import java.io.File; +import java.util.regex.Pattern; import junit.framework.Test; @@ -49,11 +50,11 @@ import org.eclipse.core.runtime.NullProgressMonitor; */ public class PDOMCPPBugsTest extends BaseTestCase { ICProject cproject; - + public static Test suite() { return suite(PDOMCPPBugsTest.class); } - + @Override protected void setUp() throws Exception { super.setUp(); @@ -74,6 +75,7 @@ public class PDOMCPPBugsTest extends BaseTestCase { pdom.acquireWriteLock(0); try { WritablePDOM wpdom = (WritablePDOM) pdom; + IIndexBinding[] b = wpdom.findBindings(Pattern.compile(".+"), false, IndexFilter.ALL, null); wpdom.setProperty("a", "b"); assertEquals("b", wpdom.getProperty("a")); wpdom.setProperty("c", "d"); @@ -84,16 +86,16 @@ public class PDOMCPPBugsTest extends BaseTestCase { pdom.releaseWriteLock(0, true); } } - + public void testProjectPDOMProperties() throws Exception { PDOM pdom = (PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject); pdom.acquireReadLock(); try { String id= pdom.getProperty(IIndexFragment.PROPERTY_FRAGMENT_ID); assertNotNull(id); - + CCoreInternals.getPDOMManager().reindex(cproject); - + String id2= pdom.getProperty(IIndexFragment.PROPERTY_FRAGMENT_ID); assertNotNull(id2); assertEquals(id, id2); @@ -101,33 +103,33 @@ public class PDOMCPPBugsTest extends BaseTestCase { pdom.releaseReadLock(); } } - + public void testProjectPDOMPropertiesOnExport() throws Exception { // this test is currently failing on the cdt test build machine, but // not on my local linux or windows boxes. - + File tmp= new File(System.getProperty("java.io.tmpdir")+"/temp"+System.currentTimeMillis()+".pdom"); IIndexLocationConverter cvr= new ResourceContainerRelativeLocationConverter(cproject.getProject()); final PDOMManager pdomManager = CCoreInternals.getPDOMManager(); pdomManager.exportProjectPDOM(cproject, tmp, cvr); - + IWritableIndexFragment pdom = new WritablePDOM(tmp, cvr, new ChunkCache(), LanguageManager.getInstance().getPDOMLinkageFactoryMappings()); pdom.acquireReadLock(); try { String id= pdom.getProperty(IIndexFragment.PROPERTY_FRAGMENT_ID); assertNotNull("Exported pdom ID is null", id); - + String id2 = getFragmentID(cproject); assertNotNull("Project pdom ID is null", id2); assertFalse("Project pdom ID equals export PDOM id", id2.equals(id)); - + pdomManager.reindex(cproject); waitForIndexer(cproject); - + String id3= pdom.getProperty(IIndexFragment.PROPERTY_FRAGMENT_ID); assertNotNull("Exported pdom ID is null after project reindex", id3); assertEquals("Exported pdom ID hasChanged during reindex", id, id3); - + String id4= getFragmentID(cproject); assertNotNull("Reindexed project pdom ID is null", id4); assertFalse("Reindexex project pdom ID equals exported pdom ID", id4.equals(id)); @@ -149,7 +151,7 @@ public class PDOMCPPBugsTest extends BaseTestCase { } return id2; } - + public void testInterruptingAcquireReadLock() throws Exception { final PDOM pdom= (PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject); final boolean[] ok= {false}; @@ -162,7 +164,7 @@ public class PDOMCPPBugsTest extends BaseTestCase { pdom.acquireReadLock(); } catch (InterruptedException e) { ok[0]= true; - } + } } }; other.start(); @@ -176,7 +178,7 @@ public class PDOMCPPBugsTest extends BaseTestCase { pdom.acquireWriteLock(); pdom.releaseWriteLock(); } - + public void testInterruptingAcquireWriteLock() throws Exception { final WritablePDOM pdom= (WritablePDOM) CCoreInternals.getPDOMManager().getPDOM(cproject); final boolean[] ok= {false}; @@ -191,7 +193,7 @@ public class PDOMCPPBugsTest extends BaseTestCase { } catch (InterruptedException e) { ok[0]= true; pdom.releaseReadLock(); - } + } } }; other.start(); @@ -205,20 +207,20 @@ public class PDOMCPPBugsTest extends BaseTestCase { pdom.acquireWriteLock(); pdom.releaseWriteLock(); } - + public void test191679() throws Exception { IProject project= cproject.getProject(); IFolder cHeaders= cproject.getProject().getFolder("cHeaders"); cHeaders.create(true, true, npm()); LanguageManager lm= LanguageManager.getInstance(); - - IFile cHeader= TestSourceReader.createFile(cHeaders, "cSource.c", "void foo(int i){}"); + + IFile cHeader= TestSourceReader.createFile(cHeaders, "cSource.c", "void foo(int i){}"); IFile cppSource= TestSourceReader.createFile(cHeaders, "cppSource.cpp", "extern \"C\" void foo(int i); void ref() {foo(1);}"); - + IndexerPreferences.set(project, IndexerPreferences.KEY_INDEXER_ID, IPDOMManager.ID_FAST_INDEXER); CCorePlugin.getIndexManager().reindex(cproject); waitForIndexer(cproject); - + final PDOM pdom= (PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject); pdom.acquireReadLock(); try { @@ -230,17 +232,17 @@ public class PDOMCPPBugsTest extends BaseTestCase { } assertTrue(ib[0] instanceof IFunction); assertFalse(ib[0] instanceof ICPPBinding); - + assertTrue(ib[1] instanceof IFunction); assertTrue(ib[1] instanceof ICPPBinding); - + IName[] nms= pdom.findNames(ib[0], IIndexFragment.FIND_REFERENCES | IIndexFragment.SEARCH_ACROSS_LANGUAGE_BOUNDARIES); assertEquals(1, nms.length); assertTrue(nms[0].getFileLocation().getFileName().endsWith(".cpp")); nms= pdom.findNames(ib[0], IIndexFragment.FIND_REFERENCES); assertEquals(0, nms.length); - + nms= pdom.findNames(ib[1], IIndexFragment.FIND_DEFINITIONS | IIndexFragment.SEARCH_ACROSS_LANGUAGE_BOUNDARIES); assertEquals(1, nms.length); assertTrue(nms[0].getFileLocation().getFileName().endsWith(".c")); diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMNameTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMNameTests.java new file mode 100644 index 00000000000..d7e9c2c8d47 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMNameTests.java @@ -0,0 +1,105 @@ +package org.eclipse.cdt.internal.pdom.tests; + +import junit.framework.Test; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.IPDOMManager; +import org.eclipse.cdt.core.index.IIndexBinding; +import org.eclipse.cdt.core.index.IndexFilter; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.testplugin.CProjectHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.core.testplugin.util.TestSourceReader; +import org.eclipse.cdt.internal.core.CCoreInternals; +import org.eclipse.cdt.internal.core.pdom.PDOM; +import org.eclipse.cdt.internal.core.pdom.dom.IPDOMIterator; +import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding; +import org.eclipse.cdt.internal.core.pdom.dom.PDOMName; +import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.NullProgressMonitor; + +public class PDOMNameTests extends BaseTestCase { + private ICProject cproject; + + public static Test suite() { + return suite(PDOMNameTests.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + cproject= CProjectHelper.createCCProject("PDOMNameTest"+System.currentTimeMillis(), "bin", IPDOMManager.ID_FAST_INDEXER); + waitForIndexer(cproject); + } + + @Override + protected void tearDown() throws Exception { + if (cproject != null) { + cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, new NullProgressMonitor()); + } + super.tearDown(); + } + + public void testExternalReferences() throws Exception { + IProject project = cproject.getProject(); + TestSourceReader.createFile(project, "file.cpp", "extern void func_cpp() { func_cpp(); }"); + TestSourceReader.createFile(project, "file.c", "extern void func_c() { func_c(); }"); + + IndexerPreferences.set(project, IndexerPreferences.KEY_INDEXER_ID, IPDOMManager.ID_FAST_INDEXER); + CCorePlugin.getIndexManager().reindex(cproject); + waitForIndexer(cproject); + + PDOM pdom = (PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject); + pdom.acquireWriteLock(); + try { + IIndexBinding[] bindings = pdom.findBindings(new char[][]{"func_cpp".toCharArray()}, IndexFilter.ALL, npm()); + assertEquals(1, bindings.length); + assertTrue(bindings[0] instanceof PDOMBinding); + + PDOMBinding binding_cpp = (PDOMBinding) bindings[0]; + PDOMName name_cpp = binding_cpp.getFirstReference(); + assertNotNull(name_cpp); + assertSame(binding_cpp.getLinkage(), name_cpp.getLinkage()); + + bindings = pdom.findBindings(new char[][]{"func_c".toCharArray()}, IndexFilter.ALL, npm()); + assertEquals(1, bindings.length); + assertTrue(bindings[0] instanceof PDOMBinding); + + PDOMBinding binding_c = (PDOMBinding) bindings[0]; + PDOMName name_c = binding_c.getFirstReference(); + assertNotNull(name_c); + assertSame(binding_c.getLinkage(), name_c.getLinkage()); + + // Check that the external references list is currently empty. + IPDOMIterator extNames = binding_cpp.getExternalReferences(); + assertNotNull(extNames); + assertFalse(extNames.hasNext()); + + // Make sure the C++ binding and the C name are in different linkages, then add the name + // as an external reference of the binding. + assertNotSame(binding_cpp.getLinkage(), name_c.getLinkage()); + binding_cpp.addReference(name_c); + + // Make sure there is an external reference, then retrieve it. Then make sure there + // aren't anymore external references. + extNames = binding_cpp.getExternalReferences(); + assertNotNull(extNames); + assertTrue(extNames.hasNext()); + PDOMName extRef = extNames.next(); + assertNotNull(extRef); + assertFalse(extNames.hasNext()); + + // Check that the external reference is the same as the C name that was added, that the + // external reference does not have the same linkage as the binding, and that it does + // have the same linkage as the initial name. + assertSame(name_c.getLinkage(), extRef.getLinkage()); + assertEquals(name_c.getRecord(), extRef.getRecord()); + assertNotSame(binding_cpp.getLinkage(), extRef.getLinkage()); + assertSame(binding_c.getLinkage(), extRef.getLinkage()); + } finally { + pdom.releaseWriteLock(); + } + } +} diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMTests.java index 67222982b06..11a4f5a1f05 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMTests.java @@ -30,6 +30,7 @@ public class PDOMTests extends TestSuite { suite.addTest(PDOMCPPBugsTest.suite()); suite.addTest(PDOMSearchTest.suite()); suite.addTest(PDOMLocationTests.suite()); + suite.addTest(PDOMNameTests.suite()); suite.addTest(PDOMProviderTests.suite()); suite.addTest(EnumerationTests.suite()); suite.addTest(ClassTests.suite()); 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 c4efdd95f21..e98dd0854d0 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 @@ -76,6 +76,7 @@ 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; @@ -242,10 +243,11 @@ public class PDOM extends PlatformObject implements IPDOM { * * 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 xyz. */ - private static final int MIN_SUPPORTED_VERSION= version(160, 0); - private static final int MAX_SUPPORTED_VERSION= version(160, Short.MAX_VALUE); - private static final int DEFAULT_VERSION = version(160, 0); + private static final int MIN_SUPPORTED_VERSION= version(161, 0); + private static final int MAX_SUPPORTED_VERSION= version(161, Short.MAX_VALUE); + private static final int DEFAULT_VERSION = version(161, 0); private static int version(int major, int minor) { return (major << 16) + minor; @@ -1166,6 +1168,12 @@ public class PDOM extends PlatformObject implements IPDOM { names.add(name); } } + IPDOMIterator iterator = pdomBinding.getExternalReferences(); + while(iterator.hasNext()) { + name = iterator.next(); + if (isCommitted(name)) + names.add(name); + } } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/PDOMExternalReferencesList.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/PDOMExternalReferencesList.java new file mode 100644 index 00000000000..c0d08a0a9e3 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/PDOMExternalReferencesList.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2013 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 + */ +package org.eclipse.cdt.internal.core.pdom.db; + +import java.util.NoSuchElementException; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.internal.core.pdom.PDOM; +import org.eclipse.cdt.internal.core.pdom.dom.IPDOMIterator; +import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage; +import org.eclipse.cdt.internal.core.pdom.dom.PDOMName; +import org.eclipse.core.runtime.CoreException; + +/** + * A utility class for storing a list of external references. An external reference is + * a PDOMName that references a PDOMBinding in a different linkage. + *

+ * External references are stored in a singly linked list with one node for each linkage. + * Each node stores a list of PDOMNames. The PDOMName.BindingList is used to store the + * list. + *

+ * Each node has three fields: + * { + * INT_SIZE linkageId; + * PTR_SIZE nextNode; + * PTR_SIZE nameListHead; + * } + *

+ * An iterator is provided to examine all names in the list. An iterator is needed (instead + * of a simple list) so that we have a chance to move to the next linkage's node when we get + * to the end of a name list. + */ +public class PDOMExternalReferencesList { + + private final PDOM pdom; + private final long record; + + /** + * Create a new instance at the given location in the given PDOM. + */ + public PDOMExternalReferencesList(PDOM pdom, long record) throws CoreException { + this.pdom = pdom; + this.record = record; + } + + /** + * Return an iterator for examining all names in the external references list. Does + * not return null. + */ + public IPDOMIterator getIterator() throws CoreException { + return new Iterator(record); + } + + /** + * Add the given name to this list. + */ + public void add(PDOMName name) throws CoreException { + // External references are stored in a linked list of linkages. Each node in that list + // is a list that uses the PDOMName binding list. + + PDOMLinkage nameLinkage = name.getLinkage(); + int nameLinkageID = nameLinkage.getLinkageID(); + + // Search through the nodes to find the one for the new name's linkage. Keep track of + // the record that held the last examined node so that a new node can be appended if + // needed. + long lastAddr = record; + long nodeRec = 0; + while((nodeRec = pdom.getDB().getRecPtr(lastAddr)) != 0) { + // Each node looks like { int linkageID; recPtr nextNode; recPtr headOfList; } + int linkageID = pdom.getDB().getInt(nodeRec); + if (linkageID == nameLinkageID) + break; + + lastAddr = nodeRec + Database.INT_SIZE; + } + + // If there isn't already a node for this linkage, then create a new one. + if (nodeRec == 0) { + nodeRec = pdom.getDB().malloc(Database.INT_SIZE + Database.PTR_SIZE + Database.PTR_SIZE); + + // Setup the new node for this linkage and initialize the list ptr with an empty list. + pdom.getDB().putInt(nodeRec, nameLinkageID); + pdom.getDB().putRecPtr(nodeRec + Database.INT_SIZE, 0); + pdom.getDB().putRecPtr(nodeRec + Database.INT_SIZE + Database.PTR_SIZE, 0); + + // Finally insert the new node right after the last one that was examined. + pdom.getDB().putRecPtr(lastAddr, nodeRec); + } + + // If the list is not empty then modify the first element to be right after the name that + // is being inserted. + long namerec = pdom.getDB().getRecPtr(nodeRec + Database.INT_SIZE + Database.PTR_SIZE); + if (namerec != 0) { + PDOMName first = new PDOMName(nameLinkage, namerec); + first.setPrevInBinding(name); + name.setNextInBinding(first); + } + + // Finally, make the new name the first element in the list. + pdom.getDB().putRecPtr(nodeRec + Database.INT_SIZE + Database.PTR_SIZE, name.getRecord()); + } + + private class Iterator implements IPDOMIterator { + private long nodeAddr; + private long node; + + private PDOMName next; + + public Iterator(long record) throws CoreException { + this.nodeAddr = record; + this.node = 0; + + // Initialize next by advancing to the first name. + this.next = advance(); + } + + @Override + public boolean hasNext() throws CoreException { + return next != null; + } + + @Override + public PDOMName next() throws CoreException { + if (next == null) + throw new NoSuchElementException(); + + PDOMName ret = next; + next = ret.getNextInBinding(); + if (next == null) + next = advance(); + return ret; + } + + /** + * Advance to the next linkage node that has a non-empty list of names. Return the + * PDOMName at the head of the list. This is the next name that should be returned + * from #next(). Return null if there are no more linkage nodes. + */ + private PDOMName advance() throws CoreException { + // Look through all linkage nodes to find the next one that has a non-empty + // names list. + while(true) { + // Skip over all nodes that don't have any names in their list. + long nextNameRec = 0; + while(nodeAddr != 0) { + node = pdom.getDB().getRecPtr(nodeAddr); + if (node == 0) + return null; + + nextNameRec = pdom.getDB().getRecPtr(node + Database.INT_SIZE + Database.PTR_SIZE); + if (nextNameRec != 0) + break; + nodeAddr = node + Database.INT_SIZE; + } + + // If nothing is found then there is no more iterating. + if (nodeAddr == 0 + || nextNameRec == 0) + return null; + + // If a node is found that has a name in the list, then update this iterator to + // point to the next linkage's node (this is so the next call to advance starts + // at the right place). + nodeAddr = pdom.getDB().getRecPtr(node + Database.INT_SIZE); + + // Load node's linkage and use it to return the first name in this node's list. + int linkageID = pdom.getDB().getInt(node); + PDOMLinkage linkage = pdom.getLinkage(linkageID); + if (linkage != null) + return new PDOMName(linkage, nextNameRec); + + // Generate a log message about the linkageID that is no longer valid, but continue + // to the next node. + CCorePlugin.log("Could not load linkage for external reference from linkageID " + linkageID); //$NON-NLS-1$ + } + } + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/IPDOMIterator.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/IPDOMIterator.java new file mode 100644 index 00000000000..f9e5143a273 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/IPDOMIterator.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 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 + */ +package org.eclipse.cdt.internal.core.pdom.dom; + +import java.util.NoSuchElementException; + +import org.eclipse.core.runtime.CoreException; + +/** + * A generic interface for iterating through lists that are stored in the PDOM. The + * difference between this interface and the standard one in java.util is that this + * one can throw a CoreException from either method. Also, this one does not provide + * a way to remove elements. + */ +public interface IPDOMIterator { + + /** + * Return true if the next call to #next will yield a value and false otherwise. + * + * @see java.util.Iterator#hasNext() + */ + public boolean hasNext() throws CoreException; + + /** + * Return the next element in the iteration. Throws {@link NoSuchElementException} if + * there are no elements left in the iteration. + * + * @see java.util.Iterator#next + */ + public T 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 22b81c33e48..cb5198df1dc 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 @@ -37,6 +37,7 @@ import org.eclipse.cdt.internal.core.index.IIndexScope; import org.eclipse.cdt.internal.core.pdom.PDOM; import org.eclipse.cdt.internal.core.pdom.db.Database; import org.eclipse.cdt.internal.core.pdom.db.IString; +import org.eclipse.cdt.internal.core.pdom.db.PDOMExternalReferencesList; import org.eclipse.cdt.internal.core.pdom.tag.PDOMTaggable; import org.eclipse.core.runtime.CoreException; @@ -46,13 +47,14 @@ import org.eclipse.core.runtime.CoreException; public abstract class PDOMBinding extends PDOMNamedNode implements IPDOMBinding { public static final PDOMBinding[] EMPTY_PDOMBINDING_ARRAY = {}; - private static final int FIRST_DECL_OFFSET = PDOMNamedNode.RECORD_SIZE + 0; // size 4 + private static final int FIRST_DECL_OFFSET = PDOMNamedNode.RECORD_SIZE + 0; // size 4 private static final int FIRST_DEF_OFFSET = PDOMNamedNode.RECORD_SIZE + 4; // size 4 private static final int FIRST_REF_OFFSET = PDOMNamedNode.RECORD_SIZE + 8; // size 4 private static final int LOCAL_TO_FILE = PDOMNamedNode.RECORD_SIZE + 12; // size 4 + private static final int FIRST_EXTREF_OFFSET = PDOMNamedNode.RECORD_SIZE + 16; // size 4 @SuppressWarnings("hiding") - protected static final int RECORD_SIZE = PDOMNamedNode.RECORD_SIZE + 16; + protected static final int RECORD_SIZE = PDOMNamedNode.RECORD_SIZE + 20; private byte hasDeclaration= -1; protected PDOMBinding(PDOMLinkage linkage, PDOMNode parent, char[] name) throws CoreException { @@ -95,7 +97,8 @@ public abstract class PDOMBinding extends PDOMNamedNode implements IPDOMBinding Database db = pdom.getDB(); return db.getRecPtr(record + FIRST_DECL_OFFSET) == 0 && db.getRecPtr(record + FIRST_DEF_OFFSET) == 0 - && db.getRecPtr(record + FIRST_REF_OFFSET) == 0; + && db.getRecPtr(record + FIRST_REF_OFFSET) == 0 + && db.getRecPtr(record + FIRST_EXTREF_OFFSET) == 0; } @Override @@ -132,6 +135,14 @@ public abstract class PDOMBinding extends PDOMNamedNode implements IPDOMBinding } public final void addReference(PDOMName name) throws CoreException { + // This needs to filter between the local and external lists because it can be used in + // contexts that don't know which type of list they are iterating over. E.g., this is + // used when deleting names from a PDOMFile. + if (!getLinkage().equals(name.getLinkage())) { + new PDOMExternalReferencesList(getPDOM(), record + FIRST_EXTREF_OFFSET).add(name); + return; + } + PDOMName first = getFirstReference(); if (first != null) { first.setPrevInBinding(name); @@ -165,7 +176,25 @@ public abstract class PDOMBinding extends PDOMNamedNode implements IPDOMBinding return namerec != 0 ? new PDOMName(getLinkage(), namerec) : null; } + /** + * Returns an iterator over the names in other linkages that reference this binding. Does + * not return null. + */ + public IPDOMIterator getExternalReferences() throws CoreException { + return new PDOMExternalReferencesList(getPDOM(), record + FIRST_EXTREF_OFFSET).getIterator(); + } + public void setFirstReference(PDOMName name) throws CoreException { + // This needs to filter between the local and external lists because it can be used in + // contexts that don't know which type of list they are iterating over. E.g., this is + // used when deleting names from a PDOMFile. + if (name != null + && !getLinkage().equals(name.getLinkage())) { + new PDOMExternalReferencesList(getPDOM(), record + FIRST_EXTREF_OFFSET).add(name); + return; + } + + // Otherwise put the reference into list of locals. long namerec = name != null ? name.getRecord() : 0; getDB().putRecPtr(record + FIRST_REF_OFFSET, namerec); } 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 d222425d19c..d6da2296727 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 @@ -33,7 +33,7 @@ import org.eclipse.core.runtime.IPath; public final class PDOMName implements IIndexFragmentName, IASTFileLocation { private final PDOMLinkage linkage; private final long record; - + private static final int FILE_REC_OFFSET = 0; private static final int FILE_NEXT_OFFSET = 4; private static final int CALLER_REC_OFFSET = 8; @@ -42,7 +42,7 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { private static final int BINDING_NEXT_OFFSET = 20; private static final int NODE_OFFSET_OFFSET = 24; // 3-byte unsigned int (sufficient for files <= 16mb) private static final int NODE_LENGTH_OFFSET = 27; // short (sufficient for names <= 32k) - private static final int FLAGS = 29; + private static final int FLAGS = 29; private static final int RECORD_SIZE = 30; // 30 yields a 32-byte block. (31 would trigger a 40-byte block) @@ -58,7 +58,6 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { public static final int READ_ACCESS = 0x20; public static final int WRITE_ACCESS = 0x40; - public PDOMName(PDOMLinkage linkage, IASTName name, PDOMFile file, PDOMBinding binding, PDOMName caller) throws CoreException { this.linkage = linkage; @@ -67,7 +66,7 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { // What kind of name are we int flags= getRoleOfName(name); - + flags |= binding.getAdditionalNameFlags(flags, name); db.putByte(record + FLAGS, (byte) flags); @@ -85,7 +84,7 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { } db.putRecPtr(record + BINDING_REC_OFFSET, binding.getRecord()); - + db.putRecPtr(record + FILE_REC_OFFSET, file.getRecord()); if (caller != null) { db.putRecPtr(record + CALLER_REC_OFFSET, caller.getRecord()); @@ -100,18 +99,22 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { private int getRoleOfName(IASTName name) { if (name.isDefinition()) { return IS_DEFINITION; - } + } if (name.isDeclaration()) { return IS_DECLARATION; - } + } return IS_REFERENCE; } - + public PDOMName(PDOMLinkage linkage, long nameRecord) { this.linkage = linkage; this.record = nameRecord; } - + + public PDOMLinkage getLinkage() { + return linkage; + } + public long getRecord() { return record; } @@ -127,7 +130,7 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { public PDOM getPDOM() { return linkage.getPDOM(); } - + @Override public PDOMBinding getBinding() throws CoreException { long bindingrec = getRecField(BINDING_REC_OFFSET); @@ -160,11 +163,11 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { public PDOMName getNextInBinding() throws CoreException { return getNameField(BINDING_NEXT_OFFSET); } - + public void setNextInBinding(PDOMName name) throws CoreException { setNameField(BINDING_NEXT_OFFSET, name); } - + @Override public PDOMFile getFile() throws CoreException { long filerec = linkage.getDB().getRecPtr(record + FILE_REC_OFFSET); @@ -188,11 +191,11 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { long getEnclosingDefinitionRecord() throws CoreException { return linkage.getDB().getRecPtr(record + CALLER_REC_OFFSET); } - + public PDOMName getNextInFile() throws CoreException { return getNameField(FILE_NEXT_OFFSET); } - + public void setNextInFile(PDOMName name) throws CoreException { setNameField(FILE_NEXT_OFFSET, name); } @@ -223,7 +226,7 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { public String toString() { return new String(getSimpleID()); } - + private int getFlags(int mask) throws CoreException { return linkage.getDB().getByte(record + FLAGS) & mask; } @@ -254,7 +257,7 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { public boolean isBaseSpecifier() throws CoreException { return getFlags(INHERIT_FRIEND_INLINE_MASK) == IS_INHERITANCE_SPEC; } - + @Override public boolean isInlineNamespaceDefinition() throws CoreException { return getFlags(INHERIT_FRIEND_INLINE_MASK) == IS_INLINE_NAMESPACE; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/indexview/CountNodeAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/indexview/CountNodeAction.java index ad4f01a3995..14a6d73b991 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/indexview/CountNodeAction.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/indexview/CountNodeAction.java @@ -26,6 +26,7 @@ import org.eclipse.cdt.internal.core.CCoreInternals; import org.eclipse.cdt.internal.core.pdom.IPDOM; import org.eclipse.cdt.internal.core.pdom.PDOM; import org.eclipse.cdt.internal.core.pdom.db.IBTreeVisitor; +import org.eclipse.cdt.internal.core.pdom.dom.IPDOMIterator; 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.PDOMMacro; @@ -59,11 +60,11 @@ public class CountNodeAction extends IndexAction { static final int REFS = 3; static final int DECLS = 4; static final int DEFS = 5; - + @Override public void run() { final int[] count = new int[6]; - + try { ISelection selection = viewer.getSelection(); if (!(selection instanceof IStructuredSelection)) @@ -115,6 +116,10 @@ public class CountNodeAction extends IndexAction { .getFirstReference(); name != null; name = name .getNextInBinding()) ++count[REFS]; + for (IPDOMIterator i = binding.getExternalReferences(); + i.hasNext(); + i.next()) + ++count[REFS]; for (PDOMName name = binding .getFirstDeclaration(); name != null; name = name .getNextInBinding()) @@ -141,7 +146,7 @@ public class CountNodeAction extends IndexAction { Thread.currentThread().interrupt(); return; } - + MessageDialog.openInformation(null, CUIPlugin.getResourceString("IndexView.CountSymbols.title"), //$NON-NLS-1$ CUIPlugin.getFormattedString("IndexView.CountSymbols.message", //$NON-NLS-1$