Refactor: split *LightClassGenerationSupport into separate files

Move cli binding trace implementation to top level
This commit is contained in:
Pavel V. Talanov
2018-03-09 13:15:38 +01:00
parent f421181e16
commit 32516a3f33
17 changed files with 720 additions and 576 deletions
@@ -0,0 +1,167 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiSearchScopeUtil
import com.intellij.util.Function
import com.intellij.util.SmartList
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForScript
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.load.java.components.FilesByFacadeFqNameIndexer
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
class CliKotlinAsJavaSupport(
project: Project,
private val traceHolder: CliTraceHolder
): KotlinAsJavaSupport() {
private val psiManager = PsiManager.getInstance(project)
override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
return findFacadeFilesInPackage(packageFqName, scope)
.groupBy { it.javaFileFacadeFqName }
.mapNotNull {
KtLightClassForFacade.createForFacade(
psiManager,
it.key,
scope,
it.value
)
}
}
override fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection<String> {
return findFacadeFilesInPackage(packageFqName, scope)
.map { it.javaFileFacadeFqName.shortName().asString() }
}
private fun findFacadeFilesInPackage(
packageFqName: FqName,
scope: GlobalSearchScope
) = traceHolder.bindingContext.get(FilesByFacadeFqNameIndexer.FACADE_FILES_BY_PACKAGE_NAME, packageFqName)
?.filter { PsiSearchScopeUtil.isInScope(scope, it) }
.orEmpty()
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val filesForFacade = findFilesForFacade(facadeFqName, scope)
if (filesForFacade.isEmpty()) return emptyList()
return listOfNotNull<PsiClass>(
KtLightClassForFacade.createForFacade(
psiManager,
facadeFqName,
scope,
filesForFacade
)
)
}
override fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
if (scriptFqName.isRoot) {
return emptyList()
}
return findFilesForPackage(scriptFqName.parent(), scope).mapNotNull { file ->
file.script?.takeIf { it.fqName == scriptFqName }?.let { it -> getLightClassForScript(it) }
}
}
override fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
//
return emptyList()
}
override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile> {
if (facadeFqName.isRoot) return emptyList()
return traceHolder.bindingContext.get(FilesByFacadeFqNameIndexer.FACADE_FILES_BY_FQ_NAME, facadeFqName)?.filter {
PsiSearchScopeUtil.isInScope(scope, it)
} ?: emptyList()
}
override fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName, searchScope: GlobalSearchScope
): Collection<KtClassOrObject> {
val files = findFilesForPackage(packageFqName, searchScope)
val result = SmartList<KtClassOrObject>()
for (file in files) {
for (declaration in file.declarations) {
if (declaration is KtClassOrObject) {
result.add(declaration)
}
}
}
return result
}
override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean {
return !traceHolder.module.getPackage(fqName).isEmpty()
}
override fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName> {
val packageView = traceHolder.module.getPackage(fqn)
val members = packageView.memberScope.getContributedDescriptors(
DescriptorKindFilter.PACKAGES,
MemberScope.ALL_NAME_FILTER
)
return ContainerUtil.mapNotNull(
members,
object : Function<DeclarationDescriptor, FqName> {
override fun `fun`(member: DeclarationDescriptor): FqName? {
if (member is PackageViewDescriptor) {
return member.fqName
}
return null
}
})
}
override fun getLightClass(classOrObject: KtClassOrObject): KtLightClass? =
KtLightClassForSourceDeclaration.create(classOrObject)
override fun getLightClassForScript(script: KtScript): KtLightClassForScript? =
KtLightClassForScript.create(script)
override fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject> {
return ResolveSessionUtils.getClassDescriptorsByFqName(traceHolder.module, fqName).mapNotNull {
val element = DescriptorToSourceUtils.getSourceFromDescriptor(it)
if (element is KtClassOrObject && PsiSearchScopeUtil.isInScope(
searchScope,
element
)
) {
element
}
else null
}
}
override fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtFile> {
return traceHolder.bindingContext.get(BindingContext.PACKAGE_TO_FILES, fqName)?.filter {
PsiSearchScopeUtil.isInScope(searchScope, it)
} ?: emptyList()
}
}
@@ -16,41 +16,15 @@
package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiSearchScopeUtil
import com.intellij.util.Function
import com.intellij.util.SmartList
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.LightClassBuilder
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolderImpl
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForScript
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.load.java.components.FilesByFacadeFqNameIndexer
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
import kotlin.properties.Delegates
import org.jetbrains.kotlin.resolve.BindingContext
/**
* This class solves the problem of interdependency between analyzing Kotlin code and generating JetLightClasses
@@ -102,179 +76,4 @@ class CliLightClassGenerationSupport(private val traceHolder: CliTraceHolder) :
override fun analyzeWithContent(element: KtClassOrObject) = traceHolder.bindingContext
}
class CliTraceHolder : CodeAnalyzerInitializer {
var bindingContext: BindingContext by Delegates.notNull()
private set
var module: ModuleDescriptor by Delegates.notNull()
private set
override fun initialize(trace: BindingTrace, module: ModuleDescriptor, codeAnalyzer: KotlinCodeAnalyzer) {
this.bindingContext = trace.bindingContext
this.module = module
if (trace !is CliBindingTrace) {
throw IllegalArgumentException("Shared trace is expected to be subclass of ${CliBindingTrace::class.java.simpleName} class")
}
trace.setKotlinCodeAnalyzer(codeAnalyzer)
}
override fun createTrace(): BindingTraceContext {
return NoScopeRecordCliBindingTrace()
}
// TODO: needs better name + list of keys to skip somewhere
class NoScopeRecordCliBindingTrace : CliBindingTrace() {
override fun <K, V> record(slice: WritableSlice<K, V>, key: K, value: V) {
if (slice === BindingContext.LEXICAL_SCOPE || slice == BindingContext.DATA_FLOW_INFO_BEFORE) {
// In the compiler there's no need to keep scopes
return
}
super.record(slice, key, value)
}
override fun toString(): String {
return NoScopeRecordCliBindingTrace::class.java.name
}
}
open class CliBindingTrace @TestOnly constructor() : BindingTraceContext() {
private var kotlinCodeAnalyzer: KotlinCodeAnalyzer? = null
override fun toString(): String {
return CliBindingTrace::class.java.name
}
fun setKotlinCodeAnalyzer(kotlinCodeAnalyzer: KotlinCodeAnalyzer) {
this.kotlinCodeAnalyzer = kotlinCodeAnalyzer
}
override fun <K, V> get(slice: ReadOnlySlice<K, V>, key: K): V? {
val value = super.get(slice, key)
if (value == null) {
if (BindingContext.FUNCTION === slice || BindingContext.VARIABLE === slice) {
if (key is KtDeclaration) {
if (!KtPsiUtil.isLocal(key)) {
kotlinCodeAnalyzer!!.resolveToDescriptor(key)
return super.get<K, V>(slice, key)
}
}
}
}
return value
}
}
}
class CliKotlinAsJavaSupport(
project: Project,
private val traceHolder: CliTraceHolder
): KotlinAsJavaSupport() {
private val psiManager = PsiManager.getInstance(project)
override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
return findFacadeFilesInPackage(packageFqName, scope)
.groupBy { it.javaFileFacadeFqName }
.mapNotNull { KtLightClassForFacade.createForFacade(psiManager, it.key, scope, it.value) }
}
override fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection<String> {
return findFacadeFilesInPackage(packageFqName, scope)
.map { it.javaFileFacadeFqName.shortName().asString() }
}
private fun findFacadeFilesInPackage(
packageFqName: FqName,
scope: GlobalSearchScope
) = traceHolder.bindingContext.get(FilesByFacadeFqNameIndexer.FACADE_FILES_BY_PACKAGE_NAME, packageFqName)
?.filter { PsiSearchScopeUtil.isInScope(scope, it) }
.orEmpty()
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val filesForFacade = findFilesForFacade(facadeFqName, scope)
if (filesForFacade.isEmpty()) return emptyList()
return listOfNotNull<PsiClass>(
KtLightClassForFacade.createForFacade(psiManager, facadeFqName, scope, filesForFacade))
}
override fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
if (scriptFqName.isRoot) {
return emptyList()
}
return findFilesForPackage(scriptFqName.parent(), scope).mapNotNull { file ->
file.script?.takeIf { it.fqName == scriptFqName }?.let { it -> getLightClassForScript(it) }
}
}
override fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
//
return emptyList()
}
override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile> {
if (facadeFqName.isRoot) return emptyList()
return traceHolder.bindingContext.get(FilesByFacadeFqNameIndexer.FACADE_FILES_BY_FQ_NAME, facadeFqName)?.filter {
PsiSearchScopeUtil.isInScope(scope, it)
} ?: emptyList()
}
override fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject> {
val files = findFilesForPackage(packageFqName, searchScope)
val result = SmartList<KtClassOrObject>()
for (file in files) {
for (declaration in file.declarations) {
if (declaration is KtClassOrObject) {
result.add(declaration)
}
}
}
return result
}
override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean {
return !traceHolder.module.getPackage(fqName).isEmpty()
}
override fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName> {
val packageView = traceHolder.module.getPackage(fqn)
val members = packageView.memberScope.getContributedDescriptors(DescriptorKindFilter.PACKAGES, MemberScope.ALL_NAME_FILTER)
return ContainerUtil.mapNotNull(members, object : Function<DeclarationDescriptor, FqName> {
override fun `fun`(member: DeclarationDescriptor): FqName? {
if (member is PackageViewDescriptor) {
return member.fqName
}
return null
}
})
}
override fun getLightClass(classOrObject: KtClassOrObject): KtLightClass? = KtLightClassForSourceDeclaration.create(classOrObject)
override fun getLightClassForScript(script: KtScript): KtLightClassForScript? = KtLightClassForScript.create(script)
override fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject> {
return ResolveSessionUtils.getClassDescriptorsByFqName(traceHolder.module, fqName).mapNotNull {
val element = DescriptorToSourceUtils.getSourceFromDescriptor(it)
if (element is KtClassOrObject && PsiSearchScopeUtil.isInScope(searchScope, element)) {
element
}
else null
}
}
override fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtFile> {
return traceHolder.bindingContext.get(BindingContext.PACKAGE_TO_FILES, fqName)?.filter {
PsiSearchScopeUtil.isInScope(searchScope, it)
} ?: emptyList()
}
}
@@ -0,0 +1,87 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.cli.jvm.compiler
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
import kotlin.properties.Delegates
class CliTraceHolder : CodeAnalyzerInitializer {
var bindingContext: BindingContext by Delegates.notNull()
private set
var module: ModuleDescriptor by Delegates.notNull()
private set
override fun initialize(trace: BindingTrace, module: ModuleDescriptor, codeAnalyzer: KotlinCodeAnalyzer) {
this.bindingContext = trace.bindingContext
this.module = module
if (trace !is CliBindingTrace) {
throw IllegalArgumentException("Shared trace is expected to be subclass of ${CliBindingTrace::class.java.simpleName} class")
}
trace.setKotlinCodeAnalyzer(codeAnalyzer)
}
override fun createTrace(): BindingTraceContext {
return NoScopeRecordCliBindingTrace()
}
}
// TODO: needs better name + list of keys to skip somewhere
class NoScopeRecordCliBindingTrace : CliBindingTrace() {
override fun <K, V> record(slice: WritableSlice<K, V>, key: K, value: V) {
if (slice === BindingContext.LEXICAL_SCOPE || slice == BindingContext.DATA_FLOW_INFO_BEFORE) {
// In the compiler there's no need to keep scopes
return
}
super.record(slice, key, value)
}
override fun toString(): String {
return NoScopeRecordCliBindingTrace::class.java.name
}
}
open class CliBindingTrace @TestOnly constructor() : BindingTraceContext() {
private var kotlinCodeAnalyzer: KotlinCodeAnalyzer? = null
override fun toString(): String {
return CliBindingTrace::class.java.name
}
fun setKotlinCodeAnalyzer(kotlinCodeAnalyzer: KotlinCodeAnalyzer) {
this.kotlinCodeAnalyzer = kotlinCodeAnalyzer
}
override fun <K, V> get(slice: ReadOnlySlice<K, V>, key: K): V? {
val value = super.get(slice, key)
if (value == null) {
if (BindingContext.FUNCTION === slice || BindingContext.VARIABLE === slice) {
if (key is KtDeclaration) {
if (!KtPsiUtil.isLocal(key)) {
kotlinCodeAnalyzer!!.resolveToDescriptor(key)
return super.get<K, V>(slice, key)
}
}
}
}
return value
}
}
@@ -372,12 +372,12 @@ object KotlinToJVMBytecodeCompiler {
// of the compiled modules (.class) to the list of scopes of the source module
val scope = if (moduleOutputs.isEmpty()) sourcesOnly else sourcesOnly.uniteWith(DirectoriesScope(project, moduleOutputs))
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
project,
sourceFiles,
CliTraceHolder.NoScopeRecordCliBindingTrace(),
environment.configuration,
environment::createPackagePartProvider,
sourceModuleSearchScope = scope
project,
sourceFiles,
NoScopeRecordCliBindingTrace(),
environment.configuration,
environment::createPackagePartProvider,
sourceModuleSearchScope = scope
)
}
@@ -22,8 +22,8 @@ import org.jetbrains.kotlin.cli.common.repl.CompiledReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ILineId
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplHistory
import org.jetbrains.kotlin.cli.jvm.compiler.CliTraceHolder
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
@@ -56,7 +56,7 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
val module: ModuleDescriptorImpl
val trace: BindingTraceContext = CliTraceHolder.NoScopeRecordCliBindingTrace()
val trace: BindingTraceContext = NoScopeRecordCliBindingTrace()
init {
// Module source scope is empty because all binary classes are in the dependency module, and all source classes are guaranteed
@@ -0,0 +1,65 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.asJava
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
abstract class KotlinAsJavaSupport {
// Returns only immediately declared classes/objects, package classes are not included (they have no declarations)
abstract fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName,
searchScope: GlobalSearchScope
): Collection<KtClassOrObject>
/*
* Finds files whose package declaration is exactly {@code fqName}. For example, if a file declares
* package a.b.c
* it will not be returned for fqName "a.b"
*
* If the resulting collection is empty, it means that this package has not other declarations than sub-packages
*/
abstract fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtFile>
abstract fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject>
abstract fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean
abstract fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName>
abstract fun getLightClass(classOrObject: KtClassOrObject): KtLightClass?
abstract fun getLightClassForScript(script: KtScript): KtLightClass?
abstract fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection<String>
abstract fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile>
companion object {
@JvmStatic
fun getInstance(project: Project): KotlinAsJavaSupport {
return ServiceManager.getService(
project,
KotlinAsJavaSupport::class.java
)
}
}
}
@@ -18,66 +18,15 @@ package org.jetbrains.kotlin.asJava
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.builder.LightClassBuilderResult
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForScript
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
typealias LightClassBuilder = (LightClassConstructionContext) -> LightClassBuilderResult
abstract class KotlinAsJavaSupport {
// Returns only immediately declared classes/objects, package classes are not included (they have no declarations)
abstract fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName,
searchScope: GlobalSearchScope
): Collection<KtClassOrObject>
/*
* Finds files whose package declaration is exactly {@code fqName}. For example, if a file declares
* package a.b.c
* it will not be returned for fqName "a.b"
*
* If the resulting collection is empty, it means that this package has not other declarations than sub-packages
*/
abstract fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtFile>
abstract fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject>
abstract fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean
abstract fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName>
abstract fun getLightClass(classOrObject: KtClassOrObject): KtLightClass?
abstract fun getLightClassForScript(script: KtScript): KtLightClassForScript?
abstract fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection<String>
abstract fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile>
companion object {
@JvmStatic
fun getInstance(project: Project): KotlinAsJavaSupport {
return ServiceManager.getService(project, KotlinAsJavaSupport::class.java)
}
}
}
abstract class LightClassGenerationSupport {
abstract fun createDataHolderForClass(classOrObject: KtClassOrObject, builder: LightClassBuilder): LightClassDataHolder.ForClass
@@ -21,7 +21,7 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.analyzer.common.CommonAnalyzerFacade
import org.jetbrains.kotlin.cli.jvm.compiler.CliTraceHolder
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.container.get
@@ -104,7 +104,7 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
val separateModules = groupedByModule.size == 1 && groupedByModule.keys.single() == null
val result = analyzeModuleContents(
moduleContext, ktFiles, CliTraceHolder.NoScopeRecordCliBindingTrace(),
moduleContext, ktFiles, NoScopeRecordCliBindingTrace(),
languageVersionSettings, separateModules, loadJvmTarget(testFilesInModule)
)
if (oldModule != result.moduleDescriptor) {
@@ -35,9 +35,9 @@ import org.jetbrains.kotlin.checkers.CheckerTestUtil;
import org.jetbrains.kotlin.checkers.CompilerTestLanguageVersionSettings;
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsKt;
import org.jetbrains.kotlin.cli.jvm.compiler.CliTraceHolder;
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace;
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.config.*;
@@ -466,7 +466,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
try {
GenerationState generationState = GenerationUtils.compileFiles(
myFiles.getPsiFiles(), myEnvironment, getClassBuilderFactory(),
new CliTraceHolder.NoScopeRecordCliBindingTrace()
new NoScopeRecordCliBindingTrace()
);
classFileFactory = generationState.getFactory();
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.codegen
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAllTo
import org.jetbrains.kotlin.cli.jvm.compiler.CliTraceHolder
import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
@@ -48,7 +48,7 @@ object GenerationUtils {
files: List<KtFile>,
environment: KotlinCoreEnvironment,
classBuilderFactory: ClassBuilderFactory = ClassBuilderFactories.TEST,
trace: BindingTrace = CliTraceHolder.CliBindingTrace()
trace: BindingTrace = CliBindingTrace()
): GenerationState =
compileFiles(files, environment.configuration, classBuilderFactory, environment::createPackagePartProvider, trace)
@@ -59,7 +59,7 @@ object GenerationUtils {
configuration: CompilerConfiguration,
classBuilderFactory: ClassBuilderFactory,
packagePartProvider: (GlobalSearchScope) -> PackagePartProvider,
trace: BindingTrace = CliTraceHolder.CliBindingTrace()
trace: BindingTrace = CliBindingTrace()
): GenerationState {
val analysisResult =
JvmResolveUtil.analyzeAndCheckForErrors(files.first().project, files, configuration, packagePartProvider, trace)
@@ -21,9 +21,9 @@ import com.intellij.openapi.util.io.FileUtil;
import junit.framework.ComparisonFailure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.analyzer.AnalysisResult;
import org.jetbrains.kotlin.cli.jvm.compiler.CliTraceHolder;
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace;
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM;
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt;
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
@@ -189,7 +189,7 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
KotlinCoreEnvironment.createForTests(getTestRootDisposable(), configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
registerJavacIfNeeded(environment);
AnalysisResult result = TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
environment.getProject(), environment.getSourceFiles(), new CliTraceHolder.NoScopeRecordCliBindingTrace(),
environment.getProject(), environment.getSourceFiles(), new NoScopeRecordCliBindingTrace(),
configuration, environment::createPackagePartProvider
);
@@ -18,8 +18,8 @@ package org.jetbrains.kotlin.renderer
import com.intellij.openapi.editor.impl.DocumentImpl
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.cli.jvm.compiler.CliTraceHolder
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
@@ -60,7 +60,7 @@ abstract class AbstractDescriptorRendererTest : KotlinTestWithEnvironment() {
val container = createContainerForLazyResolve(
context,
FileBasedDeclarationProviderFactory(context.storageManager, listOf(psiFile)),
CliTraceHolder.NoScopeRecordCliBindingTrace(),
NoScopeRecordCliBindingTrace(),
JvmPlatform,
JvmTarget.JVM_1_6,
targetEnvironment,
@@ -19,8 +19,9 @@ package org.jetbrains.kotlin.resolve.lazy
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.jvm.compiler.CliTraceHolder
import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.container.ComponentProvider
@@ -35,7 +36,7 @@ object JvmResolveUtil {
@JvmOverloads
fun createContainer(environment: KotlinCoreEnvironment, files: Collection<KtFile> = emptyList()): ComponentProvider =
TopDownAnalyzerFacadeForJVM.createContainer(
environment.project, files, CliTraceHolder.NoScopeRecordCliBindingTrace(),
environment.project, files, NoScopeRecordCliBindingTrace(),
environment.configuration, { PackagePartProvider.Empty }, ::FileBasedDeclarationProviderFactory
)
@@ -53,7 +54,7 @@ object JvmResolveUtil {
files: Collection<KtFile>,
configuration: CompilerConfiguration,
packagePartProvider: (GlobalSearchScope) -> PackagePartProvider,
trace: BindingTrace = CliTraceHolder.CliBindingTrace()
trace: BindingTrace = CliBindingTrace()
): AnalysisResult {
for (file in files) {
AnalyzingUtils.checkForSyntacticErrors(file)
@@ -85,7 +86,7 @@ object JvmResolveUtil {
files: Collection<KtFile>,
configuration: CompilerConfiguration,
packagePartProviderFactory: (GlobalSearchScope) -> PackagePartProvider,
trace: BindingTrace = CliTraceHolder.CliBindingTrace()
trace: BindingTrace = CliBindingTrace()
): AnalysisResult {
return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
project, files, trace, configuration, packagePartProviderFactory
@@ -0,0 +1,308 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.psi.impl.compiled.ClsClassImpl
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory
import org.jetbrains.kotlin.asJava.classes.*
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.caches.lightClasses.ClsJavaStubByVirtualFileCache
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
import org.jetbrains.kotlin.idea.caches.lightClasses.platformMutabilityWrapper
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
import org.jetbrains.kotlin.idea.stubindex.*
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.utils.sure
import java.util.*
class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport() {
private val psiManager: PsiManager = PsiManager.getInstance(project)
override fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection<String> {
val facadeFilesInPackage = runReadAction {
KotlinFileFacadeClassByPackageIndex.getInstance()
.get(packageFqName.asString(), project, scope)
}
return facadeFilesInPackage.map { it.javaFileFacadeFqName.shortName().asString() }
}
override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val facadeFilesInPackage = runReadAction {
KotlinFileFacadeClassByPackageIndex.getInstance()
.get(packageFqName.asString(), project, scope)
}
val groupedByFqNameAndModuleInfo = facadeFilesInPackage.groupBy {
Pair(it.javaFileFacadeFqName, it.getModuleInfo())
}
return groupedByFqNameAndModuleInfo.flatMap {
val (key, files) = it
val (fqName, moduleInfo) = key
createLightClassForFileFacade(fqName, files, moduleInfo)
}
}
override fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject> {
return runReadAction {
KotlinFullClassNameIndex.getInstance().get(
fqName.asString(),
project,
KotlinSourceFilterScope.sourceAndClassFiles(searchScope, project)
)
}
}
override fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtFile> {
return runReadAction {
PackageIndexUtil.findFilesWithExactPackage(
fqName,
KotlinSourceFilterScope.sourceAndClassFiles(
searchScope,
project
),
project
)
}
}
override fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName,
searchScope: GlobalSearchScope
): Collection<KtClassOrObject> {
return KotlinTopLevelClassByPackageIndex.getInstance().get(
packageFqName.asString(), project,
KotlinSourceFilterScope.sourceAndClassFiles(searchScope, project)
)
}
override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean {
return PackageIndexUtil.packageExists(
fqName,
KotlinSourceFilterScope.sourceAndClassFiles(
scope,
project
),
project
)
}
override fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName> {
return PackageIndexUtil.getSubPackageFqNames(
fqn,
KotlinSourceFilterScope.sourceAndClassFiles(
scope,
project
),
project,
MemberScope.ALL_NAME_FILTER
)
}
override fun getLightClass(classOrObject: KtClassOrObject): KtLightClass? {
val virtualFile = classOrObject.containingFile.virtualFile
if (virtualFile != null) {
when {
ProjectRootsUtil.isProjectSourceFile(project, virtualFile) ->
return KtLightClassForSourceDeclaration.create(classOrObject)
ProjectRootsUtil.isLibraryClassFile(project, virtualFile) ->
return getLightClassForDecompiledClassOrObject(classOrObject)
ProjectRootsUtil.isLibrarySourceFile(project, virtualFile) ->
return SourceNavigationHelper.getOriginalClass(classOrObject) as? KtLightClass
}
}
if ((classOrObject.containingFile as? KtFile)?.analysisContext != null ||
classOrObject.containingFile.originalFile.virtualFile != null
) {
// explicit request to create light class from dummy.kt
return KtLightClassForSourceDeclaration.create(classOrObject)
}
return null
}
override fun getLightClassForScript(script: KtScript): KtLightClassForScript? =
KtLightClassForScript.create(script)
private fun withFakeLightClasses(
lightClassForFacade: KtLightClassForFacade?,
facadeFiles: List<KtFile>
): List<PsiClass> {
if (lightClassForFacade == null) return emptyList()
val lightClasses = ArrayList<PsiClass>()
lightClasses.add(lightClassForFacade)
if (facadeFiles.size > 1) {
lightClasses.addAll(facadeFiles.map {
FakeLightClassForFileOfPackage(lightClassForFacade, it)
})
}
return lightClasses
}
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val filesByModule = findFilesForFacade(facadeFqName, scope).groupBy(KtFile::getModuleInfo)
return filesByModule.flatMap {
createLightClassForFileFacade(facadeFqName, it.value, it.key)
}
}
override fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
return KotlinScriptFqnIndex.instance.get(scriptFqName.asString(), project, scope).mapNotNull {
getLightClassForScript(it)
}
}
override fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
if (fqName.isRoot) return emptyList()
return findPackageParts(fqName, scope) + findPlatformWrapper(fqName, scope)
}
private fun findPackageParts(fqName: FqName, scope: GlobalSearchScope): List<KtLightClassForDecompiledDeclaration> {
val facadeKtFiles = StaticFacadeIndexUtil.getMultifileClassForPart(fqName, scope, project)
val partShortName = fqName.shortName().asString()
val partClassFileShortName = partShortName + ".class"
return facadeKtFiles.mapNotNull { facadeKtFile ->
if (facadeKtFile is KtClsFile) {
val partClassFile = facadeKtFile.virtualFile.parent.findChild(partClassFileShortName) ?: return@mapNotNull null
val javaClsClass = createClsJavaClassFromVirtualFile(facadeKtFile, partClassFile, null) ?: return@mapNotNull null
KtLightClassForDecompiledDeclaration(javaClsClass, null, facadeKtFile)
} else {
// TODO should we build light classes for parts from source?
null
}
}
}
private fun findPlatformWrapper(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
return platformMutabilityWrapper(fqName) {
JavaPsiFacade.getInstance(
project
).findClass(it, scope)
}?.let { listOf(it) }.orEmpty()
}
fun createLightClassForFileFacade(
facadeFqName: FqName,
facadeFiles: List<KtFile>,
moduleInfo: IdeaModuleInfo
): List<PsiClass> {
val (clsFiles, sourceFiles) = facadeFiles.partition { it is KtClsFile }
val lightClassesForClsFacades = clsFiles.mapNotNull { createLightClassForDecompiledKotlinFile(it as KtClsFile) }
if (moduleInfo is ModuleSourceInfo && sourceFiles.isNotEmpty()) {
val lightClassForFacade = KtLightClassForFacade.createForFacade(
psiManager, facadeFqName, moduleInfo.contentScope(), sourceFiles
)
return withFakeLightClasses(lightClassForFacade, sourceFiles) + lightClassesForClsFacades
} else {
return lightClassesForClsFacades
}
}
override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile> {
return runReadAction {
KotlinFileFacadeFqNameIndex.INSTANCE.get(facadeFqName.asString(), project, scope)
}
}
private fun getLightClassForDecompiledClassOrObject(decompiledClassOrObject: KtClassOrObject): KtLightClassForDecompiledDeclaration? {
if (decompiledClassOrObject is KtEnumEntry) {
return null
}
val containingKtFile = decompiledClassOrObject.containingFile as? KtClsFile ?: return null
val rootLightClassForDecompiledFile = createLightClassForDecompiledKotlinFile(containingKtFile) ?: return null
return findCorrespondingLightClass(decompiledClassOrObject, rootLightClassForDecompiledFile)
}
private fun findCorrespondingLightClass(
decompiledClassOrObject: KtClassOrObject,
rootLightClassForDecompiledFile: KtLightClassForDecompiledDeclaration
): KtLightClassForDecompiledDeclaration {
val relativeFqName = getClassRelativeName(decompiledClassOrObject)
val iterator = relativeFqName.pathSegments().iterator()
val base = iterator.next()
assert(rootLightClassForDecompiledFile.name == base.asString()) { "Light class for file:\n" + decompiledClassOrObject.containingKtFile.virtualFile.canonicalPath + "\nwas expected to have name: " + base.asString() + "\n Actual: " + rootLightClassForDecompiledFile.name }
var current: KtLightClassForDecompiledDeclaration = rootLightClassForDecompiledFile
while (iterator.hasNext()) {
val name = iterator.next()
val innerClass = current.findInnerClassByName(name.asString(), false).sure {
"Could not find corresponding inner/nested class " + relativeFqName + " in class " + decompiledClassOrObject.fqName + "\n" + "File: " + decompiledClassOrObject.containingKtFile.virtualFile.name
}
current = innerClass as KtLightClassForDecompiledDeclaration
}
return current
}
private fun getClassRelativeName(decompiledClassOrObject: KtClassOrObject): FqName {
val name = decompiledClassOrObject.nameAsName!!
val parent = PsiTreeUtil.getParentOfType(
decompiledClassOrObject,
KtClassOrObject::class.java,
true
)
if (parent == null) {
assert(decompiledClassOrObject.isTopLevel())
return FqName.topLevel(name)
}
return getClassRelativeName(parent).child(name)
}
private fun createLightClassForDecompiledKotlinFile(file: KtClsFile): KtLightClassForDecompiledDeclaration? {
val virtualFile = file.virtualFile ?: return null
val classOrObject = file.declarations.filterIsInstance<KtClassOrObject>().singleOrNull()
val javaClsClass = createClsJavaClassFromVirtualFile(
file, virtualFile,
correspondingClassOrObject = classOrObject
) ?: return null
return KtLightClassForDecompiledDeclaration(javaClsClass, classOrObject, file)
}
private fun createClsJavaClassFromVirtualFile(
mirrorFile: KtFile,
classFile: VirtualFile,
correspondingClassOrObject: KtClassOrObject?
): ClsClassImpl? {
val javaFileStub = ClsJavaStubByVirtualFileCache.getInstance(project).get(classFile) ?: return null
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
val manager = PsiManager.getInstance(mirrorFile.project)
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, classFile)) {
override fun getNavigationElement(): PsiElement {
if (correspondingClassOrObject != null) {
return correspondingClassOrObject.navigationElement.containingFile
}
return super.getNavigationElement()
}
override fun getStub() = javaFileStub
override fun getMirror() = mirrorFile
override fun isPhysical() = false
}
javaFileStub.psi = fakeFile
return fakeFile.classes.single() as ClsClassImpl
}
}
@@ -17,41 +17,19 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.psi.impl.compiled.ClsClassImpl
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.LightClassBuilder
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.classes.*
import org.jetbrains.kotlin.asJava.classes.shouldNotBeVisibleAsLightClass
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.caches.lightClasses.*
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
import org.jetbrains.kotlin.idea.stubindex.*
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope.Companion.sourceAndClassFiles
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.idea.caches.lightClasses.IDELightClassContexts
import org.jetbrains.kotlin.idea.caches.lightClasses.LazyLightClassDataHolder
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.utils.sure
import java.util.*
class IDELightClassGenerationSupport(project: Project) : LightClassGenerationSupport() {
private val scopeFileComparator = JavaElementFinder.byClasspathComparator(GlobalSearchScope.allScope(project))
@@ -113,276 +91,3 @@ class IDELightClassGenerationSupport(project: Project) : LightClassGenerationSup
override fun analyzeWithContent(element: KtClassOrObject) = element.analyzeWithContent()
}
class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport() {
private val psiManager: PsiManager = PsiManager.getInstance(project)
override fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection<String> {
val facadeFilesInPackage = runReadAction {
KotlinFileFacadeClassByPackageIndex.getInstance().get(packageFqName.asString(), project, scope)
}
return facadeFilesInPackage.map { it.javaFileFacadeFqName.shortName().asString() }
}
override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val facadeFilesInPackage = runReadAction {
KotlinFileFacadeClassByPackageIndex.getInstance().get(packageFqName.asString(), project, scope)
}
val groupedByFqNameAndModuleInfo = facadeFilesInPackage.groupBy {
Pair(it.javaFileFacadeFqName, it.getModuleInfo())
}
return groupedByFqNameAndModuleInfo.flatMap {
val (key, files) = it
val (fqName, moduleInfo) = key
createLightClassForFileFacade(fqName, files, moduleInfo)
}
}
override fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject> {
return runReadAction {
KotlinFullClassNameIndex.getInstance().get(fqName.asString(), project, sourceAndClassFiles(searchScope, project))
}
}
override fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtFile> {
return runReadAction {
PackageIndexUtil.findFilesWithExactPackage(fqName, sourceAndClassFiles(searchScope, project), project)
}
}
override fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName,
searchScope: GlobalSearchScope
): Collection<KtClassOrObject> {
return KotlinTopLevelClassByPackageIndex.getInstance().get(
packageFqName.asString(), project, sourceAndClassFiles(searchScope, project)
)
}
override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean {
return PackageIndexUtil.packageExists(fqName, sourceAndClassFiles(scope, project), project)
}
override fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName> {
return PackageIndexUtil.getSubPackageFqNames(fqn, sourceAndClassFiles(scope, project), project, MemberScope.ALL_NAME_FILTER)
}
override fun getLightClass(classOrObject: KtClassOrObject): KtLightClass? {
val virtualFile = classOrObject.containingFile.virtualFile
if (virtualFile != null) {
when {
ProjectRootsUtil.isProjectSourceFile(project, virtualFile) ->
return KtLightClassForSourceDeclaration.create(classOrObject)
ProjectRootsUtil.isLibraryClassFile(project, virtualFile) ->
return getLightClassForDecompiledClassOrObject(classOrObject)
ProjectRootsUtil.isLibrarySourceFile(project, virtualFile) ->
return SourceNavigationHelper.getOriginalClass(classOrObject) as? KtLightClass
}
}
if ((classOrObject.containingFile as? KtFile)?.analysisContext != null ||
classOrObject.containingFile.originalFile.virtualFile != null
) {
// explicit request to create light class from dummy.kt
return KtLightClassForSourceDeclaration.create(classOrObject)
}
return null
}
override fun getLightClassForScript(script: KtScript): KtLightClassForScript? = KtLightClassForScript.create(script)
private fun withFakeLightClasses(
lightClassForFacade: KtLightClassForFacade?,
facadeFiles: List<KtFile>
): List<PsiClass> {
if (lightClassForFacade == null) return emptyList()
val lightClasses = ArrayList<PsiClass>()
lightClasses.add(lightClassForFacade)
if (facadeFiles.size > 1) {
lightClasses.addAll(facadeFiles.map {
FakeLightClassForFileOfPackage(lightClassForFacade, it)
})
}
return lightClasses
}
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val filesByModule = findFilesForFacade(facadeFqName, scope).groupBy(KtFile::getModuleInfo)
return filesByModule.flatMap {
createLightClassForFileFacade(facadeFqName, it.value, it.key)
}
}
override fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
return KotlinScriptFqnIndex.instance.get(scriptFqName.asString(), project, scope).mapNotNull {
getLightClassForScript(it)
}
}
override fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
if (fqName.isRoot) return emptyList()
return findPackageParts(fqName, scope) + findPlatformWrapper(fqName, scope)
}
private fun findPackageParts(fqName: FqName, scope: GlobalSearchScope): List<KtLightClassForDecompiledDeclaration> {
val facadeKtFiles = StaticFacadeIndexUtil.getMultifileClassForPart(fqName, scope, project)
val partShortName = fqName.shortName().asString()
val partClassFileShortName = partShortName + ".class"
return facadeKtFiles.mapNotNull { facadeKtFile ->
if (facadeKtFile is KtClsFile) {
val partClassFile = facadeKtFile.virtualFile.parent.findChild(partClassFileShortName) ?: return@mapNotNull null
val javaClsClass = createClsJavaClassFromVirtualFile(facadeKtFile, partClassFile, null) ?: return@mapNotNull null
KtLightClassForDecompiledDeclaration(javaClsClass, null, facadeKtFile)
} else {
// TODO should we build light classes for parts from source?
null
}
}
}
private fun findPlatformWrapper(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
return platformMutabilityWrapper(fqName) { JavaPsiFacade.getInstance(project).findClass(it, scope) }?.let { listOf(it) }.orEmpty()
}
fun createLightClassForFileFacade(
facadeFqName: FqName,
facadeFiles: List<KtFile>,
moduleInfo: IdeaModuleInfo
): List<PsiClass> {
val (clsFiles, sourceFiles) = facadeFiles.partition { it is KtClsFile }
val lightClassesForClsFacades = clsFiles.mapNotNull { createLightClassForDecompiledKotlinFile(it as KtClsFile) }
if (moduleInfo is ModuleSourceInfo && sourceFiles.isNotEmpty()) {
val lightClassForFacade = KtLightClassForFacade.createForFacade(
psiManager, facadeFqName, moduleInfo.contentScope(), sourceFiles
)
return withFakeLightClasses(lightClassForFacade, sourceFiles) + lightClassesForClsFacades
} else {
return lightClassesForClsFacades
}
}
override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile> {
return runReadAction {
KotlinFileFacadeFqNameIndex.INSTANCE.get(facadeFqName.asString(), project, scope)
}
}
private fun getLightClassForDecompiledClassOrObject(decompiledClassOrObject: KtClassOrObject): KtLightClassForDecompiledDeclaration? {
if (decompiledClassOrObject is KtEnumEntry) {
return null
}
val containingKtFile = decompiledClassOrObject.containingFile as? KtClsFile ?: return null
val rootLightClassForDecompiledFile = createLightClassForDecompiledKotlinFile(containingKtFile) ?: return null
return findCorrespondingLightClass(decompiledClassOrObject, rootLightClassForDecompiledFile)
}
private fun findCorrespondingLightClass(
decompiledClassOrObject: KtClassOrObject,
rootLightClassForDecompiledFile: KtLightClassForDecompiledDeclaration
): KtLightClassForDecompiledDeclaration {
val relativeFqName = getClassRelativeName(decompiledClassOrObject)
val iterator = relativeFqName.pathSegments().iterator()
val base = iterator.next()
assert(rootLightClassForDecompiledFile.name == base.asString()) { "Light class for file:\n" + decompiledClassOrObject.containingKtFile.virtualFile.canonicalPath + "\nwas expected to have name: " + base.asString() + "\n Actual: " + rootLightClassForDecompiledFile.name }
var current: KtLightClassForDecompiledDeclaration = rootLightClassForDecompiledFile
while (iterator.hasNext()) {
val name = iterator.next()
val innerClass = current.findInnerClassByName(name.asString(), false).sure {
"Could not find corresponding inner/nested class " + relativeFqName + " in class " + decompiledClassOrObject.fqName + "\n" + "File: " + decompiledClassOrObject.containingKtFile.virtualFile.name
}
current = innerClass as KtLightClassForDecompiledDeclaration
}
return current
}
private fun getClassRelativeName(decompiledClassOrObject: KtClassOrObject): FqName {
val name = decompiledClassOrObject.nameAsName!!
val parent = PsiTreeUtil.getParentOfType(decompiledClassOrObject, KtClassOrObject::class.java, true)
if (parent == null) {
assert(decompiledClassOrObject.isTopLevel())
return FqName.topLevel(name)
}
return getClassRelativeName(parent).child(name)
}
private fun createLightClassForDecompiledKotlinFile(file: KtClsFile): KtLightClassForDecompiledDeclaration? {
val virtualFile = file.virtualFile ?: return null
val classOrObject = file.declarations.filterIsInstance<KtClassOrObject>().singleOrNull()
val javaClsClass = createClsJavaClassFromVirtualFile(
file, virtualFile,
correspondingClassOrObject = classOrObject
) ?: return null
return KtLightClassForDecompiledDeclaration(javaClsClass, classOrObject, file)
}
private fun createClsJavaClassFromVirtualFile(
mirrorFile: KtFile,
classFile: VirtualFile,
correspondingClassOrObject: KtClassOrObject?
): ClsClassImpl? {
val javaFileStub = ClsJavaStubByVirtualFileCache.getInstance(project).get(classFile) ?: return null
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
val manager = PsiManager.getInstance(mirrorFile.project)
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, classFile)) {
override fun getNavigationElement(): PsiElement {
if (correspondingClassOrObject != null) {
return correspondingClassOrObject.navigationElement.containingFile
}
return super.getNavigationElement()
}
override fun getStub() = javaFileStub
override fun getMirror() = mirrorFile
override fun isPhysical() = false
}
javaFileStub.psi = fakeFile
return fakeFile.classes.single() as ClsClassImpl
}
}
class KtFileClassProviderImpl(val kotlinAsJavaSupport: KotlinAsJavaSupport) : KtFileClassProvider {
override fun getFileClasses(file: KtFile): Array<PsiClass> {
// TODO We don't currently support finding light classes for scripts
if (file.isCompiled || file.isScript()) {
return PsiClass.EMPTY_ARRAY
}
val result = arrayListOf<PsiClass>()
file.declarations.filterIsInstance<KtClassOrObject>().map { it.toLightClass() }.filterNotNullTo(result)
val moduleInfo = file.getModuleInfo()
val jvmClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file)
val fileClassFqName = file.javaFileFacadeFqName
val facadeClasses = when {
file.analysisContext != null && file.hasTopLevelCallables() ->
listOf(KtLightClassForFacade.createForSyntheticFile(PsiManager.getInstance(file.project), fileClassFqName, file))
jvmClassInfo.withJvmMultifileClass ->
kotlinAsJavaSupport.getFacadeClasses(fileClassFqName, moduleInfo.contentScope())
file.hasTopLevelCallables() ->
(kotlinAsJavaSupport as IDEKotlinAsJavaSupport).createLightClassForFileFacade(
fileClassFqName, listOf(file), moduleInfo
)
else -> emptyList<PsiClass>()
}
facadeClasses.filterTo(result) {
it is KtLightClassForFacade && file in it.files
}
return result.toTypedArray()
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtFileClassProvider
import org.jetbrains.kotlin.psi.analysisContext
class KtFileClassProviderImpl(val kotlinAsJavaSupport: KotlinAsJavaSupport) :
KtFileClassProvider {
override fun getFileClasses(file: KtFile): Array<PsiClass> {
// TODO We don't currently support finding light classes for scripts
if (file.isCompiled || file.isScript()) {
return PsiClass.EMPTY_ARRAY
}
val result = arrayListOf<PsiClass>()
file.declarations.filterIsInstance<KtClassOrObject>().map { it.toLightClass() }.filterNotNullTo(result)
val moduleInfo = file.getModuleInfo()
val jvmClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file)
val fileClassFqName = file.javaFileFacadeFqName
val facadeClasses = when {
file.analysisContext != null && file.hasTopLevelCallables() ->
listOf(
KtLightClassForFacade.createForSyntheticFile(
PsiManager.getInstance(
file.project
), fileClassFqName, file
)
)
jvmClassInfo.withJvmMultifileClass ->
kotlinAsJavaSupport.getFacadeClasses(fileClassFqName, moduleInfo.contentScope())
file.hasTopLevelCallables() ->
(kotlinAsJavaSupport as IDEKotlinAsJavaSupport).createLightClassForFileFacade(
fileClassFqName, listOf(file), moduleInfo
)
else -> emptyList<PsiClass>()
}
facadeClasses.filterTo(result) {
it is KtLightClassForFacade && file in it.files
}
return result.toTypedArray()
}
}
@@ -11,9 +11,9 @@ import com.intellij.util.io.URLUtil
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
@@ -49,7 +49,7 @@ abstract class AbstractKotlinUastTest : AbstractUastTest() {
initializeKotlinEnvironment()
val trace = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace()
val trace = NoScopeRecordCliBindingTrace()
val environment = kotlinCoreEnvironment!!
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(