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:
committed by
Alexander Udalov
parent
3ffe495346
commit
5059513106
@@ -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
-5
@@ -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
|
||||
|
||||
+10
@@ -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/");
|
||||
|
||||
+10
@@ -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/");
|
||||
|
||||
Generated
+10
@@ -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/");
|
||||
|
||||
+9
@@ -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
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
private class A {
|
||||
inline fun test(crossinline s: () -> Unit) {
|
||||
object {
|
||||
fun run() {
|
||||
//s()
|
||||
}
|
||||
}.run()
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
class A {
|
||||
private class B {
|
||||
inline fun test(crossinline s: () -> Unit) {
|
||||
object {
|
||||
fun run() {
|
||||
s()
|
||||
}
|
||||
}.run()
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
// IGNORE_BACKEND_LEGACY: JVM
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
class A {
|
||||
private class B {
|
||||
inline fun test(crossinline s: () -> Unit) {
|
||||
object {
|
||||
fun run() {
|
||||
//s()
|
||||
}
|
||||
}.run()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user