From b9633375b4b4046b1b9569aaa43daa628b878fea Mon Sep 17 00:00:00 2001 From: Vladimir Sukharev Date: Thu, 30 Mar 2023 09:37:57 +0000 Subject: [PATCH] [K2/N] KT-56030: Support Objective-C overloading by param names only Merge-request: KT-MR-8901 Merged-by: Vladimir Sukharev --- .../kotlin/cli/js/klib/compilerPipeline.kt | 3 +- .../cli/common/FirSessionConstructionUtils.kt | 9 +- .../kotlin/fir/pipeline/convertToIr.kt | 9 +- .../fir/session/ComponentsContainers.kt | 3 + .../kotlin/fir/session/FirJsSessionFactory.kt | 2 - .../fir/session/FirNativeSessionFactory.kt | 4 +- .../fir/session/FirSessionFactoryHelper.kt | 2 - .../fir/backend/jvm/FirJvmKotlinMangler.kt | 20 +- .../fir/backend/jvm/FirJvmMangleComputer.kt | 247 +-------------- .../fir/backend/FirBasedKotlinMangler.kt | 31 ++ .../fir/backend/FirExportCheckerVisitor.kt | 66 ++++ .../kotlin/fir/backend/FirMangleComputer.kt | 292 ++++++++++++++++++ .../backend/generators/AdapterGenerator.kt | 2 - .../kotlin/fir/signaturer/FirMangler.kt | 30 ++ .../FirIntersectionScopeOverrideChecker.kt | 3 +- .../kotlin/fir/scopes/FirOverrideChecker.kt | 6 +- .../scopes/impl/FirClassUseSiteMemberScope.kt | 3 +- .../scopes/impl/FirDelegatedMemberScope.kt | 2 +- .../serialization/mangle/ManglerChecker.kt | 14 +- .../serialization/mangle/mangleUtils.kt | 2 +- .../jetbrains/kotlin/backend/konan/Fir2Ir.kt | 16 +- .../kotlin/backend/konan/FirFrontend.kt | 12 +- .../backend/konan/FirNativeKotlinMangler.kt | 121 ++++++++ .../backend/konan/FirNativeOverrideChecker.kt | 74 +++++ .../kotlin/backend/konan/FirObjCInterop.kt | 156 ++++++++++ .../kotlin/backend/konan/ObjCInterop.kt | 25 +- .../backend.native/tests/build.gradle | 7 +- 27 files changed, 852 insertions(+), 309 deletions(-) create mode 100644 compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirBasedKotlinMangler.kt create mode 100644 compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirExportCheckerVisitor.kt create mode 100644 compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirMangleComputer.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirNativeKotlinMangler.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirNativeOverrideChecker.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirObjCInterop.kt diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/compilerPipeline.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/compilerPipeline.kt index ef57d19f67d..ae5131dc25d 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/compilerPipeline.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/compilerPipeline.kt @@ -17,7 +17,6 @@ import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.prepareJsSessions import org.jetbrains.kotlin.config.CommonConfigurationKeys -import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector @@ -28,6 +27,7 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.backend.Fir2IrExtensions import org.jetbrains.kotlin.fir.backend.Fir2IrVisibilityConverter import org.jetbrains.kotlin.fir.backend.extractFirDeclarations +import org.jetbrains.kotlin.fir.backend.jvm.FirJvmKotlinMangler import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar @@ -133,6 +133,7 @@ fun transformFirToIr( linkViaSignatures = false, signatureComposerCreator = null, irMangler = JsManglerIr, + firManglerCreator = { FirJvmKotlinMangler() }, visibilityConverter = Fir2IrVisibilityConverter.Default, kotlinBuiltIns = builtInsModule ?: DefaultBuiltIns.Instance, diagnosticReporter = diagnosticsReporter, diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/FirSessionConstructionUtils.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/FirSessionConstructionUtils.kt index 23532c3a6e9..b6b4af5a7ea 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/FirSessionConstructionUtils.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/FirSessionConstructionUtils.kt @@ -9,10 +9,7 @@ import org.jetbrains.kotlin.KtSourceFile import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices import org.jetbrains.kotlin.cli.jvm.compiler.pipeline.GroupedKtSources import org.jetbrains.kotlin.config.* -import org.jetbrains.kotlin.fir.DependencyListForCliModule -import org.jetbrains.kotlin.fir.FirModuleData -import org.jetbrains.kotlin.fir.FirModuleDataImpl -import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.checkers.registerExtendedCommonCheckers import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider @@ -160,6 +157,7 @@ fun prepareNativeSessions( extensionRegistrars: List, isCommonSource: (F) -> Boolean, fileBelongsToModule: (F, String) -> Boolean, + registerExtraComponents: ((FirSession) -> Unit) = {}, ): List> { return prepareSessions( files, configuration, rootModuleName, NativePlatforms.unspecifiedNativePlatform, NativePlatformAnalyzerServices, @@ -171,7 +169,7 @@ fun prepareNativeSessions( libraryList.moduleDataProvider, extensionRegistrars, configuration.languageVersionSettings, - registerExtraComponents = {}, + registerExtraComponents, ) } ) { _, moduleData, sessionProvider, sessionConfigurator -> @@ -181,6 +179,7 @@ fun prepareNativeSessions( extensionRegistrars, configuration.languageVersionSettings, sessionConfigurator, + registerExtraComponents, ) } } diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt index b2a1ca28b2d..d3bc6c610e7 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.backend.jvm.FirJvmVisibilityConverter import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.resolve.ScopeSession +import org.jetbrains.kotlin.fir.signaturer.FirMangler import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrMangler import org.jetbrains.kotlin.ir.declarations.IrModuleFragment @@ -55,6 +56,7 @@ fun FirResult.convertToIrAndActualizeForJvm( linkViaSignatures = linkViaSignatures, signatureComposerCreator = { JvmIdSignatureDescriptor(JvmDescriptorMangler(null)) }, irMangler = JvmIrMangler, + firManglerCreator = { FirJvmKotlinMangler() }, visibilityConverter = FirJvmVisibilityConverter, diagnosticReporter = diagnosticReporter, languageVersionSettings = languageVersionSettings, @@ -67,6 +69,7 @@ fun FirResult.convertToIrAndActualize( linkViaSignatures: Boolean, signatureComposerCreator: (() -> IdSignatureComposer)?, irMangler: KotlinMangler.IrMangler, + firManglerCreator: () -> FirMangler, visibilityConverter: Fir2IrVisibilityConverter, kotlinBuiltIns: KotlinBuiltIns, diagnosticReporter: DiagnosticReporter, @@ -79,7 +82,7 @@ fun FirResult.convertToIrAndActualize( val commonMemberStorage = Fir2IrCommonMemberStorage( generateSignatures = linkViaSignatures, signatureComposerCreator = signatureComposerCreator, - manglerCreator = { FirJvmKotlinMangler() } // TODO: replace with potentially simpler version for other backends. + manglerCreator = firManglerCreator ) when (outputs.size) { @@ -94,7 +97,9 @@ fun FirResult.convertToIrAndActualize( irMangler, visibilityConverter, kotlinBuiltIns, - ) + ).also { result -> + fir2IrResultPostCompute(result) + } actualizationResult = null } else -> { diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt index 607a63e952d..5bca32bf0c1 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.impl.FirTypeResolverImpl import org.jetbrains.kotlin.fir.resolve.transformers.FirDummyCompilerLazyDeclarationResolver import org.jetbrains.kotlin.fir.resolve.transformers.PlatformSupertypeUpdater import org.jetbrains.kotlin.fir.resolve.transformers.plugin.GeneratedClassIndex +import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker import org.jetbrains.kotlin.fir.scopes.FirOverrideService import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper import org.jetbrains.kotlin.fir.scopes.PlatformSpecificOverridabilityRules @@ -70,6 +71,8 @@ fun FirSession.registerCommonComponents(languageVersionSettings: LanguageVersion register(FirSamConstructorStorage::class, FirSamConstructorStorage(this)) register(FirOverrideService::class, FirOverrideService(this)) register(FirDynamicMembersStorage::class, FirDynamicMembersStorage(this)) + register(FirEnumEntriesSupport::class, FirEnumEntriesSupport(this)) + register(FirOverrideChecker::class, FirStandardOverrideChecker(this)) } @OptIn(SessionConfiguration::class) diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJsSessionFactory.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJsSessionFactory.kt index 79764fb2517..0909aebe1d5 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJsSessionFactory.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJsSessionFactory.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.fir.resolve.providers.impl.FirBuiltinSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.impl.FirExtensionSyntheticFunctionInterfaceProvider import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper -import org.jetbrains.kotlin.fir.scopes.impl.FirEnumEntriesSupport import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.name.Name @@ -98,6 +97,5 @@ object FirJsSessionFactory : FirAbstractSessionFactory() { register(ConeCallConflictResolverFactory::class, JsCallConflictResolverFactory) register(FirPlatformClassMapper::class, FirPlatformClassMapper.Default) register(FirOverridesBackwardCompatibilityHelper::class, FirOverridesBackwardCompatibilityHelper.Default()) - register(FirEnumEntriesSupport::class, FirEnumEntriesSupport(this)) } } diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt index 1ca283e5129..36023011a11 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt @@ -37,7 +37,9 @@ object FirNativeSessionFactory : FirAbstractSessionFactory() { moduleDataProvider, languageVersionSettings, extensionRegistrars, - registerExtraComponents, + registerExtraComponents = { session -> + registerExtraComponents(session) + }, createKotlinScopeProvider = { FirKotlinScopeProvider { _, declaredMemberScope, _, _, _ -> declaredMemberScope } }, createProviders = { session, builtinsModuleData, kotlinScopeProvider -> val forwardDeclarationsModuleData = BinaryModuleData.createDependencyModuleData( diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirSessionFactoryHelper.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirSessionFactoryHelper.kt index 4f057b76ede..3bba78cf5ae 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirSessionFactoryHelper.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirSessionFactoryHelper.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider import org.jetbrains.kotlin.fir.resolve.calls.ConeCallConflictResolverFactory import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper -import org.jetbrains.kotlin.fir.scopes.impl.FirEnumEntriesSupport import org.jetbrains.kotlin.fir.session.environment.AbstractProjectEnvironment import org.jetbrains.kotlin.fir.session.environment.AbstractProjectFileSearchScope import org.jetbrains.kotlin.incremental.components.EnumWhenTracker @@ -128,6 +127,5 @@ object FirSessionFactoryHelper { register(ConeCallConflictResolverFactory::class, DefaultCallConflictResolverFactory) register(FirPlatformClassMapper::class, FirPlatformClassMapper.Default) register(FirOverridesBackwardCompatibilityHelper::class, FirOverridesBackwardCompatibilityHelper.Default()) - register(FirEnumEntriesSupport::class, FirEnumEntriesSupport(this)) } } diff --git a/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmKotlinMangler.kt b/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmKotlinMangler.kt index 16258b55e6a..1a885071d0c 100644 --- a/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmKotlinMangler.kt +++ b/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmKotlinMangler.kt @@ -7,27 +7,11 @@ package org.jetbrains.kotlin.fir.backend.jvm import org.jetbrains.kotlin.backend.common.serialization.mangle.* import org.jetbrains.kotlin.fir.NoMutableState +import org.jetbrains.kotlin.fir.backend.FirBasedKotlinMangler import org.jetbrains.kotlin.fir.declarations.FirDeclaration -import org.jetbrains.kotlin.fir.signaturer.FirMangler @NoMutableState -class FirJvmKotlinMangler : AbstractKotlinMangler(), FirMangler { - - override fun FirDeclaration.mangleString(compatibleMode: Boolean): String = getMangleComputer(MangleMode.FULL, compatibleMode).computeMangle(this) - - override fun FirDeclaration.signatureString(compatibleMode: Boolean): String = getMangleComputer(MangleMode.SIGNATURE, compatibleMode).computeMangle(this) - - override fun FirDeclaration.fqnString(compatibleMode: Boolean): String = getMangleComputer(MangleMode.FQNAME, compatibleMode).computeMangle(this) - - override fun FirDeclaration.isExported(compatibleMode: Boolean): Boolean = true - - override fun getExportChecker(compatibleMode: Boolean): KotlinExportChecker { - return object : KotlinExportChecker { - override fun check(declaration: FirDeclaration, type: SpecialDeclarationType): Boolean = true - - override fun FirDeclaration.isPlatformSpecificExported(): Boolean = true - } - } +class FirJvmKotlinMangler : FirBasedKotlinMangler() { override fun getMangleComputer(mode: MangleMode, compatibleMode: Boolean): KotlinMangleComputer { return FirJvmMangleComputer(StringBuilder(256), mode) diff --git a/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmMangleComputer.kt b/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmMangleComputer.kt index 65d99bcbbe8..2d3d333bcf5 100644 --- a/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmMangleComputer.kt +++ b/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmMangleComputer.kt @@ -5,25 +5,17 @@ package org.jetbrains.kotlin.fir.backend.jvm -import org.jetbrains.kotlin.backend.common.serialization.mangle.* -import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleMode +import org.jetbrains.kotlin.fir.backend.FirMangleComputer +import org.jetbrains.kotlin.fir.containingClassLookupTag import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor -import org.jetbrains.kotlin.fir.declarations.utils.isExpect -import org.jetbrains.kotlin.fir.declarations.utils.isStatic import org.jetbrains.kotlin.fir.java.declarations.FirJavaField -import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.originalForSubstitutionOverride +import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.providers.firProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider -import org.jetbrains.kotlin.fir.resolve.toSymbol -import org.jetbrains.kotlin.fir.signaturer.irName -import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag -import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol -import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid +import org.jetbrains.kotlin.fir.types.coneType /** * A mangle computer that generates a mangled name for a Kotlin declaration represented by [FirDeclaration]. @@ -31,20 +23,8 @@ import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid class FirJvmMangleComputer( builder: StringBuilder, mode: MangleMode, -) : BaseKotlinMangleComputer< - /*Declaration=*/FirDeclaration, - /*Type=*/ConeKotlinType, - /*TypeParameter=*/ConeTypeParameterLookupTag, - /*ValueParameter=*/FirValueParameter, - /*TypeParameterContainer=*/FirMemberDeclaration, - /*FunctionDeclaration=*/FirFunction, - /*Session=*/FirSession, - >(builder, mode) { - - override fun getTypeSystemContext(session: FirSession) = object : ConeInferenceContext { - override val session: FirSession - get() = session - } +) : FirMangleComputer(builder, mode) { + override val visitor = JvmVisitor() override fun FirFunction.platformSpecificSuffix(): String? = if (this is FirSimpleFunction && name.asString() == "main") @@ -56,6 +36,7 @@ class FirJvmMangleComputer( override fun copy(newMode: MangleMode): FirJvmMangleComputer = FirJvmMangleComputer(builder, newMode) + // FIXME this implementation causes the mangler to deliver different result than IR mangler. Consider using base method instead override fun FirDeclaration.visitParent() { val (parentPackageFqName, parentClassId) = when (this) { is FirCallableDeclaration -> this.containingClassLookupTag()?.classId?.let { it.packageFqName to it } ?: return @@ -75,71 +56,7 @@ class FirJvmMangleComputer( } } - override fun FirDeclaration.visit() { - accept(Visitor(), null) - } - - private fun FirFunction.mangleFunction(isCtor: Boolean, isStatic: Boolean, container: FirDeclaration) { - - isRealExpect = isRealExpect || (this as? FirMemberDeclaration)?.isExpect == true - - if (container is FirMemberDeclaration) { - typeParameterContainers.add(container) - } - visitParent() - - builder.appendName(MangleConstant.FUNCTION_NAME_PREFIX) - - platformSpecificFunctionName()?.let { - builder.append(it) - return - } - - val name = this.irName - builder.append(name.asString()) - - platformSpecificSuffix()?.let { - builder.append(MangleConstant.PLATFORM_FUNCTION_MARKER) - builder.append(it) - } - - mangleSignature(isCtor, isStatic, container) - } - - private fun FirFunction.mangleSignature(isCtor: Boolean, isStatic: Boolean, container: FirDeclaration) { - if (!mode.signature) { - return - } - - if (isStatic) { - builder.appendSignature(MangleConstant.STATIC_MEMBER_MARK) - } - - contextReceivers.forEach { - builder.appendSignature(MangleConstant.CONTEXT_RECEIVER_PREFIX) - mangleType(builder, it.typeRef.coneType, moduleData.session) - } - - val receiverType = receiverParameter?.typeRef ?: (this as? FirPropertyAccessor)?.propertySymbol?.fir?.receiverParameter?.typeRef - receiverType?.let { - builder.appendSignature(MangleConstant.EXTENSION_RECEIVER_PREFIX) - mangleType(builder, it.coneType, moduleData.session) - } - - valueParameters.collectForMangler(builder, MangleConstant.VALUE_PARAMETERS) { - appendSignature(specialValueParamPrefix(it)) - mangleValueParameter(this, it, moduleData.session) - } - (container as? FirTypeParametersOwner)?.typeParameters?.withIndex()?.toList().orEmpty() - .collectForMangler(builder, MangleConstant.TYPE_PARAMETERS) { (index, typeParameter) -> - mangleTypeParameter(this, typeParameter.symbol.toLookupTag(), index, moduleData.session) - } - - if (!isCtor && !returnTypeRef.isUnit && addReturnType()) { - mangleType(builder, returnTypeRef.coneType, moduleData.session) - } - } - + // FIXME this implementation causes the mangler to deliver different result than IR mangler. Consider using base method instead override fun getEffectiveParent(typeParameter: ConeTypeParameterLookupTag): FirMemberDeclaration = typeParameter.symbol.fir.run { fun FirTypeParameter.sameAs(other: FirTypeParameter) = @@ -161,124 +78,7 @@ class FirJvmMangleComputer( throw IllegalStateException("Should not be here!") } - override fun renderDeclaration(declaration: FirDeclaration) = declaration.render() - - override fun getTypeParameterName(typeParameter: ConeTypeParameterLookupTag) = typeParameter.name.asString() - - override fun isVararg(valueParameter: FirValueParameter) = valueParameter.isVararg - - override fun getValueParameterType(valueParameter: FirValueParameter) = valueParameter.returnTypeRef.coneType - - override fun getIndexOfTypeParameter(typeParameter: ConeTypeParameterLookupTag, container: FirMemberDeclaration) = - container.typeParameters.indexOf(typeParameter.symbol.fir) - - override fun mangleType(tBuilder: StringBuilder, type: ConeKotlinType, declarationSiteSession: FirSession) { - when (type) { - is ConeLookupTagBasedType -> { - when (val symbol = type.lookupTag.toSymbol(declarationSiteSession)) { - is FirTypeAliasSymbol -> { - mangleType(tBuilder, type.fullyExpandedType(declarationSiteSession), declarationSiteSession) - return - } - - is FirClassSymbol -> with(copy(MangleMode.FQNAME)) { symbol.fir.visit() } - is FirTypeParameterSymbol -> tBuilder.mangleTypeParameterReference(symbol.toLookupTag()) - // This is performed for a case with invisible class-like symbol in fake override - null -> (type.lookupTag as? ConeClassLikeLookupTag)?.let { - tBuilder.append(it.classId) - } - } - - mangleTypeArguments(tBuilder, type, declarationSiteSession) - - if (type.isMarkedNullable) { - tBuilder.appendSignature(MangleConstant.Q_MARK) - } - - if (type.hasEnhancedNullability) { - tBuilder.appendSignature(MangleConstant.ENHANCED_NULLABILITY_MARK) - } - } - - is ConeRawType -> { - mangleType(tBuilder, type.lowerBound, declarationSiteSession) - } - - is ConeFlexibleType -> { - with(declarationSiteSession.typeContext) { - // Need to reproduce type approximation done for flexible types in TypeTranslator. - // For now, we replicate the current behaviour of Fir2IrTypeConverter and just take the upper bound - val upper = type.upperBound - if (upper is ConeClassLikeType) { - val lower = type.lowerBound as? ConeClassLikeType ?: error("Expecting class-like type, got ${type.lowerBound}") - val intermediate = if (lower.lookupTag == upper.lookupTag) { - lower.replaceArguments(upper.getArguments()) - } else lower - val mixed = if (upper.isNullable) intermediate.makeNullable() else intermediate.makeDefinitelyNotNullOrNotNull() - mangleType(tBuilder, mixed as ConeKotlinType, declarationSiteSession) - } else mangleType(tBuilder, upper, declarationSiteSession) - } - } - - is ConeDefinitelyNotNullType -> { - // E.g. not-null type parameter in Java - mangleType(tBuilder, type.original, declarationSiteSession) - } - - is ConeCapturedType -> { - mangleType(tBuilder, type.lowerType ?: type.constructor.supertypes!!.first(), declarationSiteSession) - } - - is ConeIntersectionType -> { - // TODO: add intersectionTypeApproximation - mangleType(tBuilder, type.intersectedTypes.first(), declarationSiteSession) - } - - else -> error("Unexpected type $type") - } - } - - private inner class Visitor : FirVisitorVoid() { - - override fun visitElement(element: FirElement) = error("unexpected element ${element.render()}") - - override fun visitRegularClass(regularClass: FirRegularClass) { - isRealExpect = isRealExpect or regularClass.isExpect - typeParameterContainers.add(regularClass) - regularClass.mangleSimpleDeclaration(regularClass.name.asString()) - } - - override fun visitAnonymousObject(anonymousObject: FirAnonymousObject) { - anonymousObject.mangleSimpleDeclaration("") - } - - override fun visitVariable(variable: FirVariable) { - isRealExpect = isRealExpect or variable.isExpect - typeParameterContainers.add(variable) - variable.visitParent() - - val isStaticProperty = variable.isStatic - if (isStaticProperty) { - builder.appendSignature(MangleConstant.STATIC_MEMBER_MARK) - } - - variable.receiverParameter?.typeRef?.let { - builder.appendSignature(MangleConstant.EXTENSION_RECEIVER_PREFIX) - mangleType(builder, it.coneType, variable.moduleData.session) - } - - variable.typeParameters.withIndex().toList() - .collectForMangler(builder, MangleConstant.TYPE_PARAMETERS) { (index, typeParameter) -> - mangleTypeParameter(this, typeParameter.symbol.toLookupTag(), index, variable.moduleData.session) - } - - builder.append(variable.name.asString()) - } - - override fun visitProperty(property: FirProperty) { - visitVariable(property) - } - + protected inner class JvmVisitor : Visitor() { override fun visitField(field: FirField) { if (field is FirJavaField) { field.mangleSimpleDeclaration(field.name.asString()) @@ -286,30 +86,5 @@ class FirJvmMangleComputer( visitVariable(field) } } - - override fun visitEnumEntry(enumEntry: FirEnumEntry) { - enumEntry.mangleSimpleDeclaration(enumEntry.name.asString()) - } - - override fun visitTypeAlias(typeAlias: FirTypeAlias) = - typeAlias.mangleSimpleDeclaration(typeAlias.name.asString()) - - override fun visitSimpleFunction(simpleFunction: FirSimpleFunction) { - isRealExpect = isRealExpect || simpleFunction.isExpect - val isStatic = simpleFunction.isStatic - simpleFunction.mangleFunction(false, isStatic, simpleFunction) - } - - override fun visitConstructor(constructor: FirConstructor) = - constructor.mangleFunction(isCtor = true, isStatic = false, constructor) - - override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor) { - if (propertyAccessor is FirSyntheticPropertyAccessor) { - // No need to distinguish between the accessor and its delegate. - visitSimpleFunction(propertyAccessor.delegate) - } else { - propertyAccessor.mangleFunction(isCtor = false, propertyAccessor.isStatic, propertyAccessor.propertySymbol.fir) - } - } } } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirBasedKotlinMangler.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirBasedKotlinMangler.kt new file mode 100644 index 00000000000..4cde1a1af6f --- /dev/null +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirBasedKotlinMangler.kt @@ -0,0 +1,31 @@ +/* + * 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.fir.backend + +import org.jetbrains.kotlin.backend.common.serialization.mangle.AbstractKotlinMangler +import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinExportChecker +import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleMode +import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.signaturer.FirMangler + +abstract class FirBasedKotlinMangler : AbstractKotlinMangler(), FirMangler { + override fun FirDeclaration.mangleString(compatibleMode: Boolean): String = getMangleComputer(MangleMode.FULL, compatibleMode).computeMangle(this) + + override fun FirDeclaration.signatureString(compatibleMode: Boolean): String = getMangleComputer(MangleMode.SIGNATURE, compatibleMode).computeMangle(this) + + override fun FirDeclaration.fqnString(compatibleMode: Boolean): String = getMangleComputer(MangleMode.FQNAME, compatibleMode).computeMangle(this) + + override fun FirDeclaration.isExported(compatibleMode: Boolean): Boolean = true + + override fun getExportChecker(compatibleMode: Boolean): KotlinExportChecker { + return object : KotlinExportChecker { + override fun check(declaration: FirDeclaration, type: SpecialDeclarationType): Boolean = true + + override fun FirDeclaration.isPlatformSpecificExported(): Boolean = true + } + } +} \ No newline at end of file diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirExportCheckerVisitor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirExportCheckerVisitor.kt new file mode 100644 index 00000000000..f02f8978683 --- /dev/null +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirExportCheckerVisitor.kt @@ -0,0 +1,66 @@ +/* + * 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.fir.backend + +import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinExportChecker +import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType +import org.jetbrains.kotlin.backend.common.serialization.mangle.ir.isAnonymous +import org.jetbrains.kotlin.backend.common.serialization.mangle.publishedApiAnnotation +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.utils.visibility +import org.jetbrains.kotlin.fir.resolve.firClassLike +import org.jetbrains.kotlin.fir.resolve.getContainingDeclaration +import org.jetbrains.kotlin.fir.resolve.providers.toSymbol +import org.jetbrains.kotlin.fir.visitors.FirVisitor +import org.jetbrains.kotlin.name.ClassId + +abstract class FirExportCheckerVisitor : FirVisitor(), KotlinExportChecker { + override fun check(declaration: FirDeclaration, type: SpecialDeclarationType): Boolean = + declaration.accept(this, type) + + override fun visitElement(element: FirElement, data: SpecialDeclarationType): Boolean = + TODO("Should have not been reached") + + private fun D.globalMemberIsExported(): Boolean where D : FirMemberDeclaration { + val visibility = visibility + if (visibility.isPublicAPI || visibility === Visibilities.Internal) return true + if (visibility === Visibilities.Local) return false + return annotations.hasAnnotation(ClassId.topLevel(publishedApiAnnotation), moduleData.session) || isPlatformSpecificExported() + } + + private fun D.isExported(): Boolean where D : FirCallableDeclaration { + val classId = symbol.callableId.classId ?: return globalMemberIsExported() + return visibility !== Visibilities.Local && + classId.toSymbol(moduleData.session)!!.fir.accept(this@FirExportCheckerVisitor, SpecialDeclarationType.REGULAR) + } + + private fun D.isExported(): Boolean where D : FirClassLikeDeclaration { + val containingDeclaration = getContainingDeclaration(moduleData.session) ?: return globalMemberIsExported() + return visibility !== Visibilities.Local && + containingDeclaration.accept(this@FirExportCheckerVisitor, SpecialDeclarationType.REGULAR) + } + + override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: SpecialDeclarationType): Boolean = + !simpleFunction.name.isAnonymous && simpleFunction.isExported() + + override fun visitRegularClass(regularClass: FirRegularClass, data: SpecialDeclarationType): Boolean { + if (data == SpecialDeclarationType.ANON_INIT) return false + if (regularClass.name.isAnonymous) return false + return regularClass.isExported() + } + + override fun visitConstructor(constructor: FirConstructor, data: SpecialDeclarationType): Boolean { + return constructor.returnTypeRef.firClassLike(constructor.moduleData.session)!!.isExported() + } + + override fun visitProperty(property: FirProperty, data: SpecialDeclarationType): Boolean = property.isExported() + + override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: SpecialDeclarationType): Boolean = false + + override fun visitAnonymousObject(anonymousObject: FirAnonymousObject, data: SpecialDeclarationType): Boolean = false +} diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirMangleComputer.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirMangleComputer.kt new file mode 100644 index 00000000000..d270a978786 --- /dev/null +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/FirMangleComputer.kt @@ -0,0 +1,292 @@ +/* + * 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.fir.backend + +import org.jetbrains.kotlin.backend.common.serialization.mangle.BaseKotlinMangleComputer +import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleConstant +import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleMode +import org.jetbrains.kotlin.backend.common.serialization.mangle.collectForMangler +import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor +import org.jetbrains.kotlin.fir.declarations.utils.isExpect +import org.jetbrains.kotlin.fir.declarations.utils.isStatic +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider +import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.signaturer.irName +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag +import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag +import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid + +/** + * A base mangle computer that generates a mangled name for a Kotlin declaration represented by [FirDeclaration]. + */ +open class FirMangleComputer( + builder: StringBuilder, + mode: MangleMode, +) : BaseKotlinMangleComputer< + /*Declaration=*/FirDeclaration, + /*Type=*/ConeKotlinType, + /*TypeParameter=*/ConeTypeParameterLookupTag, + /*ValueParameter=*/FirValueParameter, + /*TypeParameterContainer=*/FirMemberDeclaration, + /*FunctionDeclaration=*/FirFunction, + /*Session=*/FirSession, + >(builder, mode) { + protected open val visitor = Visitor() + + override fun copy(newMode: MangleMode): FirMangleComputer = + FirMangleComputer(builder, newMode) + + override fun getTypeSystemContext(session: FirSession) = object : ConeInferenceContext { + override val session: FirSession + get() = session + } + + @OptIn(SymbolInternals::class) + override fun FirDeclaration.visitParent() { + val (parentPackageFqName, parentClassId) = when (this) { + is FirCallableDeclaration -> this.symbol.callableId.packageName.let { it to containingClassLookupTag()?.classId } + is FirClassLikeDeclaration -> this.symbol.classId.let { it.packageFqName to it.outerClassId } + else -> return + } + if (parentClassId != null && !parentClassId.isLocal) { + val parentClassLike = this.moduleData.session.symbolProvider.getClassLikeSymbolByClassId(parentClassId)?.fir + ?: error("Attempt to find parent ($parentClassId) for probably-local declaration!") + if (parentClassLike is FirRegularClass || parentClassLike is FirTypeAlias) { + parentClassLike.visit() + } else { + error("Strange class-like declaration: ${parentClassLike.render()}") + } + } else if (parentClassId == null && !parentPackageFqName.isRoot) { + builder.appendName(parentPackageFqName.asString()) + } + } + + override fun FirDeclaration.visit() { + accept(visitor, null) + } + + private fun FirFunction.mangleFunction(isCtor: Boolean, isStatic: Boolean, container: FirDeclaration) { + + isRealExpect = isRealExpect || (this as? FirMemberDeclaration)?.isExpect == true + + if (container is FirMemberDeclaration) { + typeParameterContainers.add(container) + } + visitParent() + + builder.appendName(MangleConstant.FUNCTION_NAME_PREFIX) + + platformSpecificFunctionName()?.let { + builder.append(it) + return + } + + val name = this.irName + builder.append(name.asString()) + + platformSpecificSuffix()?.let { + builder.append(MangleConstant.PLATFORM_FUNCTION_MARKER) + builder.append(it) + } + + mangleSignature(isCtor, isStatic, container) + } + + private fun FirFunction.mangleSignature(isCtor: Boolean, isStatic: Boolean, container: FirDeclaration) { + if (!mode.signature) { + return + } + + if (isStatic) { + builder.appendSignature(MangleConstant.STATIC_MEMBER_MARK) + } + + contextReceivers.forEach { + builder.appendSignature(MangleConstant.CONTEXT_RECEIVER_PREFIX) + mangleType(builder, it.typeRef.coneType, moduleData.session) + } + + val receiverType = receiverParameter?.typeRef ?: (this as? FirPropertyAccessor)?.propertySymbol?.fir?.receiverParameter?.typeRef + receiverType?.let { + builder.appendSignature(MangleConstant.EXTENSION_RECEIVER_PREFIX) + mangleType(builder, it.coneType, moduleData.session) + } + + valueParameters.collectForMangler(builder, MangleConstant.VALUE_PARAMETERS) { + appendSignature(specialValueParamPrefix(it)) + mangleValueParameter(this, it, moduleData.session) + } + (container as? FirTypeParametersOwner)?.typeParameters?.withIndex()?.toList().orEmpty() + .collectForMangler(builder, MangleConstant.TYPE_PARAMETERS) { (index, typeParameter) -> + mangleTypeParameter(this, typeParameter.symbol.toLookupTag(), index, moduleData.session) + } + + if (!isCtor && !returnTypeRef.isUnit && addReturnType()) { + mangleType(builder, returnTypeRef.coneType, moduleData.session) + } + } + + @OptIn(SymbolInternals::class) + override fun getEffectiveParent(typeParameter: ConeTypeParameterLookupTag): FirMemberDeclaration = typeParameter.symbol.fir.run { + this.containingDeclarationSymbol.fir as FirMemberDeclaration + } + + override fun renderDeclaration(declaration: FirDeclaration) = declaration.render() + + override fun getTypeParameterName(typeParameter: ConeTypeParameterLookupTag) = typeParameter.name.asString() + + override fun isVararg(valueParameter: FirValueParameter) = valueParameter.isVararg + + override fun getValueParameterType(valueParameter: FirValueParameter) = valueParameter.returnTypeRef.coneType + + override fun getIndexOfTypeParameter(typeParameter: ConeTypeParameterLookupTag, container: FirMemberDeclaration) = + container.typeParameters.indexOf(typeParameter.symbol.fir) + + override fun mangleType(tBuilder: StringBuilder, type: ConeKotlinType, declarationSiteSession: FirSession) { + when (type) { + is ConeLookupTagBasedType -> { + when (val symbol = type.lookupTag.toSymbol(declarationSiteSession)) { + is FirTypeAliasSymbol -> { + mangleType(tBuilder, type.fullyExpandedType(declarationSiteSession), declarationSiteSession) + return + } + + is FirClassSymbol -> with(copy(MangleMode.FQNAME)) { symbol.fir.visit() } + is FirTypeParameterSymbol -> tBuilder.mangleTypeParameterReference(symbol.toLookupTag()) + // This is performed for a case with invisible class-like symbol in fake override + null -> (type.lookupTag as? ConeClassLikeLookupTag)?.let { + tBuilder.append(it.classId) + } + } + + mangleTypeArguments(tBuilder, type, declarationSiteSession) + + if (type.isMarkedNullable) { + tBuilder.appendSignature(MangleConstant.Q_MARK) + } + + if (type.hasEnhancedNullability) { + tBuilder.appendSignature(MangleConstant.ENHANCED_NULLABILITY_MARK) + } + } + + is ConeRawType -> { + mangleType(tBuilder, type.lowerBound, declarationSiteSession) + } + + is ConeFlexibleType -> { + with(declarationSiteSession.typeContext) { + // Need to reproduce type approximation done for flexible types in TypeTranslator. + // For now, we replicate the current behaviour of Fir2IrTypeConverter and just take the upper bound + val upper = type.upperBound + if (upper is ConeClassLikeType) { + val lower = type.lowerBound as? ConeClassLikeType ?: error("Expecting class-like type, got ${type.lowerBound}") + val intermediate = if (lower.lookupTag == upper.lookupTag) { + lower.replaceArguments(upper.getArguments()) + } else lower + val mixed = if (upper.isNullable) intermediate.makeNullable() else intermediate.makeDefinitelyNotNullOrNotNull() + mangleType(tBuilder, mixed as ConeKotlinType, declarationSiteSession) + } else mangleType(tBuilder, upper, declarationSiteSession) + } + } + + is ConeDefinitelyNotNullType -> { + // E.g. not-null type parameter in Java + mangleType(tBuilder, type.original, declarationSiteSession) + } + + is ConeCapturedType -> { + mangleType(tBuilder, type.lowerType ?: type.constructor.supertypes!!.first(), declarationSiteSession) + } + + is ConeIntersectionType -> { + // TODO: add intersectionTypeApproximation + mangleType(tBuilder, type.intersectedTypes.first(), declarationSiteSession) + } + + else -> error("Unexpected type $type") + } + } + + protected open inner class Visitor : FirVisitorVoid() { + + override fun visitElement(element: FirElement) = error("unexpected element ${element.render()}") + + override fun visitRegularClass(regularClass: FirRegularClass) { + isRealExpect = isRealExpect or regularClass.isExpect + typeParameterContainers.add(regularClass) + regularClass.mangleSimpleDeclaration(regularClass.name.asString()) + } + + override fun visitAnonymousObject(anonymousObject: FirAnonymousObject) { + anonymousObject.mangleSimpleDeclaration("") + } + + override fun visitVariable(variable: FirVariable) { + isRealExpect = isRealExpect or variable.isExpect + typeParameterContainers.add(variable) + variable.visitParent() + + val isStaticProperty = variable.isStatic + if (isStaticProperty) { + builder.appendSignature(MangleConstant.STATIC_MEMBER_MARK) + } + + variable.receiverParameter?.typeRef?.let { + builder.appendSignature(MangleConstant.EXTENSION_RECEIVER_PREFIX) + mangleType(builder, it.coneType, variable.moduleData.session) + } + + variable.typeParameters.withIndex().toList() + .collectForMangler(builder, MangleConstant.TYPE_PARAMETERS) { (index, typeParameter) -> + mangleTypeParameter(this, typeParameter.symbol.toLookupTag(), index, variable.moduleData.session) + } + + builder.append(variable.name.asString()) + } + + override fun visitProperty(property: FirProperty) { + visitVariable(property) + } + + override fun visitField(field: FirField) { + visitVariable(field) + } + + override fun visitEnumEntry(enumEntry: FirEnumEntry) { + enumEntry.mangleSimpleDeclaration(enumEntry.name.asString()) + } + + override fun visitTypeAlias(typeAlias: FirTypeAlias) = + typeAlias.mangleSimpleDeclaration(typeAlias.name.asString()) + + override fun visitSimpleFunction(simpleFunction: FirSimpleFunction) { + isRealExpect = isRealExpect || simpleFunction.isExpect + val isStatic = simpleFunction.isStatic + simpleFunction.mangleFunction(false, isStatic, simpleFunction) + } + + override fun visitConstructor(constructor: FirConstructor) = + constructor.mangleFunction(isCtor = true, isStatic = false, constructor) + + override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor) { + if (propertyAccessor is FirSyntheticPropertyAccessor) { + // No need to distinguish between the accessor and its delegate. + visitSimpleFunction(propertyAccessor.delegate) + } else { + propertyAccessor.mangleFunction(isCtor = false, propertyAccessor.isStatic, propertyAccessor.propertySymbol.fir) + } + } + } +} \ No newline at end of file diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt index a667a8218b2..8f822e56736 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt @@ -221,8 +221,6 @@ internal class AdapterGenerator( isExpect = false, isFakeOverride = false ).also { irAdapterFunction -> - irAdapterFunction.metadata = FirMetadataSource.Function(firAdaptee) - symbolTable.enterScope(irAdapterFunction) irAdapterFunction.dispatchReceiverParameter = null val boundReceiver = boundDispatchReceiver ?: boundExtensionReceiver diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/signaturer/FirMangler.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/signaturer/FirMangler.kt index cf147902df0..cdda6c7dd99 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/signaturer/FirMangler.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/signaturer/FirMangler.kt @@ -5,10 +5,40 @@ package org.jetbrains.kotlin.fir.signaturer +import org.jetbrains.kotlin.backend.common.serialization.mangle.AbstractKotlinMangler +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.fir.backend.FirMetadataSource import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrMetadataSourceOwner import org.jetbrains.kotlin.ir.util.KotlinMangler interface FirMangler : KotlinMangler { override val manglerName: String get() = "Fir" } + +class Ir2FirManglerAdapter(private val delegate: FirMangler) : AbstractKotlinMangler(), + KotlinMangler.IrMangler { + override val manglerName: String + get() = delegate.manglerName + + private fun IrDeclaration.fir(): FirDeclaration = ((this as IrMetadataSourceOwner).metadata as FirMetadataSource).fir!! + + override fun IrDeclaration.isExported(compatibleMode: Boolean): Boolean = delegate.run { fir().isExported(compatibleMode) } + + override fun IrDeclaration.mangleString(compatibleMode: Boolean): String = delegate.run { fir().mangleString(compatibleMode) } + + override fun IrDeclaration.signatureString(compatibleMode: Boolean): String = delegate.run { fir().signatureString(compatibleMode) } + + override fun IrDeclaration.fqnString(compatibleMode: Boolean): String = delegate.run { fir().fqnString(compatibleMode) } + + override fun getMangleComputer(mode: MangleMode, compatibleMode: Boolean): KotlinMangleComputer = + error("Should not have been reached") + + override fun getExportChecker(compatibleMode: Boolean): KotlinExportChecker { + error("Should not be called") + } +} diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirIntersectionScopeOverrideChecker.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirIntersectionScopeOverrideChecker.kt index d8bf270054b..ca2f5fc471f 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirIntersectionScopeOverrideChecker.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirIntersectionScopeOverrideChecker.kt @@ -9,7 +9,6 @@ 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.impl.FirStandardOverrideChecker /** * That class is expected to work just the same as FirStandardOverrideChecker for regular members, @@ -20,7 +19,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker * while in Java class they would be treated equally. */ class FirIntersectionScopeOverrideChecker(session: FirSession) : FirOverrideChecker { - private val standardOverrideChecker = FirStandardOverrideChecker(session) + private val standardOverrideChecker = session.firOverrideChecker private val platformSpecificOverridabilityRules = session.platformSpecificOverridabilityRules override fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideChecker.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideChecker.kt index 3f75c189efa..d8dd81f281e 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideChecker.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideChecker.kt @@ -5,12 +5,14 @@ package org.jetbrains.kotlin.fir.scopes +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.FirSessionComponent 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.symbols.impl.FirNamedFunctionSymbol -interface FirOverrideChecker { +interface FirOverrideChecker : FirSessionComponent { fun isOverriddenFunction( overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction @@ -26,3 +28,5 @@ fun FirOverrideChecker.isOverriddenFunction( overrideCandidate: FirNamedFunctionSymbol, baseDeclaration: FirNamedFunctionSymbol ): Boolean = isOverriddenFunction(overrideCandidate.fir, baseDeclaration.fir) + +val FirSession.firOverrideChecker: FirOverrideChecker by FirSession.sessionComponentAccessor() diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassUseSiteMemberScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassUseSiteMemberScope.kt index f7293052782..5aa431fb19b 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassUseSiteMemberScope.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassUseSiteMemberScope.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isStatic import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope import org.jetbrains.kotlin.fir.scopes.FirTypeScope +import org.jetbrains.kotlin.fir.scopes.firOverrideChecker import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.name.Name @@ -23,7 +24,7 @@ class FirClassUseSiteMemberScope( ) : AbstractFirUseSiteMemberScope( klass.classId, session, - FirStandardOverrideChecker(session), + session.firOverrideChecker, superTypeScopes, klass.defaultType(), declaredMemberScope diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDelegatedMemberScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDelegatedMemberScope.kt index 730480799b9..96697551c94 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDelegatedMemberScope.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDelegatedMemberScope.kt @@ -37,7 +37,7 @@ class FirDelegatedMemberScope( private val delegateFields: List, ) : FirContainingNamesAwareScope() { private val dispatchReceiverType = containingClass.defaultType() - private val overrideChecker = FirStandardOverrideChecker(session) + private val overrideChecker = session.firOverrideChecker override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) { declaredMemberScope.processFunctionsByName(name, processor) diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/mangle/ManglerChecker.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/mangle/ManglerChecker.kt index f130715d1a3..8a71d854e0a 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/mangle/ManglerChecker.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/mangle/ManglerChecker.kt @@ -15,7 +15,15 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.name.SpecialNames -class ManglerChecker(vararg _manglers: KotlinMangler) : IrElementVisitorVoid { +class ManglerChecker( + vararg _manglers: KotlinMangler, + private val needsChecking: (IrDeclarationBase) -> Boolean = hasDescriptor +) : IrElementVisitorVoid { + + companion object { + val hasDescriptor: (IrDeclarationBase) -> Boolean = { it.symbol.hasDescriptor } + val hasMetadata: (IrDeclarationBase) -> Boolean = { (it as? IrMetadataSourceOwner)?.metadata != null } + } private val manglers = _manglers.toList() @@ -29,7 +37,7 @@ class ManglerChecker(vararg _manglers: KotlinMangler) : IrElement } override fun visitDeclaration(declaration: IrDeclarationBase, data: Nothing?): Boolean { - if (!declaration.symbol.hasDescriptor) return true + if (!needsChecking(declaration)) return true if (declaration.parent is IrPackageFragment) { val vis = declaration as IrDeclarationWithVisibility @@ -87,7 +95,7 @@ class ManglerChecker(vararg _manglers: KotlinMangler) : IrElement if (declaration is IrErrorDeclaration) return val exported = manglers.checkAllEqual(false, { isExportCheck(declaration) }) { m1, r1, m2, r2 -> - error("${declaration.render()}\n ${m1.manglerName}: $r1\n ${m2.manglerName}: $r2\n") + error("isExportCheck: ${declaration.render()}\n ${m1.manglerName}: $r1\n ${m2.manglerName}: $r2\n") } if (!exported) return diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/mangle/mangleUtils.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/mangle/mangleUtils.kt index 703302bc782..519968cf68b 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/mangle/mangleUtils.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/mangle/mangleUtils.kt @@ -36,4 +36,4 @@ fun Iterable.collectForMangler(builder: StringBuilder, params: MangleCons builder.append(params.suffix) } -internal val publishedApiAnnotation = FqName("kotlin.PublishedApi") +val publishedApiAnnotation = FqName("kotlin.PublishedApi") diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt index 11d55c3fd07..a0223ba0ce0 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt @@ -1,6 +1,7 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker import org.jetbrains.kotlin.backend.common.serialization.metadata.DynamicTypeDeserializer import org.jetbrains.kotlin.backend.konan.driver.PhaseContext import org.jetbrains.kotlin.backend.konan.driver.phases.Fir2IrOutput @@ -18,16 +19,15 @@ import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.descriptors.deserialization.PlatformDependentTypeTransformer import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib -import org.jetbrains.kotlin.fir.backend.Fir2IrComponents import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory -import org.jetbrains.kotlin.fir.backend.Fir2IrExtensions -import org.jetbrains.kotlin.fir.backend.Fir2IrPluginContext -import org.jetbrains.kotlin.fir.backend.Fir2IrVisibilityConverter +import org.jetbrains.kotlin.fir.backend.* import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor import org.jetbrains.kotlin.fir.pipeline.convertToIrAndActualize +import org.jetbrains.kotlin.fir.signaturer.Ir2FirManglerAdapter import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.util.SymbolTable +import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories import org.jetbrains.kotlin.storage.LockBasedStorageManager @@ -74,10 +74,17 @@ internal fun PhaseContext.fir2Ir( linkViaSignatures = false, signatureComposerCreator = null, irMangler = KonanManglerIr, + firManglerCreator = { FirNativeKotlinMangler() }, visibilityConverter = Fir2IrVisibilityConverter.Default, diagnosticReporter = diagnosticsReporter, languageVersionSettings = configuration.languageVersionSettings, kotlinBuiltIns = builtInsModule ?: DefaultBuiltIns.Instance, + fir2IrResultPostCompute = { + // it's important to compare manglers before actualization, since IR will be actualized, while FIR won't + irModuleFragment.acceptVoid( + ManglerChecker(KonanManglerIr, Ir2FirManglerAdapter(FirNativeKotlinMangler()), needsChecking = ManglerChecker.hasMetadata) + ) + } ).also { (it.irModuleFragment.descriptor as? FirModuleDescriptor)?.let { it.allDependencyModules = librariesDescriptors } } @@ -86,7 +93,6 @@ internal fun PhaseContext.fir2Ir( } val symbols = createKonanSymbols(irModuleFragment, components, pluginContext) - // TODO KT-55580 Invoke CopyDefaultValuesToActualPhase, same as PsiToir phase does. val renderDiagnosticNames = configuration.getBoolean(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME) FirDiagnosticsCompilerResultsReporter.reportToMessageCollector(diagnosticsReporter, messageCollector, renderDiagnosticNames) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt index d3794dd0fc3..035cdf33732 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt @@ -10,20 +10,19 @@ import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.prepareNativeSessions import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory -import org.jetbrains.kotlin.fir.BinaryModuleData -import org.jetbrains.kotlin.fir.DependencyListForCliModule -import org.jetbrains.kotlin.fir.FirModuleCapabilities +import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar import org.jetbrains.kotlin.fir.pipeline.FirResult import org.jetbrains.kotlin.fir.pipeline.buildResolveAndCheckFir -import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.ImplicitIntegerCoercionModuleCapability +import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker import org.jetbrains.kotlin.library.isInterop import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.CommonPlatforms import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerServices +@OptIn(SessionConfiguration::class) internal fun PhaseContext.firFrontend(input: KotlinCoreEnvironment): FirOutput { val configuration = input.configuration val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) @@ -56,7 +55,10 @@ internal fun PhaseContext.firFrontend(input: KotlinCoreEnvironment): FirOutput { val sessionsWithSources = prepareNativeSessions( ktFiles, configuration, mainModuleName, resolvedLibraries, dependencyList, - extensionRegistrars, isCommonSourceForPsi, fileBelongsToModuleForPsi + extensionRegistrars, isCommonSourceForPsi, fileBelongsToModuleForPsi, + registerExtraComponents = { + it.register(FirOverrideChecker::class, FirNativeOverrideChecker(it)) + }, ) val outputs = sessionsWithSources.map { (session, sources) -> diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirNativeKotlinMangler.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirNativeKotlinMangler.kt new file mode 100644 index 00000000000..f60c7ba51a4 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirNativeKotlinMangler.kt @@ -0,0 +1,121 @@ +/* + * 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.FirBasedKotlinMangler +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 : FirBasedKotlinMangler() { + override fun getMangleComputer(mode: MangleMode, compatibleMode: Boolean): KotlinMangleComputer { + return FirNativeKotlinMangleComputer(StringBuilder(256), mode) + } + + override fun getExportChecker(compatibleMode: Boolean): KotlinExportChecker = 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.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}") + } +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirNativeOverrideChecker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirNativeOverrideChecker.kt new file mode 100644 index 00000000000..cd10f923f7b --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirNativeOverrideChecker.kt @@ -0,0 +1,74 @@ +/* + * 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 + } +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirObjCInterop.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirObjCInterop.kt new file mode 100644 index 00000000000..55d3b143df1 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirObjCInterop.kt @@ -0,0 +1,156 @@ +/* + * 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.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 { + 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() + 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? FirClassSymbol } }, + 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().any { + it.hasAnnotation(externalObjCClassClassId, session) + } + +@OptIn(SymbolInternals::class) +fun FirClassSymbol<*>.parentsWithSelf(session: FirSession): Sequence { + return generateSequence(fir) { it.getContainingDeclaration(session) } +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt index 596e78bf19a..68ce56e9fc0 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBasedClassConstructorDescriptor import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.getPublicSignature import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -30,21 +29,15 @@ import org.jetbrains.kotlin.types.typeUtil.supertypes internal val interopPackageName = InteropFqNames.packageName internal val objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject")) -internal val objCObjectIdSignature = getTopLevelPublicSignature(objCObjectFqName) private val objCClassFqName = interopPackageName.child(Name.identifier("ObjCClass")) -private val objCClassIdSignature = getTopLevelPublicSignature(objCClassFqName) private val objCProtocolFqName = interopPackageName.child(Name.identifier("ObjCProtocol")) -private val objCProtocolIdSignature = getTopLevelPublicSignature(objCProtocolFqName) internal val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass")) -private val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod")) -private val objCDirectFqName = interopPackageName.child(Name.identifier("ObjCDirect")) -private val objCConstructorFqName = FqName("kotlinx.cinterop.ObjCConstructor") -private val objCFactoryFqName = interopPackageName.child(Name.identifier("ObjCFactory")) +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")) private val objcnamesForwardDeclarationsPackageName = Name.identifier("objcnames") -private fun getTopLevelPublicSignature(fqName: FqName): IdSignature.CommonSignature = - getPublicSignature(fqName.parent(), fqName.shortName().asString()) - fun ClassDescriptor.isObjCClass(): Boolean = this.containingDeclaration.fqNameSafe != interopPackageName && this.getAllSuperClassifiers().any { it.fqNameSafe == objCObjectFqName } // TODO: this is not cheap. Cache me! @@ -59,7 +52,7 @@ private fun IrClass.selfOrAnySuperClass(pred: (IrClass) -> Boolean): Boolean { } internal fun IrClass.isObjCClass() = this.packageFqName != interopPackageName && - selfOrAnySuperClass { objCObjectIdSignature == it.symbol.signature } + selfOrAnySuperClass { it.hasEqualFqName(objCObjectFqName) } fun ClassDescriptor.isExternalObjCClass(): Boolean = this.isObjCClass() && this.parentsWithSelf.filterIsInstance().any { @@ -78,10 +71,10 @@ fun ClassDescriptor.isObjCMetaClass(): Boolean = this.getAllSuperClassifiers().a } fun IrClass.isObjCMetaClass(): Boolean = selfOrAnySuperClass { - objCClassIdSignature == it.symbol.signature + it.hasEqualFqName(objCClassFqName) } -fun IrClass.isObjCProtocolClass(): Boolean = objCProtocolIdSignature == symbol.signature +fun IrClass.isObjCProtocolClass(): Boolean = hasEqualFqName(objCProtocolFqName) fun ClassDescriptor.isObjCProtocolClass(): Boolean = this.fqNameSafe == objCProtocolFqName @@ -123,9 +116,7 @@ private fun FunctionDescriptor.decodeObjCMethodAnnotation(): ObjCMethodInfo? { selector = it.getStringValue("selector"), encoding = it.getStringValue("encoding"), isStret = it.getArgumentValueOrNull("isStret") ?: false, - directSymbol = this.annotations.findAnnotation(objCDirectFqName)?.let { - it.getStringValue("symbol") - }, + directSymbol = this.annotations.findAnnotation(objCDirectFqName)?.getStringValue("symbol"), ) } diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index 9b8ac841aa0..ffab2de61e0 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -4917,7 +4917,7 @@ standaloneTest("interop_opengl_teapot") { if (PlatformInfo.isAppleTarget(project)) { interopTest("interop_objc_smoke") { - enabled = !isNoopGC && !isK2(project) // KT-56030 + enabled = !isNoopGC source = "interop/objc/smoke.kt" interop = 'objcSmoke' doBeforeBuild { @@ -4968,7 +4968,7 @@ if (PlatformInfo.isAppleTarget(project)) { } interopTestMultifile("interop_objc_tests") { - disabled = isK2(project) // KT-55909 + disabled = isK2(project) // KT-57504 source = "interop/objc/tests/" interop = 'objcTests' flags = ['-tr', '-e', 'main'] @@ -5259,8 +5259,7 @@ if (PlatformInfo.isAppleTarget(project)) { standaloneTest("interop_kt55653") { // Test depends on macOS-specific AppKit - enabled = (project.testTarget == 'macos_x64' || project.testTarget == 'macos_arm64' || project.testTarget == null) && - !isK2(project) // KT-56030 + enabled = (project.testTarget == 'macos_x64' || project.testTarget == 'macos_arm64' || project.testTarget == null) source = "interop/objc/kt55653/main.kt" useGoldenData = true UtilsKt.dependsOnPlatformLibs(it)