Exclude private members from ABI classes
This commit is contained in:
@@ -40,7 +40,16 @@ public class ClassBuilderMode {
|
||||
* Full function bodies
|
||||
*/
|
||||
public final static ClassBuilderMode FULL = new ClassBuilderMode(
|
||||
/* bodies = */ true,
|
||||
/* bodies = */ true,
|
||||
/* metadata = */ true,
|
||||
/* sourceRetention = */ false,
|
||||
/* generateMultiFileFacadePartClasses = */ true);
|
||||
|
||||
/**
|
||||
* ABI for compilation (non-private signatures + inline function bodies)
|
||||
*/
|
||||
public final static ClassBuilderMode ABI = new ClassBuilderMode(
|
||||
/* bodies = */ true,
|
||||
/* metadata = */ true,
|
||||
/* sourceRetention = */ false,
|
||||
/* generateMultiFileFacadePartClasses = */ true);
|
||||
|
||||
@@ -245,7 +245,9 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
DescriptorSerializer serializer =
|
||||
DescriptorSerializer.createForLambda(new JvmSerializerExtension(v.getSerializationBindings(), state));
|
||||
|
||||
ProtoBuf.Function functionProto = serializer.functionProto(freeLambdaDescriptor).build();
|
||||
ProtoBuf.Function.Builder builder = serializer.functionProto(freeLambdaDescriptor);
|
||||
if (builder == null) return;
|
||||
ProtoBuf.Function functionProto = builder.build();
|
||||
|
||||
WriteAnnotationUtilKt.writeKotlinMetadata(v, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, 0, av -> {
|
||||
writeAnnotationData(av, serializer, functionProto);
|
||||
|
||||
@@ -652,7 +652,8 @@ class CoroutineCodegenForNamedFunction private constructor(
|
||||
override fun generateKotlinMetadataAnnotation() {
|
||||
writeKotlinMetadata(v, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, 0) { av ->
|
||||
val serializer = DescriptorSerializer.createForLambda(JvmSerializerExtension(v.serializationBindings, state))
|
||||
val functionProto = serializer.functionProto(createFreeFakeLambdaDescriptor(suspendFunctionJvmView)).build()
|
||||
val functionProto =
|
||||
serializer.functionProto(createFreeFakeLambdaDescriptor(suspendFunctionJvmView))?.build() ?: return@writeKotlinMetadata
|
||||
AsmUtil.writeAnnotationData(av, serializer, functionProto)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -47,6 +47,13 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
|
||||
override val metadataVersion = state.metadataVersion
|
||||
|
||||
override fun shouldUseTypeTable(): Boolean = useTypeTable
|
||||
override fun shouldSerializeFunction(descriptor: FunctionDescriptor): Boolean {
|
||||
return classBuilderMode != ClassBuilderMode.ABI || descriptor.visibility != Visibilities.PRIVATE
|
||||
}
|
||||
|
||||
override fun shouldSerializeProperty(descriptor: PropertyDescriptor): Boolean {
|
||||
return classBuilderMode != ClassBuilderMode.ABI || descriptor.visibility != Visibilities.PRIVATE
|
||||
}
|
||||
|
||||
override fun serializeClass(
|
||||
descriptor: ClassDescriptor,
|
||||
@@ -103,7 +110,7 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
|
||||
for (localVariable in localVariables) {
|
||||
val propertyDescriptor = createFreeFakeLocalPropertyDescriptor(localVariable)
|
||||
val serializer = DescriptorSerializer.createForLambda(this)
|
||||
proto.addExtension(extension, serializer.propertyProto(propertyDescriptor).build())
|
||||
proto.addExtension(extension, serializer.propertyProto(propertyDescriptor)?.build() ?: continue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-6
@@ -108,8 +108,8 @@ class DescriptorSerializer private constructor(
|
||||
if (descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue
|
||||
|
||||
when (descriptor) {
|
||||
is PropertyDescriptor -> builder.addProperty(propertyProto(descriptor))
|
||||
is FunctionDescriptor -> builder.addFunction(functionProto(descriptor))
|
||||
is PropertyDescriptor -> propertyProto(descriptor)?.let { builder.addProperty(it) }
|
||||
is FunctionDescriptor -> functionProto(descriptor)?.let { builder.addFunction(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,9 @@ class DescriptorSerializer private constructor(
|
||||
return false
|
||||
}
|
||||
|
||||
fun propertyProto(descriptor: PropertyDescriptor): ProtoBuf.Property.Builder {
|
||||
fun propertyProto(descriptor: PropertyDescriptor): ProtoBuf.Property.Builder? {
|
||||
if (!extension.shouldSerializeProperty(descriptor)) return null
|
||||
|
||||
val builder = ProtoBuf.Property.newBuilder()
|
||||
|
||||
val local = createChildSerializer(descriptor)
|
||||
@@ -281,7 +283,9 @@ class DescriptorSerializer private constructor(
|
||||
else
|
||||
descriptor.visibility
|
||||
|
||||
fun functionProto(descriptor: FunctionDescriptor): ProtoBuf.Function.Builder {
|
||||
fun functionProto(descriptor: FunctionDescriptor): ProtoBuf.Function.Builder? {
|
||||
if (!extension.shouldSerializeFunction(descriptor)) return null
|
||||
|
||||
val builder = ProtoBuf.Function.newBuilder()
|
||||
|
||||
val local = createChildSerializer(descriptor)
|
||||
@@ -638,8 +642,8 @@ class DescriptorSerializer private constructor(
|
||||
|
||||
for (declaration in sort(members)) {
|
||||
when (declaration) {
|
||||
is PropertyDescriptor -> builder.addProperty(propertyProto(declaration))
|
||||
is FunctionDescriptor -> builder.addFunction(functionProto(declaration))
|
||||
is PropertyDescriptor -> propertyProto(declaration)?.let { builder.addProperty(it) }
|
||||
is FunctionDescriptor -> functionProto(declaration)?.let { builder.addFunction(it) }
|
||||
is TypeAliasDescriptor -> builder.addTypeAlias(typeAliasProto(declaration))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ abstract class SerializerExtension {
|
||||
|
||||
open fun shouldUseTypeTable(): Boolean = false
|
||||
open fun shouldUseNormalizedVisibility(): Boolean = false
|
||||
open fun shouldSerializeFunction(descriptor: FunctionDescriptor): Boolean = true
|
||||
open fun shouldSerializeProperty(descriptor: PropertyDescriptor): Boolean = true
|
||||
|
||||
interface ClassMembersProducer {
|
||||
fun getCallableMembers(classDescriptor: ClassDescriptor): Collection<CallableMemberDescriptor>
|
||||
|
||||
@@ -19,6 +19,7 @@ dependencies {
|
||||
compile(projectTests(":j2k"))
|
||||
compile(projectTests(":idea:idea-android"))
|
||||
compile(projectTests(":jps-plugin"))
|
||||
compile(projectTests(":plugins:jvm-abi-gen"))
|
||||
compile(projectTests(":plugins:android-extensions-compiler"))
|
||||
compile(projectTests(":plugins:android-extensions-ide"))
|
||||
compile(projectTests(":plugins:android-extensions-jps"))
|
||||
|
||||
@@ -152,6 +152,7 @@ import org.jetbrains.kotlin.jps.incremental.AbstractJsProtoComparisonTest
|
||||
import org.jetbrains.kotlin.jps.incremental.AbstractJvmProtoComparisonTest
|
||||
import org.jetbrains.kotlin.kapt.cli.test.AbstractArgumentParsingTest
|
||||
import org.jetbrains.kotlin.kapt.cli.test.AbstractKaptToolIntegrationTest
|
||||
import org.jetbrains.kotlin.jvm.abi.AbstractCompareJvmAbiTest
|
||||
import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTest
|
||||
import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptContextTest
|
||||
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
|
||||
@@ -1069,6 +1070,12 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("plugins/jvm-abi-gen/test", "plugins/jvm-abi-gen/testData") {
|
||||
testClass<AbstractCompareJvmAbiTest> {
|
||||
model("compare", recursive = false, extension = null)
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("plugins/kapt3/kapt3-compiler/test", "plugins/kapt3/kapt3-compiler/testData") {
|
||||
testClass<AbstractClassFileToSourceStubConverterTest> {
|
||||
model("converter")
|
||||
|
||||
+3
-2
@@ -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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -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
|
||||
Reference in New Issue
Block a user