Kapt: add flag kapt.use.jvm.ir for enabling JVM IR support

#KT-49682
This commit is contained in:
Alexander Udalov
2022-05-31 14:39:44 +02:00
parent 65c7a7f939
commit 7e9d7c895a
13 changed files with 133 additions and 44 deletions
@@ -85,11 +85,6 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<Boolean> IR =
CompilerConfigurationKey.create("IR");
// Temporary option to make it possible to test kapt with JVM IR. As soon as all tests start to pass, it can be removed,
// and the backend (old or IR) will be deduced from the compiler arguments provided by the user.
public static final CompilerConfigurationKey<Boolean> USE_KAPT_WITH_JVM_IR =
CompilerConfigurationKey.create("Enable JVM IR for kapt");
public static final CompilerConfigurationKey<Boolean> USE_PSI_CLASS_FILES_READING =
CompilerConfigurationKey.create("use a slower (PSI-based) class files reading implementation");
@@ -203,10 +203,7 @@ open class JvmIrCodegenFactory(
// We need to compile all files we reference in Klibs
irModuleFragment.files.addAll(dependencies.flatMap { it.files })
if (!input.configuration.getBoolean(JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT) && !input.configuration.getBoolean(
JVMConfigurationKeys.USE_KAPT_WITH_JVM_IR
)
) {
if (!input.configuration.getBoolean(JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT)) {
val originalBindingContext = input.bindingContext as? CleanableBindingContext
?: error("BindingContext should be cleanable in JVM IR to avoid leaking memory: ${input.bindingContext}")
originalBindingContext.clear()
@@ -187,8 +187,7 @@ object GenerationUtils {
configureGenerationState: GenerationState.Builder.() -> Unit = {},
): GenerationState {
val isIrBackend =
(classBuilderFactory.classBuilderMode == ClassBuilderMode.FULL && configuration.getBoolean(JVMConfigurationKeys.IR)) ||
configuration.getBoolean(JVMConfigurationKeys.USE_KAPT_WITH_JVM_IR)
configuration.getBoolean(JVMConfigurationKeys.IR)
val generationState = GenerationState.Builder(
project, classBuilderFactory, analysisResult.moduleDescriptor, analysisResult.bindingContext,
configuration
@@ -24,6 +24,7 @@ import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.isInfoAsWarnings
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.isKaptKeepKdocCommentsInStubs
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.isKaptVerbose
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.isUseJvmIr
import org.jetbrains.kotlin.gradle.model.builder.KaptModelBuilder
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation
@@ -119,6 +120,10 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
return getBooleanOptionValue(BooleanOption.KAPT_KEEP_KDOC_COMMENTS_IN_STUBS)
}
fun Project.isUseJvmIr(): Boolean {
return getBooleanOptionValue(BooleanOption.KAPT_USE_JVM_IR)
}
fun Project.classLoadersCacheSize(): Int = findPropertySafe(CLASSLOADERS_CACHE_SIZE)?.toString()?.toInt() ?: 0
fun Project.disableClassloaderCacheForProcessors(): Set<String> {
@@ -219,7 +224,8 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
),
KAPT_INFO_AS_WARNINGS("kapt.info.as.warnings", false),
KAPT_INCLUDE_COMPILE_CLASSPATH("kapt.include.compile.classpath", true),
KAPT_KEEP_KDOC_COMMENTS_IN_STUBS("kapt.keep.kdoc.comments.in.stubs", true)
KAPT_KEEP_KDOC_COMMENTS_IN_STUBS("kapt.keep.kdoc.comments.in.stubs", true),
KAPT_USE_JVM_IR("kapt.use.jvm.ir", false),
}
}
@@ -573,6 +579,7 @@ internal fun buildKaptSubpluginOptions(
pluginOptions += SubpluginOption("keepKdocCommentsInStubs", "${project.isKaptKeepKdocCommentsInStubs()}")
pluginOptions += SubpluginOption("showProcessorTimings", "${kaptExtension.showProcessorStats}")
pluginOptions += SubpluginOption("detectMemoryLeaks", kaptExtension.detectMemoryLeaks)
pluginOptions += SubpluginOption("useJvmIr", "${project.isUseJvmIr()}")
pluginOptions += SubpluginOption("infoAsWarnings", "${project.isInfoAsWarnings()}")
pluginOptions += FilesSubpluginOption("stubs", kaptStubsDir)
@@ -126,6 +126,7 @@ enum class KaptFlag(val description: String, val defaultValue: Boolean = false)
INCREMENTAL_APT("Incremental annotation processing (apt mode)"),
STRIP_METADATA("Strip @Metadata annotations from stubs"),
KEEP_KDOC_COMMENTS_IN_STUBS("Keep KDoc comments in stubs", defaultValue = true),
USE_JVM_IR("Use JVM IR backend")
;
}
@@ -207,6 +207,13 @@ enum class KaptCliOption(
"Keep KDoc comments in stubs"
),
USE_JVM_IR(
"useJvmIr",
"true | false",
"Use JVM IR backend",
cliToolOption = CliToolOption("-Kapt-use-jvm-ir", FLAG)
),
DETECT_MEMORY_LEAKS_OPTION("detectMemoryLeaks", "true | false", "Detect memory leaks in annotation processors"),
INCLUDE_COMPILE_CLASSPATH(
"includeCompileClasspath",
@@ -262,19 +262,18 @@ abstract class AbstractKapt3Extension(
): KaptContextForStubGeneration {
val builderFactory = OriginCollectingClassBuilderFactory(ClassBuilderMode.KAPT3)
val configuration = compilerConfiguration.copy().apply {
put(JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT, true)
}
val targetId = TargetId(
name = compilerConfiguration[CommonConfigurationKeys.MODULE_NAME] ?: module.name.asString(),
name = configuration[CommonConfigurationKeys.MODULE_NAME] ?: module.name.asString(),
type = "java-production"
)
val isIrBackend = compilerConfiguration.getBoolean(JVMConfigurationKeys.USE_KAPT_WITH_JVM_IR)
val generationState = GenerationState.Builder(
project,
builderFactory,
module,
bindingContext,
compilerConfiguration
).targetId(targetId)
val isIrBackend = options.flags[KaptFlag.USE_JVM_IR]
val generationState = GenerationState.Builder(project, builderFactory, module, bindingContext, configuration)
.targetId(targetId)
.isIrBackend(isIrBackend)
.build()
@@ -283,7 +282,7 @@ abstract class AbstractKapt3Extension(
files,
generationState,
if (isIrBackend)
JvmIrCodegenFactory(compilerConfiguration, compilerConfiguration.get(CLIConfigurationKeys.PHASE_CONFIG))
JvmIrCodegenFactory(configuration, configuration[CLIConfigurationKeys.PHASE_CONFIG])
else DefaultCodegenFactory
)
}
@@ -127,6 +127,8 @@ class Kapt3CommandLineProcessor : CommandLineProcessor {
STRICT_MODE_OPTION -> setFlag(KaptFlag.STRICT, value)
STRIP_METADATA_OPTION -> setFlag(KaptFlag.STRIP_METADATA, value)
KEEP_KDOC_COMMENTS_IN_STUBS -> setFlag(KaptFlag.KEEP_KDOC_COMMENTS_IN_STUBS, value)
USE_JVM_IR -> setFlag(KaptFlag.USE_JVM_IR, value)
SHOW_PROCESSOR_STATS -> setFlag(KaptFlag.SHOW_PROCESSOR_STATS, value)
DUMP_PROCESSOR_STATS -> processorsStatsReportFile = File(value)
INCLUDE_COMPILE_CLASSPATH -> setFlag(KaptFlag.INCLUDE_COMPILE_CLASSPATH, value)
@@ -176,7 +176,7 @@ abstract class AbstractKotlinKapt3IntegrationTest : KotlinKapt3TestBase() {
.trimTrailingWhitespacesAndAddNewlineAtEOF()
.let { AbstractClassFileToSourceStubConverterTest.removeMetadataAnnotationContents(it) }
KotlinTestUtils.assertEqualsToFile(txtFile, actual)
checkTxtAccordingToBackend(txtFile, actual)
} finally {
options.sourcesOutputDir.deleteRecursively()
options.incrementalDataOutputDir?.deleteRecursively()
@@ -37,8 +37,6 @@ import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.CodegenTestFiles
import org.jetbrains.kotlin.codegen.GenerationUtils
import org.jetbrains.kotlin.codegen.OriginCollectingClassBuilderFactory
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.kapt.base.test.JavaKaptContextTest
import org.jetbrains.kotlin.kapt3.Kapt3ComponentRegistrar.KaptComponentContributor
@@ -170,14 +168,6 @@ abstract class AbstractKotlinKapt3Test : KotlinKapt3TestBase() {
}
}
override fun updateConfiguration(configuration: CompilerConfiguration) {
super.updateConfiguration(configuration)
if (backend.isIR) {
configuration.put(JVMConfigurationKeys.USE_KAPT_WITH_JVM_IR, true)
}
}
protected fun convert(
kaptContext: KaptContextForStubGeneration,
javaFiles: List<File>,
@@ -333,15 +323,7 @@ open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test(
}
}
val irTxtFile = File(txtFile.parentFile, txtFile.nameWithoutExtension + "_ir.txt")
val expectedFile =
if (backend.isIR && irTxtFile.exists()) irTxtFile
else txtFile
KotlinTestUtils.assertEqualsToFile(expectedFile, actual)
if (backend.isIR && txtFile.exists() && irTxtFile.exists() && txtFile.readText() == irTxtFile.readText()) {
fail("JVM and JVM_IR golden files are identical. Remove $irTxtFile.")
}
checkTxtAccordingToBackend(txtFile, actual)
}
}
@@ -10,7 +10,6 @@ import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.TypeElement
// For now, just run one simple test to make sure Kapt launches at all with IR switched on.
class IrKotlinKapt3IntegrationTests : AbstractIrKotlinKapt3IntegrationTest(), CustomJdkTestLauncher {
override fun test(
name: String,
@@ -7,6 +7,9 @@ package org.jetbrains.kotlin.kapt3.test
import org.jetbrains.kotlin.base.kapt3.KaptFlag
import org.jetbrains.kotlin.codegen.CodegenTestCase
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.util.KtTestUtil
import java.io.File
@@ -48,6 +51,27 @@ abstract class KotlinKapt3TestBase : CodegenTestCase() {
return KtTestUtil.tmpDir(name).also(directoriesToCleanup::add)
}
protected fun checkTxtAccordingToBackend(txtFile: File, actual: String) {
val irTxtFile = File(txtFile.parentFile, txtFile.nameWithoutExtension + "_ir.txt")
val expectedFile =
if (backend.isIR && irTxtFile.exists()) irTxtFile
else txtFile
KotlinTestUtils.assertEqualsToFile(expectedFile, actual)
if (backend.isIR && txtFile.exists() && irTxtFile.exists() && txtFile.readText() == irTxtFile.readText()) {
fail("JVM and JVM_IR golden files are identical. Remove $irTxtFile.")
}
}
override fun updateConfiguration(configuration: CompilerConfiguration) {
super.updateConfiguration(configuration)
if (backend.isIR) {
configuration.put(JVMConfigurationKeys.IR, true)
configuration.put(JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT, true)
}
}
override fun doTest(filePath: String) {
val testFile = File(filePath)
@@ -57,6 +81,10 @@ abstract class KotlinKapt3TestBase : CodegenTestCase() {
addOrRemoveFlag(KaptFlag.STRICT, testFile)
addOrRemoveFlag(KaptFlag.DUMP_DEFAULT_PARAMETER_VALUES, testFile)
if (backend.isIR) {
kaptFlagsToAdd.add(KaptFlag.USE_JVM_IR)
}
super.doTest(filePath)
}
}
}
@@ -0,0 +1,73 @@
package error;
public final class NonExistentClass {
}
////////////////////
package test;
import java.lang.System;
/**
* KDoc comment.
*/
@kotlin.Suppress(names = {"UNRESOLVED_REFERENCE"})
@kotlin.Metadata()
public final class Simple {
public Simple() {
super();
}
@MyAnnotation()
public final void myMethod() {
}
public final int heavyMethod() {
return 0;
}
}
////////////////////
package test;
import java.lang.System;
@kotlin.Metadata()
public enum EnumClass {
/*public static final*/ BLACK /* = new BLACK() */,
/*public static final*/ WHITE /* = new WHITE() */;
EnumClass() {
}
}
////////////////////
package test;
import java.lang.System;
@kotlin.Metadata()
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
public abstract @interface MyAnnotation {
}
////////////////////
package test;
import java.lang.System;
@kotlin.Metadata()
public enum EnumClass2 {
/*public static final*/ WHITE /* = new WHITE(null) */,
/*public static final*/ RED /* = new RED(null) */;
@org.jetbrains.annotations.NotNull()
private final java.lang.String blah = null;
EnumClass2(java.lang.String blah) {
}
}