Check descriptor visibility before excluding method from ABI class

This commit is contained in:
Alexey Tsvetkov
2018-12-11 22:41:58 +03:00
parent 34d96dff28
commit 495cfec9e9
4 changed files with 34 additions and 2 deletions
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.jvm.abi.asm
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.AbstractClassBuilder
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.*
@@ -22,10 +24,15 @@ internal class AbiClassBuilder(private val cv: ClassVisitor) : AbstractClassBuil
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
if (isPrivate(access) || isClinit(name, access)) return EMPTY_METHOD_VISITOR
// if both descriptor's and access's visibilities are private, we can generate an empty method
// 1. we need to check a descriptor, because inline reified functions
// might have a non-private visibility in ABI, but they are private in bytecode
// 2. we need to check an access, because synthetic methods
// for default parameters have private visibility, but public in bytecode
val descriptor = origin.descriptor as? MemberDescriptor
if (isPrivate(access) && descriptor != null && isPrivate(descriptor) || isClinit(name, access)) return EMPTY_METHOD_VISITOR
val mv = super.newMethod(origin, access, name, desc, signature, exceptions)
val descriptor = origin.descriptor
// inline function bodies are part of ABI,
// but non-inline functions can be thrown out
if (descriptor is FunctionDescriptor && descriptor.isInline) return mv
@@ -67,6 +74,9 @@ internal class AbiClassBuilder(private val cv: ClassVisitor) : AbstractClassBuil
super.defineClass(origin, version, access, name, signature, superName, interfaces)
}
private fun isPrivate(descriptor: MemberDescriptor): Boolean =
descriptor.visibility == Visibilities.PRIVATE
private fun isPrivate(access: Int): Boolean =
(access and Opcodes.ACC_PRIVATE) == Opcodes.ACC_PRIVATE
@@ -44,6 +44,11 @@ public class CompileAgainstJvmAbiTestGenerated extends AbstractCompileAgainstJvm
runTest("plugins/jvm-abi-gen/testData/compile/clinit/");
}
@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/");
@@ -0,0 +1,13 @@
package app
import lib.*
fun runAppAndReturnOk(): String {
val a = safeCall<Int>(10) { it * it }
if (a != 100) error("a is '$a', but is expected to be '100'")
val b = safeCall<Int>(null) { it * it }
if (b != null) error("b is '$b', but is expected to be 'null'")
return "OK"
}
@@ -0,0 +1,4 @@
package lib
inline fun <reified T> safeCall(x: Any?, fn: (T) -> T): T? =
if (x is T) fn(x) else null