[JVM IR] KTIJ-24335 Deduplicate class symbols of duplicate built-ins
- When the IR backend is invoked from the bytecode tool window, multiple descriptors may exist of the same built-in. For example, there may be multiple descriptors for `Boolean`. - This caused issues when the `IrClassifierSymbol` of a type was used as a key for some built-ins map (such as `primitiveRefProviders` in `JvmSharedVariablesManager`). Because while the reference map is built from predeclared built-ins, with duplicate descriptors, multiple `IrClassifierSymbol`s may exist for the same type. Such a duplicated symbol would lead to an exception. - The solution invents a special symbol table which de-duplicates built-ins at the point where `IrClassSymbol`s are created. I tried other approaches: - It's possible to fix the symptoms in `JvmSharedVariablesManager` and `JvmSymbols` by turning the keys of the requisite maps, i.e. `primitiveRefProviders` and `arraysCopyOfFunctions`, from `IrClassifierSymbol` to something like a `Name` or `PrimitiveType`. This however touches the IR backend beyond the IDE and still leaves error potential for all the other possibly duplicated built-ins. - I tried to handle built-ins in `DeclarationStubGenerator` specially, to circumvent the issue of duplicated descriptors, but this is not a solution because the duplicated `IrClassSymbol`s will already be part of the IR at that point. ^KTIJ-24335 fixed
This commit is contained in:
committed by
Space Team
parent
ba2e716682
commit
f11ea92601
+21
-7
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
|||||||
import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector
|
import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector
|
||||||
import org.jetbrains.kotlin.idea.MainFunctionDetector
|
import org.jetbrains.kotlin.idea.MainFunctionDetector
|
||||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||||
|
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||||
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
|
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
|
||||||
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrLinker
|
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrLinker
|
||||||
import org.jetbrains.kotlin.ir.builders.TranslationPluginContext
|
import org.jetbrains.kotlin.ir.builders.TranslationPluginContext
|
||||||
@@ -44,6 +45,7 @@ import org.jetbrains.kotlin.library.metadata.KlibModuleOrigin
|
|||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
|
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
|
||||||
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
|
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
|
||||||
|
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorForNotFoundClasses
|
import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorForNotFoundClasses
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl
|
import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.fragments.EvaluatorFragmentInfo
|
import org.jetbrains.kotlin.psi2ir.generators.fragments.EvaluatorFragmentInfo
|
||||||
@@ -60,7 +62,7 @@ open class JvmIrCodegenFactory(
|
|||||||
private val externalSymbolTable: SymbolTable? = null,
|
private val externalSymbolTable: SymbolTable? = null,
|
||||||
private val jvmGeneratorExtensions: JvmGeneratorExtensionsImpl = JvmGeneratorExtensionsImpl(configuration),
|
private val jvmGeneratorExtensions: JvmGeneratorExtensionsImpl = JvmGeneratorExtensionsImpl(configuration),
|
||||||
private val evaluatorFragmentInfoForPsi2Ir: EvaluatorFragmentInfo? = null,
|
private val evaluatorFragmentInfoForPsi2Ir: EvaluatorFragmentInfo? = null,
|
||||||
private val stubSettings: StubSettings = StubSettings(),
|
private val ideCodegenSettings: IdeCodegenSettings = IdeCodegenSettings(),
|
||||||
) : CodegenFactory {
|
) : CodegenFactory {
|
||||||
|
|
||||||
@IDEAPluginsCompatibilityAPI(IDEAPlatforms._221, message = "Please migrate to the other constructor", plugins = "Android Studio")
|
@IDEAPluginsCompatibilityAPI(IDEAPlatforms._221, message = "Please migrate to the other constructor", plugins = "Android Studio")
|
||||||
@@ -81,15 +83,17 @@ open class JvmIrCodegenFactory(
|
|||||||
externalSymbolTable,
|
externalSymbolTable,
|
||||||
jvmGeneratorExtensions,
|
jvmGeneratorExtensions,
|
||||||
evaluatorFragmentInfoForPsi2Ir,
|
evaluatorFragmentInfoForPsi2Ir,
|
||||||
StubSettings(shouldStubAndNotLinkUnboundSymbols = shouldStubAndNotLinkUnboundSymbols),
|
IdeCodegenSettings(shouldStubAndNotLinkUnboundSymbols = shouldStubAndNotLinkUnboundSymbols),
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param shouldStubOrphanedExpectSymbols See [stubOrphanedExpectSymbols].
|
* @param shouldStubOrphanedExpectSymbols See [stubOrphanedExpectSymbols].
|
||||||
|
* @param shouldDeduplicateBuiltInSymbols See [SymbolTableWithBuiltInsDeduplication].
|
||||||
*/
|
*/
|
||||||
data class StubSettings(
|
data class IdeCodegenSettings(
|
||||||
val shouldStubAndNotLinkUnboundSymbols: Boolean = false,
|
val shouldStubAndNotLinkUnboundSymbols: Boolean = false,
|
||||||
val shouldStubOrphanedExpectSymbols: Boolean = false,
|
val shouldStubOrphanedExpectSymbols: Boolean = false,
|
||||||
|
val shouldDeduplicateBuiltInSymbols: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class JvmIrBackendInput(
|
data class JvmIrBackendInput(
|
||||||
@@ -110,6 +114,7 @@ open class JvmIrCodegenFactory(
|
|||||||
val notifyCodegenStart: () -> Unit,
|
val notifyCodegenStart: () -> Unit,
|
||||||
) : CodegenFactory.CodegenInput
|
) : CodegenFactory.CodegenInput
|
||||||
|
|
||||||
|
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||||
override fun convertToIr(input: CodegenFactory.IrConversionInput): JvmIrBackendInput {
|
override fun convertToIr(input: CodegenFactory.IrConversionInput): JvmIrBackendInput {
|
||||||
val enableIdSignatures =
|
val enableIdSignatures =
|
||||||
input.configuration.getBoolean(JVMConfigurationKeys.LINK_VIA_SIGNATURES) ||
|
input.configuration.getBoolean(JVMConfigurationKeys.LINK_VIA_SIGNATURES) ||
|
||||||
@@ -122,7 +127,10 @@ open class JvmIrCodegenFactory(
|
|||||||
val signaturer =
|
val signaturer =
|
||||||
if (enableIdSignatures) JvmIdSignatureDescriptor(mangler)
|
if (enableIdSignatures) JvmIdSignatureDescriptor(mangler)
|
||||||
else DisabledIdSignatureDescriptor
|
else DisabledIdSignatureDescriptor
|
||||||
val symbolTable = SymbolTable(signaturer, IrFactoryImpl)
|
val symbolTable = when {
|
||||||
|
ideCodegenSettings.shouldDeduplicateBuiltInSymbols -> SymbolTableWithBuiltInsDeduplication(signaturer, IrFactoryImpl)
|
||||||
|
else -> SymbolTable(signaturer, IrFactoryImpl)
|
||||||
|
}
|
||||||
mangler to symbolTable
|
mangler to symbolTable
|
||||||
}
|
}
|
||||||
val messageLogger = input.configuration.irMessageLogger
|
val messageLogger = input.configuration.irMessageLogger
|
||||||
@@ -142,6 +150,13 @@ open class JvmIrCodegenFactory(
|
|||||||
jvmGeneratorExtensions,
|
jvmGeneratorExtensions,
|
||||||
fragmentContext = if (evaluatorFragmentInfoForPsi2Ir != null) FragmentContext() else null,
|
fragmentContext = if (evaluatorFragmentInfoForPsi2Ir != null) FragmentContext() else null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Built-ins deduplication must be enabled immediately so that there is no chance for duplicate built-in symbols to occur. For
|
||||||
|
// example, the creation of `IrPluginContextImpl` might already lead to duplicate built-in symbols via `BuiltinSymbolsBase`.
|
||||||
|
if (symbolTable is SymbolTableWithBuiltInsDeduplication) {
|
||||||
|
(psi2irContext.irBuiltIns as? IrBuiltInsOverDescriptors)?.let { symbolTable.bindIrBuiltIns(it) }
|
||||||
|
}
|
||||||
|
|
||||||
val pluginExtensions = IrGenerationExtension.getInstances(input.project)
|
val pluginExtensions = IrGenerationExtension.getInstances(input.project)
|
||||||
|
|
||||||
val stubGenerator =
|
val stubGenerator =
|
||||||
@@ -215,8 +230,7 @@ open class JvmIrCodegenFactory(
|
|||||||
irLinker.deserializeIrModuleHeader(it, kotlinLibrary, _moduleName = it.name.asString())
|
irLinker.deserializeIrModuleHeader(it, kotlinLibrary, _moduleName = it.name.asString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val irProviders = if (ideCodegenSettings.shouldStubAndNotLinkUnboundSymbols) {
|
||||||
val irProviders = if (stubSettings.shouldStubAndNotLinkUnboundSymbols) {
|
|
||||||
listOf(stubGenerator)
|
listOf(stubGenerator)
|
||||||
} else {
|
} else {
|
||||||
val stubGeneratorForMissingClasses = DeclarationStubGeneratorForNotFoundClasses(stubGenerator)
|
val stubGeneratorForMissingClasses = DeclarationStubGeneratorForNotFoundClasses(stubGenerator)
|
||||||
@@ -240,7 +254,7 @@ open class JvmIrCodegenFactory(
|
|||||||
// We need to compile all files we reference in Klibs
|
// We need to compile all files we reference in Klibs
|
||||||
irModuleFragment.files.addAll(dependencies.flatMap { it.files })
|
irModuleFragment.files.addAll(dependencies.flatMap { it.files })
|
||||||
|
|
||||||
if (stubSettings.shouldStubOrphanedExpectSymbols) {
|
if (ideCodegenSettings.shouldStubOrphanedExpectSymbols) {
|
||||||
irModuleFragment.stubOrphanedExpectSymbols(stubGenerator)
|
irModuleFragment.stubOrphanedExpectSymbols(stubGenerator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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.backend.jvm
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.containingPackage
|
||||||
|
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||||
|
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.util.IdSignatureComposer
|
||||||
|
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||||
|
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [SymbolTable] that de-duplicates built-ins for which multiple descriptor instances exist.
|
||||||
|
*
|
||||||
|
* For example, if multiple descriptors exist for the built-in `Boolean` type, the ordinary [SymbolTable] generates multiple
|
||||||
|
* [IrClassSymbol]s, one for each descriptor instance. If the wrong `Boolean` class symbol is encountered in `JvmSharedVariableManager`,
|
||||||
|
* an exception will occur (see KTIJ-24335).
|
||||||
|
*
|
||||||
|
* This class instead relies on the symbols declared in [IrBuiltIns]. Any descriptors which reference a built-in by name will be resolved
|
||||||
|
* to that built-in, regardless of whether that descriptor's instance matches with the descriptor of the built-in.
|
||||||
|
*/
|
||||||
|
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||||
|
class SymbolTableWithBuiltInsDeduplication(
|
||||||
|
signaturer: IdSignatureComposer,
|
||||||
|
irFactory: IrFactory,
|
||||||
|
) : SymbolTable(signaturer, irFactory) {
|
||||||
|
/**
|
||||||
|
* As long as [IrBuiltIns] aren't bound, the symbol table will operate like [SymbolTable], as it must be assumed that built-ins are
|
||||||
|
* still being created.
|
||||||
|
*/
|
||||||
|
private var irBuiltIns: IrBuiltInsOverDescriptors? = null
|
||||||
|
|
||||||
|
fun bindIrBuiltIns(irBuiltIns: IrBuiltInsOverDescriptors) {
|
||||||
|
if (this.irBuiltIns == null) {
|
||||||
|
this.irBuiltIns = irBuiltIns
|
||||||
|
} else {
|
||||||
|
throw IllegalStateException("`irBuiltIns` have already been bound.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ObsoleteDescriptorBasedAPI
|
||||||
|
override fun referenceClass(descriptor: ClassDescriptor): IrClassSymbol {
|
||||||
|
val irBuiltIns = this.irBuiltIns ?: return super.referenceClass(descriptor)
|
||||||
|
|
||||||
|
// We need to find out whether `descriptor` is possibly a built-in symbol before it's actually retrieved to break recursion as
|
||||||
|
// `irBuiltIns.findClass` uses `referenceClass` recursively.
|
||||||
|
val builtInDescriptor = irBuiltIns.findBuiltInClassDescriptor(descriptor)
|
||||||
|
if (builtInDescriptor != null) {
|
||||||
|
// We need to delegate to the supertype implementation here to break recursion. `findBuiltInClassDescriptor` will return
|
||||||
|
// `descriptor` even if `descriptor` was found via `findBuiltInClassDescriptor`.
|
||||||
|
return super.referenceClass(builtInDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.referenceClass(descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrBuiltInsOverDescriptors.findBuiltInClassDescriptor(descriptor: ClassDescriptor): ClassDescriptor? {
|
||||||
|
val packageFqName = descriptor.containingPackage() ?: return null
|
||||||
|
return findClassDescriptor(descriptor.name, packageFqName)
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-3
@@ -21,7 +21,10 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
|||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
|
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
|
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
||||||
import org.jetbrains.kotlin.ir.descriptors.*
|
import org.jetbrains.kotlin.ir.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
||||||
@@ -486,10 +489,13 @@ class IrBuiltInsOverDescriptors(
|
|||||||
) as? ClassDescriptor)?.let { symbolTable.referenceClass(it) }
|
) as? ClassDescriptor)?.let { symbolTable.referenceClass(it) }
|
||||||
|
|
||||||
override fun findClass(name: Name, packageFqName: FqName): IrClassSymbol? =
|
override fun findClass(name: Name, packageFqName: FqName): IrClassSymbol? =
|
||||||
(builtIns.builtInsModule.getPackage(packageFqName).memberScope.getContributedClassifier(
|
findClassDescriptor(name, packageFqName)?.let { symbolTable.referenceClass(it) }
|
||||||
|
|
||||||
|
fun findClassDescriptor(name: Name, packageFqName: FqName): ClassDescriptor? =
|
||||||
|
builtIns.builtInsModule.getPackage(packageFqName).memberScope.getContributedClassifier(
|
||||||
name,
|
name,
|
||||||
NoLookupLocation.FROM_BACKEND
|
NoLookupLocation.FROM_BACKEND
|
||||||
) as? ClassDescriptor)?.let { symbolTable.referenceClass(it) }
|
) as? ClassDescriptor
|
||||||
|
|
||||||
override fun findBuiltInClassMemberFunctions(builtInClass: IrClassSymbol, name: Name): Iterable<IrSimpleFunctionSymbol> =
|
override fun findBuiltInClassMemberFunctions(builtInClass: IrClassSymbol, name: Name): Iterable<IrSimpleFunctionSymbol> =
|
||||||
builtInClass.descriptor.unsubstitutedMemberScope
|
builtInClass.descriptor.unsubstitutedMemberScope
|
||||||
|
|||||||
Reference in New Issue
Block a user