Exclude private members from ABI classes

This commit is contained in:
Alexey Tsvetkov
2018-11-29 19:46:16 +03:00
parent 33faa00184
commit 903f6bc79b
17 changed files with 291 additions and 12 deletions
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.jvm.abi.asm.AbiClassBuilder
import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTrace
@@ -64,10 +65,10 @@ class JvmAbiAnalysisHandlerExtension(
private object AbiBinaries : ClassBuilderFactory {
override fun getClassBuilderMode(): ClassBuilderMode =
ClassBuilderMode.FULL
ClassBuilderMode.ABI
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder =
AbstractClassBuilder.Concrete(ClassWriter(0))
AbiClassBuilder(ClassWriter(0))
override fun asText(builder: ClassBuilder): String =
throw UnsupportedOperationException("AbiBinaries generator asked for text")
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.asm
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.AbstractClassBuilder
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.ClassVisitor
import org.jetbrains.org.objectweb.asm.FieldVisitor
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
internal class AbiClassBuilder(private val cv: ClassVisitor) : AbstractClassBuilder() {
override fun getVisitor(): ClassVisitor = cv
override fun newMethod(
origin: JvmDeclarationOrigin,
access: Int,
name: String,
desc: String,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
if (isPrivate(access)) return EMPTY_METHOD_VISITOR
return super.newMethod(origin, access, name, desc, signature, exceptions)
}
override fun newField(
origin: JvmDeclarationOrigin,
access: Int,
name: String,
desc: String,
signature: String?,
value: Any?
): FieldVisitor {
if (isPrivate(access)) return EMPTY_FIELD_VISITOR
return super.newField(origin, access, name, desc, signature, value)
}
override fun defineClass(
origin: PsiElement?,
version: Int,
access: Int,
name: String,
signature: String?,
superName: String,
interfaces: Array<out String>
) {
if (isPrivate(access)) return
super.defineClass(origin, version, access, name, signature, superName, interfaces)
}
private fun isPrivate(access: Int): Boolean =
(access and Opcodes.ACC_PRIVATE) == Opcodes.ACC_PRIVATE
}
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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 AbstractCompareJvmAbiTest : BaseJvmAbiTest() {
fun doTest(path: String) {
val testDir = File(path)
val base = Compilation(testDir, "base").also { make(it) }
if (testDir.resolve("sameAbi").exists()) {
val sameAbi = Compilation(testDir, "sameAbi").also { make(it) }
assertEqualDirectories(sameAbi.abiDir, base.abiDir, forgiveExtraFiles = false)
}
if (testDir.resolve("differentAbi").exists()) {
val differentAbi = Compilation(testDir, "differentAbi").also { make(it) }
assertFails("$base and $differentAbi abi are equal") {
assertEqualDirectories(differentAbi.abiDir, base.abiDir, forgiveExtraFiles = false)
}
}
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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 junit.framework.TestCase
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.utils.TestMessageCollector
import java.io.File
abstract class BaseJvmAbiTest : TestCase() {
private lateinit var workingDir: File
override fun setUp() {
super.setUp()
workingDir = createTempDir(javaClass.simpleName)
workingDir.deleteOnExit()
}
override fun tearDown() {
workingDir.deleteRecursively()
super.tearDown()
}
private val abiPluginJar = File("dist/kotlinc/lib/jvm-abi-gen.jar")
private fun abiOption(option: String, value: String): String =
"plugin:${JvmAbiCommandLineProcessor.COMPILER_PLUGIN_ID}:$option=$value"
inner class Compilation(val projectDir: File, val name: String) {
val srcDir: File
get() = projectDir.resolve(name)
val destinationDir: File
get() = workingDir.resolve("$name/out")
val abiDir: File
get() = workingDir.resolve("$name/abi")
override fun toString(): String =
"compilation '$name'"
}
fun make(compilation: Compilation) {
check(abiPluginJar.exists()) { "Plugin jar '$abiPluginJar' does not exist" }
check(compilation.srcDir.exists()) { "Source dir '${compilation.srcDir}' does not exist" }
val messageCollector = TestMessageCollector()
val compiler = K2JVMCompiler()
val args = compiler.createArguments().apply {
freeArgs = listOf(compilation.srcDir.canonicalPath)
pluginClasspaths = arrayOf(abiPluginJar.canonicalPath)
pluginOptions = arrayOf(abiOption("outputDir", compilation.abiDir.canonicalPath))
destination = compilation.destinationDir.canonicalPath
}
val exitCode = compiler.exec(messageCollector, Services.EMPTY, args)
if (exitCode != ExitCode.OK || messageCollector.errors.isNotEmpty()) {
val errorLines = listOf("Could not compile $compilation", "Exit code: $exitCode", "Errors:") + messageCollector.errors
error(errorLines.joinToString("\n"))
}
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.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 CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInCompare() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/jvm-abi-gen/testData/compare"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, false);
}
@TestMetadata("classPrivateMemebers")
public void testClassPrivateMemebers() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/classPrivateMemebers/");
}
@TestMetadata("topLevelPrivateMembers")
public void testTopLevelPrivateMembers() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/topLevelPrivateMembers/");
}
}
@@ -0,0 +1,15 @@
package test
class A {
val publicVal = 0
fun publicMethod() = 0
internal val internalVal = 0
internal fun internalMethod() = 0
protected val protectedVal = 0
protected fun protectedMethod() = 0
private val privateVal = 0
private fun privateMethod() = 0
}
@@ -0,0 +1,12 @@
package test
class A {
val publicVal = 0
fun publicMethod() = 0
internal val internalVal = 0
internal fun internalMethod() = 0
protected val protectedVal = 0
protected fun protectedMethod() = 0
}
@@ -0,0 +1,13 @@
package test
val publicVal = 0
const val publicConst = 0
fun publicFun() = 0
internal val internalVal = 0
internal const val internalConst = 0
internal fun internalFun() = 0
private val privateVal = 0
private const val privateConst = 0
private fun privateFun() = 0
@@ -0,0 +1,9 @@
package test
val publicVal = 0
const val publicConst = 0
fun publicFun() = 0
internal val internalVal = 0
internal const val internalConst = 0
internal fun internalFun() = 0