diff --git a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiCommandLineProcessor.kt b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiCommandLineProcessor.kt index c8f65a5b20f..431f2f1bffd 100644 --- a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiCommandLineProcessor.kt +++ b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiCommandLineProcessor.kt @@ -22,17 +22,28 @@ class JvmAbiCommandLineProcessor : CommandLineProcessor { "Output path for generated files. This can be either a directory or a jar file.", true ) + + val REMOVE_DEBUG_INFO_OPTION: CliOption = + CliOption( + "removeDebugInfo", + "true/false", + "Remove debug info from the generated class files. False by default. Note that if ABI jars are used for incremental " + + "compilation, it's not safe to remove debug info because debugger will behave incorrectly on non-recompiled call " + + "sites of inline functions.", + false, + ) } override val pluginId: String get() = COMPILER_PLUGIN_ID override val pluginOptions: Collection - get() = listOf(OUTPUT_PATH_OPTION) + get() = listOf(OUTPUT_PATH_OPTION, REMOVE_DEBUG_INFO_OPTION) override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) { when (option) { OUTPUT_PATH_OPTION -> configuration.put(JvmAbiConfigurationKeys.OUTPUT_PATH, value) + REMOVE_DEBUG_INFO_OPTION -> configuration.put(JvmAbiConfigurationKeys.REMOVE_DEBUG_INFO, value == "true") else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}") } } diff --git a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiComponentRegistrar.kt b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiComponentRegistrar.kt index 3d5d709c1c2..706a572c8c3 100644 --- a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiComponentRegistrar.kt +++ b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiComponentRegistrar.kt @@ -20,7 +20,10 @@ class JvmAbiComponentRegistrar : CompilerPluginRegistrar() { val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) val builderExtension = JvmAbiClassBuilderInterceptor() - val outputExtension = JvmAbiOutputExtension(File(outputPath), builderExtension.abiClassInfo, messageCollector) + val outputExtension = JvmAbiOutputExtension( + File(outputPath), builderExtension.abiClassInfo, messageCollector, + configuration.getBoolean(JvmAbiConfigurationKeys.REMOVE_DEBUG_INFO), + ) ClassGeneratorExtension.registerExtension(builderExtension) ClassFileFactoryFinalizerExtension.registerExtension(outputExtension) } diff --git a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiConfigurationKeys.kt b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiConfigurationKeys.kt index d915199da5d..20e4c9b108b 100644 --- a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiConfigurationKeys.kt +++ b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiConfigurationKeys.kt @@ -10,4 +10,6 @@ import org.jetbrains.kotlin.config.CompilerConfigurationKey object JvmAbiConfigurationKeys { val OUTPUT_PATH: CompilerConfigurationKey = CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.OUTPUT_PATH_OPTION.description) + val REMOVE_DEBUG_INFO: CompilerConfigurationKey = + CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_DEBUG_INFO_OPTION.description) } diff --git a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiOutputExtension.kt b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiOutputExtension.kt index ece0e288665..ba1d57727bc 100644 --- a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiOutputExtension.kt +++ b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiOutputExtension.kt @@ -24,11 +24,12 @@ class JvmAbiOutputExtension( private val outputPath: File, private val abiClassInfos: Map, private val messageCollector: MessageCollector, + private val removeDebugInfo: Boolean, ) : ClassFileFactoryFinalizerExtension { override fun finalizeClassFactory(factory: ClassFileFactory) { // We need to wait until the end to produce any output in order to strip classes // from the InnerClasses attributes. - val outputFiles = AbiOutputFiles(abiClassInfos, factory) + val outputFiles = AbiOutputFiles(abiClassInfos, factory, removeDebugInfo) if (outputPath.extension == "jar") { // We don't include the runtime or main class in interface jars and always reset time stamps. CompileEnvironmentUtil.writeToJar( @@ -47,8 +48,11 @@ class JvmAbiOutputExtension( private class InnerClassInfo(val name: String, val outerName: String?, val innerName: String?, val access: Int) - private class AbiOutputFiles(val abiClassInfos: Map, val outputFiles: OutputFileCollection) : - OutputFileCollection { + private class AbiOutputFiles( + val abiClassInfos: Map, + val outputFiles: OutputFileCollection, + val removeDebugInfo: Boolean, + ) : OutputFileCollection { override fun get(relativePath: String): OutputFile? { error("AbiOutputFiles does not implement `get`.") } @@ -104,9 +108,23 @@ class JvmAbiOutputExtension( ?: return null val visitor = super.visitMethod(access, name, descriptor, signature, exceptions) + if (info == AbiMethodInfo.KEEP || access and (Opcodes.ACC_NATIVE or Opcodes.ACC_ABSTRACT) != 0) { + return object : MethodVisitor(Opcodes.API_VERSION, visitor) { + override fun visitLineNumber(line: Int, start: Label?) { + if (!removeDebugInfo) { + super.visitLineNumber(line, start) + } + } - if (info == AbiMethodInfo.KEEP || access and (Opcodes.ACC_NATIVE or Opcodes.ACC_ABSTRACT) != 0) - return visitor + override fun visitLocalVariable( + name: String?, descriptor: String?, signature: String?, start: Label?, end: Label?, index: Int, + ) { + if (!removeDebugInfo) { + super.visitLocalVariable(name, descriptor, signature, start, end, index) + } + } + } + } return object : MethodVisitor(Opcodes.API_VERSION, visitor) { override fun visitCode() { @@ -123,13 +141,15 @@ class JvmAbiOutputExtension( } } - // Strip source debug extensions if there are no inline functions. override fun visitSource(source: String?, debug: String?) { - // TODO Normalize and strip unused line numbers from SourceDebugExtensions - if (methodInfo.values.any { it == AbiMethodInfo.KEEP }) - super.visitSource(source, debug) - else - super.visitSource(source, null) + when { + removeDebugInfo -> super.visitSource(null, null) + methodInfo.values.none { it == AbiMethodInfo.KEEP } -> { + // Strip SourceDebugExtension attribute if there are no inline functions. + super.visitSource(source, null) + } + else -> super.visitSource(source, debug) + } } // Remove inner classes which are not present in the abi jar. @@ -139,11 +159,15 @@ class JvmAbiOutputExtension( innerClassInfos[name] = InnerClassInfo(name, outerName, innerName, access) } - // Strip private declarations from the Kotlin Metadata annotation. - override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor { + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { + // Strip @SourceDebugExtension annotation if we're removing debug info. + if (removeDebugInfo && descriptor == JvmAnnotationNames.SOURCE_DEBUG_EXTENSION_DESC) + return null + val delegate = super.visitAnnotation(descriptor, visible) if (descriptor != JvmAnnotationNames.METADATA_DESC) return delegate + // Strip private declarations from the Kotlin Metadata annotation. return abiMetadataProcessor(delegate) } diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/BaseJvmAbiTest.kt b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/BaseJvmAbiTest.kt index 89d0dbc8f60..f8907a8e31a 100644 --- a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/BaseJvmAbiTest.kt +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/BaseJvmAbiTest.kt @@ -75,9 +75,12 @@ abstract class BaseJvmAbiTest : TestCase() { freeArgs = listOf(compilation.srcDir.canonicalPath) classpath = (abiDependencies + kotlinJvmStdlib).joinToString(File.pathSeparator) { it.canonicalPath } pluginClasspaths = arrayOf(abiPluginJar.canonicalPath) - pluginOptions = arrayOf( + pluginOptions = listOfNotNull( abiOption(JvmAbiCommandLineProcessor.OUTPUT_PATH_OPTION.optionName, compilation.abiDir.canonicalPath), - ) + abiOption(JvmAbiCommandLineProcessor.REMOVE_DEBUG_INFO_OPTION.optionName, true.toString()).takeIf { + InTextDirectivesUtils.findStringWithPrefixes(directives, "// REMOVE_DEBUG_INFO") != null + } + ).toTypedArray() destination = compilation.destinationDir.canonicalPath noSourceDebugExtension = InTextDirectivesUtils.findStringWithPrefixes(directives, "// NO_SOURCE_DEBUG_EXTENSION") != null diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/CompareJvmAbiTestGenerated.java b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/CompareJvmAbiTestGenerated.java index 742e1cd2123..3c473022bda 100644 --- a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/CompareJvmAbiTestGenerated.java +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/CompareJvmAbiTestGenerated.java @@ -55,6 +55,26 @@ public class CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest { runTest("plugins/jvm-abi-gen/testData/compare/constant/"); } + @TestMetadata("debugInfoLineNumberTable") + public void testDebugInfoLineNumberTable() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/debugInfoLineNumberTable/"); + } + + @TestMetadata("debugInfoLocalVariableTable") + public void testDebugInfoLocalVariableTable() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/debugInfoLocalVariableTable/"); + } + + @TestMetadata("debugInfoSourceDebugExtension") + public void testDebugInfoSourceDebugExtension() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/debugInfoSourceDebugExtension/"); + } + + @TestMetadata("debugInfoSourceFile") + public void testDebugInfoSourceFile() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/debugInfoSourceFile/"); + } + @TestMetadata("declarationOrderInline") public void testDeclarationOrderInline() throws Exception { runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderInline/"); @@ -105,6 +125,11 @@ public class CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest { runTest("plugins/jvm-abi-gen/testData/compare/privateTypealias/"); } + @TestMetadata("removeDebugInfo") + public void testRemoveDebugInfo() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/removeDebugInfo/"); + } + @TestMetadata("returnType") public void testReturnType() throws Exception { runTest("plugins/jvm-abi-gen/testData/compare/returnType/"); diff --git a/plugins/jvm-abi-gen/testData/compare/debugInfoLineNumberTable/base/test.kt b/plugins/jvm-abi-gen/testData/compare/debugInfoLineNumberTable/base/test.kt new file mode 100644 index 00000000000..8458103b275 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/debugInfoLineNumberTable/base/test.kt @@ -0,0 +1,7 @@ +package test + +inline fun sum(x: Int, y: Int): Int { + // Comment before local variable + val result = x + y + return result +} diff --git a/plugins/jvm-abi-gen/testData/compare/debugInfoLineNumberTable/differentAbi/test.kt b/plugins/jvm-abi-gen/testData/compare/debugInfoLineNumberTable/differentAbi/test.kt new file mode 100644 index 00000000000..f06c3841ed8 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/debugInfoLineNumberTable/differentAbi/test.kt @@ -0,0 +1,7 @@ +package test + +inline fun sum(x: Int, y: Int): Int { + val result = x + y + // Comment after local variable + return result +} diff --git a/plugins/jvm-abi-gen/testData/compare/debugInfoLocalVariableTable/base/test.kt b/plugins/jvm-abi-gen/testData/compare/debugInfoLocalVariableTable/base/test.kt new file mode 100644 index 00000000000..ddd1c6a265d --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/debugInfoLocalVariableTable/base/test.kt @@ -0,0 +1,6 @@ +package test + +inline fun sum(x: Int, y: Int): Int { + val result = x + y + return result +} diff --git a/plugins/jvm-abi-gen/testData/compare/debugInfoLocalVariableTable/differentAbi/test.kt b/plugins/jvm-abi-gen/testData/compare/debugInfoLocalVariableTable/differentAbi/test.kt new file mode 100644 index 00000000000..ea0d5924dbf --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/debugInfoLocalVariableTable/differentAbi/test.kt @@ -0,0 +1,6 @@ +package test + +inline fun sum(x: Int, y: Int): Int { + val resultResult = x + y + return resultResult +} diff --git a/plugins/jvm-abi-gen/testData/compare/debugInfoSourceDebugExtension/base/test.kt b/plugins/jvm-abi-gen/testData/compare/debugInfoSourceDebugExtension/base/test.kt new file mode 100644 index 00000000000..7c7a59ed82c --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/debugInfoSourceDebugExtension/base/test.kt @@ -0,0 +1,6 @@ +package test + +fun forEach0(list: List, block: (T) -> Unit) { + // Comment before the function + list.forEach(block) +} diff --git a/plugins/jvm-abi-gen/testData/compare/debugInfoSourceDebugExtension/differentAbi/test.kt b/plugins/jvm-abi-gen/testData/compare/debugInfoSourceDebugExtension/differentAbi/test.kt new file mode 100644 index 00000000000..93976e77f80 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/debugInfoSourceDebugExtension/differentAbi/test.kt @@ -0,0 +1,6 @@ +package test + +fun forEach0(list: List, block: (T) -> Unit) { + list.forEach(block) + // Comment after the function +} diff --git a/plugins/jvm-abi-gen/testData/compare/debugInfoSourceFile/base/test1.kt b/plugins/jvm-abi-gen/testData/compare/debugInfoSourceFile/base/test1.kt new file mode 100644 index 00000000000..b8403714705 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/debugInfoSourceFile/base/test1.kt @@ -0,0 +1,3 @@ +package test + +class C diff --git a/plugins/jvm-abi-gen/testData/compare/debugInfoSourceFile/differentAbi/test2.kt b/plugins/jvm-abi-gen/testData/compare/debugInfoSourceFile/differentAbi/test2.kt new file mode 100644 index 00000000000..b8403714705 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/debugInfoSourceFile/differentAbi/test2.kt @@ -0,0 +1,3 @@ +package test + +class C diff --git a/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/base/test.kt b/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/base/test.kt new file mode 100644 index 00000000000..1d94e9a333b --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/base/test.kt @@ -0,0 +1,7 @@ +package test + +inline fun forEach0(list: List, block: (T) -> Unit): List { + val list1 = list + list1.forEach(block) + return list1 +} diff --git a/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/base/test1.kt b/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/base/test1.kt new file mode 100644 index 00000000000..b8403714705 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/base/test1.kt @@ -0,0 +1,3 @@ +package test + +class C diff --git a/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/directives.txt b/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/directives.txt new file mode 100644 index 00000000000..c7dd14f5001 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/directives.txt @@ -0,0 +1 @@ +// REMOVE_DEBUG_INFO diff --git a/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/sameAbi/test.kt b/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/sameAbi/test.kt new file mode 100644 index 00000000000..203070f09f4 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/sameAbi/test.kt @@ -0,0 +1,13 @@ +package test + +inline fun forEach0(list: List, block: (T) -> Unit): List { + val list2 = list + + list2.forEach(block) + + + + + + return list2 +} diff --git a/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/sameAbi/test2.kt b/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/sameAbi/test2.kt new file mode 100644 index 00000000000..b8403714705 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/removeDebugInfo/sameAbi/test2.kt @@ -0,0 +1,3 @@ +package test + +class C