jvm-abi-gen: Strip inline functions in private classes

Inline functions in private classes can only be accessed from Java and
we do not need to preserve their bodies for the abi.
This commit is contained in:
Steven Schäfer
2021-08-06 10:27:13 +02:00
committed by Alexander Udalov
parent 3ffe495346
commit 5059513106
12 changed files with 111 additions and 12 deletions
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMapping;
@@ -78,8 +79,11 @@ public class InlineUtil {
if (descriptor == null) return false;
if (isInline(descriptor) && descriptor instanceof DeclarationDescriptorWithVisibility) {
DescriptorVisibility visibility = ((DeclarationDescriptorWithVisibility) descriptor).getVisibility();
if (!DescriptorVisibilities.isPrivate(visibility))
return true;
if (!DescriptorVisibilities.isPrivate(visibility)) {
ClassDescriptor containingClass = DescriptorUtils.getContainingClass(descriptor);
if (containingClass == null || !DescriptorVisibilities.isPrivate(containingClass.getVisibility()))
return true;
}
}
return isInPublicInlineScope(descriptor.getContainingDeclaration());
}
@@ -6,14 +6,20 @@
package org.jetbrains.kotlin.backend.jvm.ir
import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.CachedFieldsForObjectInstances
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.JvmSymbols
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineOnly
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.backend.jvm.codegen.representativeUpperBound
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
@@ -426,15 +432,25 @@ private val IrDeclaration.original: IrDeclaration
// Declarations in the scope of an externally visible inline function are implicitly part of the
// public ABI of a Kotlin module. This function returns the visibility of a containing inline function
// (determined *before* lowering), or null if the given declaration is not in the scope of an inline function.
//
// Currently, we mark all declarations in the scope of a public inline function as public, even if they are
// contained in a nested private inline function. This is an over approximation, since private declarations
// inside of a public inline function can still escape if they are used without being regenerated.
// See `plugins/jvm-abi-gen/testData/compile/inlineNoRegeneration` for an example.
val IrDeclaration.inlineScopeVisibility: DescriptorVisibility?
get() {
var owner: IrDeclaration? = original
var result: DescriptorVisibility? = null
while (owner != null) {
if (owner is IrFunction && owner.isInline) {
if (!DescriptorVisibilities.isPrivate(owner.visibility))
return owner.visibility
result = owner.visibility
result = if (!DescriptorVisibilities.isPrivate(owner.visibility)) {
if (owner.parentClassOrNull?.visibility?.let(DescriptorVisibilities::isPrivate) == true)
DescriptorVisibilities.PRIVATE
else
return owner.visibility
} else {
owner.visibility
}
}
owner = owner.parent.safeAs<IrDeclaration>()?.original
}
@@ -12,6 +12,7 @@ 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.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.MemberDescriptor
@@ -65,11 +66,17 @@ class JvmAbiClassBuilderInterceptor : ClassBuilderInterceptorExtension {
override fun interceptClassBuilderFactory(interceptedFactory: ClassBuilderFactory, bindingContext: BindingContext, diagnostics: DiagnosticSink): ClassBuilderFactory =
object : DelegatingClassBuilderFactory(interceptedFactory) {
override fun newClassBuilder(origin: JvmDeclarationOrigin): DelegatingClassBuilder =
AbiInfoClassBuilder(interceptedFactory.newClassBuilder(origin))
override fun newClassBuilder(origin: JvmDeclarationOrigin): DelegatingClassBuilder {
val descriptor = origin.descriptor as? ClassDescriptor
val isPrivate = descriptor?.visibility?.let(DescriptorVisibilities::isPrivate) ?: false
return AbiInfoClassBuilder(interceptedFactory.newClassBuilder(origin), isPrivate)
}
}
private inner class AbiInfoClassBuilder(private val delegate: ClassBuilder) : DelegatingClassBuilder() {
private inner class AbiInfoClassBuilder(
private val delegate: ClassBuilder,
private val isPrivateClass: Boolean
) : DelegatingClassBuilder() {
lateinit var internalName: String
var localOrAnonymousClass = false
var publicAbi = false
@@ -104,7 +111,7 @@ class JvmAbiClassBuilderInterceptor : ClassBuilderInterceptorExtension {
// 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)) {
if (name.endsWith(FOR_INLINE_SUFFIX) && !isPrivateClass) {
// 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)
@@ -119,7 +126,7 @@ class JvmAbiClassBuilderInterceptor : ClassBuilderInterceptorExtension {
}
// Copy inline functions verbatim
if (origin.descriptor?.safeAs<FunctionDescriptor>()?.isInline == true) {
if (origin.descriptor?.safeAs<FunctionDescriptor>()?.isInline == true && !isPrivateClass) {
methodInfos[Method(name, desc)] = AbiMethodInfo.KEEP
} else {
methodInfos[Method(name, desc)] = AbiMethodInfo.STRIP
@@ -85,6 +85,16 @@ public class CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest {
runTest("plugins/jvm-abi-gen/testData/compare/functionBody/");
}
@TestMetadata("inlineFunInPrivateClass")
public void testInlineFunInPrivateClass() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/inlineFunInPrivateClass/");
}
@TestMetadata("inlineFunInPrivateNestedClass")
public void testInlineFunInPrivateNestedClass() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/inlineFunInPrivateNestedClass/");
}
@TestMetadata("inlineFunctionBody")
public void testInlineFunctionBody() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/inlineFunctionBody/");
@@ -85,6 +85,16 @@ public class IrCompareJvmAbiTestGenerated extends AbstractIrCompareJvmAbiTest {
runTest("plugins/jvm-abi-gen/testData/compare/functionBody/");
}
@TestMetadata("inlineFunInPrivateClass")
public void testInlineFunInPrivateClass() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/inlineFunInPrivateClass/");
}
@TestMetadata("inlineFunInPrivateNestedClass")
public void testInlineFunInPrivateNestedClass() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/inlineFunInPrivateNestedClass/");
}
@TestMetadata("inlineFunctionBody")
public void testInlineFunctionBody() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/inlineFunctionBody/");
@@ -85,6 +85,16 @@ public class LegacyCompareJvmAbiTestGenerated extends AbstractLegacyCompareJvmAb
runTest("plugins/jvm-abi-gen/testData/compare/functionBody/");
}
@TestMetadata("inlineFunInPrivateClass")
public void testInlineFunInPrivateClass() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/inlineFunInPrivateClass/");
}
@TestMetadata("inlineFunInPrivateNestedClass")
public void testInlineFunInPrivateNestedClass() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/inlineFunInPrivateNestedClass/");
}
@TestMetadata("inlineFunctionBody")
public void testInlineFunctionBody() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/inlineFunctionBody/");
@@ -0,0 +1,9 @@
private class A {
inline fun test(crossinline s: () -> Unit) {
object {
fun run() {
s()
}
}.run()
}
}
@@ -0,0 +1 @@
// IGNORE_BACKEND_LEGACY: JVM
@@ -0,0 +1,9 @@
private class A {
inline fun test(crossinline s: () -> Unit) {
object {
fun run() {
//s()
}
}.run()
}
}
@@ -0,0 +1,11 @@
class A {
private class B {
inline fun test(crossinline s: () -> Unit) {
object {
fun run() {
s()
}
}.run()
}
}
}
@@ -0,0 +1 @@
// IGNORE_BACKEND_LEGACY: JVM
@@ -0,0 +1,11 @@
class A {
private class B {
inline fun test(crossinline s: () -> Unit) {
object {
fun run() {
//s()
}
}.run()
}
}
}