[Native][IR] Partial linkage support for cached inline functions

- Support partial linkage for declarations from inline functions that are lazily deserialized from a KLIB with static cache
- Linker API: Split IrModuleDeserializer.deserializeIrSymbol() into deserializeIrSymbolOrFail() and tryDeserializeIrSymbol()
- Linker API: Refactor KotlinIrLinker.handleSignatureIdNotFoundInModuleWithDependencies() to deserializeOrReturnUnboundIrSymbolIfPartialLinkageEnabled()

^KT-52478
This commit is contained in:
Dmitriy Dolovov
2022-06-23 01:20:52 +04:00
parent 66de825508
commit 532b9e2c5f
18 changed files with 378 additions and 202 deletions
@@ -13,6 +13,7 @@ dependencies {
compileOnly(project(":kotlin-reflect-api"))
compileOnly(intellijCore())
compileOnly(commonDependency("org.jetbrains.intellij.deps:trove4j"))
}
sourceSets {
@@ -97,7 +97,7 @@ abstract class BasicIrModuleDeserializer(
// TODO: fix to topLevel checker
override fun contains(idSig: IdSignature): Boolean = idSig in moduleReversedFileIndex
open fun tryDeserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol? {
override fun tryDeserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol? {
val topLevelSignature = idSig.topLevelSignature()
val fileLocalDeserializationState = moduleReversedFileIndex[topLevelSignature] ?: return null
@@ -107,9 +107,9 @@ abstract class BasicIrModuleDeserializer(
return fileLocalDeserializationState.fileDeserializer.symbolDeserializer.deserializeIrSymbol(idSig, symbolKind)
}
final override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind) =
tryDeserializeIrSymbol(idSig, symbolKind)
?: error("No file for ${idSig.topLevelSignature()} (@ $idSig) in module $moduleDescriptor")
override fun deserializedSymbolNotFound(idSig: IdSignature): Nothing {
error("No file for ${idSig.topLevelSignature()} (@ $idSig) in module $moduleDescriptor")
}
override val moduleFragment: IrModuleFragment = IrModuleFragmentImpl(moduleDescriptor, linker.builtIns, emptyList())
@@ -113,14 +113,16 @@ class CurrentModuleWithICDeserializer(
return idSig in dirtyDeclarations || idSig.topLevelSignature() in icDeserializer || idSig in delegate
}
override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
override fun tryDeserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
dirtyDeclarations[idSig]?.let { return it }
if (idSig.topLevelSignature() in icDeserializer) return icDeserializer.deserializeIrSymbol(idSig, symbolKind)
if (idSig.topLevelSignature() in icDeserializer) return icDeserializer.deserializeIrSymbolOrFail(idSig, symbolKind)
return delegate.deserializeIrSymbol(idSig, symbolKind)
return delegate.deserializeIrSymbolOrFail(idSig, symbolKind)
}
override fun deserializedSymbolNotFound(idSig: IdSignature): Nothing = delegate.deserializedSymbolNotFound(idSig)
override fun addModuleReachableTopLevel(idSig: IdSignature) {
assert(idSig in icDeserializer)
icDeserializer.addModuleReachableTopLevel(idSig)
@@ -74,18 +74,8 @@ class FileDeserializationState(
::addIdSignature,
linker::handleExpectActualMapping,
symbolProcessor = linker.symbolProcessor,
) { idSig, symbolKind ->
val topLevelSig = idSig.topLevelSignature()
val actualModuleDeserializer = moduleDeserializer.findModuleDeserializerForTopLevelId(topLevelSig)
?: run {
// The symbol might be gone in newer version of dependency KLIB. Then the KLIB that was compiled against
// the older version of dependency KLIB will still have a reference to non-existing symbol. And the linker will have to
// handle such situation appropriately. See KT-41378.
linker.handleSignatureIdNotFoundInModuleWithDependencies(idSig, moduleDeserializer)
}
actualModuleDeserializer.deserializeIrSymbol(idSig, symbolKind)
) { idSignature, symbolKind ->
linker.deserializeOrReturnUnboundIrSymbolIfPartialLinkageEnabled(idSignature, symbolKind, moduleDeserializer)
}
val declarationDeserializer = IrDeclarationDeserializer(
@@ -58,7 +58,8 @@ enum class IrModuleDeserializerKind {
abstract class IrModuleDeserializer(private val _moduleDescriptor: ModuleDescriptor?, val libraryAbiVersion: KotlinAbiVersion) {
abstract operator fun contains(idSig: IdSignature): Boolean
abstract fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol
abstract fun tryDeserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol?
abstract fun deserializedSymbolNotFound(idSig: IdSignature): Nothing
val moduleDescriptor: ModuleDescriptor get() = _moduleDescriptor ?: error("No ModuleDescriptor provided")
@@ -72,7 +73,7 @@ abstract class IrModuleDeserializer(private val _moduleDescriptor: ModuleDescrip
val signature = symbol.signature
require(signature != null) { "Symbol is not public API: ${symbol.descriptor}" }
assert(symbol.hasDescriptor)
deserializeIrSymbol(signature, symbol.kind())
deserializeIrSymbolOrFail(signature, symbol.kind())
}
open val klib: IrLibrary get() = error("Unsupported operation")
@@ -104,6 +105,9 @@ abstract class IrModuleDeserializer(private val _moduleDescriptor: ModuleDescrip
open fun signatureDeserializerForFile(fileName: String): IdSignatureDeserializer = error("Unsupported")
}
fun IrModuleDeserializer.deserializeIrSymbolOrFail(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol =
tryDeserializeIrSymbol(idSig, symbolKind) ?: deserializedSymbolNotFound(idSig)
// Used to resolve built in symbols like `kotlin.ir.internal.*` or `kotlin.FunctionN`
class IrModuleDeserializerWithBuiltIns(
private val builtIns: IrBuiltIns,
@@ -199,16 +203,17 @@ class IrModuleDeserializerWithBuiltIns(
}
}
override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
override fun tryDeserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol? {
irBuiltInsMap[idSig]?.let { return it }
val topLevel = idSig.topLevelSignature()
if (checkIsFunctionInterface(topLevel)) return resolveFunctionalInterface(idSig, symbolKind)
return delegate.deserializeIrSymbol(idSig, symbolKind)
return delegate.tryDeserializeIrSymbol(idSig, symbolKind)
}
override fun deserializedSymbolNotFound(idSig: IdSignature): Nothing = delegate.deserializedSymbolNotFound(idSig)
override fun declareIrSymbol(symbol: IrSymbol) {
val signature = symbol.signature
if (signature != null && checkIsFunctionInterface(signature))
@@ -253,11 +258,13 @@ open class CurrentModuleDeserializer(
) : IrModuleDeserializer(moduleFragment.descriptor, KotlinAbiVersion.CURRENT) {
override fun contains(idSig: IdSignature): Boolean = false // TODO:
override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
override fun tryDeserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): Nothing =
error("Unreachable execution: there could not be back-links (sig: $idSig)")
}
override fun declareIrSymbol(symbol: IrSymbol) {}
override fun deserializedSymbolNotFound(idSig: IdSignature): Nothing =
error("Unreachable execution: there could not be back-links (sig: $idSig)")
override fun declareIrSymbol(symbol: IrSymbol) = Unit
override val kind get() = IrModuleDeserializerKind.CURRENT
}
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.backend.common.overrides.FileLocalAwareLinker
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.*
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedDeclarationsProcessor
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedDeclarationsSupport
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -19,14 +18,7 @@ import org.jetbrains.kotlin.ir.builders.TranslationPluginContext
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.linkage.IrDeserializer
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrErrorType
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.uniqueName
import org.jetbrains.kotlin.name.Name
@@ -59,34 +51,36 @@ abstract class KotlinIrLinker(
private lateinit var linkerExtensions: Collection<IrDeserializer.IrLinkerExtension>
protected open val unlinkedDeclarationsSupport: UnlinkedDeclarationsSupport = UnlinkedDeclarationsSupport.DISABLED
protected open val userVisibleIrModulesSupport: UserVisibleIrModulesSupport = UserVisibleIrModulesSupport.DEFAULT
protected open val unlinkedDeclarationsSupport: UnlinkedDeclarationsSupport get() = UnlinkedDeclarationsSupport.DISABLED
protected open val userVisibleIrModulesSupport: UserVisibleIrModulesSupport get() = UserVisibleIrModulesSupport.DEFAULT
fun handleSignatureIdNotFoundInModuleWithDependencies(
fun deserializeOrReturnUnboundIrSymbolIfPartialLinkageEnabled(
idSignature: IdSignature,
symbolKind: BinarySymbolData.SymbolKind,
moduleDeserializer: IrModuleDeserializer
): IrModuleDeserializer {
if (unlinkedDeclarationsSupport.allowUnboundSymbols) {
return object : IrModuleDeserializer(null, KotlinAbiVersion.CURRENT) {
override fun contains(idSig: IdSignature): Boolean = false
): IrSymbol {
val topLevelSignature: IdSignature = idSignature.topLevelSignature()
override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
return referenceDeserializedSymbol(symbolTable, null, symbolKind, idSig)
}
// Note: The top-level symbol might be gone in newer version of dependency KLIB. Then the KLIB that was compiled against
// the older version of dependency KLIB will still have a reference to non-existing symbol. And the linker will have to
// handle such situation appropriately. See KT-41378.
val actualModuleDeserializer: IrModuleDeserializer? = moduleDeserializer.findModuleDeserializerForTopLevelId(topLevelSignature)
override val moduleFragment: IrModuleFragment
get() = TODO("Not yet implemented")
override val moduleDependencies: Collection<IrModuleDeserializer> get() = emptyList()
override val kind: IrModuleDeserializerKind
get() = TODO("Not yet implemented")
}
} else {
throw SignatureIdNotFoundInModuleWithDependencies(
idSignature = idSignature,
problemModuleDeserializer = moduleDeserializer,
allModuleDeserializers = deserializersForModules.values,
userVisibleIrModulesSupport = userVisibleIrModulesSupport
).raiseIssue(messageLogger)
// Note: It might happen that the top-level symbol still exists in KLIB, but nested symbol has been removed.
// Then the `actualModuleDeserializer` will be non-null, but `actualModuleDeserializer.tryDeserializeIrSymbol()` call
// will return null.
val symbol: IrSymbol? = actualModuleDeserializer?.tryDeserializeIrSymbol(idSignature, symbolKind)
return symbol ?: run {
if (unlinkedDeclarationsSupport.allowUnboundSymbols)
referenceDeserializedSymbol(symbolTable, null, symbolKind, idSignature)
else
throw SignatureIdNotFoundInModuleWithDependencies(
idSignature = idSignature,
problemModuleDeserializer = moduleDeserializer,
allModuleDeserializers = deserializersForModules.values,
userVisibleIrModulesSupport = userVisibleIrModulesSupport
).raiseIssue(messageLogger)
}
}
@@ -221,93 +215,16 @@ abstract class KotlinIrLinker(
deserializersForModules.values.forEach { it.init() }
}
private fun markUnlinkedClassifiers(): Set<IrClassifierSymbol> {
if (!unlinkedDeclarationsSupport.allowUnboundSymbols) return emptySet()
val entries = fakeOverrideBuilder.fakeOverrideCandidates
val result = mutableSetOf<IrClassifierSymbol>()
fun IrType.isUnlinked(visited: MutableSet<IrClassifierSymbol>): Boolean {
val simpleType = this as? IrSimpleType ?: return this !is IrErrorType
val classifier = simpleType.classifier
if (!classifier.isBound) {
return true
}
if (classifier in result) {
return true
}
if (!visited.add(classifier)) return false
val superTypes = when (val decl = classifier.owner) {
is IrClass -> decl.superTypes
is IrTypeParameter -> decl.superTypes
else -> emptyList()
}
if (superTypes.any { it.isUnlinked(visited) }) {
result.add(classifier)
return true
}
for (ta in simpleType.arguments) {
if (ta is IrTypeProjection) {
val projected = ta.type
if (projected.isUnlinked(visited)) {
return true
}
}
}
return false
}
fun IrClass.isUnlinked(visited: MutableSet<IrClassifierSymbol>): Boolean {
if (symbol in result) return true
for (s in superTypes) {
if (s.isUnlinked(visited)) {
result.add(symbol)
return true
}
}
return false
}
val toRemove = mutableListOf<IrClass>()
for (e in entries) {
val klass = e.key
if (klass.isUnlinked(mutableSetOf())) {
toRemove.add(klass)
}
}
toRemove.forEach { entries.remove(it) }
return result
}
private fun applyToModules(transformer: IrElementTransformerVoid) {
deserializersForModules.values.forEach { it.moduleFragment.transformChildrenVoid(transformer) }
}
override fun postProcess() {
finalizeExpectActualLinker()
val unlinkedClassifiers = markUnlinkedClassifiers()
unlinkedDeclarationsSupport.markUsedClassifiersExcludingUnlinkedFromFakeOverrideBuilding(fakeOverrideBuilder)
fakeOverrideBuilder.provideFakeOverrides()
triedToDeserializeDeclarationForSymbol.clear()
unlinkedDeclarationsSupport.whenUnboundSymbolsAllowed { unlinkedMarkerTypeHandler ->
val t = UnlinkedDeclarationsProcessor(builtIns, unlinkedClassifiers, unlinkedMarkerTypeHandler, messageLogger)
t.addLinkageErrorIntoUnlinkedClasses()
applyToModules(t.signatureTransformer())
applyToModules(t.usageTransformer())
unlinkedDeclarationsSupport.processUnlinkedDeclarations(messageLogger) {
deserializersForModules.values.map { it.moduleFragment }
}
// TODO: fix IrPluginContext to make it not produce additional external reference
@@ -359,7 +276,7 @@ abstract class KotlinIrLinker(
?: error("No module for name '$moduleName' found")
assert(signature == signature.topLevelSignature()) { "Signature '$signature' has to be top level" }
if (signature !in moduleDeserializer) error("No signature $signature in module $moduleName")
return moduleDeserializer.deserializeIrSymbol(signature, topLevelKindToSymbolKind(kind)).also {
return moduleDeserializer.deserializeIrSymbolOrFail(signature, topLevelKindToSymbolKind(kind)).also {
deserializeAllReachableTopLevels()
}
}
@@ -0,0 +1,176 @@
/*
* 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.backend.common.serialization.unlinked
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UsedClassifierSymbolStatus.*
import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrConstantObject
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.types.IrErrorType
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.util.parentClassOrNull
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
abstract class BasicUnlinkedDeclarationsSupport : UnlinkedDeclarationsSupport {
protected abstract val handler: UnlinkedDeclarationsSupport.UnlinkedMarkerTypeHandler
protected abstract val builtIns: IrBuiltIns
private val usedClassifierSymbols = UsedClassifierSymbols()
final override fun markUsedClassifiersExcludingUnlinkedFromFakeOverrideBuilding(fakeOverrideBuilder: FakeOverrideBuilder) {
if (!allowUnboundSymbols) return
val entries = fakeOverrideBuilder.fakeOverrideCandidates
if (entries.isEmpty()) return
val toRemove = hashSetOf<IrClass>()
for (clazz in entries.keys) {
if (clazz.symbol.isUnlinkedClassifier(visited = hashSetOf())) {
toRemove += clazz
}
}
entries -= toRemove
}
private fun IrType.isUnlinkedType(visited: MutableSet<IrClassifierSymbol>): Boolean {
val simpleType = this as? IrSimpleType ?: return this !is IrErrorType
if (simpleType.classifier.isUnlinkedClassifier(visited))
return true
for (argument in simpleType.arguments) {
if (argument is IrTypeProjection) {
if (argument.type.isUnlinkedType(visited))
return true
}
}
return false
}
private fun IrClassifierSymbol.isUnlinkedClassifier(visited: MutableSet<IrClassifierSymbol>): Boolean {
when (val status = usedClassifierSymbols[this]) {
UNLINKED, LINKED -> return status.isUnlinked
null -> {
// Unknown classifier. Continue.
}
}
if (!isBound || (hasDescriptor && descriptor is NotFoundClasses.MockClassDescriptor))
return usedClassifierSymbols.register(this, UNLINKED)
if (!visited.add(this))
return false // Recursion avoidance.
when (val classifier = owner) {
is IrClass -> {
if (classifier.parentClassOrNull?.symbol?.isUnlinkedClassifier(visited) == true)
return usedClassifierSymbols.register(this, UNLINKED)
for (typeParameter in classifier.typeParameters) {
if (typeParameter.superTypes.any { it.isUnlinkedType(visited) })
return usedClassifierSymbols.register(this, UNLINKED)
}
if (classifier.superTypes.any { it.isUnlinkedType(visited) })
return usedClassifierSymbols.register(this, UNLINKED)
}
is IrTypeParameter -> {
if (classifier.superTypes.any { it.isUnlinkedType(visited) })
return usedClassifierSymbols.register(this, UNLINKED)
}
}
return usedClassifierSymbols.register(this, LINKED)
}
override fun markUsedClassifiersInInlineLazyIrFunction(function: IrFunction) {
if (!allowUnboundSymbols) return
function.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
fun visitType(type: IrType?) {
type?.isUnlinkedType(visited = hashSetOf())
}
override fun visitValueParameter(declaration: IrValueParameter) {
visitType(declaration.type)
super.visitValueParameter(declaration)
}
override fun visitTypeParameter(declaration: IrTypeParameter) {
declaration.superTypes.forEach(::visitType)
super.visitTypeParameter(declaration)
}
override fun visitFunction(declaration: IrFunction) {
visitType(declaration.returnType)
super.visitFunction(declaration)
}
override fun visitField(declaration: IrField) {
visitType(declaration.type)
super.visitField(declaration)
}
override fun visitVariable(declaration: IrVariable) {
visitType(declaration.type)
super.visitVariable(declaration)
}
override fun visitExpression(expression: IrExpression) {
visitType(expression.type)
super.visitExpression(expression)
}
override fun visitClassReference(expression: IrClassReference) {
visitType(expression.classType)
super.visitClassReference(expression)
}
override fun visitConstantObject(expression: IrConstantObject) {
expression.typeArguments.forEach(::visitType)
super.visitConstantObject(expression)
}
override fun visitTypeOperator(expression: IrTypeOperatorCall) {
visitType(expression.typeOperand)
super.visitTypeOperator(expression)
}
})
}
override fun processUnlinkedDeclarations(messageLogger: IrMessageLogger, lazyRoots: () -> List<IrElement>) {
if (!allowUnboundSymbols) return
val processor = UnlinkedDeclarationsProcessor(builtIns, usedClassifierSymbols, handler, messageLogger)
processor.addLinkageErrorIntoUnlinkedClasses()
val roots = lazyRoots()
val signatureTransformer = processor.signatureTransformer()
roots.forEach { it.transformChildrenVoid(signatureTransformer) }
val usageTransformer = processor.usageTransformer()
roots.forEach { it.transformChildrenVoid(usageTransformer) }
}
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.backend.common.serialization.unlinked
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedDeclarationsSupport.UnlinkedMarkerTypeHandler
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UsedClassifierSymbolStatus.Companion.isUnlinked
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
@@ -27,39 +28,34 @@ import org.jetbrains.kotlin.name.Name
internal class UnlinkedDeclarationsProcessor(
private val builtIns: IrBuiltIns,
private val unlinkedClassifiers: Set<IrClassifierSymbol>,
private val usedClassifierSymbols: UsedClassifierSymbols,
private val unlinkedMarkerTypeHandler: UnlinkedMarkerTypeHandler,
private val messageLogger: IrMessageLogger
) {
companion object {
private val errorOrigin = object : IrStatementOriginImpl("LINKAGE ERROR") {}
private val ERROR_ORIGIN = object : IrStatementOriginImpl("LINKAGE ERROR") {}
}
fun addLinkageErrorIntoUnlinkedClasses() {
for (u in unlinkedClassifiers) {
if (u is IrClassSymbol) {
val klass = u.owner
val anonInitializer = klass.declarations.firstOrNull { it is IrAnonymousInitializer } as IrAnonymousInitializer? ?: run {
builtIns.irFactory.createAnonymousInitializer(
klass.startOffset,
klass.endOffset,
IrDeclarationOrigin.DEFINED,
IrAnonymousInitializerSymbolImpl()
).also {
it.body = builtIns.irFactory.createBlockBody(klass.startOffset, klass.endOffset)
it.parent = klass
klass.declarations.add(it)
}
usedClassifierSymbols.forEachClassSymbolToPatch { unlinkedSymbol ->
val clazz = unlinkedSymbol.owner
clazz.reportUnlinkedSymbolsWarning("Class", clazz.fqNameForIrSerialization)
val anonInitializer = clazz.declarations.firstNotNullOfOrNull { it as? IrAnonymousInitializer }
?: builtIns.irFactory.createAnonymousInitializer(
clazz.startOffset,
clazz.endOffset,
IrDeclarationOrigin.DEFINED,
IrAnonymousInitializerSymbolImpl()
).also {
it.body = builtIns.irFactory.createBlockBody(clazz.startOffset, clazz.endOffset)
it.parent = clazz
clazz.declarations.add(it)
}
anonInitializer.body.statements.clear()
anonInitializer.body.statements.clear()
anonInitializer.body.statements.add(clazz.throwLinkageError(clazz.symbol))
klass.reportUnlinkedSymbolsWarning("Class", klass.fqNameForIrSerialization)
anonInitializer.body.statements.add(klass.throwLinkageError(klass.symbol))
klass.superTypes = klass.superTypes.filter { !it.isUnlinked() }
}
clazz.superTypes = clazz.superTypes.filter { !it.isUnlinked() }
}
}
@@ -255,7 +251,7 @@ internal class UnlinkedDeclarationsProcessor(
return false
}
private fun IrClassifierSymbol.isUnlinked(): Boolean = !isBound || this in unlinkedClassifiers
private fun IrClassifierSymbol.isUnlinked(): Boolean = !isBound || usedClassifierSymbols[this].isUnlinked
private fun IrType.isUnlinked(): Boolean {
val simpleType = this as? IrSimpleType ?: return false
@@ -287,7 +283,7 @@ internal class UnlinkedDeclarationsProcessor(
private fun IrElement.throwLinkageError(unlinkedSymbol: IrSymbol?, message: String = "Unlinked IR symbol"): IrCall {
val messageLiteral = message + unlinkedSymbol?.signature?.render()?.let { " $it" }.orEmpty()
val irCall = IrCallImpl(startOffset, endOffset, builtIns.nothingType, builtIns.linkageErrorSymbol, 0, 1, errorOrigin)
val irCall = IrCallImpl(startOffset, endOffset, builtIns.nothingType, builtIns.linkageErrorSymbol, 0, 1, ERROR_ORIGIN)
irCall.putValueArgument(0, IrConstImpl.string(startOffset, endOffset, builtIns.stringType, messageLiteral))
return irCall
}
@@ -338,7 +334,7 @@ internal class UnlinkedDeclarationsProcessor(
if (!symbol.isUnlinked() && !expression.type.isUnlinked()) return expression
reportWarning(
"Accessing declaration contains unlinked symbol ${symbol.signature?.render() ?: ""}",
"Accessing declaration with unlinked symbol ${symbol.signature?.render() ?: ""}",
currentFile?.location(expression.startOffset)
)
@@ -5,11 +5,22 @@
package org.jetbrains.kotlin.backend.common.serialization.unlinked
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.IrMessageLogger
interface UnlinkedDeclarationsSupport {
val allowUnboundSymbols: Boolean
fun <T : Any> whenUnboundSymbolsAllowed(action: (UnlinkedMarkerTypeHandler) -> T): T?
/** For general use in IR linker. */
fun markUsedClassifiersExcludingUnlinkedFromFakeOverrideBuilding(fakeOverrideBuilder: FakeOverrideBuilder)
/** For local use only in inline lazy-IR functions. */
fun markUsedClassifiersInInlineLazyIrFunction(function: IrFunction)
fun processUnlinkedDeclarations(messageLogger: IrMessageLogger, lazyRoots: () -> List<IrElement>)
interface UnlinkedMarkerTypeHandler {
val unlinkedMarkerType: IrType
@@ -19,7 +30,9 @@ interface UnlinkedDeclarationsSupport {
companion object {
val DISABLED = object : UnlinkedDeclarationsSupport {
override val allowUnboundSymbols get() = false
override fun <T : Any> whenUnboundSymbolsAllowed(action: (UnlinkedMarkerTypeHandler) -> T): T? = null
override fun markUsedClassifiersExcludingUnlinkedFromFakeOverrideBuilding(fakeOverrideBuilder: FakeOverrideBuilder) = Unit
override fun markUsedClassifiersInInlineLazyIrFunction(function: IrFunction) = Unit
override fun processUnlinkedDeclarations(messageLogger: IrMessageLogger, lazyRoots: () -> List<IrElement>) = Unit
}
}
}
@@ -0,0 +1,60 @@
/*
* 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.backend.common.serialization.unlinked
import gnu.trove.THashSet
import gnu.trove.TObjectByteHashMap
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UsedClassifierSymbolStatus.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
internal enum class UsedClassifierSymbolStatus(val isUnlinked: Boolean) {
/** IR symbol of unlinked classifier. */
UNLINKED(true),
/** IR symbol of linked classifier. */
LINKED(false);
companion object {
val UsedClassifierSymbolStatus?.isUnlinked: Boolean get() = this?.isUnlinked == true
}
}
internal class UsedClassifierSymbols {
private val symbols = TObjectByteHashMap<IrClassifierSymbol>()
private val patchedSymbols = THashSet<IrClassSymbol>() // To avoid re-patching what already has been patched.
fun forEachClassSymbolToPatch(patchAction: (IrClassSymbol) -> Unit) {
symbols.forEachEntry { symbol, code ->
if (symbol.isBound && code.status == UNLINKED && symbol is IrClassSymbol && patchedSymbols.add(symbol)) {
patchAction(symbol)
}
true
}
}
operator fun get(symbol: IrClassifierSymbol): UsedClassifierSymbolStatus? = symbols[symbol].status
fun register(symbol: IrClassifierSymbol, status: UsedClassifierSymbolStatus): Boolean {
symbols.put(symbol, status.code)
return status.isUnlinked
}
companion object {
private inline val Byte.status: UsedClassifierSymbolStatus?
get() = when (this) {
1.toByte() -> UNLINKED
2.toByte() -> LINKED
else -> null
}
private inline val UsedClassifierSymbolStatus.code: Byte
get() = when (this) {
UNLINKED -> 1.toByte()
LINKED -> 2.toByte()
}
}
}
@@ -376,7 +376,7 @@ fun getIrModuleInfoForKlib(
val irBuiltIns = IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable)
val allowUnboundSymbols = configuration[JSConfigurationKeys.PARTIAL_LINKAGE] ?: false
val unlinkedDeclarationsSupport = JsUnlinkedDeclarationsSupport(allowUnboundSymbols)
val unlinkedDeclarationsSupport = JsUnlinkedDeclarationsSupport(irBuiltIns, allowUnboundSymbols)
val irLinker = JsIrLinker(
currentModule = null,
@@ -435,7 +435,7 @@ fun getIrModuleInfoForSourceFiles(
}
val allowUnboundSymbols = configuration[JSConfigurationKeys.PARTIAL_LINKAGE] ?: false
val unlinkedDeclarationsSupport = JsUnlinkedDeclarationsSupport(allowUnboundSymbols)
val unlinkedDeclarationsSupport = JsUnlinkedDeclarationsSupport(irBuiltIns, allowUnboundSymbols)
val irLinker = JsIrLinker(
currentModule = psi2IrContext.moduleDescriptor,
@@ -30,7 +30,10 @@ class JsIrLinker(
override val translationPluginContext: TranslationPluginContext?,
private val icData: ICData? = null,
friendModules: Map<String, Collection<String>> = emptyMap(),
override val unlinkedDeclarationsSupport: UnlinkedDeclarationsSupport = JsUnlinkedDeclarationsSupport(allowUnboundSymbols = false),
override val unlinkedDeclarationsSupport: UnlinkedDeclarationsSupport = JsUnlinkedDeclarationsSupport(
builtIns,
allowUnboundSymbols = false
),
private val stubGenerator: DeclarationStubGenerator? = null
) : KotlinIrLinker(
currentModule = currentModule,
@@ -35,12 +35,8 @@ class JsLazyIrModuleDeserializer(
private val descriptorFinder = DescriptorByIdSignatureFinderImpl(moduleDescriptor, JsManglerDesc)
private fun resolveDescriptor(idSig: IdSignature): DeclarationDescriptor {
return descriptorFinder.findDescriptorBySignature(idSig) ?: error("No descriptor found for $idSig")
}
override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
val descriptor = resolveDescriptor(idSig)
override fun tryDeserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol? {
val descriptor = descriptorFinder.findDescriptorBySignature(idSig) ?: return null
val declaration = stubGenerator.run {
when (symbolKind) {
@@ -57,6 +53,8 @@ class JsLazyIrModuleDeserializer(
return declaration.symbol
}
override fun deserializedSymbolNotFound(idSig: IdSignature): Nothing = error("No descriptor found for $idSig")
@OptIn(ObsoleteDescriptorBasedAPI::class)
override fun declareIrSymbol(symbol: IrSymbol) {
if (symbol is IrFieldSymbol) {
@@ -5,19 +5,20 @@
package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedDeclarationsSupport
import org.jetbrains.kotlin.backend.common.serialization.unlinked.BasicUnlinkedDeclarationsSupport
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedDeclarationsSupport.UnlinkedMarkerTypeHandler
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.types.IrErrorType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.impl.IrErrorTypeImpl
import org.jetbrains.kotlin.types.Variance
class JsUnlinkedDeclarationsSupport(override val allowUnboundSymbols: Boolean) : UnlinkedDeclarationsSupport {
private val handler = object : UnlinkedMarkerTypeHandler {
class JsUnlinkedDeclarationsSupport(
override val builtIns: IrBuiltIns,
override val allowUnboundSymbols: Boolean
) : BasicUnlinkedDeclarationsSupport() {
override val handler = object : UnlinkedMarkerTypeHandler {
override val unlinkedMarkerType = IrErrorTypeImpl(null, emptyList(), Variance.INVARIANT)
override fun IrType.isUnlinkedMarkerType() = this is IrErrorType
}
override fun <T : Any> whenUnboundSymbolsAllowed(action: (UnlinkedMarkerTypeHandler) -> T): T? =
if (allowUnboundSymbols) action(handler) else null
}
@@ -134,12 +134,10 @@ class JvmIrLinker(
DescriptorByIdSignatureFinderImpl.LookupMode.MODULE_ONLY
)
private fun resolveDescriptor(idSig: IdSignature): DeclarationDescriptor {
return descriptorFinder.findDescriptorBySignature(idSig) ?: error("No descriptor found for $idSig")
}
private fun resolveDescriptor(idSig: IdSignature): DeclarationDescriptor? = descriptorFinder.findDescriptorBySignature(idSig)
override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
val descriptor = resolveDescriptor(idSig)
override fun tryDeserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol? {
val descriptor = resolveDescriptor(idSig) ?: return null
val declaration = stubGenerator.run {
when (symbolKind) {
@@ -156,6 +154,8 @@ class JvmIrLinker(
return declaration.symbol
}
override fun deserializedSymbolNotFound(idSig: IdSignature): Nothing = error("No descriptor found for $idSig")
override fun declareIrSymbol(symbol: IrSymbol) {
if (symbol is IrFieldSymbol) {
declareJavaFieldStub(symbol)
@@ -112,6 +112,7 @@ internal class IrProviderForCEnumAndCStructStubs(
generateIrIfNeeded(s, file)
s.owner
}
// TODO: better error message?
else -> error("Unexpected symbol kind $symbolKind for sig $idSignature")
}
}
@@ -606,8 +606,8 @@ internal class KonanIrLinker(
return cenumsProvider.getDeclaration(descriptor, idSig, file, symbolKind).symbol
}
override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
val descriptor = descriptorByIdSignatureFinder.findDescriptorBySignature(idSig) ?: error("Expecting descriptor for $idSig")
override fun tryDeserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol? {
val descriptor = descriptorByIdSignatureFinder.findDescriptorBySignature(idSig) ?: return null
// If library is cached we don't need to create an IrClass for struct or enum.
if (!isLibraryCached && descriptor.isCEnumsOrCStruct()) return resolveCEnumsOrStruct(descriptor, idSig, symbolKind)
@@ -616,6 +616,8 @@ internal class KonanIrLinker(
return symbolOwner.symbol
}
override fun deserializedSymbolNotFound(idSig: IdSignature): Nothing = error("No descriptor found for $idSig")
override val moduleFragment: IrModuleFragment = KonanIrModuleFragmentImpl(moduleDescriptor, builtIns)
override val moduleDependencies: Collection<IrModuleDeserializer> = listOfNotNull(forwardDeclarationDeserializer)
@@ -664,6 +666,8 @@ internal class KonanIrLinker(
return (stubGenerator.generateMemberStub(descriptor) as IrSymbolOwner).symbol
}
override fun deserializedSymbolNotFound(idSig: IdSignature): Nothing = error("No descriptor found for $idSig")
private val inlineFunctionReferences by lazy {
cachedLibraries.getLibraryCache(klib)!!.serializedInlineFunctionBodies.associateBy {
fileDeserializationStates[it.file].declarationDeserializer.symbolDeserializer.deserializeIdSignature(it.functionSignature)
@@ -673,7 +677,7 @@ internal class KonanIrLinker(
fun deserializeInlineFunction(function: IrFunction): InlineFunctionOriginInfo {
val packageFragment = function.getPackageFragment() as? IrExternalPackageFragment
?: error("Expected an external package fragment for ${function.render()}")
if (function.parents.any { (it as? IrFunction)?.isInline == true}) {
if (function.parents.any { (it as? IrFunction)?.isInline == true }) {
// Already deserialized by the top-most inline function.
return InlineFunctionOriginInfo(
function,
@@ -742,8 +746,13 @@ internal class KonanIrLinker(
}
}
unlinkedDeclarationsSupport.markUsedClassifiersExcludingUnlinkedFromFakeOverrideBuilding(fakeOverrideBuilder)
unlinkedDeclarationsSupport.markUsedClassifiersInInlineLazyIrFunction(function)
fakeOverrideBuilder.provideFakeOverrides()
unlinkedDeclarationsSupport.processUnlinkedDeclarations(linker.messageLogger) { listOf(function) }
return InlineFunctionOriginInfo(function, fileDeserializationState.file, inlineFunctionReference.startOffset, inlineFunctionReference.endOffset)
}
@@ -829,10 +838,10 @@ internal class KonanIrLinker(
override fun contains(idSig: IdSignature): Boolean = idSig.isForwardDeclarationSignature()
private fun resolveDescriptor(idSig: IdSignature): ClassDescriptor =
private fun resolveDescriptor(idSig: IdSignature): ClassDescriptor? =
with(idSig as IdSignature.CommonSignature) {
val classId = ClassId(packageFqName(), FqName(declarationFqName), false)
moduleDescriptor.findClassAcrossModuleDependencies(classId) ?: error("No declaration found with $idSig")
moduleDescriptor.findClassAcrossModuleDependencies(classId)
}
private fun buildForwardDeclarationStub(descriptor: ClassDescriptor): IrClass {
@@ -841,11 +850,11 @@ internal class KonanIrLinker(
}
}
override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
override fun tryDeserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol? {
require(symbolKind == BinarySymbolData.SymbolKind.CLASS_SYMBOL) {
"Only class could be a Forward declaration $idSig (kind $symbolKind)"
}
val descriptor = resolveDescriptor(idSig)
val descriptor = resolveDescriptor(idSig) ?: return null
val actualModule = descriptor.module
if (actualModule !== moduleDescriptor) {
val moduleDeserializer = resolveModuleDeserializer(actualModule, idSig)
@@ -856,6 +865,8 @@ internal class KonanIrLinker(
return declaredDeclaration.getOrPut(idSig) { buildForwardDeclarationStub(descriptor) }.symbol
}
override fun deserializedSymbolNotFound(idSig: IdSignature): Nothing = error("No descriptor found for $idSig")
override val moduleFragment: IrModuleFragment = KonanIrModuleFragmentImpl(moduleDescriptor, builtIns)
override val moduleDependencies: Collection<IrModuleDeserializer> = emptyList()
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedDeclarationsSupport
import org.jetbrains.kotlin.backend.common.serialization.unlinked.BasicUnlinkedDeclarationsSupport
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedDeclarationsSupport.UnlinkedMarkerTypeHandler
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.types.IrErrorType
@@ -16,10 +16,13 @@ import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
* Kotlin/Native backend does not support [IrErrorType].
* So, let's use a special instance of nullable [kotlin.Any] as a marker-type instead.
*/
class KonanUnlinkedDeclarationsSupport(irBuiltIns: IrBuiltIns, override val allowUnboundSymbols: Boolean) : UnlinkedDeclarationsSupport {
private val handler = object : UnlinkedMarkerTypeHandler {
class KonanUnlinkedDeclarationsSupport(
override val builtIns: IrBuiltIns,
override val allowUnboundSymbols: Boolean
) : BasicUnlinkedDeclarationsSupport() {
override val handler = object : UnlinkedMarkerTypeHandler {
override val unlinkedMarkerType = IrSimpleTypeImpl(
classifier = irBuiltIns.anyClass,
classifier = builtIns.anyClass,
hasQuestionMark = true,
arguments = emptyList(),
annotations = emptyList()
@@ -27,7 +30,4 @@ class KonanUnlinkedDeclarationsSupport(irBuiltIns: IrBuiltIns, override val allo
override fun IrType.isUnlinkedMarkerType(): Boolean = this === unlinkedMarkerType
}
override fun <T : Any> whenUnboundSymbolsAllowed(action: (UnlinkedMarkerTypeHandler) -> T): T? =
if (allowUnboundSymbols) action(handler) else null
}