From 88892ec65de320375341d2f3e771a0828470eaa7 Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Fri, 25 Sep 2020 10:42:41 +0200 Subject: [PATCH] Introduce StringAppendGenerator --- .../org/jetbrains/kotlin/codegen/Callable.kt | 3 +- .../kotlin/codegen/DescriptorAsmUtil.java | 34 ++------ .../kotlin/codegen/ExpressionCodegen.java | 29 ++++--- .../FunctionsFromAnyGeneratorImpl.java | 16 ++-- .../kotlin/codegen/StringAppendGenerator.kt | 79 +++++++++++++++++++ .../kotlin/codegen/intrinsics/Concat.kt | 38 +++++---- .../kotlin/codegen/intrinsics/RangeTo.kt | 3 +- 7 files changed, 134 insertions(+), 68 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/StringAppendGenerator.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/Callable.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/Callable.kt index 32700490400..c5ad724fca1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/Callable.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/Callable.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.codegen +import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.org.objectweb.asm.Type @@ -42,7 +43,7 @@ interface Callable { } } - fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap) { + fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap, state: GenerationState) { } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/DescriptorAsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/DescriptorAsmUtil.java index 15b7ce13c4c..1d28575724a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/DescriptorAsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/DescriptorAsmUtil.java @@ -69,12 +69,6 @@ import static org.jetbrains.kotlin.types.TypeUtils.isNullableType; import static org.jetbrains.org.objectweb.asm.Opcodes.*; public class DescriptorAsmUtil { - private static final Set STRING_BUILDER_OBJECT_APPEND_ARG_TYPES = Sets.newHashSet( - getType(String.class), - getType(StringBuffer.class), - getType(CharSequence.class) - ); - private DescriptorAsmUtil() { } @@ -501,12 +495,12 @@ public class DescriptorAsmUtil { return index; } - public static void genInvokeAppendMethod(@NotNull InstructionAdapter v, @NotNull Type type, @Nullable KotlinType kotlinType) { - genInvokeAppendMethod(v, type, kotlinType, null); + public static void genInvokeAppendMethod(@NotNull StringAppendGenerator generator, @NotNull Type type, @Nullable KotlinType kotlinType) { + genInvokeAppendMethod(generator, type, kotlinType, null); } public static void genInvokeAppendMethod( - @NotNull InstructionAdapter v, + @NotNull StringAppendGenerator generator, @NotNull Type type, @Nullable KotlinType kotlinType, @Nullable KotlinTypeMapper typeMapper @@ -515,33 +509,19 @@ public class DescriptorAsmUtil { CallableMethod specializedToString = getSpecializedToStringCallableMethodOrNull(kotlinType, typeMapper); if (specializedToString != null) { - specializedToString.genInvokeInstruction(v); + specializedToString.genInvokeInstruction(generator.getMv()); 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); + StackValue.coerce(type, kotlinType, appendParameterType, nullableAnyType, generator.getMv()); } else { - appendParameterType = stringBuilderAppendType(type); + appendParameterType = type; } - v.invokevirtual("java/lang/StringBuilder", "append", "(" + appendParameterType.getDescriptor() + ")Ljava/lang/StringBuilder;", false); - } - - private static Type stringBuilderAppendType(Type type) { - switch (type.getSort()) { - case Type.OBJECT: - return STRING_BUILDER_OBJECT_APPEND_ARG_TYPES.contains(type) ? type : OBJECT_TYPE; - case Type.ARRAY: - return OBJECT_TYPE; - case Type.BYTE: - case Type.SHORT: - return Type.INT_TYPE; - default: - return type; - } + generator.invokeAppend(appendParameterType); } public static StackValue genToString( diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 3e18f0460d4..874f14aa073 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -925,28 +925,27 @@ public class ExpressionCodegen extends KtVisitor impleme } else { return StackValue.operation(type, v -> { - genStringBuilderConstructor(v); - invokeAppendForEntries(v, entries); - v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); + StringAppendGenerator generator = StringAppendGenerator.Companion.create(state, v); + generator.genStringBuilderConstructorIfNeded(); + invokeAppendForEntries(generator, entries); + generator.genToString(); return Unit.INSTANCE; }); } } - private void invokeAppendForEntries(InstructionAdapter v, List entries) { + private void invokeAppendForEntries(StringAppendGenerator generator, List entries) { for (StringTemplateEntry entry : entries) { if (entry instanceof StringTemplateEntry.Expression) { - invokeAppend(v, ((StringTemplateEntry.Expression) entry).expression); + invokeAppend(generator, ((StringTemplateEntry.Expression) entry).expression); } else { String value = ((StringTemplateEntry.Constant) entry).value; if (value.length() == 1) { - v.iconst(value.charAt(0)); - genInvokeAppendMethod(v, Type.CHAR_TYPE, null); + generator.addCharConstant(value.charAt(0)); } else { - v.aconst(value); - genInvokeAppendMethod(v, JAVA_STRING_TYPE, null); + generator.addStringConstant(value); } } } @@ -2840,7 +2839,7 @@ public class ExpressionCodegen extends KtVisitor impleme myFrameMap.leaveTemp(firstReceiverType); } - callableMethod.afterReceiverGeneration(v, myFrameMap); + callableMethod.afterReceiverGeneration(v, myFrameMap, state); } } @@ -4301,7 +4300,7 @@ public class ExpressionCodegen extends KtVisitor impleme } } - public void invokeAppend(InstructionAdapter v, KtExpression expr) { + public void invokeAppend(StringAppendGenerator generator, KtExpression expr) { expr = KtPsiUtil.safeDeparenthesize(expr); ConstantValue compileTimeConstant = getPrimitiveOrStringCompileTimeConstant(expr); @@ -4315,15 +4314,15 @@ public class ExpressionCodegen extends KtVisitor impleme Type leftType = expressionType(left); if (leftType.equals(JAVA_STRING_TYPE)) { - invokeAppend(v, left); - invokeAppend(v, right); + invokeAppend(generator, left); + invokeAppend(generator, right); return; } } } else if (expr instanceof KtStringTemplateExpression) { List entries = preprocessStringTemplate((KtStringTemplateExpression) expr); - invokeAppendForEntries(v, entries); + invokeAppendForEntries(generator, entries); return; } } @@ -4336,7 +4335,7 @@ public class ExpressionCodegen extends KtVisitor impleme gen(expr, exprType, exprKotlinType); } - genInvokeAppendMethod(v, exprType, exprKotlinType, typeMapper); + genInvokeAppendMethod(generator, exprType, exprKotlinType, typeMapper); } @Nullable diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java index 1d9b3896a01..c14cf81bdda 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java @@ -101,18 +101,19 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator { InstructionAdapter iv = new InstructionAdapter(mv); mv.visitCode(); - genStringBuilderConstructor(iv); + StringAppendGenerator generator = StringAppendGenerator.Companion.create(generationState, iv); + generator.genStringBuilderConstructorIfNeded(); boolean first = true; + for (PropertyDescriptor propertyDescriptor : properties) { if (first) { - iv.aconst(classDescriptor.getName() + "(" + propertyDescriptor.getName().asString() + "="); + generator.addStringConstant(classDescriptor.getName() + "(" + propertyDescriptor.getName().asString() + "="); first = false; } else { - iv.aconst(", " + propertyDescriptor.getName().asString() + "="); + generator.addStringConstant(", " + propertyDescriptor.getName().asString() + "="); } - genInvokeAppendMethod(iv, JAVA_STRING_TYPE, null); JvmKotlinType type = genOrLoadOnStack(iv, context, propertyDescriptor, 0); Type asmType = type.getType(); @@ -131,13 +132,12 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator { kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType(); } } - genInvokeAppendMethod(iv, asmType, kotlinType, typeMapper); + genInvokeAppendMethod(generator, asmType, kotlinType, typeMapper); } - iv.aconst(")"); - genInvokeAppendMethod(iv, JAVA_STRING_TYPE, null); + generator.addStringConstant(")"); - iv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); + generator.genToString(); iv.areturn(JAVA_STRING_TYPE); FunctionCodegen.endVisit(mv, toStringMethodName, getDeclaration()); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StringAppendGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/StringAppendGenerator.kt new file mode 100644 index 00000000000..8b3d4a2269e --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StringAppendGenerator.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen + +import com.google.common.collect.Sets +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter + +class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAdapter) { + + @JvmOverloads + fun genStringBuilderConstructorIfNeded(swap: Boolean = false) { + if (useInvokeDynamic) return + mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder") + mv.dup() + mv.invokespecial("java/lang/StringBuilder", "", "()V", false) + if (swap) { + mv.swap() + } + } + + fun addArgOnStack(type: Type) { + + } + + fun addCharConstant(value: Int) { + mv.iconst(value) + invokeAppend(Type.CHAR_TYPE) + } + + fun addStringConstant(value: String) { + mv.aconst(value) + invokeAppend(JAVA_STRING_TYPE) + } + + fun invokeAppend(type: Type) { + if (!useInvokeDynamic) { + mv.invokevirtual( + "java/lang/StringBuilder", + "append", + "(" + stringBuilderAppendType(type) + ")Ljava/lang/StringBuilder;", + false + ) + } + } + + fun genToString() { + if (!useInvokeDynamic) { + mv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false) + } + } + + companion object { + private val STRING_BUILDER_OBJECT_APPEND_ARG_TYPES: Set = Sets.newHashSet( + AsmTypes.getType(String::class.java), + AsmTypes.getType(StringBuffer::class.java), + AsmTypes.getType(CharSequence::class.java) + ) + + private fun stringBuilderAppendType(type: Type): Type { + return when (type.sort) { + Type.OBJECT -> if (STRING_BUILDER_OBJECT_APPEND_ARG_TYPES.contains(type)) type else AsmTypes.OBJECT_TYPE + Type.ARRAY -> AsmTypes.OBJECT_TYPE + Type.BYTE, Type.SHORT -> Type.INT_TYPE + else -> type + } + } + + fun create(state: GenerationState, mv: InstructionAdapter) = StringAppendGenerator(false, mv) + + } +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/Concat.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/Concat.kt index 9880aad0ef8..57ed6b00728 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/Concat.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/Concat.kt @@ -18,8 +18,8 @@ package org.jetbrains.kotlin.codegen.intrinsics import com.intellij.psi.PsiElement import org.jetbrains.kotlin.codegen.* -import org.jetbrains.kotlin.codegen.AsmUtil.genStringBuilderConstructor import org.jetbrains.kotlin.codegen.DescriptorAsmUtil.genInvokeAppendMethod +import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtCallableReferenceExpression @@ -39,26 +39,27 @@ class Concat : IntrinsicMethod() { arguments: List, receiver: StackValue ): Type { + val generator = StringAppendGenerator.create(codegen.state, v) if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) { - // LHS + RHS - genStringBuilderConstructor(v) - codegen.invokeAppend(v, element.left) - codegen.invokeAppend(v, element.right) + // LHS + RHS + generator.genStringBuilderConstructorIfNeded() + codegen.invokeAppend(generator, element.left) + codegen.invokeAppend(generator, element.right) } else { // Explicit plus call LHS?.plus(RHS) or LHS.plus(RHS) receiver.put(AsmTypes.JAVA_STRING_TYPE, v) - genStringBuilderConstructor(v) - v.swap() - genInvokeAppendMethod(v, returnType, null) - codegen.invokeAppend(v, arguments[0]) + generator.genStringBuilderConstructorIfNeded(true) + genInvokeAppendMethod(generator, returnType, null) + codegen.invokeAppend(generator, arguments[0]) } - - v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false) + generator.genToString() return JAVA_STRING_TYPE } + override fun toCallable(method: CallableMethod): Callable = object : IntrinsicCallable(method) { + lateinit var generator: StringAppendGenerator override fun invokeMethodWithArguments( resolvedCall: ResolvedCall<*>, receiver: StackValue, @@ -81,9 +82,14 @@ class Concat : IntrinsicMethod() { } } - override fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap) { - v.generateNewInstanceDupAndPlaceBeforeStackTop(frameMap, AsmTypes.JAVA_STRING_TYPE, "java/lang/StringBuilder") - v.invokespecial("java/lang/StringBuilder", "", "(Ljava/lang/String;)V", false) + override fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap, state: GenerationState) { + generator = StringAppendGenerator.create(state, v) + if (!generator.useInvokeDynamic) { + v.generateNewInstanceDupAndPlaceBeforeStackTop(frameMap, JAVA_STRING_TYPE, "java/lang/StringBuilder") + v.invokespecial("java/lang/StringBuilder", "", "(Ljava/lang/String;)V", false) + } else { + generator.invokeAppend(JAVA_STRING_TYPE) + } } override fun invokeIntrinsic(v: InstructionAdapter) { @@ -91,8 +97,8 @@ class Concat : IntrinsicMethod() { // in case of callable reference passed to a generic function, e.g.: // charArrayOf('O', 'K').fold("", String::plus) // TODO Make String::plus generic, and invoke proper StringBuilder#append. - genInvokeAppendMethod(v, AsmTypes.OBJECT_TYPE, null) - v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false) + generator.invokeAppend(AsmTypes.OBJECT_TYPE) + generator.genToString() } } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/RangeTo.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/RangeTo.kt index e16403198d9..f19cf199891 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/RangeTo.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/RangeTo.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.codegen.Callable import org.jetbrains.kotlin.codegen.CallableMethod import org.jetbrains.kotlin.codegen.FrameMap import org.jetbrains.kotlin.codegen.generateNewInstanceDupAndPlaceBeforeStackTop +import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type.* import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -48,7 +49,7 @@ class RangeTo : IntrinsicMethod() { nullOr(method.dispatchReceiverType, argType), nullOr(method.extensionReceiverType, argType) ) { - override fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap) { + override fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap, state: GenerationState) { v.generateNewInstanceDupAndPlaceBeforeStackTop(frameMap, argType, returnType.internalName) }