Generate specialized 'toString' for inline classes when possible

#KT-25613
This commit is contained in:
Dmitry Petrov
2018-10-03 15:53:52 +03:00
parent f68ce4b35b
commit 5304754e88
13 changed files with 181 additions and 6 deletions
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.config.JvmTarget;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.load.java.JavaVisibilities;
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
@@ -665,8 +666,23 @@ public class AsmUtil {
}
public static void genInvokeAppendMethod(@NotNull InstructionAdapter v, @NotNull Type type, @Nullable KotlinType kotlinType) {
genInvokeAppendMethod(v, type, kotlinType, null);
}
public static void genInvokeAppendMethod(
@NotNull InstructionAdapter v,
@NotNull Type type,
@Nullable KotlinType kotlinType,
@Nullable KotlinTypeMapper typeMapper
) {
Type appendParameterType;
if (kotlinType != null && InlineClassesUtilsKt.isInlineClassType(kotlinType)) {
CallableMethod specializedToString = getSpecializedToStringCallableMethodOrNull(kotlinType, typeMapper);
if (specializedToString != null) {
specializedToString.genInvokeInstruction(v);
appendParameterType = AsmTypes.JAVA_STRING_TYPE;
}
else if (kotlinType != null && InlineClassesUtilsKt.isInlineClassType(kotlinType)) {
appendParameterType = OBJECT_TYPE;
SimpleType nullableAnyType = kotlinType.getConstructor().getBuiltIns().getNullableAnyType();
StackValue.coerce(type, kotlinType, appendParameterType, nullableAnyType, v);
@@ -681,9 +697,17 @@ public class AsmUtil {
public static StackValue genToString(
@NotNull StackValue receiver,
@NotNull Type receiverType,
@Nullable KotlinType receiverKotlinType
@Nullable KotlinType receiverKotlinType,
@Nullable KotlinTypeMapper typeMapper
) {
return StackValue.operation(JAVA_STRING_TYPE, v -> {
CallableMethod specializedToString = getSpecializedToStringCallableMethodOrNull(receiverKotlinType, typeMapper);
if (specializedToString != null) {
receiver.put(receiverType, receiverKotlinType, v);
specializedToString.genInvokeInstruction(v);
return null;
}
Type type;
KotlinType kotlinType;
if (receiverKotlinType != null && InlineClassesUtilsKt.isInlineClassType(receiverKotlinType)) {
@@ -701,6 +725,36 @@ public class AsmUtil {
});
}
@Nullable
private static CallableMethod getSpecializedToStringCallableMethodOrNull(
@Nullable KotlinType receiverKotlinType,
@Nullable KotlinTypeMapper typeMapper
) {
if (typeMapper == null) return null;
if (receiverKotlinType == null) return null;
if (!InlineClassesUtilsKt.isInlineClassType(receiverKotlinType)) return null;
if (receiverKotlinType.isMarkedNullable()) return null;
DeclarationDescriptor receiverTypeDescriptor = receiverKotlinType.getConstructor().getDeclarationDescriptor();
assert receiverTypeDescriptor instanceof ClassDescriptor && ((ClassDescriptor) receiverTypeDescriptor).isInline() :
"Inline class type expected: " + receiverKotlinType;
ClassDescriptor receiverClassDescriptor = (ClassDescriptor) receiverTypeDescriptor;
FunctionDescriptor toStringDescriptor = receiverClassDescriptor.getUnsubstitutedMemberScope()
.getContributedFunctions(Name.identifier("toString"), NoLookupLocation.FROM_BACKEND)
.stream()
.filter(
f -> f.getValueParameters().size() == 0
&& KotlinBuiltIns.isString(f.getReturnType())
&& f.getDispatchReceiverParameter() != null
&& f.getExtensionReceiverParameter() == null
)
.findFirst()
.orElseThrow(() -> new AssertionError("'toString' not found in member scope of " + receiverClassDescriptor));
return typeMapper.mapToCallableMethod(toStringDescriptor, false, OwnerKind.ERASED_INLINE_CLASS);
}
static void genHashCode(MethodVisitor mv, InstructionAdapter iv, Type type, JvmTarget jvmTarget) {
if (type.getSort() == Type.ARRAY) {
Type elementType = correctElementType(type);
@@ -833,7 +833,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
StringTemplateEntry entry = entries.get(0);
if (entry instanceof StringTemplateEntry.Expression) {
KtExpression expr = ((StringTemplateEntry.Expression) entry).expression;
return genToString(gen(expr), expressionType(expr), kotlinType(expr));
return genToString(gen(expr), expressionType(expr), kotlinType(expr), typeMapper);
}
else {
return StackValue.constant(((StringTemplateEntry.Constant) entry).value, type);
@@ -3879,7 +3879,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
gen(expr, exprType, exprKotlinType);
}
genInvokeAppendMethod(v, exprType, exprKotlinType);
genInvokeAppendMethod(v, exprType, exprKotlinType, typeMapper);
}
@Nullable
@@ -122,7 +122,7 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
}
}
}
genInvokeAppendMethod(iv, asmType, type.getKotlinType());
genInvokeAppendMethod(iv, asmType, type.getKotlinType(), typeMapper);
}
iv.aconst(")");