diff --git a/generators/build.gradle.kts b/generators/build.gradle.kts index a6fcc1ef826..a485a90942b 100644 --- a/generators/build.gradle.kts +++ b/generators/build.gradle.kts @@ -50,6 +50,7 @@ dependencies { testCompile(protobufCompareSourceSet.output) testCompile(projectTests(":compiler:cli")) + testCompile(projectTests(":compiler:incremental-compilation-impl")) testCompile(projectTests(":plugins:jvm-abi-gen")) testCompile(projectTests(":plugins:android-extensions-compiler")) testCompile(projectTests(":plugins:parcelize:parcelize-compiler")) diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 41530fda1ea..136399a7981 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -19,9 +19,7 @@ import org.jetbrains.kotlin.fir.plugin.AbstractFirAllOpenDiagnosticTest import org.jetbrains.kotlin.generators.TestGroup import org.jetbrains.kotlin.generators.impl.generateTestGroupSuite import org.jetbrains.kotlin.incremental.* -import org.jetbrains.kotlin.jvm.abi.AbstractCompareJvmAbiTest -import org.jetbrains.kotlin.jvm.abi.AbstractCompileAgainstJvmAbiTest -import org.jetbrains.kotlin.jvm.abi.AbstractJvmAbiContentTest +import org.jetbrains.kotlin.jvm.abi.* import org.jetbrains.kotlin.kapt.cli.test.AbstractArgumentParsingTest import org.jetbrains.kotlin.kapt.cli.test.AbstractKaptToolIntegrationTest import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTest @@ -169,15 +167,45 @@ fun main(args: Array) { testGroup("plugins/jvm-abi-gen/test", "plugins/jvm-abi-gen/testData") { testClass { - model("compare", recursive = false, extension = null) + model("compare", recursive = false, extension = null, targetBackend = TargetBackend.JVM) } testClass { - model("content", recursive = false, extension = null) + model("content", recursive = false, extension = null, targetBackend = TargetBackend.JVM) } testClass { - model("compile", recursive = false, extension = null) + model("compile", recursive = false, extension = null, targetBackend = TargetBackend.JVM) + } + + testClass { + model("compare", recursive = false, extension = null, targetBackend = TargetBackend.JVM_IR) + } + + testClass { + model("content", recursive = false, extension = null, targetBackend = TargetBackend.JVM_IR) + } + + testClass { + model("compile", recursive = false, extension = null, targetBackend = TargetBackend.JVM_IR) + } + } + + testGroup( + "plugins/jvm-abi-gen/test", "plugins/jvm-abi-gen/testData", + testRunnerMethodName = "runTestWithCustomIgnoreDirective", + additionalRunnerArguments = listOf("\"// IGNORE_BACKEND_LEGACY: \"") + ) { + testClass { + model("compare", recursive = false, extension = null, targetBackend = TargetBackend.JVM) + } + + testClass { + model("content", recursive = false, extension = null, targetBackend = TargetBackend.JVM) + } + + testClass { + model("compile", recursive = false, extension = null, targetBackend = TargetBackend.JVM) } } diff --git a/plugins/jvm-abi-gen/build.gradle.kts b/plugins/jvm-abi-gen/build.gradle.kts index e9e7d1e4c98..8a183d092eb 100644 --- a/plugins/jvm-abi-gen/build.gradle.kts +++ b/plugins/jvm-abi-gen/build.gradle.kts @@ -1,3 +1,4 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar description = "ABI generation for Kotlin/JVM" @@ -6,7 +7,16 @@ plugins { id("jps-compatible") } +val shadows: Configuration by configurations.creating { + isTransitive = false +} +configurations.getByName("compileOnly").extendsFrom(shadows) +configurations.getByName("testCompile").extendsFrom(shadows) + dependencies { + // Should come before compiler dependencies, see comment in "compiler/build.gradle.kts" + testRuntimeOnly(intellijDep()) + compileOnly(project(":compiler:util")) compileOnly(project(":compiler:cli")) compileOnly(project(":compiler:backend")) @@ -15,14 +25,19 @@ dependencies { compileOnly(project(":compiler:plugin-api")) compileOnly(project(":kotlin-build-common")) + // Include kotlinx.metadata for metadata stripping. + // Note that kotlinx-metadata-jvm already includes kotlinx-metadata, core:metadata, core:metadata.jvm, + // and protobuf-lite, so we only need to include kotlinx-metadata-jvm in the shadow jar. + compileOnly(project(":kotlinx-metadata")) + shadows(project(":kotlinx-metadata-jvm")) + compileOnly(intellijCoreDep()) { includeJars("intellij-core", "asm-all", rootProject = rootProject) } testRuntimeOnly(project(":kotlin-compiler")) - testCompile(commonDep("junit:junit")) - testCompile(projectTests(":compiler:tests-common")) - testCompile(projectTests(":compiler:incremental-compilation-impl")) - testRuntime(intellijCoreDep()) + testImplementation(commonDep("junit:junit")) + testImplementation(projectTests(":compiler:tests-common")) + testImplementation(projectTests(":compiler:incremental-compilation-impl")) } sourceSets { @@ -30,6 +45,20 @@ sourceSets { "test" { projectDefault() } } +noDefaultJar() +runtimeJar(tasks.register("shadowJar")) { + callGroovy("manifestAttributes", manifest, project) + manifest.attributes["Implementation-Version"] = archiveVersion + + from(mainSourceSet.output) + configurations = listOf(shadows) + relocate("kotlinx.metadata", "org.jetbrains.kotlin.jvm.abi.kotlinx.metadata") + mergeServiceFiles() // This is needed to relocate the services files for kotlinx.metadata +} + +val test by tasks +test.dependsOn("shadowJar") + projectTest(parallel = true) { workingDir = rootDir dependsOn(":dist") 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 new file mode 100644 index 00000000000..12f719f231b --- /dev/null +++ b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiClassBuilderInterceptor.kt @@ -0,0 +1,153 @@ +/* + * Copyright 2010-2020 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 + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.codegen.ClassBuilder +import org.jetbrains.kotlin.codegen.ClassBuilderFactory +import org.jetbrains.kotlin.codegen.DelegatingClassBuilder +import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory +import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension +import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.MemberDescriptor +import org.jetbrains.kotlin.diagnostics.DiagnosticSink +import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jetbrains.org.objectweb.asm.AnnotationVisitor +import org.jetbrains.org.objectweb.asm.MethodVisitor +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.commons.Method + +enum class AbiMethodInfo { + KEEP, + STRIP, +} + +sealed class AbiClassInfo { + object Public : AbiClassInfo() + class Stripped(val methodInfo: Map) : AbiClassInfo() +} + +/** + * Record ABI information for all classes in the current compilation unit. + * + * This needs to be a `ClassBuilderInterceptor`, since we need the descriptors + * in order to know which methods can be safely stripped from the output. + * On the other hand we cannot produce any output in this pass, since we need + * to know which classes are part of the public ABI in order to produce the + * correct `InnerClasses` attributes. + * + * --- + * + * Classes which are private, local, or anonymous can be stripped unless + * they are marked as part of the public ABI by a bit in the Kotlin + * metadata annotation. Public ABI classes have to be kept verbatim, since + * they are copied to all call sites of a surrounding inline function. + * + * If we keep a class we will strip the bodies from all non-inline function, + * remove private functions, and copy inline functions verbatim. There is one + * exception to this for inline suspend functions. + * + * For an inline suspend function `f` we will usually generate two methods, + * a non-inline method `f` and an inline method `f$$forInline`. The former can + * be stripped. However, if `f` is not callable directly, we only generate a + * single inline method `f` which should be kept. + */ +class JvmAbiClassBuilderInterceptor : ClassBuilderInterceptorExtension { + val abiClassInfo: MutableMap = mutableMapOf() + + override fun interceptClassBuilderFactory(interceptedFactory: ClassBuilderFactory, bindingContext: BindingContext, diagnostics: DiagnosticSink): ClassBuilderFactory = + object : DelegatingClassBuilderFactory(interceptedFactory) { + override fun newClassBuilder(origin: JvmDeclarationOrigin): DelegatingClassBuilder = + AbiInfoClassBuilder(interceptedFactory.newClassBuilder(origin)) + } + + private inner class AbiInfoClassBuilder(private val delegate: ClassBuilder) : DelegatingClassBuilder() { + lateinit var internalName: String + var localOrAnonymousClass = false + var publicAbi = false + val methodInfos = mutableMapOf() + val maskedMethods = mutableSetOf() // Methods which should be stripped even if they are marked as KEEP + + override fun getDelegate(): ClassBuilder = delegate + + override fun defineClass(origin: PsiElement?, version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array) { + internalName = name + super.defineClass(origin, version, access, name, signature, superName, interfaces) + } + + override fun visitOuterClass(owner: String, name: String?, desc: String?) { + localOrAnonymousClass = true + super.visitOuterClass(owner, name, desc) + } + + override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor { + // inline suspend functions are a special case: Unless they use reified type parameters, + // we will transform the original method and generate a $$forInline method for the inliner. + // Only the latter needs to be kept, the former can be stripped. Unfortunately, there is no + // metadata to indicate this (the inliner simply first checks for a method such as `f$$forInline` + // and then checks for `f` if this method doesn't exist) so we have to remember to strip the + // original methods if there was a $$forInline version. + if (name.endsWith(FOR_INLINE_SUFFIX)) { + // Note that origin.descriptor is null on the JVM BE in this case. + methodInfos[Method(name, desc)] = AbiMethodInfo.KEEP + maskedMethods += Method(name.removeSuffix(FOR_INLINE_SUFFIX), desc) + return super.newMethod(origin, access, name, desc, signature, exceptions) + } + + // Remove private functions from the ABI jars + val descriptor = origin.descriptor as? MemberDescriptor + if (access and Opcodes.ACC_PRIVATE != 0 && descriptor?.visibility?.let(DescriptorVisibilities::isPrivate) == true + || name == "" || name.startsWith("access\$") && access and Opcodes.ACC_SYNTHETIC != 0) { + return super.newMethod(origin, access, name, desc, signature, exceptions) + } + + // Copy inline functions verbatim + if (origin.descriptor?.safeAs()?.isInline == true) { + methodInfos[Method(name, desc)] = AbiMethodInfo.KEEP + } else { + methodInfos[Method(name, desc)] = AbiMethodInfo.STRIP + } + return super.newMethod(origin, access, name, desc, signature, exceptions) + } + + // Parse the public ABI flag from the Kotlin metadata annotation + override fun newAnnotation(desc: String, visible: Boolean): AnnotationVisitor { + val delegate = super.newAnnotation(desc, visible) + if (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 + } + super.visit(name, value) + } + } + } + + override fun done() { + // Remove local or anonymous classes unless they are in the scope of an inline function and + // strip non-inline methods from all other classe. + when { + publicAbi -> + abiClassInfo[internalName] = AbiClassInfo.Public + !localOrAnonymousClass -> { + for (method in maskedMethods) { + methodInfos.replace(method, AbiMethodInfo.STRIP) + } + abiClassInfo[internalName] = AbiClassInfo.Stripped(methodInfos) + } + } + super.done() + } + } +} 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 dbb3f1a6e92..f7f5aa0e07e 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 @@ -15,19 +15,33 @@ class JvmAbiCommandLineProcessor : CommandLineProcessor { companion object { const val COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.jvm.abi" - val OUTPUT_DIR_OPTION: CliOption = - CliOption("outputDir", "", "Output path for the generated files", required = true) + val OUTPUT_PATH_OPTION: CliOption = + CliOption( + "outputDir", + "", + "Output path for generated files. This can be either a directory or a jar file.", + true + ) + + val LEGACY_ABI_GEN_OPTION: CliOption = + CliOption( + "useLegacyAbiGen", + "true|false", + "Use the legacy two pass implementation of jvm-abi-gen.", + false + ) } override val pluginId: String get() = COMPILER_PLUGIN_ID override val pluginOptions: Collection - get() = listOf(OUTPUT_DIR_OPTION) + get() = listOf(OUTPUT_PATH_OPTION, LEGACY_ABI_GEN_OPTION) override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) { when (option) { - OUTPUT_DIR_OPTION -> configuration.put(JvmAbiConfigurationKeys.OUTPUT_DIR, value) + OUTPUT_PATH_OPTION -> configuration.put(JvmAbiConfigurationKeys.OUTPUT_PATH, value) + LEGACY_ABI_GEN_OPTION -> configuration.put(JvmAbiConfigurationKeys.LEGACY_ABI_GEN, 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 7470c138a66..5124fb214d9 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 @@ -6,6 +6,8 @@ package org.jetbrains.kotlin.jvm.abi import com.intellij.mock.MockProject +import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension +import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.JVMConfigurationKeys @@ -14,13 +16,23 @@ import java.io.File class JvmAbiComponentRegistrar : ComponentRegistrar { override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { - @Suppress("NAME_SHADOWING") - val configuration = configuration.copy() - configuration.get(JvmAbiConfigurationKeys.OUTPUT_DIR)?.let { - val dir = File(it) - configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, dir) + val outputPath = configuration.getNotNull(JvmAbiConfigurationKeys.OUTPUT_PATH) + if (configuration.get(JvmAbiConfigurationKeys.LEGACY_ABI_GEN, false)) { + // Use the two-pass implementation + require(!outputPath.endsWith(".jar")) { + "Legacy jvm-abi-gen does not support jar output." + } + val extension = JvmAbiAnalysisHandlerExtension(configuration.copy().apply { + put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(outputPath)) + }) + AnalysisHandlerExtension.registerExtension(project, extension) + } else { + // Use the single-pass implementation, using the new ABI flag in the metadata. + configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) + val builderExtension = JvmAbiClassBuilderInterceptor() + val outputExtension = JvmAbiOutputExtension(File(outputPath), builderExtension.abiClassInfo) + ClassBuilderInterceptorExtension.registerExtension(project, builderExtension) + ClassFileFactoryFinalizerExtension.registerExtension(project, outputExtension) } - val extension = JvmAbiAnalysisHandlerExtension(configuration) - AnalysisHandlerExtension.registerExtension(project, extension) } } \ No newline at end of file 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 92e911ca147..011522c765f 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 @@ -8,7 +8,9 @@ package org.jetbrains.kotlin.jvm.abi import org.jetbrains.kotlin.config.CompilerConfigurationKey object JvmAbiConfigurationKeys { - val OUTPUT_DIR: CompilerConfigurationKey = - CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.OUTPUT_DIR_OPTION.description) + val OUTPUT_PATH: CompilerConfigurationKey = + CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.OUTPUT_PATH_OPTION.description) + val LEGACY_ABI_GEN: CompilerConfigurationKey = + CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.LEGACY_ABI_GEN_OPTION.description) } \ No newline at end of file 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 new file mode 100644 index 00000000000..69ec7697f7e --- /dev/null +++ b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiMetadataProcessor.kt @@ -0,0 +1,200 @@ +/* + * Copyright 2010-2020 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 + +import kotlinx.metadata.* +import kotlinx.metadata.jvm.JvmClassExtensionVisitor +import kotlinx.metadata.jvm.JvmPackageExtensionVisitor +import kotlinx.metadata.jvm.KotlinClassHeader +import kotlinx.metadata.jvm.KotlinClassMetadata +import org.jetbrains.kotlin.load.java.JvmAnnotationNames.* +import org.jetbrains.org.objectweb.asm.AnnotationVisitor +import org.jetbrains.org.objectweb.asm.Opcodes + +/** + * Wrap the visitor for a Kotlin Metadata annotation to strip out private and local + * functions, properties, and type aliases as well as local delegated properties. + */ +fun abiMetadataProcessor(internalName: String, publicClasses: Set, annotationVisitor: AnnotationVisitor): AnnotationVisitor = + kotlinClassHeaderVisitor { header -> + val newHeader = when (val metadata = KotlinClassMetadata.read(header)) { + is KotlinClassMetadata.Class -> { + val writer = KotlinClassMetadata.Class.Writer() + metadata.accept(AbiKmClassVisitor(internalName, publicClasses, writer)) + writer.write(header.metadataVersion, header.extraInt).header + } + is KotlinClassMetadata.FileFacade -> { + val writer = KotlinClassMetadata.FileFacade.Writer() + metadata.accept(AbiKmPackageVisitor(writer)) + writer.write(header.metadataVersion, header.extraInt).header + } + is KotlinClassMetadata.MultiFileClassPart -> { + val writer = KotlinClassMetadata.MultiFileClassPart.Writer() + metadata.accept(AbiKmPackageVisitor(writer)) + writer.write(metadata.facadeClassName, header.metadataVersion, header.extraInt).header + } + else -> header + } + + // Write out the stripped annotation + annotationVisitor.visitKotlinMetadata(newHeader) + } + +/** + * Parse a KotlinClassHeader from an existing Kotlin Metadata annotation visitor. + */ +private fun kotlinClassHeaderVisitor(body: (KotlinClassHeader) -> Unit): AnnotationVisitor = + object : AnnotationVisitor(Opcodes.API_VERSION) { + var kind: Int = 1 + var metadataVersion: IntArray = intArrayOf() + var data1: MutableList = mutableListOf() + var data2: MutableList = mutableListOf() + var extraString: String = "" + var packageName: String = "" + var extraInt: Int = 0 + + override fun visit(name: String, value: Any?) { + when (name) { + KIND_FIELD_NAME -> kind = value as Int + METADATA_EXTRA_INT_FIELD_NAME -> extraInt = value as Int + METADATA_VERSION_FIELD_NAME -> metadataVersion = value as IntArray + METADATA_EXTRA_STRING_FIELD_NAME -> extraString = value as String + METADATA_PACKAGE_NAME_FIELD_NAME -> packageName = value as String + } + } + + override fun visitArray(name: String): AnnotationVisitor { + val destination = when (name) { + METADATA_DATA_FIELD_NAME -> data1 + METADATA_STRINGS_FIELD_NAME -> data2 + else -> error("Wrong array field in kotlin metadata: $name") + } + return object : AnnotationVisitor(Opcodes.ASM7) { + override fun visit(name: String?, value: Any?) { + destination += value as String + } + } + } + + + override fun visitEnd() { + body(KotlinClassHeader( + kind, + metadataVersion, + data1.toTypedArray(), + data2.toTypedArray(), + extraString, + packageName, + extraInt + )) + } + } + +/** + * Serialize a KotlinClassHeader to an existing Kotlin Metadata annotation visitor. + */ +private fun AnnotationVisitor.visitKotlinMetadata(header: KotlinClassHeader) { + visit(KIND_FIELD_NAME, header.kind) + visit(METADATA_VERSION_FIELD_NAME, header.metadataVersion) + visitArray(METADATA_DATA_FIELD_NAME).apply { + header.data1.forEach { visit(null, it) } + visitEnd() + } + visitArray(METADATA_STRINGS_FIELD_NAME).apply { + header.data2.forEach { visit(null, it) } + visitEnd() + } + visit(METADATA_EXTRA_STRING_FIELD_NAME, header.extraString) + visit(METADATA_PACKAGE_NAME_FIELD_NAME, header.packageName) + visit(METADATA_EXTRA_INT_FIELD_NAME, header.extraInt) + visitEnd() +} + +/** + * Class metadata adapter which removes private functions, properties, type aliases, + * and local delegated properties. + */ +private class AbiKmClassVisitor(private val internalName: String, private val publicClasses: Set, delegate: KmClassVisitor) : KmClassVisitor(delegate) { + override fun visitConstructor(flags: Flags): KmConstructorVisitor? { + if (!isPrivateDeclaration(flags)) + return super.visitConstructor(flags) + return null + } + + override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? { + if (!isPrivateDeclaration(flags)) + return super.visitFunction(flags, name) + return null + } + + override fun visitProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor? { + if (!isPrivateDeclaration(flags)) + return super.visitProperty(flags, name, getterFlags, setterFlags) + return null + } + + override fun visitNestedClass(name: String) { + if ("$internalName\$$name" in publicClasses) + super.visitNestedClass(name) + } + + override fun visitCompanionObject(name: String) { + if ("$internalName\$$name" in publicClasses) + super.visitCompanionObject(name) + } + + override fun visitTypeAlias(flags: Flags, name: String): KmTypeAliasVisitor? { + if (!isPrivateDeclaration(flags)) + return super.visitTypeAlias(flags, name) + return null + } + + override fun visitExtensions(type: KmExtensionType): KmClassExtensionVisitor? { + val delegate = super.visitExtensions(type) + if (type != JvmClassExtensionVisitor.TYPE) return delegate + return object : JvmClassExtensionVisitor(delegate as JvmClassExtensionVisitor?) { + override fun visitLocalDelegatedProperty( + flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags + ): KmPropertyVisitor? = null + } + } +} + +/** + * Class metadata adapter which removes private functions, properties, and type aliases. + */ +private class AbiKmPackageVisitor(delegate: KmPackageVisitor) : KmPackageVisitor(delegate) { + override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? { + if (!isPrivateDeclaration(flags)) + return super.visitFunction(flags, name) + return null + } + + override fun visitProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor? { + if (!isPrivateDeclaration(flags)) + return super.visitProperty(flags, name, getterFlags, setterFlags) + return null + } + + override fun visitTypeAlias(flags: Flags, name: String): KmTypeAliasVisitor? { + if (!isPrivateDeclaration(flags)) + return super.visitTypeAlias(flags, name) + return null + } + + override fun visitExtensions(type: KmExtensionType): KmPackageExtensionVisitor? { + val delegate = super.visitExtensions(type) + if (type != JvmPackageExtensionVisitor.TYPE) return delegate + return object : JvmPackageExtensionVisitor(delegate as JvmPackageExtensionVisitor?) { + override fun visitLocalDelegatedProperty( + flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags + ): KmPropertyVisitor? = null + } + } +} + +private fun isPrivateDeclaration(flags: Flags): Boolean = + Flag.IS_PRIVATE(flags) || Flag.IS_PRIVATE_TO_THIS(flags) || Flag.IS_LOCAL(flags) 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 new file mode 100644 index 00000000000..dcf31b69398 --- /dev/null +++ b/plugins/jvm-abi-gen/src/org/jetbrains/kotlin/jvm/abi/JvmAbiOutputExtension.kt @@ -0,0 +1,248 @@ +/* + * Copyright 2010-2020 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 + +import org.jetbrains.kotlin.backend.common.output.OutputFile +import org.jetbrains.kotlin.backend.common.output.OutputFileCollection +import org.jetbrains.kotlin.backend.common.output.SimpleOutputBinaryFile +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.output.writeAllTo +import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil +import org.jetbrains.kotlin.codegen.ClassFileFactory +import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension +import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.org.objectweb.asm.* +import org.jetbrains.org.objectweb.asm.commons.Method +import org.jetbrains.org.objectweb.asm.tree.* +import java.io.File + +class JvmAbiOutputExtension( + private val outputPath: File, + private val abiClassInfos: Map, +) : 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) + if (outputPath.extension == "jar") { + // We don't include the runtime or main class in interface jars and always reset time stamps. + CompileEnvironmentUtil.writeToJar(outputPath, false, true, true, null, outputFiles, MessageCollector.NONE) + } else { + outputFiles.writeAllTo(outputPath) + } + } + + private class AbiOutputFiles(val abiClassInfos: Map, val outputFiles: OutputFileCollection) : OutputFileCollection { + override fun get(relativePath: String): OutputFile? { + error("AbiOutputFiles does not implement `get`.") + } + + override fun asList(): List { + val metadata = outputFiles.asList().filter { + !it.relativePath.endsWith(".class") + }.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 + + abiInfo is AbiClassInfo.Public -> + // Copy verbatim + outputFile + + else -> /* abiInfo is AbiClassInfo.Stripped */ { + val methodInfo = (abiInfo as AbiClassInfo.Stripped).methodInfo + val writer = ClassWriter(0) + ClassReader(outputFile.asByteArray()).accept(object : ClassVisitor(Opcodes.API_VERSION, writer) { + // We remember all non-inline declarations in the stripped class so that we can + // order them deterministically after all inline methods. + private val fieldNodes = mutableListOf() + private val methodNodes = mutableListOf() + + // Strip private fields. + override fun visitField(access: Int, name: String?, descriptor: String?, signature: String?, value: Any?): FieldVisitor? { + if (access and Opcodes.ACC_PRIVATE != 0) + return null + return FieldNode(Opcodes.API_VERSION, access, name, descriptor, signature, value).also { + fieldNodes += it + } + } + + override fun visitMethod(access: Int, name: String, descriptor: String, signature: String?, exceptions: Array?): MethodVisitor? { + val info = methodInfo[Method(name, descriptor)] + ?: return null + + if (info == AbiMethodInfo.KEEP) + return super.visitMethod(access, name, descriptor, signature, exceptions) + + return StrippedMethodNode(access, name, descriptor, signature, exceptions).also { + methodNodes += it + } + } + + // 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) + } + + // Remove inner classes which are not present in the abi jar. + override fun visitInnerClass(name: String?, outerName: String?, innerName: String?, access: Int) { + if (name in abiClassInfos.keys) { + super.visitInnerClass(name, outerName, innerName, access) + } + } + + // Strip private declarations from the Kotlin Metadata annotation. + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor { + val delegate = super.visitAnnotation(descriptor, visible) + if (descriptor != JvmAnnotationNames.METADATA_DESC) + return delegate + return abiMetadataProcessor(internalName, abiClassInfos.keys, delegate) + } + + override fun visitEnd() { + fieldNodes.sortBy { "${it.name}${it.desc}" } + methodNodes.sortBy { "${it.name}${it.desc}" } + for (field in fieldNodes) { + field.accept(writer) + } + for (method in methodNodes) { + method.accept(writer) + } + super.visitEnd() + } + }, 0) + SimpleOutputBinaryFile(outputFile.sourceFiles, outputFile.relativePath, writer.toByteArray()) + } + } + } + + return metadata + classFiles + } + } +} + +private class StrippedMethodNode( + val access: Int, + val name: String, + val desc: String, + val signature: String?, + val exceptions: Array?, +) : MethodVisitor(Opcodes.API_VERSION) { + private class MethodAnnotation( + val visible: Boolean, + val node: AnnotationNode, + ) + + private class MethodTypeAnnotation( + val typeRef: Int, + val typePath: TypePath?, + val visible: Boolean, + val node: AnnotationNode, + ) + + private class MethodParameterAnnotation( + val parameter: Int, + val visible: Boolean, + val node: AnnotationNode, + ) + + private val parameters = mutableListOf() + private val annotations = mutableListOf() + private val typeAnnotations = mutableListOf() + private var visibleAnnotableParameterCount = 0 + private var invisibleAnnotableParameterCount = 0 + private val parameterAnnotations = mutableListOf() + private val attributes = mutableListOf() + + override fun visitParameter(name: String?, access: Int) { + parameters.add(ParameterNode(name, access)) + } + + override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor = + AnnotationNode(Opcodes.API_VERSION, descriptor).also { + annotations.add(MethodAnnotation(visible, it)) + } + + override fun visitTypeAnnotation(typeRef: Int, typePath: TypePath?, descriptor: String, visible: Boolean): AnnotationVisitor = + AnnotationNode(Opcodes.API_VERSION, descriptor).also { + typeAnnotations.add(MethodTypeAnnotation(typeRef, typePath, visible, it)) + } + + override fun visitAttribute(attribute: Attribute) { + attributes.add(attribute) + } + + override fun visitAnnotableParameterCount(parameterCount: Int, visible: Boolean) { + if (visible) { + visibleAnnotableParameterCount = parameterCount + } else { + invisibleAnnotableParameterCount = parameterCount + } + } + + override fun visitParameterAnnotation(parameter: Int, descriptor: String, visible: Boolean): AnnotationVisitor = + AnnotationNode(Opcodes.API_VERSION, descriptor).also { + parameterAnnotations.add(MethodParameterAnnotation(parameter, visible, it)) + } + + fun accept(classVisitor: ClassVisitor) { + val visitor = classVisitor.visitMethod(access, name, desc, signature, exceptions) + + for (parameter in parameters) { + visitor.visitParameter(parameter.name, parameter.access) + } + + for (annotation in annotations) { + visitor.visitAnnotation(annotation.node.desc, annotation.visible)?.let { + annotation.node.accept(it) + } + } + + for (typeAnnotation in typeAnnotations) { + visitor.visitTypeAnnotation( + typeAnnotation.typeRef, + typeAnnotation.typePath, + typeAnnotation.node.desc, + typeAnnotation.visible, + )?.let { + typeAnnotation.node.accept(it) + } + } + + visitor.visitAnnotableParameterCount(visibleAnnotableParameterCount, true) + visitor.visitAnnotableParameterCount(invisibleAnnotableParameterCount, false) + for (parameterAnnotation in parameterAnnotations) { + visitor.visitParameterAnnotation( + parameterAnnotation.parameter, + parameterAnnotation.node.desc, + parameterAnnotation.visible + )?.let { + parameterAnnotation.node.accept(it) + } + } + + for (attribute in attributes) { + visitor.visitAttribute(attribute) + } + + if (access and (Opcodes.ACC_NATIVE or Opcodes.ACC_ABSTRACT) == 0) { + visitor.visitCode() + visitor.visitInsn(Opcodes.ACONST_NULL) + visitor.visitInsn(Opcodes.ATHROW) + visitor.visitMaxs(0, parameters.size) + } + visitor.visitEnd() + } +} diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractIrCompareJvmAbiTest.kt b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractIrCompareJvmAbiTest.kt new file mode 100644 index 00000000000..2bfbfa3b312 --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractIrCompareJvmAbiTest.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.jvm.abi + +import org.jetbrains.kotlin.incremental.testingUtils.assertEqualDirectories +import java.io.File +import kotlin.test.assertFails + +abstract class AbstractIrCompareJvmAbiTest : AbstractCompareJvmAbiTest() { + override val useIrBackend = true +} diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractIrCompileAgainstJvmAbiTest.kt b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractIrCompileAgainstJvmAbiTest.kt new file mode 100644 index 00000000000..ba0f3941eaf --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractIrCompileAgainstJvmAbiTest.kt @@ -0,0 +1,8 @@ +package org.jetbrains.kotlin.jvm.abi + +import java.io.File +import java.net.URLClassLoader + +abstract class AbstractIrCompileAgainstJvmAbiTest : AbstractCompileAgainstJvmAbiTest() { + override val useIrBackend = true +} diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractIrJvmAbiContentTest.kt b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractIrJvmAbiContentTest.kt new file mode 100644 index 00000000000..9849ee6d5a7 --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractIrJvmAbiContentTest.kt @@ -0,0 +1,12 @@ +package org.jetbrains.kotlin.jvm.abi + +import com.intellij.openapi.util.io.systemIndependentPath +import org.jetbrains.kotlin.codegen.BytecodeListingTextCollectingVisitor +import org.jetbrains.kotlin.incremental.isClassFile +import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase +import org.jetbrains.org.objectweb.asm.* +import java.io.File + +abstract class AbstractIrJvmAbiContentTest : AbstractJvmAbiContentTest() { + override val useIrBackend = true +} diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractLegacyCompareJvmAbiTest.kt b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractLegacyCompareJvmAbiTest.kt new file mode 100644 index 00000000000..47cc4121644 --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractLegacyCompareJvmAbiTest.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2020 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 + +abstract class AbstractLegacyCompareJvmAbiTest : AbstractCompareJvmAbiTest() { + override val useLegacyAbiGen: Boolean + get() = true +} \ No newline at end of file diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractLegacyCompileAgainstJvmAbiTest.kt b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractLegacyCompileAgainstJvmAbiTest.kt new file mode 100644 index 00000000000..6a6a7e10887 --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractLegacyCompileAgainstJvmAbiTest.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2020 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 + +abstract class AbstractLegacyCompileAgainstJvmAbiTest : AbstractCompileAgainstJvmAbiTest() { + override val useLegacyAbiGen: Boolean + get() = true +} \ No newline at end of file diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractLegacyJvmAbiContentTest.kt b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractLegacyJvmAbiContentTest.kt new file mode 100644 index 00000000000..9d591d6b865 --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/AbstractLegacyJvmAbiContentTest.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2020 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 + +abstract class AbstractLegacyJvmAbiContentTest : AbstractJvmAbiContentTest() { + override val useLegacyAbiGen: Boolean + get() = true +} \ No newline at end of file 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 7b22ec9dc35..e0650a0dabc 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 @@ -64,8 +64,12 @@ abstract class BaseJvmAbiTest : TestCase() { freeArgs = listOf(compilation.srcDir.canonicalPath) classpath = (abiDependencies + kotlinJvmStdlib).joinToString(File.pathSeparator) { it.canonicalPath } pluginClasspaths = arrayOf(abiPluginJar.canonicalPath) - pluginOptions = arrayOf(abiOption("outputDir", compilation.abiDir.canonicalPath)) + pluginOptions = listOfNotNull( + abiOption(JvmAbiCommandLineProcessor.OUTPUT_PATH_OPTION.optionName, compilation.abiDir.canonicalPath), + if (useLegacyAbiGen) abiOption("useLegacyAbiGen", "true") else null + ).toTypedArray() destination = compilation.destinationDir.canonicalPath + useOldBackend = !useIrBackend } val exitCode = compiler.exec(messageCollector, Services.EMPTY, args) if (exitCode != ExitCode.OK || messageCollector.errors.isNotEmpty()) { @@ -74,6 +78,12 @@ abstract class BaseJvmAbiTest : TestCase() { } } + protected open val useIrBackend: Boolean + get() = false + + protected open val useLegacyAbiGen: Boolean + get() = false + protected val kotlinJvmStdlib = File("dist/kotlinc/lib/kotlin-stdlib.jar").also { check(it.exists()) { "Stdlib file '$it' does not exist" } } 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 8c342b2d4ef..dd7d35d65f3 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 @@ -9,6 +9,7 @@ import com.intellij.testFramework.TestDataPath; import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; import org.jetbrains.kotlin.test.TestMetadata; import org.junit.runner.RunWith; @@ -22,11 +23,11 @@ import java.util.regex.Pattern; @RunWith(JUnit3RunnerWithInners.class) public class CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest { private void runTest(String testDataFilePath) throws Exception { - KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } public void testAllFilesPresentInCompare() throws Exception { - KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/compare"), Pattern.compile("^([^\\.]+)$"), null, false); + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/compare"), Pattern.compile("^([^\\.]+)$"), null, TargetBackend.JVM, false); } @TestMetadata("classFlags") 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 a86c043b5fc..3d86ca4c1c0 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 @@ -9,6 +9,7 @@ import com.intellij.testFramework.TestDataPath; import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; import org.jetbrains.kotlin.test.TestMetadata; import org.junit.runner.RunWith; @@ -22,11 +23,11 @@ import java.util.regex.Pattern; @RunWith(JUnit3RunnerWithInners.class) public class CompileAgainstJvmAbiTestGenerated extends AbstractCompileAgainstJvmAbiTest { private void runTest(String testDataFilePath) throws Exception { - KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } public void testAllFilesPresentInCompile() throws Exception { - KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/compile"), Pattern.compile("^([^\\.]+)$"), null, false); + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/compile"), Pattern.compile("^([^\\.]+)$"), null, TargetBackend.JVM, false); } @TestMetadata("anonymousObject") diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/IrCompareJvmAbiTestGenerated.java b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/IrCompareJvmAbiTestGenerated.java new file mode 100644 index 00000000000..0c33c6dee65 --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/IrCompareJvmAbiTestGenerated.java @@ -0,0 +1,87 @@ +/* + * Copyright 2010-2021 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; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/jvm-abi-gen/testData/compare") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class IrCompareJvmAbiTestGenerated extends AbstractIrCompareJvmAbiTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInCompare() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/compare"), Pattern.compile("^([^\\.]+)$"), null, TargetBackend.JVM_IR, false); + } + + @TestMetadata("classFlags") + public void testClassFlags() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/classFlags/"); + } + + @TestMetadata("classPrivateMemebers") + public void testClassPrivateMemebers() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/classPrivateMemebers/"); + } + + @TestMetadata("clinit") + public void testClinit() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/clinit/"); + } + + @TestMetadata("constant") + public void testConstant() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/constant/"); + } + + @TestMetadata("functionBody") + public void testFunctionBody() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/functionBody/"); + } + + @TestMetadata("inlineFunctionBody") + public void testInlineFunctionBody() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/inlineFunctionBody/"); + } + + @TestMetadata("parameterName") + public void testParameterName() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/parameterName/"); + } + + @TestMetadata("privateTypealias") + public void testPrivateTypealias() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/privateTypealias/"); + } + + @TestMetadata("returnType") + public void testReturnType() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/returnType/"); + } + + @TestMetadata("superClass") + public void testSuperClass() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/superClass/"); + } + + @TestMetadata("topLevelPrivateMembers") + public void testTopLevelPrivateMembers() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/topLevelPrivateMembers/"); + } +} diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/IrCompileAgainstJvmAbiTestGenerated.java b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/IrCompileAgainstJvmAbiTestGenerated.java new file mode 100644 index 00000000000..b7f9ef30bde --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/IrCompileAgainstJvmAbiTestGenerated.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2021 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; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/jvm-abi-gen/testData/compile") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class IrCompileAgainstJvmAbiTestGenerated extends AbstractIrCompileAgainstJvmAbiTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInCompile() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/compile"), Pattern.compile("^([^\\.]+)$"), null, TargetBackend.JVM_IR, false); + } + + @TestMetadata("anonymousObject") + public void testAnonymousObject() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/anonymousObject/"); + } + + @TestMetadata("classes") + public void testClasses() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/classes/"); + } + + @TestMetadata("clinit") + public void testClinit() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/clinit/"); + } + + @TestMetadata("inlineAnonymousObject") + public void testInlineAnonymousObject() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/inlineAnonymousObject/"); + } + + @TestMetadata("inlineCapture") + public void testInlineCapture() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/inlineCapture/"); + } + + @TestMetadata("inlineReifiedFunction") + public void testInlineReifiedFunction() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/inlineReifiedFunction/"); + } + + @TestMetadata("privateOnlyConstructors") + public void testPrivateOnlyConstructors() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/privateOnlyConstructors/"); + } + + @TestMetadata("topLevel") + public void testTopLevel() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/topLevel/"); + } +} diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/IrJvmAbiContentTestGenerated.java b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/IrJvmAbiContentTestGenerated.java new file mode 100644 index 00000000000..556368ab15a --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/IrJvmAbiContentTestGenerated.java @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2021 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; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/jvm-abi-gen/testData/content") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class IrJvmAbiContentTestGenerated extends AbstractIrJvmAbiContentTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInContent() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/content"), Pattern.compile("^([^\\.]+)$"), null, TargetBackend.JVM_IR, false); + } + + @TestMetadata("class") + public void testClass() throws Exception { + runTest("plugins/jvm-abi-gen/testData/content/class/"); + } +} 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 fbbb87cf1d4..f2fa70a5799 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 @@ -9,6 +9,7 @@ import com.intellij.testFramework.TestDataPath; import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; import org.jetbrains.kotlin.test.TestMetadata; import org.junit.runner.RunWith; @@ -22,11 +23,11 @@ import java.util.regex.Pattern; @RunWith(JUnit3RunnerWithInners.class) public class JvmAbiContentTestGenerated extends AbstractJvmAbiContentTest { private void runTest(String testDataFilePath) throws Exception { - KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } public void testAllFilesPresentInContent() throws Exception { - KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/content"), Pattern.compile("^([^\\.]+)$"), null, false); + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/content"), Pattern.compile("^([^\\.]+)$"), null, TargetBackend.JVM, false); } @TestMetadata("class") diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/LegacyCompareJvmAbiTestGenerated.java b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/LegacyCompareJvmAbiTestGenerated.java new file mode 100644 index 00000000000..029b53a8bc7 --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/LegacyCompareJvmAbiTestGenerated.java @@ -0,0 +1,87 @@ +/* + * Copyright 2010-2021 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; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/jvm-abi-gen/testData/compare") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class LegacyCompareJvmAbiTestGenerated extends AbstractLegacyCompareJvmAbiTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM, testDataFilePath, "// IGNORE_BACKEND_LEGACY: "); + } + + public void testAllFilesPresentInCompare() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/compare"), Pattern.compile("^([^\\.]+)$"), null, TargetBackend.JVM, false); + } + + @TestMetadata("classFlags") + public void testClassFlags() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/classFlags/"); + } + + @TestMetadata("classPrivateMemebers") + public void testClassPrivateMemebers() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/classPrivateMemebers/"); + } + + @TestMetadata("clinit") + public void testClinit() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/clinit/"); + } + + @TestMetadata("constant") + public void testConstant() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/constant/"); + } + + @TestMetadata("functionBody") + public void testFunctionBody() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/functionBody/"); + } + + @TestMetadata("inlineFunctionBody") + public void testInlineFunctionBody() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/inlineFunctionBody/"); + } + + @TestMetadata("parameterName") + public void testParameterName() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/parameterName/"); + } + + @TestMetadata("privateTypealias") + public void testPrivateTypealias() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/privateTypealias/"); + } + + @TestMetadata("returnType") + public void testReturnType() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/returnType/"); + } + + @TestMetadata("superClass") + public void testSuperClass() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/superClass/"); + } + + @TestMetadata("topLevelPrivateMembers") + public void testTopLevelPrivateMembers() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compare/topLevelPrivateMembers/"); + } +} diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/LegacyCompileAgainstJvmAbiTestGenerated.java b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/LegacyCompileAgainstJvmAbiTestGenerated.java new file mode 100644 index 00000000000..6907226bc04 --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/LegacyCompileAgainstJvmAbiTestGenerated.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2021 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; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/jvm-abi-gen/testData/compile") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class LegacyCompileAgainstJvmAbiTestGenerated extends AbstractLegacyCompileAgainstJvmAbiTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM, testDataFilePath, "// IGNORE_BACKEND_LEGACY: "); + } + + public void testAllFilesPresentInCompile() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/compile"), Pattern.compile("^([^\\.]+)$"), null, TargetBackend.JVM, false); + } + + @TestMetadata("anonymousObject") + public void testAnonymousObject() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/anonymousObject/"); + } + + @TestMetadata("classes") + public void testClasses() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/classes/"); + } + + @TestMetadata("clinit") + public void testClinit() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/clinit/"); + } + + @TestMetadata("inlineAnonymousObject") + public void testInlineAnonymousObject() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/inlineAnonymousObject/"); + } + + @TestMetadata("inlineCapture") + public void testInlineCapture() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/inlineCapture/"); + } + + @TestMetadata("inlineReifiedFunction") + public void testInlineReifiedFunction() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/inlineReifiedFunction/"); + } + + @TestMetadata("privateOnlyConstructors") + public void testPrivateOnlyConstructors() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/privateOnlyConstructors/"); + } + + @TestMetadata("topLevel") + public void testTopLevel() throws Exception { + runTest("plugins/jvm-abi-gen/testData/compile/topLevel/"); + } +} diff --git a/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/LegacyJvmAbiContentTestGenerated.java b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/LegacyJvmAbiContentTestGenerated.java new file mode 100644 index 00000000000..c044efed66b --- /dev/null +++ b/plugins/jvm-abi-gen/test/org/jetbrains/kotlin/jvm/abi/LegacyJvmAbiContentTestGenerated.java @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2021 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; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/jvm-abi-gen/testData/content") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class LegacyJvmAbiContentTestGenerated extends AbstractLegacyJvmAbiContentTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM, testDataFilePath, "// IGNORE_BACKEND_LEGACY: "); + } + + public void testAllFilesPresentInContent() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/jvm-abi-gen/testData/content"), Pattern.compile("^([^\\.]+)$"), null, TargetBackend.JVM, false); + } + + @TestMetadata("class") + public void testClass() throws Exception { + runTest("plugins/jvm-abi-gen/testData/content/class/"); + } +} diff --git a/plugins/jvm-abi-gen/testData/compile/classes/lib/classes.kt b/plugins/jvm-abi-gen/testData/compile/classes/lib/classes.kt index 28708b9687c..c26a85c3556 100644 --- a/plugins/jvm-abi-gen/testData/compile/classes/lib/classes.kt +++ b/plugins/jvm-abi-gen/testData/compile/classes/lib/classes.kt @@ -13,6 +13,8 @@ open class A : I { fun aMethod(): Int = 30 inline fun aInlineMethod(): Int = 40 + private class AB {} + companion object { const val aConst: Int = 50 }