diff --git a/compiler/cli/bin/kotlinc-jklib b/compiler/cli/bin/kotlinc-jklib new file mode 100755 index 0000000000000..4370c2f50a891 --- /dev/null +++ b/compiler/cli/bin/kotlinc-jklib @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# Copyright 2010-2021 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. + +export KOTLIN_COMPILER=org.jetbrains.kotlin.cli.jklib.K2JKlibCompiler + +DIR="${BASH_SOURCE[0]%/*}" +: ${DIR:="."} + +"${DIR}"/kotlinc "$@" diff --git a/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/K2JKlibCompilerArgumentsCopyGenerated.kt b/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/K2JKlibCompilerArgumentsCopyGenerated.kt new file mode 100644 index 0000000000000..398e886de7fb7 --- /dev/null +++ b/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/K2JKlibCompilerArgumentsCopyGenerated.kt @@ -0,0 +1,22 @@ +@file:Suppress("unused", "DuplicatedCode") + +// DO NOT EDIT MANUALLY! +// Generated by generators/tests/org/jetbrains/kotlin/generators/arguments/GenerateCompilerArgumentsCopy.kt +// To regenerate run 'generateCompilerArgumentsCopy' task + +package org.jetbrains.kotlin.cli.common.arguments + +@OptIn(org.jetbrains.kotlin.utils.IDEAPluginsCompatibilityAPI::class) +fun copyK2JKlibCompilerArguments(from: K2JKlibCompilerArguments, to: K2JKlibCompilerArguments): K2JKlibCompilerArguments { + copyCommonCompilerArguments(from, to) + + to.classpath = from.classpath + to.destination = from.destination + to.friendPaths = from.friendPaths?.copyOf() + to.klibLibraries = from.klibLibraries + to.moduleName = from.moduleName + to.noReflect = from.noReflect + to.noStdlib = from.noStdlib + + return to +} diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JKlibCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JKlibCompilerArguments.kt new file mode 100644 index 0000000000000..25fb598e40e2b --- /dev/null +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JKlibCompilerArguments.kt @@ -0,0 +1,256 @@ +/* + * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cli.common.arguments + +import org.jetbrains.kotlin.config.* + +class K2JKlibCompilerArguments : CommonCompilerArguments() { + companion object { + @JvmStatic private val serialVersionUID = 0L + } + + @Argument(value = "-d", valueDescription = "", description = "Destination for generated files.") + var destination: String? = null + set(value) { + checkFrozen() + field = if (value.isNullOrEmpty()) null else value + } + + @Argument(value = "-p", valueDescription = "", description = "") + var produce: String? = null + set(value) { + checkFrozen() + field = if (value.isNullOrEmpty()) null else value + } + + @Argument( + value = "-classpath", + shortName = "-cp", + valueDescription = "", + description = "List of directories and JAR/ZIP archives to search for user .kotlin_metadata files." + ) + var classpath: String? = null + set(value) { + checkFrozen() + field = if (value.isNullOrEmpty()) null else value + } + + @Argument(value = "-module-name", valueDescription = "", description = "Name of the generated .kotlin_module file.") + var moduleName: String? = null + set(value) { + checkFrozen() + field = if (value.isNullOrEmpty()) null else value + } + + @Argument( + value = "-Xfriend-paths", + valueDescription = "", + description = "Paths to output directories for friend modules (modules whose internals should be visible)." + ) + var friendPaths: Array? = null + set(value) { + checkFrozen() + field = value + } + + @Argument( + value = "-Xklib", + valueDescription = "", + description = "Paths to cross-platform libraries in the .klib format." + ) + var klibLibraries: String? = null + set(value) { + checkFrozen() + field = if (value.isNullOrEmpty()) null else value + } + + @Argument( + value = "-no-stdlib", + description = "Don't automatically include the Kotlin/JVM stdlib and Kotlin reflection dependencies in the classpath." + ) + var noStdlib = false + set(value) { + checkFrozen() + field = value + } + + @Argument( + value = "-Xcompile-builtins-as-part-of-stdlib", + description = "Enable behaviour needed to compile builtins as part of JVM stdlib" + ) + var expectBuiltinsAsPartOfStdlib = false + set(value) { + checkFrozen() + field = value + } + + + @Argument(value = "-no-jdk", description = "Don't automatically include the Java runtime in the classpath.") + var noJdk = false + set(value) { + checkFrozen() + field = value + } + + + @Argument(value = "-no-reflect", description = "Don't automatically include the Kotlin reflection dependency in the classpath.") + var noReflect = false + set(value) { + checkFrozen() + field = value + } + @Argument( + value = "-Xtype-enhancement-improvements-strict-mode", + description = """Enable strict mode for improvements to type enhancement for loaded Java types based on nullability annotations, +including the ability to read type-use annotations from class files. +See KT-45671 for more details.""" + ) + var typeEnhancementImprovementsInStrictMode = false + set(value) { + checkFrozen() + field = value + } + + @Argument( + value = "-Xenhance-type-parameter-types-to-def-not-null", + description = "Enhance not-null-annotated type parameter types to definitely-non-nullable types ('@NotNull T' => 'T & Any')." + ) + var enhanceTypeParameterTypesToDefNotNull = false + set(value) { + checkFrozen() + field = value + } + + @Argument( + value = "-Xjvm-default", + valueDescription = "{all|all-compatibility|disable}", + description = """Emit JVM default methods for interface declarations with bodies. The default is 'disable'. +-Xjvm-default=all Generate JVM default methods for all interface declarations with bodies in the module. + Do not generate 'DefaultImpls' stubs for interface declarations with bodies. If an interface inherits a method with a + body from an interface compiled in 'disable' mode and doesn't override it, then a 'DefaultImpls' stub will be + generated for it. + This BREAKS BINARY COMPATIBILITY if some client code relies on the presence of 'DefaultImpls' classes. + Note that if interface delegation is used, all interface methods are delegated. +-Xjvm-default=all-compatibility Like 'all', but additionally generate compatibility stubs in the 'DefaultImpls' classes. + Compatibility stubs can help library and runtime authors maintain backward binary compatibility + for existing clients compiled against previous library versions. + 'all' and 'all-compatibility' modes change the library ABI surface that will be used by clients after + the recompilation of the library. Because of this, clients might be incompatible with previous library + versions. This usually means that proper library versioning is required, for example with major version increases in SemVer. + In subtypes of Kotlin interfaces compiled in 'all' or 'all-compatibility' mode, 'DefaultImpls' + compatibility stubs will invoke the default method of the interface with standard JVM runtime resolution semantics. + Perform additional compatibility checks for classes inheriting generic interfaces where in some cases an + additional implicit method with specialized signatures was generated in 'disable' mode. + Unlike in 'disable' mode, the compiler will report an error if such a method is not overridden explicitly + and the class is not annotated with '@JvmDefaultWithoutCompatibility' (see KT-39603 for more details). +-Xjvm-default=disable Default behavior. Do not generate JVM default methods.""" + ) + var jvmDefault: String = JvmDefaultMode.DISABLE.description + set(value) { + checkFrozen() + field = value + } + + @Argument( + value = "-Xvalue-classes", + description = "Enable experimental value classes." + ) + var valueClasses = false + set(value) { + checkFrozen() + field = value + } + + @Argument( + value = "-Xjsr305", + deprecatedName = "-Xjsr305-annotations", + valueDescription = + "{ignore/strict/warn}" + + "|under-migration:{ignore/strict/warn}" + + "|@:{ignore/strict/warn}", + description = + """Specify the behavior of 'JSR-305' nullability annotations: +-Xjsr305={ignore/strict/warn} global (all non-@UnderMigration annotations) +-Xjsr305=under-migration:{ignore/strict/warn} all @UnderMigration annotations +-Xjsr305=@:{ignore/strict/warn} annotation with the given fully qualified class name +Modes: +* ignore +* strict (experimental; treat like other supported nullability annotations) +* warn (report a warning)""", + ) + var jsr305: Array? = null + set(value) { + checkFrozen() + field = value + } + + @Argument( + value = "-Xsupport-compatqual-checker-framework-annotations", + valueDescription = "enable|disable", + description = + """Specify the behavior for Checker Framework 'compatqual' annotations ('NullableDecl'/'NonNullDecl'). +The default value is 'enable'.""", + ) + var supportCompatqualCheckerFrameworkAnnotations: String? = null + set(value) { + checkFrozen() + field = if (value.isNullOrEmpty()) null else value + } + + @Argument( + value = "-Xjspecify-annotations", + valueDescription = "ignore|strict|warn", + description = + """Specify the behavior of 'jspecify' annotations. +The default value is 'warn'.""", + ) + var jspecifyAnnotations: String? = null + set(value) { + checkFrozen() + field = value + } + + @Argument( + value = "Xmultifile-parts-inherit", + description = "Compile multifile classes as a hierarchy of parts and a facade." + ) + var inheritMultifileParts: Boolean = false + set(value) { + checkFrozen() + field = value + } + + @Argument( + value = "-Xoutput-builtins-metadata", + description = "Output builtins metadata as .kotlin_builtins files", + ) + var outputBuiltinsMetadata: Boolean = false + set(value) { + checkFrozen() + field = value + } + + @Argument( + value = "-Xnullability-annotations", + valueDescription = "@:{ignore/strict/warn}", + description = + """Specify the behavior for specific Java nullability annotations (provided with fully qualified package name). +Modes: +* ignore +* strict +* warn (report a warning)""", + ) + var nullabilityAnnotations: Array? = null + set(value) { + checkFrozen() + field = value + } + + override fun copyOf(): Freezable = TODO() // copyK2JKlibCompilerArguments(this, K2JKlibCompilerArguments()) + + override val configurator: CommonCompilerArgumentsConfigurator = K2JKlibCompilerArgumentsConfigurator() + +} diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JKlibCompilerArgumentsConfigurator.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JKlibCompilerArgumentsConfigurator.kt new file mode 100644 index 0000000000000..874690c171c56 --- /dev/null +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JKlibCompilerArgumentsConfigurator.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cli.common.arguments + +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.config.* + +class K2JKlibCompilerArgumentsConfigurator : CommonCompilerArgumentsConfigurator() { + override fun configureAnalysisFlags( + arguments: CommonCompilerArguments, + collector: MessageCollector, + languageVersion: LanguageVersion, + ): MutableMap, Any> = with(arguments) { + require(this is K2JKlibCompilerArguments) + val result = super.configureAnalysisFlags(arguments, collector, languageVersion) + result[JvmAnalysisFlags.javaTypeEnhancementState] = JavaTypeEnhancementStateParser(collector, languageVersion.toKotlinVersion()) + .parse(jsr305, supportCompatqualCheckerFrameworkAnnotations, jspecifyAnnotations, nullabilityAnnotations) + + result[JvmAnalysisFlags.inheritMultifileParts] = inheritMultifileParts + result[JvmAnalysisFlags.outputBuiltinsMetadata] = outputBuiltinsMetadata + if (expectBuiltinsAsPartOfStdlib && !stdlibCompilation) { + collector.report( + CompilerMessageSeverity.ERROR, + "-Xcompile-builtins-as-part-of-stdlib must not be used without -Xstdlib-compilation" + ) + } + result[JvmAnalysisFlags.expectBuiltinsAsPartOfStdlib] = expectBuiltinsAsPartOfStdlib + return result + } + + private fun K2JVMCompilerArguments.configureJvmDefaultMode(collector: MessageCollector?): JvmDefaultMode? = when { + jvmDefaultStable != null -> JvmDefaultMode.fromStringOrNull(jvmDefaultStable).also { + if (it == null) { + collector?.report( + CompilerMessageSeverity.ERROR, + "Unknown -jvm-default mode: $jvmDefaultStable, supported modes: " + + "${JvmDefaultMode.entries.map(JvmDefaultMode::description)}" + ) + } + } + jvmDefault != null -> JvmDefaultMode.fromStringOrNullOld(jvmDefault).also { + if (it == null) { + collector?.report( + CompilerMessageSeverity.ERROR, + "Unknown -Xjvm-default mode: $jvmDefault, supported modes: " + + "${JvmDefaultMode.entries.map(JvmDefaultMode::oldDescription)}" + ) + } + } + else -> null + } + + override fun configureLanguageFeatures( + arguments: CommonCompilerArguments, + collector: MessageCollector, + ): MutableMap = with(arguments) { + require(this is K2JKlibCompilerArguments) + val result = super.configureLanguageFeatures(arguments, collector) + if (typeEnhancementImprovementsInStrictMode) { + result[LanguageFeature.TypeEnhancementImprovementsInStrictMode] = LanguageFeature.State.ENABLED + } + if (enhanceTypeParameterTypesToDefNotNull) { + result[LanguageFeature.ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated] = LanguageFeature.State.ENABLED + } + if (valueClasses) { + result[LanguageFeature.ValueClasses] = LanguageFeature.State.ENABLED + } + return result + } +} diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/CommonKLibResolver.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/CommonKLibResolver.kt new file mode 100644 index 0000000000000..ce58ef71ef094 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/CommonKLibResolver.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cli.jklib + +import org.jetbrains.kotlin.config.DuplicatedUniqueNameStrategy +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.file.ZipFileSystemAccessor +import org.jetbrains.kotlin.library.KotlinLibrary +import org.jetbrains.kotlin.library.KotlinLibraryProperResolverWithAttributes +import org.jetbrains.kotlin.library.UnresolvedLibrary +import org.jetbrains.kotlin.library.impl.createKotlinLibraryComponents +import org.jetbrains.kotlin.library.metadata.resolver.KotlinLibraryResolveResult +import org.jetbrains.kotlin.library.metadata.resolver.KotlinLibraryResolver +import org.jetbrains.kotlin.library.metadata.resolver.impl.libraryResolver +import org.jetbrains.kotlin.util.Logger + +object CommonKLibResolver { + fun resolve( + libraries: Collection, + logger: Logger, + zipAccessor: ZipFileSystemAccessor? = null, + lenient: Boolean = false, + knownIrProviders: List = listOf(), + duplicatedUniqueNameStrategy: DuplicatedUniqueNameStrategy = DuplicatedUniqueNameStrategy.DENY, + ): KotlinLibraryResolveResult = + resolveWithoutDependencies( + libraries, + logger, + zipAccessor, + lenient, + knownIrProviders, + duplicatedUniqueNameStrategy, + ).resolveWithDependencies() + + fun resolveWithoutDependencies( + libraries: Collection, + logger: Logger, + zipAccessor: ZipFileSystemAccessor?, + lenient: Boolean = false, + knownIrProviders: List = listOf(), + duplicatedUniqueNameStrategy: DuplicatedUniqueNameStrategy, + ): KLibResolution { + val unresolvedLibraries = libraries.map { UnresolvedLibrary(it, lenient) } + val libraryAbsolutePaths = libraries.map { File(it).absolutePath } + // Configure the resolver to only work with absolute paths for now. + val libraryResolver = KLibResolverHelper( + directLibs = libraryAbsolutePaths, + distributionKlib = null, + skipCurrentDir = false, + logger = logger, + zipAccessor = zipAccessor, + knownIrProviders = knownIrProviders, + ).libraryResolver(resolveManifestDependenciesLenient = true) + + return KLibResolution( + libraryResolver, + libraryResolver.resolveWithoutDependencies( + unresolvedLibraries = unresolvedLibraries, + noStdLib = true, + noDefaultLibs = true, + noEndorsedLibs = true, + duplicatedUniqueNameStrategy = duplicatedUniqueNameStrategy, + ) + ) + } +} + +class KLibResolution( + private val libraryResolver: KotlinLibraryResolver, + val libraries: List +) { + fun resolveWithDependencies(): KotlinLibraryResolveResult { + return with(libraryResolver) { + libraries.resolveDependencies() + } + } +} + +private class KLibResolverHelper( + directLibs: List, + distributionKlib: String?, + skipCurrentDir: Boolean, + logger: Logger, + private val zipAccessor: ZipFileSystemAccessor?, + knownIrProviders: List, +) : KotlinLibraryProperResolverWithAttributes( + directLibs = directLibs, + distributionKlib = distributionKlib, + skipCurrentDir = skipCurrentDir, + logger = logger, + knownIrProviders = knownIrProviders, +) { + // Stick with the default KotlinLibrary for now. + override fun libraryComponentBuilder(file: File, isDefault: Boolean) = createKotlinLibraryComponents(file, isDefault, zipAccessor) +} \ No newline at end of file diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/FirJKlibSessionFactory.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/FirJKlibSessionFactory.kt new file mode 100644 index 0000000000000..78a473e944eda --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/FirJKlibSessionFactory.kt @@ -0,0 +1,265 @@ +/* + * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cli.jklib + +import org.jetbrains.kotlin.config.AnalysisFlags +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.SessionConfiguration +import org.jetbrains.kotlin.fir.checkers.registerJvmCheckers +import org.jetbrains.kotlin.fir.deserialization.ModuleDataProvider +import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider +import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar +import org.jetbrains.kotlin.fir.java.FirJvmTargetProvider +import org.jetbrains.kotlin.fir.java.JavaSymbolProvider +import org.jetbrains.kotlin.fir.java.deserialization.FirJvmBuiltinsSymbolProvider +import org.jetbrains.kotlin.fir.java.deserialization.FirJvmClasspathBuiltinSymbolProvider +import org.jetbrains.kotlin.fir.java.deserialization.JvmClassFileBasedSymbolProvider +import org.jetbrains.kotlin.fir.java.deserialization.OptionalAnnotationClassesProvider +import org.jetbrains.kotlin.fir.languageVersionSettings +import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCloneableSymbolProvider +import org.jetbrains.kotlin.fir.resolve.providers.impl.FirFallbackBuiltinSymbolProvider +import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped +import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider +import org.jetbrains.kotlin.fir.session.FirAbstractSessionFactory +import org.jetbrains.kotlin.fir.session.FirJvmIncrementalCompilationSymbolProviders +import org.jetbrains.kotlin.fir.session.FirSessionConfigurator +import org.jetbrains.kotlin.fir.session.FirSharableJavaComponents +import org.jetbrains.kotlin.fir.session.KlibBasedSymbolProvider +import org.jetbrains.kotlin.fir.session.environment.AbstractProjectEnvironment +import org.jetbrains.kotlin.fir.session.environment.AbstractProjectFileSearchScope +import org.jetbrains.kotlin.fir.session.registerDefaultComponents +import org.jetbrains.kotlin.fir.session.registerJavaComponents +import org.jetbrains.kotlin.library.KotlinLibrary +import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addToStdlib.runIf + +@OptIn(SessionConfiguration::class) +object FirJKlibSessionFactory : FirAbstractSessionFactory() { + override fun createPlatformSpecificSharedProviders( + session: FirSession, + moduleData: FirModuleData, + scopeProvider: FirKotlinScopeProvider, + context: Context + ): List { + return listOf( + FirCloneableSymbolProvider(session, moduleData, scopeProvider), + OptionalAnnotationClassesProvider( + session, + SingleModuleDataProvider(moduleData), + scopeProvider, + context.packagePartProvider + ), + ) + } + + // ==================================== Shared library session ==================================== + + /** + * See documentation to [FirAbstractSessionFactory.createSharedLibrarySession] + */ + fun createSharedLibrarySession( + mainModuleName: Name, + projectEnvironment: AbstractProjectEnvironment, + extensionRegistrars: List, + packagePartProvider: PackagePartProvider, + languageVersionSettings: LanguageVersionSettings, + predefinedJavaComponents: FirSharableJavaComponents?, + ): FirSession { + val context = Context(predefinedJavaComponents, projectEnvironment, packagePartProvider) + return createSharedLibrarySession( + mainModuleName, + context, + languageVersionSettings, + extensionRegistrars + ) + } + + // ==================================== Library session ==================================== + + /** + * See documentation to [FirAbstractSessionFactory.createLibrarySession] + */ + fun createLibrarySession( + resolvedLibraries: List, + sharedLibrarySession: FirSession, + moduleDataProvider: ModuleDataProvider, + projectEnvironment: AbstractProjectEnvironment, + extensionRegistrars: List, + scope: AbstractProjectFileSearchScope, + packagePartProvider: PackagePartProvider, + languageVersionSettings: LanguageVersionSettings, + predefinedJavaComponents: FirSharableJavaComponents?, + ): FirSession { + val kotlinClassFinder = projectEnvironment.getKotlinClassFinder(scope) + val context = Context(predefinedJavaComponents, projectEnvironment, packagePartProvider) + return createLibrarySession( + context, + sharedLibrarySession, + moduleDataProvider, + languageVersionSettings, + extensionRegistrars, + createSeparateSharedProvidersInHmppCompilation = false, + createProviders = { session, kotlinScopeProvider -> + listOf( + KlibBasedSymbolProvider( + session, moduleDataProvider, kotlinScopeProvider, resolvedLibraries, + ), + JvmClassFileBasedSymbolProvider( + session, + moduleDataProvider, + kotlinScopeProvider, + packagePartProvider, + kotlinClassFinder, + projectEnvironment.getFirJavaFacade(session, moduleDataProvider.allModuleData.last(), scope) + ), + ) + } + ) + } + + override fun createKotlinScopeProviderForLibrarySession(): FirKotlinScopeProvider { + return FirKotlinScopeProvider(::wrapScopeWithJvmMapped) + } + + override fun FirSession.registerLibrarySessionComponents(c: Context) { + registerDefaultComponents() + registerJavaComponents(c.projectEnvironment.getJavaModuleResolver(), c.predefinedJavaComponents) + } + + // ==================================== Platform session ==================================== + + /** + * See documentation to [FirAbstractSessionFactory.createSourceSession] + */ + fun createSourceSession( + moduleData: FirModuleData, + javaSourcesScope: AbstractProjectFileSearchScope, + projectEnvironment: AbstractProjectEnvironment, + createIncrementalCompilationSymbolProviders: (FirSession) -> FirJvmIncrementalCompilationSymbolProviders?, + extensionRegistrars: List, + configuration: CompilerConfiguration, + predefinedJavaComponents: FirSharableJavaComponents?, + needRegisterJavaElementFinder: Boolean, + packagePartProvider: PackagePartProvider, // TODO create a separate SourceContext to not pass this argument here + isForLeafHmppModule: Boolean, + init: FirSessionConfigurator.() -> Unit, + ): FirSession { + val context = Context(predefinedJavaComponents, projectEnvironment, packagePartProvider) + return createSourceSession( + moduleData, + context = context, + extensionRegistrars, + configuration, + isForLeafHmppModule = isForLeafHmppModule, + init, + createProviders = { session, kotlinScopeProvider, symbolProvider, generatedSymbolsProvider -> + val javaSymbolProvider = + JavaSymbolProvider(session, projectEnvironment.getFirJavaFacade(session, moduleData, javaSourcesScope)) + session.register(JavaSymbolProvider::class, javaSymbolProvider) + + val incrementalCompilationSymbolProviders = createIncrementalCompilationSymbolProviders(session) + + val providers = listOfNotNull( + symbolProvider, + generatedSymbolsProvider, + *(incrementalCompilationSymbolProviders?.previousFirSessionsSymbolProviders?.toTypedArray() ?: emptyArray()), + incrementalCompilationSymbolProviders?.symbolProviderForBinariesFromIncrementalCompilation, + javaSymbolProvider, + initializeForStdlibIfNeeded(projectEnvironment, session, kotlinScopeProvider), + ) + SourceProviders( + providers, + incrementalCompilationSymbolProviders?.optionalAnnotationClassesProviderForBinariesFromIncrementalCompilation + ) + } + ).also { + if (needRegisterJavaElementFinder) { + projectEnvironment.registerAsJavaElementFinder(it) + } + } + } + + override fun createKotlinScopeProviderForSourceSession( + moduleData: FirModuleData, + languageVersionSettings: LanguageVersionSettings, + ): FirKotlinScopeProvider { + if (languageVersionSettings.getFlag(AnalysisFlags.stdlibCompilation) && moduleData.isCommon) return FirKotlinScopeProvider() + return FirKotlinScopeProvider { klass, declaredScope, useSiteSession, scopeSession, memberRequiredPhase -> + wrapScopeWithJvmMapped( + klass, + declaredScope, + useSiteSession, + scopeSession, + memberRequiredPhase, + filterOutJvmPlatformDeclarations = !languageVersionSettings.getFlag(AnalysisFlags.stdlibCompilation) + ) + } + } + + override fun FirSessionConfigurator.registerPlatformCheckers(c: Context) { + registerJvmCheckers() + } + + + override fun FirSessionConfigurator.registerExtraPlatformCheckers(c: Context) { + } + + override fun FirSession.registerSourceSessionComponents(c: Context) { + registerDefaultComponents() + registerJavaComponents(c.projectEnvironment.getJavaModuleResolver(), c.predefinedJavaComponents) + register(FirJvmTargetProvider::class, FirJvmTargetProvider(JvmTarget.Companion.DEFAULT)) + } + + override val requiresSpecialSetupOfSourceProvidersInHmppCompilation: Boolean + get() = false + + // ==================================== Common parts ==================================== + + // ==================================== Utilities ==================================== + + class Context( + val predefinedJavaComponents: FirSharableJavaComponents?, + val projectEnvironment: AbstractProjectEnvironment, + val packagePartProvider: PackagePartProvider, + ) + + private fun initializeForStdlibIfNeeded( + projectEnvironment: AbstractProjectEnvironment, + session: FirSession, + kotlinScopeProvider: FirKotlinScopeProvider, + ): FirSymbolProvider? { + return runIf( + session.languageVersionSettings.getFlag(AnalysisFlags.stdlibCompilation) && + !session.moduleData.isCommon + && session.moduleData.dependsOnDependencies.isEmpty() + ) { + val kotlinClassFinder = projectEnvironment.getKotlinClassFinder(projectEnvironment.getSearchScopeForProjectLibraries()) + FirJvmClasspathBuiltinSymbolProvider( + session, + session.moduleData, + kotlinScopeProvider + ) { kotlinClassFinder.findBuiltInsData(it) } + } + } + + private fun initializeBuiltinsProvider( + session: FirSession, + builtinsModuleData: FirModuleData, + kotlinScopeProvider: FirKotlinScopeProvider, + kotlinClassFinder: KotlinClassFinder, + ): FirJvmBuiltinsSymbolProvider = FirJvmBuiltinsSymbolProvider( + session, + FirFallbackBuiltinSymbolProvider(session, builtinsModuleData, kotlinScopeProvider) + ) { kotlinClassFinder.findBuiltInsData(it) } +} \ No newline at end of file diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/JKlibMangleComputer.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/JKlibMangleComputer.kt new file mode 100644 index 0000000000000..89da8655eb3e4 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/JKlibMangleComputer.kt @@ -0,0 +1,568 @@ +/* + * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cli.jklib + +import org.jetbrains.kotlin.backend.common.serialization.mangle.* +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.IrBuiltIns +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.descriptors.IrImplementingDelegateDescriptor +import org.jetbrains.kotlin.ir.descriptors.IrPropertyDelegateDescriptor +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrScriptSymbol +import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol +import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.IrVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext +import org.jetbrains.kotlin.types.error.ErrorClassDescriptor +import org.jetbrains.kotlin.types.model.* +import org.jetbrains.kotlin.types.typeUtil.isUnit + +/* + * TODO: + * Most of the code in this file is copied from `IrMangleComputer.kt` and `DescriptorMangleComputer.kt` in the Kotlin compiler. This + * copying is needed to redefine `mangleTypeArguments` from `BaseKotlinMangleComputer`, so that effective variance is always used when + * computing type signature, see `mangleTypeArgumentsUsingEffectiveVariance`. These changes are required to fix differences in type + * variance of function arguments computed on the serialization step by K2 from the ones computed during deserialization by K1. + * When the K1 klib deserialization part of the K2CL pipeline transitions to K2, code in this file should no longer be needed. + */ + +open class JKlibIrMangleComputerBase( + builder: StringBuilder, + mode: MangleMode, + protected val compatibleMode: Boolean, + allowOutOfScopeTypeParameters: Boolean = false, +) : BaseKotlinMangleComputer< + /*Declaration=*/IrDeclaration, + /*Type=*/IrType, + /*TypeParameter=*/IrTypeParameterSymbol, + /*ValueParameter=*/IrValueParameter, + /*TypeParameterContainer=*/IrDeclaration, + /*FunctionDeclaration=*/IrFunction, + /*Session=*/Nothing?, + >(builder, mode, allowOutOfScopeTypeParameters) { + + fun mangleTypeArgumentsUsingEffectiveVariance(tBuilder: StringBuilder, type: IrType, declarationSiteSession: Nothing?) = + with(getTypeSystemContext(declarationSiteSession)) { + val typeArguments = type.getArguments().zip(type.typeConstructor().getParameters()) + if (typeArguments.isEmpty()) return + @Suppress("UNUSED_DESTRUCTURED_PARAMETER_ENTRY") + typeArguments.collectForMangler(tBuilder, MangleConstant.TYPE_ARGUMENTS) { (typeArgument, typeParameter) -> + when { + typeArgument.isStarProjection() -> appendSignature(MangleConstant.STAR_MARK) + else -> { + val variance = AbstractTypeChecker.effectiveVariance( + typeParameter.getVariance(), typeArgument.getVariance() + ) ?: typeArgument.getVariance() + if (variance != TypeVariance.INV) { + appendSignature(variance.presentation) + appendSignature(MangleConstant.VARIANCE_SEPARATOR) + } + + @Suppress("UNCHECKED_CAST") + mangleType(this, typeArgument.getType() as IrType, declarationSiteSession) + } + } + } + } + + final override fun getTypeSystemContext(session: Nothing?) = object : IrTypeSystemContext { + override val irBuiltIns: IrBuiltIns + get() = throw UnsupportedOperationException("Builtins are unavailable") + } + + override fun copy(newMode: MangleMode) = JKlibIrMangleComputerBase(builder, newMode, compatibleMode) + + final override fun IrDeclaration.visitParent() { + parent.acceptVoid(Visitor()) + } + + final override fun IrDeclaration.visit() { + acceptVoid(Visitor()) + } + + override fun IrDeclaration.asTypeParameterContainer(): IrDeclaration = + this + + override fun IrDeclaration.visitParentForFunctionMangling() { + val declarationParent = parent + val realParent = if (declarationParent is IrField && declarationParent.origin == IrDeclarationOrigin.DELEGATE) + declarationParent.parent + else + declarationParent + realParent.acceptVoid(Visitor()) + } + + override fun getContextParameters(function: IrFunction): List = + function + .parameters + .filter { it.kind == IrParameterKind.Context } + .filterNot { it.isHidden } + + override fun getExtensionReceiverParameter(function: IrFunction): IrValueParameter? = + function + .parameters + .firstOrNull { it.kind == IrParameterKind.ExtensionReceiver } + ?.takeUnless { it.isHidden } + + override fun getRegularParameters(function: IrFunction): List = + function + .parameters + .filter { it.kind == IrParameterKind.Regular } + .filterNot { it.isHidden } + + override fun getReturnType(function: IrFunction) = function.returnType + + override fun getTypeParametersWithIndices(function: IrFunction, container: IrDeclaration): List> = + function.typeParameters.map { IndexedValue(it.index, it.symbol) } + + override fun isUnit(type: IrType) = type.isUnit() + + @OptIn(UnsafeDuringIrConstructionAPI::class) + final override fun getEffectiveParent(typeParameter: IrTypeParameterSymbol): IrDeclaration = typeParameter.owner.run { + when (val irParent = parent) { + is IrSimpleFunction -> irParent.correspondingPropertySymbol?.owner ?: irParent + is IrTypeParametersContainer -> irParent + else -> error("Unexpected type parameter container ${irParent.render()} for TP ${render()}") + } + } + + override fun renderDeclaration(declaration: IrDeclaration) = declaration.render() + + @OptIn(UnsafeDuringIrConstructionAPI::class) + override fun getTypeParameterName(typeParameter: IrTypeParameterSymbol) = typeParameter.owner.name.asString() + + final override fun isVararg(valueParameter: IrValueParameter) = valueParameter.varargElementType != null + + final override fun getValueParameterType(valueParameter: IrValueParameter) = valueParameter.type + + @OptIn(UnsafeDuringIrConstructionAPI::class) + final override fun getIndexOfTypeParameter(typeParameter: IrTypeParameterSymbol, container: IrDeclaration) = typeParameter.owner.index + + @OptIn(UnsafeDuringIrConstructionAPI::class) + final override fun mangleType(tBuilder: StringBuilder, type: IrType, declarationSiteSession: Nothing?) { + when (type) { + is IrSimpleType -> { + when (val classifier = type.classifier) { + is IrClassSymbol -> with(copy(MangleMode.FQNAME)) { classifier.owner.visit() } + is IrTypeParameterSymbol -> tBuilder.mangleTypeParameterReference(classifier) + is IrScriptSymbol -> {} + } + + mangleTypeArgumentsUsingEffectiveVariance(tBuilder, type, null) + + //TODO + if (type.isMarkedNullable()) tBuilder.appendSignature(MangleConstant.Q_MARK) + + mangleTypePlatformSpecific(type, tBuilder) + } + is IrDynamicType -> tBuilder.appendSignature(MangleConstant.DYNAMIC_MARK) + is IrErrorType -> tBuilder.appendSignature(MangleConstant.ERROR_MARK) + } + } + + private inner class Visitor : IrVisitorVoid() { + + override fun visitElement(element: IrElement) = + error("unexpected element ${element.render()}") + + override fun visitScript(declaration: IrScript) { + declaration.visitParent() + } + + override fun visitClass(declaration: IrClass) { + typeParameterContainers.add(declaration) + + val className = declaration.name.asString() + declaration.mangleSimpleDeclaration(className) + } + + override fun visitPackageFragment(declaration: IrPackageFragment) { + declaration.packageFqName.let { if (!it.isRoot) builder.appendName(it.asString()) } + } + + override fun visitProperty(declaration: IrProperty) { + val accessor = declaration.run { getter ?: setter } + require(accessor != null || declaration.backingField != null) { + "Expected at least one accessor or backing field for property ${declaration.render()}" + } + + typeParameterContainers.add(declaration) + declaration.visitParent() + + val isStaticProperty = if (accessor != null) + accessor.let { + it.dispatchReceiverParameter == null && declaration.parent !is IrPackageFragment && !declaration.parent.isFacadeClass + } + else { + // Fake override for a Java field + val backingField = declaration.resolveFakeOverride()?.backingField + ?: error("Expected at least one accessor or a backing field for property ${declaration.render()}") + backingField.isStatic + } + + if (isStaticProperty) { + builder.appendSignature(MangleConstant.STATIC_MEMBER_MARK) + } + + val contextParameters = accessor?.parameters?.filter { it.kind == IrParameterKind.Context }.orEmpty() + if (contextParameters.isNotEmpty()) { + contextParameters.collectForMangler(builder, MangleConstant.VALUE_PARAMETERS) { + mangleValueParameter(this, it, null) + } + } + + accessor?.parameters?.firstOrNull { it.kind == IrParameterKind.ExtensionReceiver }?.let { + builder.appendSignature(MangleConstant.EXTENSION_RECEIVER_PREFIX) + mangleValueParameter(builder, it, null) + } + + val typeParameters = accessor?.typeParameters ?: emptyList() + + typeParameters.collectForMangler(builder, MangleConstant.TYPE_PARAMETERS) { + mangleTypeParameter(this, it.symbol, it.index, null) + } + + builder.append(declaration.name.asString()) + + if (declaration.isSyntheticForJavaField) { + builder.append(MangleConstant.JAVA_FIELD_SUFFIX) + } + } + + private val IrProperty.isSyntheticForJavaField: Boolean + get() = origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && getter == null && setter == null + + @OptIn(UnsafeDuringIrConstructionAPI::class) + override fun visitField(declaration: IrField) { + val prop = declaration.correspondingPropertySymbol + if (compatibleMode || prop == null) { // act as used to be (KT-48912) + // test compiler/testData/codegen/box/ir/serializationRegressions/anonFakeOverride.kt + declaration.mangleSimpleDeclaration(declaration.name.asString()) + } else { + visitProperty(prop.owner) + } + } + + override fun visitEnumEntry(declaration: IrEnumEntry) { + declaration.mangleSimpleDeclaration(declaration.name.asString()) + } + + @OptIn(UnsafeDuringIrConstructionAPI::class) + override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer) { + val klass = declaration.parentAsClass + val anonInitializers = klass.declarations.filterIsInstance() + + val anonName = buildString { + append(MangleConstant.ANON_INIT_NAME_PREFIX) + if (anonInitializers.size > 1) { + append(MangleConstant.LOCAL_DECLARATION_INDEX_PREFIX) + append(anonInitializers.indexOf(declaration)) + } + } + + declaration.mangleSimpleDeclaration(anonName) + } + + override fun visitTypeAlias(declaration: IrTypeAlias) = + declaration.mangleSimpleDeclaration(declaration.name.asString()) + + override fun visitTypeParameter(declaration: IrTypeParameter) { + getEffectiveParent(declaration.symbol).visit() + + builder.appendSignature(MangleConstant.TYPE_PARAM_INDEX_PREFIX) + builder.appendSignature(declaration.index) + } + + @OptIn(UnsafeDuringIrConstructionAPI::class) + override fun visitSimpleFunction(declaration: IrSimpleFunction) { + val container = declaration.correspondingPropertySymbol?.owner ?: declaration + val isStatic = declaration.dispatchReceiverParameter == null && + (container.parent !is IrPackageFragment && !container.parent.isFacadeClass) + declaration.mangleFunction( + name = declaration.name, + isConstructor = false, + isStatic = isStatic, + container = container, + session = null + ) + } + + override fun visitConstructor(declaration: IrConstructor) { + declaration.mangleFunction( + name = declaration.name, + isConstructor = true, + isStatic = false, + container = declaration, + session = null + ) + } + } +} + +open class JKlibDescriptorMangleComputerBase(builder: StringBuilder, mode: MangleMode) : + BaseKotlinMangleComputer< + /*Declaration=*/DeclarationDescriptor, + /*Type=*/KotlinType, + /*TypeParameter=*/TypeParameterDescriptor, + /*ValueParameter=*/ParameterDescriptor, + /*TypeParameterContainer=*/DeclarationDescriptor, // CallableDescriptor or ClassDescriptor + /*FunctionDeclaration=*/FunctionDescriptor, + /*Session=*/Nothing?, + >(builder, mode) { + fun mangleTypeArgumentsUsingEffectiveVariance(tBuilder: StringBuilder, type: KotlinType, declarationSiteSession: Nothing?) = + with(getTypeSystemContext(declarationSiteSession)) { + val typeArguments = type.getArguments().zip(type.typeConstructor().getParameters()) + if (typeArguments.isEmpty()) return + @Suppress("UNUSED_DESTRUCTURED_PARAMETER_ENTRY") + typeArguments.collectForMangler(tBuilder, MangleConstant.TYPE_ARGUMENTS) { (typeArgument, typeParameter) -> + when { + typeArgument.isStarProjection() -> appendSignature(MangleConstant.STAR_MARK) + else -> { + val variance = AbstractTypeChecker.effectiveVariance( + typeParameter.getVariance(), typeArgument.getVariance() + ) ?: typeArgument.getVariance() + if (variance != TypeVariance.INV) { + appendSignature(variance.presentation) + appendSignature(MangleConstant.VARIANCE_SEPARATOR) + } + + @Suppress("UNCHECKED_CAST") + mangleType(this, typeArgument.getType() as KotlinType, declarationSiteSession) + } + } + } + } + + final override fun getTypeSystemContext(session: Nothing?): TypeSystemContext = SimpleClassicTypeSystemContext + + override fun copy(newMode: MangleMode) = JKlibDescriptorMangleComputerBase(builder, newMode) + + final override fun DeclarationDescriptor.visitParent() { + containingDeclaration?.visit() + } + + final override fun DeclarationDescriptor.visit() { + accept(Visitor(), null) + } + + private fun reportUnexpectedDescriptor(descriptor: DeclarationDescriptor) { + error("unexpected descriptor $descriptor") + } + + open fun PropertyDescriptor.platformSpecificSuffix(): String? = null + + private val CallableDescriptor.isRealStatic: Boolean + get() = dispatchReceiverParameter == null && containingDeclaration !is PackageFragmentDescriptor + + override fun DeclarationDescriptor.asTypeParameterContainer(): DeclarationDescriptor = + this + + override fun getContextParameters(function: FunctionDescriptor): List = + function.contextReceiverParameters + + override fun getExtensionReceiverParameter(function: FunctionDescriptor): ParameterDescriptor? = + function.extensionReceiverParameter + + override fun getRegularParameters(function: FunctionDescriptor): List = + function.valueParameters + + override fun getReturnType(function: FunctionDescriptor): KotlinType? = + function.returnType + + override fun getTypeParametersWithIndices( + function: FunctionDescriptor, + container: DeclarationDescriptor, + ): List> = + (container as? CallableDescriptor) + ?.typeParameters + .orEmpty() + .filter { it.containingDeclaration == container } + .map { IndexedValue(it.index, it) } + + override fun isUnit(type: KotlinType) = type.isUnit() + + final override fun isVararg(valueParameter: ParameterDescriptor) = valueParameter.varargElementType != null + + final override fun getValueParameterType(valueParameter: ParameterDescriptor): KotlinType = + valueParameter.type + + @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") + final override fun mangleType(tBuilder: StringBuilder, wrappedType: KotlinType, declarationSiteSession: Nothing?) { + when (val type = wrappedType.unwrap()) { + is SimpleType -> { + + when (val classifier = type.constructor.declarationDescriptor) { + is ErrorClassDescriptor -> { + tBuilder.appendSignature(MangleConstant.ERROR_MARK) + return + } + is ClassDescriptor -> with(copy(MangleMode.FQNAME)) { classifier.visit() } + is TypeParameterDescriptor -> tBuilder.mangleTypeParameterReference(classifier) + else -> error("Unexpected classifier: $classifier") + } + + mangleTypeArgumentsUsingEffectiveVariance(tBuilder, type, null) + + if (type.isMarkedNullable) tBuilder.appendSignature(MangleConstant.Q_MARK) + + mangleTypePlatformSpecific(type, tBuilder) + } + is DynamicType -> tBuilder.appendSignature(MangleConstant.DYNAMIC_MARK) + is FlexibleType -> { + // Reproduce type approximation done for flexible types in TypeTranslator. + val upper = type.upperBound + val upperDescriptor = upper.constructor.declarationDescriptor + ?: error("No descriptor for type $upper") + if (upperDescriptor is ClassDescriptor) { + val lower = type.lowerBound + val lowerDescriptor = lower.constructor.declarationDescriptor as? ClassDescriptor + ?: error("No class descriptor for lower type $lower of $type") + val intermediate = if (lowerDescriptor == upperDescriptor && type !is RawType) { + lower.replace(newArguments = upper.arguments) + } else lower + val mixed = intermediate.makeNullableAsSpecified(upper.isMarkedNullable) + mangleType(tBuilder, mixed, null) + } else mangleType(tBuilder, upper, null) + } + } + } + + final override fun getEffectiveParent(typeParameter: TypeParameterDescriptor) = typeParameter.containingDeclaration + + override fun renderDeclaration(declaration: DeclarationDescriptor) = DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(declaration) + + override fun getTypeParameterName(typeParameter: TypeParameterDescriptor) = typeParameter.name.asString() + + final override fun getIndexOfTypeParameter(typeParameter: TypeParameterDescriptor, container: DeclarationDescriptor) = + typeParameter.index + + private fun manglePropertyAccessor(accessor: PropertyAccessorDescriptor) { + val property = accessor.correspondingProperty + accessor.mangleFunction( + name = accessor.name, + isConstructor = false, + isStatic = property.isRealStatic, + container = property, + session = null + ) + } + + protected open fun visitModuleDeclaration(descriptor: ModuleDescriptor) = reportUnexpectedDescriptor(descriptor) + + private inner class Visitor : DeclarationDescriptorVisitor { + + override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: Nothing?) { + descriptor.fqName.let { if (!it.isRoot) builder.appendName(it.asString()) } + } + + override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor, data: Nothing?) = reportUnexpectedDescriptor(descriptor) + + override fun visitVariableDescriptor(descriptor: VariableDescriptor, data: Nothing?) = reportUnexpectedDescriptor(descriptor) + + override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Nothing?) { + descriptor.mangleFunction( + name = descriptor.name, + isConstructor = false, + isStatic = descriptor.isRealStatic, + container = descriptor, + session = null + ) + } + + override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor, data: Nothing?) { + descriptor.containingDeclaration.visit() + + builder.appendSignature(MangleConstant.TYPE_PARAM_INDEX_PREFIX) + builder.appendSignature(descriptor.index) + } + + override fun visitClassDescriptor(descriptor: ClassDescriptor, data: Nothing?) { + typeParameterContainers.add(descriptor) + descriptor.mangleSimpleDeclaration(descriptor.name.asString()) + } + + override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: Nothing?) { + descriptor.mangleSimpleDeclaration(descriptor.name.asString()) + } + + override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: Nothing?) { + visitModuleDeclaration(descriptor) + } + + override fun visitConstructorDescriptor(constructorDescriptor: ConstructorDescriptor, data: Nothing?) { + constructorDescriptor.mangleFunction( + name = constructorDescriptor.name, + isConstructor = true, + isStatic = constructorDescriptor.isRealStatic, + container = constructorDescriptor, + session = null + ) + } + + override fun visitScriptDescriptor(scriptDescriptor: ScriptDescriptor, data: Nothing?) = + visitClassDescriptor(scriptDescriptor, data) + + override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Nothing?) { + + if (descriptor is IrImplementingDelegateDescriptor) { + descriptor.mangleSimpleDeclaration(descriptor.name.asString()) + } else { + val actualDescriptor = (descriptor as? IrPropertyDelegateDescriptor)?.correspondingProperty ?: descriptor + + typeParameterContainers.add(actualDescriptor) + actualDescriptor.containingDeclaration.visit() + + if (actualDescriptor.isRealStatic) { + builder.appendSignature(MangleConstant.STATIC_MEMBER_MARK) + } + + val contextParameters = actualDescriptor.contextReceiverParameters + if (contextParameters.isNotEmpty()) { + contextParameters.collectForMangler(builder, MangleConstant.VALUE_PARAMETERS) { + mangleValueParameter(this, it, null) + } + } + + val extensionReceiver = actualDescriptor.extensionReceiverParameter + if (extensionReceiver != null) { + builder.appendSignature(MangleConstant.EXTENSION_RECEIVER_PREFIX) + mangleValueParameter(builder, extensionReceiver, null) + } + + actualDescriptor.typeParameters.collectForMangler(builder, MangleConstant.TYPE_PARAMETERS) { + mangleTypeParameter(this, it, it.index, null) + } + + builder.append(actualDescriptor.name.asString()) + + actualDescriptor.platformSpecificSuffix()?.let { + builder.appendSignature(it) + } + } + } + + override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor, data: Nothing?) = + reportUnexpectedDescriptor(descriptor) + + override fun visitPropertyGetterDescriptor(descriptor: PropertyGetterDescriptor, data: Nothing?) { + manglePropertyAccessor(descriptor) + } + + override fun visitPropertySetterDescriptor(descriptor: PropertySetterDescriptor, data: Nothing?) { + manglePropertyAccessor(descriptor) + } + + override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor, data: Nothing?) = + reportUnexpectedDescriptor(descriptor) + } +} diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/JKlibMangler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/JKlibMangler.kt new file mode 100644 index 0000000000000..aee95a3d48884 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/JKlibMangler.kt @@ -0,0 +1,369 @@ +/* + * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cli.jklib + +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.MangleConstant +import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleMode +import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.DescriptorBasedKotlinManglerImpl +import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.DescriptorExportCheckerVisitor +import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.DescriptorMangleComputer +import org.jetbrains.kotlin.backend.common.serialization.mangle.ir.IrBasedKotlinManglerImpl +import org.jetbrains.kotlin.backend.common.serialization.mangle.ir.IrExportCheckerVisitor +import org.jetbrains.kotlin.backend.common.serialization.mangle.ir.IrMangleComputer +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor +import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations +import org.jetbrains.kotlin.descriptors.containingPackage +import org.jetbrains.kotlin.idea.MainFunctionDetector +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.util.getPackageFragment +import org.jetbrains.kotlin.ir.util.hasAnnotation +import org.jetbrains.kotlin.ir.util.parentClassOrNull +import org.jetbrains.kotlin.load.java.JSPECIFY_NULL_MARKED_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor +import org.jetbrains.kotlin.load.java.lazy.descriptors.isJavaField +import org.jetbrains.kotlin.load.java.typeEnhancement.ENHANCED_NULLABILITY_ANNOTATIONS +import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability +import org.jetbrains.kotlin.load.kotlin.JvmType +import org.jetbrains.kotlin.load.kotlin.JvmTypeFactory +import org.jetbrains.kotlin.load.kotlin.TypeMappingConfiguration +import org.jetbrains.kotlin.load.kotlin.TypeMappingMode +import org.jetbrains.kotlin.load.kotlin.mapType +import org.jetbrains.kotlin.load.kotlin.signature +import org.jetbrains.kotlin.load.kotlin.signatures +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.SimpleType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext +import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations + +/* + * TODO: + * Code in this file is mostly copied from `JvmMangler.kt` in the Kotlin compiler. The difference is that these custom manglers aim to + * compute plain JVM signatures for Java methods. For example instead of this: + * ``` + * java/lang/Comparator.thenComparing(java.util.function.Function?){0ยง?>} + * ``` + * We will get: + * ``` + * java/util/Comparator.thenComparing(Ljava/util/function/Function;)Ljava/util/Comparator; + * ``` + * This logic aims to fix signature differences between K1 and K2. And thus, when the K1 klib deserialization part of the K2CL pipeline + * transitions to K2, code in this file should no longer be needed. + */ + +object JKlibIrMangler : IrBasedKotlinManglerImpl() { + private class JKlibIrExportChecker(compatibleMode: Boolean) : IrExportCheckerVisitor(compatibleMode) { + override fun IrDeclaration.isPlatformSpecificExported() = false + } + + @OptIn(ObsoleteDescriptorBasedAPI::class) + override fun IrDeclaration.signatureString(compatibleMode: Boolean): String { + if (!getPackageFragment().packageFqName.asString().isKotlinPackage() && isJavaBackedCallable()) { + (descriptor as? CallableDescriptor)?.computeJvmSignatureSafe()?.let { + return it + } + } + // Copied from `super` + return getMangleComputer(MangleMode.SIGNATURE, compatibleMode).computeMangle(this) + } + + private class JKlibIrManglerComputer(builder: StringBuilder, mode: MangleMode, compatibleMode: Boolean) : + JKlibIrMangleComputerBase(builder, mode, compatibleMode) { + override fun copy(newMode: MangleMode): JKlibIrMangleComputerBase = + JKlibIrManglerComputer(builder, newMode, compatibleMode) + + override fun addReturnTypeSpecialCase(function: IrFunction): Boolean = false + + @OptIn(ObsoleteDescriptorBasedAPI::class) + override fun mangleTypePlatformSpecific(type: IrType, tBuilder: StringBuilder) { + if (type.hasAnnotation(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION)) { + tBuilder.append(MangleConstant.ENHANCED_NULLABILITY_MARK) + } + } + } + + override fun getExportChecker(compatibleMode: Boolean): KotlinExportChecker = JKlibIrExportChecker(compatibleMode) + + override fun getMangleComputer(mode: MangleMode, compatibleMode: Boolean): KotlinMangleComputer = + JKlibIrManglerComputer(StringBuilder(256), mode, compatibleMode) +} + +class JKlibDescriptorMangler(private val mainDetector: MainFunctionDetector?) : DescriptorBasedKotlinManglerImpl() { + private object ExportChecker : DescriptorExportCheckerVisitor() { + override fun DeclarationDescriptor.isPlatformSpecificExported() = true + } + + override fun DeclarationDescriptor.signatureString(compatibleMode: Boolean): String { + if (this.containingPackage()?.asString()?.isKotlinPackage() == false && this is JavaCallableMemberDescriptor || containingDeclaration is JavaClassDescriptor) { + (this as? CallableDescriptor)?.computeJvmSignatureSafe()?.let { + return it + } + } + // Copied from `super` + return getMangleComputer(MangleMode.SIGNATURE, compatibleMode).computeMangle(this) + } + + private class JKlibDescriptorManglerComputer( + builder: StringBuilder, + private val mainDetector: MainFunctionDetector?, + mode: MangleMode + ) : JKlibDescriptorMangleComputerBase(builder, mode) { + override fun addReturnTypeSpecialCase(function: FunctionDescriptor): Boolean = false + + override fun copy(newMode: MangleMode): JKlibDescriptorMangleComputerBase = JKlibDescriptorManglerComputer(builder, mainDetector, newMode) + + private fun isMainFunction(descriptor: FunctionDescriptor): Boolean = + mainDetector != null && mainDetector.isMain(descriptor) + + override fun FunctionDescriptor.platformSpecificSuffix(): String? = + if (isMainFunction(this)) source.containingFile.name else null + + override fun PropertyDescriptor.platformSpecificSuffix(): String? { + // Since LV 1.4 there is a feature PreferJavaFieldOverload which allows to have java and kotlin + // properties with the same signature on the same level. + // For more details see JvmPlatformOverloadsSpecificityComparator.kt + return if (isJavaField) MangleConstant.JAVA_FIELD_SUFFIX else null + } + + override fun visitModuleDeclaration(descriptor: ModuleDescriptor) { + // In general, having module descriptor as `containingDeclaration` for regular declaration is considered an error (in JS/Native) + // because there should be `PackageFragmentDescriptor` in between + // but on JVM there is `SyntheticJavaPropertyDescriptor` whose parent is a module. So let just skip it. + } + + override fun mangleTypePlatformSpecific(type: KotlinType, tBuilder: StringBuilder) { + // Disambiguate between 'double' and '@NotNull java.lang.Double' types in mixed Java/Kotlin class hierarchies + if (SimpleClassicTypeSystemContext.hasEnhancedNullability(type)) { + tBuilder.appendSignature(MangleConstant.ENHANCED_NULLABILITY_MARK) + } + } + } + + override fun getExportChecker(compatibleMode: Boolean): KotlinExportChecker = ExportChecker + + override fun getMangleComputer(mode: MangleMode, compatibleMode: Boolean): KotlinMangleComputer = + JKlibDescriptorManglerComputer(StringBuilder(256), mainDetector, mode) +} + +fun String.isKotlinPackage(): Boolean { + return this == "kotlin" || startsWith("kotlin.") +} + +private object JvmTypeFactoryImpl : JvmTypeFactory { + private val BOOLEAN = JvmType.Primitive(JvmPrimitiveType.BOOLEAN) + private val CHAR = JvmType.Primitive(JvmPrimitiveType.CHAR) + private val BYTE = JvmType.Primitive(JvmPrimitiveType.BYTE) + private val SHORT = JvmType.Primitive(JvmPrimitiveType.SHORT) + private val INT = JvmType.Primitive(JvmPrimitiveType.INT) + private val FLOAT = JvmType.Primitive(JvmPrimitiveType.FLOAT) + private val LONG = JvmType.Primitive(JvmPrimitiveType.LONG) + private val DOUBLE = JvmType.Primitive(JvmPrimitiveType.DOUBLE) + + override fun boxType(possiblyPrimitiveType: JvmType) = + when { + possiblyPrimitiveType is JvmType.Primitive && possiblyPrimitiveType.jvmPrimitiveType != null -> + createObjectType( + JvmClassName.byFqNameWithoutInnerClasses(possiblyPrimitiveType.jvmPrimitiveType!!.wrapperFqName).internalName + ) + else -> possiblyPrimitiveType + } + + override fun createFromString(representation: String): JvmType { + assert(representation.isNotEmpty()) { "empty string as JvmType" } + val firstChar = representation[0] + + JvmPrimitiveType.values().firstOrNull { it.desc[0] == firstChar }?.let { + return JvmType.Primitive(it) + } + + return when (firstChar) { + 'V' -> JvmType.Primitive(null) + '[' -> JvmType.Array(createFromString(representation.substring(1))) + else -> { + assert(firstChar == 'L' && representation.endsWith(';')) { + "Type that is not primitive nor array should be Object, but '$representation' was found" + } + + JvmType.Object(representation.substring(1, representation.length - 1)) + } + } + } + + override fun createPrimitiveType(primitiveType: PrimitiveType): JvmType = + when (primitiveType) { + PrimitiveType.BOOLEAN -> BOOLEAN + PrimitiveType.CHAR -> CHAR + PrimitiveType.BYTE -> BYTE + PrimitiveType.SHORT -> SHORT + PrimitiveType.INT -> INT + PrimitiveType.FLOAT -> FLOAT + PrimitiveType.LONG -> LONG + PrimitiveType.DOUBLE -> DOUBLE + } + + override fun createObjectType(internalName: String): JvmType.Object = + JvmType.Object(internalName) + + override fun toString(type: JvmType): String = + when (type) { + is JvmType.Array -> "[" + toString(type.elementType) + is JvmType.Primitive -> type.jvmPrimitiveType?.desc ?: "V" + is JvmType.Object -> "L" + type.internalName + ";" + } + + override val javaLangClassType: JvmType + get() = createObjectType("java/lang/Class") + +} + +internal object TypeMappingConfigurationImpl : TypeMappingConfiguration { + override fun commonSupertype(types: Collection): KotlinType { + throw AssertionError("There should be no intersection type in existing descriptors, but found: " + types.joinToString()) + } + + override fun getPredefinedTypeForClass(classDescriptor: ClassDescriptor): JvmType? = null + override fun getPredefinedInternalNameForClass(classDescriptor: ClassDescriptor): String? = null + + override fun preprocessType(kotlinType: KotlinType): KotlinType? { + return null +// type. + } + + override fun processErrorType(kotlinType: KotlinType, descriptor: ClassDescriptor) { + // DO nothing + } +} + +fun StringBuilder.appendErasedType(type: KotlinType) { + append(type.mapToJvmType()) +} + +fun KotlinType.mapToJvmType(): JvmType = + mapType(this, JvmTypeFactoryImpl, TypeMappingMode.DEFAULT, TypeMappingConfigurationImpl, descriptorTypeWriter = null) + +fun hasVoidReturnType(descriptor: CallableDescriptor): Boolean { + if (descriptor is ConstructorDescriptor) return true + return KotlinBuiltIns.isUnit(descriptor.returnType!!) && !TypeUtils.isNullableType(descriptor.returnType!!) + && descriptor !is PropertyGetterDescriptor +} + +fun FunctionDescriptor.computeJvmDescriptor(withReturnType: Boolean = true, withName: Boolean = true): String = buildString { + if (withName) { + append(if (this@computeJvmDescriptor is ConstructorDescriptor) "" else name.asString()) + } + + append("(") + + extensionReceiverParameter?.let { + appendErasedType(it.type) + } + + for (parameter in valueParameters) { + appendErasedType(parameter.type) + } + + append(")") + + if (withReturnType) { + if (hasVoidReturnType(this@computeJvmDescriptor)) { + append("V") + } else { + val returnType = returnType!! + // Workaround for b/447548985 + // Given a class annotated with `@NullMarked`, there will be such difference between K1 and K2 signatures for + // functions containing primitive types in arguments/return value: + // ``` + // kotlinjavainterop/NullMarkedClass.getNonNullInteger()I // K2 + // kotlinjavainterop/NullMarkedClass.getNonNullInteger()Ljava/lang/Integer; // K1 + // ``` + // The reason is that in K1 the return type is annotated with `@EnhancedNullability`, but in K2 it isn't. + // The workaround forces adding `@EnhancedNullability` for such types so that they are always boxed + // when computing function's signature. + if (returnType is SimpleType && + KotlinBuiltIns.isPrimitiveType(returnType) && + !returnType.hasEnhancedNullability() && + containingDeclaration.annotations.hasAnnotation(JSPECIFY_NULL_MARKED_ANNOTATION_FQ_NAME) + ) { + val newType = returnType.replaceAnnotations( + CompositeAnnotations( + returnType.annotations, + ENHANCED_NULLABILITY_ANNOTATIONS + ) + ) + appendErasedType(newType) + } else { + appendErasedType(returnType) + } + } + } +} + +fun CallableDescriptor.computeJvmSignature(): String? = signatures { + if (DescriptorUtils.isLocal(this@computeJvmSignature)) return null + + val classDescriptor = containingDeclaration as? ClassDescriptor ?: return null + if (classDescriptor.name.isSpecial) return null + + signature( + classDescriptor, + (original as? FunctionDescriptor ?: return null).computeJvmDescriptor() + ) +} + +fun CallableDescriptor.computeJvmSignatureSafe(): String? { + return try { + computeJvmSignature() + } catch (_: Exception) { + null + } +} + +fun IrDeclaration.isDeclaredInJava(): Boolean { + if (origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) return true + val ownerClass = parentClassOrNull + if (ownerClass?.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) return true + return false +} + +@OptIn(UnsafeDuringIrConstructionAPI::class) +fun IrDeclaration.isJavaBackedCallable(): Boolean { + if (isDeclaredInJava()) return true + + when (this) { + is IrSimpleFunction -> { + if (this.isFakeOverride && overriddenSymbols.any { it.owner.isDeclaredInJava() }) return true + } + is IrProperty -> { + // Check accessors and their overrides + val accs = listOfNotNull(getter, setter) + if (accs.any { it.isFakeOverride && it.overriddenSymbols.any { s -> s.owner.isDeclaredInJava() } }) return true + } + } + return false +} diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/JKlibModuleSerializer.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/JKlibModuleSerializer.kt new file mode 100644 index 0000000000000..093494de7c6a5 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/JKlibModuleSerializer.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cli.jklib + +import org.jetbrains.kotlin.backend.common.serialization.* +import org.jetbrains.kotlin.ir.IrDiagnosticReporter +import org.jetbrains.kotlin.ir.declarations.IrFile + +object JKlibGlobalDeclarationTable : GlobalDeclarationTable(JKlibIrMangler) + +class JKlibModuleSerializer( + settings: IrSerializationSettings, + diagnosticReporter: IrDiagnosticReporter, +) : IrModuleSerializer( + settings, diagnosticReporter +) { + + override val globalDeclarationTable = JKlibGlobalDeclarationTable + + + override fun createSerializerForFile(file: IrFile): IrFileSerializer = + IrFileSerializer(settings, DeclarationTable.Default(globalDeclarationTable)) + +} \ No newline at end of file diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/K2JKlibCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/K2JKlibCompiler.kt new file mode 100644 index 0000000000000..51d8041b5bf97 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jklib/K2JKlibCompiler.kt @@ -0,0 +1,948 @@ +/* + * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cli.jklib + +import com.intellij.openapi.Disposable +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext +import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl +import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols +import org.jetbrains.kotlin.backend.common.overrides.IrLinkerFakeOverrideProvider +import org.jetbrains.kotlin.backend.common.serialization.* +import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData +import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor +import org.jetbrains.kotlin.backend.common.toLogger +import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensionsImpl +import org.jetbrains.kotlin.backend.jvm.JvmIrDeserializerImpl +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns +import org.jetbrains.kotlin.builtins.jvm.JvmBuiltInsPackageFragmentProvider +import org.jetbrains.kotlin.cli.common.* +import org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR +import org.jetbrains.kotlin.cli.common.arguments.K2JKlibCompilerArguments +import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot +import org.jetbrains.kotlin.cli.common.fir.reportToMessageCollector +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.EXCEPTION +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.messages.MessageUtil +import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil +import org.jetbrains.kotlin.cli.common.modules.ModuleBuilder +import org.jetbrains.kotlin.cli.jvm.compiler.* +import org.jetbrains.kotlin.cli.jvm.compiler.legacy.pipeline.convertToIrAndActualizeForJvm +import org.jetbrains.kotlin.cli.jvm.compiler.legacy.pipeline.createProjectEnvironment +import org.jetbrains.kotlin.cli.jvm.config.* +import org.jetbrains.kotlin.cli.jvm.configureJdkHomeFromSystemProperty +import org.jetbrains.kotlin.codegen.CompilationException +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.config.CommonConfigurationKeys.MODULE_NAME +import org.jetbrains.kotlin.container.get +import org.jetbrains.kotlin.context.ContextForNewModule +import org.jetbrains.kotlin.context.ProjectContext +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory +import org.jetbrains.kotlin.fir.DependencyListForCliModule +import org.jetbrains.kotlin.fir.backend.jvm.JvmFir2IrExtensions +import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar +import org.jetbrains.kotlin.fir.pipeline.* +import org.jetbrains.kotlin.fir.session.FirSharableJavaComponents +import org.jetbrains.kotlin.fir.session.environment.AbstractProjectFileSearchScope +import org.jetbrains.kotlin.fir.session.firCachesFactoryForCliMode +import org.jetbrains.kotlin.frontend.java.di.createContainerForLazyResolveWithJava +import org.jetbrains.kotlin.frontend.java.di.initialize +import org.jetbrains.kotlin.idea.MainFunctionDetector +import org.jetbrains.kotlin.incremental.components.* +import org.jetbrains.kotlin.ir.InternalSymbolFinderAPI +import org.jetbrains.kotlin.ir.IrBuiltIns +import org.jetbrains.kotlin.ir.IrDiagnosticReporter +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner +import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl +import org.jetbrains.kotlin.ir.descriptors.IrDescriptorBasedFunctionFactory +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI +import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl +import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator +import org.jetbrains.kotlin.ir.util.IdSignature +import org.jetbrains.kotlin.ir.util.SymbolTable +import org.jetbrains.kotlin.ir.util.getNameWithAssert +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.library.* +import org.jetbrains.kotlin.library.impl.BuiltInsPlatform +import org.jetbrains.kotlin.library.impl.buildKotlinLibrary +import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories +import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary +import org.jetbrains.kotlin.library.metadata.resolver.impl.KotlinResolvedLibraryImpl +import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor +import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver +import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment +import org.jetbrains.kotlin.load.java.structure.JavaClass +import org.jetbrains.kotlin.load.java.structure.impl.VirtualFileBoundJavaClass +import org.jetbrains.kotlin.load.kotlin.JavaFlexibleTypeDeserializer +import org.jetbrains.kotlin.metadata.builtins.BuiltInsBinaryVersion +import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.jvm.JvmPlatforms +import org.jetbrains.kotlin.platform.konan.NativePlatforms +import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors +import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl +import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl +import org.jetbrains.kotlin.resolve.BindingTraceContext +import org.jetbrains.kotlin.resolve.CompilerEnvironment +import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver +import org.jetbrains.kotlin.resolve.jvm.multiplatform.OptionalAnnotationPackageFragmentProvider +import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.util.klibMetadataVersionOrDefault +import org.jetbrains.kotlin.utils.KotlinPaths +import org.jetbrains.kotlin.utils.PathUtil +import org.jetbrains.kotlin.utils.memoryOptimizedMap +import java.util.* + +/** + * This class is the entry-point for compiling Kotlin code into a Klib with references to jars. + * + */ +@OptIn(UnsafeDuringIrConstructionAPI::class, ObsoleteDescriptorBasedAPI::class) +class K2JKlibCompiler : CLICompiler() { + private lateinit var klibFactories: KlibMetadataFactories + private lateinit var storageManager: StorageManager + private var runtimeModule: ModuleDescriptor? = null + private lateinit var configuration: CompilerConfiguration + private val _descriptors: MutableMap = mutableMapOf() + override val platform: TargetPlatform + get() = JvmPlatforms.defaultJvmPlatform + + override fun createArguments() = K2JKlibCompilerArguments() + + override fun setupPlatformSpecificArgumentsAndServices( + configuration: CompilerConfiguration, + arguments: K2JKlibCompilerArguments, + services: Services, + ) { + // No specific arguments yet + } + + override fun MutableList.addPlatformOptions(arguments: K2JKlibCompilerArguments) {} + + enum class OutputKind { + LIBRARY, IR, + } + + @UnsafeDuringIrConstructionAPI + class JKlibLinker( + module: ModuleDescriptor, + messageCollector: MessageCollector, + irBuiltIns: IrBuiltIns, + symbolTable: SymbolTable, + val stubGenerator: DeclarationStubGeneratorImpl, + val mangler: JKlibDescriptorMangler, + ) : KotlinIrLinker(module, messageCollector, irBuiltIns, symbolTable, emptyList()) { + override val returnUnboundSymbolsIfSignatureNotFound + get() = false + + private val javaName = Name.identifier("java") + + private fun DeclarationDescriptor.isJavaDescriptor(): Boolean { + if (this is PackageFragmentDescriptor) { + return this is LazyJavaPackageFragment || fqName.startsWith(javaName) + } + + return this is JavaClassDescriptor || this is JavaCallableMemberDescriptor || (containingDeclaration?.isJavaDescriptor() == true) + } + + override fun platformSpecificSymbol(symbol: IrSymbol): Boolean { + return symbol.descriptor.isJavaDescriptor() + } + + private fun DeclarationDescriptor.isCleanDescriptor(): Boolean { + if (this is PropertyAccessorDescriptor) return correspondingProperty.isCleanDescriptor() + return this is DeserializedDescriptor + } + + private fun declareJavaFieldStub(symbol: IrFieldSymbol): IrField { + return with(stubGenerator) { + val old = stubGenerator.unboundSymbolGeneration + try { + stubGenerator.unboundSymbolGeneration = true + generateFieldStub(symbol.descriptor) + } finally { + stubGenerator.unboundSymbolGeneration = old + } + } + } + + override val fakeOverrideBuilder = IrLinkerFakeOverrideProvider( + linker = this, + symbolTable = symbolTable, + mangler = JKlibIrMangler, + typeSystem = IrTypeSystemContextImpl(builtIns), + friendModules = emptyMap(), + partialLinkageSupport = partialLinkageSupport, + ) + + override fun isBuiltInModule(moduleDescriptor: ModuleDescriptor): Boolean = + moduleDescriptor === moduleDescriptor.builtIns.builtInsModule + + private val IrLibrary.libContainsErrorCode: Boolean + get() = this is KotlinLibrary && this.containsErrorCode + + override fun createModuleDeserializer( + moduleDescriptor: ModuleDescriptor, + klib: KotlinLibrary?, + strategyResolver: (String) -> DeserializationStrategy, + ): IrModuleDeserializer { + if (klib == null) { + return MetadataJVMModuleDeserializer(moduleDescriptor, emptyList()) + } + + val libraryAbiVersion = klib.versions.abiVersion ?: KotlinAbiVersion.CURRENT + return JKlibModuleDeserializer( + moduleDescriptor, + klib, + strategyResolver, + libraryAbiVersion, + klib.libContainsErrorCode, + ) + } + + private val mappedClassSymbols = mutableMapOf() + + private fun FqName.isMappedType(): Boolean { + return (JavaToKotlinClassMap.mapJavaToKotlin(this) ?: JavaToKotlinClassMap.mapKotlinToJava(toUnsafe())) != null + } + + private fun withKotlinBuiltinsHack(idSig: IdSignature, f: () -> IrSymbol?): IrSymbol? { + val symbol = f() + if (idSig is IdSignature.CommonSignature) { + val fqName = FqName("${idSig.packageFqName}.${idSig.declarationFqName}") + if (symbol != null) { + if (symbol is IrClassSymbol && fqName.isMappedType() && fqName !in mappedClassSymbols) { + mappedClassSymbols[fqName] = symbol + } + return symbol + } + + val funName = idSig.nameSegments.last() + for (declaration in mappedClassSymbols.values.flatMap { it.owner.declarations }) { + if (declaration.getNameWithAssert().asString() == funName) { + return declaration.symbol + } + } + return null + } + return symbol + } + + private inner class MetadataJVMModuleDeserializer( + moduleDescriptor: ModuleDescriptor, + dependencies: List, + ) : IrModuleDeserializer(moduleDescriptor, KotlinAbiVersion.CURRENT) { + + override fun contains(idSig: IdSignature): Boolean = true + + private val descriptorFinder = DescriptorByIdSignatureFinderImpl( + moduleDescriptor, + mangler, + DescriptorByIdSignatureFinderImpl.LookupMode.MODULE_ONLY, + ) + + private fun resolveDescriptor(idSig: IdSignature): DeclarationDescriptor? = descriptorFinder.findDescriptorBySignature(idSig) + + override fun tryDeserializeIrSymbol( + idSig: IdSignature, + symbolKind: BinarySymbolData.SymbolKind, + ): IrSymbol? = withKotlinBuiltinsHack(idSig) { + val descriptor = resolveDescriptor(idSig) ?: return@withKotlinBuiltinsHack null + + val declaration = stubGenerator.run { + when (symbolKind) { + BinarySymbolData.SymbolKind.CLASS_SYMBOL -> generateClassStub(descriptor as ClassDescriptor) + BinarySymbolData.SymbolKind.PROPERTY_SYMBOL -> generatePropertyStub(descriptor as PropertyDescriptor) + BinarySymbolData.SymbolKind.FUNCTION_SYMBOL -> generateFunctionStub(descriptor as FunctionDescriptor) + BinarySymbolData.SymbolKind.CONSTRUCTOR_SYMBOL -> generateConstructorStub(descriptor as ClassConstructorDescriptor) + BinarySymbolData.SymbolKind.ENUM_ENTRY_SYMBOL -> generateEnumEntryStub(descriptor as ClassDescriptor) + BinarySymbolData.SymbolKind.TYPEALIAS_SYMBOL -> generateTypeAliasStub(descriptor as TypeAliasDescriptor) + BinarySymbolData.SymbolKind.STANDALONE_FIELD_SYMBOL -> generateFieldStub(descriptor as PropertyDescriptor) + else -> error("Unexpected type $symbolKind for sig $idSig") + } + } + + return@withKotlinBuiltinsHack declaration.symbol + } + + override fun deserializedSymbolNotFound(idSig: IdSignature): Nothing = error("No descriptor found for $idSig") + + override fun declareIrSymbol(symbol: IrSymbol) { + if (symbol is IrFieldSymbol) { + declareJavaFieldStub(symbol) + } else { + stubGenerator.generateMemberStub(symbol.descriptor) + } + } + + override val moduleFragment: IrModuleFragment = IrModuleFragmentImpl(moduleDescriptor) + override val moduleDependencies: Collection = dependencies + + override val kind + get() = IrModuleDeserializerKind.SYNTHETIC + } + + private val deserializedFilesInKlibOrder = mutableMapOf>() + + private inner class JKlibModuleDeserializer( + moduleDescriptor: ModuleDescriptor, + klib: IrLibrary, + strategyResolver: (String) -> DeserializationStrategy, + libraryAbiVersion: KotlinAbiVersion, + allowErrorCode: Boolean, + ) : BasicIrModuleDeserializer( + this, + moduleDescriptor, + klib, + strategyResolver, + libraryAbiVersion, + allowErrorCode, + ) { + + override fun init(delegate: IrModuleDeserializer) { + super.init(delegate) + deserializedFilesInKlibOrder[moduleFragment] = fileDeserializationStates.memoryOptimizedMap { it.file } + } + + private val descriptorSignatures = mutableMapOf() + + private val descriptorByIdSignatureFinder = DescriptorByIdSignatureFinderImpl( + moduleDescriptor, + mangler, + DescriptorByIdSignatureFinderImpl.LookupMode.MODULE_ONLY, + ) + + private val deserializedSymbols = mutableMapOf() + + override fun tryDeserializeIrSymbol( + idSig: IdSignature, + symbolKind: BinarySymbolData.SymbolKind, + ): IrSymbol? = withKotlinBuiltinsHack(idSig) { + super.tryDeserializeIrSymbol(idSig, symbolKind)?.let { + return@withKotlinBuiltinsHack it + } + deserializedSymbols[idSig]?.let { + return@withKotlinBuiltinsHack it + } + val descriptor = descriptorByIdSignatureFinder.findDescriptorBySignature(idSig) ?: return@withKotlinBuiltinsHack null + descriptorSignatures[descriptor] = idSig + return@withKotlinBuiltinsHack (stubGenerator.generateMemberStub(descriptor) as IrSymbolOwner).symbol + } + } + + override fun createCurrentModuleDeserializer( + moduleFragment: IrModuleFragment, + dependencies: Collection, + ): IrModuleDeserializer = JvmCurrentModuleDeserializer(moduleFragment, dependencies) + + private inner class JvmCurrentModuleDeserializer( + moduleFragment: IrModuleFragment, + dependencies: Collection, + ) : CurrentModuleDeserializer(moduleFragment, dependencies) { + override fun declareIrSymbol(symbol: IrSymbol) { + val descriptor = symbol.descriptor + + if (descriptor.isJavaDescriptor()) { + // Wrap java declaration with lazy ir + if (symbol is IrFieldSymbol) { + declareJavaFieldStub(symbol) + } else { + stubGenerator.generateMemberStub(descriptor) + } + return + } + + if (descriptor.isCleanDescriptor()) { + stubGenerator.generateMemberStub(descriptor) + return + } + + super.declareIrSymbol(symbol) + } + } + } + + public override fun doExecute( + arguments: K2JKlibCompilerArguments, + configuration: CompilerConfiguration, + rootDisposable: Disposable, + paths: KotlinPaths?, + ): ExitCode { + this.configuration = configuration + val messageCollector = configuration.getNotNull(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY) + val destination = File( + arguments.destination.let { + if (it.isNullOrBlank()) { + messageCollector.report(ERROR, "Specify destination via -d") + return ExitCode.INTERNAL_ERROR + } + it + }) + val outputKind = OutputKind.valueOf((arguments.produce ?: "ir").uppercase()) + val exitCodeKlib = compileLibrary(arguments, rootDisposable, paths, destination) + if (outputKind == OutputKind.LIBRARY || exitCodeKlib != ExitCode.OK) return exitCodeKlib + return ExitCode.OK + } + + fun createJarDependenciesModuleDescriptor( + projectEnvironment: VfsBasedProjectEnvironment, + projectContext: ProjectContext, + ): ModuleDescriptorImpl { + val languageVersionSettings = configuration.languageVersionSettings + val fallbackBuiltIns = JvmBuiltIns(storageManager, JvmBuiltIns.Kind.FALLBACK).apply { + initialize(builtInsModule, languageVersionSettings) + } + + val platform = JvmPlatforms.defaultJvmPlatform + val dependenciesContext = ContextForNewModule( + projectContext, + Name.special(""), + fallbackBuiltIns, + platform, + ) + + // Scope for the dependency module contains everything except files present in the scope for the + // source module + val scope = TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(projectContext.project) + val dependencyScope = GlobalSearchScope.notScope(scope) + + val moduleClassResolver = SourceOrBinaryModuleClassResolver(scope) + val lookupTracker = LookupTracker.DO_NOTHING + val expectActualTracker = ExpectActualTracker.DoNothing + val inlineConstTracker = InlineConstTracker.DoNothing + val enumWhenTracker = EnumWhenTracker.DoNothing + + val configureJavaClassFinder = null + val implicitsResolutionFilter = null + val packagePartProvider = projectEnvironment.getPackagePartProvider(dependencyScope.toAbstractProjectFileSearchScope()) + val trace = NoScopeRecordCliBindingTrace(projectContext.project) + val dependenciesContainer = createContainerForLazyResolveWithJava( + platform, + dependenciesContext, + trace, + DeclarationProviderFactory.EMPTY, + dependencyScope, + moduleClassResolver, + CompilerEnvironment, + lookupTracker, + expectActualTracker, + inlineConstTracker, + enumWhenTracker, + packagePartProvider, + languageVersionSettings, + useBuiltInsProvider = true, + configureJavaClassFinder = configureJavaClassFinder, + implicitsResolutionFilter = implicitsResolutionFilter, + ) + moduleClassResolver.compiledCodeResolver = dependenciesContainer.get() + + dependenciesContext.setDependencies( + listOf(dependenciesContext.module, fallbackBuiltIns.builtInsModule) + ) + dependenciesContext.initializeModuleContents( + CompositePackageFragmentProvider( + listOf( + moduleClassResolver.compiledCodeResolver.packageFragmentProvider, + dependenciesContainer.get(), + dependenciesContainer.get(), + ), + "CompositeProvider@TopDownAnalyzerForJvm for dependencies ${dependenciesContext.module}", + ) + ) + return dependenciesContext.module + } + + @Suppress("UNUSED") + class CompilationResult( + val pluginContext: IrPluginContext, + val mainModuleFragment: IrModuleFragment, + ) + + @Suppress("UNUSED") + fun compileKlibAndDeserializeIr( + arguments: K2JKlibCompilerArguments, + configuration: CompilerConfiguration, + rootDisposable: Disposable, + paths: KotlinPaths?, + ): CompilationResult { + this.configuration = configuration + val klibDestination = File(System.getProperty("java.io.tmpdir"), "${UUID.randomUUID()}.klib").also { + require(!it.exists) { "Collision writing intermediate KLib $it" } + it.deleteOnExit() + } + compileLibrary(arguments, rootDisposable, paths, klibDestination) + return compileIr(arguments, rootDisposable, klibDestination) + } + + @OptIn(InternalSymbolFinderAPI::class) + fun compileIr( + arguments: K2JKlibCompilerArguments, + disposable: Disposable, + klib: File, + ): CompilationResult { + val messageCollector = configuration.getNotNull(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY) + configuration.put(MODULE_NAME, arguments.moduleName ?: JvmProtoBufUtil.DEFAULT_MODULE_NAME) + + val projectEnvironment = createProjectEnvironment( + configuration, + disposable, + EnvironmentConfigFiles.JVM_CONFIG_FILES, + messageCollector, + ) + + val klibFiles = configuration.getList(JVMConfigurationKeys.KLIB_PATHS) + klib.absolutePath + + val projectContext = ProjectContext(projectEnvironment.project, "TopDownAnalyzer for JKlib") + storageManager = projectContext.storageManager + val builtIns = JvmBuiltIns(projectContext.storageManager, JvmBuiltIns.Kind.FROM_DEPENDENCIES) + + klibFactories = KlibMetadataFactories({ builtIns }, JavaFlexibleTypeDeserializer) + val trace = BindingTraceContext(projectContext.project) + + val allDependencies = CommonKLibResolver.resolve(klibFiles, messageCollector.toLogger()) + val moduleDependencies = + allDependencies.getFullResolvedList().associate { klib -> klib.library to klib.resolvedDependencies.map { d -> d.library } } + .toMap() + val sortedDependencies = sortDependencies(moduleDependencies) + + val jarDepsModuleDescriptor = createJarDependenciesModuleDescriptor(projectEnvironment, projectContext) + val descriptors = sortedDependencies.map { getModuleDescriptor(it) } + jarDepsModuleDescriptor + descriptors.forEach { descriptor -> descriptor.setDependencies(descriptors) } + + val mainModuleLib = sortedDependencies.find { it.libraryFile == klib } + + val mainModule = getModuleDescriptor(mainModuleLib!!) + + val mangler = JKlibDescriptorMangler( + MainFunctionDetector(trace.bindingContext, configuration.languageVersionSettings) + ) + val symbolTable = SymbolTable(IdSignatureDescriptor(mangler), IrFactoryImpl) + val typeTranslator = TypeTranslatorImpl(symbolTable, configuration.languageVersionSettings, mainModule) + val irBuiltIns = IrBuiltInsOverDescriptors(mainModule.builtIns, typeTranslator, symbolTable) + val stubGenerator = DeclarationStubGeneratorImpl( + mainModule, + symbolTable, + irBuiltIns, + DescriptorByIdSignatureFinderImpl(mainModule, mangler), + JvmGeneratorExtensionsImpl(configuration), + ).apply { unboundSymbolGeneration = true } + val linker = JKlibLinker( + module = mainModule, + messageCollector = messageCollector, + irBuiltIns = irBuiltIns, + symbolTable = symbolTable, + stubGenerator = stubGenerator, + mangler = mangler, + ) + + val pluginContext = IrPluginContextImpl( + mainModule, + trace.bindingContext, + configuration.languageVersionSettings, + symbolTable, + typeTranslator, + irBuiltIns, + linker = linker, + messageCollector = messageCollector, + ) + + // Deserialize modules + // We explicitly use the DeserializationStrategy.ALL to deserialize the whole world, + // so that we don't rely on linker side effects for proper deserialization. + linker.deserializeIrModuleHeader( + jarDepsModuleDescriptor, + null, + { DeserializationStrategy.ALL }, + jarDepsModuleDescriptor.name.asString(), + ) + + lateinit var mainModuleFragment: IrModuleFragment + for (dep in sortedDependencies) { + val descriptor = getModuleDescriptor(dep) + when { + descriptor == mainModule -> { + mainModuleFragment = linker.deserializeIrModuleHeader(descriptor, dep, { DeserializationStrategy.ALL }) + } + else -> linker.deserializeIrModuleHeader(descriptor, dep, { DeserializationStrategy.ALL }) + } + } + + irBuiltIns.functionFactory = IrDescriptorBasedFunctionFactory(irBuiltIns, symbolTable, typeTranslator, null, true) + + linker.init(null) + ExternalDependenciesGenerator(symbolTable, listOf(linker)).generateUnboundSymbolsAsDependencies() + linker.postProcess(inOrAfterLinkageStep = true) + + linker.checkNoUnboundSymbols(symbolTable, "Found unbound symbol") + + return CompilationResult(pluginContext, mainModuleFragment) + } + + private fun getModuleDescriptor(current: KotlinLibrary): ModuleDescriptorImpl { + if (current in _descriptors) { + return _descriptors.getValue(current) + } + + val isBuiltIns = current.unresolvedDependencies.isEmpty() + + val lookupTracker = LookupTracker.DO_NOTHING + val md = klibFactories.DefaultDeserializedDescriptorFactory.createDescriptorOptionalBuiltIns( + current, + configuration.languageVersionSettings, + storageManager, + runtimeModule?.builtIns, + packageAccessHandler = null, + lookupTracker = lookupTracker, + ) + if (isBuiltIns) runtimeModule = md + + _descriptors[current] = md + + return md + } + + private fun compileLibrary( + arguments: K2JKlibCompilerArguments, + rootDisposable: Disposable, + paths: KotlinPaths?, + destination: File, + ): ExitCode { + val collector = configuration.getNotNull(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY) + val performanceManager = configuration.getNotNull(CLIConfigurationKeys.PERF_MANAGER) + + val pluginLoadResult = loadPlugins(paths, arguments, configuration, rootDisposable) + if (pluginLoadResult != ExitCode.OK) return pluginLoadResult + + val commonSources = arguments.commonSources?.toSet() ?: emptySet() + + for (arg in arguments.freeArgs) { + configuration.addKotlinSourceRoot(arg, isCommon = arg in commonSources, hmppModuleName = null) + } + + with(configuration) { + if (arguments.noJdk) { + put(JVMConfigurationKeys.NO_JDK, true) + } else { + configureJdkHomeFromSystemProperty() + } + configuration.configureJdkClasspathRoots() + if (!arguments.noStdlib) { + getLibraryFromHome( + paths, + KotlinPaths::stdlibPath, + PathUtil.KOTLIN_JAVA_STDLIB_JAR, + messageCollector, + "'-no-stdlib'", + )?.let { file -> + add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(file)) + add(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, "kotlin.stdlib") + } + getLibraryFromHome( + paths, + KotlinPaths::scriptRuntimePath, + PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR, + messageCollector, + "'-no-stdlib'", + )?.let { file -> + add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(file)) + add(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, "kotlin.script.runtime") + } + } + // "-no-stdlib" implies "-no-reflect": otherwise we would be able to transitively read stdlib + // classes through kotlin-reflect, + // which is likely not what user wants since s/he manually provided "-no-stdlib"` + if (!arguments.noReflect && !arguments.noStdlib) { + getLibraryFromHome( + paths, + KotlinPaths::reflectPath, + PathUtil.KOTLIN_JAVA_REFLECT_JAR, + messageCollector, + "'-no-reflect' or '-no-stdlib'", + )?.let { file -> + add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(file)) + add(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, "kotlin.reflect") + } + } + arguments.klibLibraries?.let { libraries -> + put( + JVMConfigurationKeys.KLIB_PATHS, + libraries.split(File.pathSeparator.toRegex()).filterNot(String::isEmpty), + ) + } + for (path in arguments.classpath?.split(java.io.File.pathSeparatorChar).orEmpty()) { + add(CLIConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(java.io.File(path))) + } + } + + val moduleName = arguments.moduleName ?: JvmProtoBufUtil.DEFAULT_MODULE_NAME + configuration.put(MODULE_NAME, moduleName) + + configuration.put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage) + configuration.put( + CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME, + arguments.renderInternalDiagnosticNames, + ) + + val projectEnvironment = createProjectEnvironment( + configuration, + rootDisposable, + EnvironmentConfigFiles.JVM_CONFIG_FILES, + collector, + ) + val groupedSources = collectSources(configuration, projectEnvironment, collector) + + if (groupedSources.isEmpty()) { + if (arguments.version) { + return ExitCode.OK + } + collector.report(ERROR, "No source files") + return COMPILATION_ERROR + } + + try { + val rootModuleNameAsString = configuration.getNotNull(MODULE_NAME) + val rootModuleName = Name.special("<${rootModuleNameAsString}>") + + val module = ModuleBuilder( + configuration[MODULE_NAME] ?: JvmProtoBufUtil.DEFAULT_MODULE_NAME, + destination.path, + "java-production", + ) + with(module) { + arguments.friendPaths?.forEach { addFriendDir(it) } + arguments.classpath?.split(File.pathSeparator)?.forEach { addClasspathEntry(it) } + } + + val diagnosticsReporter = DiagnosticReporterFactory.createPendingReporter(collector) + + val klibFiles = configuration.getList(JVMConfigurationKeys.KLIB_PATHS) + + val logger = collector.toLogger() + + val resolvedLibraries = klibFiles.map { KotlinResolvedLibraryImpl(resolveSingleFileKlib(File(it), logger)) } + val extensionRegistrars = FirExtensionRegistrar.getInstances(projectEnvironment.project) + val ltFiles = groupedSources.let { it.commonSources + it.platformSources }.toList() + + val libraryList = DependencyListForCliModule.build(Name.identifier(moduleName)) { + dependencies(configuration.jvmClasspathRoots.map { it.absolutePath }) + dependencies(configuration.jvmModularRoots.map { it.absolutePath }) + dependencies(resolvedLibraries.map { it.library.libraryFile.absolutePath }) + friendDependencies(configuration[JVMConfigurationKeys.FRIEND_PATHS] ?: emptyList()) + friendDependencies(module.getFriendPaths()) + } + + val librariesScope = projectEnvironment.getSearchScopeForProjectLibraries() + + val sessionsWithSources = prepareJKlibSessions( + projectEnvironment, + ltFiles, + configuration, + rootModuleName, + resolvedLibraries, + libraryList, + extensionRegistrars, + metadataCompilationMode = false, + isCommonSource = groupedSources.isCommonSourceForLt, + fileBelongsToModule = groupedSources.fileBelongsToModuleForLt, + librariesScope = librariesScope, + ) + val outputs = sessionsWithSources.map { (session, files) -> + val firFiles = session.buildFirViaLightTree( + files, + diagnosticsReporter, + performanceManager::addSourcesStats, + ) + resolveAndCheckFir(session, firFiles, diagnosticsReporter) + } + + outputs.runPlatformCheckers(diagnosticsReporter) + + val firFiles = outputs.flatMap { it.fir } + checkKotlinPackageUsageForLightTree(configuration, firFiles) + + val renderDiagnosticName = configuration.getBoolean(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME) + if (diagnosticsReporter.hasErrors) { + diagnosticsReporter.reportToMessageCollector(collector, renderDiagnosticName) + return COMPILATION_ERROR + } + + val firResult = FirResult(outputs) + + val fir2IrExtensions = JvmFir2IrExtensions(configuration, JvmIrDeserializerImpl()) + val irGenerationExtensions = IrGenerationExtension.getInstances(projectEnvironment.project) + val fir2IrResult = firResult.convertToIrAndActualizeForJvm( + fir2IrExtensions, + configuration, + diagnosticsReporter, + irGenerationExtensions, + ) + + val produceHeaderKlib = true + + val serializerOutput = serializeModuleIntoKlib( + moduleName = fir2IrResult.irModuleFragment.name.asString(), + irModuleFragment = fir2IrResult.irModuleFragment, + configuration = configuration, + diagnosticReporter = diagnosticsReporter, + cleanFiles = emptyList(), + dependencies = resolvedLibraries.map { it.library }, + createModuleSerializer = { irDiagnosticReporter: IrDiagnosticReporter -> + JKlibModuleSerializer(IrSerializationSettings(configuration), irDiagnosticReporter) + }, + metadataSerializer = Fir2KlibMetadataSerializer( + configuration, + firResult.outputs, + produceHeaderKlib = produceHeaderKlib, + fir2IrActualizedResult = fir2IrResult, + exportKDoc = false, + ), + ) + + val versions = KotlinLibraryVersioning( + abiVersion = KotlinAbiVersion.CURRENT, + compilerVersion = KotlinCompilerVersion.getVersion(), + metadataVersion = configuration.klibMetadataVersionOrDefault(), + ) + + buildKotlinLibrary( + linkDependencies = serializerOutput.neededLibraries, + ir = serializerOutput.serializedIr, + metadata = serializerOutput.serializedMetadata ?: error("expected serialized metadata"), + versions = versions, + output = destination.absolutePath, + moduleName = configuration[MODULE_NAME]!!, + nopack = false, + manifestProperties = null, + builtInsPlatform = BuiltInsPlatform.COMMON, + nativeTargets = emptyList(), + ) + } catch (e: CompilationException) { + collector.report( + EXCEPTION, + OutputMessageUtil.renderException(e), + MessageUtil.psiElementToMessageLocation(e.element), + ) + return ExitCode.INTERNAL_ERROR + } + + return ExitCode.OK + } + + override fun executableScriptFileName(): String = "kotlinc" + + public override fun createMetadataVersion(versionArray: IntArray): BinaryVersion = BuiltInsBinaryVersion(*versionArray) + + companion object { + @JvmStatic + fun main(args: Array) { + doMain(K2JKlibCompiler(), args) + } + } +} + +private class SourceOrBinaryModuleClassResolver(private val sourceScope: GlobalSearchScope) : ModuleClassResolver { + lateinit var compiledCodeResolver: JavaDescriptorResolver + lateinit var sourceCodeResolver: JavaDescriptorResolver + + override fun resolveClass(javaClass: JavaClass): ClassDescriptor? { + val resolver = if (javaClass is VirtualFileBoundJavaClass && javaClass.isFromSourceCodeInScope(sourceScope)) sourceCodeResolver + else compiledCodeResolver + return resolver.resolveClass(javaClass) + } +} + +/** + * Creates library session and sources session for klib compilation Number of created session + * depends on mode of MPP: + * - disabled + * - legacy (one platform and one common module) + * - HMPP (multiple number of modules) + */ +fun prepareJKlibSessions( + projectEnvironment: VfsBasedProjectEnvironment, + files: List, + configuration: CompilerConfiguration, + rootModuleName: Name, + resolvedLibraries: List, + libraryList: DependencyListForCliModule, + extensionRegistrars: List, + metadataCompilationMode: Boolean, + librariesScope: AbstractProjectFileSearchScope, + isCommonSource: (F) -> Boolean, + fileBelongsToModule: (F, String) -> Boolean, +): List> { + val predefinedJavaComponents = FirSharableJavaComponents(firCachesFactoryForCliMode) + val packagePartProviderForLibraries = projectEnvironment.getPackagePartProvider(librariesScope) + + return SessionConstructionUtils.prepareSessions( + files, + configuration, + rootModuleName, + NativePlatforms.unspecifiedNativePlatform, + metadataCompilationMode, + libraryList, + extensionRegistrars, + isCommonSource, + isScript = { false }, + fileBelongsToModule, + createSharedLibrarySession = { + FirJKlibSessionFactory.createSharedLibrarySession( + rootModuleName, + projectEnvironment, + extensionRegistrars, + packagePartProviderForLibraries, + configuration.languageVersionSettings, + predefinedJavaComponents = predefinedJavaComponents, + ) + }, + createLibrarySession = { sharedLibrarySession -> + FirJKlibSessionFactory.createLibrarySession( + resolvedLibraries.map { it.library }, + sharedLibrarySession, + libraryList.moduleDataProvider, + projectEnvironment, + extensionRegistrars, + librariesScope, + packagePartProviderForLibraries, + configuration.languageVersionSettings, + predefinedJavaComponents = predefinedJavaComponents, + ) + }, + ) { _, moduleData, isForLeafHmppModule, sessionConfigurator -> + FirJKlibSessionFactory.createSourceSession( + moduleData = moduleData, + javaSourcesScope = projectEnvironment.getSearchScopeForProjectJavaSources(), + projectEnvironment = projectEnvironment, + createIncrementalCompilationSymbolProviders = { null }, + extensionRegistrars = extensionRegistrars, + configuration = configuration, + predefinedJavaComponents = predefinedJavaComponents, + needRegisterJavaElementFinder = true, + packagePartProvider = packagePartProviderForLibraries, + init = sessionConfigurator, + isForLeafHmppModule = isForLeafHmppModule, + ) + } +} diff --git a/generators/tests/org/jetbrains/kotlin/generators/arguments/GenerateCompilerArgumentsCopy.kt b/generators/tests/org/jetbrains/kotlin/generators/arguments/GenerateCompilerArgumentsCopy.kt index b3dd0503bedd6..d2fb9f331a855 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/arguments/GenerateCompilerArgumentsCopy.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/arguments/GenerateCompilerArgumentsCopy.kt @@ -26,6 +26,7 @@ private val CLASSES_TO_PROCESS: List> = listOf( K2JSCompilerArguments::class, K2WasmCompilerArguments::class, K2JVMCompilerArguments::class, + K2JKlibCompilerArguments::class, ) private val PACKAGE_TO_DIR_MAPPING: Map = mapOf(