From da07daf5cedc7532803c78ec56cade5e61ad903d Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 5 Jul 2016 08:57:06 +0200 Subject: [PATCH] stub hierarchy: use less memory when reading from index --- .../impl/HierarchyServiceImpl.java | 6 +- .../psi/stubsHierarchy/impl/Imports.java | 50 +++- .../stubsHierarchy/impl/NameEnvironment.java | 21 ++ .../stubsHierarchy/impl/SerializedUnit.java | 217 ++++++++++++++++++ .../psi/stubsHierarchy/impl/StubEnter.java | 93 ++------ .../impl/StubHierarchyIndex.java | 35 ++- .../psi/stubsHierarchy/impl/Symbol.java | 16 +- .../java/stubs/index/JavaUnitDescriptor.java | 168 -------------- 8 files changed, 346 insertions(+), 260 deletions(-) create mode 100644 java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SerializedUnit.java delete mode 100644 java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaUnitDescriptor.java diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/HierarchyServiceImpl.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/HierarchyServiceImpl.java index 50e99120097c..d0d171d7fd2a 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/HierarchyServiceImpl.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/HierarchyServiceImpl.java @@ -79,14 +79,14 @@ public class HierarchyServiceImpl extends HierarchyService { } private void loadUnits(BitSet files, int[] indexKeys, StubEnter stubEnter) { - FileBasedIndexImpl.IdValueProcessor processor = new FileBasedIndexImpl.IdValueProcessor() { + FileBasedIndexImpl.IdValueProcessor processor = new FileBasedIndexImpl.IdValueProcessor() { final ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); int count = 0; @Override - public boolean process(int fileId, IndexTree.Unit unit) { + public boolean process(int fileId, SerializedUnit unit) { if (indicator != null && ++count % 128 == 0) indicator.checkCanceled(); if (files.get(fileId)) { - stubEnter.unitEnter(unit, fileId); + unit.readUnit(stubEnter, fileId); } return true; } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Imports.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Imports.java index 2b1ece3f88a8..baded25636ca 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Imports.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Imports.java @@ -15,10 +15,16 @@ */ package com.intellij.psi.stubsHierarchy.impl; +import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; import com.intellij.util.ArrayUtil; import com.intellij.util.BitUtil; +import com.intellij.util.io.DataInputOutputUtil; +import org.jetbrains.annotations.NotNull; -public class Imports { +import java.io.DataOutput; +import java.io.IOException; + +class Imports { public final static long[] EMPTY_ARRAY = ArrayUtil.EMPTY_LONG_ARRAY; public static final int onDemandMask = 1 << 29; @@ -50,4 +56,46 @@ public class Imports { if (onDemand) lower |= onDemandMask; return (((long)alias) << 32) | lower; } + + private final static int IS_STATIC = 1; + private final static int IS_ON_DEMAND = 2; + private final static int HAS_ALIAS = 4; + + static void writeImports(@NotNull DataOutput out, IndexTree.Unit value) throws IOException { + DataInputOutputUtil.writeINT(out, value.imports.length); + for (IndexTree.Import anImport : value.imports) { + writeImport(out, anImport); + } + } + + static long[] readImports(UnitInputStream in) throws IOException { + int importCount = DataInputOutputUtil.readINT(in); + long[] imports = importCount == 0 ? EMPTY_ARRAY : new long[importCount]; + for (int i = 0; i < importCount; i++) { + imports[i] = readImport(in); + } + return imports; + } + + private static void writeImport(@NotNull DataOutput out, IndexTree.Import anImport) throws IOException { + SerializedUnit.writeQualifiedName(out, anImport.myFullname); + boolean hasAlias = anImport.myAlias != 0; + int flags = 0; + flags = BitUtil.set(flags, IS_STATIC, anImport.myStaticImport); + flags = BitUtil.set(flags, IS_ON_DEMAND, anImport.myOnDemand); + flags = BitUtil.set(flags, HAS_ALIAS, hasAlias); + out.writeByte(flags); + if (hasAlias) { + out.writeInt(anImport.myAlias); + } + } + + private static long readImport(UnitInputStream in) throws IOException { + int fullname = in.names.readQualifiedName(in); + int flags = in.readByte(); + return mkImport(fullname, + BitUtil.isSet(flags, IS_STATIC), BitUtil.isSet(flags, IS_ON_DEMAND), + BitUtil.isSet(flags, HAS_ALIAS) ? in.readInt() : 0); + } + } 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 2c9db64a54c9..048d6fbc9682 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 @@ -18,9 +18,14 @@ package com.intellij.psi.stubsHierarchy.impl; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.psi.CommonClassNames; import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; +import com.intellij.util.io.DataInputOutputUtil; import gnu.trove.TIntArrayList; import gnu.trove.TLongIntHashMap; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + class NameEnvironment extends UserDataHolderBase { public static final int OBJECT_NAME = IndexTree.hashIdentifier("Object"); public static final int NO_NAME = 0; @@ -67,6 +72,22 @@ class NameEnvironment extends UserDataHolderBase { return id; } + /** + * @see SerializedUnit#writeQualifiedName(DataOutput, int[]) + */ + @QNameId int readQualifiedName(DataInput in) throws IOException { + int id = 0; + int len = DataInputOutputUtil.readINT(in); + for (int i = 0; i < len; i++) { + id = qualifiedName(id, in.readInt()); + } + return id; + } + + int memberQualifiedName(@QNameId int ownerName, @ShortName int name) { + return name == NO_NAME || ownerName < 0 ? -1 : qualifiedName(ownerName, name); + } + @QNameId int qualifiedName(@QNameId int prefix, @ShortName int shortName) { int existing = findExistingName(prefix, shortName); return existing >= 0 ? existing : addName(prefix, shortName); diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SerializedUnit.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SerializedUnit.java new file mode 100644 index 000000000000..492f30a8fe03 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SerializedUnit.java @@ -0,0 +1,217 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.stubsHierarchy.impl; + +import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; +import com.intellij.psi.stubsHierarchy.impl.Symbol.ClassSymbol; +import com.intellij.psi.stubsHierarchy.impl.Symbol.MemberSymbol; +import com.intellij.psi.stubsHierarchy.impl.Symbol.PackageSymbol; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.io.DataInputOutputUtil; +import com.intellij.util.io.DataOutputStream; +import com.intellij.util.io.UnsyncByteArrayInputStream; +import com.intellij.util.io.UnsyncByteArrayOutputStream; +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author peter + */ +class SerializedUnit { + private final byte[] myBytes; + + SerializedUnit(byte[] bytes) { + myBytes = bytes; + } + + SerializedUnit(IndexTree.Unit unit) { + try { + //noinspection IOResourceOpenedButNotSafelyClosed + UnsyncByteArrayOutputStream stream = new UnsyncByteArrayOutputStream(); + writeUnit(new DataOutputStream(stream), unit); + myBytes = stream.toByteArray(); + } + catch (IOException impossible) { + throw new RuntimeException(impossible); + } + } + + byte[] getSerializedBytes() { + return myBytes; + } + + void readUnit(StubEnter stubEnter, int fileId) { + try { + enterUnit(new UnitInputStream(new UnsyncByteArrayInputStream(myBytes), fileId, stubEnter)); + } + catch (IOException impossible) { + throw new RuntimeException(impossible); + } + } + + /** + * @see NameEnvironment#readQualifiedName(DataInput) + */ + static void writeQualifiedName(DataOutput out, @QNameId int[] array) throws IOException { + DataInputOutputUtil.writeINT(out, array.length); + for (int i : array) { + out.writeInt(i); + } + } + + // unit + + private static void writeUnit(@NotNull DataOutput out, IndexTree.Unit value) throws IOException { + writeQualifiedName(out, value.myPackageName); + out.writeByte(value.myUnitType); + if (value.myUnitType != IndexTree.BYTECODE) { + Imports.writeImports(out, value); + } + // class Declaration + DataInputOutputUtil.writeINT(out, value.myDecls.length); + for (IndexTree.ClassDecl def : value.myDecls) { + saveClassDecl(out, def); + } + } + + private static void enterUnit(UnitInputStream in) throws IOException { + PackageSymbol pkg = in.stubEnter.enterPackage(in); + byte type = in.readByte(); + long[] imports = type == IndexTree.BYTECODE ? Imports.EMPTY_ARRAY : Imports.readImports(in); + UnitInfo unitInfo = UnitInfo.mkUnitInfo(type, imports); + + int classCount = DataInputOutputUtil.readINT(in); + for (int i = 0; i < classCount; i++) { + readClassDecl(in, unitInfo, pkg, pkg.myQualifiedName); + } + } + + // class + + private static void saveClassDecl(@NotNull DataOutput out, IndexTree.ClassDecl value) throws IOException { + DataInputOutputUtil.writeINT(out, value.myStubId); + DataInputOutputUtil.writeINT(out, value.myMods); + out.writeInt(value.myName); + writeSupers(out, value); + writeMembers(out, value.myDecls); + } + + private static ClassSymbol readClassDecl(UnitInputStream in, UnitInfo info, Symbol owner, @QNameId int ownerName) throws IOException { + int stubId = DataInputOutputUtil.readINT(in); + int mods = DataInputOutputUtil.readINT(in); + @ShortName int name = in.readInt(); + @QNameId int[] superNames = readSupers(in); + + @QNameId int qname = in.names.memberQualifiedName(ownerName, name); + ClassSymbol symbol = in.stubEnter.classEnter(info, owner, stubId, mods, name, superNames, qname, in.fileId); + + readMembers(in, info, qname, symbol); + return symbol; + } + + // supers + + private static void writeSupers(@NotNull DataOutput out, IndexTree.ClassDecl value) throws IOException { + DataInputOutputUtil.writeINT(out, value.mySupers.length); + for (int[] aSuper : value.mySupers) { + writeQualifiedName(out, aSuper); + } + } + + private static @QNameId int[] readSupers(UnitInputStream in) throws IOException { + @QNameId int[] superNames = new int[DataInputOutputUtil.readINT(in)]; + for (int i = 0; i < superNames.length; i++) { + superNames[i] = in.names.readQualifiedName(in); + } + return superNames; + } + + // members + + private static void writeMembers(@NotNull DataOutput out, IndexTree.Decl[] decls) throws IOException { + DataInputOutputUtil.writeINT(out, decls.length); + for (IndexTree.Decl def : decls) { + saveDecl(out, def); + } + } + + private static void readMembers(UnitInputStream in, + UnitInfo info, + @QNameId int ownerName, + MemberSymbol symbol) throws IOException { + int memberCount = DataInputOutputUtil.readINT(in); + if (memberCount == 0) return; + + List members = new ArrayList<>(); + for (int i = 0; i < memberCount; i++) { + ContainerUtil.addIfNotNull(members, readDecl(in, info, symbol, ownerName)); + } + symbol.setMembers(members); + } + + // decl: class or member + + private static void saveDecl(@NotNull DataOutput out, IndexTree.Decl value) throws IOException { + if (value instanceof IndexTree.ClassDecl) { + out.writeBoolean(true); + saveClassDecl(out, (IndexTree.ClassDecl)value); + } else if (value instanceof IndexTree.MemberDecl) { + out.writeBoolean(false); + writeMembers(out, ((IndexTree.MemberDecl)value).myDecls); + } + } + + private static ClassSymbol readDecl(UnitInputStream in, UnitInfo info, Symbol owner, @QNameId int ownerName) throws IOException { + if (in.readBoolean()) { + return readClassDecl(in, info, owner, ownerName); + } + + readMembers(in, info, ownerName, new MemberSymbol(owner)); + return null; + } + + @Override + public boolean equals(Object o) { + return this == o || o instanceof SerializedUnit && Arrays.equals(myBytes, ((SerializedUnit)o).myBytes); + } + + @Override + public int hashCode() { + int result = myBytes.length; + int length = Math.min(30, myBytes.length); + for (int i = 0; i < length; i++) { + result = 31 * result + myBytes[i]; + } + return result; + } +} + +class UnitInputStream extends DataInputStream { + final int fileId; + final StubEnter stubEnter; + final NameEnvironment names; + + UnitInputStream(InputStream in, int fileId, StubEnter stubEnter) { + super(in); + this.fileId = fileId; + this.stubEnter = stubEnter; + this.names = stubEnter.myNameEnvironment; + } +} 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 dcfbe0373dc6..b8d72da404ec 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 @@ -19,13 +19,15 @@ import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; import com.intellij.util.BitUtil; import org.jetbrains.annotations.Nullable; +import java.io.DataInput; +import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; -import static com.intellij.psi.stubsHierarchy.impl.Symbol.*; +import static com.intellij.psi.stubsHierarchy.impl.Symbol.ClassSymbol; +import static com.intellij.psi.stubsHierarchy.impl.Symbol.PackageSymbol; public class StubEnter { - private final NameEnvironment myNameEnvironment; + final NameEnvironment myNameEnvironment; private final Symbols mySymbols; private final StubHierarchyConnector myStubHierarchyConnector; @@ -37,85 +39,30 @@ public class StubEnter { myStubHierarchyConnector = new StubHierarchyConnector(myNameEnvironment, symbols); } - void unitEnter(IndexTree.Unit unit, int fileId) { - @QNameId int pkgName = unit.myPackageName.length > 0 ? myNameEnvironment.internQualifiedName(unit.myPackageName) : 0; - enter(unit.myDecls, UnitInfo.mkUnitInfo(unit.myUnitType, internImports(unit)), mySymbols.enterPackage(pkgName), pkgName, fileId); + PackageSymbol enterPackage(DataInput in) throws IOException { + return mySymbols.enterPackage(myNameEnvironment.readQualifiedName(in)); } - private long[] internImports(IndexTree.Unit unit) { - long[] imports = unit.imports.length == 0 ? Imports.EMPTY_ARRAY : new long[unit.imports.length]; - for (int i = 0; i < unit.imports.length; i++) { - imports[i] = processImport(unit.imports[i]); - } - return imports; - } - - private long processImport(IndexTree.Import anImport) { - int fullname = myNameEnvironment.internQualifiedName(anImport.myFullname); - return Imports.mkImport(fullname, anImport.myStaticImport, anImport.myOnDemand, anImport.myAlias); - } - - private void enter(IndexTree.ClassDecl[] trees, UnitInfo info, Symbol owner, @QNameId int ownerName, int fileId) { - for (IndexTree.ClassDecl tree : trees) { - enter(tree, info, owner, ownerName, fileId); - } - } - - private ClassSymbol[] enter(IndexTree.Decl[] trees, UnitInfo info, Symbol owner, @QNameId int ownerName, int fileId) { - ClassSymbol[] members = new ClassSymbol[trees.length]; - int i = 0; - for (IndexTree.Decl tree : trees) { - ClassSymbol member = enter(tree, info, owner, ownerName, fileId); - if (member != null && member.myShortName != 0) { - members[i++] = member; - } - } - 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; - } - - private ClassSymbol enter(IndexTree.Decl tree, UnitInfo info, Symbol owner, @QNameId int ownerName, int fileId) { - if (tree instanceof IndexTree.ClassDecl) { - return classEnter((IndexTree.ClassDecl)tree, info, owner, ownerName, fileId); - } - if (tree instanceof IndexTree.MemberDecl) { - memberEnter((IndexTree.MemberDecl)tree, info, owner, ownerName, fileId); - return null; - } - return null; - } - - private void memberEnter(IndexTree.MemberDecl tree, UnitInfo info, Symbol owner, @QNameId int ownerName, int fileId) { - MemberSymbol mc = new MemberSymbol(owner); - mc.setMembers(enter(tree.myDecls, info, mc, ownerName, fileId)); - } - - private ClassSymbol classEnter(IndexTree.ClassDecl tree, UnitInfo info, Symbol owner, @QNameId int ownerName, int fileId) { - int flags = checkFlags(tree.myMods, owner, info.getType() == IndexTree.BYTECODE); - - int name = tree.myName; - @QNameId int qname = name == NameEnvironment.NO_NAME || ownerName < 0 ? -1 - : myNameEnvironment.qualifiedName(ownerName, name); - @CompactArray(QualifiedName.class) Object supers = internSupers(tree.myMods, tree.mySupers); - ClassSymbol classSymbol = mySymbols.enterClass(fileId, tree.myStubId, flags, name, owner, info, supers, qname); + ClassSymbol classEnter(UnitInfo info, + Symbol owner, + int stubId, + int mods, + @ShortName int name, + @QNameId int[] superNames, + @QNameId int qname, int fileId) throws IOException { + int flags = checkFlags(mods, owner, info.getType() == IndexTree.BYTECODE); + @CompactArray(QualifiedName.class) Object supers = internSupers(mods, superNames); + ClassSymbol classSymbol = mySymbols.enterClass(fileId, stubId, flags, name, owner, info, supers, qname); if (uncompleted != null) { uncompleted.add(classSymbol); } - if (tree.myDecls.length > 0) { - classSymbol.setMembers(enter(tree.myDecls, info, classSymbol, qname, fileId)); - } return classSymbol; } @Nullable @CompactArray(QualifiedName.class) - private Object internSupers(int flags, int[][] superNames) { + Object internSupers(int flags, int[] superNames) { if (BitUtil.isSet(flags, IndexTree.ANNOTATION)) { return myNameEnvironment.java_lang_annotation_Annotation; } @@ -125,12 +72,12 @@ public class StubEnter { return isEnum ? myNameEnvironment.java_lang_Enum : null; } if (superNames.length == 1 && !isEnum) { - return new QualifiedName(myNameEnvironment.internQualifiedName(superNames[0])); + return new QualifiedName(superNames[0]); } QualifiedName[] array = new QualifiedName[superNames.length + (isEnum ? 1 : 0)]; for (int i = 0; i < superNames.length; i++) { - array[i] = new QualifiedName(myNameEnvironment.internQualifiedName(superNames[i])); + array[i] = new QualifiedName(superNames[i]); } if (isEnum) { array[array.length - 1] = myNameEnvironment.java_lang_Enum; diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyIndex.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyIndex.java index 057b7160aa0c..2de47735eaf4 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyIndex.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyIndex.java @@ -21,14 +21,17 @@ import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileWithId; import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; -import com.intellij.psi.impl.java.stubs.index.JavaUnitDescriptor; import com.intellij.psi.stubsHierarchy.StubHierarchyIndexer; import com.intellij.util.indexing.*; import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.DataInputOutputUtil; import com.intellij.util.io.EnumeratorIntegerDescriptor; import com.intellij.util.io.KeyDescriptor; import org.jetbrains.annotations.NotNull; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.stream.IntStream; @@ -36,29 +39,29 @@ import java.util.stream.IntStream; /** * @author peter */ -public class StubHierarchyIndex extends FileBasedIndexExtension implements PsiDependentIndex { +public class StubHierarchyIndex extends FileBasedIndexExtension implements PsiDependentIndex { private static final int KEY_COUNT = 20; static final int[] BINARY_KEYS = IntStream.rangeClosed(1, KEY_COUNT).toArray(); static final int[] SOURCE_KEYS = IntStream.rangeClosed(-KEY_COUNT, -1).toArray(); - static final ID INDEX_ID = ID.create("jvm.hierarchy"); + static final ID INDEX_ID = ID.create("jvm.hierarchy"); private static final StubHierarchyIndexer[] ourIndexers = StubHierarchyIndexer.EP_NAME.getExtensions(); @NotNull @Override - public ID getName() { + public ID getName() { return INDEX_ID; } @NotNull @Override - public DataIndexer getIndexer() { + public DataIndexer getIndexer() { return inputData -> { for (StubHierarchyIndexer indexer : ourIndexers) { VirtualFile file = inputData.getFile(); IndexTree.Unit unit = indexer.handlesFile(file) ? indexer.indexFile(inputData) : null; if (unit != null && unit.myDecls.length > 0) { int[] keys = file.getFileType().isBinary() ? BINARY_KEYS : SOURCE_KEYS; - return Collections.singletonMap(keys[((VirtualFileWithId) file).getId() % keys.length], unit); + return Collections.singletonMap(keys[((VirtualFileWithId) file).getId() % keys.length], new SerializedUnit(unit)); } } return Collections.emptyMap(); @@ -73,13 +76,27 @@ public class StubHierarchyIndex extends FileBasedIndexExtension getValueExternalizer() { - return JavaUnitDescriptor.INSTANCE; + public DataExternalizer getValueExternalizer() { + return new DataExternalizer() { + @Override + public void save(@NotNull DataOutput out, SerializedUnit value) throws IOException { + byte[] bytes = value.getSerializedBytes(); + DataInputOutputUtil.writeINT(out, bytes.length); + out.write(bytes); + } + + @Override + public SerializedUnit read(@NotNull DataInput in) throws IOException { + byte[] bytes = new byte[DataInputOutputUtil.readINT(in)]; + in.readFully(bytes); + return new SerializedUnit(bytes); + } + }; } @Override public int getVersion() { - return IndexTree.STUB_HIERARCHY_ENABLED ? 5 + Arrays.stream(ourIndexers).mapToInt(StubHierarchyIndexer::getVersion).sum() : 0; + return IndexTree.STUB_HIERARCHY_ENABLED ? 6 + Arrays.stream(ourIndexers).mapToInt(StubHierarchyIndexer::getVersion).sum() : 0; } @NotNull 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 20f5bb1116c3..ed438237d753 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,9 +21,7 @@ import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collections; -import java.util.Comparator; -import java.util.Set; +import java.util.*; /** * Java symbols needed for hierarchy building. Mostly classes ({@link ClassSymbol}) or packages ({@link PackageSymbol}), @@ -200,12 +198,18 @@ public abstract class Symbol { (ClassSymbol[])myMembers; } - void setMembers(ClassSymbol[] members) { - myMembers = members.length == 0 ? null : members.length == 1 ? members[0] : members; + void setMembers(List members) { + myMembers = members.isEmpty() ? null : members.size() == 1 ? members.get(0) : toSortedArray(members); + } + + private static ClassSymbol[] toSortedArray(List members) { + ClassSymbol[] array = members.toArray(new ClassSymbol[members.size()]); + Arrays.sort(array, CLASS_SYMBOL_BY_NAME_COMPARATOR); + return array; } } - public static final Comparator CLASS_SYMBOL_BY_NAME_COMPARATOR = (s1, s2) -> { + private static final Comparator CLASS_SYMBOL_BY_NAME_COMPARATOR = (s1, s2) -> { int name1 = s1.myShortName; int name2 = s2.myShortName; return (name1 < name2) ? -1 : ((name1 == name2) ? 0 : 1); diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaUnitDescriptor.java b/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaUnitDescriptor.java deleted file mode 100644 index ad2fddadbe36..000000000000 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaUnitDescriptor.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.impl.java.stubs.index; - -import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; -import com.intellij.util.BitUtil; -import com.intellij.util.io.DataExternalizer; -import com.intellij.util.io.DataInputOutputUtil; -import org.jetbrains.annotations.NotNull; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; - -import static com.intellij.psi.impl.java.stubs.index.JavaUnitDescriptor.ImportFlags.*; - -public class JavaUnitDescriptor implements DataExternalizer { - public static final JavaUnitDescriptor INSTANCE = new JavaUnitDescriptor(); - - private static void writeIntArray(DataOutput out, int[] array) throws IOException { - DataInputOutputUtil.writeINT(out, array.length); - for (int i : array) { - out.writeInt(i); - } - } - private static int[] readIntArray(DataInput in) throws IOException { - int length = DataInputOutputUtil.readINT(in); - int[] result = new int[length]; - for (int i = 0; i < length; i++) { - result[i] = in.readInt(); - } - return result; - } - - @Override - public void save(@NotNull DataOutput out, IndexTree.Unit value) throws IOException { - writeIntArray(out, value.myPackageName); - out.writeByte(value.myUnitType); - if (value.myUnitType != IndexTree.BYTECODE) { - DataInputOutputUtil.writeINT(out, value.imports.length); - for (IndexTree.Import anImport : value.imports) { - writeImport(out, anImport); - } - } - // class Declaration - DataInputOutputUtil.writeINT(out, value.myDecls.length); - for (IndexTree.ClassDecl def : value.myDecls) { - saveClassDecl(out, def); - } - } - - private void saveClassDecl(@NotNull DataOutput out, IndexTree.ClassDecl value) throws IOException { - DataInputOutputUtil.writeINT(out, value.myStubId); - DataInputOutputUtil.writeINT(out, value.myMods); - out.writeInt(value.myName); - DataInputOutputUtil.writeINT(out, value.mySupers.length); - for (int[] aSuper : value.mySupers) { - writeIntArray(out, aSuper); - } - DataInputOutputUtil.writeINT(out, value.myDecls.length); - for (IndexTree.Decl def : value.myDecls) { - saveDecl(out, def); - } - } - - private void saveDecl(@NotNull DataOutput out, IndexTree.Decl value) throws IOException { - if (value instanceof IndexTree.ClassDecl) { - out.writeBoolean(true); - saveClassDecl(out, (IndexTree.ClassDecl)value); - } else if (value instanceof IndexTree.MemberDecl) { - out.writeBoolean(false); - IndexTree.MemberDecl memberDecl = (IndexTree.MemberDecl)value; - DataInputOutputUtil.writeINT(out, memberDecl.myDecls.length); - for (IndexTree.Decl def : memberDecl.myDecls) { - saveDecl(out, def); - } - } - } - - @Override - public IndexTree.Unit read(@NotNull DataInput in) throws IOException { - int[] pid = readIntArray(in); - byte type = in.readByte(); - IndexTree.Import[] imports = IndexTree.Import.EMPTY_ARRAY; - if (type != IndexTree.BYTECODE) { - imports = new IndexTree.Import[DataInputOutputUtil.readINT(in)]; - for (int i = 0; i < imports.length; i++) { - imports[i] = readImport(in); - } - } - IndexTree.ClassDecl[] classes = new IndexTree.ClassDecl[DataInputOutputUtil.readINT(in)]; - for (int i = 0; i < classes.length; i++) { - classes[i] = readClassDecl(in); - } - return new IndexTree.Unit(pid, type, imports, classes); - } - - private IndexTree.ClassDecl readClassDecl(DataInput in) throws IOException { - int stubId = DataInputOutputUtil.readINT(in); - int mods = DataInputOutputUtil.readINT(in); - int name = in.readInt(); - int[][] supers = new int[DataInputOutputUtil.readINT(in)][]; - for (int i = 0; i < supers.length; i++) { - supers[i] = readIntArray(in); - } - IndexTree.Decl[] decls = new IndexTree.Decl[DataInputOutputUtil.readINT(in)]; - for (int i = 0; i < decls.length; i++) { - decls[i] = readDecl(in); - } - return new IndexTree.ClassDecl(stubId, mods, name, supers, decls); - } - - private IndexTree.Decl readDecl(DataInput in) throws IOException { - boolean isClassDecl = in.readBoolean(); - if (isClassDecl) { - return readClassDecl(in); - } - else { - IndexTree.Decl[] decls = new IndexTree.Decl[DataInputOutputUtil.readINT(in)]; - for (int i = 0; i < decls.length; i++) { - decls[i] = readDecl(in); - } - return new IndexTree.MemberDecl(decls); - } - } - - interface ImportFlags { - int IS_STATIC = 1; - int IS_ON_DEMAND = 2; - int HAS_ALIAS = 4; - } - - private static void writeImport(@NotNull DataOutput out, IndexTree.Import anImport) throws IOException { - writeIntArray(out, anImport.myFullname); - boolean hasAlias = anImport.myAlias != 0; - int flags = 0; - flags = BitUtil.set(flags, IS_STATIC, anImport.myStaticImport); - flags = BitUtil.set(flags, IS_ON_DEMAND, anImport.myOnDemand); - flags = BitUtil.set(flags, HAS_ALIAS, hasAlias); - out.writeByte(flags); - if (hasAlias) { - out.writeInt(anImport.myAlias); - } - } - - @NotNull - private static IndexTree.Import readImport(@NotNull DataInput in) throws IOException { - int[] fullname = readIntArray(in); - int flags = in.readByte(); - return new IndexTree.Import(fullname, - BitUtil.isSet(flags, IS_STATIC), BitUtil.isSet(flags, IS_ON_DEMAND), - BitUtil.isSet(flags, HAS_ALIAS) ? in.readInt() : 0); - } - -}