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:
@@ -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
|
||||
+97
@@ -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 = "]")
|
||||
}
|
||||
+54
-174
@@ -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 {
|
||||
|
||||
+8
-24
@@ -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))
|
||||
|
||||
+8
-9
@@ -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
|
||||
|
||||
+13
-31
@@ -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? {
|
||||
|
||||
Reference in New Issue
Block a user