From dba127a4d8caa82a37f9c1a36d0f97427c140baf Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Fri, 19 Mar 2021 02:07:37 -0700 Subject: [PATCH] Add AnalysisHandlerExtension extension point for K2Native frontendPhase is moved out of back phases to allow frontend only mode. AnalysisHandlerExtension allows compiler plugins to: 1. Intercept and override the default analysis. 2. Utilize the compiler infrastructure to do custom analysis. A well know plugin on the JVM platform is KAPT. --- .../org/jetbrains/kotlin/cli/bc/K2Native.kt | 1 + .../cli/bc/K2NativeCompilerArguments.kt | 3 ++ .../jetbrains/kotlin/backend/konan/Context.kt | 2 ++ .../backend/konan/KonanConfigurationKeys.kt | 2 ++ .../kotlin/backend/konan/KonanDriver.kt | 31 +++++++++++++++++ .../konan/TopDownAnalyzerFacadeForKonan.kt | 33 +++++++++++++++---- .../kotlin/backend/konan/ToplevelPhases.kt | 23 +------------ 7 files changed, 67 insertions(+), 28 deletions(-) diff --git a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 18077773de8..62876143f47 100644 --- a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -180,6 +180,7 @@ class K2Native : CLICompiler() { put(PRINT_DESCRIPTORS, arguments.printDescriptors) put(PRINT_LOCATIONS, arguments.printLocations) put(PRINT_BITCODE, arguments.printBitCode) + put(PRINT_FILES, arguments.printFiles) put(PURGE_USER_LIBS, arguments.purgeUserLibs) diff --git a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt index ddbf57d4422..85d0315bcf0 100644 --- a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt +++ b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt @@ -199,6 +199,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { @Argument(value = "-Xprint-locations", deprecatedName = "--print_locations", description = "Print locations") var printLocations: Boolean = false + @Argument(value = "-Xprint-files", description = "Print files") + var printFiles: Boolean = false + @Argument(value="-Xpurge-user-libs", deprecatedName = "--purge_user_libs", description = "Don't link unused libraries even explicitly specified") var purgeUserLibs: Boolean = false diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 383a943f5e7..ce778417859 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -410,6 +410,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { fun shouldPrintLocations() = config.configuration.getBoolean(KonanConfigKeys.PRINT_LOCATIONS) + fun shouldPrintFiles() = config.configuration.getBoolean(KonanConfigKeys.PRINT_FILES) + fun shouldProfilePhases() = config.phaseConfig.needProfiling fun shouldContainDebugInfo() = config.debug diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index d13186fc95a..78800279d8a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -106,6 +106,8 @@ class KonanConfigKeys { = CompilerConfigurationKey.create("print ir with descriptors") val PRINT_LOCATIONS: CompilerConfigurationKey = CompilerConfigurationKey.create("print locations") + val PRINT_FILES: CompilerConfigurationKey + = CompilerConfigurationKey.create("print files") val PRODUCE: CompilerConfigurationKey = CompilerConfigurationKey.create("compiler output kind") val PURGE_USER_LIBS: CompilerConfigurationKey diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt index b37374d5b8d..bcd513dad64 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt @@ -5,9 +5,12 @@ package org.jetbrains.kotlin.backend.konan +import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.backend.common.phaser.CompilerPhase import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel +import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.utils.addToStdlib.cast fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEnvironment) { @@ -25,6 +28,8 @@ fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEnvironme if (konanConfig.infoArgsOnly) return + if (!context.frontendPhase()) return + try { toplevelPhase.cast>().invokeToplevel(context.phaseConfig, context, Unit) } finally { @@ -36,3 +41,29 @@ fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEnvironme } } +// returns true if should generate code. +internal fun Context.frontendPhase(): Boolean { + lateinit var analysisResult: AnalysisResult + + do { + val analyzerWithCompilerReport = AnalyzerWithCompilerReport(messageCollector, + environment.configuration.languageVersionSettings) + + // Build AST and binding info. + analyzerWithCompilerReport.analyzeAndReport(environment.getSourceFiles()) { + TopDownAnalyzerFacadeForKonan.analyzeFiles(environment.getSourceFiles(), this) + } + if (analyzerWithCompilerReport.hasErrors()) { + throw KonanCompilationException() + } + analysisResult = analyzerWithCompilerReport.analysisResult + if (analysisResult is AnalysisResult.RetryWithAdditionalRoots) { + environment.addKotlinSourceRoots(analysisResult.additionalKotlinRoots) + } + } while(analysisResult is AnalysisResult.RetryWithAdditionalRoots) + + moduleDescriptor = analysisResult.moduleDescriptor + bindingContext = analysisResult.bindingContext + + return analysisResult.shouldGenerateCode +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt index 46027d9fbde..a33c7c84fbf 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt @@ -28,9 +28,11 @@ import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.extensions.AnalysisHandlerExtension import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory import org.jetbrains.kotlin.serialization.konan.KotlinResolvedModuleDescriptors import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult internal object TopDownAnalyzerFacadeForKonan { @@ -64,7 +66,7 @@ internal object TopDownAnalyzerFacadeForKonan { additionalPackages += functionInterfacePackageFragmentProvider(projectContext.storageManager, module) } - return analyzeFilesWithGivenTrace(files, BindingTraceContext(), moduleContext, context, additionalPackages) + return analyzeFilesWithGivenTrace(files, BindingTraceContext(), moduleContext, context, projectContext, additionalPackages) } fun analyzeFilesWithGivenTrace( @@ -72,15 +74,16 @@ internal object TopDownAnalyzerFacadeForKonan { trace: BindingTrace, moduleContext: ModuleContext, context: Context, + projectContext: ProjectContext, additionalPackages: List = emptyList() ): AnalysisResult { // we print out each file we compile if frontend phase is verbose files.takeIf { - frontendPhase in context.phaseConfig.verbose + context.shouldPrintFiles() } ?.forEach(::println) - val analyzerForKonan = createTopDownAnalyzerProviderForKonan( + val container = createTopDownAnalyzerProviderForKonan( moduleContext, trace, FileBasedDeclarationProviderFactory(moduleContext.storageManager, files), context.config.configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS)!!, @@ -89,10 +92,28 @@ internal object TopDownAnalyzerFacadeForKonan { initContainer(context.config) }.apply { postprocessComponents(context, files) - }.get() + } - analyzerForKonan.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files) - return AnalysisResult.success(trace.bindingContext, moduleContext.module) + val analyzerForKonan = container.get() + val project = context.config.project + val moduleDescriptor = moduleContext.module + val analysisHandlerExtensions = AnalysisHandlerExtension.getInstances(project) + + // Mimic the behavior in the jvm frontend. The extensions have 2 chances to override the normal analysis: + // * If any of the extensions returns a non-null result, use it. Otherwise do the normal analysis. + // * `analysisCompleted` can be used to override the result, too. + var result = analysisHandlerExtensions.firstNotNullResult { extension -> + extension.doAnalysis(project, moduleDescriptor, projectContext, files, trace, container) + } ?: run { + analyzerForKonan.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files) + AnalysisResult.success(trace.bindingContext, moduleDescriptor) + } + + result = analysisHandlerExtensions.firstNotNullResult { extension -> + extension.analysisCompleted(project, moduleDescriptor, trace, files) + } ?: result + + return result } fun checkForErrors(files: Collection, bindingContext: BindingContext) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index bc29ab00f34..9f00fba0f2a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -75,26 +75,6 @@ internal fun konanUnitPhase( op: Context.() -> Unit ) = namedOpUnitPhase(name, description, prerequisite, op) -internal val frontendPhase = konanUnitPhase( - op = { - val environment = environment - val analyzerWithCompilerReport = AnalyzerWithCompilerReport(messageCollector, - environment.configuration.languageVersionSettings) - - // Build AST and binding info. - analyzerWithCompilerReport.analyzeAndReport(environment.getSourceFiles()) { - TopDownAnalyzerFacadeForKonan.analyzeFiles(environment.getSourceFiles(), this) - } - if (analyzerWithCompilerReport.hasErrors()) { - throw KonanCompilationException() - } - moduleDescriptor = analyzerWithCompilerReport.analysisResult.moduleDescriptor - bindingContext = analyzerWithCompilerReport.analysisResult.bindingContext - }, - name = "Frontend", - description = "Frontend builds AST" -) - /** * Valid from [createSymbolTablePhase] until [destroySymbolTablePhase]. */ @@ -425,8 +405,7 @@ private val backendCodegen = namedUnitPhase( val toplevelPhase: CompilerPhase<*, Unit, Unit> = namedUnitPhase( name = "Compiler", description = "The whole compilation process", - lower = frontendPhase then - createSymbolTablePhase then + lower = createSymbolTablePhase then objCExportPhase then buildCExportsPhase then psiToIrPhase then