diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassBuilder.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassBuilder.kt new file mode 100644 index 00000000000..cf440071842 --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassBuilder.kt @@ -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, + generateClassFilter: GenerationState.GenerateClassFilter, + context: LightClassConstructionContext, + generate: (state: GenerationState, files: Collection) -> Unit +): LightClassBuilderResult { + + val javaFileStub = createJavaFileStub(project, packageFqName, files) + val bindingContext: BindingContext + + val state: GenerationState + + try { + val stubStack = Stack>() + + @Suppress("UNCHECKED_CAST") + stubStack.push(javaFileStub as StubElement) + + 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): 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): VirtualFile { + return files.first().viewProvider.virtualFile +} + +private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) { + val path = if (virtualFile == 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) \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassData.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassData.kt deleted file mode 100644 index 2fc6c4ec00f..00000000000 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassData.kt +++ /dev/null @@ -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 diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataHolder.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataHolder.kt new file mode 100644 index 00000000000..eea59880542 --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataHolder.kt @@ -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() + LightClassUtil.findClass(javaFileStub) { cls -> + names.add(cls.qualifiedName ?: "") + false + } + return names.joinToString(prefix = "[", postfix = "]") +} \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataProvider.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataProvider.kt index edd7f60af87..11dbdd6e766 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataProvider.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataProvider.kt @@ -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( +abstract class LightClassDataProvider( private val project: Project ) : CachedValueProvider { - - abstract val files: Collection - abstract val packageFqName: FqName - - abstract fun getContext(files: Collection): LightClassConstructionContext - abstract fun createLightClassData(javaFileStub: PsiJavaFileStub, bindingContext: BindingContext, extraDiagnostics: Diagnostics): T - - abstract val generateClassFilter: GenerationState.GenerateClassFilter - abstract fun generate(state: GenerationState, files: Collection) abstract val isLocal: Boolean + open val valueAbsent: Boolean = false + override fun compute(): CachedValueProvider.Result? { - 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>() - - @Suppress("UNCHECKED_CAST") - stubStack.push(javaFileStub as StubElement) - - 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): 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): VirtualFile { - return files.first().viewProvider.virtualFile - } - - private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) { - val path = if (virtualFile == 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(classOrObject.project) { - - private val file: KtFile - get() = classOrObject.containingKtFile + LightClassDataProvider(classOrObject.project) { override val isLocal: Boolean get() = classOrObject.isLocal - override fun getContext(files: Collection): 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 - 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) { - 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(project) { - override val packageFqName: FqName - get() = facadeFqName.parent() - +) : LightClassDataProvider(project) { override val isLocal: Boolean get() = false + abstract val files: Collection - override fun getContext(files: Collection): 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) { - 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 { diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForFacade.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForFacade.kt index df9654dbac0..1a4ca524c26 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForFacade.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForFacade.kt @@ -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, + private val lightClassDataCache: CachedValue, files: Collection ) : 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>(20, 30) { - override fun createValue(key: StubCacheKey): CachedValue { + val cache = object : SLRUCache>(20, 30) { + override fun createValue(key: StubCacheKey): CachedValue { val stubProvider = LightClassDataProviderForFileFacade.ByProjectSource(project, key.fqName, key.searchScope) - return CachedValuesManager.getManager(project).createCachedValue(stubProvider, /*trackValue = */false) + return CachedValuesManager.getManager(project).createCachedValue(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 { + operator fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue { 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() - LightClassUtil.findClass(javaFileStub) { cls -> - names.add(cls.qualifiedName ?: "") - 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( + createCachedValue( LightClassDataProviderForFileFacade.ByFile(manager.project, facadeClassFqName, file), /*trackValue = */false ) return KtLightClassForFacade(manager, facadeClassFqName, cachedValue, listOf(file)) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForInterfaceDefaultImpls.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForInterfaceDefaultImpls.kt index 9cffc3a4c31..59dcc96dc24 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForInterfaceDefaultImpls.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForInterfaceDefaultImpls.kt @@ -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 diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt index bd1a6003ee9..49d394ad010 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt @@ -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>("JAVA_API_STUB") + private val JAVA_API_STUB = Key.create>("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 { + fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue { var value = getOutermostClassOrObject(classOrObject).getUserData(JAVA_API_STUB) // stub computed for outer class can be used for inner/nested ?: classOrObject.getUserData(JAVA_API_STUB) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/duplicateJvmSignatureUtil.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/duplicateJvmSignatureUtil.kt index 917ead12c84..26fdc20af27 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/duplicateJvmSignatureUtil.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/duplicateJvmSignatureUtil.kt @@ -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? {