[FIR/Native] Move fir-related code from backend.native module to new one

^KT-61249
This commit is contained in:
Pavel Kunyavskiy
2023-08-16 11:22:08 +02:00
committed by Space Team
parent fdab636a1b
commit 8bcd1f4efa
16 changed files with 103 additions and 47 deletions
@@ -171,6 +171,7 @@ dependencies {
compilerApi project(":kotlin-util-klib-metadata")
compilerApi project(":compiler:ir.serialization.common")
compilerApi project(":compiler:fir:fir-serialization")
compilerApi project(":compiler:fir:native")
compilerApi project(":compiler:ir.psi2ir")
compilerApi project(":kotlin-native-shared")
use(IntellijDependenciesKt) {
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.isEmpty
import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.backend.native.FirNativeKotlinMangler
import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor
import org.jetbrains.kotlin.fir.pipeline.convertToIrAndActualize
import org.jetbrains.kotlin.fir.signaturer.Ir2FirManglerAdapter
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.backend.native.FirNativeOverrideChecker
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
import org.jetbrains.kotlin.fir.pipeline.FirResult
import org.jetbrains.kotlin.fir.pipeline.ModuleCompilerAnalyzedOutput
@@ -1,121 +0,0 @@
/*
* 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.konan
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinExportChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinMangleComputer
import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleMode
import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.backend.FirMangler
import org.jetbrains.kotlin.fir.backend.FirExportCheckerVisitor
import org.jetbrains.kotlin.fir.backend.FirMangleComputer
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
class FirNativeKotlinMangler : FirMangler() {
override fun getMangleComputer(mode: MangleMode, compatibleMode: Boolean): KotlinMangleComputer<FirDeclaration> {
return FirNativeKotlinMangleComputer(StringBuilder(256), mode)
}
override fun getExportChecker(compatibleMode: Boolean): KotlinExportChecker<FirDeclaration> = FirNativeExportCheckerVisitor()
override fun FirDeclaration.isExported(compatibleMode: Boolean): Boolean =
getExportChecker(compatibleMode).check(this, SpecialDeclarationType.REGULAR)
}
class FirNativeExportCheckerVisitor : FirExportCheckerVisitor() {
/**
* mimics AbstractKonanDescriptorMangler::DeclarationDescriptor.isPlatformSpecificExport()
*/
override fun FirDeclaration.isPlatformSpecificExported(): Boolean {
fun List<FirAnnotation>.hasAnnotation(fqName: FqName) = hasAnnotation(ClassId.topLevel(fqName), moduleData.session)
if (this is FirCallableDeclaration && isSubstitutionOrIntersectionOverride)
return false
if (annotations.hasAnnotation(RuntimeNames.symbolNameAnnotation)) {
// Treat any `@SymbolName` declaration as exported.
return true
}
if (annotations.hasAnnotation(KonanFqNames.gcUnsafeCall)) {
// Treat any `@GCUnsafeCall` declaration as exported.
return true
}
if (annotations.hasAnnotation(RuntimeNames.exportForCppRuntime)) {
// Treat any `@ExportForCppRuntime` declaration as exported.
return true
}
if (annotations.hasAnnotation(RuntimeNames.cnameAnnotation)) {
// Treat `@CName` declaration as exported.
return true
}
if (annotations.hasAnnotation(RuntimeNames.exportForCompilerAnnotation)) {
return true
}
return false
}
}
class FirNativeKotlinMangleComputer(
builder: StringBuilder,
mode: MangleMode,
) : FirMangleComputer(builder, mode) {
override fun copy(newMode: MangleMode): FirNativeKotlinMangleComputer =
FirNativeKotlinMangleComputer(builder, newMode)
/**
* mimics FunctionDescriptor.platformSpecificFunctionName()
*/
override fun FirFunction.platformSpecificFunctionName(): String? {
val session = moduleData.session
val scopeSession = ScopeSession()
getInitMethodIfObjCConstructor(session, scopeSession)
?.getObjCMethodInfoFromOverriddenFunctions(session, scopeSession)
?.let {
return buildString {
receiverParameter?.let {
append(it.getTypeName(session))
append(".")
}
append("objc:")
append(it.selector)
if ((this@platformSpecificFunctionName is FirConstructor) && isObjCConstructor(session)) append("#Constructor")
if (this@platformSpecificFunctionName is FirPropertyAccessor) {
append("#Accessor")
}
}
}
return null
}
override fun FirFunction.specialValueParamPrefix(param: FirValueParameter): String {
val session = moduleData.session
return if (this.hasObjCMethodAnnotation(session) || this.hasObjCFactoryAnnotation(session) || this.isObjCClassMethod(session))
"${param.name}:"
else
""
}
}
private fun FirReceiverParameter.getTypeName(session: FirSession): String {
return when (val symbol = typeRef.coneType.toSymbol(session)) {
is FirClassLikeSymbol -> symbol.classId.shortClassName.asString()
is FirTypeParameterSymbol -> symbol.name.asString()
else -> error("Unexpected symbol class: ${symbol?.javaClass?.name}")
}
}
@@ -1,74 +0,0 @@
/*
* 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.konan
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker
import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker
class FirNativeOverrideChecker(private val session: FirSession) : FirOverrideChecker {
private val standardOverrideChecker = FirStandardOverrideChecker(session)
override fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean =
overrideCandidate.isPlatformOverriddenFunction(session, baseDeclaration)
?: standardOverrideChecker.isOverriddenFunction(overrideCandidate, baseDeclaration)
override fun isOverriddenProperty(overrideCandidate: FirCallableDeclaration, baseDeclaration: FirProperty): Boolean =
overrideCandidate.isPlatformOverriddenProperty(baseDeclaration)
?: standardOverrideChecker.isOverriddenProperty(overrideCandidate, baseDeclaration)
// FIXME KT-57640: Revise the necessity of platform-specific property overridability handling
private fun FirCallableDeclaration.isPlatformOverriddenProperty(baseDeclaration: FirProperty): Boolean? {
if (this !is FirProperty || name != baseDeclaration.name) {
return null
}
if (this.isExternalObjCClassProperty(session) && baseDeclaration.isExternalObjCClassProperty(session))
return true
return null
}
/**
* mimics ObjCOverridabilityCondition.isOverridable
*/
private fun FirSimpleFunction.isPlatformOverriddenFunction(session: FirSession, baseDeclaration: FirSimpleFunction): Boolean? {
if (this.name != baseDeclaration.name) {
return null
}
val superInfo = baseDeclaration.decodeObjCMethodAnnotation(session) ?: return null
val subInfo = decodeObjCMethodAnnotation(session)
return if (subInfo != null) {
// Overriding Objective-C method by Objective-C method in interop stubs.
// Don't even check method signatures, so this check is weaker than the standard one
superInfo.selector == subInfo.selector
} else {
// Overriding Objective-C method by Kotlin method.
if (!parameterNamesMatch(this, baseDeclaration)) false else null
}
}
/**
* mimics ObjCInteropKt.parameterNamesMatch
*/
private fun parameterNamesMatch(first: FirSimpleFunction, second: FirSimpleFunction): Boolean {
// The original Objective-C method selector is represented as
// function name and parameter names (except first).
if (first.valueParameters.size != second.valueParameters.size) {
return false
}
first.valueParameters.forEachIndexed { index, parameter ->
if (index > 0 && parameter.name != second.valueParameters[index].name) {
return false
}
}
return true
}
}
@@ -1,162 +0,0 @@
/*
* 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.konan
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.getContainingClass
import org.jetbrains.kotlin.fir.resolve.getContainingDeclaration
import org.jetbrains.kotlin.fir.resolve.providers.toSymbol
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenFunctions
import org.jetbrains.kotlin.fir.scopes.processAllFunctions
import org.jetbrains.kotlin.fir.scopes.scopeForClass
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.DFS
private val objCDirectClassId = ClassId.topLevel(objCDirectFqName)
private val objCMethodClassId = ClassId.topLevel(objCMethodFqName)
private val objCObjectClassId = ClassId.topLevel(objCObjectFqName)
private val objCFactoryClassId = ClassId.topLevel(objCFactoryFqName)
private val objCConstructorClassId = ClassId.topLevel(objCConstructorFqName)
private val externalObjCClassClassId = ClassId.topLevel(externalObjCClassFqName)
@OptIn(SymbolInternals::class)
internal fun FirFunction.getObjCMethodInfoFromOverriddenFunctions(session: FirSession, scopeSession: ScopeSession): ObjCMethodInfo? {
decodeObjCMethodAnnotation(session)?.let {
return it
}
// recursively find ObjCMethod annotation in getDirectOverriddenFunctions() (same as `overriddenDescriptors` in K1)
return when (val symbol = this.symbol) {
is FirNamedFunctionSymbol -> {
val firClassSymbol = containingClassLookupTag()?.toSymbol(session) as FirClassSymbol<*>?
firClassSymbol?.let {
val unsubstitutedScope = it.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false, memberRequiredPhase = null)
// call of `processFunctionsByName()` is needed only for necessary side-effect before `getDirectOverriddenFunctions` call
unsubstitutedScope.processFunctionsByName(symbol.name) {}
unsubstitutedScope.getDirectOverriddenFunctions(symbol).firstNotNullOfOrNull {
assert(it.fir != this) { "Function ${symbol.name}() is wrongly contained in its own getDirectOverriddenFunctions" }
it.fir.getObjCMethodInfoFromOverriddenFunctions(session, scopeSession)
}
}
}
else -> null
}
}
/**
* mimics ConstructorDescriptor.getObjCInitMethod()
*/
@OptIn(SymbolInternals::class)
private fun FirConstructor.getObjCInitMethod(session: FirSession, scopeSession: ScopeSession): FirFunction? {
this.annotations.getAnnotationByClassId(objCConstructorClassId, session)?.let { annotation ->
val initSelector: String = annotation.constStringArgument("initSelector")
val classSymbol = containingClassLookupTag()?.toSymbol(session) as FirClassSymbol<*>
val initSelectors = mutableListOf<FirFunction>()
classSymbol.fir.scopeForClass(ConeSubstitutor.Empty, session, scopeSession, classSymbol.toLookupTag(), memberRequiredPhase = null)
.processAllFunctions {
if (it.fir.decodeObjCMethodAnnotation(session)?.selector == initSelector)
initSelectors.add(it.fir)
}
return initSelectors.singleOrNull()
?: error("expected one init method for $classSymbol $initSelector, got ${initSelectors.size}")
}
return null
}
/**
* mimics FunctionDescriptor.decodeObjCMethodAnnotation()
*/
internal fun FirFunction.decodeObjCMethodAnnotation(session: FirSession): ObjCMethodInfo? =
annotations.getAnnotationByClassId(objCMethodClassId, session)?.let {
ObjCMethodInfo(
selector = it.constStringArgument("selector"),
encoding = it.constStringArgument("encoding"),
isStret = it.constBooleanArgumentOrNull("isStret") ?: false,
directSymbol = annotations.getAnnotationByClassId(objCDirectClassId, session)?.constStringArgument("symbol"),
)
}
private fun FirAnnotation.constStringArgument(argumentName: String): String =
constArgument(argumentName) as? String ?: error("Expected string constant value of argument '$argumentName' at annotation $this")
private fun FirAnnotation.constBooleanArgumentOrNull(argumentName: String): Boolean? =
constArgument(argumentName) as Boolean?
private fun FirAnnotation.constArgument(argumentName: String) =
(argumentMapping.mapping[Name.identifier(argumentName)] as? FirConstExpression<*>)?.value
internal fun FirFunction.hasObjCFactoryAnnotation(session: FirSession) = this.annotations.hasAnnotation(objCFactoryClassId, session)
internal fun FirFunction.hasObjCMethodAnnotation(session: FirSession) = this.annotations.hasAnnotation(objCMethodClassId, session)
/**
* almost mimics FunctionDescriptor.isObjCClassMethod(), apart from `it.isObjCClass()` changed to `it.symbol.isObjCClass(session)` for simplicity
*/
internal fun FirFunction.isObjCClassMethod(session: FirSession) =
getContainingClass(session).let { it is FirClass && it.symbol.isObjCClass(session) }
/**
* mimics ConstructorDescriptor.isObjCConstructor()
*/
internal fun FirConstructor.isObjCConstructor(session: FirSession) =
this.annotations.hasAnnotation(objCConstructorClassId, session)
/**
* mimics IrClass.isObjCClass()
*/
private fun FirClassSymbol<*>.isObjCClass(session: FirSession) = classId.packageFqName != interopPackageName &&
selfOrAnySuperClass(session) {
it.classId == objCObjectClassId
}
/**
* almost mimics `IrClass.selfOrAnySuperClass()` apart from using of classsymbol instead of class itself, to use `classId.toSymbol()`
*/
private fun FirClassSymbol<*>.selfOrAnySuperClass(session: FirSession, pred: (FirClassSymbol<*>) -> Boolean): Boolean =
DFS.ifAny(
listOf(this),
{ current ->
current.resolvedSuperTypes.mapNotNull {
(it.classId?.toSymbol(session) as? FirClassLikeSymbol)?.fullyExpandedClass(session)
}
},
pred
)
internal fun FirFunction.getInitMethodIfObjCConstructor(session: FirSession, scopeSession: ScopeSession): FirFunction? =
if (this is FirConstructor && isObjCConstructor(session))
getObjCInitMethod(session, scopeSession)
else
this
internal fun FirProperty.isExternalObjCClassProperty(session: FirSession) =
(getContainingClassSymbol(session) as? FirClassSymbol)?.isExternalObjCClass(session) == true
internal fun FirClassSymbol<*>.isExternalObjCClass(session: FirSession): Boolean =
isObjCClass(session) &&
parentsWithSelf(session).filterIsInstance<FirClass>().any {
it.hasAnnotation(externalObjCClassClassId, session)
}
@OptIn(SymbolInternals::class)
fun FirClassSymbol<*>.parentsWithSelf(session: FirSession): Sequence<FirClassLikeDeclaration> {
return generateSequence<FirClassLikeDeclaration>(fir) { it.getContainingDeclaration(session) }
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NativeRuntimeNames
internal const val NATIVE_PTR_NAME = "NativePtr"
internal const val NON_NULL_NATIVE_PTR_NAME = "NonNullNativePtr"
@@ -34,7 +35,7 @@ object KonanFqNames {
val objCMethod = FqName("kotlinx.cinterop.ObjCMethod")
val hasFinalizer = FqName("kotlin.native.internal.HasFinalizer")
val hasFreezeHook = FqName("kotlin.native.internal.HasFreezeHook")
val gcUnsafeCall = FqName("kotlin.native.internal.GCUnsafeCall")
val gcUnsafeCall = NativeRuntimeNames.Annotations.gcUnsafeCallClassId.asSingleFqName()
val eagerInitialization = FqName("kotlin.native.EagerInitialization")
val noReorderFields = FqName("kotlin.native.internal.NoReorderFields")
val objCName = FqName("kotlin.native.ObjCName")
@@ -20,6 +20,8 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.NativeForwardDeclarationKind
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NativeStandardInteropNames
import org.jetbrains.kotlin.native.interop.ObjCMethodInfo
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
import org.jetbrains.kotlin.resolve.constants.BooleanValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -30,14 +32,14 @@ import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.supertypes
internal val interopPackageName = InteropFqNames.packageName
internal val objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject"))
internal val objCObjectFqName = NativeStandardInteropNames.objCObjectClassId.asSingleFqName()
private val objCClassFqName = interopPackageName.child(Name.identifier("ObjCClass"))
private val objCProtocolFqName = interopPackageName.child(Name.identifier("ObjCProtocol"))
internal val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass"))
internal val objCDirectFqName = interopPackageName.child(Name.identifier("ObjCDirect"))
internal val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod"))
internal val objCConstructorFqName = FqName("kotlinx.cinterop.ObjCConstructor")
internal val objCFactoryFqName = interopPackageName.child(Name.identifier("ObjCFactory"))
internal val externalObjCClassFqName = NativeStandardInteropNames.externalObjCClassClassId.asSingleFqName()
internal val objCDirectFqName = NativeStandardInteropNames.objCDirectClassId.asSingleFqName()
internal val objCMethodFqName = NativeStandardInteropNames.objCMethodClassId.asSingleFqName()
internal val objCConstructorFqName = NativeStandardInteropNames.objCConstructorClassId.asSingleFqName()
internal val objCFactoryFqName = NativeStandardInteropNames.objCFactoryClassId.asSingleFqName()
fun ClassDescriptor.isObjCClass(): Boolean =
this.containingDeclaration.fqNameSafe != interopPackageName &&
@@ -112,11 +114,6 @@ fun ClassDescriptor.isKotlinObjCClass(): Boolean = this.isObjCClass() && !this.i
fun IrClass.isKotlinObjCClass(): Boolean = this.isObjCClass() && !this.isExternalObjCClass()
data class ObjCMethodInfo(val selector: String,
val encoding: String,
val isStret: Boolean,
val directSymbol: String?)
private fun FunctionDescriptor.decodeObjCMethodAnnotation(): ObjCMethodInfo? {
assert (this.kind.isReal)
@@ -1,13 +1,14 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.NativeRuntimeNames
object RuntimeNames {
val symbolNameAnnotation = FqName("kotlin.native.SymbolName")
val cnameAnnotation = FqName("kotlin.native.CName")
val symbolNameAnnotation = NativeRuntimeNames.Annotations.symbolNameClassId.asSingleFqName()
val cnameAnnotation = NativeRuntimeNames.Annotations.cNameClassId.asSingleFqName()
val frozenAnnotation = FqName("kotlin.native.internal.Frozen")
val exportForCppRuntime = FqName("kotlin.native.internal.ExportForCppRuntime")
val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
val exportForCppRuntime = NativeRuntimeNames.Annotations.exportForCppRuntimeClassId.asSingleFqName()
val exportForCompilerAnnotation = NativeRuntimeNames.Annotations.exportForCompilerClassId.asSingleFqName()
val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo")
val cCall = FqName("kotlinx.cinterop.internal.CCall")
val cStructMemberAt = FqName("kotlinx.cinterop.internal.CStruct.MemberAt")
@@ -51,6 +51,7 @@ import org.jetbrains.kotlin.konan.ForeignExceptionMode
import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.native.interop.ObjCMethodInfo
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module