Mangle inline class members

<IMPL_SUFFIX> for method is a method signature hash,
if method value parameter types contain inline class types,
otherwise 'impl'.

Constructor methods are named as 'constructor-<IMPL_SUFFIX>'.

Synthesized 'box' and 'unbox' methods are named as
'<METHOD_NAME>-<IMPL_SUFFIX>'.

Erased implementations of overriding and non-overriding methods
are named as '<METHOD_NAME>-<IMPL_SUFFIX>'.

Fully specialized implementation of 'equals' will have a special suffix.
This commit is contained in:
Dmitry Petrov
2018-09-06 10:12:50 +03:00
parent aec173ac5c
commit cafaa3e13c
40 changed files with 398 additions and 184 deletions
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
@@ -74,7 +75,8 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
) {
MethodContext context = fieldOwnerContext.intoFunction(function);
JvmDeclarationOrigin methodOrigin = JvmDeclarationOriginKt.OtherOrigin(function);
MethodVisitor mv = v.newMethod(methodOrigin, getAccess(), "toString", getToStringDesc(), null, null);
String toStringMethodName = mapFunctionName(function);
MethodVisitor mv = v.newMethod(methodOrigin, getAccess(), toStringMethodName, getToStringDesc(), null, null);
if (fieldOwnerContext.getContextKind() != OwnerKind.ERASED_INLINE_CLASS && classDescriptor.isInline()) {
FunctionCodegen.generateMethodInsideInlineClassWrapper(methodOrigin, function, classDescriptor, mv, typeMapper);
@@ -84,7 +86,7 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
mv.visitAnnotation(Type.getDescriptor(NotNull.class), false);
if (!generationState.getClassBuilderMode().generateBodies) {
FunctionCodegen.endVisit(mv, "toString", getDeclaration());
FunctionCodegen.endVisit(mv, toStringMethodName, getDeclaration());
return;
}
@@ -129,7 +131,7 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
iv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
iv.areturn(JAVA_STRING_TYPE);
FunctionCodegen.endVisit(mv, "toString", getDeclaration());
FunctionCodegen.endVisit(mv, toStringMethodName, getDeclaration());
}
@Override
@@ -138,7 +140,8 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
) {
MethodContext context = fieldOwnerContext.intoFunction(function);
JvmDeclarationOrigin methodOrigin = JvmDeclarationOriginKt.OtherOrigin(function);
MethodVisitor mv = v.newMethod(methodOrigin, getAccess(), "hashCode", getHashCodeDesc(), null, null);
String hashCodeMethodName = mapFunctionName(function);
MethodVisitor mv = v.newMethod(methodOrigin, getAccess(), hashCodeMethodName, getHashCodeDesc(), null, null);
if (fieldOwnerContext.getContextKind() != OwnerKind.ERASED_INLINE_CLASS && classDescriptor.isInline()) {
FunctionCodegen.generateMethodInsideInlineClassWrapper(methodOrigin, function, classDescriptor, mv, typeMapper);
@@ -146,7 +149,7 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
}
if (!generationState.getClassBuilderMode().generateBodies) {
FunctionCodegen.endVisit(mv, "hashCode", getDeclaration());
FunctionCodegen.endVisit(mv, hashCodeMethodName, getDeclaration());
return;
}
@@ -193,8 +196,11 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
mv.visitInsn(IRETURN);
FunctionCodegen.endVisit(mv, "hashCode", getDeclaration());
FunctionCodegen.endVisit(mv, hashCodeMethodName, getDeclaration());
}
private String mapFunctionName(@NotNull FunctionDescriptor functionDescriptor) {
return typeMapper.mapFunctionName(functionDescriptor, fieldOwnerContext.getContextKind());
}
@Override
@@ -203,7 +209,8 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
) {
MethodContext context = fieldOwnerContext.intoFunction(function);
JvmDeclarationOrigin methodOrigin = JvmDeclarationOriginKt.OtherOrigin(function);
MethodVisitor mv = v.newMethod(methodOrigin, getAccess(), "equals", getEqualsDesc(), null, null);
String equalsMethodName = mapFunctionName(function);
MethodVisitor mv = v.newMethod(methodOrigin, getAccess(), equalsMethodName, getEqualsDesc(), null, null);
boolean isErasedInlineClassKind = fieldOwnerContext.getContextKind() == OwnerKind.ERASED_INLINE_CLASS;
if (!isErasedInlineClassKind && classDescriptor.isInline()) {
@@ -214,7 +221,7 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
mv.visitParameterAnnotation(isErasedInlineClassKind ? 1 : 0, Type.getDescriptor(Nullable.class), false);
if (!generationState.getClassBuilderMode().generateBodies) {
FunctionCodegen.endVisit(mv, "equals", getDeclaration());
FunctionCodegen.endVisit(mv, equalsMethodName, getDeclaration());
return;
}
@@ -261,7 +268,7 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
iv.iconst(0);
iv.areturn(Type.INT_TYPE);
FunctionCodegen.endVisit(mv, "equals", getDeclaration());
FunctionCodegen.endVisit(mv, equalsMethodName, getDeclaration());
}
private int generateBasicChecksAndStoreTarget(InstructionAdapter iv, Label eq, Label ne) {
@@ -414,12 +414,12 @@ public abstract class StackValue {
private static void invokeBoxMethod(
@NotNull InstructionAdapter v,
Type boxedType,
Type underlyingType
@NotNull Type boxedType,
@NotNull Type underlyingType
) {
v.invokestatic(
boxedType.getInternalName(),
InlineClassDescriptorResolver.BOX_METHOD_NAME.asString(),
KotlinTypeMapper.BOX_JVM_METHOD_NAME,
Type.getMethodDescriptor(boxedType, underlyingType),
false
);
@@ -440,10 +440,14 @@ public abstract class StackValue {
}
}
private static void invokeUnboxMethod(@NotNull InstructionAdapter v, Type owner, Type resultType) {
private static void invokeUnboxMethod(
@NotNull InstructionAdapter v,
@NotNull Type owner,
@NotNull Type resultType
) {
v.invokevirtual(
owner.getInternalName(),
InlineClassDescriptorResolver.UNBOX_METHOD_NAME.asString(),
KotlinTypeMapper.UNBOX_JVM_METHOD_NAME,
"()" + resultType.getDescriptor(),
false
);
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.codegen.isRangeOrProgression
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
@@ -236,7 +236,7 @@ private fun AbstractInsnNode.isInlineClassUnboxing(state: GenerationState) =
}
private fun MethodInsnNode.isInlineClassBoxingMethodDescriptor(state: GenerationState): Boolean {
if (name != InlineClassDescriptorResolver.BOX_METHOD_NAME.asString()) return false
if (name != KotlinTypeMapper.BOX_JVM_METHOD_NAME) return false
val ownerType = Type.getObjectType(owner)
val descriptor = state.jvmBackendClassResolver.resolveToClassDescriptors(ownerType).singleOrNull() ?: return false
@@ -247,7 +247,7 @@ private fun MethodInsnNode.isInlineClassBoxingMethodDescriptor(state: Generation
}
private fun MethodInsnNode.isInlineClassUnboxingMethodDescriptor(state: GenerationState): Boolean {
if (name != InlineClassDescriptorResolver.UNBOX_METHOD_NAME.asString()) return false
if (name != KotlinTypeMapper.UNBOX_JVM_METHOD_NAME) return false
val ownerType = Type.getObjectType(owner)
val descriptor = state.jvmBackendClassResolver.resolveToClassDescriptors(ownerType).singleOrNull() ?: return false
@@ -1010,7 +1010,7 @@ public class KotlinTypeMapper {
? JvmAbi.getterName(propertyName)
: JvmAbi.setterName(propertyName);
return mangleMemberNameIfRequired(isAccessor ? "access$" + accessorName : accessorName, descriptor);
return mangleMemberNameIfRequired(isAccessor ? "access$" + accessorName : accessorName, descriptor, kind);
}
else if (isFunctionLiteral(descriptor)) {
PsiElement element = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor);
@@ -1029,11 +1029,8 @@ public class KotlinTypeMapper {
else if (isLocalFunction(descriptor) || isFunctionExpression(descriptor)) {
return OperatorNameConventions.INVOKE.asString();
}
else if (OwnerKind.ERASED_INLINE_CLASS == kind && descriptor instanceof ConstructorDescriptor) {
return JvmAbi.ERASED_INLINE_CONSTRUCTOR_NAME;
}
else {
return mangleMemberNameIfRequired(descriptor.getName().asString(), descriptor);
return mangleMemberNameIfRequired(descriptor.getName().asString(), descriptor, kind);
}
}
@@ -1062,15 +1059,46 @@ public class KotlinTypeMapper {
}
@NotNull
private String mangleMemberNameIfRequired(@NotNull String name, @NotNull CallableMemberDescriptor descriptor) {
if (descriptor.getContainingDeclaration() instanceof ScriptDescriptor) {
private String mangleMemberNameIfRequired(
@NotNull String name,
@NotNull CallableMemberDescriptor descriptor,
@Nullable OwnerKind kind
) {
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
if (containingDeclaration instanceof ScriptDescriptor && descriptor instanceof PropertyDescriptor) {
//script properties should be public
return name;
}
String manglingSuffix = InlineClassManglingUtilsKt.getInlineClassValueParametersManglingSuffix(descriptor);
if (manglingSuffix != null) {
name += "-" + manglingSuffix;
// Special methods for inline classes.
if (InlineClassDescriptorResolver.isSynthesizedBoxMethod(descriptor)) {
return BOX_JVM_METHOD_NAME;
}
if (InlineClassDescriptorResolver.isSynthesizedUnboxMethod(descriptor)) {
return UNBOX_JVM_METHOD_NAME;
}
if (InlineClassDescriptorResolver.isSpecializedEqualsMethod(descriptor)) {
return name;
}
// Constructor:
// either a constructor method for inline class (should be mangled),
// or should stay as it is ('<init>').
if (descriptor instanceof ConstructorDescriptor) {
if (kind == OwnerKind.ERASED_INLINE_CLASS) {
name = JvmAbi.ERASED_INLINE_CONSTRUCTOR_NAME;
}
else {
return name;
}
}
String suffix = InlineClassManglingUtilsKt.getInlineClassSignatureManglingSuffix(descriptor);
if (suffix != null) {
name += suffix;
}
else if (kind == OwnerKind.ERASED_INLINE_CLASS) {
name += JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS;
}
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
@@ -1090,6 +1118,12 @@ public class KotlinTypeMapper {
return name;
}
public static final String BOX_JVM_METHOD_NAME =
InlineClassDescriptorResolver.BOX_METHOD_NAME + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS;
public static final String UNBOX_JVM_METHOD_NAME =
InlineClassDescriptorResolver.UNBOX_METHOD_NAME + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS;
@NotNull
private String getModuleName(@NotNull CallableMemberDescriptor descriptor) {
String deserialized = ModuleNameKt.getJvmModuleNameForDeserializedDescriptor(descriptor);
@@ -15,19 +15,24 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.isInlineClassType
import org.jetbrains.kotlin.types.KotlinType
import java.security.MessageDigest
import java.util.*
fun getInlineClassValueParametersManglingSuffix(descriptor: CallableMemberDescriptor): String? {
fun getInlineClassSignatureManglingSuffix(descriptor: CallableMemberDescriptor): String? {
if (descriptor !is FunctionDescriptor) return null
if (descriptor is ConstructorDescriptor) return null
if (InlineClassDescriptorResolver.isSynthesizedBoxMethod(descriptor)) return null
if (InlineClassDescriptorResolver.isSynthesizedBoxOrUnboxMethod(descriptor)) return null
val actualValueParameterTypes = listOfNotNull(descriptor.extensionReceiverParameter?.type) + descriptor.valueParameters.map { it.type }
if (actualValueParameterTypes.none { it.requiresFunctionNameMangling() }) return null
return md5radix36string(collectSignatureForMangling(actualValueParameterTypes))
return getInlineClassSignatureManglingSuffix(actualValueParameterTypes)
}
fun getInlineClassSignatureManglingSuffix(valueParameterTypes: List<KotlinType>) =
if (valueParameterTypes.none { it.requiresFunctionNameMangling() })
null
else
"-" + md5base64(collectSignatureForMangling(valueParameterTypes))
private fun KotlinType.requiresFunctionNameMangling() =
isInlineClassThatRequiresMangling() || isTypeParameterWithUpperBoundThatRequiresMangling()
@@ -61,11 +66,8 @@ private fun getSignatureElementForMangling(type: KotlinType): String = buildStri
}
}
private fun md5radix36string(signatureForMangling: String): String {
val d = MessageDigest.getInstance("MD5").digest(signatureForMangling.toByteArray())
var acc = 0L
for (i in 0..4) {
acc = (acc shl 8) + (d[i].toLong() and 0xFFL)
}
return acc.toString(36)
private fun md5base64(signatureForMangling: String): String {
val d = MessageDigest.getInstance("MD5").digest(signatureForMangling.toByteArray()).copyOfRange(0, 5)
// base64 URL encoder without padding uses exactly the characters allowed in both JVM bytecode and Dalvik bytecode names
return Base64.getUrlEncoder().withoutPadding().encodeToString(d)
}