Compare commits

...

6 Commits

Author SHA1 Message Date
Ting-Yuan Huang
5c0e119b13 AnalysisHandlerExtensionTest: set output dir for JVM 2021-03-29 18:55:50 +03:00
Ting-Yuan Huang
b7e168181a Tests for AnalysisHandlerExtension
Add two common use cases of AnalysisHandlerExtension:
1. repeated analysis with new source roots.
2. frontend only mode
2021-03-29 18:54:37 +03:00
Ting-Yuan Huang
abefa59e68 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.
2021-03-29 18:54:37 +03:00
Ting-Yuan Huang
e79c3de57d Add AnalysisHandlerExtension extension point for K2JsCompiler
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.
2021-03-29 18:54:36 +03:00
Ting-Yuan Huang
d10e639e96 Add AnalysisHandlerExtension extension point for K2MetadataCompiler
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.
2021-03-29 18:54:36 +03:00
Ting-Yuan Huang
4d4752cf83 Move AnalysisHandlerExtension out of frontend.java
A dummy AnalysisHandlerExtension that extends the new extension is kept
in frontend.java for backward compatbility.
2021-03-29 18:54:36 +03:00
18 changed files with 367 additions and 84 deletions

View File

@@ -252,17 +252,28 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
return COMPILATION_ERROR;
}
AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(
messageCollector, CommonConfigurationKeysKt.getLanguageVersionSettings(configuration)
);
analyzerWithCompilerReport.analyzeAndReport(sourcesFiles, () -> TopDownAnalyzerFacadeForJS.analyzeFiles(sourcesFiles, config));
if (analyzerWithCompilerReport.hasErrors()) {
return COMPILATION_ERROR;
}
AnalysisResult analysisResult;
do {
AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(
messageCollector, CommonConfigurationKeysKt.getLanguageVersionSettings(configuration)
);
List<KtFile> sources = environmentForJS.getSourceFiles();
analyzerWithCompilerReport.analyzeAndReport(sourcesFiles, () -> TopDownAnalyzerFacadeForJS.analyzeFiles(sources, config));
if (analyzerWithCompilerReport.hasErrors()) {
return COMPILATION_ERROR;
}
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
analysisResult = analyzerWithCompilerReport.getAnalysisResult();
if (analysisResult instanceof JsAnalysisResult.RetryWithAdditionalRoots) {
environmentForJS.addKotlinSourceRoots(((JsAnalysisResult.RetryWithAdditionalRoots) analysisResult).getAdditionalKotlinRoots());
}
} while(analysisResult instanceof JsAnalysisResult.RetryWithAdditionalRoots);
if (!analysisResult.getShouldGenerateCode())
return OK;
AnalysisResult analysisResult = analyzerWithCompilerReport.getAnalysisResult();
assert analysisResult instanceof JsAnalysisResult : "analysisResult should be instance of JsAnalysisResult, but " + analysisResult;
JsAnalysisResult jsAnalysisResult = (JsAnalysisResult) analysisResult;

View File

@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.cli.metadata
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
@@ -33,7 +34,16 @@ internal fun runCommonAnalysisForSerialization(
return null
}
return runCommonAnalysis(environment, dependOnBuiltins, dependencyContainer)
var analyzer: AnalyzerWithCompilerReport
do {
analyzer = runCommonAnalysis(environment, dependOnBuiltins, dependencyContainer)
val result = analyzer.analysisResult
if (result is AnalysisResult.RetryWithAdditionalRoots) {
environment.addKotlinSourceRoots(result.additionalKotlinRoots)
}
} while (result is AnalysisResult.RetryWithAdditionalRoots)
return if (analyzer.analysisResult.shouldGenerateCode) analyzer else null
}
private fun runCommonAnalysis(

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,34 +16,11 @@
package org.jetbrains.kotlin.resolve.jvm.extensions
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTrace
interface AnalysisHandlerExtension {
companion object : ProjectExtensionDescriptor<AnalysisHandlerExtension>(
interface AnalysisHandlerExtension : org.jetbrains.kotlin.resolve.extensions.AnalysisHandlerExtension {
companion object : ProjectExtensionDescriptor<org.jetbrains.kotlin.resolve.extensions.AnalysisHandlerExtension>(
"org.jetbrains.kotlin.analyzeCompleteHandlerExtension",
AnalysisHandlerExtension::class.java
org.jetbrains.kotlin.resolve.extensions.AnalysisHandlerExtension::class.java
)
fun doAnalysis(
project: Project,
module: ModuleDescriptor,
projectContext: ProjectContext,
files: Collection<KtFile>,
bindingTrace: BindingTrace,
componentProvider: ComponentProvider
): AnalysisResult? = null
fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
bindingTrace: BindingTrace,
files: Collection<KtFile>
): AnalysisResult? = null
}

View File

@@ -40,11 +40,13 @@ import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.resolve.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragmentProvider
import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
class CommonAnalysisParameters(
val metadataPartProviderFactory: (ModuleContent<*>) -> MetadataPartProvider
@@ -148,10 +150,11 @@ class CommonResolverForModuleFactory(
dependenciesContainer
)
val projectContext = ProjectContext(project, "metadata serializer")
@Suppress("NAME_SHADOWING")
val resolver = ResolverForSingleModuleProject<ModuleInfo>(
"sources for metadata serializer",
ProjectContext(project, "metadata serializer"),
projectContext,
moduleInfo,
resolverForModuleFactory,
GlobalSearchScope.allScope(project),
@@ -167,9 +170,24 @@ class CommonResolverForModuleFactory(
val container = resolver.resolverForModule(moduleInfo).componentProvider
container.get<LazyTopDownAnalyzer>().analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
val analysisHandlerExtensions = AnalysisHandlerExtension.getInstances(project)
val trace = container.get<BindingTrace>()
return AnalysisResult.success(container.get<BindingTrace>().bindingContext, moduleDescriptor)
// 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, 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 {
container.get<LazyTopDownAnalyzer>().analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
AnalysisResult.success(trace.bindingContext, moduleDescriptor)
}
result = analysisHandlerExtensions.firstNotNullResult { extension ->
extension.analysisCompleted(project, moduleDescriptor, trace, files)
} ?: result
return result
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2010-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.extensions
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTrace
interface AnalysisHandlerExtension {
companion object : ProjectExtensionDescriptor<AnalysisHandlerExtension>(
"org.jetbrains.kotlin.analyzeCompleteHandlerExtension",
AnalysisHandlerExtension::class.java
)
fun doAnalysis(
project: Project,
module: ModuleDescriptor,
projectContext: ProjectContext,
files: Collection<KtFile>,
bindingTrace: BindingTrace,
componentProvider: ComponentProvider
): AnalysisResult? = null
fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
bindingTrace: BindingTrace,
files: Collection<KtFile>
): AnalysisResult? = null
}

View File

@@ -60,7 +60,7 @@ abstract class AbstractDiagnosticsTestWithJsStdLib : AbstractDiagnosticsTest() {
moduleTrace.record<ModuleDescriptor, ModuleKind>(MODULE_KIND, moduleContext.module, getModuleKind(files))
config.configuration.languageVersionSettings = languageVersionSettings
return TopDownAnalyzerFacadeForJS.analyzeFilesWithGivenTrace(
files, moduleTrace, moduleContext, config.configuration, CompilerEnvironment,
files, moduleTrace, moduleContext, config.configuration, CompilerEnvironment, config.project
)
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright 2010-2018 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
import com.intellij.mock.MockProject
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.common.CLITool
import org.jetbrains.kotlin.cli.js.K2JSCompiler
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.test.CompilerTestUtil
import org.jetbrains.kotlin.test.TestCaseWithTmpdir
import java.io.File
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
private data class TestKtFile(
val name: String,
val content: String
)
private val classNotFound = TestKtFile("C.kt", "class C : ClassNotFound")
private val repeatedAnalysis = TestKtFile("D.kt", "class D : Generated")
class AnalysisHandlerExtensionTest : TestCaseWithTmpdir() {
// Writes the service file only; CustomComponentRegistrar is already in classpath.
private fun writePlugin(): String {
val jarFile = tmpdir.resolve("plugin.jar")
ZipOutputStream(jarFile.outputStream()).use {
val entry = ZipEntry("META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar")
it.putNextEntry(entry)
it.write(CustomComponentRegistrar::class.java.name.toByteArray())
}
return jarFile.absolutePath
}
private fun runTest(compiler: CLITool<*>, src: TestKtFile, extras: List<String> = emptyList()) {
val mainKt = tmpdir.resolve(src.name).apply {
writeText(src.content)
}
val plugin = writePlugin()
val args = listOf("-Xplugin=$plugin", mainKt.absolutePath)
CompilerTestUtil.executeCompilerAssertSuccessful(compiler, args + extras)
}
fun testShouldNotGenerateCodeJVM() {
runTest(K2JVMCompiler(), classNotFound, listOf("-d", tmpdir.resolve("out").absolutePath))
}
fun testShouldNotGenerateCodeJS() {
runTest(K2JSCompiler(), classNotFound, listOf("-output", tmpdir.resolve("out.js").absolutePath))
}
fun testShouldNotGenerateCodeMetadata() {
runTest(K2MetadataCompiler(), classNotFound, listOf("-d", tmpdir.resolve("out").absolutePath))
}
fun testRepeatedAnalysisJVM() {
runTest(K2JVMCompiler(), repeatedAnalysis, listOf("-d", tmpdir.resolve("out").absolutePath))
}
fun testRepeatedAnalysisJS() {
runTest(K2JSCompiler(), repeatedAnalysis, listOf("-output", tmpdir.resolve("out.js").absolutePath))
}
fun testRepeatedAnalysisMetadata() {
runTest(K2MetadataCompiler(), repeatedAnalysis, listOf("-d", tmpdir.resolve("out").absolutePath))
}
}
class CustomComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
AnalysisHandlerExtension.registerExtension(project, CustomAnalysisHandler())
}
}
// Assume that the directory of input files is writeable!
private class CustomAnalysisHandler : AnalysisHandlerExtension {
override fun doAnalysis(
project: Project,
module: ModuleDescriptor,
projectContext: ProjectContext,
files: Collection<KtFile>,
bindingTrace: BindingTrace,
componentProvider: ComponentProvider
): AnalysisResult? {
val filenames = files.map { it.name }
if (repeatedAnalysis.name in filenames) {
if ("Generated.kt" in filenames) {
return null
} else {
val outDir = File(files.single { it.name == repeatedAnalysis.name }.virtualFilePath).parentFile
outDir.resolve("Generated.kt").apply {
writeText("interface Generated")
}
return AnalysisResult.RetryWithAdditionalRoots(BindingContext.EMPTY, module, emptyList(), listOf(outDir))
}
}
return AnalysisResult.success(BindingContext.EMPTY, module, false)
}
}

View File

@@ -48,7 +48,7 @@ class JsVersionRequirementTest : AbstractVersionRequirementTest() {
}
val trace = BindingTraceContext()
val analysisResult = TopDownAnalyzerFacadeForJS.analyzeFilesWithGivenTrace(
ktFiles, trace, createModule(environment), environment.configuration, CompilerEnvironment,
ktFiles, trace, createModule(environment), environment.configuration, CompilerEnvironment, environment.project
)
// There are INVISIBLE_REFERENCE errors on RequireKotlin and K2JSTranslator refuses to translate the code otherwise
@@ -63,7 +63,7 @@ class JsVersionRequirementTest : AbstractVersionRequirementTest() {
override fun loadModule(directory: File): ModuleDescriptor {
val environment = createEnvironment(extraDependencies = listOf(File(directory, "lib.meta.js")))
return TopDownAnalyzerFacadeForJS.analyzeFilesWithGivenTrace(
emptyList(), BindingTraceContext(), createModule(environment), environment.configuration, CompilerEnvironment,
emptyList(), BindingTraceContext(), createModule(environment), environment.configuration, CompilerEnvironment, environment.project
).moduleDescriptor
}

View File

@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.frontend.js.di
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.context.ModuleContext
@@ -37,7 +38,7 @@ import org.jetbrains.kotlin.resolve.createContainer
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
fun createTopDownAnalyzerForJs(
fun createContainerForJS(
moduleContext: ModuleContext,
bindingTrace: BindingTrace,
declarationProviderFactory: DeclarationProviderFactory,
@@ -46,7 +47,7 @@ fun createTopDownAnalyzerForJs(
expectActualTracker: ExpectActualTracker,
additionalPackages: List<PackageFragmentProvider>,
targetEnvironment: TargetEnvironment,
): LazyTopDownAnalyzer {
): StorageComponentContainer {
val storageComponentContainer = createContainer("TopDownAnalyzerForJs", JsPlatformAnalyzerServices) {
configureModule(
moduleContext,
@@ -67,5 +68,21 @@ fun createTopDownAnalyzerForJs(
packagePartProviders += additionalPackages
moduleDescriptor.initialize(CompositePackageFragmentProvider(packagePartProviders))
}
return storageComponentContainer.get<LazyTopDownAnalyzer>()
return storageComponentContainer
}
fun createTopDownAnalyzerForJs(
moduleContext: ModuleContext,
bindingTrace: BindingTrace,
declarationProviderFactory: DeclarationProviderFactory,
languageVersionSettings: LanguageVersionSettings,
lookupTracker: LookupTracker,
expectActualTracker: ExpectActualTracker,
additionalPackages: List<PackageFragmentProvider>,
targetEnvironment: TargetEnvironment,
): LazyTopDownAnalyzer {
return createContainerForJS(
moduleContext, bindingTrace, declarationProviderFactory, languageVersionSettings,
lookupTracker, expectActualTracker, additionalPackages, targetEnvironment
).get<LazyTopDownAnalyzer>()
}

View File

@@ -6,18 +6,20 @@
package org.jetbrains.kotlin.js.analyze
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.functions.functionInterfacePackageFragmentProvider
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.ContextForNewModule
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.frontend.js.di.createTopDownAnalyzerForJs
import org.jetbrains.kotlin.frontend.js.di.createContainerForJS
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
@@ -31,12 +33,13 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.js.JsPlatforms
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.js.KotlinJavascriptSerializationUtil
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.serialization.js.PackagesWithHeaderMetadata
import org.jetbrains.kotlin.utils.JsMetadataVersion
import java.lang.Exception
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
abstract class AbstractTopDownAnalyzerFacadeForJS {
@@ -82,7 +85,7 @@ abstract class AbstractTopDownAnalyzerFacadeForJS {
val trace = BindingTraceContext()
trace.record(MODULE_KIND, context.module, moduleKind)
return analyzeFilesWithGivenTrace(files, trace, context, configuration, targetEnvironment, additionalPackages)
return analyzeFilesWithGivenTrace(files, trace, context, configuration, targetEnvironment, project, additionalPackages)
}
protected abstract fun loadIncrementalCacheMetadata(
@@ -98,6 +101,7 @@ abstract class AbstractTopDownAnalyzerFacadeForJS {
moduleContext: ModuleContext,
configuration: CompilerConfiguration,
targetEnvironment: TargetEnvironment,
project: Project,
additionalPackages: List<PackageFragmentProvider> = emptyList()
): JsAnalysisResult {
val lookupTracker = configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER) ?: LookupTracker.DO_NOTHING
@@ -106,7 +110,8 @@ abstract class AbstractTopDownAnalyzerFacadeForJS {
val packageFragment = configuration[JSConfigurationKeys.INCREMENTAL_DATA_PROVIDER]?.let {
loadIncrementalCacheMetadata(it, moduleContext, lookupTracker, languageVersionSettings)
}
val analyzerForJs = createTopDownAnalyzerForJs(
val container = createContainerForJS(
moduleContext, trace,
FileBasedDeclarationProviderFactory(moduleContext.storageManager, files),
languageVersionSettings,
@@ -115,8 +120,38 @@ abstract class AbstractTopDownAnalyzerFacadeForJS {
additionalPackages + listOfNotNull(packageFragment),
targetEnvironment,
)
analyzerForJs.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
return JsAnalysisResult.success(trace, 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, it. Otherwise do the normal analysis.
// * `analysisCompleted` can be used to override the result, too.
var result = analysisHandlerExtensions.firstNotNullResult { extension ->
extension.doAnalysis(project, moduleContext.module, moduleContext, files, trace, container)
} ?: run {
container.get<LazyTopDownAnalyzer>().analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
AnalysisResult.success(trace.bindingContext, moduleContext.module)
}
result = analysisHandlerExtensions.firstNotNullResult { extension ->
extension.analysisCompleted(project, moduleContext.module, trace, files)
} ?: result
return when (result) {
is JsAnalysisResult -> result
else -> {
// AnalysisHandlerExtension returns a BindingContext, not BindingTrace. Therefore, synthesize one here.
val bindingTrace = DelegatingBindingTrace(result.bindingContext, "DelegatingBindingTrace by AnalysisHandlerExtension")
when (result) {
is AnalysisResult.RetryWithAdditionalRoots -> JsAnalysisResult.RetryWithAdditionalRoots(
bindingTrace,
result.moduleDescriptor,
result.additionalKotlinRoots
)
else -> JsAnalysisResult.success(bindingTrace, result.moduleDescriptor, result.shouldGenerateCode)
}
}
}
}
fun checkForErrors(allFiles: Collection<KtFile>, bindingContext: BindingContext, errorPolicy: ErrorTolerancePolicy): Boolean {

View File

@@ -21,15 +21,27 @@ import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.ErrorUtils
import java.io.File
class JsAnalysisResult(
open class JsAnalysisResult(
val bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor
) : AnalysisResult(bindingTrace.bindingContext, moduleDescriptor) {
moduleDescriptor: ModuleDescriptor,
shouldGenerateCode: Boolean
) : AnalysisResult(bindingTrace.bindingContext, moduleDescriptor, shouldGenerateCode) {
companion object {
@JvmStatic fun success(trace: BindingTrace, module: ModuleDescriptor): JsAnalysisResult {
return JsAnalysisResult(trace, module)
return JsAnalysisResult(trace, module, true)
}
@JvmStatic fun success(trace: BindingTrace, module: ModuleDescriptor, shouldGenerateCode: Boolean): JsAnalysisResult {
return JsAnalysisResult(trace, module, shouldGenerateCode)
}
}
class RetryWithAdditionalRoots(
bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor,
val additionalKotlinRoots: List<File>,
) : JsAnalysisResult(bindingTrace, moduleDescriptor,false)
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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