Revert "Revert "[light classes] drop old light classes and backend: iteration #4""
This reverts commit 3d9a58627f.
This commit is contained in:
+98
-11
@@ -5,33 +5,120 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.psi.util.CachedValue
|
||||||
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
|
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
|
||||||
import org.jetbrains.kotlin.asJava.KotlinExtraDiagnosticsProvider
|
import org.jetbrains.kotlin.asJava.KotlinExtraDiagnosticsProvider
|
||||||
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
import org.jetbrains.kotlin.asJava.builder.LightClassDataProviderForFileFacade
|
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
|
import org.jetbrains.kotlin.asJava.builder.buildLightClass
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.safeIsLocal
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.safeScript
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.shouldNotBeVisibleAsLightClass
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtScript
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||||
|
|
||||||
|
private val JAVA_API_STUB_FOR_SCRIPT = Key.create<CachedValue<LightClassDataHolder.ForScript>>("JAVA_API_STUB_FOR_SCRIPT")
|
||||||
|
private val JAVA_API_STUB = Key.create<CachedValue<LightClassDataHolder.ForClass>>("JAVA_API_STUB")
|
||||||
|
private val JAVA_API_STUB_LOCK = Key.create<Any>("JAVA_API_STUB_LOCK")
|
||||||
|
|
||||||
|
private val javaApiStubInitIsRunning: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
|
||||||
|
|
||||||
class CliExtraDiagnosticsProvider : KotlinExtraDiagnosticsProvider {
|
class CliExtraDiagnosticsProvider : KotlinExtraDiagnosticsProvider {
|
||||||
override fun forClassOrObject(kclass: KtClassOrObject): Diagnostics {
|
override fun forClassOrObject(kclass: KtClassOrObject): Diagnostics {
|
||||||
val lightClassDataHolder = KtLightClassForSourceDeclaration.getLightClassDataHolder(kclass)
|
if (kclass.shouldNotBeVisibleAsLightClass()) {
|
||||||
if (lightClassDataHolder is InvalidLightClassDataHolder) {
|
|
||||||
return Diagnostics.EMPTY
|
return Diagnostics.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
return lightClassDataHolder.extraDiagnostics
|
val containingScript = kclass.containingKtFile.safeScript()
|
||||||
|
return when {
|
||||||
|
!kclass.safeIsLocal() && containingScript != null -> getLightClassCachedValue(containingScript).value
|
||||||
|
else -> getLightClassCachedValue(kclass).value
|
||||||
|
}.extraDiagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun forFacade(file: KtFile, moduleScope: GlobalSearchScope): Diagnostics {
|
override fun forFacade(file: KtFile, moduleScope: GlobalSearchScope): Diagnostics {
|
||||||
val project = file.project
|
val project = file.project
|
||||||
val facadeFqName = JvmFileClassUtil.getFileClassInfoNoResolve(file).facadeClassFqName
|
val facadeFqName = JvmFileClassUtil.getFileClassInfoNoResolve(file).facadeClassFqName
|
||||||
return LightClassDataProviderForFileFacade(project, facadeFqName, moduleScope)
|
val files = KotlinAsJavaSupport.getInstance(project)
|
||||||
.compute()
|
.findFilesForFacade(facadeFqName, moduleScope)
|
||||||
?.value
|
.ifEmpty { return Diagnostics.EMPTY }
|
||||||
?.extraDiagnostics
|
|
||||||
?: Diagnostics.EMPTY
|
return LightClassGenerationSupport.getInstance(project).createDataHolderForFacade(files) { constructionContext ->
|
||||||
|
buildLightClass(facadeFqName.parent(), files, ClassFilterForFacade, constructionContext) generate@{ state, files ->
|
||||||
|
val representativeFile = files.first()
|
||||||
|
val fileClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(representativeFile)
|
||||||
|
if (!fileClassInfo.withJvmMultifileClass) {
|
||||||
|
val codegen = state.factory.forPackage(representativeFile.packageFqName, files)
|
||||||
|
codegen.generate()
|
||||||
|
state.factory.done()
|
||||||
|
return@generate
|
||||||
|
}
|
||||||
|
|
||||||
|
val codegen = state.factory.forMultifileClass(facadeFqName, files)
|
||||||
|
codegen.generate()
|
||||||
|
state.factory.done()
|
||||||
|
}
|
||||||
|
}.extraDiagnostics
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLightClassCachedValue(script: KtScript): CachedValue<LightClassDataHolder.ForScript> {
|
||||||
|
return script.getUserData(JAVA_API_STUB_FOR_SCRIPT) ?: createCachedValueForScript(script).also {
|
||||||
|
script.putUserData(JAVA_API_STUB_FOR_SCRIPT, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createCachedValueForScript(script: KtScript): CachedValue<LightClassDataHolder.ForScript> =
|
||||||
|
CachedValuesManager.getManager(script.project).createCachedValue(LightClassDataProviderForScript(script), false)
|
||||||
|
|
||||||
|
private fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<LightClassDataHolder.ForClass> {
|
||||||
|
val outerClassValue = getOutermostClassOrObject(classOrObject).getUserData(JAVA_API_STUB)
|
||||||
|
outerClassValue?.let {
|
||||||
|
// stub computed for outer class can be used for inner/nested
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
// the idea behind this locking approach:
|
||||||
|
// Thread T1 starts to calculate value for A it acquires lock for A
|
||||||
|
//
|
||||||
|
// Assumption 1: Lets say A calculation requires another value e.g. B to be calculated
|
||||||
|
// Assumption 2: Thread T2 wants to calculate value for B
|
||||||
|
|
||||||
|
// to avoid dead-lock case we mark thread as doing calculation and acquire lock only once per thread
|
||||||
|
// as a trade-off to prevent dependent value could be calculated several time
|
||||||
|
// due to CAS (within putUserDataIfAbsent etc) the same instance of calculated value will be used
|
||||||
|
val value: CachedValue<LightClassDataHolder.ForClass> = if (!javaApiStubInitIsRunning.get()) {
|
||||||
|
classOrObject.getUserData(JAVA_API_STUB) ?: run {
|
||||||
|
val lock = classOrObject.putUserDataIfAbsent(JAVA_API_STUB_LOCK, Object())
|
||||||
|
synchronized(lock) {
|
||||||
|
try {
|
||||||
|
javaApiStubInitIsRunning.set(true)
|
||||||
|
computeLightClassCachedValue(classOrObject)
|
||||||
|
} finally {
|
||||||
|
javaApiStubInitIsRunning.set(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
computeLightClassCachedValue(classOrObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<LightClassDataHolder.ForClass> {
|
||||||
|
val value = classOrObject.getUserData(JAVA_API_STUB) ?: run {
|
||||||
|
val manager = CachedValuesManager.getManager(classOrObject.project)
|
||||||
|
val cachedValue = manager.createCachedValue(
|
||||||
|
LightClassDataProviderForClassOrObject(classOrObject), false
|
||||||
|
)
|
||||||
|
classOrObject.putUserDataIfAbsent(JAVA_API_STUB, cachedValue)
|
||||||
|
}
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
||||||
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
|
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
|
||||||
|
import org.jetbrains.kotlin.asJava.builder.buildLightClass
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.safeIsLocal
|
||||||
|
import org.jetbrains.kotlin.codegen.MemberCodegen
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
|
||||||
|
internal class LightClassDataProviderForClassOrObject(
|
||||||
|
private val classOrObject: KtClassOrObject
|
||||||
|
) : CachedValueProvider<LightClassDataHolder.ForClass> {
|
||||||
|
private fun computeLightClassData(): LightClassDataHolder.ForClass {
|
||||||
|
val file = classOrObject.containingKtFile
|
||||||
|
val packageFqName = file.packageFqName
|
||||||
|
return LightClassGenerationSupport.getInstance(classOrObject.project)
|
||||||
|
.createDataHolderForClass(classOrObject) { constructionContext ->
|
||||||
|
buildLightClass(
|
||||||
|
packageFqName,
|
||||||
|
listOf(file),
|
||||||
|
ClassFilterForClassOrObject(classOrObject),
|
||||||
|
constructionContext
|
||||||
|
) { state, files ->
|
||||||
|
val packageCodegen = state.factory.forPackage(packageFqName, files)
|
||||||
|
val packagePartType = Type.getObjectType(JvmFileClassUtil.getFileClassInternalName(file))
|
||||||
|
val context = state.rootContext.intoPackagePart(packageCodegen.packageFragment, packagePartType, file)
|
||||||
|
MemberCodegen.genClassOrObject(context, getOutermostClassOrObject(classOrObject), state, null)
|
||||||
|
state.factory.done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compute(): CachedValueProvider.Result<LightClassDataHolder.ForClass>? {
|
||||||
|
val trackerService = KotlinModificationTrackerService.getInstance(classOrObject.project)
|
||||||
|
return CachedValueProvider.Result.create(
|
||||||
|
computeLightClassData(),
|
||||||
|
if (classOrObject.safeIsLocal()) trackerService.modificationTracker else trackerService.outOfBlockModificationTracker
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return this::class.java.name + " for " + classOrObject.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ClassFilterForClassOrObject(private val classOrObject: KtClassOrObject) : GenerationState.GenerateClassFilter() {
|
||||||
|
|
||||||
|
override fun shouldGeneratePackagePart(ktFile: KtFile) = true
|
||||||
|
override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject) = shouldGenerateClass(processingClassOrObject)
|
||||||
|
|
||||||
|
override fun shouldGenerateClassMembers(processingClassOrObject: KtClassOrObject): Boolean {
|
||||||
|
if (classOrObject === processingClassOrObject) return true
|
||||||
|
|
||||||
|
// process all children
|
||||||
|
if (classOrObject.isAncestor(processingClassOrObject, true)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local classes should be process by CodegenAnnotatingVisitor to
|
||||||
|
// decide what class they should be placed in.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// class A
|
||||||
|
// fun foo() {
|
||||||
|
// trait Z: A {}
|
||||||
|
// fun bar() {
|
||||||
|
// class <caret>O2: Z {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// TODO: current method will process local classes in irrelevant declarations, it should be fixed.
|
||||||
|
// We generate all enclosing classes
|
||||||
|
|
||||||
|
if (classOrObject.safeIsLocal() && processingClassOrObject.safeIsLocal()) {
|
||||||
|
val commonParent = PsiTreeUtil.findCommonParent(classOrObject, processingClassOrObject)
|
||||||
|
return commonParent != null && commonParent !is PsiFile
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject)
|
||||||
|
// generate outer classes but not their members
|
||||||
|
= shouldGenerateClassMembers(processingClassOrObject) || processingClassOrObject.isAncestor(classOrObject, true)
|
||||||
|
|
||||||
|
override fun shouldGenerateScript(script: KtScript) = PsiTreeUtil.isAncestor(script, classOrObject, false)
|
||||||
|
override fun shouldGenerateCodeFragment(script: KtCodeFragment) = false
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class LightClassDataProviderForScript(private val script: KtScript) : CachedValueProvider<LightClassDataHolder.ForScript> {
|
||||||
|
private fun computeLightClassData(): LightClassDataHolder.ForScript {
|
||||||
|
return LightClassGenerationSupport.getInstance(script.project).createDataHolderForScript(script) { constructionContext ->
|
||||||
|
buildLightClass(
|
||||||
|
script.fqName.parent(),
|
||||||
|
listOf(script.containingKtFile),
|
||||||
|
ClassFilterForScript(script),
|
||||||
|
constructionContext
|
||||||
|
) generate@{ state, files ->
|
||||||
|
val scriptFile = files.first()
|
||||||
|
val codegen = state.factory.forPackage(scriptFile.packageFqName, files)
|
||||||
|
codegen.generate()
|
||||||
|
state.factory.done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compute(): CachedValueProvider.Result<LightClassDataHolder.ForScript> =
|
||||||
|
CachedValueProvider.Result.create(
|
||||||
|
computeLightClassData(),
|
||||||
|
KotlinModificationTrackerService.getInstance(script.project).outOfBlockModificationTracker
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun toString(): String = this::class.java.name + " for ${script.fqName}"
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ClassFilterForScript(val script: KtScript) : GenerationState.GenerateClassFilter() {
|
||||||
|
override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject): Boolean =
|
||||||
|
shouldGenerateClass(processingClassOrObject)
|
||||||
|
|
||||||
|
override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject): Boolean =
|
||||||
|
processingClassOrObject.isAncestor(script, true)
|
||||||
|
|
||||||
|
override fun shouldGenerateClassMembers(processingClassOrObject: KtClassOrObject): Boolean = true
|
||||||
|
|
||||||
|
override fun shouldGeneratePackagePart(ktFile: KtFile): Boolean = script.containingKtFile === ktFile
|
||||||
|
|
||||||
|
override fun shouldGenerateScript(script: KtScript): Boolean = this.script === script
|
||||||
|
override fun shouldGenerateCodeFragment(script: KtCodeFragment) = false
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object ClassFilterForFacade : GenerationState.GenerateClassFilter() {
|
||||||
|
override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject) = shouldGenerateClass(processingClassOrObject)
|
||||||
|
override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject) = KtPsiUtil.isLocal(processingClassOrObject)
|
||||||
|
override fun shouldGeneratePackagePart(ktFile: KtFile) = true
|
||||||
|
override fun shouldGenerateScript(script: KtScript) = false
|
||||||
|
override fun shouldGenerateCodeFragment(script: KtCodeFragment) = false
|
||||||
|
}
|
||||||
-186
@@ -5,195 +5,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.asJava.builder
|
package org.jetbrains.kotlin.asJava.builder
|
||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
|
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
|
||||||
import com.intellij.psi.util.CachedValueProvider
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
|
||||||
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
|
||||||
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
|
|
||||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.safeIsLocal
|
|
||||||
import org.jetbrains.kotlin.codegen.MemberCodegen
|
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
|
||||||
|
|
||||||
class LightClassDataProviderForClassOrObject(
|
|
||||||
private val classOrObject: KtClassOrObject
|
|
||||||
) : CachedValueProvider<LightClassDataHolder.ForClass> {
|
|
||||||
|
|
||||||
private fun computeLightClassData(): LightClassDataHolder.ForClass {
|
|
||||||
val file = classOrObject.containingKtFile
|
|
||||||
val packageFqName = file.packageFqName
|
|
||||||
return LightClassGenerationSupport.getInstance(classOrObject.project)
|
|
||||||
.createDataHolderForClass(classOrObject) { constructionContext ->
|
|
||||||
buildLightClass(
|
|
||||||
packageFqName,
|
|
||||||
listOf(file),
|
|
||||||
ClassFilterForClassOrObject(classOrObject),
|
|
||||||
constructionContext
|
|
||||||
) { state, files ->
|
|
||||||
val packageCodegen = state.factory.forPackage(packageFqName, files)
|
|
||||||
val packagePartType = Type.getObjectType(JvmFileClassUtil.getFileClassInternalName(file))
|
|
||||||
val context = state.rootContext.intoPackagePart(packageCodegen.packageFragment, packagePartType, file)
|
|
||||||
MemberCodegen.genClassOrObject(context, getOutermostClassOrObject(classOrObject), state, null)
|
|
||||||
state.factory.done()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun compute(): CachedValueProvider.Result<LightClassDataHolder.ForClass>? {
|
|
||||||
val trackerService = KotlinModificationTrackerService.getInstance(classOrObject.project)
|
|
||||||
return CachedValueProvider.Result.create(
|
|
||||||
computeLightClassData(),
|
|
||||||
if (classOrObject.safeIsLocal()) trackerService.modificationTracker else trackerService.outOfBlockModificationTracker
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return this::class.java.name + " for " + classOrObject.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LightClassDataProviderForFileFacade constructor(
|
|
||||||
private val project: Project, private val facadeFqName: FqName, private val searchScope: GlobalSearchScope
|
|
||||||
) : CachedValueProvider<LightClassDataHolder.ForFacade> {
|
|
||||||
fun findFiles(): Collection<KtFile> = KotlinAsJavaSupport.getInstance(project).findFilesForFacade(facadeFqName, searchScope)
|
|
||||||
|
|
||||||
private fun computeLightClassData(files: Collection<KtFile>): LightClassDataHolder.ForFacade {
|
|
||||||
return LightClassGenerationSupport.getInstance(project).createDataHolderForFacade(files) { constructionContext ->
|
|
||||||
buildLightClass(facadeFqName.parent(), files, ClassFilterForFacade, constructionContext) generate@{ state, files ->
|
|
||||||
val representativeFile = files.first()
|
|
||||||
val fileClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(representativeFile)
|
|
||||||
if (!fileClassInfo.withJvmMultifileClass) {
|
|
||||||
val codegen = state.factory.forPackage(representativeFile.packageFqName, files)
|
|
||||||
codegen.generate()
|
|
||||||
state.factory.done()
|
|
||||||
return@generate
|
|
||||||
}
|
|
||||||
|
|
||||||
val codegen = state.factory.forMultifileClass(facadeFqName, files)
|
|
||||||
codegen.generate()
|
|
||||||
state.factory.done()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun compute(): CachedValueProvider.Result<LightClassDataHolder.ForFacade>? {
|
|
||||||
val files = findFiles()
|
|
||||||
if (files.isEmpty()) return null
|
|
||||||
|
|
||||||
return CachedValueProvider.Result.create(
|
|
||||||
computeLightClassData(files),
|
|
||||||
KotlinModificationTrackerService.getInstance(project).outOfBlockModificationTracker
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return this::class.java.name + " for $facadeFqName"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class LightClassDataProviderForScript(private val script: KtScript) : CachedValueProvider<LightClassDataHolder.ForScript> {
|
|
||||||
private fun computeLightClassData(): LightClassDataHolder.ForScript {
|
|
||||||
return LightClassGenerationSupport.getInstance(script.project).createDataHolderForScript(script) { constructionContext ->
|
|
||||||
buildLightClass(
|
|
||||||
script.fqName.parent(),
|
|
||||||
listOf(script.containingKtFile),
|
|
||||||
ClassFilterForScript(script),
|
|
||||||
constructionContext
|
|
||||||
) generate@{ state, files ->
|
|
||||||
val scriptFile = files.first()
|
|
||||||
val codegen = state.factory.forPackage(scriptFile.packageFqName, files)
|
|
||||||
codegen.generate()
|
|
||||||
state.factory.done()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun compute(): CachedValueProvider.Result<LightClassDataHolder.ForScript>? =
|
|
||||||
CachedValueProvider.Result.create(
|
|
||||||
computeLightClassData(),
|
|
||||||
KotlinModificationTrackerService.getInstance(script.project).outOfBlockModificationTracker
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun toString(): String = this::class.java.name + " for ${script.fqName}"
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StubComputationTracker {
|
interface StubComputationTracker {
|
||||||
fun onStubComputed(javaFileStub: PsiJavaFileStub, context: LightClassConstructionContext)
|
fun onStubComputed(javaFileStub: PsiJavaFileStub, context: LightClassConstructionContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class ClassFilterForClassOrObject(private val classOrObject: KtClassOrObject) : GenerationState.GenerateClassFilter() {
|
|
||||||
|
|
||||||
override fun shouldGeneratePackagePart(ktFile: KtFile) = true
|
|
||||||
override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject) = shouldGenerateClass(processingClassOrObject)
|
|
||||||
|
|
||||||
override fun shouldGenerateClassMembers(processingClassOrObject: KtClassOrObject): Boolean {
|
|
||||||
if (classOrObject === processingClassOrObject) return true
|
|
||||||
|
|
||||||
// process all children
|
|
||||||
if (classOrObject.isAncestor(processingClassOrObject, true)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Local classes should be process by CodegenAnnotatingVisitor to
|
|
||||||
// decide what class they should be placed in.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// class A
|
|
||||||
// fun foo() {
|
|
||||||
// trait Z: A {}
|
|
||||||
// fun bar() {
|
|
||||||
// class <caret>O2: Z {}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// TODO: current method will process local classes in irrelevant declarations, it should be fixed.
|
|
||||||
// We generate all enclosing classes
|
|
||||||
|
|
||||||
if (classOrObject.safeIsLocal() && processingClassOrObject.safeIsLocal()) {
|
|
||||||
val commonParent = PsiTreeUtil.findCommonParent(classOrObject, processingClassOrObject)
|
|
||||||
return commonParent != null && commonParent !is PsiFile
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject)
|
|
||||||
// generate outer classes but not their members
|
|
||||||
= shouldGenerateClassMembers(processingClassOrObject) || processingClassOrObject.isAncestor(classOrObject, true)
|
|
||||||
|
|
||||||
override fun shouldGenerateScript(script: KtScript) = PsiTreeUtil.isAncestor(script, classOrObject, false)
|
|
||||||
override fun shouldGenerateCodeFragment(script: KtCodeFragment) = false
|
|
||||||
}
|
|
||||||
|
|
||||||
object ClassFilterForFacade : GenerationState.GenerateClassFilter() {
|
|
||||||
override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject) = shouldGenerateClass(processingClassOrObject)
|
|
||||||
override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject) = KtPsiUtil.isLocal(processingClassOrObject)
|
|
||||||
override fun shouldGeneratePackagePart(ktFile: KtFile) = true
|
|
||||||
override fun shouldGenerateScript(script: KtScript) = false
|
|
||||||
override fun shouldGenerateCodeFragment(script: KtCodeFragment) = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ClassFilterForScript(val script: KtScript) : GenerationState.GenerateClassFilter() {
|
|
||||||
override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject): Boolean =
|
|
||||||
shouldGenerateClass(processingClassOrObject)
|
|
||||||
|
|
||||||
override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject): Boolean =
|
|
||||||
processingClassOrObject.isAncestor(script, true)
|
|
||||||
|
|
||||||
override fun shouldGenerateClassMembers(processingClassOrObject: KtClassOrObject): Boolean = true
|
|
||||||
|
|
||||||
override fun shouldGeneratePackagePart(ktFile: KtFile): Boolean = script.containingKtFile === ktFile
|
|
||||||
|
|
||||||
override fun shouldGenerateScript(script: KtScript): Boolean = this.script === script
|
|
||||||
override fun shouldGenerateCodeFragment(script: KtCodeFragment) = false
|
|
||||||
}
|
|
||||||
|
|||||||
-19
@@ -5,20 +5,16 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.asJava.classes
|
package org.jetbrains.kotlin.asJava.classes
|
||||||
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.impl.PsiSuperMethodImplUtil
|
import com.intellij.psi.impl.PsiSuperMethodImplUtil
|
||||||
import com.intellij.psi.impl.light.LightEmptyImplementsList
|
import com.intellij.psi.impl.light.LightEmptyImplementsList
|
||||||
import com.intellij.psi.impl.light.LightModifierList
|
import com.intellij.psi.impl.light.LightModifierList
|
||||||
import com.intellij.psi.util.CachedValue
|
|
||||||
import com.intellij.psi.util.CachedValueProvider
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
import com.intellij.util.IncorrectOperationException
|
import com.intellij.util.IncorrectOperationException
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
||||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
|
|
||||||
import org.jetbrains.kotlin.asJava.builder.LightClassDataProviderForScript
|
|
||||||
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
|
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
|
||||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
|
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
|
||||||
@@ -163,9 +159,6 @@ abstract class KtLightClassForScript(val script: KtScript) : KtLazyLightClass(sc
|
|||||||
override fun toString() = "${KtLightClassForScript::class.java.simpleName}:${script.fqName}"
|
override fun toString() = "${KtLightClassForScript::class.java.simpleName}:${script.fqName}"
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val JAVA_API_STUB_FOR_SCRIPT = Key.create<CachedValue<LightClassDataHolder.ForScript>>("JAVA_API_STUB_FOR_SCRIPT")
|
|
||||||
|
|
||||||
fun create(script: KtScript): KtLightClassForScript? =
|
fun create(script: KtScript): KtLightClassForScript? =
|
||||||
CachedValuesManager.getCachedValue(script) {
|
CachedValuesManager.getCachedValue(script) {
|
||||||
CachedValueProvider.Result
|
CachedValueProvider.Result
|
||||||
@@ -186,17 +179,5 @@ abstract class KtLightClassForScript(val script: KtScript) : KtLazyLightClass(sc
|
|||||||
createUltraLightClassForScript(script)
|
createUltraLightClassForScript(script)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLightClassCachedValue(script: KtScript): CachedValue<LightClassDataHolder.ForScript> {
|
|
||||||
return script.getUserData(JAVA_API_STUB_FOR_SCRIPT) ?: createCachedValueForScript(script).also {
|
|
||||||
script.putUserData(
|
|
||||||
JAVA_API_STUB_FOR_SCRIPT,
|
|
||||||
it,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createCachedValueForScript(script: KtScript): CachedValue<LightClassDataHolder.ForScript> =
|
|
||||||
CachedValuesManager.getManager(script.project).createCachedValue(LightClassDataProviderForScript(script), false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-72
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.asJava.classes
|
|||||||
import com.intellij.openapi.components.ServiceManager
|
import com.intellij.openapi.components.ServiceManager
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.impl.InheritanceImplUtil
|
import com.intellij.psi.impl.InheritanceImplUtil
|
||||||
@@ -18,7 +17,6 @@ import com.intellij.psi.scope.PsiScopeProcessor
|
|||||||
import com.intellij.psi.search.SearchScope
|
import com.intellij.psi.search.SearchScope
|
||||||
import com.intellij.psi.stubs.IStubElementType
|
import com.intellij.psi.stubs.IStubElementType
|
||||||
import com.intellij.psi.stubs.StubElement
|
import com.intellij.psi.stubs.StubElement
|
||||||
import com.intellij.psi.util.CachedValue
|
|
||||||
import com.intellij.psi.util.CachedValueProvider
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
import com.intellij.psi.util.PsiUtilCore
|
import com.intellij.psi.util.PsiUtilCore
|
||||||
@@ -28,9 +26,6 @@ import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
|||||||
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
|
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
|
||||||
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.UNSURE
|
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.UNSURE
|
||||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
|
|
||||||
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.FakeFileForLightClass
|
||||||
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
|
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
|
||||||
import org.jetbrains.kotlin.asJava.elements.KtLightModifierList
|
import org.jetbrains.kotlin.asJava.elements.KtLightModifierList
|
||||||
@@ -310,12 +305,6 @@ abstract class KtLightClassForSourceDeclaration(
|
|||||||
override fun getImplementsList(): PsiReferenceList? = _implementsList
|
override fun getImplementsList(): PsiReferenceList? = _implementsList
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val JAVA_API_STUB = Key.create<CachedValue<LightClassDataHolder.ForClass>>("JAVA_API_STUB")
|
|
||||||
private val JAVA_API_STUB_LOCK = Key.create<Any>("JAVA_API_STUB_LOCK")
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
private val javaApiStubInitIsRunning: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
|
|
||||||
|
|
||||||
private val jetTokenToPsiModifier = listOf(
|
private val jetTokenToPsiModifier = listOf(
|
||||||
PUBLIC_KEYWORD to PsiModifier.PUBLIC,
|
PUBLIC_KEYWORD to PsiModifier.PUBLIC,
|
||||||
INTERNAL_KEYWORD to PsiModifier.PUBLIC,
|
INTERNAL_KEYWORD to PsiModifier.PUBLIC,
|
||||||
@@ -347,67 +336,6 @@ abstract class KtLightClassForSourceDeclaration(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLightClassDataHolder(classOrObject: KtClassOrObject): LightClassDataHolder.ForClass {
|
|
||||||
if (classOrObject.shouldNotBeVisibleAsLightClass()) {
|
|
||||||
return InvalidLightClassDataHolder
|
|
||||||
}
|
|
||||||
|
|
||||||
val containingScript = classOrObject.containingKtFile.safeScript()
|
|
||||||
return when {
|
|
||||||
!classOrObject.safeIsLocal() && containingScript != null ->
|
|
||||||
KtLightClassForScript.getLightClassCachedValue(containingScript).value
|
|
||||||
|
|
||||||
else ->
|
|
||||||
getLightClassCachedValue(classOrObject).value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<LightClassDataHolder.ForClass> {
|
|
||||||
val outerClassValue = getOutermostClassOrObject(classOrObject).getUserData(JAVA_API_STUB)
|
|
||||||
outerClassValue?.let {
|
|
||||||
// stub computed for outer class can be used for inner/nested
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
// the idea behind this locking approach:
|
|
||||||
// Thread T1 starts to calculate value for A it acquires lock for A
|
|
||||||
//
|
|
||||||
// Assumption 1: Lets say A calculation requires another value e.g. B to be calculated
|
|
||||||
// Assumption 2: Thread T2 wants to calculate value for B
|
|
||||||
|
|
||||||
// to avoid dead-lock case we mark thread as doing calculation and acquire lock only once per thread
|
|
||||||
// as a trade-off to prevent dependent value could be calculated several time
|
|
||||||
// due to CAS (within putUserDataIfAbsent etc) the same instance of calculated value will be used
|
|
||||||
val value: CachedValue<LightClassDataHolder.ForClass> = if (!javaApiStubInitIsRunning.get()) {
|
|
||||||
classOrObject.getUserData(JAVA_API_STUB) ?: run {
|
|
||||||
val lock = classOrObject.putUserDataIfAbsent(JAVA_API_STUB_LOCK, Object())
|
|
||||||
synchronized(lock) {
|
|
||||||
try {
|
|
||||||
javaApiStubInitIsRunning.set(true)
|
|
||||||
computeLightClassCachedValue(classOrObject)
|
|
||||||
} finally {
|
|
||||||
javaApiStubInitIsRunning.set(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
computeLightClassCachedValue(classOrObject)
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeLightClassCachedValue(
|
|
||||||
classOrObject: KtClassOrObject
|
|
||||||
): CachedValue<LightClassDataHolder.ForClass> {
|
|
||||||
val value = classOrObject.getUserData(JAVA_API_STUB) ?: run {
|
|
||||||
val manager = CachedValuesManager.getManager(classOrObject.project)
|
|
||||||
val cachedValue = manager.createCachedValue(
|
|
||||||
LightClassDataProviderForClassOrObject(classOrObject), false
|
|
||||||
)
|
|
||||||
classOrObject.putUserDataIfAbsent(JAVA_API_STUB, cachedValue)
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkSuperTypeByFQName(classDescriptor: ClassDescriptor, qualifiedName: String, deep: Boolean): Boolean {
|
private fun checkSuperTypeByFQName(classDescriptor: ClassDescriptor, qualifiedName: String, deep: Boolean): Boolean {
|
||||||
if (CommonClassNames.JAVA_LANG_OBJECT == qualifiedName) return true
|
if (CommonClassNames.JAVA_LANG_OBJECT == qualifiedName) return true
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user