Avoid multiple subsequent reading of the same class-file in front-end

^KT-23466 Fixed
This commit is contained in:
Denis Zharkov
2018-12-13 13:51:56 +03:00
parent bc61e5a3bc
commit f153d97f40
20 changed files with 148 additions and 64 deletions
@@ -28,6 +28,7 @@ import gnu.trove.THashSet
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex
import org.jetbrains.kotlin.cli.jvm.index.SingleJavaFileRootsIndex 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.JavaClass
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser 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<ClassId, JavaClass?> = THashMap() private val binaryCache: MutableMap<ClassId, JavaClass?> = THashMap()
private val signatureParsingComponent = BinaryClassSignatureParser() 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 val virtualFile = findVirtualFileForTopLevelClass(classId, searchScope) ?: return null
if (useFastClassFilesReading && virtualFile.extension == "class") { 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 // 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 // When it become wrong because we introduce the modules into CLI, it's worth to consider
// having different KotlinCliJavaFileManagerImpl's for different modules // having different KotlinCliJavaFileManagerImpl's for different modules
val classContent = virtualFile.contentsToByteArray()
if (virtualFile.nameWithoutExtension.contains("$") && isNotTopLevelClass(classContent)) return@getOrPut null
classId.outerClassId?.let { outerClassId -> classId.outerClassId?.let { outerClassId ->
val outerClass = findClass(outerClassId, searchScope) val outerClass = outerClassFromRequest ?: findClass(outerClassId, searchScope)
return@getOrPut outerClass?.findInnerClass(classId.shortClassName)
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) } val resolver = ClassifierResolutionContext { findClass(it, allScope) }
BinaryJavaClass( BinaryJavaClass(
@@ -10,6 +10,7 @@ import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.fir.java.symbols.JavaClassSymbol import org.jetbrains.kotlin.fir.java.symbols.JavaClassSymbol
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
import org.jetbrains.kotlin.fir.symbols.ConeSymbol 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.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
@@ -38,7 +39,7 @@ class JavaSymbolProvider(val project: Project) : FirSymbolProvider {
override fun getSymbolByFqName(classId: ClassId): ConeSymbol? { override fun getSymbolByFqName(classId: ClassId): ConeSymbol? {
return classCache.lookupCacheOrCalculate(classId) { return classCache.lookupCacheOrCalculate(classId) {
val facade = KotlinJavaPsiFacade.getInstance(project) val facade = KotlinJavaPsiFacade.getInstance(project)
val foundClass = facade.findClass(classId, allScope) val foundClass = facade.findClass(JavaClassFinder.Request(classId), allScope)
foundClass?.let { javaClass -> JavaClassSymbol(javaClass) } foundClass?.let { javaClass -> JavaClassSymbol(javaClass) }
} }
} }
@@ -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.JavaClass
import org.jetbrains.kotlin.load.java.structure.impl.JavaPackageImpl 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.name.FqName
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
@@ -33,10 +32,10 @@ class JavaClassFinderImpl : AbstractJavaClassFinder() {
super.initialize(trace, codeAnalyzer) 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 findPackage(fqName: FqName) = javaFacade.findPackage(fqName.asString(), javaSearchScope)?.let { JavaPackageImpl(it, javaSearchScope) }
override fun knownClassNamesInPackage(packageFqName: FqName): Set<String>? = javaFacade.knownClassNamesInPackage(packageFqName) override fun knownClassNamesInPackage(packageFqName: FqName): Set<String>? = javaFacade.knownClassNamesInPackage(packageFqName)
} }
@@ -201,11 +201,16 @@ class BinaryJavaClass(
override fun visitAnnotation(desc: String, visible: Boolean) = override fun visitAnnotation(desc: String, visible: Boolean) =
BinaryJavaAnnotation.addAnnotation(annotations, desc, context, signatureParser) 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 val access = ownInnerClassNameToAccess[name] ?: return null
return virtualFile.parent.findChild("${virtualFile.nameWithoutExtension}$$name.class")?.let { 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
)
} }
} }
} }
@@ -28,14 +28,17 @@ class KotlinBinaryClassCache : Disposable {
private class RequestCache { private class RequestCache {
internal var virtualFile: VirtualFile? = null internal var virtualFile: VirtualFile? = null
internal var modificationStamp: Long = 0 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 virtualFile = file
virtualFileKotlinClass = aClass this.result = result
modificationStamp = file.modificationStamp modificationStamp = file.modificationStamp
return aClass return result
} }
} }
@@ -53,7 +56,9 @@ class KotlinBinaryClassCache : Disposable {
} }
companion object { 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.fileType !== JavaClassFileType.INSTANCE) return null
if (file.name == PsiJavaModule.MODULE_INFO_CLS_FILE) return null if (file.name == PsiJavaModule.MODULE_INFO_CLS_FILE) return null
@@ -62,7 +67,7 @@ class KotlinBinaryClassCache : Disposable {
val requestCache = service.cache.get() val requestCache = service.cache.get()
if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) { if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) {
return requestCache.virtualFileKotlinClass return requestCache.result
} }
val aClass = ApplicationManager.getApplication().runReadAction(Computable { val aClass = ApplicationManager.getApplication().runReadAction(Computable {
@@ -27,14 +27,17 @@ class KotlinBinaryClassCache : Disposable {
private class RequestCache { private class RequestCache {
internal var virtualFile: VirtualFile? = null internal var virtualFile: VirtualFile? = null
internal var modificationStamp: Long = 0 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 virtualFile = file
virtualFileKotlinClass = aClass this.result = result
modificationStamp = file.modificationStamp modificationStamp = file.modificationStamp
return aClass return result
} }
} }
@@ -52,14 +55,16 @@ class KotlinBinaryClassCache : Disposable {
} }
companion object { 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.fileType !== JavaClassFileType.INSTANCE) return null
val service = ServiceManager.getService(KotlinBinaryClassCache::class.java) val service = ServiceManager.getService(KotlinBinaryClassCache::class.java)
val requestCache = service.cache.get() val requestCache = service.cache.get()
if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) { if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) {
return requestCache.virtualFileKotlinClass return requestCache.result
} }
val aClass = ApplicationManager.getApplication().runReadAction(Computable { val aClass = ApplicationManager.getApplication().runReadAction(Computable {
@@ -27,14 +27,17 @@ class KotlinBinaryClassCache : Disposable {
private class RequestCache { private class RequestCache {
internal var virtualFile: VirtualFile? = null internal var virtualFile: VirtualFile? = null
internal var modificationStamp: Long = 0 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 virtualFile = file
virtualFileKotlinClass = aClass this.result = result
modificationStamp = file.modificationStamp modificationStamp = file.modificationStamp
return aClass return result
} }
} }
@@ -52,14 +55,16 @@ class KotlinBinaryClassCache : Disposable {
} }
companion object { 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.fileType !== JavaClassFileType.INSTANCE) return null
val service = ServiceManager.getService(KotlinBinaryClassCache::class.java) val service = ServiceManager.getService(KotlinBinaryClassCache::class.java)
val requestCache = service.cache.get() val requestCache = service.cache.get()
if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) { if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) {
return requestCache.virtualFileKotlinClass return requestCache.result
} }
val aClass = ApplicationManager.getApplication().runReadAction(Computable { val aClass = ApplicationManager.getApplication().runReadAction(Computable {
@@ -27,12 +27,12 @@ import org.jetbrains.kotlin.utils.sure
abstract class VirtualFileFinder : KotlinClassFinder { abstract class VirtualFileFinder : KotlinClassFinder {
abstract fun findVirtualFileWithHeader(classId: ClassId): VirtualFile? 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 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 var file = (javaClass as? VirtualFileBoundJavaClass)?.virtualFile ?: return null
if (javaClass.outerClass != 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" } 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 { private fun classFileName(jClass: JavaClass): String {
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.load.kotlin
import com.intellij.ide.highlighter.JavaClassFileType import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.vfs.VirtualFile 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.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.util.PerformanceCounter import org.jetbrains.kotlin.util.PerformanceCounter
@@ -56,17 +57,19 @@ class VirtualFileKotlinClass private constructor(
private val perfCounter = PerformanceCounter.create("Binary class from Kotlin file") private val perfCounter = PerformanceCounter.create("Binary class from Kotlin file")
@Deprecated("Use KotlinBinaryClassCache") @Deprecated("Use KotlinBinaryClassCache")
fun create(file: VirtualFile, fileContent: ByteArray?): VirtualFileKotlinClass? { fun create(file: VirtualFile, fileContent: ByteArray?): KotlinClassFinder.Result? {
return perfCounter.time { return perfCounter.time {
assert(file.fileType == JavaClassFileType.INSTANCE) { "Trying to read binary data from a non-class file $file" } assert(file.fileType == JavaClassFileType.INSTANCE) { "Trying to read binary data from a non-class file $file" }
try { try {
val byteContent = fileContent ?: file.contentsToByteArray(false) val byteContent = fileContent ?: file.contentsToByteArray(false)
if (!byteContent.isEmpty()) { if (!byteContent.isEmpty()) {
return@time FileBasedKotlinClass.create(byteContent) { val kotlinJvmBinaryClass = FileBasedKotlinClass.create(byteContent) { name, classVersion, header, innerClasses ->
name, classVersion, header, innerClasses ->
VirtualFileKotlinClass(file, name, classVersion, header, innerClasses) VirtualFileKotlinClass(file, name, classVersion, header, innerClasses)
} }
return@time kotlinJvmBinaryClass?.let(::KotlinClass)
?: KotlinClassFinder.Result.ClassFileContent(byteContent)
} }
} }
catch (e: FileNotFoundException) { catch (e: FileNotFoundException) {
@@ -18,11 +18,11 @@ package org.jetbrains.kotlin.resolve.jvm
import com.intellij.psi.impl.file.impl.JavaFileManager import com.intellij.psi.impl.file.impl.JavaFileManager
import com.intellij.psi.search.GlobalSearchScope 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.load.java.structure.JavaClass
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
interface KotlinCliJavaFileManager : JavaFileManager { interface KotlinCliJavaFileManager : JavaFileManager {
fun findClass(classId: ClassId, searchScope: GlobalSearchScope): JavaClass? fun findClass(request: JavaClassFinder.Request, searchScope: GlobalSearchScope): JavaClass?
fun knownClassNamesInPackage(packageFqName: FqName): Set<String>? fun knownClassNamesInPackage(packageFqName: FqName): Set<String>?
} }
@@ -43,6 +43,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.asJava.KtLightClassMarker; import org.jetbrains.kotlin.asJava.KtLightClassMarker;
import org.jetbrains.kotlin.idea.KotlinLanguage; 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.JavaClassFinderImpl;
import org.jetbrains.kotlin.load.java.structure.JavaClass; 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.JavaClassImpl;
@@ -104,9 +105,10 @@ public class KotlinJavaPsiFacade {
return emptyModifierList; 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 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(); String qualifiedName = classId.asSingleFqName().asString();
if (shouldUseSlowResolve()) { if (shouldUseSlowResolve()) {
@@ -119,7 +121,7 @@ public class KotlinJavaPsiFacade {
for (KotlinPsiElementFinderWrapper finder : finders()) { for (KotlinPsiElementFinderWrapper finder : finders()) {
if (finder instanceof CliFinder) { if (finder instanceof CliFinder) {
JavaClass aClass = ((CliFinder) finder).findClass(classId, scope); JavaClass aClass = ((CliFinder) finder).findClass(request, scope);
if (aClass != null) return aClass; if (aClass != null) return aClass;
} }
else { else {
@@ -383,8 +385,8 @@ public class KotlinJavaPsiFacade {
return javaFileManager.findClass(qualifiedName, scope); return javaFileManager.findClass(qualifiedName, scope);
} }
public JavaClass findClass(@NotNull ClassId classId, @NotNull GlobalSearchScope scope) { public JavaClass findClass(@NotNull JavaClassFinder.Request request, @NotNull GlobalSearchScope scope) {
return javaFileManager.findClass(classId, scope); return javaFileManager.findClass(request, scope);
} }
@Nullable @Nullable
@@ -18,7 +18,7 @@ package org.jetbrains.kotlin.javac.components
import org.jetbrains.kotlin.javac.JavacWrapper import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.load.java.AbstractJavaClassFinder 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.name.FqName
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
@@ -32,10 +32,12 @@ class JavacBasedClassFinder : AbstractJavaClassFinder() {
super.initialize(trace, codeAnalyzer) 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 findPackage(fqName: FqName) = javac.findPackage(fqName, javaSearchScope)
override fun knownClassNamesInPackage(packageFqName: FqName) = javac.knownClassNamesInPackage(packageFqName) override fun knownClassNamesInPackage(packageFqName: FqName) = javac.knownClassNamesInPackage(packageFqName)
} }
@@ -22,7 +22,15 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
interface JavaClassFinder { 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? fun findPackage(fqName: FqName): JavaPackage?
@@ -706,7 +706,12 @@ class LazyJavaClassMemberScope(
) )
} else null } else null
} else { } 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) LazyJavaClassDescriptor(c, ownerDescriptor, it)
.also(c.components.javaClassesTracker::reportClass) .also(c.components.javaClassesTracker::reportClass)
} }
@@ -22,10 +22,12 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation 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.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaPackage import org.jetbrains.kotlin.load.java.structure.JavaPackage
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind 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.KotlinJvmBinaryClass
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.name.ClassId 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.name.SpecialNames
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.storage.NullableLazyValue import org.jetbrains.kotlin.storage.NullableLazyValue
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.alwaysTrue import org.jetbrains.kotlin.utils.alwaysTrue
import java.util.* import java.util.*
@@ -51,12 +54,14 @@ class LazyJavaPackageScope(
c.storageManager.createMemoizedFunctionWithNullableValues<FindClassRequest, ClassDescriptor> classByRequest@{ request -> c.storageManager.createMemoizedFunctionWithNullableValues<FindClassRequest, ClassDescriptor> classByRequest@{ request ->
val requestClassId = ClassId(ownerDescriptor.fqName, request.name) val requestClassId = ClassId(ownerDescriptor.fqName, request.name)
val kotlinBinaryClass = val kotlinClassOrClassFileContent =
// These branches should be semantically equal, but the first one could be faster // These branches should be semantically equal, but the first one could be faster
if (request.javaClass != null) if (request.javaClass != null)
c.components.kotlinClassFinder.findKotlinClass(request.javaClass) c.components.kotlinClassFinder.findKotlinClassOrContent(request.javaClass)
else else
c.components.kotlinClassFinder.findKotlinClass(requestClassId) c.components.kotlinClassFinder.findKotlinClassOrContent(requestClassId)
val kotlinBinaryClass = kotlinClassOrClassFileContent?.toKotlinJvmBinaryClass()
val classId = kotlinBinaryClass?.classId val classId = kotlinBinaryClass?.classId
// Nested/local classes can be found when running in CLI in case when request.name looks like 'Outer$Inner' // 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.Found -> kotlinResult.descriptor
is KotlinClassLookupResult.SyntheticClass -> null is KotlinClassLookupResult.SyntheticClass -> null
is KotlinClassLookupResult.NotFound -> { 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<KotlinClassFinder.Result.ClassFileContent>()
?.content
)
)
if (javaClass?.lightClassOriginKind == LightClassOriginKind.BINARY) { if (javaClass?.lightClassOriginKind == LightClassOriginKind.BINARY) {
throw IllegalStateException( throw IllegalStateException(
@@ -21,7 +21,23 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.deserialization.KotlinMetadataFinder import org.jetbrains.kotlin.serialization.deserialization.KotlinMetadataFinder
interface KotlinClassFinder : 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()
}
} }
@@ -25,7 +25,8 @@ import kotlin.reflect.jvm.internal.structure.ReflectJavaClass
import kotlin.reflect.jvm.internal.structure.ReflectJavaPackage import kotlin.reflect.jvm.internal.structure.ReflectJavaPackage
class ReflectJavaClassFinder(private val classLoader: ClassLoader) : JavaClassFinder { 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 packageFqName = classId.packageFqName
val relativeClassName = classId.relativeClassName.asString().replace('.', '$') val relativeClassName = classId.relativeClassName.asString().replace('.', '$')
val name = val name =
@@ -18,19 +18,19 @@ package kotlin.reflect.jvm.internal.components
import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder 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.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import java.io.InputStream import java.io.InputStream
class ReflectKotlinClassFinder(private val classLoader: ClassLoader) : KotlinClassFinder { class ReflectKotlinClassFinder(private val classLoader: ClassLoader) : KotlinClassFinder {
private fun findKotlinClass(fqName: String): KotlinJvmBinaryClass? { private fun findKotlinClass(fqName: String): KotlinClassFinder.Result? {
return classLoader.tryLoadClass(fqName)?.let { ReflectKotlinClass.create(it) } 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 // TODO: go through javaClass's class loader
return findKotlinClass(javaClass.fqName?.asString() ?: return null) return findKotlinClass(javaClass.fqName?.asString() ?: return null)
} }
@@ -60,7 +60,7 @@ object IDEKotlinBinaryClassCache {
return null return null
} }
val kotlinBinaryClass = KotlinBinaryClassCache.getKotlinBinaryClass(file, fileContent) val kotlinBinaryClass = KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file, fileContent)?.toKotlinJvmBinaryClass()
val isKotlinBinaryClass = kotlinBinaryClass != null val isKotlinBinaryClass = kotlinBinaryClass != null
if (file is VirtualFileWithId) { if (file is VirtualFileWithId) {
@@ -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.JavaClass
import org.jetbrains.kotlin.load.java.structure.classId import org.jetbrains.kotlin.load.java.structure.classId
import org.jetbrains.kotlin.load.kotlin.* 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.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform 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 org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
import java.io.InputStream import java.io.InputStream
@@ -109,16 +113,16 @@ class DirectoryBasedClassFinder(
val packageDirectory: VirtualFile, val packageDirectory: VirtualFile,
val directoryPackageFqName: FqName val directoryPackageFqName: FqName
) : KotlinClassFinder { ) : 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) { if (classId.packageFqName != directoryPackageFqName) {
return null return null
} }
val targetName = classId.relativeClassName.pathSegments().joinToString("$", postfix = ".class") val targetName = classId.relativeClassName.pathSegments().joinToString("$", postfix = ".class")
val virtualFile = packageDirectory.findChild(targetName) val virtualFile = packageDirectory.findChild(targetName)
if (virtualFile != null && isKotlinWithCompatibleAbiVersion(virtualFile)) { if (virtualFile != null && isKotlinWithCompatibleAbiVersion(virtualFile)) {
return IDEKotlinBinaryClassCache.getKotlinBinaryClass(virtualFile) return IDEKotlinBinaryClassCache.getKotlinBinaryClass(virtualFile)?.let(::KotlinClass)
} }
return null return null
} }