From 546eea1982654d280a3004da26e5ec4fdd47ed37 Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Wed, 4 Nov 2020 21:16:03 +0100 Subject: [PATCH] IC mangling: Old JVM BE: Fallback to old mangling rules if classfile does not contain functions mangled in the new way. --- .../kotlin/codegen/ExpressionCodegen.java | 23 ++++- .../codegen/inlineClassesCodegenUtil.kt | 48 +++++++++++ .../kotlin/codegen/state/KotlinTypeMapper.kt | 7 +- .../codegen/state/inlineClassManglingUtils.kt | 86 +++++++++++++------ 4 files changed, 136 insertions(+), 28 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 72a7d575b0e..c6957d5c7bb 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2684,7 +2684,19 @@ public class ExpressionCodegen extends KtVisitor impleme fd = ArgumentGeneratorKt.getFunctionWithDefaultArguments(fd); } - return typeMapper.mapToCallableMethod(fd, superCall, null, resolvedCall); + CallableMethod method = typeMapper.mapToCallableMethod(fd, superCall, null, resolvedCall); + + if (method.getAsmMethod().getName().contains("-")) { + Boolean classFileContainsMethod = + InlineClassesCodegenUtilKt.classFileContainsMethod(fd, state, method.getAsmMethod()); + if (classFileContainsMethod != null && !classFileContainsMethod) { + typeMapper.setUseOldManglingRulesForFunctionAcceptingInlineClass(true); + method = typeMapper.mapToCallableMethod(fd, superCall, null, resolvedCall); + typeMapper.setUseOldManglingRulesForFunctionAcceptingInlineClass(false); + } + } + + return method; } public void invokeMethodWithArguments( @@ -2900,6 +2912,15 @@ public class ExpressionCodegen extends KtVisitor impleme sourceCompiler.initializeInlineFunctionContext(functionDescriptor); JvmMethodSignature signature = typeMapper.mapSignatureWithGeneric(functionDescriptor, sourceCompiler.getContextKind()); + if (signature.getAsmMethod().getName().contains("-")) { + Boolean classFileContainsMethod = + InlineClassesCodegenUtilKt.classFileContainsMethod(functionDescriptor, state, signature.getAsmMethod()); + if (classFileContainsMethod != null && !classFileContainsMethod) { + typeMapper.setUseOldManglingRulesForFunctionAcceptingInlineClass(true); + signature = typeMapper.mapSignatureWithGeneric(functionDescriptor, sourceCompiler.getContextKind()); + typeMapper.setUseOldManglingRulesForFunctionAcceptingInlineClass(false); + } + } Type methodOwner = typeMapper.mapImplementationOwner(functionDescriptor); if (isDefaultCompilation) { return new InlineCodegenForDefaultBody(functionDescriptor, this, state, methodOwner, signature, sourceCompiler); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inlineClassesCodegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inlineClassesCodegenUtil.kt index cb76c239864..bb3a5ddb850 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inlineClassesCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inlineClassesCodegenUtil.kt @@ -5,15 +5,28 @@ package org.jetbrains.kotlin.codegen +import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor +import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource +import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.isInlineClassType import org.jetbrains.kotlin.resolve.underlyingRepresentation +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import org.jetbrains.kotlin.types.typeUtil.isTypeParameter +import org.jetbrains.org.objectweb.asm.ClassReader +import org.jetbrains.org.objectweb.asm.ClassVisitor +import org.jetbrains.org.objectweb.asm.MethodVisitor +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.commons.Method fun KotlinType.isInlineClassWithUnderlyingTypeAnyOrAnyN(): Boolean { if (!isInlineClassType()) return false @@ -26,4 +39,39 @@ fun CallableDescriptor.isGenericParameter(): Boolean { if (containingDeclaration is AnonymousFunctionDescriptor) return true val index = containingDeclaration.valueParameters.indexOf(this) return containingDeclaration.overriddenDescriptors.any { it.original.valueParameters[index].type.isTypeParameter() } +} + +fun classFileContainsMethod(descriptor: FunctionDescriptor, state: GenerationState, method: Method): Boolean? { + if (descriptor !is DeserializedSimpleFunctionDescriptor) return null + + val classId: ClassId = when { + descriptor.containingDeclaration is DeserializedClassDescriptor -> { + (descriptor.containingDeclaration as DeserializedClassDescriptor).classId ?: return null + } + descriptor.containerSource is JvmPackagePartSource -> { + (descriptor.containerSource as JvmPackagePartSource).classId + } + else -> { + return null + } + } + + val bytes = VirtualFileFinder.getInstance(state.project, state.module).findVirtualFileWithHeader(classId) + ?.contentsToByteArray() ?: return null + var found = false + ClassReader(bytes).accept(object : ClassVisitor(Opcodes.API_VERSION) { + override fun visitMethod( + access: Int, + name: String?, + descriptor: String?, + signature: String?, + exceptions: Array? + ): MethodVisitor? { + if (name == method.name && descriptor == method.descriptor) { + found = true + } + return super.visitMethod(access, name, descriptor, signature, exceptions) + } + }, ClassReader.SKIP_FRAMES) + return found } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt index defc6208da7..d2eb9630794 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt @@ -91,6 +91,7 @@ class KotlinTypeMapper @JvmOverloads constructor( ) : KotlinTypeMapperBase() { private val isReleaseCoroutines = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines) val jvmDefaultMode = languageVersionSettings.getFlag(JvmAnalysisFlags.jvmDefaultMode) + var useOldManglingRulesForFunctionAcceptingInlineClass: Boolean = false private val typeMappingConfiguration = object : TypeMappingConfiguration { override fun commonSupertype(types: Collection): KotlinType { @@ -636,7 +637,11 @@ class KotlinTypeMapper @JvmOverloads constructor( // so that we don't have to repeat the same logic in reflection // in case of properties without getter methods. if (kind !== OwnerKind.PROPERTY_REFERENCE_SIGNATURE || descriptor.isPropertyWithGetterSignaturePresent()) { - val suffix = getManglingSuffixBasedOnKotlinSignature(descriptor, shouldMangleByReturnType) + val suffix = getManglingSuffixBasedOnKotlinSignature( + descriptor, + shouldMangleByReturnType, + useOldManglingRulesForFunctionAcceptingInlineClass + ) if (suffix != null) { newName += suffix } else if (kind === OwnerKind.ERASED_INLINE_CLASS) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/inlineClassManglingUtils.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/inlineClassManglingUtils.kt index 79a2974981b..d03f5e6d275 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/inlineClassManglingUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/inlineClassManglingUtils.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.codegen.state -import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -14,14 +13,14 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForParameterTypes import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound import java.security.MessageDigest import java.util.* fun getManglingSuffixBasedOnKotlinSignature( descriptor: CallableMemberDescriptor, - shouldMangleByReturnType: Boolean + shouldMangleByReturnType: Boolean, + useOldManglingRules: Boolean ): String? { if (descriptor !is FunctionDescriptor) return null if (descriptor is ConstructorDescriptor) return null @@ -33,41 +32,76 @@ fun getManglingSuffixBasedOnKotlinSignature( val unwrappedDescriptor = descriptor.unwrapInitialDescriptorForSuspendFunction() - // If a function accepts inline class parameters, mangle its name. - if (requiresFunctionNameManglingForParameterTypes(descriptor) || - (shouldMangleByReturnType && requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) - ) { + if (useOldManglingRules) { + if (requiresFunctionNameManglingForParameterTypes(descriptor)) { + return "-" + md5base64(collectSignatureForMangling(descriptor, useOldManglingRules)) + } + // If a class member function returns inline class value, mangle its name. // NB here function can be a suspend function JVM view with return type replaced with 'Any', // should unwrap it and take original return type instead. - val signature = collectSignatureForMangling(descriptor) + - if (shouldMangleByReturnType && requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) - ":" + getSignatureElementForMangling(unwrappedDescriptor.returnType!!) - else "" - return "-" + md5base64(signature) + if (shouldMangleByReturnType) { + if (requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) { + return "-" + md5base64( + ":" + getSignatureElementForMangling( + unwrappedDescriptor.returnType!!, useOldManglingRules + ) + ) + } + } + } else { + // If a function accepts inline class parameters, mangle its name. + if (requiresFunctionNameManglingForParameterTypes(descriptor) || + (shouldMangleByReturnType && requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) + ) { + // If a class member function returns inline class value, mangle its name. + // NB here function can be a suspend function JVM view with return type replaced with 'Any', + // should unwrap it and take original return type instead. + val signature = collectSignatureForMangling(descriptor, useOldManglingRules) + + if (shouldMangleByReturnType && requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) + ":" + getSignatureElementForMangling(unwrappedDescriptor.returnType!!, useOldManglingRules) + else "" + return "-" + md5base64(signature) + } } + return null } -private fun collectSignatureForMangling(descriptor: CallableMemberDescriptor): String { +private fun collectSignatureForMangling(descriptor: CallableMemberDescriptor, useOldManglingRules: Boolean): String { val types = listOfNotNull(descriptor.extensionReceiverParameter?.type) + descriptor.valueParameters.map { it.type } - return types.joinToString { getSignatureElementForMangling(it) } + return types.joinToString { getSignatureElementForMangling(it, useOldManglingRules) } } -private fun getSignatureElementForMangling(type: KotlinType): String = buildString { +private fun getSignatureElementForMangling(type: KotlinType, useOldManglingRules: Boolean): String = buildString { val descriptor = type.constructor.declarationDescriptor ?: return "" - when (descriptor) { - is ClassDescriptor -> if (descriptor.isInline) { - append('L') - append(descriptor.fqNameUnsafe) - if (type.isMarkedNullable) append('?') - append(';') - } else { - append('x') - } + if (useOldManglingRules) { + when (descriptor) { + is ClassDescriptor -> { + append('L') + append(descriptor.fqNameUnsafe) + if (type.isMarkedNullable) append('?') + append(';') + } - is TypeParameterDescriptor -> { - append(getSignatureElementForMangling(descriptor.representativeUpperBound)) + is TypeParameterDescriptor -> { + append(getSignatureElementForMangling(descriptor.representativeUpperBound, useOldManglingRules)) + } + } + } else { + when (descriptor) { + is ClassDescriptor -> if (descriptor.isInline) { + append('L') + append(descriptor.fqNameUnsafe) + if (type.isMarkedNullable) append('?') + append(';') + } else { + append('x') + } + + is TypeParameterDescriptor -> { + append(getSignatureElementForMangling(descriptor.representativeUpperBound, useOldManglingRules)) + } } } }