possibility to have several stub trees in a file (CR-IC-6823)

This commit is contained in:
Konstantin Ulitin
2014-12-17 17:21:18 +03:00
parent 8436e58a6e
commit 9f366d8b6a
12 changed files with 244 additions and 37 deletions
@@ -33,6 +33,7 @@ import com.intellij.psi.*
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.impl.PsiManagerEx
import com.intellij.psi.impl.file.impl.FileManagerImpl
import com.intellij.psi.impl.source.PsiFileWithStubSupport
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiSearchHelper
import com.intellij.testFramework.IdeaTestUtil
@@ -359,4 +360,20 @@ public class IndexTest extends JavaCodeInsightFixtureTestCase {
assert JavaPsiFacade.getInstance(project).findClass("Foo3", scope)
}
public void "test do not collect stub tree while holding stub elements"() throws IOException {
final VirtualFile vFile = myFixture.addClass("class Foo {}").getContainingFile().getVirtualFile();
PsiFileWithStubSupport psiFile = getPsiManager().findFile(vFile) as PsiFileWithStubSupport;
assertNotNull(psiFile);
def clazz = findClass("Foo")
assertNotNull(clazz)
def stubTreeHash = psiFile.getStubTree().hashCode()
PlatformTestUtil.tryGcSoftlyReachableObjects();
def stubTree = psiFile.getStubTree()
assertNotNull(stubTree)
assertEquals(stubTreeHash, stubTree.hashCode())
}
}
@@ -22,7 +22,11 @@ package com.intellij.psi.stubs;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.psi.PsiFile;
import com.intellij.psi.tree.IStubFileElementType;
import org.jetbrains.annotations.NotNull;
public interface PsiFileStub<T extends PsiFile> extends StubElement<T>, UserDataHolder {
IStubFileElementType getType();
@NotNull
PsiFileStub[] getStubRoots();
}
@@ -29,6 +29,7 @@ public class PsiFileStubImpl<T extends PsiFile> extends StubBase<T> implements P
public static final IStubFileElementType TYPE = new IStubFileElementType(Language.ANY);
private volatile T myFile;
private volatile String myInvalidationReason;
private volatile PsiFileStub[] myStubRoots;
public PsiFileStubImpl(final T file) {
super(null, null);
@@ -64,4 +65,14 @@ public class PsiFileStubImpl<T extends PsiFile> extends StubBase<T> implements P
public IStubFileElementType getType() {
return TYPE;
}
@NotNull
@Override
public PsiFileStub[] getStubRoots() {
return myStubRoots != null ? myStubRoots : new PsiFileStub[]{this};
}
public void setStubRoots(@NotNull PsiFileStub[] roots) {
myStubRoots = roots;
}
}
@@ -78,7 +78,17 @@ public class StubSerializationHelper {
FileLocalStringEnumerator storage = new FileLocalStringEnumerator(true);
StubOutputStream stubOutputStream = new StubOutputStream(out, storage);
doSerialize(rootStub, stubOutputStream);
if (rootStub instanceof PsiFileStub) {
final PsiFileStub[] roots = ((PsiFileStub)rootStub).getStubRoots();
DataInputOutputUtil.writeINT(stubOutputStream, roots.length);
for (PsiFileStub root : roots) {
doSerialize(root, stubOutputStream);
}
}
else {
DataInputOutputUtil.writeINT(stubOutputStream, 1);
doSerialize(rootStub, stubOutputStream);
}
DataOutputStream resultStream = new DataOutputStream(stream);
DataInputOutputUtil.writeINT(resultStream, storage.myStrings.size());
byte[] buffer = IOUtil.allocReadWriteUTFBuffer();
@@ -111,7 +121,24 @@ public class StubSerializationHelper {
++i;
}
return deserialize(inputStream, null);
int stubFilesCount = DataInputOutputUtil.readINT(inputStream);
if (stubFilesCount > 1) {
final List<PsiFileStub> stubs = new ArrayList<PsiFileStub>(stubFilesCount);
while (stubFilesCount-- > 0) {
final PsiFileStub fileStub = (PsiFileStub)deserialize(inputStream, null);
stubs.add(fileStub);
}
final PsiFileStub[] stubsArray = stubs.toArray(new PsiFileStub[stubs.size()]);
for (PsiFileStub stub : stubsArray) {
if (stub instanceof PsiFileStubImpl) {
((PsiFileStubImpl)stub).setStubRoots(stubsArray);
}
}
return stubsArray[0];
}
else {
return deserialize(inputStream, null);
}
}
String intern(String str) {
@@ -678,15 +678,10 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
final VirtualFile vFile = getVirtualFile();
if (!(vFile instanceof VirtualFileWithId)) return null;
final PsiFile stubBindingRoot = getViewProvider().getStubBindingRoot();
if (stubBindingRoot != this) {
LOG.error("Attempted to create stubs for non-root file: " + this + ", stub binding root: " + stubBindingRoot);
return null;
}
ObjectStubTree tree = StubTreeLoader.getInstance().readOrBuild(getProject(), vFile, this);
if (!(tree instanceof StubTree)) return null;
StubTree stubHolder = (StubTree)tree;
final List<Pair<IStubFileElementType, PsiFile>> roots = StubTreeBuilder.getStubbedRoots(getViewProvider());
synchronized (PsiLock.LOCK) {
if (getTreeElement() != null) return null;
@@ -694,9 +689,17 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
final StubTree derefdOnLock = derefStub();
if (derefdOnLock != null) return derefdOnLock;
//noinspection unchecked
((StubBase)stubHolder.getRoot()).setPsi(this);
myStub = new SoftReference<StubTree>(stubHolder);
final PsiFileStub[] stubRoots = stubHolder.getRoot().getStubRoots();
int matchingRoot = 0;
for (Pair<IStubFileElementType, PsiFile> root : roots) {
final PsiFileStub matchingStub = stubRoots[matchingRoot++];
//noinspection unchecked
((StubBase)matchingStub).setPsi(root.second);
final StubTree stubTree = new StubTree(matchingStub);
stubTree.setDebugInfo("created in getStubTree()");
if (root.second == this) stubHolder = stubTree;
((PsiFileImpl)root.second).myStub = new SoftReference<StubTree>(stubTree);
}
return stubHolder;
}
}
@@ -780,6 +783,7 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
public int getStartOffsetInParent() {
return calcTreeElement().getStartOffsetInParent();
}
@Override
public int getTextOffset() {
return calcTreeElement().getTextOffset();
@@ -25,7 +25,7 @@ import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.IStubFileElementType;
public class CumulativeStubVersion {
private static final int VERSION = 27;
private static final int VERSION = 28;
public static int getCumulativeVersion() {
int version = VERSION;
@@ -30,7 +30,7 @@ import java.util.Map;
* Date: 8/3/12
*/
public class ObjectStubTree<T extends Stub> {
private static final Key<ObjectStubTree> STUB_TO_TREE_REFERENCE = Key.create("stub to tree reference");
protected static final Key<ObjectStubTree> STUB_TO_TREE_REFERENCE = Key.create("stub to tree reference");
protected final ObjectStubBase myRoot;
private String myDebugInfo;
protected final List<T> myPlainList = new ArrayList<T>();
@@ -53,12 +53,17 @@ public class ObjectStubTree<T extends Stub> {
return myPlainList;
}
@NotNull
public List<T> getPlainListFromAllRoots() {
return getPlainList();
}
@NotNull
public Map<StubIndexKey, Map<Object, int[]>> indexStubTree() {
StubIndexSink sink = new StubIndexSink();
for (int i = 0, plainListSize = myPlainList.size(); i < plainListSize; i++) {
final Stub stub = myPlainList.get(i);
final List<T> plainList = getPlainListFromAllRoots();
for (int i = 0, plainListSize = plainList.size(); i < plainListSize; i++) {
final Stub stub = plainList.get(i);
sink.myStubIdx = i;
StubSerializationUtil.getSerializer(stub).indexStub(stub, sink);
}
@@ -66,11 +71,15 @@ public class ObjectStubTree<T extends Stub> {
return sink.getResult();
}
protected static void enumerateStubs(@NotNull Stub root, @NotNull List<Stub> result) {
((ObjectStubBase)root).id = result.size();
protected void enumerateStubs(@NotNull Stub root, @NotNull List<Stub> result) {
enumerateStubs(root, result, 0);
}
protected static void enumerateStubs(@NotNull Stub root, @NotNull List<Stub> result, int idOffset) {
((ObjectStubBase)root).id = idOffset + result.size();
result.add(root);
for (Stub child : root.getChildrenStubs()) {
enumerateStubs(child, result);
enumerateStubs(child, result, idOffset);
}
}
@@ -19,8 +19,14 @@
*/
package com.intellij.psi.stubs;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class StubTree extends ObjectStubTree<StubElement<?>> {
public StubTree(@NotNull final PsiFileStub root) {
@@ -31,6 +37,41 @@ public class StubTree extends ObjectStubTree<StubElement<?>> {
super((ObjectStubBase)root, withBackReference);
}
@Override
protected void enumerateStubs(@NotNull Stub root, @NotNull List<Stub> result) {
final PsiFileStub[] files = ((PsiFileStub)root).getStubRoots();
int idOffset = 0;
final List<Stub> dummyList = new ArrayList<Stub>();
for (PsiFileStub file : files) {
if (file == root) break;
final int fileStubsCount;
final ObjectStubTree existingTree = file.getUserData(STUB_TO_TREE_REFERENCE);
if (existingTree != null) {
fileStubsCount = existingTree.getPlainList().size();
}
else {
dummyList.clear();
enumerateStubs(file, dummyList, idOffset);
fileStubsCount = dummyList.size();
}
idOffset += fileStubsCount;
}
enumerateStubs(root, result, idOffset);
}
@NotNull
@Override
public List<StubElement<?>> getPlainListFromAllRoots() {
return ContainerUtil.concat(getRoot().getStubRoots(), new Function<PsiFileStub, Collection<? extends StubElement<?>>>() {
@Override
public Collection<? extends StubElement<?>> fun(PsiFileStub stub) {
final ObjectStubTree existingTree = stub.getUserData(STUB_TO_TREE_REFERENCE);
//noinspection unchecked
return existingTree != null ? existingTree.getPlainList() : new StubTree(stub, false).getPlainList();
}
});
}
@NotNull
@Override
public PsiFileStub getRoot() {
@@ -20,16 +20,30 @@ import com.intellij.lang.LanguageParserDefinitions;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiFile;
import com.intellij.psi.StubBuilder;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.IStubFileElementType;
import com.intellij.util.Function;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileContent;
import com.intellij.util.indexing.FileContentImpl;
import com.intellij.util.indexing.IndexingDataKeys;
import com.intellij.util.indexing.SubstitutedFileType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class StubTreeBuilder {
private static final Key<Stub> stubElementKey = Key.create("stub.tree.for.file.content");
@@ -59,7 +73,8 @@ public class StubTreeBuilder {
CharSequence contentAsText = inputData.getContentAsText();
FileContentImpl fileContent = (FileContentImpl)inputData;
PsiFile psi = fileContent.getPsiFileForPsiDependentIndex();
psi = psi.getViewProvider().getStubBindingRoot();
final FileViewProvider viewProvider = psi.getViewProvider();
psi = viewProvider.getStubBindingRoot();
psi.putUserData(IndexingDataKeys.FILE_TEXT_CONTENT_KEY, contentAsText);
// if we load AST, it should be easily gc-able. See PsiFileImpl.createTreeElementPointer()
@@ -80,11 +95,31 @@ public class StubTreeBuilder {
stubFileElementType = null;
}
if (stubFileElementType != null) {
StubBuilder stubBuilder = stubFileElementType.getBuilder();
final StubBuilder stubBuilder = stubFileElementType.getBuilder();
if (stubBuilder instanceof LightStubBuilder) {
LightStubBuilder.FORCED_AST.set(fileContent.getLighterASTForPsiDependentIndex());
}
data = stubBuilder.buildStubTree(psi);
final List<Pair<IStubFileElementType, PsiFile>> stubbedRoots = getStubbedRoots(viewProvider);
if (stubbedRoots.size() > 1) {
final List<PsiFileStub> stubs = new ArrayList<PsiFileStub>(stubbedRoots.size());
stubs.add((PsiFileStub)data);
for (Pair<IStubFileElementType, PsiFile> stubbedRoot : stubbedRoots) {
if (psi == stubbedRoot.second) continue;
final StubElement element = stubbedRoot.first.getBuilder().buildStubTree(stubbedRoot.second);
if (element instanceof PsiFileStub) {
stubs.add((PsiFileStub)element);
}
}
final PsiFileStub[] stubsArray = stubs.toArray(new PsiFileStub[stubs.size()]);
for (PsiFileStub stub : stubsArray) {
if (stub instanceof PsiFileStubImpl) {
((PsiFileStubImpl)stub).setStubRoots(stubsArray);
}
}
}
}
}
finally {
@@ -97,4 +132,37 @@ public class StubTreeBuilder {
return data;
}
}
/** Order is deterministic. First element matches {@link FileViewProvider#getStubBindingRoot()} */
@NotNull
public static List<Pair<IStubFileElementType, PsiFile>> getStubbedRoots(@NotNull FileViewProvider viewProvider) {
final List<Trinity<Language, IStubFileElementType, PsiFile>> roots =
new SmartList<Trinity<Language, IStubFileElementType, PsiFile>>();
final PsiFile stubBindingRoot = viewProvider.getStubBindingRoot();
for (Language language : viewProvider.getLanguages()) {
final PsiFile file = viewProvider.getPsi(language);
if (file instanceof PsiFileImpl) {
final IElementType contentType = ((PsiFileImpl)file).getContentElementType();
if (contentType instanceof IStubFileElementType) {
roots.add(Trinity.create(language, (IStubFileElementType)contentType, file));
}
}
}
ContainerUtil.sort(roots, new Comparator<Trinity<Language, IStubFileElementType, PsiFile>>() {
@Override
public int compare(Trinity<Language, IStubFileElementType, PsiFile> o1, Trinity<Language, IStubFileElementType, PsiFile> o2) {
if (o1.third == stubBindingRoot) return o2.third == stubBindingRoot ? 0 : -1;
else if (o2.third == stubBindingRoot) return 1;
else return StringUtil.compare(o1.first.getID(), o2.first.getID(), false);
}
});
return ContainerUtil.map(roots, new Function<Trinity<Language, IStubFileElementType, PsiFile>, Pair<IStubFileElementType, PsiFile>>() {
@Override
public Pair<IStubFileElementType, PsiFile> fun(Trinity<Language, IStubFileElementType, PsiFile> trinity) {
return Pair.create(trinity.second, trinity.third);
}
});
}
}
@@ -3,17 +3,16 @@ package com.intellij.psi.stubs;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiPlainTextFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.PsiFileWithStubSupport;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IStubFileElementType;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -52,11 +51,12 @@ public abstract class StubProcessingHelperBase {
boolean customStubs = false;
if (candidatePsiFile != null && !(candidatePsiFile instanceof PsiPlainTextFile)) {
candidatePsiFile = candidatePsiFile.getViewProvider().getStubBindingRoot();
if (candidatePsiFile instanceof PsiFileWithStubSupport) {
psiFile = (PsiFileWithStubSupport)candidatePsiFile;
final FileViewProvider viewProvider = candidatePsiFile.getViewProvider();
final PsiFile stubBindingRoot = viewProvider.getStubBindingRoot();
if (stubBindingRoot instanceof PsiFileWithStubSupport) {
psiFile = (PsiFileWithStubSupport)stubBindingRoot;
stubTree = psiFile.getStubTree();
if (stubTree == null && psiFile instanceof PsiFileImpl) {
if (stubTree == null && psiFile instanceof PsiFileImpl) {
IElementType contentElementType = ((PsiFileImpl)psiFile).getContentElementType();
if (contentElementType instanceof IStubFileElementType) {
stubTree = ((PsiFileImpl)psiFile).calcStubTree();
@@ -67,6 +67,26 @@ public abstract class StubProcessingHelperBase {
}
}
}
if (!customStubs && stubTree != null) {
final List<PsiFileStub> roots = new SmartList<PsiFileStub>(stubTree.getRoot());
final List<Pair<IStubFileElementType, PsiFile>> stubbedRoots = StubTreeBuilder.getStubbedRoots(viewProvider);
for (Pair<IStubFileElementType, PsiFile> stubbedRoot : stubbedRoots) {
if (stubbedRoot.second == stubBindingRoot) continue;
if (stubbedRoot.second instanceof PsiFileImpl) {
final StubTree secondaryStubTree = ((PsiFileImpl)stubbedRoot.second).calcStubTree();
if (secondaryStubTree != null) {
final PsiFileStub root = secondaryStubTree.getRoot();
roots.add(root);
}
}
}
final PsiFileStub[] rootsArray = roots.toArray(new PsiFileStub[roots.size()]);
for (PsiFileStub root : rootsArray) {
if (root instanceof PsiFileStubImpl) {
((PsiFileStubImpl)root).setStubRoots(rootsArray);
}
}
}
}
if (stubTree == null && psiFile == null) {
@@ -81,7 +101,7 @@ public abstract class StubProcessingHelperBase {
return processor.process((Psi)psiFile); // e.g. dom indices
}
stubTree = (StubTree)objectStubTree;
final List<StubElement<?>> plained = stubTree.getPlainList();
final List<StubElement<?>> plained = stubTree.getPlainListFromAllRoots();
for (int i = 0, size = value.size(); i < size; i++) {
final int stubTreeIndex = value.get(i);
if (stubTreeIndex >= plained.size()) {
@@ -128,7 +148,7 @@ public abstract class StubProcessingHelperBase {
}
}
else {
final List<StubElement<?>> plained = stubTree.getPlainList();
final List<StubElement<?>> plained = stubTree.getPlainListFromAllRoots();
for (int i = 0, size = value.size(); i < size; i++) {
final int stubTreeIndex = value.get(i);
if (stubTreeIndex >= plained.size()) {
@@ -348,7 +348,10 @@ public class StubUpdatingIndex extends CustomImplementationFileBasedIndexExtensi
if (!data.isEmpty()) {
final SerializedStubTree stub = data.values().iterator().next();
ObjectStubBase root = (ObjectStubBase)stub.getStub(true);
Map<StubIndexKey, Map<Object, int[]>> map = new ObjectStubTree(root, false).indexStubTree();
ObjectStubTree objectStubTree = root instanceof PsiFileStub ? new StubTree((PsiFileStub)root, false) :
new ObjectStubTree(root, false);
Map<StubIndexKey, Map<Object, int[]>> map = objectStubTree.indexStubTree();
// xxx:fix refs inplace
stubTree = (Map)map;
@@ -32,7 +32,7 @@ import com.intellij.psi.impl.source.DummyHolder;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.tree.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.IStubFileElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.CharTable;
@@ -46,7 +46,7 @@ import javax.swing.*;
/**
* @author peter
*/
public class TemplateDataElementType extends IFileElementType implements ITemplateDataElementType {
public class TemplateDataElementType extends IStubFileElementType implements ITemplateDataElementType {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.templateLanguages.TemplateDataElementType");
public static final LanguageExtension<TreePatcher> TREE_PATCHER = new LanguageExtension<TreePatcher>("com.intellij.lang.treePatcher", new SimpleTreePatcher());
@@ -72,7 +72,8 @@ public class TemplateDataElementType extends IFileElementType implements ITempla
public ASTNode parseContents(ASTNode chameleon) {
final CharTable table = SharedImplUtil.findCharTableByTree(chameleon);
final FileElement treeElement = new DummyHolder(((TreeElement)chameleon).getManager(), null, table).getTreeElement();
final PsiFile file = (PsiFile)TreeUtil.getFileElement((TreeElement)chameleon).getPsi();
final FileElement fileElement = TreeUtil.getFileElement((TreeElement)chameleon);
final PsiFile file = (PsiFile)fileElement.getPsi();
PsiFile originalFile = file.getOriginalFile();
final TemplateLanguageFileViewProvider viewProvider = (TemplateLanguageFileViewProvider)originalFile.getViewProvider();
@@ -112,8 +113,10 @@ public class TemplateDataElementType extends IFileElementType implements ITempla
DebugUtil.checkTreeStructure(parsed);
DebugUtil.checkTreeStructure(treeElement);
DebugUtil.checkTreeStructure(chameleon);
DebugUtil.checkTreeStructure(file.getNode());
DebugUtil.checkTreeStructure(originalFile.getNode());
if (fileElement != chameleon) {
DebugUtil.checkTreeStructure(file.getNode());
DebugUtil.checkTreeStructure(originalFile.getNode());
}
return childNode;
}