From 4fddc9abcd31d25d427ab3e0a41aa93b62fa0175 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Sat, 25 Jun 2016 12:53:03 +0200 Subject: [PATCH 1/6] synchronize on model to avoid concurrency problems: when background search runs & Edt renderer calls FindResultUsageInfo#isValid --- .../intellij/find/impl/FindManagerImpl.java | 281 +++++++++--------- 1 file changed, 145 insertions(+), 136 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java index 600b4a651afb..65f871303e05 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java @@ -536,175 +536,184 @@ public class FindManagerImpl extends FindManager { int offset, @NotNull FindModel model, @NotNull final VirtualFile file) { - FileType ftype = file.getFileType(); - Language lang = null; - if (ftype instanceof LanguageFileType) { - lang = ((LanguageFileType)ftype).getLanguage(); - } - - CommentsLiteralsSearchData data = model.getUserData(ourCommentsLiteralsSearchDataKey); - if (data == null || !Comparing.equal(data.lastFile, file) || !data.model.equals(model)) { - SyntaxHighlighter highlighter = getHighlighter(file, lang); - - if (highlighter == null) { - // no syntax highlighter -> no search - return NOT_FOUND_RESULT; + synchronized (model) { + FileType ftype = file.getFileType(); + Language lang = null; + if (ftype instanceof LanguageFileType) { + lang = ((LanguageFileType)ftype).getLanguage(); } - TokenSet tokensOfInterest = TokenSet.EMPTY; - Set relevantLanguages; - if (lang != null) { - final Language finalLang = lang; - relevantLanguages = ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public Set compute() { - THashSet result = new THashSet(); + CommentsLiteralsSearchData data = model.getUserData(ourCommentsLiteralsSearchDataKey); + if (data == null || !Comparing.equal(data.lastFile, file) || !data.model.equals(model)) { + SyntaxHighlighter highlighter = getHighlighter(file, lang); - FileViewProvider viewProvider = PsiManager.getInstance(myProject).findViewProvider(file); - if (viewProvider != null) { - result.addAll(viewProvider.getLanguages()); - } - - if (result.isEmpty()) { - result.add(finalLang); - } - return result; - } - }); - - for (Language relevantLanguage:relevantLanguages) { - tokensOfInterest = addTokenTypesForLanguage(model, relevantLanguage, tokensOfInterest); + if (highlighter == null) { + // no syntax highlighter -> no search + return NOT_FOUND_RESULT; } - if(model.isInStringLiteralsOnly()) { - // TODO: xml does not have string literals defined so we add XmlAttributeValue element type as convenience - final Lexer xmlLexer = getHighlighter(null, Language.findLanguageByID("XML")).getHighlightingLexer(); - final String marker = "xxx"; - xmlLexer.start(""); + TokenSet tokensOfInterest = TokenSet.EMPTY; + Set relevantLanguages; + if (lang != null) { + final Language finalLang = lang; + relevantLanguages = ApplicationManager.getApplication().runReadAction(new Computable>() { + @Override + public Set compute() { + THashSet result = new THashSet(); - while (!marker.equals(xmlLexer.getTokenText())) { - xmlLexer.advance(); - if (xmlLexer.getTokenType() == null) break; + FileViewProvider viewProvider = PsiManager.getInstance(myProject).findViewProvider(file); + if (viewProvider != null) { + result.addAll(viewProvider.getLanguages()); + } + + if (result.isEmpty()) { + result.add(finalLang); + } + return result; + } + }); + + for (Language relevantLanguage : relevantLanguages) { + tokensOfInterest = addTokenTypesForLanguage(model, relevantLanguage, tokensOfInterest); } - IElementType convenienceXmlAttrType = xmlLexer.getTokenType(); - if (convenienceXmlAttrType != null) { - tokensOfInterest = TokenSet.orSet(tokensOfInterest, TokenSet.create(convenienceXmlAttrType)); - } - } - } else { - relevantLanguages = ContainerUtil.newHashSet(); - if (ftype instanceof AbstractFileType) { - if (model.isInCommentsOnly()) { - tokensOfInterest = TokenSet.create(CustomHighlighterTokenType.LINE_COMMENT, CustomHighlighterTokenType.MULTI_LINE_COMMENT); - } if (model.isInStringLiteralsOnly()) { - tokensOfInterest = TokenSet.orSet(tokensOfInterest, TokenSet.create(CustomHighlighterTokenType.STRING, CustomHighlighterTokenType.SINGLE_QUOTED_STRING)); + // TODO: xml does not have string literals defined so we add XmlAttributeValue element type as convenience + final Lexer xmlLexer = getHighlighter(null, Language.findLanguageByID("XML")).getHighlightingLexer(); + final String marker = "xxx"; + xmlLexer.start(""); + + while (!marker.equals(xmlLexer.getTokenText())) { + xmlLexer.advance(); + if (xmlLexer.getTokenType() == null) break; + } + + IElementType convenienceXmlAttrType = xmlLexer.getTokenType(); + if (convenienceXmlAttrType != null) { + tokensOfInterest = TokenSet.orSet(tokensOfInterest, TokenSet.create(convenienceXmlAttrType)); + } } } + else { + relevantLanguages = ContainerUtil.newHashSet(); + if (ftype instanceof AbstractFileType) { + if (model.isInCommentsOnly()) { + tokensOfInterest = TokenSet.create(CustomHighlighterTokenType.LINE_COMMENT, CustomHighlighterTokenType.MULTI_LINE_COMMENT); + } + if (model.isInStringLiteralsOnly()) { + tokensOfInterest = TokenSet.orSet(tokensOfInterest, TokenSet + .create(CustomHighlighterTokenType.STRING, CustomHighlighterTokenType.SINGLE_QUOTED_STRING)); + } + } + } + + Matcher matcher = model.isRegularExpressions() ? compileRegExp(model, "") : null; + StringSearcher searcher = matcher != null ? null : new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), true); + SyntaxHighlighterOverEditorHighlighter highlighterAdapter = + new SyntaxHighlighterOverEditorHighlighter(highlighter, file, myProject); + data = + new CommentsLiteralsSearchData(file, relevantLanguages, highlighterAdapter, tokensOfInterest, searcher, matcher, model.clone()); + data.highlighter.restart(text); + model.putUserData(ourCommentsLiteralsSearchDataKey, data); } - Matcher matcher = model.isRegularExpressions() ? compileRegExp(model, ""):null; - StringSearcher searcher = matcher != null ? null: new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), true); - SyntaxHighlighterOverEditorHighlighter highlighterAdapter = new SyntaxHighlighterOverEditorHighlighter(highlighter, file, myProject); - data = new CommentsLiteralsSearchData(file, relevantLanguages, highlighterAdapter, tokensOfInterest, searcher, matcher, model.clone()); - data.highlighter.restart(text); - model.putUserData(ourCommentsLiteralsSearchDataKey, data); - } + int initialStartOffset = model.isForward() && data.startOffset < offset ? data.startOffset : 0; + data.highlighter.resetPosition(initialStartOffset); + final Lexer lexer = data.highlighter.getHighlightingLexer(); - int initialStartOffset = model.isForward() && data.startOffset < offset ? data.startOffset : 0; - data.highlighter.resetPosition(initialStartOffset); - final Lexer lexer = data.highlighter.getHighlightingLexer(); + IElementType tokenType; + TokenSet tokens = data.tokensOfInterest; - IElementType tokenType; - TokenSet tokens = data.tokensOfInterest; + int lastGoodOffset = 0; + boolean scanningForward = model.isForward(); + FindResultImpl prevFindResult = NOT_FOUND_RESULT; - int lastGoodOffset = 0; - boolean scanningForward = model.isForward(); - FindResultImpl prevFindResult = NOT_FOUND_RESULT; + while ((tokenType = lexer.getTokenType()) != null) { + if (lexer.getState() == 0) lastGoodOffset = lexer.getTokenStart(); - while((tokenType = lexer.getTokenType()) != null) { - if (lexer.getState() == 0) lastGoodOffset = lexer.getTokenStart(); + final TextAttributesKey[] keys = data.highlighter.getTokenHighlights(tokenType); - final TextAttributesKey[] keys = data.highlighter.getTokenHighlights(tokenType); - - if (tokens.contains(tokenType) || - (model.isInStringLiteralsOnly() && ChunkExtractor.isHighlightedAsString(keys)) || - (model.isInCommentsOnly() && ChunkExtractor.isHighlightedAsComment(keys)) - ) { - int start = lexer.getTokenStart(); - int end = lexer.getTokenEnd(); - if (model.isInStringLiteralsOnly()) { // skip literal quotes itself from matching - char c = text.charAt(start); - if (c == '"' || c == '\'') { - while (start < end && c == text.charAt(start)) { - ++start; - if (c == text.charAt(end - 1) && start < end) --end; + if (tokens.contains(tokenType) || + (model.isInStringLiteralsOnly() && ChunkExtractor.isHighlightedAsString(keys)) || + (model.isInCommentsOnly() && ChunkExtractor.isHighlightedAsComment(keys)) + ) { + int start = lexer.getTokenStart(); + int end = lexer.getTokenEnd(); + if (model.isInStringLiteralsOnly()) { // skip literal quotes itself from matching + char c = text.charAt(start); + if (c == '"' || c == '\'') { + while (start < end && c == text.charAt(start)) { + ++start; + if (c == text.charAt(end - 1) && start < end) --end; + } } } - } - while(true) { - FindResultImpl findResult = null; + while (true) { + FindResultImpl findResult = null; - if (data.searcher != null) { - int matchStart = data.searcher.scan(text, textArray, start, end); + if (data.searcher != null) { + int matchStart = data.searcher.scan(text, textArray, start, end); - if (matchStart != -1 && matchStart >= start) { - final int matchEnd = matchStart + model.getStringToFind().length(); - if (matchStart >= offset || !scanningForward) - findResult = new FindResultImpl(matchStart, matchEnd); - else { - start = matchEnd; - continue; - } - } - } else if (start <= end) { - data.matcher.reset(StringPattern.newBombedCharSequence(text.subSequence(start, end))); - if (data.matcher.find()) { - final int matchEnd = start + data.matcher.end(); - int matchStart = start + data.matcher.start(); - if (matchStart >= offset || !scanningForward) { - findResult = new FindResultImpl(matchStart, matchEnd); - } - else { - int diff = 0; - if (start == end) { - diff = scanningForward ? 1:-1; + if (matchStart != -1 && matchStart >= start) { + final int matchEnd = matchStart + model.getStringToFind().length(); + if (matchStart >= offset || !scanningForward) + findResult = new FindResultImpl(matchStart, matchEnd); + else { + start = matchEnd; + continue; } - start = matchEnd + diff; + } + } + else if (start <= end) { + data.matcher.reset(StringPattern.newBombedCharSequence(text.subSequence(start, end))); + if (data.matcher.find()) { + final int matchEnd = start + data.matcher.end(); + int matchStart = start + data.matcher.start(); + if (matchStart >= offset || !scanningForward) { + findResult = new FindResultImpl(matchStart, matchEnd); + } + else { + int diff = 0; + if (start == end) { + diff = scanningForward ? 1 : -1; + } + start = matchEnd + diff; + continue; + } + } + } + + if (findResult != null) { + if (scanningForward) { + data.startOffset = lastGoodOffset; + return findResult; + } + else { + + if (findResult.getEndOffset() >= offset) return prevFindResult; + prevFindResult = findResult; + start = findResult.getEndOffset(); continue; } } + break; } - - if (findResult != null) { - if (scanningForward) { - data.startOffset = lastGoodOffset; - return findResult; - } else { - - if (findResult.getEndOffset() >= offset) return prevFindResult; - prevFindResult = findResult; - start = findResult.getEndOffset(); - continue; - } + } + else { + Language tokenLang = tokenType.getLanguage(); + if (tokenLang != lang && tokenLang != Language.ANY && !data.relevantLanguages.contains(tokenLang)) { + tokens = addTokenTypesForLanguage(model, tokenLang, tokens); + data.tokensOfInterest = tokens; + data.relevantLanguages.add(tokenLang); } - break; - } - } else { - Language tokenLang = tokenType.getLanguage(); - if (tokenLang != lang && tokenLang != Language.ANY && !data.relevantLanguages.contains(tokenLang)) { - tokens = addTokenTypesForLanguage(model, tokenLang, tokens); - data.tokensOfInterest = tokens; - data.relevantLanguages.add(tokenLang); } + + lexer.advance(); } - lexer.advance(); + return prevFindResult; } - - return prevFindResult; } private static TokenSet addTokenTypesForLanguage(FindModel model, Language lang, TokenSet tokensOfInterest) { From df8fa93a897db7e9e1151857d7f9b0351660b352 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 24 Jun 2016 22:40:40 +0200 Subject: [PATCH 2/6] stub hierarchy: keep qualified names only in package symbols --- .../stubsHierarchy/impl/NameEnvironment.java | 12 ++----- .../psi/stubsHierarchy/impl/StubEnter.java | 32 +++++++++++-------- .../impl/StubHierarchyConnector.java | 9 ++++-- .../psi/stubsHierarchy/impl/Symbol.java | 28 +++++++--------- .../psi/stubsHierarchy/impl/Symbols.java | 22 +++++++------ .../impl/java/stubs/hierarchy/IndexTree.java | 2 +- 6 files changed, 53 insertions(+), 52 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NameEnvironment.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NameEnvironment.java index e97f18ff7bd4..0d5e4144ff5c 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NameEnvironment.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NameEnvironment.java @@ -22,8 +22,9 @@ import org.jetbrains.annotations.Nullable; import java.util.Arrays; public class NameEnvironment extends UserDataHolderBase { + public static final int OBJECT_NAME = IndexTree.hashIdentifier("Object"); public final QualifiedName empty; - public final QualifiedName java_lang_Object; + public final QualifiedName java_lang; public final QualifiedName java_lang_Enum; public final QualifiedName[] annotation; public final NamesEnumerator myNamesEnumerator; @@ -31,7 +32,7 @@ public class NameEnvironment extends UserDataHolderBase { public NameEnvironment() { myNamesEnumerator = new NamesEnumerator(); empty = myNamesEnumerator.getFullName(new int[]{}, true); - java_lang_Object = fromString("java.lang.Object", true); + java_lang = fromString("java.lang", true); java_lang_Enum = fromString("java.lang.Enum", true); annotation = new QualifiedName[]{fromString("java.lang.annotation.Annotation", true)}; } @@ -57,13 +58,6 @@ public class NameEnvironment extends UserDataHolderBase { return ids[ids.length - 1]; } - public QualifiedName qualifiedName(Symbol owner, int shortName) { - if (shortName == NamesEnumerator.NO_NAME || owner == null || owner.myQualifiedName == null) { - return null; - } - return qualifiedName(owner.myQualifiedName, shortName, true); - } - public QualifiedName qualifiedName(QualifiedName prefix, int shortName, boolean create) { if (shortName == NamesEnumerator.NO_NAME) return null; diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java index 77dab13e8492..144a87d863e5 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java @@ -17,6 +17,7 @@ package com.intellij.psi.stubsHierarchy.impl; import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; import com.intellij.psi.stubsHierarchy.stubs.*; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; @@ -38,20 +39,20 @@ public class StubEnter { void unitEnter(Unit tree) { PackageSymbol pkg = tree.myPackageId != null ? mySymbols.enterPackage(tree.myPackageId) : mySymbols.myRootPackage; - enter(tree.myClasses, tree.myUnitInfo, pkg); + enter(tree.myClasses, tree.myUnitInfo, pkg, pkg.myQualifiedName); } - private void enter(ClassDeclaration[] trees, UnitInfo info, Symbol owner) { + private void enter(ClassDeclaration[] trees, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName) { for (ClassDeclaration tree : trees) { - enter(tree, info, owner); + enter(tree, info, owner, ownerName); } } - private ClassSymbol[] enter(Declaration[] trees, UnitInfo info, Symbol owner) { + private ClassSymbol[] enter(Declaration[] trees, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName) { ClassSymbol[] members = new ClassSymbol[trees.length]; int i = 0; for (Declaration tree : trees) { - ClassSymbol member = enter(tree, info, owner); + ClassSymbol member = enter(tree, info, owner, ownerName); if (member != null && member.myShortName != 0) { members[i++] = member; } @@ -61,24 +62,24 @@ public class StubEnter { return members; } - private ClassSymbol enter(Declaration tree, UnitInfo info, Symbol owner) { + private ClassSymbol enter(Declaration tree, UnitInfo info, Symbol owner, QualifiedName ownerName) { if (tree instanceof ClassDeclaration) { - return classEnter((ClassDeclaration)tree, info, owner); + return classEnter((ClassDeclaration)tree, info, owner, ownerName); } if (tree instanceof MemberDeclaration) { - memberEnter((MemberDeclaration)tree, info, owner); + memberEnter((MemberDeclaration)tree, info, owner, ownerName); return null; } return null; } - private void memberEnter(MemberDeclaration tree, UnitInfo info, Symbol owner) { + private void memberEnter(MemberDeclaration tree, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName) { MemberSymbol mc = new MemberSymbol(owner); - ClassSymbol[] members = enter(tree.myDeclarations, info, mc); + ClassSymbol[] members = enter(tree.myDeclarations, info, mc, ownerName); mc.setMembers(members); } - private ClassSymbol classEnter(ClassDeclaration tree, UnitInfo info, Symbol owner) { + private ClassSymbol classEnter(ClassDeclaration tree, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName) { int flags = checkFlags(tree.mods, owner); if (info.getType() == IndexTree.BYTECODE) { flags |= IndexTree.COMPILED; @@ -88,12 +89,15 @@ public class StubEnter { supers = myNameEnvironment.annotation; } - ClassSymbol classSymbol = mySymbols.enterClass(tree.myClassAnchor, flags, tree.myName, owner, info, supers); + int name = tree.myName; + QualifiedName qname = name == NamesEnumerator.NO_NAME || ownerName == null ? null + : myNameEnvironment.qualifiedName(ownerName, name, true); + ClassSymbol classSymbol = mySymbols.enterClass(tree.myClassAnchor, flags, name, owner, info, supers, qname); - if (uncompleted != null) { + if (uncompleted != null) { uncompleted.add(classSymbol); } - ClassSymbol[] members = enter(tree.myDeclarations, info, classSymbol); + ClassSymbol[] members = enter(tree.myDeclarations, info, classSymbol, qname); classSymbol.setMembers(members); return classSymbol; } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java index f1c3e41117ce..395569fee488 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java @@ -54,12 +54,12 @@ public class StubHierarchyConnector { } } - if (c.myQualifiedName == myNameEnvironment.java_lang_Object || c.isHierarchyIncomplete()) { + if (isJavaLangObject(c) || c.isHierarchyIncomplete()) { c.mySuperClasses = Symbol.ClassSymbol.EMPTY_ARRAY; } else { for (Iterator iter = supertypes.iterator(); iter.hasNext();) { Symbol s = iter.next(); - if (!(s instanceof Symbol.ClassSymbol) || s.myQualifiedName == myNameEnvironment.java_lang_Object) { + if (!(s instanceof Symbol.ClassSymbol) || isJavaLangObject(s)) { iter.remove(); } } @@ -77,4 +77,9 @@ public class StubHierarchyConnector { c.myUnitInfo = null; } + private boolean isJavaLangObject(Symbol s) { + return s.myShortName == NameEnvironment.OBJECT_NAME && + s.myOwner instanceof Symbol.PackageSymbol && + ((Symbol.PackageSymbol)s.myOwner).myQualifiedName == myNameEnvironment.java_lang; + } } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java index 4ffcbed374fe..324e9b8cf392 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java @@ -30,13 +30,11 @@ import java.util.Comparator; public abstract class Symbol { public int myFlags; public int myShortName; - public final QualifiedName myQualifiedName; public final Symbol myOwner; - public Symbol(int flags, Symbol owner, QualifiedName qualifiedName, int name) { + public Symbol(int flags, Symbol owner, int name) { this.myFlags = flags; this.myOwner = owner; - this.myQualifiedName = qualifiedName; this.myShortName = name; } @@ -49,9 +47,6 @@ public abstract class Symbol { return ClassSymbol.EMPTY_ARRAY; } - public void setMembers(ClassSymbol[] members) { - } - public boolean isStatic() { return BitUtil.isSet(myFlags, IndexTree.STATIC); } @@ -77,9 +72,11 @@ public abstract class Symbol { } public static class PackageSymbol extends Symbol { + final QualifiedName myQualifiedName; + public PackageSymbol(Symbol owner, QualifiedName fullname, int name) { - super(IndexTree.PACKAGE, owner, fullname, name); - setMembers(ClassSymbol.EMPTY_ARRAY); + super(IndexTree.PACKAGE, owner, name); + myQualifiedName = fullname; } } @@ -97,13 +94,12 @@ public abstract class Symbol { private ClassSymbol[] myMembers; ClassSymbol(StubClassAnchor classAnchor, - int flags, - Symbol owner, - QualifiedName fullname, - int name, - UnitInfo unitInfo, - QualifiedName[] supers) { - super(flags | IndexTree.CLASS, owner, fullname, name); + int flags, + Symbol owner, + int name, + UnitInfo unitInfo, + QualifiedName[] supers) { + super(flags | IndexTree.CLASS, owner, name); this.myClassAnchor = classAnchor; this.mySuperNames = supers; this.myUnitInfo = unitInfo; @@ -184,7 +180,7 @@ public abstract class Symbol { public static class MemberSymbol extends Symbol { private ClassSymbol[] myMembers; public MemberSymbol(Symbol owner) { - super(IndexTree.MEMBER, owner, null, NamesEnumerator.NO_NAME); + super(IndexTree.MEMBER, owner, NamesEnumerator.NO_NAME); } public ClassSymbol[] members() { return myMembers; diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbols.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbols.java index 13017b18ab46..5b026932de72 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbols.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbols.java @@ -63,21 +63,23 @@ public class Symbols { return (ClassSymbol[])cs; } - public ClassSymbol enterClass(ClassAnchor classAnchor, int flags, int shortName, Symbol owner, UnitInfo info, QualifiedName[] supers) { - QualifiedName qualifiedName = myNameEnvironment.qualifiedName(owner, shortName); + public ClassSymbol enterClass(ClassAnchor classAnchor, + int flags, + int shortName, + Symbol owner, + UnitInfo info, + QualifiedName[] supers, + @Nullable QualifiedName qualifiedName) { StubClassAnchor stubClassAnchor = new StubClassAnchor(myClassSymbols.size(), classAnchor); - ClassSymbol c = new ClassSymbol(stubClassAnchor, flags, owner, qualifiedName, shortName, info, supers); + ClassSymbol c = new ClassSymbol(stubClassAnchor, flags, owner, shortName, info, supers); myClassSymbols.add(c); - putClassByName(c); + if (qualifiedName != null) { + putClassByName(c, qualifiedName.myId); + } return c; } - private void putClassByName(ClassSymbol classSymbol) { - QualifiedName name = classSymbol.myQualifiedName; - // anonymous class - if (name == null) - return; - int nameId = name.myId; + private void putClassByName(ClassSymbol classSymbol, int nameId) { ensureByNameCapacity(nameId); Object cs = myClassSymbolsByNameId[nameId]; if (cs == null) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/hierarchy/IndexTree.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/hierarchy/IndexTree.java index b7a148b21be1..7ec276f72e56 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/hierarchy/IndexTree.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/hierarchy/IndexTree.java @@ -43,7 +43,7 @@ public class IndexTree { public static final byte JAVA = 1; public static final byte GROOVY = 2; - private static int hashIdentifier(@Nullable String s) { + public static int hashIdentifier(@Nullable String s) { if (s == null) return 0; // not using String.hashCode because this way there's less collisions for short package names like 'com' From d30e4bcedaa10db0db16d0d3948322aec75c1c88 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 24 Jun 2016 22:59:36 +0200 Subject: [PATCH 3/6] stub hierarchy: mark binary classes also incomplete when unable to resolve their supers --- .../impl/StubHierarchyConnector.java | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java index 395569fee488..9ae2fa34c197 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java @@ -29,6 +29,23 @@ public class StubHierarchyConnector { myResolve = new StubResolver(symbols, this); } + private void resolveName(Symbol.ClassSymbol place, QualifiedName name, Set result) throws IncompleteHierarchyException { + if (place.isCompiled()) { + Symbol.ClassSymbol[] candidates = myResolve.findGlobalType(name); + if (candidates.length == 0) { + throw new IncompleteHierarchyException(); + } + + Collections.addAll(result, candidates); + } else { + for (Symbol symbol : myResolve.resolveBase(place, name.myComponents)) { + if (symbol instanceof Symbol.ClassSymbol) { + result.add((Symbol.ClassSymbol)symbol); + } + } + } + } + void connect(Symbol sym) { Symbol.ClassSymbol c = (Symbol.ClassSymbol) sym; @@ -37,39 +54,28 @@ public class StubHierarchyConnector { } // Determine supertype. - Set supertypes = new HashSet(); + Set supertypes = new HashSet<>(); for (QualifiedName name : c.mySuperNames) { - if (c.isCompiled()) { - if (name != null) { - Collections.addAll(supertypes, myResolve.findGlobalType(name)); - } - } else { - try { - supertypes.addAll(myResolve.resolveBase(c, name.myComponents)); - } - catch (IncompleteHierarchyException ignore) { - c.markHierarchyIncomplete(); - break; - } + try { + resolveName(c, name, supertypes); + } + catch (IncompleteHierarchyException ignore) { + c.markHierarchyIncomplete(); + return; } } if (isJavaLangObject(c) || c.isHierarchyIncomplete()) { c.mySuperClasses = Symbol.ClassSymbol.EMPTY_ARRAY; } else { - for (Iterator iter = supertypes.iterator(); iter.hasNext();) { - Symbol s = iter.next(); - if (!(s instanceof Symbol.ClassSymbol) || isJavaLangObject(s)) { + for (Iterator iter = supertypes.iterator(); iter.hasNext();) { + Symbol.ClassSymbol s = iter.next(); + if (isJavaLangObject(s)) { iter.remove(); } } - if (supertypes.isEmpty()) { - c.mySuperClasses = Symbol.ClassSymbol.EMPTY_ARRAY; - } - else { - //noinspection SuspiciousToArrayCall - c.mySuperClasses = supertypes.toArray(new Symbol.ClassSymbol[supertypes.size()]); - } + c.mySuperClasses = + supertypes.isEmpty() ? Symbol.ClassSymbol.EMPTY_ARRAY : supertypes.toArray(new Symbol.ClassSymbol[supertypes.size()]); } // cleaning up From 602e2e08f75e990238ba4e0e4862bff13fbc30f3 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 25 Jun 2016 12:05:49 +0200 Subject: [PATCH 4/6] stub hierarchy: store supers in a more compact form --- .../impl/StubHierarchyConnector.java | 43 ++++++++++--------- .../psi/stubsHierarchy/impl/Symbol.java | 28 +++++++++--- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java index 9ae2fa34c197..597ab6ab929c 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java @@ -53,34 +53,35 @@ public class StubHierarchyConnector { ((Symbol.ClassSymbol)c.myOwner).connect(this); } + Object supers = c.mySuperClasses; + if (supers == null) { + c.setSupers(Collections.emptySet()); + return; + } + // Determine supertype. Set supertypes = new HashSet<>(); - for (QualifiedName name : c.mySuperNames) { - try { - resolveName(c, name, supertypes); - } - catch (IncompleteHierarchyException ignore) { - c.markHierarchyIncomplete(); - return; - } - } - if (isJavaLangObject(c) || c.isHierarchyIncomplete()) { - c.mySuperClasses = Symbol.ClassSymbol.EMPTY_ARRAY; - } else { - for (Iterator iter = supertypes.iterator(); iter.hasNext();) { - Symbol.ClassSymbol s = iter.next(); - if (isJavaLangObject(s)) { - iter.remove(); + try { + if (supers instanceof QualifiedName[]) { + for (QualifiedName name : (QualifiedName[])supers) { + resolveName(c, name, supertypes); } + } else { + resolveName(c, (QualifiedName)supers, supertypes); } - c.mySuperClasses = - supertypes.isEmpty() ? Symbol.ClassSymbol.EMPTY_ARRAY : supertypes.toArray(new Symbol.ClassSymbol[supertypes.size()]); + } + catch (IncompleteHierarchyException ignore) { + c.markHierarchyIncomplete(); + return; } - // cleaning up - c.mySuperNames = null; - c.myUnitInfo = null; + for (Iterator iterator = supertypes.iterator(); iterator.hasNext();) { + if (isJavaLangObject(iterator.next())) { + iterator.remove(); + } + } + c.setSupers(supertypes); } private boolean isJavaLangObject(Symbol s) { diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java index 324e9b8cf392..731853bee307 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java @@ -21,7 +21,9 @@ import com.intellij.util.BitUtil; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; +import java.util.Collections; import java.util.Comparator; +import java.util.Set; /** * Java symbols needed for hierarchy building. Mostly classes ({@link ClassSymbol}) or packages ({@link PackageSymbol}), @@ -88,9 +90,14 @@ public abstract class Symbol { public static final ClassSymbol[] EMPTY_ARRAY = new ClassSymbol[0]; final StubClassAnchor myClassAnchor; - ClassSymbol[] mySuperClasses; + + /** + * null for empty 'supers' list + * ClassSymbol/QualifiedName for a single resolved/unresolved super + * ClassSymbol[]/QualifiedName[] for multiple resolved/unresolved supers + */ + Object mySuperClasses; UnitInfo myUnitInfo; - QualifiedName[] mySuperNames; private ClassSymbol[] myMembers; ClassSymbol(StubClassAnchor classAnchor, @@ -101,7 +108,7 @@ public abstract class Symbol { QualifiedName[] supers) { super(flags | IndexTree.CLASS, owner, name); this.myClassAnchor = classAnchor; - this.mySuperNames = supers; + this.mySuperClasses = supers.length == 0 ? null : supers.length == 1 ? supers[0] : supers; this.myUnitInfo = unitInfo; } @@ -133,7 +140,9 @@ public abstract class Symbol { @NotNull ClassSymbol[] rawSuperClasses() { assert isConnectStarted(); - return mySuperClasses == null ? EMPTY_ARRAY : mySuperClasses; + return mySuperClasses instanceof ClassSymbol ? new ClassSymbol[]{(ClassSymbol)mySuperClasses} : + mySuperClasses instanceof ClassSymbol[] ? (ClassSymbol[])mySuperClasses : + EMPTY_ARRAY; } boolean isCompiled() { @@ -149,12 +158,17 @@ public abstract class Symbol { } void markHierarchyIncomplete() { - mySuperClasses = EMPTY_ARRAY; - mySuperNames = null; - myUnitInfo = null; + setSupers(Collections.emptySet()); myFlags = BitUtil.set(myFlags, HIERARCHY_INCOMPLETE, true); } + void setSupers(Set supers) { + mySuperClasses = supers.isEmpty() ? null : + supers.size() == 1 ? supers.iterator().next() : + supers.toArray(new ClassSymbol[supers.size()]); + myUnitInfo = null; + } + boolean isHierarchyIncomplete() { return BitUtil.isSet(myFlags, HIERARCHY_INCOMPLETE); } From bbadd9d7dff7fb1568e7e3469438c703cb8ae7fa Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 25 Jun 2016 12:47:50 +0200 Subject: [PATCH 5/6] stub hierarchy: use less memory for storing nested classes --- .../psi/stubsHierarchy/impl/StubEnter.java | 11 ++++-- .../psi/stubsHierarchy/impl/StubResolver.java | 36 +++--------------- .../psi/stubsHierarchy/impl/Symbol.java | 37 ++++++++++--------- 3 files changed, 34 insertions(+), 50 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java index 144a87d863e5..5d1b7d94904b 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java @@ -57,7 +57,11 @@ public class StubEnter { members[i++] = member; } } - members = members.length == 0 ? ClassSymbol.EMPTY_ARRAY : Arrays.copyOf(members, i); + if (i == 0) return ClassSymbol.EMPTY_ARRAY; + + if (i < members.length) { + members = Arrays.copyOf(members, i); + } Arrays.sort(members, CLASS_SYMBOL_BY_NAME_COMPARATOR); return members; } @@ -97,8 +101,9 @@ public class StubEnter { if (uncompleted != null) { uncompleted.add(classSymbol); } - ClassSymbol[] members = enter(tree.myDeclarations, info, classSymbol, qname); - classSymbol.setMembers(members); + if (tree.myDeclarations.length > 0) { + classSymbol.setMembers(enter(tree.myDeclarations, info, classSymbol, qname)); + } return classSymbol; } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubResolver.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubResolver.java index d34cedbe152f..6ebfce8b5f0b 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubResolver.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubResolver.java @@ -103,35 +103,11 @@ public class StubResolver { if (!processed.add(s)) { return; } - findImmediateMemberType(s, name, symbols); + processMembers(s.getMembers(), name, symbols, false); if (s.isClass()) findInheritedMemberType((Symbol.ClassSymbol)s, name, symbols, processed); } - private static void findImmediateMemberType(Symbol s, int name, Set symbols) { - Symbol.ClassSymbol[] members = s.members(); - int index = getIndex(name, members); - if (index < 0) return; - - // elem - Symbol.ClassSymbol member = members[index]; - symbols.add(member); - // on the left - int i = index - 1; - while (i >= 0 && members[i].myShortName == name) { - member = members[i]; - symbols.add(member); - i--; - } - // on the right - i = index + 1; - while (i < members.length && members[i].myShortName == name) { - member = members[i]; - symbols.add(member); - i++; - } - } - private void findInheritedMemberType(Symbol.ClassSymbol c, int name, Set symbols, Set processed) throws IncompleteHierarchyException { for (Symbol.ClassSymbol st : c.getSuperClasses(myConnector)) @@ -204,25 +180,25 @@ public class StubResolver { return; for (Symbol.ClassSymbol c : cs.getSuperClasses(myConnector)) importFrom(c); - importMember(cs.members(), name, symbols, true); + processMembers(cs.getMembers(), name, symbols, true); } }.importFrom(tsym); } - private static void importMember(Symbol.ClassSymbol[] members, int name, Set symbols, boolean isStatic) { + private static void processMembers(Symbol.ClassSymbol[] members, int name, Set symbols, boolean requireStatic) { int index = getIndex(name, members); if (index < 0) return; // elem Symbol.ClassSymbol member = members[index]; - if (!isStatic || member.isStatic()) { + if (!requireStatic || member.isStatic()) { symbols.add(member); } // on the left int i = index - 1; while (i >= 0 && members[i].myShortName == name) { member = members[i]; - if (!isStatic || member.isStatic()) { + if (!requireStatic || member.isStatic()) { symbols.add(member); } i--; @@ -231,7 +207,7 @@ public class StubResolver { i = index + 1; while (i < members.length && members[i].myShortName == name) { member = members[i]; - if (!isStatic || member.isStatic()) { + if (!requireStatic || member.isStatic()) { symbols.add(member); } i++; diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java index 731853bee307..4eb2f6f81d5e 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java @@ -45,7 +45,7 @@ public abstract class Symbol { return myShortName; } - public ClassSymbol[] members() { + ClassSymbol[] getMembers() { return ClassSymbol.EMPTY_ARRAY; } @@ -84,7 +84,7 @@ public abstract class Symbol { /** A class for class symbols */ - public static class ClassSymbol extends Symbol { + public static class ClassSymbol extends MemberSymbol { private static final int HIERARCHY_INCOMPLETE = 1 << 20; private static final int CONNECT_STARTED = 1 << 21; public static final ClassSymbol[] EMPTY_ARRAY = new ClassSymbol[0]; @@ -98,7 +98,6 @@ public abstract class Symbol { */ Object mySuperClasses; UnitInfo myUnitInfo; - private ClassSymbol[] myMembers; ClassSymbol(StubClassAnchor classAnchor, int flags, @@ -149,14 +148,6 @@ public abstract class Symbol { return BitUtil.isSet(myFlags, IndexTree.COMPILED); } - public ClassSymbol[] members() { - return myMembers; - } - - public void setMembers(ClassSymbol[] members) { - this.myMembers = members; - } - void markHierarchyIncomplete() { setSupers(Collections.emptySet()); myFlags = BitUtil.set(myFlags, HIERARCHY_INCOMPLETE, true); @@ -192,15 +183,27 @@ public abstract class Symbol { * Represents methods, fields and other constructs that may contain anonymous or local classes. */ public static class MemberSymbol extends Symbol { - private ClassSymbol[] myMembers; - public MemberSymbol(Symbol owner) { + /** + * null when no members, or a single ClassSymbol, or ClassSymbol[] + */ + private Object myMembers = null; + + MemberSymbol(Symbol owner) { super(IndexTree.MEMBER, owner, NamesEnumerator.NO_NAME); } - public ClassSymbol[] members() { - return myMembers; + + MemberSymbol(int flags, Symbol owner, int name) { + super(flags, owner, name); } - public void setMembers(ClassSymbol[] members) { - this.myMembers = members; + + ClassSymbol[] getMembers() { + return myMembers == null ? ClassSymbol.EMPTY_ARRAY : + myMembers instanceof ClassSymbol ? new ClassSymbol[]{(ClassSymbol)myMembers} : + (ClassSymbol[])myMembers; + } + + void setMembers(ClassSymbol[] members) { + myMembers = members.length == 0 ? null : members.length == 1 ? members[0] : members; } } From d5c21fffb4b8dbc72353abab295e0ed82b42357d Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 25 Jun 2016 13:56:48 +0200 Subject: [PATCH 6/6] avoid accidental boxing during stub hierarchy creation --- .../intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java index 88127f0ebf63..0c10c1ff6425 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java @@ -176,7 +176,7 @@ public class SingleClassHierarchy extends ClassHierarchy { // compacting result = Arrays.copyOf(result, i); - Arrays.sort(result, Comparator.comparing(a -> a.myFileId)); + Arrays.sort(result, (a1, a2) -> Integer.compare(a1.myFileId, a2.myFileId)); return result; }