From f0282bcfdfeefd01a1ae4d4038c14a9fda8bb67d Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 11 Sep 2020 17:02:25 +0200 Subject: [PATCH] JVM IR: add module name for internal functions before "$default" The change is a bit complicated because the name mapping logic is refactored so that we would compute the needed suffix first (either module name, or multifile part class name), and then shove it into the mangled name between the original function name and "$default", if the function in question is the default arguments adapter. The main motivation for this change was to fix KT-41809, but as a side effect, private functions with default arguments in multifile parts now also contain the file name, just like normal private functions. #KT-41809 Fixed --- .../jvm/codegen/MethodSignatureMapper.kt | 41 ++++++++++++------- .../defaultArguments/internalNameMangling.kt | 12 ++++++ .../defaultArguments/internalNameMangling.txt | 13 ++++++ .../privateFunctionInMultifilePart.kt | 7 ++++ .../privateFunctionInMultifilePart.txt | 11 +++++ .../privateFunctionInMultifilePart_ir.txt | 13 ++++++ .../codegen/BytecodeListingTestGenerated.java | 23 +++++++++++ .../ir/IrBytecodeListingTestGenerated.java | 23 +++++++++++ 8 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeListing/defaultArguments/internalNameMangling.kt create mode 100644 compiler/testData/codegen/bytecodeListing/defaultArguments/internalNameMangling.txt create mode 100644 compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart.kt create mode 100644 compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart.txt create mode 100644 compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart_ir.txt diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt index 8f598f7f399..1fd565e47e1 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt @@ -23,7 +23,6 @@ import org.jetbrains.kotlin.codegen.replaceValueParametersIn import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter import org.jetbrains.kotlin.codegen.state.JVM_SUPPRESS_WILDCARDS_ANNOTATION_FQ_NAME -import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.codegen.state.extractTypeMappingModeFromAnnotation import org.jetbrains.kotlin.codegen.state.isMethodWithDeclarationSiteWildcardsFqName import org.jetbrains.kotlin.descriptors.DescriptorVisibilities @@ -45,6 +44,7 @@ import org.jetbrains.kotlin.load.kotlin.signatures import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil +import org.jetbrains.kotlin.name.NameUtils import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature @@ -108,25 +108,38 @@ class MethodSignatureMapper(private val context: JvmBackendContext) { private fun mangleMemberNameIfRequired(name: String, function: IrSimpleFunction): String { val newName = JvmCodegenUtil.sanitizeNameIfNeeded(name, context.state.languageVersionSettings) - if (function.isTopLevel) { - if (DescriptorVisibilities.isPrivate(function.suspendFunctionOriginal().visibility) && - newName != "" && (function.parent as? IrClass)?.attributeOwnerId in context.multifileFacadeForPart - ) { - return "$newName$${function.parentAsClass.name.asString()}" - } - return newName + val suffix = when { + function.isTopLevel -> + if (function.isInvisibleInMultifilePart()) function.parentAsClass.name.asString() else null + function.shouldMangleAsInternal() -> + NameUtils.sanitizeAsJavaIdentifier(getModuleName(function)) + else -> null + } ?: return newName + + if (function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) { + assert(newName.endsWith(JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX)) { "Default adapter should end with \$default: ${function.render()}" } + return newName.substringBeforeLast(JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX) + "$" + suffix + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX } - return if (function.shouldMangleAsInternal()) - KotlinTypeMapper.InternalNameMapper.mangleInternalName(newName, getModuleName(function)) - else - newName + return "$newName$$suffix" } + private fun IrSimpleFunction.isInvisibleInMultifilePart(): Boolean = + name.asString() != "" && + (parent as? IrClass)?.attributeOwnerId in context.multifileFacadeForPart && + (DescriptorVisibilities.isPrivate(suspendFunctionOriginal().visibility) || + originalForDefaultAdapter?.isInvisibleInMultifilePart() == true) + private fun IrSimpleFunction.shouldMangleAsInternal(): Boolean = - origin != JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR && + (origin != JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR && visibility == DescriptorVisibilities.INTERNAL && - !isPublishedApi() + !isPublishedApi()) + || originalForDefaultAdapter?.shouldMangleAsInternal() == true + + private val IrSimpleFunction.originalForDefaultAdapter: IrSimpleFunction? + get() = if (origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) { + ((body?.statements?.lastOrNull() as? IrReturn)?.value as? IrCall)?.symbol?.owner + } else null private fun getModuleName(function: IrSimpleFunction): String = (if (function is IrLazyFunction) diff --git a/compiler/testData/codegen/bytecodeListing/defaultArguments/internalNameMangling.kt b/compiler/testData/codegen/bytecodeListing/defaultArguments/internalNameMangling.kt new file mode 100644 index 00000000000..8713c0b1785 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/defaultArguments/internalNameMangling.kt @@ -0,0 +1,12 @@ +// WITH_RUNTIME + +package test + +class A internal constructor(a: Int = 0) { + internal fun internalFunction(b: String = "") {} + + @JvmName("internalJvmNameFunction") + internal fun f(c: String = "") {} + + public fun publicFunction(d: String = "") {} +} diff --git a/compiler/testData/codegen/bytecodeListing/defaultArguments/internalNameMangling.txt b/compiler/testData/codegen/bytecodeListing/defaultArguments/internalNameMangling.txt new file mode 100644 index 00000000000..88b8e9a6386 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/defaultArguments/internalNameMangling.txt @@ -0,0 +1,13 @@ +@kotlin.Metadata +public final class test/A { + // source: 'internalNameMangling.kt' + public method (): void + public method (p0: int): void + public synthetic method (p0: int, p1: int, p2: kotlin.jvm.internal.DefaultConstructorMarker): void + public synthetic static method internalFunction$test_module$default(p0: test.A, p1: java.lang.String, p2: int, p3: java.lang.Object): void + public final method internalFunction$test_module(@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public synthetic static method internalJvmNameFunction$default(p0: test.A, p1: java.lang.String, p2: int, p3: java.lang.Object): void + public final @kotlin.jvm.JvmName method internalJvmNameFunction(@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public synthetic static method publicFunction$default(p0: test.A, p1: java.lang.String, p2: int, p3: java.lang.Object): void + public final method publicFunction(@org.jetbrains.annotations.NotNull p0: java.lang.String): void +} diff --git a/compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart.kt b/compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart.kt new file mode 100644 index 00000000000..09df7d16058 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// The remaining differences of JVM and JVM_IR here are reported at KT-36970, KT-41841. + +@file:JvmMultifileClass +@file:JvmName("A") + +private fun foo(x: String = "") {} diff --git a/compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart.txt b/compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart.txt new file mode 100644 index 00000000000..ed08c63abd7 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart.txt @@ -0,0 +1,11 @@ +@kotlin.Metadata +public final class A { + // source: 'privateFunctionInMultifilePart.kt' +} + +@kotlin.Metadata +synthetic final class A__PrivateFunctionInMultifilePartKt { + // source: 'privateFunctionInMultifilePart.kt' + synthetic static method foo$A__PrivateFunctionInMultifilePartKt$default(p0: java.lang.String, p1: int, p2: java.lang.Object): void + private final static method foo$A__PrivateFunctionInMultifilePartKt(p0: java.lang.String): void +} diff --git a/compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart_ir.txt b/compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart_ir.txt new file mode 100644 index 00000000000..60fbfc17c29 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart_ir.txt @@ -0,0 +1,13 @@ +@kotlin.Metadata +public final class A { + // source: 'privateFunctionInMultifilePart.kt' + final static method foo$A__PrivateFunctionInMultifilePartKt$default(@org.jetbrains.annotations.Nullable p0: java.lang.String, p1: int, p2: java.lang.Object): void +} + +@kotlin.Metadata +@kotlin.jvm.JvmName +synthetic final class A__PrivateFunctionInMultifilePartKt { + // source: 'privateFunctionInMultifilePart.kt' + synthetic static method foo$A__PrivateFunctionInMultifilePartKt$default(p0: java.lang.String, p1: int, p2: java.lang.Object): void + private final static method foo$A__PrivateFunctionInMultifilePartKt(p0: java.lang.String): void +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 488ff8bf96b..193d2dcc923 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -375,6 +375,29 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeListing/defaultArguments") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DefaultArguments extends AbstractBytecodeListingTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInDefaultArguments() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/defaultArguments"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @TestMetadata("internalNameMangling.kt") + public void testInternalNameMangling() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/defaultArguments/internalNameMangling.kt"); + } + + @TestMetadata("privateFunctionInMultifilePart.kt") + public void testPrivateFunctionInMultifilePart() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart.kt"); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeListing/deprecated") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java index 43f6da344cf..093a1170e5c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java @@ -345,6 +345,29 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes } } + @TestMetadata("compiler/testData/codegen/bytecodeListing/defaultArguments") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DefaultArguments extends AbstractIrBytecodeListingTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInDefaultArguments() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/defaultArguments"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("internalNameMangling.kt") + public void testInternalNameMangling() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/defaultArguments/internalNameMangling.kt"); + } + + @TestMetadata("privateFunctionInMultifilePart.kt") + public void testPrivateFunctionInMultifilePart() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/defaultArguments/privateFunctionInMultifilePart.kt"); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeListing/deprecated") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)