From f153d97f405cf5d9e240234564c51cd4f3619757 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Thu, 13 Dec 2018 13:51:56 +0300 Subject: [PATCH] Avoid multiple subsequent reading of the same class-file in front-end ^KT-23466 Fixed --- .../compiler/KotlinCliJavaFileManagerImpl.kt | 21 ++++++++++++++----- .../kotlin/fir/java/JavaSymbolProvider.kt | 3 ++- .../kotlin/load/java/JavaClassFinderImpl.kt | 5 ++--- .../impl/classFiles/BinaryJavaClass.kt | 9 ++++++-- .../load/kotlin/KotlinBinaryClassCache.kt | 17 +++++++++------ .../kotlin/KotlinBinaryClassCache.kt.as31 | 17 +++++++++------ .../kotlin/KotlinBinaryClassCache.kt.as32 | 17 +++++++++------ .../kotlin/load/kotlin/VirtualFileFinder.kt | 8 +++---- .../load/kotlin/VirtualFileKotlinClass.kt | 9 +++++--- .../resolve/jvm/KotlinCliJavaFileManager.kt | 4 ++-- .../resolve/jvm/KotlinJavaPsiFacade.java | 10 +++++---- .../javac/components/JavacBasedClassFinder.kt | 8 ++++--- .../kotlin/load/java/JavaClassFinder.kt | 10 ++++++++- .../descriptors/LazyJavaClassMemberScope.kt | 7 ++++++- .../lazy/descriptors/LazyJavaPackageScope.kt | 20 ++++++++++++++---- .../kotlin/load/kotlin/KotlinClassFinder.kt | 20 ++++++++++++++++-- .../components/ReflectJavaClassFinder.kt | 3 ++- .../components/ReflectKotlinClassFinder.kt | 10 ++++----- .../idea/caches/IDEKotlinBinaryClassCache.kt | 2 +- .../DeserializerForClassfileDecompiler.kt | 12 +++++++---- 20 files changed, 148 insertions(+), 64 deletions(-) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt index 61218c36053..62accfa5ae2 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt @@ -28,6 +28,7 @@ import gnu.trove.THashSet import org.jetbrains.kotlin.cli.jvm.index.JavaRoot import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex import org.jetbrains.kotlin.cli.jvm.index.SingleJavaFileRootsIndex +import org.jetbrains.kotlin.load.java.JavaClassFinder import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser @@ -81,7 +82,10 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ private val binaryCache: MutableMap = THashMap() private val signatureParsingComponent = BinaryClassSignatureParser() - override fun findClass(classId: ClassId, searchScope: GlobalSearchScope): JavaClass? { + fun findClass(classId: ClassId, searchScope: GlobalSearchScope): JavaClass? = findClass(JavaClassFinder.Request(classId), searchScope) + + override fun findClass(request: JavaClassFinder.Request, searchScope: GlobalSearchScope): JavaClass? { + val (classId, classFileContentFromRequest, outerClassFromRequest) = request val virtualFile = findVirtualFileForTopLevelClass(classId, searchScope) ?: return null if (useFastClassFilesReading && virtualFile.extension == "class") { @@ -92,13 +96,20 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ // This is a true assumption by now since there are two search scopes in compiler: one for sources and another one for binary // When it become wrong because we introduce the modules into CLI, it's worth to consider // having different KotlinCliJavaFileManagerImpl's for different modules - val classContent = virtualFile.contentsToByteArray() - if (virtualFile.nameWithoutExtension.contains("$") && isNotTopLevelClass(classContent)) return@getOrPut null + classId.outerClassId?.let { outerClassId -> - val outerClass = findClass(outerClassId, searchScope) - return@getOrPut outerClass?.findInnerClass(classId.shortClassName) + val outerClass = outerClassFromRequest ?: findClass(outerClassId, searchScope) + + return if (outerClass is BinaryJavaClass) + outerClass.findInnerClass(classId.shortClassName, classFileContentFromRequest) + else + outerClass?.findInnerClass(classId.shortClassName) } + // Here, we assume the class is top-level + val classContent = classFileContentFromRequest ?: virtualFile.contentsToByteArray() + if (virtualFile.nameWithoutExtension.contains("$") && isNotTopLevelClass(classContent)) return@getOrPut null + val resolver = ClassifierResolutionContext { findClass(it, allScope) } BinaryJavaClass( diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt index 26d78bcba9b..4c150746c93 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt @@ -10,6 +10,7 @@ import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.fir.java.symbols.JavaClassSymbol import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider import org.jetbrains.kotlin.fir.symbols.ConeSymbol +import org.jetbrains.kotlin.load.java.JavaClassFinder import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade @@ -38,7 +39,7 @@ class JavaSymbolProvider(val project: Project) : FirSymbolProvider { override fun getSymbolByFqName(classId: ClassId): ConeSymbol? { return classCache.lookupCacheOrCalculate(classId) { val facade = KotlinJavaPsiFacade.getInstance(project) - val foundClass = facade.findClass(classId, allScope) + val foundClass = facade.findClass(JavaClassFinder.Request(classId), allScope) foundClass?.let { javaClass -> JavaClassSymbol(javaClass) } } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/JavaClassFinderImpl.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/JavaClassFinderImpl.kt index 053f29d37a2..37f02a5019a 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/JavaClassFinderImpl.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/JavaClassFinderImpl.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.load.java import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.impl.JavaPackageImpl -import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade @@ -33,10 +32,10 @@ class JavaClassFinderImpl : AbstractJavaClassFinder() { super.initialize(trace, codeAnalyzer) } - override fun findClass(classId: ClassId): JavaClass? = javaFacade.findClass(classId, javaSearchScope) + override fun findClass(request: JavaClassFinder.Request): JavaClass? = javaFacade.findClass(request, javaSearchScope) override fun findPackage(fqName: FqName) = javaFacade.findPackage(fqName.asString(), javaSearchScope)?.let { JavaPackageImpl(it, javaSearchScope) } override fun knownClassNamesInPackage(packageFqName: FqName): Set? = javaFacade.knownClassNamesInPackage(packageFqName) -} \ No newline at end of file +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt index b4e36885dcf..87c6a94be1f 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt @@ -201,11 +201,16 @@ class BinaryJavaClass( override fun visitAnnotation(desc: String, visible: Boolean) = BinaryJavaAnnotation.addAnnotation(annotations, desc, context, signatureParser) - override fun findInnerClass(name: Name): JavaClass? { + override fun findInnerClass(name: Name): JavaClass? = findInnerClass(name, classFileContent = null) + + fun findInnerClass(name: Name, classFileContent: ByteArray?): JavaClass? { val access = ownInnerClassNameToAccess[name] ?: return null return virtualFile.parent.findChild("${virtualFile.nameWithoutExtension}$$name.class")?.let { - BinaryJavaClass(it, fqName.child(name), context.copyForMember(), signatureParser, access, this) + BinaryJavaClass( + it, fqName.child(name), context.copyForMember(), signatureParser, access, this, + classFileContent + ) } } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt index e35290bd6bf..20b80cb6dbb 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt @@ -28,14 +28,17 @@ class KotlinBinaryClassCache : Disposable { private class RequestCache { internal var virtualFile: VirtualFile? = null internal var modificationStamp: Long = 0 - internal var virtualFileKotlinClass: VirtualFileKotlinClass? = null + internal var result: KotlinClassFinder.Result? = null - fun cache(file: VirtualFile, aClass: VirtualFileKotlinClass?): VirtualFileKotlinClass? { + fun cache( + file: VirtualFile, + result: KotlinClassFinder.Result? + ): KotlinClassFinder.Result? { virtualFile = file - virtualFileKotlinClass = aClass + this.result = result modificationStamp = file.modificationStamp - return aClass + return result } } @@ -53,7 +56,9 @@ class KotlinBinaryClassCache : Disposable { } companion object { - fun getKotlinBinaryClass(file: VirtualFile, fileContent: ByteArray? = null): KotlinJvmBinaryClass? { + fun getKotlinBinaryClassOrClassFileContent( + file: VirtualFile, fileContent: ByteArray? = null + ): KotlinClassFinder.Result? { if (file.fileType !== JavaClassFileType.INSTANCE) return null if (file.name == PsiJavaModule.MODULE_INFO_CLS_FILE) return null @@ -62,7 +67,7 @@ class KotlinBinaryClassCache : Disposable { val requestCache = service.cache.get() if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) { - return requestCache.virtualFileKotlinClass + return requestCache.result } val aClass = ApplicationManager.getApplication().runReadAction(Computable { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as31 b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as31 index 3ed195c944c..ad3c0b1e8cb 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as31 +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as31 @@ -27,14 +27,17 @@ class KotlinBinaryClassCache : Disposable { private class RequestCache { internal var virtualFile: VirtualFile? = null internal var modificationStamp: Long = 0 - internal var virtualFileKotlinClass: VirtualFileKotlinClass? = null + internal var result: KotlinClassFinder.Result? = null - fun cache(file: VirtualFile, aClass: VirtualFileKotlinClass?): VirtualFileKotlinClass? { + fun cache( + file: VirtualFile, + result: KotlinClassFinder.Result? + ): KotlinClassFinder.Result? { virtualFile = file - virtualFileKotlinClass = aClass + this.result = result modificationStamp = file.modificationStamp - return aClass + return result } } @@ -52,14 +55,16 @@ class KotlinBinaryClassCache : Disposable { } companion object { - fun getKotlinBinaryClass(file: VirtualFile, fileContent: ByteArray? = null): KotlinJvmBinaryClass? { + fun getKotlinBinaryClassOrClassFileContent( + file: VirtualFile, fileContent: ByteArray? = null + ): KotlinClassFinder.Result? { if (file.fileType !== JavaClassFileType.INSTANCE) return null val service = ServiceManager.getService(KotlinBinaryClassCache::class.java) val requestCache = service.cache.get() if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) { - return requestCache.virtualFileKotlinClass + return requestCache.result } val aClass = ApplicationManager.getApplication().runReadAction(Computable { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as32 b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as32 index 3ed195c944c..ad3c0b1e8cb 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as32 +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as32 @@ -27,14 +27,17 @@ class KotlinBinaryClassCache : Disposable { private class RequestCache { internal var virtualFile: VirtualFile? = null internal var modificationStamp: Long = 0 - internal var virtualFileKotlinClass: VirtualFileKotlinClass? = null + internal var result: KotlinClassFinder.Result? = null - fun cache(file: VirtualFile, aClass: VirtualFileKotlinClass?): VirtualFileKotlinClass? { + fun cache( + file: VirtualFile, + result: KotlinClassFinder.Result? + ): KotlinClassFinder.Result? { virtualFile = file - virtualFileKotlinClass = aClass + this.result = result modificationStamp = file.modificationStamp - return aClass + return result } } @@ -52,14 +55,16 @@ class KotlinBinaryClassCache : Disposable { } companion object { - fun getKotlinBinaryClass(file: VirtualFile, fileContent: ByteArray? = null): KotlinJvmBinaryClass? { + fun getKotlinBinaryClassOrClassFileContent( + file: VirtualFile, fileContent: ByteArray? = null + ): KotlinClassFinder.Result? { if (file.fileType !== JavaClassFileType.INSTANCE) return null val service = ServiceManager.getService(KotlinBinaryClassCache::class.java) val requestCache = service.cache.get() if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) { - return requestCache.virtualFileKotlinClass + return requestCache.result } val aClass = ApplicationManager.getApplication().runReadAction(Computable { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/VirtualFileFinder.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/VirtualFileFinder.kt index 8b345e4e7b8..f5ccbdb89d5 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/VirtualFileFinder.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/VirtualFileFinder.kt @@ -27,12 +27,12 @@ import org.jetbrains.kotlin.utils.sure abstract class VirtualFileFinder : KotlinClassFinder { abstract fun findVirtualFileWithHeader(classId: ClassId): VirtualFile? - override fun findKotlinClass(classId: ClassId): KotlinJvmBinaryClass? { + override fun findKotlinClassOrContent(classId: ClassId): KotlinClassFinder.Result? { val file = findVirtualFileWithHeader(classId) ?: return null - return KotlinBinaryClassCache.getKotlinBinaryClass(file) + return KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file) } - override fun findKotlinClass(javaClass: JavaClass): KotlinJvmBinaryClass? { + override fun findKotlinClassOrContent(javaClass: JavaClass): KotlinClassFinder.Result? { var file = (javaClass as? VirtualFileBoundJavaClass)?.virtualFile ?: return null if (javaClass.outerClass != null) { @@ -41,7 +41,7 @@ abstract class VirtualFileFinder : KotlinClassFinder { file = file.parent!!.findChild(classFileName(javaClass) + ".class").sure { "Virtual file not found for $javaClass" } } - return KotlinBinaryClassCache.getKotlinBinaryClass(file) + return KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file) } private fun classFileName(jClass: JavaClass): String { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/VirtualFileKotlinClass.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/VirtualFileKotlinClass.kt index 509fcd49f17..18a12c1c261 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/VirtualFileKotlinClass.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/VirtualFileKotlinClass.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.load.kotlin import com.intellij.ide.highlighter.JavaClassFileType import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder.Result.KotlinClass import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.util.PerformanceCounter @@ -56,17 +57,19 @@ class VirtualFileKotlinClass private constructor( private val perfCounter = PerformanceCounter.create("Binary class from Kotlin file") @Deprecated("Use KotlinBinaryClassCache") - fun create(file: VirtualFile, fileContent: ByteArray?): VirtualFileKotlinClass? { + fun create(file: VirtualFile, fileContent: ByteArray?): KotlinClassFinder.Result? { return perfCounter.time { assert(file.fileType == JavaClassFileType.INSTANCE) { "Trying to read binary data from a non-class file $file" } try { val byteContent = fileContent ?: file.contentsToByteArray(false) if (!byteContent.isEmpty()) { - return@time FileBasedKotlinClass.create(byteContent) { - name, classVersion, header, innerClasses -> + val kotlinJvmBinaryClass = FileBasedKotlinClass.create(byteContent) { name, classVersion, header, innerClasses -> VirtualFileKotlinClass(file, name, classVersion, header, innerClasses) } + + return@time kotlinJvmBinaryClass?.let(::KotlinClass) + ?: KotlinClassFinder.Result.ClassFileContent(byteContent) } } catch (e: FileNotFoundException) { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/KotlinCliJavaFileManager.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/KotlinCliJavaFileManager.kt index f3fbc80fc0e..5b8bc7939d6 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/KotlinCliJavaFileManager.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/KotlinCliJavaFileManager.kt @@ -18,11 +18,11 @@ package org.jetbrains.kotlin.resolve.jvm import com.intellij.psi.impl.file.impl.JavaFileManager import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.load.java.JavaClassFinder import org.jetbrains.kotlin.load.java.structure.JavaClass -import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName interface KotlinCliJavaFileManager : JavaFileManager { - fun findClass(classId: ClassId, searchScope: GlobalSearchScope): JavaClass? + fun findClass(request: JavaClassFinder.Request, searchScope: GlobalSearchScope): JavaClass? fun knownClassNamesInPackage(packageFqName: FqName): Set? } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java index 2ec44f2c0d9..188754c9a1b 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java @@ -43,6 +43,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.asJava.KtLightClassMarker; import org.jetbrains.kotlin.idea.KotlinLanguage; +import org.jetbrains.kotlin.load.java.JavaClassFinder; import org.jetbrains.kotlin.load.java.JavaClassFinderImpl; import org.jetbrains.kotlin.load.java.structure.JavaClass; import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl; @@ -104,9 +105,10 @@ public class KotlinJavaPsiFacade { return emptyModifierList; } - public JavaClass findClass(@NotNull ClassId classId, @NotNull GlobalSearchScope scope) { + public JavaClass findClass(@NotNull JavaClassFinder.Request request, @NotNull GlobalSearchScope scope) { ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); // We hope this method is being called often enough to cancel daemon processes smoothly + ClassId classId = request.getClassId(); String qualifiedName = classId.asSingleFqName().asString(); if (shouldUseSlowResolve()) { @@ -119,7 +121,7 @@ public class KotlinJavaPsiFacade { for (KotlinPsiElementFinderWrapper finder : finders()) { if (finder instanceof CliFinder) { - JavaClass aClass = ((CliFinder) finder).findClass(classId, scope); + JavaClass aClass = ((CliFinder) finder).findClass(request, scope); if (aClass != null) return aClass; } else { @@ -383,8 +385,8 @@ public class KotlinJavaPsiFacade { return javaFileManager.findClass(qualifiedName, scope); } - public JavaClass findClass(@NotNull ClassId classId, @NotNull GlobalSearchScope scope) { - return javaFileManager.findClass(classId, scope); + public JavaClass findClass(@NotNull JavaClassFinder.Request request, @NotNull GlobalSearchScope scope) { + return javaFileManager.findClass(request, scope); } @Nullable diff --git a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/components/JavacBasedClassFinder.kt b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/components/JavacBasedClassFinder.kt index 5e7cf756dd1..608100d8e05 100644 --- a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/components/JavacBasedClassFinder.kt +++ b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/components/JavacBasedClassFinder.kt @@ -18,7 +18,7 @@ package org.jetbrains.kotlin.javac.components import org.jetbrains.kotlin.javac.JavacWrapper import org.jetbrains.kotlin.load.java.AbstractJavaClassFinder -import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.load.java.JavaClassFinder import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer @@ -32,10 +32,12 @@ class JavacBasedClassFinder : AbstractJavaClassFinder() { super.initialize(trace, codeAnalyzer) } - override fun findClass(classId: ClassId) = javac.findClass(classId, javaSearchScope) + override fun findClass(request: JavaClassFinder.Request) = + // TODO: reuse previouslyFoundClassFileContent if it's possible in javac + javac.findClass(request.classId, javaSearchScope) override fun findPackage(fqName: FqName) = javac.findPackage(fqName, javaSearchScope) override fun knownClassNamesInPackage(packageFqName: FqName) = javac.knownClassNamesInPackage(packageFqName) -} \ No newline at end of file +} diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JavaClassFinder.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JavaClassFinder.kt index cede40602da..294b93d0926 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JavaClassFinder.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JavaClassFinder.kt @@ -22,7 +22,15 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName interface JavaClassFinder { - fun findClass(classId: ClassId): JavaClass? + data class Request( + val classId: ClassId, + @Suppress("ArrayInDataClass") + val previouslyFoundClassFileContent: ByteArray? = null, + val outerClass: JavaClass? = null + ) + + fun findClass(request: Request): JavaClass? + fun findClass(classId: ClassId): JavaClass? = findClass(Request(classId)) fun findPackage(fqName: FqName): JavaPackage? diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt index a52f5860bb6..63a8827f02f 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt @@ -706,7 +706,12 @@ class LazyJavaClassMemberScope( ) } else null } else { - c.components.finder.findClass(ownerDescriptor.classId!!.createNestedClassId(name))?.let { + c.components.finder.findClass( + JavaClassFinder.Request( + ownerDescriptor.classId!!.createNestedClassId(name), + outerClass = jClass + ) + )?.let { LazyJavaClassDescriptor(c, ownerDescriptor, it) .also(c.components.javaClassesTracker::reportClass) } diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageScope.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageScope.kt index c65b57b4612..340b8889bb5 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageScope.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageScope.kt @@ -22,10 +22,12 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.load.java.JavaClassFinder import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.JavaPackage import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind +import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.name.ClassId @@ -33,6 +35,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.storage.NullableLazyValue +import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.alwaysTrue import java.util.* @@ -51,12 +54,14 @@ class LazyJavaPackageScope( c.storageManager.createMemoizedFunctionWithNullableValues classByRequest@{ request -> val requestClassId = ClassId(ownerDescriptor.fqName, request.name) - val kotlinBinaryClass = + val kotlinClassOrClassFileContent = // These branches should be semantically equal, but the first one could be faster if (request.javaClass != null) - c.components.kotlinClassFinder.findKotlinClass(request.javaClass) + c.components.kotlinClassFinder.findKotlinClassOrContent(request.javaClass) else - c.components.kotlinClassFinder.findKotlinClass(requestClassId) + c.components.kotlinClassFinder.findKotlinClassOrContent(requestClassId) + + val kotlinBinaryClass = kotlinClassOrClassFileContent?.toKotlinJvmBinaryClass() val classId = kotlinBinaryClass?.classId // Nested/local classes can be found when running in CLI in case when request.name looks like 'Outer$Inner' @@ -69,7 +74,14 @@ class LazyJavaPackageScope( is KotlinClassLookupResult.Found -> kotlinResult.descriptor is KotlinClassLookupResult.SyntheticClass -> null is KotlinClassLookupResult.NotFound -> { - val javaClass = request.javaClass ?: c.components.finder.findClass(requestClassId) + val javaClass = + request.javaClass ?: c.components.finder.findClass( + JavaClassFinder.Request( + requestClassId, + kotlinClassOrClassFileContent?.safeAs() + ?.content + ) + ) if (javaClass?.lightClassOriginKind == LightClassOriginKind.BINARY) { throw IllegalStateException( diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinClassFinder.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinClassFinder.kt index 534aa3550ac..f8315028481 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinClassFinder.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinClassFinder.kt @@ -21,7 +21,23 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.serialization.deserialization.KotlinMetadataFinder interface KotlinClassFinder : KotlinMetadataFinder { - fun findKotlinClass(classId: ClassId): KotlinJvmBinaryClass? + fun findKotlinClassOrContent(classId: ClassId): Result? - fun findKotlinClass(javaClass: JavaClass): KotlinJvmBinaryClass? + fun findKotlinClassOrContent(javaClass: JavaClass): Result? + + fun findKotlinClass(classId: ClassId): KotlinJvmBinaryClass? = + findKotlinClassOrContent(classId)?.toKotlinJvmBinaryClass() + + fun findKotlinClass(javaClass: JavaClass): KotlinJvmBinaryClass? = + findKotlinClassOrContent(javaClass)?.toKotlinJvmBinaryClass() + + sealed class Result { + fun toKotlinJvmBinaryClass(): KotlinJvmBinaryClass? = (this as? KotlinClass)?.kotlinJvmBinaryClass + + data class KotlinClass(val kotlinJvmBinaryClass: KotlinJvmBinaryClass) : Result() + data class ClassFileContent( + @Suppress("ArrayInDataClass") + val content: ByteArray + ) : Result() + } } diff --git a/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectJavaClassFinder.kt b/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectJavaClassFinder.kt index e24b592824e..9efa5bcadcd 100644 --- a/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectJavaClassFinder.kt +++ b/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectJavaClassFinder.kt @@ -25,7 +25,8 @@ import kotlin.reflect.jvm.internal.structure.ReflectJavaClass import kotlin.reflect.jvm.internal.structure.ReflectJavaPackage class ReflectJavaClassFinder(private val classLoader: ClassLoader) : JavaClassFinder { - override fun findClass(classId: ClassId): JavaClass? { + override fun findClass(request: JavaClassFinder.Request): JavaClass? { + val classId = request.classId val packageFqName = classId.packageFqName val relativeClassName = classId.relativeClassName.asString().replace('.', '$') val name = diff --git a/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectKotlinClassFinder.kt b/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectKotlinClassFinder.kt index 8bf00143306..7dacdc143fe 100644 --- a/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectKotlinClassFinder.kt +++ b/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectKotlinClassFinder.kt @@ -18,19 +18,19 @@ package kotlin.reflect.jvm.internal.components import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder -import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass +import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder.Result.KotlinClass import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import java.io.InputStream class ReflectKotlinClassFinder(private val classLoader: ClassLoader) : KotlinClassFinder { - private fun findKotlinClass(fqName: String): KotlinJvmBinaryClass? { - return classLoader.tryLoadClass(fqName)?.let { ReflectKotlinClass.create(it) } + private fun findKotlinClass(fqName: String): KotlinClassFinder.Result? { + return classLoader.tryLoadClass(fqName)?.let { ReflectKotlinClass.create(it) }?.let(::KotlinClass) } - override fun findKotlinClass(classId: ClassId) = findKotlinClass(classId.toRuntimeFqName()) + override fun findKotlinClassOrContent(classId: ClassId) = findKotlinClass(classId.toRuntimeFqName()) - override fun findKotlinClass(javaClass: JavaClass): KotlinJvmBinaryClass? { + override fun findKotlinClassOrContent(javaClass: JavaClass): KotlinClassFinder.Result? { // TODO: go through javaClass's class loader return findKotlinClass(javaClass.fqName?.asString() ?: return null) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/IDEKotlinBinaryClassCache.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/IDEKotlinBinaryClassCache.kt index 491a0032ab8..d10598075ab 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/IDEKotlinBinaryClassCache.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/IDEKotlinBinaryClassCache.kt @@ -60,7 +60,7 @@ object IDEKotlinBinaryClassCache { return null } - val kotlinBinaryClass = KotlinBinaryClassCache.getKotlinBinaryClass(file, fileContent) + val kotlinBinaryClass = KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file, fileContent)?.toKotlinJvmBinaryClass() val isKotlinBinaryClass = kotlinBinaryClass != null if (file is VirtualFileWithId) { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt index 324681529fa..86dddc81272 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt @@ -31,12 +31,16 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.classId import org.jetbrains.kotlin.load.kotlin.* +import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder.Result.KotlinClass import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform -import org.jetbrains.kotlin.serialization.deserialization.* +import org.jetbrains.kotlin.serialization.deserialization.ClassData +import org.jetbrains.kotlin.serialization.deserialization.ClassDataFinder +import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents +import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope import java.io.InputStream @@ -109,16 +113,16 @@ class DirectoryBasedClassFinder( val packageDirectory: VirtualFile, val directoryPackageFqName: FqName ) : KotlinClassFinder { - override fun findKotlinClass(javaClass: JavaClass) = findKotlinClass(javaClass.classId!!) + override fun findKotlinClassOrContent(javaClass: JavaClass) = findKotlinClassOrContent(javaClass.classId!!) - override fun findKotlinClass(classId: ClassId): KotlinJvmBinaryClass? { + override fun findKotlinClassOrContent(classId: ClassId): KotlinClassFinder.Result? { if (classId.packageFqName != directoryPackageFqName) { return null } val targetName = classId.relativeClassName.pathSegments().joinToString("$", postfix = ".class") val virtualFile = packageDirectory.findChild(targetName) if (virtualFile != null && isKotlinWithCompatibleAbiVersion(virtualFile)) { - return IDEKotlinBinaryClassCache.getKotlinBinaryClass(virtualFile) + return IDEKotlinBinaryClassCache.getKotlinBinaryClass(virtualFile)?.let(::KotlinClass) } return null }