JVM: New single-pass implementation for jvm-abi-gen

This commit is contained in:
Steven Schäfer
2020-11-30 14:03:29 +01:00
committed by Alexander Udalov
parent a54503cede
commit d08990ebb4
26 changed files with 1191 additions and 30 deletions
+1
View File
@@ -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"))
@@ -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<String>) {
testGroup("plugins/jvm-abi-gen/test", "plugins/jvm-abi-gen/testData") {
testClass<AbstractCompareJvmAbiTest> {
model("compare", recursive = false, extension = null)
model("compare", recursive = false, extension = null, targetBackend = TargetBackend.JVM)
}
testClass<AbstractJvmAbiContentTest> {
model("content", recursive = false, extension = null)
model("content", recursive = false, extension = null, targetBackend = TargetBackend.JVM)
}
testClass<AbstractCompileAgainstJvmAbiTest> {
model("compile", recursive = false, extension = null)
model("compile", recursive = false, extension = null, targetBackend = TargetBackend.JVM)
}
testClass<AbstractIrCompareJvmAbiTest> {
model("compare", recursive = false, extension = null, targetBackend = TargetBackend.JVM_IR)
}
testClass<AbstractIrJvmAbiContentTest> {
model("content", recursive = false, extension = null, targetBackend = TargetBackend.JVM_IR)
}
testClass<AbstractIrCompileAgainstJvmAbiTest> {
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<AbstractLegacyCompareJvmAbiTest> {
model("compare", recursive = false, extension = null, targetBackend = TargetBackend.JVM)
}
testClass<AbstractLegacyJvmAbiContentTest> {
model("content", recursive = false, extension = null, targetBackend = TargetBackend.JVM)
}
testClass<AbstractLegacyCompileAgainstJvmAbiTest> {
model("compile", recursive = false, extension = null, targetBackend = TargetBackend.JVM)
}
}
+33 -4
View File
@@ -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>("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")
@@ -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<Method, AbiMethodInfo>) : 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<String, AbiClassInfo> = 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<Method, AbiMethodInfo>()
val maskedMethods = mutableSetOf<Method>() // 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<out String>) {
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<out String>?): 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 == "<clinit>" || 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<FunctionDescriptor>()?.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()
}
}
}
@@ -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", "<path>", "Output path for the generated files", required = true)
val OUTPUT_PATH_OPTION: CliOption =
CliOption(
"outputDir",
"<path>",
"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<CliOption>
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}")
}
}
@@ -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)
}
}
@@ -8,7 +8,9 @@ package org.jetbrains.kotlin.jvm.abi
import org.jetbrains.kotlin.config.CompilerConfigurationKey
object JvmAbiConfigurationKeys {
val OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(JvmAbiCommandLineProcessor.OUTPUT_DIR_OPTION.description)
val OUTPUT_PATH: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(JvmAbiCommandLineProcessor.OUTPUT_PATH_OPTION.description)
val LEGACY_ABI_GEN: CompilerConfigurationKey<Boolean> =
CompilerConfigurationKey.create<Boolean>(JvmAbiCommandLineProcessor.LEGACY_ABI_GEN_OPTION.description)
}
@@ -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<String>, 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<String> = mutableListOf()
var data2: MutableList<String> = 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<String>, 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)
@@ -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<String, AbiClassInfo>,
) : 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<String, AbiClassInfo>, val outputFiles: OutputFileCollection) : OutputFileCollection {
override fun get(relativePath: String): OutputFile? {
error("AbiOutputFiles does not implement `get`.")
}
override fun asList(): List<OutputFile> {
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<FieldNode>()
private val methodNodes = mutableListOf<StrippedMethodNode>()
// 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<out String>?): 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<out String>?,
) : 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<ParameterNode>()
private val annotations = mutableListOf<MethodAnnotation>()
private val typeAnnotations = mutableListOf<MethodTypeAnnotation>()
private var visibleAnnotableParameterCount = 0
private var invisibleAnnotableParameterCount = 0
private val parameterAnnotations = mutableListOf<MethodParameterAnnotation>()
private val attributes = mutableListOf<Attribute>()
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()
}
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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" }
}
@@ -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")
@@ -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")
@@ -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/");
}
}
@@ -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/");
}
}
@@ -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/");
}
}
@@ -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")
@@ -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/");
}
}
@@ -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/");
}
}
@@ -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/");
}
}
@@ -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
}