mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
KTIJ-36616 Add KotlinSubclassesIndex to graph for Kotlin CRI support
New index was introduced because doing the same filtration on consumer side requires a lot of requests to the graph, which could add significant IO overhead. Space-RevId: 4b3db4beadbbe5063090363f74135541ee451c63 GitOrigin-RevId: e9707eb8ddcf4d18df534c73e9a750f95bab7185
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c0a1e7da7c
commit
af74b3c7a6
@@ -165,6 +165,11 @@ public final class KotlinMeta implements JvmMetadata<KotlinMeta, KotlinMeta.Diff
|
||||
return container instanceof KmClass? ((KmClass)container).getSealedSubclasses() : Collections.emptyList();
|
||||
}
|
||||
|
||||
public Iterable<KmType> getSupertypes() {
|
||||
KmDeclarationContainer container = getDeclarationContainer();
|
||||
return container instanceof KmClass? ((KmClass)container).getSupertypes() : Collections.emptyList();
|
||||
}
|
||||
|
||||
public Visibility getContainerVisibility() {
|
||||
KmDeclarationContainer container = getDeclarationContainer();
|
||||
return container instanceof KmClass? Attributes.getVisibility((KmClass)container) : Visibility.PUBLIC;
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.kotlin;
|
||||
|
||||
import kotlin.metadata.KmClassifier;
|
||||
import kotlin.metadata.KmType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.MapletFactory;
|
||||
import org.jetbrains.jps.dependency.Node;
|
||||
import org.jetbrains.jps.dependency.ReferenceID;
|
||||
import org.jetbrains.jps.dependency.impl.BackDependencyIndexImpl;
|
||||
import org.jetbrains.jps.dependency.java.JvmClass;
|
||||
import org.jetbrains.jps.dependency.java.JvmNodeReferenceID;
|
||||
import org.jetbrains.jps.dependency.java.KotlinMeta;
|
||||
import org.jetbrains.jps.util.Iterators;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <code>KotlinSubclassesIndex</code> back-dependency index for Kotlin classes that tracks direct subclass relationships.
|
||||
* This index is used by the <code>KotlinCompilerReferenceIndex</code>.
|
||||
*
|
||||
* <p>The index is built based on <b>Kotlin metadata</b> and applies several filters:
|
||||
* <ul>
|
||||
* <li>Java classes are excluded from values, so only JvmClass nodes with Kotlin metadata are accepted.
|
||||
* In fact, this means that this index stores relations like: [Kotlin/Java]Nodes to [Kotlin]Nodes</li>
|
||||
* <li>Only Kotlin classes with classKind = CLASS are indexed (filters out annotations, Kotlin file facades, etc.)</li>
|
||||
* <li>Local classes are excluded from indexing</li>
|
||||
* <li>The kotlin.Any supertype is filtered out as it's implicit for all Kotlin classes</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class KotlinSubclassesIndex extends BackDependencyIndexImpl {
|
||||
public static final String NAME = "kotlin-direct-subclasses";
|
||||
|
||||
public KotlinSubclassesIndex(@NotNull MapletFactory cFactory) {
|
||||
super(NAME, cFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ReferenceID> getIndexedDependencies(@NotNull Node<?, ?> node) {
|
||||
if (!(node instanceof JvmClass)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
JvmClass classNode = (JvmClass)node;
|
||||
|
||||
KotlinMeta kotlinMeta = KJvmUtils.getKotlinMeta(classNode);
|
||||
if (kotlinMeta == null || kotlinMeta.getKind() != 1 // CLASS
|
||||
|| classNode.isLocal()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Iterable<String> fqNames =
|
||||
Iterators.filter(
|
||||
Iterators.map(
|
||||
Iterators.filter(
|
||||
Iterators.map(kotlinMeta.getSupertypes(), KmType::getClassifier),
|
||||
kmClassifier -> kmClassifier instanceof KmClassifier.Class
|
||||
),
|
||||
kmClassifier -> ((KmClassifier.Class)kmClassifier).getName()
|
||||
), fqName -> !Objects.equals(fqName, "kotlin/Any")
|
||||
);
|
||||
|
||||
return Iterators.map(fqNames, name -> new JvmNodeReferenceID(name));
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -13,6 +13,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.dependency.DependencyGraph;
|
||||
import org.jetbrains.jps.dependency.GraphConfiguration;
|
||||
import org.jetbrains.jps.dependency.impl.DependencyGraphImpl;
|
||||
import org.jetbrains.jps.dependency.kotlin.KotlinSubclassesIndex;
|
||||
import org.jetbrains.jps.dependency.kotlin.LookupsIndex;
|
||||
|
||||
import java.io.*;
|
||||
@@ -119,7 +120,7 @@ public class StorageManager implements CloseableExt {
|
||||
if (isKotlinCriDataGenerationEnabled) {
|
||||
return new DependencyGraphImpl(
|
||||
containerFactory,
|
||||
DependencyGraphImpl.IndexFactory.create(LookupsIndex::new)
|
||||
DependencyGraphImpl.IndexFactory.create(LookupsIndex::new, KotlinSubclassesIndex::new)
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
+22
-16
@@ -8,10 +8,10 @@ import org.jetbrains.jps.dependency.BackDependencyIndex
|
||||
import org.jetbrains.jps.dependency.DependencyGraph
|
||||
import org.jetbrains.jps.dependency.ReferenceID
|
||||
import org.jetbrains.jps.dependency.java.JvmNodeReferenceID
|
||||
import org.jetbrains.jps.dependency.java.SubclassesIndex
|
||||
import org.jetbrains.jps.dependency.kotlin.KotlinSubclassesIndex
|
||||
import org.jetbrains.jps.dependency.kotlin.LookupsIndex
|
||||
|
||||
const val VERSION = 3
|
||||
const val VERSION = 4
|
||||
|
||||
internal fun prepareSerializedData(graph: DependencyGraph): ByteArray {
|
||||
val serializedDataResult = ByteArrayOutputStream().use { byteStream ->
|
||||
@@ -19,10 +19,12 @@ internal fun prepareSerializedData(graph: DependencyGraph): ByteArray {
|
||||
dataOut.writeVersion()
|
||||
|
||||
// process subtypes
|
||||
val subclassesIndex: BackDependencyIndex? = graph.getIndex(SubclassesIndex.NAME)
|
||||
val subclassesCount = subclassesIndex?.keys?.count() ?: 0
|
||||
val subclassesIndex: BackDependencyIndex? = graph.getIndex(KotlinSubclassesIndex.NAME)
|
||||
val subclassesFilteredKeys = subclassesIndex?.keys
|
||||
?.filter { key -> subclassesIndex.getDependencies(key).iterator().hasNext() }
|
||||
val subclassesCount = subclassesFilteredKeys?.count() ?: 0
|
||||
dataOut.writeInt(subclassesCount)
|
||||
subclassesIndex?.keys
|
||||
subclassesFilteredKeys
|
||||
?.sortedBy { it.toFqName() }
|
||||
?.forEach { key ->
|
||||
val dependencies = subclassesIndex.getDependencies(key).map { it.toFqName() }.sorted()
|
||||
@@ -63,23 +65,27 @@ internal fun prepareSerializedData(graph: DependencyGraph): ByteArray {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the current `ReferenceID` to its fully qualified name (FqName) representation.
|
||||
* Converts a JVM reference ID to a dot-separated fully qualified name.
|
||||
*
|
||||
* Example transformation:
|
||||
* Input: `com/intellij/AppKt$foo$3$1`
|
||||
* Output: `com.intellij.AppKt.foo`
|
||||
*
|
||||
* @return The fully qualified name as a `String`.
|
||||
* Examples:
|
||||
* - `com/example/Foo` → `com.example.Foo`
|
||||
* - `com/example/Foo$Bar` → `com.example.Foo.Bar`
|
||||
* - `com/example/Foo$1` → `com.example.Foo`
|
||||
* - `com/example/Foo$$Lambda$3` → `com.example.Foo.$Lambda`
|
||||
*/
|
||||
private fun ReferenceID.toFqName(): String {
|
||||
return (this as? JvmNodeReferenceID)?.nodeName
|
||||
?.replace('/', '.')
|
||||
?.replace(DOLLAR_DIGITS_SUFFIX_REGEX, "")
|
||||
?.replace('$', '.')
|
||||
?: this.toString()
|
||||
val jvmName = (this as? JvmNodeReferenceID)?.nodeName ?: return toString()
|
||||
|
||||
return jvmName
|
||||
.replace('/', '.')
|
||||
.replace("$$", DOUBLE_DOLLAR_PLACEHOLDER)
|
||||
.replace(DOLLAR_DIGITS_SUFFIX_REGEX, "")
|
||||
.replace('$', '.')
|
||||
.replace(DOUBLE_DOLLAR_PLACEHOLDER, ".$")
|
||||
}
|
||||
|
||||
private val DOLLAR_DIGITS_SUFFIX_REGEX = Regex("(?:\\$\\d+)+$")
|
||||
private const val DOUBLE_DOLLAR_PLACEHOLDER = "\u0000DOUBLE_DOLLAR\u0000"
|
||||
|
||||
private fun String.addFilePathIfNeeded(fileIdToPathEntryAccumulator: MutableMap<String, Int>): Int {
|
||||
return fileIdToPathEntryAccumulator.computeIfAbsent(this) { fileIdToPathEntryAccumulator.size + 1 }
|
||||
|
||||
Reference in New Issue
Block a user