jvm-abi-gen: Support removeDebugInfo option
This option removes debug info, such as: - SourceFile attribute on classes - LocalVariableTable, LineNumberTable, SourceDebugExtension attributes on inline functions - @SourceDebugExtension annotation on inline functions #KT-33020 Fixed
This commit is contained in:
committed by
Space Team
parent
73af0fffa9
commit
fe1e2b8b34
@@ -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<CliOption>
|
||||
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}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -10,4 +10,6 @@ import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
object JvmAbiConfigurationKeys {
|
||||
val OUTPUT_PATH: CompilerConfigurationKey<String> =
|
||||
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.OUTPUT_PATH_OPTION.description)
|
||||
val REMOVE_DEBUG_INFO: CompilerConfigurationKey<Boolean> =
|
||||
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_DEBUG_INFO_OPTION.description)
|
||||
}
|
||||
|
||||
@@ -24,11 +24,12 @@ class JvmAbiOutputExtension(
|
||||
private val outputPath: File,
|
||||
private val abiClassInfos: Map<String, AbiClassInfo>,
|
||||
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<String, AbiClassInfo>, val outputFiles: OutputFileCollection) :
|
||||
OutputFileCollection {
|
||||
private class AbiOutputFiles(
|
||||
val abiClassInfos: Map<String, AbiClassInfo>,
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+25
@@ -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/");
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
inline fun sum(x: Int, y: Int): Int {
|
||||
// Comment before local variable
|
||||
val result = x + y
|
||||
return result
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
inline fun sum(x: Int, y: Int): Int {
|
||||
val result = x + y
|
||||
// Comment after local variable
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package test
|
||||
|
||||
inline fun sum(x: Int, y: Int): Int {
|
||||
val result = x + y
|
||||
return result
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package test
|
||||
|
||||
inline fun sum(x: Int, y: Int): Int {
|
||||
val resultResult = x + y
|
||||
return resultResult
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package test
|
||||
|
||||
fun <T> forEach0(list: List<T>, block: (T) -> Unit) {
|
||||
// Comment before the function
|
||||
list.forEach(block)
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package test
|
||||
|
||||
fun <T> forEach0(list: List<T>, block: (T) -> Unit) {
|
||||
list.forEach(block)
|
||||
// Comment after the function
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
class C
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
class C
|
||||
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
inline fun <T> forEach0(list: List<T>, block: (T) -> Unit): List<T> {
|
||||
val list1 = list
|
||||
list1.forEach(block)
|
||||
return list1
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
class C
|
||||
@@ -0,0 +1 @@
|
||||
// REMOVE_DEBUG_INFO
|
||||
@@ -0,0 +1,13 @@
|
||||
package test
|
||||
|
||||
inline fun <T> forEach0(list: List<T>, block: (T) -> Unit): List<T> {
|
||||
val list2 = list
|
||||
|
||||
list2.forEach(block)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return list2
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
class C
|
||||
Reference in New Issue
Block a user