Light classes: Refactor construction

Introduce LightClassDataHolder: which now is reponsible for constructing clsDelegate
Move out light big chunk of delegate building logic out of LightClassDataProvider into LightClassBuilder
LightClassData only holds information about single class
This commit is contained in:
Pavel V. Talanov
2017-02-16 15:33:23 +03:00
parent 22fb9ec5e1
commit 00e84fb483
8 changed files with 314 additions and 280 deletions
@@ -0,0 +1,127 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.asJava.builder
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
import com.intellij.psi.stubs.StubElement
import com.intellij.util.containers.Stack
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
data class LightClassBuilderResult(val stub: PsiJavaFileStub, val bindingContext: BindingContext, val diagnostics: Diagnostics)
fun buildLightClass(
project: Project,
packageFqName: FqName,
files: Collection<KtFile>,
generateClassFilter: GenerationState.GenerateClassFilter,
context: LightClassConstructionContext,
generate: (state: GenerationState, files: Collection<KtFile>) -> Unit
): LightClassBuilderResult {
val javaFileStub = createJavaFileStub(project, packageFqName, files)
val bindingContext: BindingContext
val state: GenerationState
try {
val stubStack = Stack<StubElement<PsiElement>>()
@Suppress("UNCHECKED_CAST")
stubStack.push(javaFileStub as StubElement<PsiElement>)
state = GenerationState(
project,
KotlinLightClassBuilderFactory(stubStack),
context.module,
context.bindingContext,
files.toMutableList(),
CompilerConfiguration.EMPTY,
generateClassFilter,
wantsDiagnostics = false
)
state.beforeCompile()
bindingContext = state.bindingContext
generate(state, files)
val pop = stubStack.pop()
if (pop !== javaFileStub) {
LOG.error("Unbalanced stack operations: " + pop)
}
ServiceManager.getService(project, StubComputationTracker::class.java)?.onStubComputed(javaFileStub)
return LightClassBuilderResult(javaFileStub, bindingContext, state.collectedExtraJvmDiagnostics)
}
catch (e: ProcessCanceledException) {
throw e
}
catch (e: RuntimeException) {
logErrorWithOSInfo(e, packageFqName, null)
throw e
}
}
private fun createJavaFileStub(project: Project, packageFqName: FqName, files: Collection<KtFile>): PsiJavaFileStub {
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), true)
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
val manager = PsiManager.getInstance(project)
val virtualFile = getRepresentativeVirtualFile(files)
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) {
override fun getStub() = javaFileStub
override fun getPackageName() = packageFqName.asString()
override fun isPhysical() = false
}
javaFileStub.psi = fakeFile
return javaFileStub
}
private fun getRepresentativeVirtualFile(files: Collection<KtFile>): VirtualFile {
return files.first().viewProvider.virtualFile
}
private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) {
val path = if (virtualFile == null) "<null>" else virtualFile.path
LOG.error(
"Could not generate LightClass for $fqName declared in $path\n" +
"System: ${SystemInfo.OS_NAME} ${SystemInfo.OS_VERSION} Java Runtime: ${SystemInfo.JAVA_RUNTIME_VERSION}",
cause
)
}
private val LOG = Logger.getInstance(LightClassDataProvider::class.java)
@@ -1,39 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.asJava.builder
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
interface LightClassData {
val javaFileStub: PsiJavaFileStub
val extraDiagnostics: Diagnostics
}
object InvalidLightClassData: LightClassData {
override val javaFileStub: PsiJavaFileStub
get() = shouldNotBeCalled()
override val extraDiagnostics: Diagnostics
get() = shouldNotBeCalled()
private fun shouldNotBeCalled(): Nothing = throw UnsupportedOperationException("Should not be called")
}
data class LightClassDataImpl(
override val javaFileStub: PsiJavaFileStub,
override val extraDiagnostics: Diagnostics
): LightClassData
@@ -0,0 +1,97 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.asJava.builder
import com.intellij.psi.PsiClass
import com.intellij.psi.impl.DebugUtil
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.stubs.StubElement
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.LightClassUtil.findClass
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder.javaFileStub
import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
interface LightClassDataHolder {
val javaFileStub: PsiJavaFileStub
val extraDiagnostics: Diagnostics
fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData
fun findData(classOrObject: KtClassOrObject): LightClassData = findData { it.findDelegate(classOrObject) }
fun findData(classFqName: FqName): LightClassData = findData { it.findDelegate(classFqName) }
}
interface LightClassData {
val clsDelegate: PsiClass
}
class LightClassDataImpl(override val clsDelegate: PsiClass) : LightClassData {
}
object InvalidLightClassDataHolder : LightClassDataHolder {
override val javaFileStub: PsiJavaFileStub
get() = shouldNotBeCalled()
override val extraDiagnostics: Diagnostics
get() = shouldNotBeCalled()
override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass) = shouldNotBeCalled()
private fun shouldNotBeCalled(): Nothing = throw UnsupportedOperationException("Should not be called")
}
class LightClassDataHolderImpl(
override val javaFileStub: PsiJavaFileStub,
override val extraDiagnostics: Diagnostics
) : LightClassDataHolder {
override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass) = findDelegate(javaFileStub).let(::LightClassDataImpl)
}
fun PsiJavaFileStub.findDelegate(classOrObject: KtClassOrObject): PsiClass {
findClass(this) {
ClsWrapperStubPsiFactory.getOriginalElement(it as StubElement<*>) == classOrObject
}?.let { return it }
val outermostClassOrObject = getOutermostClassOrObject(classOrObject)
val ktFileText: String? = try {
outermostClassOrObject.containingFile.text
}
catch (e: Exception) {
"Can't get text for outermost class"
}
val stubFileText = DebugUtil.stubTreeToString(this)
throw IllegalStateException("Couldn't get delegate for $this\nin $ktFileText\nstub: \n$stubFileText")
}
fun PsiJavaFileStub.findDelegate(classFqName: FqName): PsiClass {
return findClass(this) {
classFqName.asString() == it.qualifiedName
} ?: throw IllegalStateException("Facade class $classFqName not found; classes in Java file stub: ${collectClassNames(javaFileStub)}")
}
private fun collectClassNames(javaFileStub: PsiJavaFileStub): String {
val names = mutableListOf<String>()
LightClassUtil.findClass(javaFileStub) { cls ->
names.add(cls.qualifiedName ?: "<null>")
false
}
return names.joinToString(prefix = "[", postfix = "]")
}
@@ -16,30 +16,17 @@
package org.jetbrains.kotlin.asJava.builder
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.StubElement
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.containers.Stack
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
import org.jetbrains.kotlin.fileClasses.getFileClassType
import org.jetbrains.kotlin.name.FqName
@@ -49,150 +36,37 @@ import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
abstract class LightClassDataProvider<T : LightClassData>(
abstract class LightClassDataProvider<T : LightClassDataHolder>(
private val project: Project
) : CachedValueProvider<T> {
abstract val files: Collection<KtFile>
abstract val packageFqName: FqName
abstract fun getContext(files: Collection<KtFile>): LightClassConstructionContext
abstract fun createLightClassData(javaFileStub: PsiJavaFileStub, bindingContext: BindingContext, extraDiagnostics: Diagnostics): T
abstract val generateClassFilter: GenerationState.GenerateClassFilter
abstract fun generate(state: GenerationState, files: Collection<KtFile>)
abstract val isLocal: Boolean
open val valueAbsent: Boolean = false
override fun compute(): CachedValueProvider.Result<T>? {
if (files.isEmpty()) return null
if (valueAbsent) return null
return CachedValueProvider.Result.create(
computeLightClassData(),
if (isLocal) PsiModificationTracker.MODIFICATION_COUNT else PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
)
}
private fun computeLightClassData(): T {
val packageFqName = packageFqName
val files = files
val context = getContext(files)
val javaFileStub = createJavaFileStub(packageFqName, files)
val bindingContext: BindingContext
val state: GenerationState
try {
val stubStack = Stack<StubElement<PsiElement>>()
@Suppress("UNCHECKED_CAST")
stubStack.push(javaFileStub as StubElement<PsiElement>)
state = GenerationState(
project,
KotlinLightClassBuilderFactory(stubStack),
context.module,
context.bindingContext,
files.toMutableList(),
CompilerConfiguration.EMPTY,
generateClassFilter,
wantsDiagnostics = false
)
state.beforeCompile()
bindingContext = state.bindingContext
generate(state, files)
val pop = stubStack.pop()
if (pop !== javaFileStub) {
LOG.error("Unbalanced stack operations: " + pop)
}
ServiceManager.getService(project, StubComputationTracker::class.java)?.onStubComputed(javaFileStub)
return createLightClassData(javaFileStub, bindingContext, state.collectedExtraJvmDiagnostics)
}
catch (e: ProcessCanceledException) {
throw e
}
catch (e: RuntimeException) {
logErrorWithOSInfo(e, packageFqName, null)
throw e
}
}
private fun createJavaFileStub(packageFqName: FqName, files: Collection<KtFile>): PsiJavaFileStub {
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), true)
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
val manager = PsiManager.getInstance(project)
val virtualFile = getRepresentativeVirtualFile(files)
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) {
override fun getStub() = javaFileStub
override fun getPackageName() = packageFqName.asString()
override fun isPhysical() = false
}
javaFileStub.psi = fakeFile
return javaFileStub
}
private fun getRepresentativeVirtualFile(files: Collection<KtFile>): VirtualFile {
return files.first().viewProvider.virtualFile
}
private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) {
val path = if (virtualFile == null) "<null>" else virtualFile.path
LOG.error(
"Could not generate LightClass for $fqName declared in $path\n" +
"System: ${SystemInfo.OS_NAME} ${SystemInfo.OS_VERSION} Java Runtime: ${SystemInfo.JAVA_RUNTIME_VERSION}",
cause
)
}
companion object {
private val LOG = Logger.getInstance(LightClassDataProvider::class.java)
}
abstract fun computeLightClassData(): T
}
class LightClassDataProviderForClassOrObject(private val classOrObject: KtClassOrObject) :
LightClassDataProvider<LightClassData>(classOrObject.project) {
private val file: KtFile
get() = classOrObject.containingKtFile
LightClassDataProvider<LightClassDataHolder>(classOrObject.project) {
override val isLocal: Boolean get() = classOrObject.isLocal
override fun getContext(files: Collection<KtFile>): LightClassConstructionContext {
return LightClassGenerationSupport.getInstance(classOrObject.project).getContextForClassOrObject(classOrObject)
}
override fun createLightClassData(
javaFileStub: PsiJavaFileStub,
bindingContext: BindingContext,
extraDiagnostics: Diagnostics): LightClassData {
bindingContext.get(BindingContext.CLASS, classOrObject) ?: return InvalidLightClassData
override fun computeLightClassData(): LightClassDataHolder {
val file = classOrObject.containingKtFile
return LightClassDataImpl(
javaFileStub,
extraDiagnostics
)
}
override val files: Collection<KtFile>
get() = listOf(file)
override val packageFqName: FqName
get() = file.packageFqName
override val generateClassFilter: GenerationState.GenerateClassFilter
get() = object : GenerationState.GenerateClassFilter() {
val packageFqName = file.packageFqName
val generateClassFilter = object : GenerationState.GenerateClassFilter() {
override fun shouldGeneratePackagePart(jetFile: KtFile): Boolean {
return true
@@ -242,13 +116,24 @@ class LightClassDataProviderForClassOrObject(private val classOrObject: KtClassO
}
}
override fun generate(state: GenerationState, files: Collection<KtFile>) {
val packageCodegen = state.factory.forPackage(packageFqName, files)
val file = classOrObject.containingKtFile
val packagePartType = state.fileClassesProvider.getFileClassType(file)
val context = state.rootContext.intoPackagePart(packageCodegen.packageFragment, packagePartType, file)
packageCodegen.generateClassOrObject(getOutermostClassOrObject(classOrObject), context)
state.factory.asList()
val classConstructionContext = LightClassGenerationSupport.getInstance(classOrObject.project).getContextForClassOrObject(classOrObject)
val (stub, bindingContext, diagnostics) = buildLightClass(classOrObject.project, packageFqName, listOf(file), generateClassFilter, classConstructionContext) {
state, files ->
val packageCodegen = state.factory.forPackage(packageFqName, files)
val packagePartType = state.fileClassesProvider.getFileClassType(file)
val context = state.rootContext.intoPackagePart(packageCodegen.packageFragment, packagePartType, file)
packageCodegen.generateClassOrObject(getOutermostClassOrObject(classOrObject), context)
state.factory.asList()
}
bindingContext.get(BindingContext.CLASS, classOrObject) ?: return InvalidLightClassDataHolder
return LightClassDataHolderImpl(
stub,
diagnostics
)
}
override fun toString(): String {
@@ -256,28 +141,17 @@ class LightClassDataProviderForClassOrObject(private val classOrObject: KtClassO
}
}
sealed class LightClassDataProviderForFileFacade private constructor(
sealed class LightClassDataProviderForFileFacade constructor(
protected val project: Project, protected val facadeFqName: FqName
) : LightClassDataProvider<LightClassDataImpl>(project) {
override val packageFqName: FqName
get() = facadeFqName.parent()
) : LightClassDataProvider<LightClassDataHolderImpl>(project) {
override val isLocal: Boolean get() = false
abstract val files: Collection<KtFile>
override fun getContext(files: Collection<KtFile>): LightClassConstructionContext {
return LightClassGenerationSupport.getInstance(project).getContextForFacade(files)
}
override val valueAbsent: Boolean
get() = files.isEmpty()
override fun createLightClassData(
javaFileStub: PsiJavaFileStub,
bindingContext: BindingContext,
extraDiagnostics: Diagnostics
): LightClassDataImpl {
return LightClassDataImpl(javaFileStub, extraDiagnostics)
}
override val generateClassFilter: GenerationState.GenerateClassFilter
get() = object : GenerationState.GenerateClassFilter() {
override fun computeLightClassData(): LightClassDataHolderImpl {
val generateClassFilter = object : GenerationState.GenerateClassFilter() {
override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject): Boolean {
return shouldGenerateClass(processingClassOrObject)
}
@@ -295,21 +169,27 @@ sealed class LightClassDataProviderForFileFacade private constructor(
}
}
override fun generate(state: GenerationState, files: Collection<KtFile>) {
if (!files.isEmpty()) {
val representativeFile = files.iterator().next()
val fileClassInfo = NoResolveFileClassesProvider.getFileClassInfo(representativeFile)
if (!fileClassInfo.withJvmMultifileClass) {
val codegen = state.factory.forPackage(representativeFile.packageFqName, files)
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION)
state.factory.asList()
return
val packageFqName = facadeFqName.parent()
val (stub, _, diagnostics) = buildLightClass(project, packageFqName, files, generateClassFilter, LightClassGenerationSupport.getInstance(project).getContextForFacade(files)) generate@ {
state, files ->
if (!files.isEmpty()) {
val representativeFile = files.iterator().next()
val fileClassInfo = NoResolveFileClassesProvider.getFileClassInfo(representativeFile)
if (!fileClassInfo.withJvmMultifileClass) {
val codegen = state.factory.forPackage(representativeFile.packageFqName, files)
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION)
state.factory.asList()
return@generate
}
}
val codegen = state.factory.forMultifileClass(facadeFqName, files)
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION)
state.factory.asList()
}
val codegen = state.factory.forMultifileClass(facadeFqName, files)
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION)
state.factory.asList()
return LightClassDataHolderImpl(stub, diagnostics)
}
override fun toString(): String {
@@ -20,7 +20,6 @@ import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Comparing
import com.intellij.psi.*
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.light.LightEmptyImplementsList
import com.intellij.psi.impl.light.LightModifierList
import com.intellij.psi.search.GlobalSearchScope
@@ -30,8 +29,7 @@ import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.SLRUCache
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.builder.LightClassDataImpl
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassDataProviderForFileFacade
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
@@ -51,17 +49,17 @@ import javax.swing.Icon
class KtLightClassForFacade private constructor(
manager: PsiManager,
private val facadeClassFqName: FqName,
private val lightClassDataCache: CachedValue<LightClassDataImpl>,
private val lightClassDataCache: CachedValue<LightClassDataHolder>,
files: Collection<KtFile>
) : KtLightClassBase(manager) {
private data class StubCacheKey(val fqName: FqName, val searchScope: GlobalSearchScope)
class FacadeStubCache(private val project: Project) {
private inner class FacadeCacheData {
val cache = object : SLRUCache<StubCacheKey, CachedValue<LightClassDataImpl>>(20, 30) {
override fun createValue(key: StubCacheKey): CachedValue<LightClassDataImpl> {
val cache = object : SLRUCache<StubCacheKey, CachedValue<LightClassDataHolder>>(20, 30) {
override fun createValue(key: StubCacheKey): CachedValue<LightClassDataHolder> {
val stubProvider = LightClassDataProviderForFileFacade.ByProjectSource(project, key.fqName, key.searchScope)
return CachedValuesManager.getManager(project).createCachedValue<LightClassDataImpl>(stubProvider, /*trackValue = */false)
return CachedValuesManager.getManager(project).createCachedValue<LightClassDataHolder>(stubProvider, /*trackValue = */false)
}
}
}
@@ -70,7 +68,7 @@ class KtLightClassForFacade private constructor(
{ CachedValueProvider.Result.create(FacadeCacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) },
/*trackValue = */ false)
operator fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue<LightClassDataImpl> {
operator fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue<LightClassDataHolder> {
synchronized (cachedValue) {
return cachedValue.value.cache.get(StubCacheKey(qualifiedName, searchScope))
}
@@ -197,21 +195,7 @@ class KtLightClassForFacade private constructor(
override fun copy() = KtLightClassForFacade(manager, facadeClassFqName, lightClassDataCache, files)
override val clsDelegate: PsiClass
get() {
val javaFileStub = lightClassDataCache.value.javaFileStub
return LightClassUtil.findClass(javaFileStub) {
facadeClassFqName.asString() == it.qualifiedName
} ?: throw IllegalStateException("Facade class $facadeClassFqName not found; classes in Java file stub: ${collectClassNames(javaFileStub)}")
}
private fun collectClassNames(javaFileStub: PsiJavaFileStub): String {
val names = mutableListOf<String>()
LightClassUtil.findClass(javaFileStub) { cls ->
names.add(cls.qualifiedName ?: "<null>")
false
}
return names.joinToString(prefix = "[", postfix = "]")
}
get() = lightClassDataCache.value.findData(facadeClassFqName).clsDelegate
override fun getNavigationElement() = files.iterator().next()
@@ -284,7 +268,7 @@ class KtLightClassForFacade private constructor(
): KtLightClassForFacade {
// TODO: refactor, using cached value doesn't make sense for this case
val cachedValue = CachedValuesManager.getManager(manager.project).
createCachedValue<LightClassDataImpl>(
createCachedValue<LightClassDataHolder>(
LightClassDataProviderForFileFacade.ByFile(manager.project, facadeClassFqName, file), /*trackValue = */false
)
return KtLightClassForFacade(manager, facadeClassFqName, cachedValue, listOf(file))
@@ -17,17 +17,16 @@
package org.jetbrains.kotlin.asJava.classes
import com.intellij.psi.*
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.stubs.StubElement
import com.intellij.psi.impl.DebugUtil
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory
import org.jetbrains.kotlin.asJava.builder.LightClassData
import org.jetbrains.kotlin.asJava.builder.findDelegate
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtClassOrObject
class KtLightClassForInterfaceDefaultImpls(
classOrObject: KtClassOrObject)
: KtLightClassForSourceDeclaration(classOrObject){
: KtLightClassForSourceDeclaration(classOrObject) {
override fun getQualifiedName(): String? = containingClass?.qualifiedName?.let { it + ".${JvmAbi.DEFAULT_IMPLS_CLASS_NAME}" }
override fun getName() = JvmAbi.DEFAULT_IMPLS_CLASS_NAME
@@ -37,11 +36,11 @@ class KtLightClassForInterfaceDefaultImpls(
return KtLightClassForInterfaceDefaultImpls(classOrObject.copy() as KtClassOrObject)
}
override fun findDelegateClass(javaFileStub: PsiJavaFileStub): PsiClass? {
val interfaceClass = LightClassUtil.findClass(javaFileStub) {
ClsWrapperStubPsiFactory.getOriginalElement(it as StubElement<*>) == classOrObject
override fun findLightClassData(): LightClassData {
return getLightClassDataHolder().findData {
it.findDelegate(classOrObject).findInnerClassByName(JvmAbi.DEFAULT_IMPLS_CLASS_NAME, false)
?: throw IllegalStateException("Couldn't get delegate for $this\n in ${DebugUtil.stubTreeToString(it)}")
}
return interfaceClass?.findInnerClassByName(JvmAbi.DEFAULT_IMPLS_CLASS_NAME, false)
}
override fun getTypeParameterList(): PsiTypeParameterList? = null
@@ -21,7 +21,6 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.Key
import com.intellij.psi.*
import com.intellij.psi.impl.DebugUtil
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.search.SearchScope
@@ -34,9 +33,9 @@ import com.intellij.util.containers.ContainerUtil
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory.getOriginalElement
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassData
import org.jetbrains.kotlin.asJava.builder.LightClassDataImpl
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassDataProviderForClassOrObject
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
@@ -79,41 +78,24 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC
abstract override fun getParent(): PsiElement?
abstract override fun getQualifiedName(): String?
override val clsDelegate: PsiClass by lazy(LazyThreadSafetyMode.PUBLICATION) {
val javaFileStub = getJavaFileStub()
override val clsDelegate: PsiClass get() = lightClassData.clsDelegate
findDelegateClass(javaFileStub) ?: run {
val outermostClassOrObject = getOutermostClassOrObject(classOrObject)
val ktFileText: String? = try {
outermostClassOrObject.containingFile.text
}
catch (e: Exception) {
"Can't get text for outermost class"
}
private val lightClassData: LightClassData by lazy(LazyThreadSafetyMode.PUBLICATION) { findLightClassData() }
val stubFileText = DebugUtil.stubTreeToString(javaFileStub)
throw IllegalStateException("Couldn't get delegate for $this\nin $ktFileText\nstub: \n$stubFileText")
}
}
open protected fun findLightClassData() = getLightClassDataHolder().findData(classOrObject)
protected open fun findDelegateClass(javaFileStub: PsiJavaFileStub): PsiClass? {
return LightClassUtil.findClass(javaFileStub) {
getOriginalElement(it as StubElement<*>) == classOrObject
}
}
private fun getJavaFileStub(): PsiJavaFileStub = getLightClassData().javaFileStub
private fun getJavaFileStub(): PsiJavaFileStub = getLightClassDataHolder().javaFileStub
protected fun getDescriptor(): ClassDescriptor? {
return LightClassGenerationSupport.getInstance(project).resolveToDescriptor(classOrObject) as? ClassDescriptor
}
private fun getLightClassData(): LightClassDataImpl {
val lightClassData = getLightClassData(classOrObject)
if (lightClassData !is LightClassDataImpl) {
protected fun getLightClassDataHolder(): LightClassDataHolder {
val lightClassData = getLightClassDataHolder(classOrObject)
if (lightClassData is InvalidLightClassDataHolder) {
LOG.error("Invalid light class data for existing light class:\n$lightClassData\n${classOrObject.getElementTextWithContext()}")
}
return lightClassData as LightClassDataImpl
return lightClassData
}
private val _containingFile: PsiFile by lazy(LazyThreadSafetyMode.PUBLICATION) {
@@ -341,7 +323,7 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC
override fun getImplementsList() = _implementsList
companion object {
private val JAVA_API_STUB = Key.create<CachedValue<LightClassData>>("JAVA_API_STUB")
private val JAVA_API_STUB = Key.create<CachedValue<LightClassDataHolder>>("JAVA_API_STUB")
private val jetTokenToPsiModifier = listOf(
PUBLIC_KEYWORD to PsiModifier.PUBLIC,
@@ -382,11 +364,11 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC
return classOrObject.getBody()?.declarations?.isEmpty() ?: true
}
fun getLightClassData(classOrObject: KtClassOrObject): LightClassData {
fun getLightClassDataHolder(classOrObject: KtClassOrObject): LightClassDataHolder {
return getLightClassCachedValue(classOrObject).value
}
fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<LightClassData> {
fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<LightClassDataHolder> {
var value =
getOutermostClassOrObject(classOrObject).getUserData(JAVA_API_STUB) // stub computed for outer class can be used for inner/nested
?: classOrObject.getUserData(JAVA_API_STUB)
@@ -18,9 +18,9 @@ package org.jetbrains.kotlin.asJava
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.builder.LightClassDataImpl
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory.cast
@@ -42,7 +42,11 @@ fun getJvmSignatureDiagnostics(element: PsiElement, otherDiagnostics: Diagnostic
}
fun getDiagnosticsForClass(ktClassOrObject: KtClassOrObject): Diagnostics {
return (KtLightClassForSourceDeclaration.getLightClassData(ktClassOrObject) as? LightClassDataImpl)?.extraDiagnostics ?: Diagnostics.EMPTY
val lightClassDataHolder = KtLightClassForSourceDeclaration.getLightClassDataHolder(ktClassOrObject)
if (lightClassDataHolder is InvalidLightClassDataHolder) {
return Diagnostics.EMPTY
}
return lightClassDataHolder.extraDiagnostics
}
fun doGetDiagnostics(): Diagnostics? {