Introduce StringAppendGenerator

This commit is contained in:
Mikhael Bogdanov
2020-09-25 10:42:41 +02:00
parent bf35818438
commit 88892ec65d
7 changed files with 134 additions and 68 deletions
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.codegen package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.org.objectweb.asm.Type 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) {
} }
} }
@@ -69,12 +69,6 @@ import static org.jetbrains.kotlin.types.TypeUtils.isNullableType;
import static org.jetbrains.org.objectweb.asm.Opcodes.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class DescriptorAsmUtil { public class DescriptorAsmUtil {
private static final Set<Type> STRING_BUILDER_OBJECT_APPEND_ARG_TYPES = Sets.newHashSet(
getType(String.class),
getType(StringBuffer.class),
getType(CharSequence.class)
);
private DescriptorAsmUtil() { private DescriptorAsmUtil() {
} }
@@ -501,12 +495,12 @@ public class DescriptorAsmUtil {
return index; return index;
} }
public static void genInvokeAppendMethod(@NotNull InstructionAdapter v, @NotNull Type type, @Nullable KotlinType kotlinType) { public static void genInvokeAppendMethod(@NotNull StringAppendGenerator generator, @NotNull Type type, @Nullable KotlinType kotlinType) {
genInvokeAppendMethod(v, type, kotlinType, null); genInvokeAppendMethod(generator, type, kotlinType, null);
} }
public static void genInvokeAppendMethod( public static void genInvokeAppendMethod(
@NotNull InstructionAdapter v, @NotNull StringAppendGenerator generator,
@NotNull Type type, @NotNull Type type,
@Nullable KotlinType kotlinType, @Nullable KotlinType kotlinType,
@Nullable KotlinTypeMapper typeMapper @Nullable KotlinTypeMapper typeMapper
@@ -515,33 +509,19 @@ public class DescriptorAsmUtil {
CallableMethod specializedToString = getSpecializedToStringCallableMethodOrNull(kotlinType, typeMapper); CallableMethod specializedToString = getSpecializedToStringCallableMethodOrNull(kotlinType, typeMapper);
if (specializedToString != null) { if (specializedToString != null) {
specializedToString.genInvokeInstruction(v); specializedToString.genInvokeInstruction(generator.getMv());
appendParameterType = AsmTypes.JAVA_STRING_TYPE; appendParameterType = AsmTypes.JAVA_STRING_TYPE;
} }
else if (kotlinType != null && InlineClassesUtilsKt.isInlineClassType(kotlinType)) { else if (kotlinType != null && InlineClassesUtilsKt.isInlineClassType(kotlinType)) {
appendParameterType = OBJECT_TYPE; appendParameterType = OBJECT_TYPE;
SimpleType nullableAnyType = kotlinType.getConstructor().getBuiltIns().getNullableAnyType(); SimpleType nullableAnyType = kotlinType.getConstructor().getBuiltIns().getNullableAnyType();
StackValue.coerce(type, kotlinType, appendParameterType, nullableAnyType, v); StackValue.coerce(type, kotlinType, appendParameterType, nullableAnyType, generator.getMv());
} }
else { else {
appendParameterType = stringBuilderAppendType(type); appendParameterType = type;
} }
v.invokevirtual("java/lang/StringBuilder", "append", "(" + appendParameterType.getDescriptor() + ")Ljava/lang/StringBuilder;", false); generator.invokeAppend(appendParameterType);
}
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;
}
} }
public static StackValue genToString( public static StackValue genToString(
@@ -925,28 +925,27 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
} }
else { else {
return StackValue.operation(type, v -> { return StackValue.operation(type, v -> {
genStringBuilderConstructor(v); StringAppendGenerator generator = StringAppendGenerator.Companion.create(state, v);
invokeAppendForEntries(v, entries); generator.genStringBuilderConstructorIfNeded();
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); invokeAppendForEntries(generator, entries);
generator.genToString();
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
} }
} }
private void invokeAppendForEntries(InstructionAdapter v, List<StringTemplateEntry> entries) { private void invokeAppendForEntries(StringAppendGenerator generator, List<StringTemplateEntry> entries) {
for (StringTemplateEntry entry : entries) { for (StringTemplateEntry entry : entries) {
if (entry instanceof StringTemplateEntry.Expression) { if (entry instanceof StringTemplateEntry.Expression) {
invokeAppend(v, ((StringTemplateEntry.Expression) entry).expression); invokeAppend(generator, ((StringTemplateEntry.Expression) entry).expression);
} }
else { else {
String value = ((StringTemplateEntry.Constant) entry).value; String value = ((StringTemplateEntry.Constant) entry).value;
if (value.length() == 1) { if (value.length() == 1) {
v.iconst(value.charAt(0)); generator.addCharConstant(value.charAt(0));
genInvokeAppendMethod(v, Type.CHAR_TYPE, null);
} }
else { else {
v.aconst(value); generator.addStringConstant(value);
genInvokeAppendMethod(v, JAVA_STRING_TYPE, null);
} }
} }
} }
@@ -2840,7 +2839,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
myFrameMap.leaveTemp(firstReceiverType); myFrameMap.leaveTemp(firstReceiverType);
} }
callableMethod.afterReceiverGeneration(v, myFrameMap); callableMethod.afterReceiverGeneration(v, myFrameMap, state);
} }
} }
@@ -4301,7 +4300,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
} }
} }
public void invokeAppend(InstructionAdapter v, KtExpression expr) { public void invokeAppend(StringAppendGenerator generator, KtExpression expr) {
expr = KtPsiUtil.safeDeparenthesize(expr); expr = KtPsiUtil.safeDeparenthesize(expr);
ConstantValue<?> compileTimeConstant = getPrimitiveOrStringCompileTimeConstant(expr); ConstantValue<?> compileTimeConstant = getPrimitiveOrStringCompileTimeConstant(expr);
@@ -4315,15 +4314,15 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
Type leftType = expressionType(left); Type leftType = expressionType(left);
if (leftType.equals(JAVA_STRING_TYPE)) { if (leftType.equals(JAVA_STRING_TYPE)) {
invokeAppend(v, left); invokeAppend(generator, left);
invokeAppend(v, right); invokeAppend(generator, right);
return; return;
} }
} }
} }
else if (expr instanceof KtStringTemplateExpression) { else if (expr instanceof KtStringTemplateExpression) {
List<StringTemplateEntry> entries = preprocessStringTemplate((KtStringTemplateExpression) expr); List<StringTemplateEntry> entries = preprocessStringTemplate((KtStringTemplateExpression) expr);
invokeAppendForEntries(v, entries); invokeAppendForEntries(generator, entries);
return; return;
} }
} }
@@ -4336,7 +4335,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
gen(expr, exprType, exprKotlinType); gen(expr, exprType, exprKotlinType);
} }
genInvokeAppendMethod(v, exprType, exprKotlinType, typeMapper); genInvokeAppendMethod(generator, exprType, exprKotlinType, typeMapper);
} }
@Nullable @Nullable
@@ -101,18 +101,19 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
InstructionAdapter iv = new InstructionAdapter(mv); InstructionAdapter iv = new InstructionAdapter(mv);
mv.visitCode(); mv.visitCode();
genStringBuilderConstructor(iv);
StringAppendGenerator generator = StringAppendGenerator.Companion.create(generationState, iv);
generator.genStringBuilderConstructorIfNeded();
boolean first = true; boolean first = true;
for (PropertyDescriptor propertyDescriptor : properties) { for (PropertyDescriptor propertyDescriptor : properties) {
if (first) { if (first) {
iv.aconst(classDescriptor.getName() + "(" + propertyDescriptor.getName().asString() + "="); generator.addStringConstant(classDescriptor.getName() + "(" + propertyDescriptor.getName().asString() + "=");
first = false; first = false;
} }
else { else {
iv.aconst(", " + propertyDescriptor.getName().asString() + "="); generator.addStringConstant(", " + propertyDescriptor.getName().asString() + "=");
} }
genInvokeAppendMethod(iv, JAVA_STRING_TYPE, null);
JvmKotlinType type = genOrLoadOnStack(iv, context, propertyDescriptor, 0); JvmKotlinType type = genOrLoadOnStack(iv, context, propertyDescriptor, 0);
Type asmType = type.getType(); Type asmType = type.getType();
@@ -131,13 +132,12 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType(); kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType();
} }
} }
genInvokeAppendMethod(iv, asmType, kotlinType, typeMapper); genInvokeAppendMethod(generator, asmType, kotlinType, typeMapper);
} }
iv.aconst(")"); generator.addStringConstant(")");
genInvokeAppendMethod(iv, JAVA_STRING_TYPE, null);
iv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); generator.genToString();
iv.areturn(JAVA_STRING_TYPE); iv.areturn(JAVA_STRING_TYPE);
FunctionCodegen.endVisit(mv, toStringMethodName, getDeclaration()); FunctionCodegen.endVisit(mv, toStringMethodName, getDeclaration());
@@ -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", "<init>", "()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<Type> = 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)
}
}
@@ -18,8 +18,8 @@ package org.jetbrains.kotlin.codegen.intrinsics
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.AsmUtil.genStringBuilderConstructor
import org.jetbrains.kotlin.codegen.DescriptorAsmUtil.genInvokeAppendMethod import org.jetbrains.kotlin.codegen.DescriptorAsmUtil.genInvokeAppendMethod
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
@@ -39,26 +39,27 @@ class Concat : IntrinsicMethod() {
arguments: List<KtExpression>, arguments: List<KtExpression>,
receiver: StackValue receiver: StackValue
): Type { ): Type {
val generator = StringAppendGenerator.create(codegen.state, v)
if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) { if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) {
// LHS + RHS // LHS + RHS
genStringBuilderConstructor(v) generator.genStringBuilderConstructorIfNeded()
codegen.invokeAppend(v, element.left) codegen.invokeAppend(generator, element.left)
codegen.invokeAppend(v, element.right) codegen.invokeAppend(generator, element.right)
} else { } else {
// Explicit plus call LHS?.plus(RHS) or LHS.plus(RHS) // Explicit plus call LHS?.plus(RHS) or LHS.plus(RHS)
receiver.put(AsmTypes.JAVA_STRING_TYPE, v) receiver.put(AsmTypes.JAVA_STRING_TYPE, v)
genStringBuilderConstructor(v) generator.genStringBuilderConstructorIfNeded(true)
v.swap() genInvokeAppendMethod(generator, returnType, null)
genInvokeAppendMethod(v, returnType, null) codegen.invokeAppend(generator, arguments[0])
codegen.invokeAppend(v, arguments[0])
} }
generator.genToString()
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
return JAVA_STRING_TYPE return JAVA_STRING_TYPE
} }
override fun toCallable(method: CallableMethod): Callable = override fun toCallable(method: CallableMethod): Callable =
object : IntrinsicCallable(method) { object : IntrinsicCallable(method) {
lateinit var generator: StringAppendGenerator
override fun invokeMethodWithArguments( override fun invokeMethodWithArguments(
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
receiver: StackValue, receiver: StackValue,
@@ -81,9 +82,14 @@ class Concat : IntrinsicMethod() {
} }
} }
override fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap) { override fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap, state: GenerationState) {
v.generateNewInstanceDupAndPlaceBeforeStackTop(frameMap, AsmTypes.JAVA_STRING_TYPE, "java/lang/StringBuilder") generator = StringAppendGenerator.create(state, v)
v.invokespecial("java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false) if (!generator.useInvokeDynamic) {
v.generateNewInstanceDupAndPlaceBeforeStackTop(frameMap, JAVA_STRING_TYPE, "java/lang/StringBuilder")
v.invokespecial("java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false)
} else {
generator.invokeAppend(JAVA_STRING_TYPE)
}
} }
override fun invokeIntrinsic(v: InstructionAdapter) { override fun invokeIntrinsic(v: InstructionAdapter) {
@@ -91,8 +97,8 @@ class Concat : IntrinsicMethod() {
// in case of callable reference passed to a generic function, e.g.: // in case of callable reference passed to a generic function, e.g.:
// charArrayOf('O', 'K').fold("", String::plus) // charArrayOf('O', 'K').fold("", String::plus)
// TODO Make String::plus generic, and invoke proper StringBuilder#append. // TODO Make String::plus generic, and invoke proper StringBuilder#append.
genInvokeAppendMethod(v, AsmTypes.OBJECT_TYPE, null) generator.invokeAppend(AsmTypes.OBJECT_TYPE)
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false) generator.genToString()
} }
} }
} }
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.codegen.Callable
import org.jetbrains.kotlin.codegen.CallableMethod import org.jetbrains.kotlin.codegen.CallableMethod
import org.jetbrains.kotlin.codegen.FrameMap import org.jetbrains.kotlin.codegen.FrameMap
import org.jetbrains.kotlin.codegen.generateNewInstanceDupAndPlaceBeforeStackTop 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.Type.* import org.jetbrains.org.objectweb.asm.Type.*
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -48,7 +49,7 @@ class RangeTo : IntrinsicMethod() {
nullOr(method.dispatchReceiverType, argType), nullOr(method.dispatchReceiverType, argType),
nullOr(method.extensionReceiverType, 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) v.generateNewInstanceDupAndPlaceBeforeStackTop(frameMap, argType, returnType.internalName)
} }