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.
This commit is contained in:
Ting-Yuan Huang
2021-03-19 02:07:37 -07:00
committed by TeamCityServer
parent 8e7b561b10
commit dba127a4d8
7 changed files with 67 additions and 28 deletions
@@ -180,6 +180,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
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)
@@ -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
@@ -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
@@ -106,6 +106,8 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create("print ir with descriptors")
val PRINT_LOCATIONS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("print locations")
val PRINT_FILES: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("print files")
val PRODUCE: CompilerConfigurationKey<CompilerOutputKind>
= CompilerConfigurationKey.create("compiler output kind")
val PURGE_USER_LIBS: CompilerConfigurationKey<Boolean>
@@ -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<CompilerPhase<Context, Unit, Unit>>().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
}
@@ -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<PackageFragmentProvider> = 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<LazyTopDownAnalyzer>()
}
analyzerForKonan.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
return AnalysisResult.success(trace.bindingContext, moduleContext.module)
val analyzerForKonan = container.get<LazyTopDownAnalyzer>()
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<KtFile>, bindingContext: BindingContext) {
@@ -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