From 11d3ead9756dd0d8f4742a4c416b0f343891f9fa Mon Sep 17 00:00:00 2001 From: Vladimir Tagakov Date: Tue, 26 Dec 2023 18:11:39 -0700 Subject: [PATCH] jvm-abi-gen: Remove effectively private classes from ABI #KT-64590 Fixed --- .../kotlin/ir/overrides/IrVisibilityUtil.kt | 10 ++- .../jvm/abi/JvmAbiClassBuilderInterceptor.kt | 57 +++++++++++----- .../kotlin/jvm/abi/JvmAbiClassInfoBuilder.kt | 66 +++++++++++++++++++ .../jvm/abi/JvmAbiCommandLineProcessor.kt | 14 ++++ .../jvm/abi/JvmAbiComponentRegistrar.kt | 10 +-- .../kotlin/jvm/abi/JvmAbiConfigurationKeys.kt | 2 + .../kotlin/jvm/abi/JvmAbiMetadataProcessor.kt | 44 +++++++++---- .../kotlin/jvm/abi/JvmAbiOutputExtension.kt | 49 ++++++++------ .../kotlin/jvm/abi/BaseJvmAbiTest.kt | 6 ++ .../jvm/abi/CompareJvmAbiTestGenerated.java | 30 +++++++++ .../CompileAgainstJvmAbiTestGenerated.java | 30 +++++++++ .../jvm/abi/JvmAbiContentTestGenerated.java | 5 ++ .../compare/multifileClass/base/test.kt | 5 ++ .../multifileClass/differentAbi/test.kt | 5 ++ .../compare/nestedPrivateClasses/base/test.kt | 9 +++ .../nestedPrivateClasses/directives.txt | 1 + .../nestedPrivateClasses/sameAbi/test.kt | 3 + .../base/test.kt | 7 ++ .../directives.txt | 1 + .../sameAbi/test.kt | 7 ++ .../base/test.kt | 9 +++ .../directives.txt | 1 + .../sameAbi/test.kt | 11 ++++ .../privateTopLevelClasses/base/test.kt | 5 ++ .../privateTopLevelClasses/directives.txt | 1 + .../privateTopLevelClasses/sameAbi/test.kt | 3 + .../base/test.kt | 5 ++ .../differentAbi/test.kt | 3 + .../compile/conflictingClasses/app/app.kt | 5 ++ .../compile/conflictingClasses/lib/AppKt.kt | 7 ++ .../compile/multifileClass/app/app.kt | 12 ++++ .../compile/multifileClass/lib/part1.kt | 7 ++ .../compile/multifileClass/lib/part2.kt | 7 ++ .../multifileClassOptimized/app/app.kt | 12 ++++ .../multifileClassOptimized/directives.txt | 1 + .../multifileClassOptimized/lib/part1.kt | 7 ++ .../multifileClassOptimized/lib/part2.kt | 7 ++ .../app/app.kt | 12 ++++ .../directives.txt | 2 + .../lib/part1.kt | 7 ++ .../lib/part2.kt | 7 ++ .../app/app.kt | 12 ++++ .../directives.txt | 1 + .../lib/part1.kt | 7 ++ .../lib/part2.kt | 7 ++ .../app/app.kt | 7 ++ .../directives.txt | 1 + .../lib/lib.kt | 13 ++++ .../annotation.kt | 7 ++ .../signatures.txt | 20 ++++++ 50 files changed, 511 insertions(+), 56 deletions(-) create mode 100644 plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiClassInfoBuilder.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/multifileClass/base/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/multifileClass/differentAbi/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/base/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/directives.txt create mode 100644 plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/sameAbi/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/base/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/directives.txt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/sameAbi/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/base/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/directives.txt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/sameAbi/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/base/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/directives.txt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/sameAbi/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateTopLevelClassesWithoutOption/base/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compare/privateTopLevelClassesWithoutOption/differentAbi/test.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/conflictingClasses/app/app.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/conflictingClasses/lib/AppKt.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClass/app/app.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClass/lib/part1.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClass/lib/part2.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/app/app.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/directives.txt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/lib/part1.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/lib/part2.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/app/app.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/directives.txt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/lib/part1.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/lib/part2.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/app/app.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/directives.txt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/lib/part1.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/lib/part2.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/app/app.kt create mode 100644 plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/directives.txt create mode 100644 plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/lib/lib.kt create mode 100644 plugins/jvm-abi-gen/testData/content/effectivelyPrivateAnnotation/annotation.kt create mode 100644 plugins/jvm-abi-gen/testData/content/effectivelyPrivateAnnotation/signatures.txt diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrVisibilityUtil.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrVisibilityUtil.kt index 2eda43aee03..35afbea5330 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrVisibilityUtil.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrVisibilityUtil.kt @@ -6,19 +6,17 @@ package org.jetbrains.kotlin.ir.overrides import org.jetbrains.kotlin.descriptors.DescriptorVisibilities -import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.util.* fun IrDeclarationWithVisibility.isEffectivelyPrivate(): Boolean { - fun DescriptorVisibility.isNonPrivate(): Boolean = - this == DescriptorVisibilities.PUBLIC - || this == DescriptorVisibilities.PROTECTED - || this == DescriptorVisibilities.INTERNAL + val isNonPrivate = visibility == DescriptorVisibilities.PUBLIC + || visibility == DescriptorVisibilities.PROTECTED + || visibility == DescriptorVisibilities.INTERNAL return when { - visibility.isNonPrivate() -> parentClassOrNull?.isEffectivelyPrivate() ?: false + isNonPrivate -> parentClassOrNull?.isEffectivelyPrivate() ?: false visibility == DescriptorVisibilities.INVISIBLE_FAKE -> { val overridesOnlyPrivateDeclarations = (this as? IrOverridableDeclaration<*>) diff --git a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiClassBuilderInterceptor.kt b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiClassBuilderInterceptor.kt index 011abc12164..5eec5586898 100644 --- a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiClassBuilderInterceptor.kt +++ b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiClassBuilderInterceptor.kt @@ -12,7 +12,9 @@ import org.jetbrains.kotlin.codegen.`when`.WhenByEnumsMapping import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.overrides.isEffectivelyPrivate import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI +import org.jetbrains.kotlin.ir.util.isFileClass import org.jetbrains.kotlin.ir.util.primaryConstructor import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAnnotationNames @@ -28,7 +30,8 @@ enum class AbiMethodInfo { sealed class AbiClassInfo { object Public : AbiClassInfo() - class Stripped(val methodInfo: Map) : AbiClassInfo() + class Stripped(val methodInfo: Map, val prune: Boolean = false) : AbiClassInfo() + object Deleted : AbiClassInfo() } /** @@ -58,8 +61,15 @@ sealed class AbiClassInfo { */ class JvmAbiClassBuilderInterceptor( private val removeDataClassCopyIfConstructorIsPrivate: Boolean, + private val removePrivateClasses: Boolean, ) : ClassGeneratorExtension { - val abiClassInfo: MutableMap = mutableMapOf() + private var abiClassInfoBuilder = JvmAbiClassInfoBuilder(removePrivateClasses) + + fun buildAbiClassInfoAndReleaseResources(): Map { + return abiClassInfoBuilder.buildClassInfo().also { + abiClassInfoBuilder = JvmAbiClassInfoBuilder(removePrivateClasses) + } + } override fun generateClass(generator: ClassGenerator, declaration: IrClass?): ClassGenerator = AbiInfoClassGenerator(generator, declaration) @@ -70,14 +80,16 @@ class JvmAbiClassBuilderInterceptor( ) : ClassGenerator by delegate { private val isPrivateClass = irClass != null && DescriptorVisibilities.isPrivate(irClass.visibility) private val isDataClass = irClass != null && irClass.isData + private val removeClassFromAbi = shouldRemoveFromAbi(irClass, removePrivateClasses) @OptIn(UnsafeDuringIrConstructionAPI::class) private val primaryConstructorIsNotInAbi = irClass?.primaryConstructor?.visibility?.let(DescriptorVisibilities::isPrivate) == true lateinit var internalName: String + lateinit var superInterfaces: List var localOrAnonymousClass = false - var publicAbi = false + var keepClassAsIs = false val methodInfos = mutableMapOf() val maskedMethods = mutableSetOf() // Methods which should be stripped even if they are marked as KEEP @@ -86,11 +98,10 @@ class JvmAbiClassBuilderInterceptor( ) { // Always keep annotation classes // TODO: Investigate whether there are cases where we can remove annotation classes from the ABI. - if (access and Opcodes.ACC_ANNOTATION != 0) { - publicAbi = true - } + keepClassAsIs = keepClassAsIs || access and Opcodes.ACC_ANNOTATION != 0 internalName = name + superInterfaces = interfaces.asList() delegate.defineClass(version, access, name, signature, superName, interfaces) } @@ -102,7 +113,8 @@ class JvmAbiClassBuilderInterceptor( override fun newMethod( declaration: IrFunction?, access: Int, name: String, desc: String, signature: String?, exceptions: Array? ): MethodVisitor { - if (publicAbi) { + if (keepClassAsIs || removeClassFromAbi) { + // We don't care about methods when we remove or keep this class completely. return delegate.newMethod(declaration, access, name, desc, signature, exceptions) } @@ -146,32 +158,40 @@ class JvmAbiClassBuilderInterceptor( // Parse the public ABI flag from the Kotlin metadata annotation override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor { val delegate = delegate.visitAnnotation(desc, visible) - if (publicAbi || desc != JvmAnnotationNames.METADATA_DESC) + if (keepClassAsIs || desc != JvmAnnotationNames.METADATA_DESC) return delegate return object : AnnotationVisitor(Opcodes.API_VERSION, delegate) { override fun visit(name: String?, value: Any?) { if ((name == JvmAnnotationNames.METADATA_EXTRA_INT_FIELD_NAME) && (value is Int)) { - publicAbi = publicAbi || value and JvmAnnotationNames.METADATA_PUBLIC_ABI_FLAG != 0 + keepClassAsIs = keepClassAsIs || value and JvmAnnotationNames.METADATA_PUBLIC_ABI_FLAG != 0 } super.visit(name, value) } } } + override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) { + abiClassInfoBuilder.addInnerClass(name, outerName) + delegate.visitInnerClass(name, outerName, innerName, access) + } + override fun done(generateSmapCopyToAnnotation: Boolean) { // Remove local or anonymous classes unless they are in the scope of an inline function and // strip non-inline methods from all other classes. - when { - publicAbi -> - abiClassInfo[internalName] = AbiClassInfo.Public - !localOrAnonymousClass && !isWhenMappingClass -> { + val classInfo = when { + keepClassAsIs -> AbiClassInfo.Public + removeClassFromAbi -> AbiClassInfo.Deleted + localOrAnonymousClass -> AbiClassInfo.Deleted + isWhenMappingClass -> AbiClassInfo.Deleted + else -> { for (method in maskedMethods) { - methodInfos.replace(method, AbiMethodInfo.STRIP) + methodInfos[method] = AbiMethodInfo.STRIP } - abiClassInfo[internalName] = AbiClassInfo.Stripped(methodInfos) + AbiClassInfo.Stripped(methodInfos) } } + abiClassInfoBuilder.recordInitialClassInfo(internalName, classInfo, superInterfaces) delegate.done(generateSmapCopyToAnnotation) } @@ -179,3 +199,10 @@ class JvmAbiClassBuilderInterceptor( get() = internalName.endsWith(WhenByEnumsMapping.MAPPINGS_CLASS_NAME_POSTFIX) } } + +private fun shouldRemoveFromAbi(irClass: IrClass?, removePrivateClasses: Boolean): Boolean = when { + irClass == null -> false + irClass.isFileClass -> false + removePrivateClasses -> irClass.isEffectivelyPrivate() + else -> false +} diff --git a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiClassInfoBuilder.kt b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiClassInfoBuilder.kt new file mode 100644 index 00000000000..1869ba0df73 --- /dev/null +++ b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiClassInfoBuilder.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2010-2024 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.jvm.abi + +/** + * Collects information about kept/stripped/deleted classes, and then adapts it so that it would be consistent if private interfaces + * are present in the module. The latter is done only if [removePrivateClasses] is enabled. + * + * When using this class, call [recordInitialClassInfo]/[addInnerClass] repeatedly until all necessary data is collected, and then call + * [buildClassInfo] to get the resulting class infos. + * + * Private interface is a special case from the ABI standpoint because it is allowed to expose private interface by inheriting a public + * class from it, see KT-20088. This code keeps all private interfaces which are implemented (perhaps indirectly) by at least one public + * (in terms of ABI) class in the module. Also, in case such private interface is nested, all its outer classes should be kept but pruned. + */ +internal class JvmAbiClassInfoBuilder(private val removePrivateClasses: Boolean) { + private val abiClassInfo = mutableMapOf() + private val innerClassToOuter = mutableMapOf() + private val allSuperInterfaces = mutableMapOf>() + + fun recordInitialClassInfo(className: String, info: AbiClassInfo, superInterfaces: List) { + abiClassInfo[className] = info + if (removePrivateClasses) { + allSuperInterfaces[className] = superInterfaces + } + } + + fun addInnerClass(innerClass: String, outerClass: String?) { + if (outerClass == null) return + if (removePrivateClasses) { + innerClassToOuter.getOrPut(innerClass) { outerClass } + } + } + + fun buildClassInfo(): Map { + if (removePrivateClasses) { + val visited = hashSetOf() + for ((className, info) in abiClassInfo) if (info != AbiClassInfo.Deleted) { + for (superInterface in allSuperInterfaces[className].orEmpty()) { + keepInterface(superInterface, visited) + } + } + } + + return abiClassInfo + } + + private fun keepInterface(className: String, visited: MutableSet) { + if (!visited.add(className)) return + + abiClassInfo[className] = AbiClassInfo.Public + + for (outerClass in generateSequence(className, innerClassToOuter::get).drop(1)) { + if (abiClassInfo[outerClass] == AbiClassInfo.Deleted) { + abiClassInfo[outerClass] = AbiClassInfo.Stripped(emptyMap(), prune = true) + } + } + + for (superInterface in allSuperInterfaces[className].orEmpty()) { + keepInterface(superInterface, visited) + } + } +} 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 ec7db93e295..e6c39fdfbb6 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 @@ -51,6 +51,15 @@ class JvmAbiCommandLineProcessor : CommandLineProcessor { " to keep working. Flipping this to true reduces stability of the ABI.", false, ) + + val REMOVE_PRIVATE_CLASSES_OPTION: CliOption = + CliOption( + "removePrivateClasses", + "true/false", + "Remove private classes from ABI. False by default due to backwards compatibility. If enabled, " + + "private top-level classes will no longer be available from Java classes in the same package.", + false, + ) } override val pluginId: String @@ -62,6 +71,7 @@ class JvmAbiCommandLineProcessor : CommandLineProcessor { REMOVE_DEBUG_INFO_OPTION, REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION, PRESERVE_DECLARATION_ORDER_OPTION, + REMOVE_PRIVATE_CLASSES_OPTION, ) override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) { @@ -73,6 +83,10 @@ class JvmAbiCommandLineProcessor : CommandLineProcessor { value == "true" ) PRESERVE_DECLARATION_ORDER_OPTION -> configuration.put(JvmAbiConfigurationKeys.PRESERVE_DECLARATION_ORDER, value == "true") + REMOVE_PRIVATE_CLASSES_OPTION -> configuration.put( + JvmAbiConfigurationKeys.REMOVE_PRIVATE_CLASSES, + 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 89224137a5a..48ee2c4b31b 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,11 +20,13 @@ class JvmAbiComponentRegistrar : CompilerPluginRegistrar() { val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) val removeDataClassCopy = configuration.getBoolean(JvmAbiConfigurationKeys.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE) - - val builderExtension = JvmAbiClassBuilderInterceptor(removeDataClassCopy) + val builderExtension = JvmAbiClassBuilderInterceptor( + removeDataClassCopy, + configuration.getBoolean(JvmAbiConfigurationKeys.REMOVE_PRIVATE_CLASSES), + ) val outputExtension = JvmAbiOutputExtension( - File(outputPath), builderExtension.abiClassInfo, messageCollector, - configuration.getBoolean(JvmAbiConfigurationKeys.REMOVE_DEBUG_INFO), + File(outputPath), builderExtension::buildAbiClassInfoAndReleaseResources, + messageCollector, configuration.getBoolean(JvmAbiConfigurationKeys.REMOVE_DEBUG_INFO), removeDataClassCopy, configuration.getBoolean(JvmAbiConfigurationKeys.PRESERVE_DECLARATION_ORDER), ) 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 fa22aa78442..8a20fd4ff20 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 @@ -16,4 +16,6 @@ object JvmAbiConfigurationKeys { CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION.description) val PRESERVE_DECLARATION_ORDER: CompilerConfigurationKey = CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.PRESERVE_DECLARATION_ORDER_OPTION.description) + val REMOVE_PRIVATE_CLASSES: CompilerConfigurationKey = + CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_PRIVATE_CLASSES_OPTION.description) } diff --git a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiMetadataProcessor.kt b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiMetadataProcessor.kt index aa13b689277..69d158dfbd8 100644 --- a/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiMetadataProcessor.kt +++ b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiMetadataProcessor.kt @@ -19,6 +19,8 @@ fun abiMetadataProcessor( annotationVisitor: AnnotationVisitor, removeDataClassCopyIfConstructorIsPrivate: Boolean, preserveDeclarationOrder: Boolean, + classesToBeDeleted: Set, + pruneClass: Boolean, ): AnnotationVisitor = kotlinClassHeaderVisitor { header -> // kotlinx-metadata only supports writing Kotlin metadata of version >= 1.4, so we need to @@ -33,13 +35,18 @@ fun abiMetadataProcessor( KotlinClassMetadata.transform(header) { metadata -> when (metadata) { is KotlinClassMetadata.Class -> { - metadata.kmClass.removePrivateDeclarations(removeDataClassCopyIfConstructorIsPrivate, preserveDeclarationOrder) + metadata.kmClass.removePrivateDeclarations( + removeDataClassCopyIfConstructorIsPrivate, + preserveDeclarationOrder, + classesToBeDeleted, + pruneClass + ) } is KotlinClassMetadata.FileFacade -> { - metadata.kmPackage.removePrivateDeclarations(preserveDeclarationOrder) + metadata.kmPackage.removePrivateDeclarations(preserveDeclarationOrder, pruneClass) } is KotlinClassMetadata.MultiFileClassPart -> { - metadata.kmPackage.removePrivateDeclarations(preserveDeclarationOrder) + metadata.kmPackage.removePrivateDeclarations(preserveDeclarationOrder, pruneClass) } else -> Unit } @@ -150,22 +157,37 @@ private fun AnnotationVisitor.visitKotlinMetadata(header: Metadata) { visitEnd() } -private fun KmClass.removePrivateDeclarations(removeCopyAlongWithConstructor: Boolean, preserveDeclarationOrder: Boolean) { - constructors.removeIf { it.visibility.isPrivate } - (this as KmDeclarationContainer).removePrivateDeclarations(copyFunShouldBeDeleted(removeCopyAlongWithConstructor), preserveDeclarationOrder) +private fun KmClass.removePrivateDeclarations( + removeCopyAlongWithConstructor: Boolean, + preserveDeclarationOrder: Boolean, + classesToBeDeleted: Set, + pruneClass: Boolean, +) { + constructors.removeIf { pruneClass || it.visibility.isPrivate } + (this as KmDeclarationContainer).removePrivateDeclarations( + copyFunShouldBeDeleted(removeCopyAlongWithConstructor), + preserveDeclarationOrder, + pruneClass, + ) + nestedClasses.removeIf { "$name\$$it" in classesToBeDeleted } + companionObject = companionObject.takeUnless { "$name\$$it" in classesToBeDeleted } localDelegatedProperties.clear() // TODO: do not serialize private type aliases once KT-17229 is fixed. } -private fun KmPackage.removePrivateDeclarations(preserveDeclarationOrder: Boolean) { - (this as KmDeclarationContainer).removePrivateDeclarations(false, preserveDeclarationOrder) +private fun KmPackage.removePrivateDeclarations(preserveDeclarationOrder: Boolean, pruneClass: Boolean) { + (this as KmDeclarationContainer).removePrivateDeclarations(false, preserveDeclarationOrder, pruneClass) localDelegatedProperties.clear() // TODO: do not serialize private type aliases once KT-17229 is fixed. } -private fun KmDeclarationContainer.removePrivateDeclarations(copyFunShouldBeDeleted: Boolean, preserveDeclarationOrder: Boolean) { - functions.removeIf { it.visibility.isPrivate || (copyFunShouldBeDeleted && it.name == "copy") } - properties.removeIf { it.visibility.isPrivate } +private fun KmDeclarationContainer.removePrivateDeclarations( + copyFunShouldBeDeleted: Boolean, + preserveDeclarationOrder: Boolean, + pruneClass: Boolean, +) { + functions.removeIf { pruneClass || it.visibility.isPrivate || (copyFunShouldBeDeleted && it.name == "copy") } + properties.removeIf { pruneClass || it.visibility.isPrivate } if (!preserveDeclarationOrder) { functions.sortWith(compareBy(KmFunction::name, { it.signature.toString() })) 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 84d2a27343b..d5f3b5c4ec2 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,7 +24,7 @@ import java.io.File class JvmAbiOutputExtension( private val outputPath: File, - private val abiClassInfos: Map, + private val abiClassInfoBuilder: () -> Map, private val messageCollector: MessageCollector, private val removeDebugInfo: Boolean, private val removeDataClassCopyIfConstructorIsPrivate: Boolean, @@ -34,7 +34,7 @@ class JvmAbiOutputExtension( // 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, removeDebugInfo, removeDataClassCopyIfConstructorIsPrivate, preserveDeclarationOrder) + AbiOutputFiles(abiClassInfoBuilder(), factory, removeDebugInfo, removeDataClassCopyIfConstructorIsPrivate, preserveDeclarationOrder) if (outputPath.extension == "jar") { // We don't include the runtime or main class in interface jars and always reset time stamps. CompileEnvironmentUtil.writeToJar( @@ -60,6 +60,10 @@ class JvmAbiOutputExtension( val removeCopyAlongWithConstructor: Boolean, val preserveDeclarationOrder: Boolean, ) : OutputFileCollection { + private val classesToBeDeleted = abiClassInfos.mapNotNullTo(mutableSetOf()) { (className, action) -> + className.takeIf { action == AbiClassInfo.Deleted } + } + override fun get(relativePath: String): OutputFile? { error("AbiOutputFiles does not implement `get`.") } @@ -70,19 +74,15 @@ class JvmAbiOutputExtension( }.sortedBy { it.relativePath } val classFiles = abiClassInfos.keys.sorted().mapNotNull { internalName -> - val outputFile = outputFiles.get("$internalName.class") - val abiInfo = abiClassInfos.getValue(internalName) - when { - // Note that outputFile may be null, e.g., for empty $DefaultImpls classes in the JVM backend. - outputFile == null -> - null + // Note that outputFile may be null, e.g., for empty $DefaultImpls classes in the JVM backend. + val outputFile = outputFiles.get("$internalName.class") ?: return@mapNotNull null - abiInfo is AbiClassInfo.Public -> - // Copy verbatim - outputFile - - else -> /* abiInfo is AbiClassInfo.Stripped */ { - val methodInfo = (abiInfo as AbiClassInfo.Stripped).methodInfo + when (val abiInfo = abiClassInfos.getValue(internalName)) { + is AbiClassInfo.Deleted -> null + is AbiClassInfo.Public -> outputFile // Copy verbatim + is AbiClassInfo.Stripped -> { + val prune = abiInfo.prune + val methodInfo = abiInfo.methodInfo val innerClassesToKeep = mutableSetOf() val noMethodsKept = methodInfo.values.none { it == AbiMethodInfo.KEEP } val writer = ClassWriter(0) @@ -101,9 +101,9 @@ class JvmAbiOutputExtension( name: String?, descriptor: String?, signature: String?, - value: Any? + value: Any?, ): FieldVisitor? { - if (access and Opcodes.ACC_PRIVATE != 0) + if (prune || access and Opcodes.ACC_PRIVATE != 0) return null return FieldNode(access, name, descriptor, signature, value).also { keptFields += it @@ -117,6 +117,7 @@ class JvmAbiOutputExtension( signature: String?, exceptions: Array? ): MethodVisitor? { + if (prune) return null val info = methodInfo[Method(name, descriptor)] ?: return null val node = MethodNode(access, name, descriptor, signature, exceptions).also { @@ -133,7 +134,7 @@ class JvmAbiOutputExtension( override fun visitSource(source: String?, debug: String?) { when { removeDebugInfo -> super.visitSource(null, null) - noMethodsKept -> { + prune || noMethodsKept -> { // Strip SourceDebugExtension attribute if there are no inline functions. super.visitSource(source, null) } @@ -151,14 +152,20 @@ class JvmAbiOutputExtension( override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { // Strip @SourceDebugExtension annotation if we're removing debug info. if (descriptor == JvmAnnotationNames.SOURCE_DEBUG_EXTENSION_DESC) { - if (removeDebugInfo || noMethodsKept) return null + if (removeDebugInfo || noMethodsKept || prune) 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, removeCopyAlongWithConstructor, preserveDeclarationOrder) + return abiMetadataProcessor( + delegate, + removeCopyAlongWithConstructor, + preserveDeclarationOrder, + classesToBeDeleted, + prune + ) } override fun visitEnd() { @@ -200,7 +207,9 @@ class JvmAbiOutputExtension( val next = stack.removeLast() add(next) // Classes form a tree by nesting, so none of the children have been visited yet. - innerClassesByOuterName[next]?.mapNotNullTo(stack) { it.name.takeIf(abiClassInfos::contains) } + innerClassesByOuterName[next]?.mapNotNullTo(stack) { info -> + info.name.takeUnless { abiClassInfos[it] == AbiClassInfo.Deleted } + } } } 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 5c1364b29ed..02cc74ff8eb 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 @@ -88,6 +88,9 @@ abstract class BaseJvmAbiTest : TestCase() { abiOption(JvmAbiCommandLineProcessor.PRESERVE_DECLARATION_ORDER_OPTION.optionName, true.toString()).takeIf { InTextDirectivesUtils.findStringWithPrefixes(directives, "// PRESERVE_DECLARATION_ORDER") != null }, + abiOption(JvmAbiCommandLineProcessor.REMOVE_PRIVATE_CLASSES_OPTION.optionName, true.toString()).takeIf { + InTextDirectivesUtils.findStringWithPrefixes(directives, "// REMOVE_PRIVATE_CLASSES") != null + }, ).toTypedArray() destination = compilation.destinationDir.canonicalPath noSourceDebugExtension = InTextDirectivesUtils.findStringWithPrefixes(directives, "// NO_SOURCE_DEBUG_EXTENSION") != null @@ -95,6 +98,9 @@ abstract class BaseJvmAbiTest : TestCase() { if (InTextDirectivesUtils.findStringWithPrefixes(directives, "// USE_K2") != null) { useK2 = true } + if (InTextDirectivesUtils.findStringWithPrefixes(directives, "// INHERIT_MULTIFILE_PARTS") != null) { + inheritMultifileParts = true + } } val exitCode = compiler.exec(messageCollector, Services.EMPTY, args) if (exitCode != ExitCode.OK || messageCollector.errors.isNotEmpty()) { 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 1bb76a651a1..05727fd70ec 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 @@ -140,6 +140,16 @@ public class CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest { runTest("plugins/jvm-abi-gen/testData/compare/lambdas/"); } + @TestMetadata("multifileClass") + public void testMultifileClass() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/multifileClass/"); + } + + @TestMetadata("nestedPrivateClasses") + public void testNestedPrivateClasses() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/"); + } + @TestMetadata("parameterName") public void testParameterName() throws Exception { runTest("plugins/jvm-abi-gen/testData/compare/parameterName/"); @@ -150,6 +160,26 @@ public class CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest { runTest("plugins/jvm-abi-gen/testData/compare/preserveDeclarationOrder/"); } + @TestMetadata("privateInterfaceDefaultImplementation") + public void testPrivateInterfaceDefaultImplementation() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/"); + } + + @TestMetadata("privateOuterClassForEffectivelyVisibleInterface") + public void testPrivateOuterClassForEffectivelyVisibleInterface() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/"); + } + + @TestMetadata("privateTopLevelClasses") + public void testPrivateTopLevelClasses() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/"); + } + + @TestMetadata("privateTopLevelClassesWithoutOption") + public void testPrivateTopLevelClassesWithoutOption() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/privateTopLevelClassesWithoutOption/"); + } + @TestMetadata("privateTypealias") public void testPrivateTypealias() throws Exception { runTest("plugins/jvm-abi-gen/testData/compare/privateTypealias/"); diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/CompileAgainstJvmAbiTestGenerated.java b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/CompileAgainstJvmAbiTestGenerated.java index 29550d3de3d..1c6e99c195d 100644 --- a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/CompileAgainstJvmAbiTestGenerated.java +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/CompileAgainstJvmAbiTestGenerated.java @@ -45,6 +45,11 @@ public class CompileAgainstJvmAbiTestGenerated extends AbstractCompileAgainstJvm runTest("plugins/jvm-abi-gen/testData/compile/clinit/"); } + @TestMetadata("conflictingClasses") + public void testConflictingClasses() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/conflictingClasses/"); + } + @TestMetadata("inlineAnnotationInstantiation") public void testInlineAnnotationInstantiation() throws Exception { runTest("plugins/jvm-abi-gen/testData/compile/inlineAnnotationInstantiation/"); @@ -100,6 +105,26 @@ public class CompileAgainstJvmAbiTestGenerated extends AbstractCompileAgainstJvm runTest("plugins/jvm-abi-gen/testData/compile/kt-40340/"); } + @TestMetadata("multifileClass") + public void testMultifileClass() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/multifileClass/"); + } + + @TestMetadata("multifileClassOptimized") + public void testMultifileClassOptimized() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/"); + } + + @TestMetadata("multifileClassOptimizedWithRemovePrivateOption") + public void testMultifileClassOptimizedWithRemovePrivateOption() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/"); + } + + @TestMetadata("multifileClassWithRemovePrivateOption") + public void testMultifileClassWithRemovePrivateOption() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/"); + } + @TestMetadata("privateAnnotationsFromJavaApp") public void testPrivateAnnotationsFromJavaApp() throws Exception { runTest("plugins/jvm-abi-gen/testData/compile/privateAnnotationsFromJavaApp/"); @@ -120,6 +145,11 @@ public class CompileAgainstJvmAbiTestGenerated extends AbstractCompileAgainstJvm runTest("plugins/jvm-abi-gen/testData/compile/privateClassesFromJavaLib/"); } + @TestMetadata("privateInterfaceImplementedByPublicClass") + public void testPrivateInterfaceImplementedByPublicClass() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/"); + } + @TestMetadata("privateOnlyConstructors") public void testPrivateOnlyConstructors() throws Exception { runTest("plugins/jvm-abi-gen/testData/compile/privateOnlyConstructors/"); diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/JvmAbiContentTestGenerated.java b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/JvmAbiContentTestGenerated.java index bc405c6a71c..8a0e5bad155 100644 --- a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/JvmAbiContentTestGenerated.java +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/JvmAbiContentTestGenerated.java @@ -55,6 +55,11 @@ public class JvmAbiContentTestGenerated extends AbstractJvmAbiContentTest { runTest("plugins/jvm-abi-gen/testData/content/class/"); } + @TestMetadata("effectivelyPrivateAnnotation") + public void testEffectivelyPrivateAnnotation() throws Exception { + runTest("plugins/jvm-abi-gen/testData/content/effectivelyPrivateAnnotation/"); + } + @TestMetadata("innerClasses") public void testInnerClasses() throws Exception { runTest("plugins/jvm-abi-gen/testData/content/innerClasses/"); diff --git a/plugins/jvm-abi-gen/testData/compare/multifileClass/base/test.kt b/plugins/jvm-abi-gen/testData/compare/multifileClass/base/test.kt new file mode 100644 index 00000000000..fe6e609e5a4 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/multifileClass/base/test.kt @@ -0,0 +1,5 @@ +@file:JvmMultifileClass +@file:JvmName("Multifile") +package test + +fun f() {} diff --git a/plugins/jvm-abi-gen/testData/compare/multifileClass/differentAbi/test.kt b/plugins/jvm-abi-gen/testData/compare/multifileClass/differentAbi/test.kt new file mode 100644 index 00000000000..794391eabd0 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/multifileClass/differentAbi/test.kt @@ -0,0 +1,5 @@ +@file:JvmMultifileClass +@file:JvmName("Multifile") +package test + +// Removal of function from a multi-file class should result in a different ABI even though the multi-file part class is package-private. diff --git a/plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/base/test.kt b/plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/base/test.kt new file mode 100644 index 00000000000..27d7658cc80 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/base/test.kt @@ -0,0 +1,9 @@ +package test + +class Class { + private companion object {} + private class PrivateClass { + companion object {} + class NestedClass + } +} diff --git a/plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/directives.txt b/plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/directives.txt new file mode 100644 index 00000000000..64d198c8982 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/directives.txt @@ -0,0 +1 @@ +// REMOVE_PRIVATE_CLASSES diff --git a/plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/sameAbi/test.kt b/plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/sameAbi/test.kt new file mode 100644 index 00000000000..0739e4bf273 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/nestedPrivateClasses/sameAbi/test.kt @@ -0,0 +1,3 @@ +package test + +class Class diff --git a/plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/base/test.kt b/plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/base/test.kt new file mode 100644 index 00000000000..2c1c7f4b7f0 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/base/test.kt @@ -0,0 +1,7 @@ +package test + +class SomeClass { + private interface PrivateInterface { + fun d() = "Default implementation of removed interface should not affect abi." + } +} diff --git a/plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/directives.txt b/plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/directives.txt new file mode 100644 index 00000000000..64d198c8982 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/directives.txt @@ -0,0 +1 @@ +// REMOVE_PRIVATE_CLASSES diff --git a/plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/sameAbi/test.kt b/plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/sameAbi/test.kt new file mode 100644 index 00000000000..c30f5ca9382 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateInterfaceDefaultImplementation/sameAbi/test.kt @@ -0,0 +1,7 @@ +package test + +class SomeClass { + private interface PrivateInterface { + fun d(): String + } +} diff --git a/plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/base/test.kt b/plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/base/test.kt new file mode 100644 index 00000000000..3b2ec125a4e --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/base/test.kt @@ -0,0 +1,9 @@ +package test + +class Outer { + class Public : NestedClass.I + + private class NestedClass { + interface I + } +} diff --git a/plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/directives.txt b/plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/directives.txt new file mode 100644 index 00000000000..64d198c8982 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/directives.txt @@ -0,0 +1 @@ +// REMOVE_PRIVATE_CLASSES diff --git a/plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/sameAbi/test.kt b/plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/sameAbi/test.kt new file mode 100644 index 00000000000..de6d1097662 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateOuterClassForEffectivelyVisibleInterface/sameAbi/test.kt @@ -0,0 +1,11 @@ +package test + +class Outer { + class Public : NestedClass.I + + private class NestedClass { + interface I + + fun someFun() = Unit + } +} diff --git a/plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/base/test.kt b/plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/base/test.kt new file mode 100644 index 00000000000..36b956bd43f --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/base/test.kt @@ -0,0 +1,5 @@ +package test + +class Class + +private class PrivateClass diff --git a/plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/directives.txt b/plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/directives.txt new file mode 100644 index 00000000000..64d198c8982 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/directives.txt @@ -0,0 +1 @@ +// REMOVE_PRIVATE_CLASSES diff --git a/plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/sameAbi/test.kt b/plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/sameAbi/test.kt new file mode 100644 index 00000000000..0739e4bf273 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateTopLevelClasses/sameAbi/test.kt @@ -0,0 +1,3 @@ +package test + +class Class diff --git a/plugins/jvm-abi-gen/testData/compare/privateTopLevelClassesWithoutOption/base/test.kt b/plugins/jvm-abi-gen/testData/compare/privateTopLevelClassesWithoutOption/base/test.kt new file mode 100644 index 00000000000..36b956bd43f --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateTopLevelClassesWithoutOption/base/test.kt @@ -0,0 +1,5 @@ +package test + +class Class + +private class PrivateClass diff --git a/plugins/jvm-abi-gen/testData/compare/privateTopLevelClassesWithoutOption/differentAbi/test.kt b/plugins/jvm-abi-gen/testData/compare/privateTopLevelClassesWithoutOption/differentAbi/test.kt new file mode 100644 index 00000000000..0739e4bf273 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compare/privateTopLevelClassesWithoutOption/differentAbi/test.kt @@ -0,0 +1,3 @@ +package test + +class Class diff --git a/plugins/jvm-abi-gen/testData/compile/conflictingClasses/app/app.kt b/plugins/jvm-abi-gen/testData/compile/conflictingClasses/app/app.kt new file mode 100644 index 00000000000..8088a987532 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/conflictingClasses/app/app.kt @@ -0,0 +1,5 @@ +package app + +//UnexpectedInnerClass although declared in a separate module +//appear to be an inner class of the class generated from this file +fun runAppAndReturnOk() = AppKt.UnexpectedInnerClass.result() diff --git a/plugins/jvm-abi-gen/testData/compile/conflictingClasses/lib/AppKt.kt b/plugins/jvm-abi-gen/testData/compile/conflictingClasses/lib/AppKt.kt new file mode 100644 index 00000000000..7fe7a59c259 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/conflictingClasses/lib/AppKt.kt @@ -0,0 +1,7 @@ +package app + +class AppKt { + object UnexpectedInnerClass { + fun result() = "OK" + } +} diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClass/app/app.kt b/plugins/jvm-abi-gen/testData/compile/multifileClass/app/app.kt new file mode 100644 index 00000000000..8b20744cc3d --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClass/app/app.kt @@ -0,0 +1,12 @@ +package app + +import lib.* + +fun runAppAndReturnOk(): String { + f1() + if (v1 != "OK") return "Fail 1" + f2() + if (v2 != "OK") return "Fail 2" + + return "OK" +} diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClass/lib/part1.kt b/plugins/jvm-abi-gen/testData/compile/multifileClass/lib/part1.kt new file mode 100644 index 00000000000..3447cfdb9de --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClass/lib/part1.kt @@ -0,0 +1,7 @@ +@file:JvmMultifileClass +@file:JvmName("Multifile") +package lib + +typealias T1 = String +fun f1() {} +val v1: T1 = "OK" diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClass/lib/part2.kt b/plugins/jvm-abi-gen/testData/compile/multifileClass/lib/part2.kt new file mode 100644 index 00000000000..24cbe2863ee --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClass/lib/part2.kt @@ -0,0 +1,7 @@ +@file:JvmMultifileClass +@file:JvmName("Multifile") +package lib + +typealias T2 = String +fun f2() {} +val v2: T2 = "OK" diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/app/app.kt b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/app/app.kt new file mode 100644 index 00000000000..8b20744cc3d --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/app/app.kt @@ -0,0 +1,12 @@ +package app + +import lib.* + +fun runAppAndReturnOk(): String { + f1() + if (v1 != "OK") return "Fail 1" + f2() + if (v2 != "OK") return "Fail 2" + + return "OK" +} diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/directives.txt b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/directives.txt new file mode 100644 index 00000000000..227cf745a8f --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/directives.txt @@ -0,0 +1 @@ +// INHERIT_MULTIFILE_PARTS diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/lib/part1.kt b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/lib/part1.kt new file mode 100644 index 00000000000..3447cfdb9de --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/lib/part1.kt @@ -0,0 +1,7 @@ +@file:JvmMultifileClass +@file:JvmName("Multifile") +package lib + +typealias T1 = String +fun f1() {} +val v1: T1 = "OK" diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/lib/part2.kt b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/lib/part2.kt new file mode 100644 index 00000000000..24cbe2863ee --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimized/lib/part2.kt @@ -0,0 +1,7 @@ +@file:JvmMultifileClass +@file:JvmName("Multifile") +package lib + +typealias T2 = String +fun f2() {} +val v2: T2 = "OK" diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/app/app.kt b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/app/app.kt new file mode 100644 index 00000000000..8b20744cc3d --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/app/app.kt @@ -0,0 +1,12 @@ +package app + +import lib.* + +fun runAppAndReturnOk(): String { + f1() + if (v1 != "OK") return "Fail 1" + f2() + if (v2 != "OK") return "Fail 2" + + return "OK" +} diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/directives.txt b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/directives.txt new file mode 100644 index 00000000000..596bad9e4a3 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/directives.txt @@ -0,0 +1,2 @@ +// INHERIT_MULTIFILE_PARTS +// REMOVE_PRIVATE_CLASSES diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/lib/part1.kt b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/lib/part1.kt new file mode 100644 index 00000000000..3447cfdb9de --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/lib/part1.kt @@ -0,0 +1,7 @@ +@file:JvmMultifileClass +@file:JvmName("Multifile") +package lib + +typealias T1 = String +fun f1() {} +val v1: T1 = "OK" diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/lib/part2.kt b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/lib/part2.kt new file mode 100644 index 00000000000..24cbe2863ee --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassOptimizedWithRemovePrivateOption/lib/part2.kt @@ -0,0 +1,7 @@ +@file:JvmMultifileClass +@file:JvmName("Multifile") +package lib + +typealias T2 = String +fun f2() {} +val v2: T2 = "OK" diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/app/app.kt b/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/app/app.kt new file mode 100644 index 00000000000..8b20744cc3d --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/app/app.kt @@ -0,0 +1,12 @@ +package app + +import lib.* + +fun runAppAndReturnOk(): String { + f1() + if (v1 != "OK") return "Fail 1" + f2() + if (v2 != "OK") return "Fail 2" + + return "OK" +} diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/directives.txt b/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/directives.txt new file mode 100644 index 00000000000..64d198c8982 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/directives.txt @@ -0,0 +1 @@ +// REMOVE_PRIVATE_CLASSES diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/lib/part1.kt b/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/lib/part1.kt new file mode 100644 index 00000000000..3447cfdb9de --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/lib/part1.kt @@ -0,0 +1,7 @@ +@file:JvmMultifileClass +@file:JvmName("Multifile") +package lib + +typealias T1 = String +fun f1() {} +val v1: T1 = "OK" diff --git a/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/lib/part2.kt b/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/lib/part2.kt new file mode 100644 index 00000000000..24cbe2863ee --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/multifileClassWithRemovePrivateOption/lib/part2.kt @@ -0,0 +1,7 @@ +@file:JvmMultifileClass +@file:JvmName("Multifile") +package lib + +typealias T2 = String +fun f2() {} +val v2: T2 = "OK" diff --git a/plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/app/app.kt b/plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/app/app.kt new file mode 100644 index 00000000000..8cfb1c08950 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/app/app.kt @@ -0,0 +1,7 @@ +package app + +import lib.* + +fun runAppAndReturnOk(): String { + return Outer.Public.result() +} diff --git a/plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/directives.txt b/plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/directives.txt new file mode 100644 index 00000000000..64d198c8982 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/directives.txt @@ -0,0 +1 @@ +// REMOVE_PRIVATE_CLASSES diff --git a/plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/lib/lib.kt b/plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/lib/lib.kt new file mode 100644 index 00000000000..256a92f699f --- /dev/null +++ b/plugins/jvm-abi-gen/testData/compile/privateInterfaceImplementedByPublicClass/lib/lib.kt @@ -0,0 +1,13 @@ +package lib + +class Outer { + object Public : Private + + private interface Private: Secret.EffectivelyPrivate + + private object Secret { + interface EffectivelyPrivate { + fun result(): String = "OK" + } + } +} diff --git a/plugins/jvm-abi-gen/testData/content/effectivelyPrivateAnnotation/annotation.kt b/plugins/jvm-abi-gen/testData/content/effectivelyPrivateAnnotation/annotation.kt new file mode 100644 index 00000000000..7a42859951f --- /dev/null +++ b/plugins/jvm-abi-gen/testData/content/effectivelyPrivateAnnotation/annotation.kt @@ -0,0 +1,7 @@ +package test + +class Outer { + private object Annotations { + annotation class EffectivelyPrivateAnnotation + } +} diff --git a/plugins/jvm-abi-gen/testData/content/effectivelyPrivateAnnotation/signatures.txt b/plugins/jvm-abi-gen/testData/content/effectivelyPrivateAnnotation/signatures.txt new file mode 100644 index 00000000000..ce446ff0e20 --- /dev/null +++ b/plugins/jvm-abi-gen/testData/content/effectivelyPrivateAnnotation/signatures.txt @@ -0,0 +1,20 @@ +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/Outer$Annotations$EffectivelyPrivateAnnotation { + // source: 'annotation.kt' + private final inner class test/Outer$Annotations + public inner class test/Outer$Annotations$EffectivelyPrivateAnnotation +} +@kotlin.Metadata +final class test/Outer$Annotations { + // source: 'annotation.kt' + private final inner class test/Outer$Annotations + public inner class test/Outer$Annotations$EffectivelyPrivateAnnotation + public final static @org.jetbrains.annotations.NotNull field INSTANCE: test.Outer$Annotations +} +@kotlin.Metadata +public final class test/Outer { + // source: 'annotation.kt' + private final inner class test/Outer$Annotations + public method (): void +}