[light classes] drop old light classes and backend: iteration #12
drop LightClassDataHolder ^KT-48773
This commit is contained in:
+46
-66
@@ -8,24 +8,21 @@ package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
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.shouldNotBeVisibleAsLightClass
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.builder.InvalidLightClassDataHolder
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.builder.LightClassDataHolder
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.builder.buildLightClass
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.builder.extraJvmDiagnosticsFromBackend
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
|
||||
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 }
|
||||
private val JAVA_API_STUB = Key.create<CachedValue<Diagnostics>>("JAVA_API_STUB")
|
||||
|
||||
object CliExtraDiagnosticsProvider {
|
||||
fun forClassOrObject(kclass: KtClassOrObject): Diagnostics {
|
||||
@@ -33,82 +30,65 @@ object CliExtraDiagnosticsProvider {
|
||||
return Diagnostics.EMPTY
|
||||
}
|
||||
|
||||
val lightClassDataHolder = getLightClassCachedValue(kclass).value
|
||||
return if (lightClassDataHolder is InvalidLightClassDataHolder) {
|
||||
Diagnostics.EMPTY
|
||||
} else {
|
||||
lightClassDataHolder.extraDiagnostics
|
||||
}
|
||||
return getLightClassCachedValue(kclass).value
|
||||
}
|
||||
|
||||
fun forFacade(file: KtFile, moduleScope: GlobalSearchScope): Diagnostics {
|
||||
fun forFacade(file: KtFile): Diagnostics = CachedValuesManager.getCachedValue(file) {
|
||||
CachedValueProvider.Result.create(
|
||||
calculateForFacade(file),
|
||||
KotlinModificationTrackerService.getInstance(file.project).outOfBlockModificationTracker,
|
||||
)
|
||||
}
|
||||
|
||||
private fun calculateForFacade(file: KtFile): Diagnostics {
|
||||
val project = file.project
|
||||
val facadeFqName = JvmFileClassUtil.getFileClassInfoNoResolve(file).facadeClassFqName
|
||||
val files = KotlinAsJavaSupport.getInstance(project)
|
||||
.findFilesForFacade(facadeFqName, moduleScope)
|
||||
val facadeCollection = KotlinAsJavaSupport.getInstance(project)
|
||||
.findFilesForFacade(facadeFqName, GlobalSearchScope.allScope(project))
|
||||
.ifEmpty { return Diagnostics.EMPTY }
|
||||
|
||||
return LightClassGenerationSupport.getInstance(project).cast<CliLightClassGenerationSupport>()
|
||||
.createDataHolderForFacade { 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 context = LightClassGenerationSupport.getInstance(project).cast<CliLightClassGenerationSupport>().context
|
||||
val (_, _, diagnostics) = extraJvmDiagnosticsFromBackend(
|
||||
facadeFqName.parent(),
|
||||
facadeCollection,
|
||||
ClassFilterForFacade,
|
||||
context,
|
||||
) 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
|
||||
val codegen = state.factory.forMultifileClass(facadeFqName, files)
|
||||
codegen.generate()
|
||||
state.factory.done()
|
||||
}
|
||||
|
||||
return diagnostics
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<LightClassDataHolder.ForClass> {
|
||||
private fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<Diagnostics> {
|
||||
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 computeLightClassCachedValue(classOrObject)
|
||||
}
|
||||
|
||||
private fun computeLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<Diagnostics> {
|
||||
val value = classOrObject.getUserData(JAVA_API_STUB) ?: run {
|
||||
val manager = CachedValuesManager.getManager(classOrObject.project)
|
||||
val cachedValue = manager.createCachedValue(LightClassDataProviderForClassOrObject(classOrObject))
|
||||
|
||||
classOrObject.putUserDataIfAbsent(JAVA_API_STUB, cachedValue)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+7
-27
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.asJava.classes.KtUltraLightSupport
|
||||
import org.jetbrains.kotlin.asJava.classes.cleanFromAnonymousTypes
|
||||
import org.jetbrains.kotlin.asJava.classes.lazyPub
|
||||
import org.jetbrains.kotlin.asJava.classes.tryGetPredefinedName
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.builder.*
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.builder.LightClassConstructionContext
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
@@ -89,30 +89,12 @@ class CliLightClassGenerationSupport(
|
||||
return ultraLightSupport
|
||||
}
|
||||
|
||||
fun createDataHolderForClass(classOrObject: KtClassOrObject, builder: LightClassBuilder): LightClassDataHolder.ForClass {
|
||||
//force resolve companion for light class generation
|
||||
traceHolder.bindingContext.get(BindingContext.CLASS, classOrObject)?.companionObjectDescriptor
|
||||
|
||||
val (_, bindingContext, diagnostics) = builder(getContext())
|
||||
|
||||
bindingContext.get(BindingContext.CLASS, classOrObject) ?: return InvalidLightClassDataHolder
|
||||
|
||||
return LightClassDataHolderImpl(diagnostics)
|
||||
}
|
||||
|
||||
fun createDataHolderForFacade(builder: LightClassBuilder): LightClassDataHolder.ForFacade {
|
||||
val (_, _, diagnostics) = builder(getContext())
|
||||
return LightClassDataHolderImpl(diagnostics)
|
||||
}
|
||||
|
||||
fun createDataHolderForScript(builder: LightClassBuilder): LightClassDataHolder.ForScript {
|
||||
val (_, _, diagnostics) = builder(getContext())
|
||||
return LightClassDataHolderImpl(diagnostics)
|
||||
}
|
||||
|
||||
private fun getContext(): LightClassConstructionContext =
|
||||
LightClassConstructionContext(
|
||||
traceHolder.bindingContext, traceHolder.module, null /* TODO: traceHolder.languageVersionSettings? */, traceHolder.jvmTarget
|
||||
internal val context: LightClassConstructionContext
|
||||
get() = LightClassConstructionContext(
|
||||
traceHolder.bindingContext,
|
||||
traceHolder.module,
|
||||
traceHolder.languageVersionSettings,
|
||||
traceHolder.jvmTarget,
|
||||
)
|
||||
|
||||
override fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor? {
|
||||
@@ -125,5 +107,3 @@ class CliLightClassGenerationSupport(
|
||||
|
||||
override fun analyzeWithContent(element: KtClassOrObject) = traceHolder.bindingContext
|
||||
}
|
||||
|
||||
typealias LightClassBuilder = (LightClassConstructionContext) -> LightClassBuilderResult
|
||||
|
||||
@@ -12,40 +12,45 @@ import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject
|
||||
import org.jetbrains.kotlin.asJava.classes.safeIsLocal
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.builder.LightClassDataHolder
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.builder.buildLightClass
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.builder.extraJvmDiagnosticsFromBackend
|
||||
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.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
internal class LightClassDataProviderForClassOrObject(
|
||||
private val classOrObject: KtClassOrObject
|
||||
) : CachedValueProvider<LightClassDataHolder.ForClass> {
|
||||
private fun computeLightClassData(): LightClassDataHolder.ForClass {
|
||||
) : CachedValueProvider<Diagnostics> {
|
||||
private fun computeLightClassData(): Diagnostics {
|
||||
val file = classOrObject.containingKtFile
|
||||
val packageFqName = file.packageFqName
|
||||
return LightClassGenerationSupport.getInstance(classOrObject.project).cast<CliLightClassGenerationSupport>()
|
||||
.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()
|
||||
}
|
||||
}
|
||||
val cliSupport = LightClassGenerationSupport.getInstance(classOrObject.project).cast<CliLightClassGenerationSupport>()
|
||||
|
||||
//force resolve companion for light class generation
|
||||
cliSupport.traceHolder.bindingContext.get(BindingContext.CLASS, classOrObject)?.companionObjectDescriptor
|
||||
|
||||
val (_, bindingContext, diagnostics) = extraJvmDiagnosticsFromBackend(
|
||||
packageFqName,
|
||||
listOf(file),
|
||||
ClassFilterForClassOrObject(classOrObject),
|
||||
cliSupport.context,
|
||||
) { 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()
|
||||
}
|
||||
|
||||
return diagnostics.takeIf { bindingContext.get(BindingContext.CLASS, classOrObject) != null } ?: Diagnostics.EMPTY
|
||||
}
|
||||
|
||||
override fun compute(): CachedValueProvider.Result<LightClassDataHolder.ForClass> {
|
||||
override fun compute(): CachedValueProvider.Result<Diagnostics> {
|
||||
val trackerService = KotlinModificationTrackerService.getInstance(classOrObject.project)
|
||||
return CachedValueProvider.Result.create(
|
||||
computeLightClassData(),
|
||||
|
||||
+8
-19
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.cli.jvm.compiler.builder
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.impl.compiled.ClsFileImpl
|
||||
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
|
||||
@@ -23,22 +22,18 @@ 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)
|
||||
data class CodeGenerationResult(val stub: PsiJavaFileStub, val bindingContext: BindingContext, val diagnostics: Diagnostics)
|
||||
|
||||
fun buildLightClass(
|
||||
fun extraJvmDiagnosticsFromBackend(
|
||||
packageFqName: FqName,
|
||||
files: Collection<KtFile>,
|
||||
generateClassFilter: GenerationState.GenerateClassFilter,
|
||||
context: LightClassConstructionContext,
|
||||
generate: (state: GenerationState, files: Collection<KtFile>) -> Unit
|
||||
): LightClassBuilderResult {
|
||||
generate: (state: GenerationState, files: Collection<KtFile>) -> Unit,
|
||||
): CodeGenerationResult {
|
||||
val project = files.first().project
|
||||
|
||||
try {
|
||||
if (Registry.`is`("kotlin.ultra.light.classes.error.on.old.backend", false)) {
|
||||
error("Access to backend detected")
|
||||
}
|
||||
|
||||
val classBuilderFactory = KotlinLightClassBuilderFactory(createJavaFileStub(packageFqName, files))
|
||||
val state = GenerationState.Builder(
|
||||
project,
|
||||
@@ -51,8 +46,7 @@ fun buildLightClass(
|
||||
put(JVMConfigurationKeys.JVM_TARGET, context.jvmTarget)
|
||||
isReadOnly = true
|
||||
}
|
||||
} ?: CompilerConfiguration.EMPTY
|
||||
|
||||
} ?: CompilerConfiguration.EMPTY,
|
||||
).generateDeclaredClassFilter(generateClassFilter).wantsDiagnostics(false).build()
|
||||
state.beforeCompile()
|
||||
state.oldBEInitTrace(files)
|
||||
@@ -60,7 +54,7 @@ fun buildLightClass(
|
||||
generate(state, files)
|
||||
|
||||
val javaFileStub = classBuilderFactory.result()
|
||||
return LightClassBuilderResult(javaFileStub, context.bindingContext, state.collectedExtraJvmDiagnostics)
|
||||
return CodeGenerationResult(javaFileStub, context.bindingContext, state.collectedExtraJvmDiagnostics)
|
||||
} catch (e: ProcessCanceledException) {
|
||||
throw e
|
||||
} catch (e: RuntimeException) {
|
||||
@@ -75,14 +69,9 @@ private fun createJavaFileStub(packageFqName: FqName, files: Collection<KtFile>)
|
||||
|
||||
val fakeFile = object : ClsFileImpl(files.first().viewProvider) {
|
||||
override fun getStub() = javaFileStub
|
||||
|
||||
override fun getPackageName() = packageFqName.asString()
|
||||
|
||||
override fun isPhysical() = false
|
||||
|
||||
override fun getText(): String {
|
||||
return files.singleOrNull()?.text ?: super.getText()
|
||||
}
|
||||
override fun getText(): String = files.singleOrNull()?.text ?: super.getText()
|
||||
}
|
||||
|
||||
javaFileStub.psi = fakeFile
|
||||
@@ -98,4 +87,4 @@ private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: V
|
||||
)
|
||||
}
|
||||
|
||||
private val LOG = Logger.getInstance(LightClassBuilderResult::class.java)
|
||||
private val LOG = Logger.getInstance(CodeGenerationResult::class.java)
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* 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.builder
|
||||
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
|
||||
interface LightClassDataHolder {
|
||||
val extraDiagnostics: Diagnostics
|
||||
|
||||
interface ForClass : LightClassDataHolder
|
||||
interface ForFacade : LightClassDataHolder
|
||||
interface ForScript : ForClass
|
||||
}
|
||||
|
||||
object InvalidLightClassDataHolder : LightClassDataHolder.ForClass {
|
||||
override val extraDiagnostics: Diagnostics get() = shouldNotBeCalled()
|
||||
|
||||
private fun shouldNotBeCalled(): Nothing = throw UnsupportedOperationException("Should not be called")
|
||||
}
|
||||
|
||||
class LightClassDataHolderImpl(
|
||||
override val extraDiagnostics: Diagnostics
|
||||
) : LightClassDataHolder.ForClass, LightClassDataHolder.ForFacade, LightClassDataHolder.ForScript
|
||||
@@ -6,7 +6,6 @@
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject
|
||||
import org.jetbrains.kotlin.asJava.classes.safeIsScript
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
@@ -19,7 +18,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
fun getJvmSignatureDiagnostics(element: PsiElement, otherDiagnostics: Diagnostics, moduleScope: GlobalSearchScope): Diagnostics? {
|
||||
fun getJvmSignatureDiagnostics(element: PsiElement, otherDiagnostics: Diagnostics): Diagnostics? {
|
||||
fun getDiagnosticsForClass(ktClassOrObject: KtClassOrObject): Diagnostics {
|
||||
val outermostClass = getOutermostClassOrObject(ktClassOrObject)
|
||||
return CliExtraDiagnosticsProvider.forClassOrObject(outermostClass)
|
||||
@@ -45,7 +44,7 @@ fun getJvmSignatureDiagnostics(element: PsiElement, otherDiagnostics: Diagnostic
|
||||
|
||||
when (parent) {
|
||||
is KtFile -> {
|
||||
return CliExtraDiagnosticsProvider.forFacade(parent, moduleScope)
|
||||
return CliExtraDiagnosticsProvider.forFacade(parent)
|
||||
}
|
||||
|
||||
is KtClassBody -> {
|
||||
|
||||
Reference in New Issue
Block a user