Improve string concatentation & string templates code generation

Reuse StringBuilder instances for nested subexpressions.
(NB StringBuilder instance for string template with a string
concatenation inside an expression entry, such as `"${"a" + "b"}"`,
will not be reused, although that doesn't seem to be a real-life issue).

 #KT-18558 Fixed Target versions 1.1.4
 #KT-13682 Fixed Target versions 1.1.4

Join adjacent strings literals, escaped strings, and constant values
(in a language version that supports const val inlining).
Use StringBuilder#append(char) for single-character constants
(e.g., " " in "$a $b").

 #KT-17280 Fixed Target versions 1.1.4
 #KT-15235 Fixed Target versions 1.1.4
This commit is contained in:
Dmitry Petrov
2017-06-20 17:08:39 +03:00
parent ae71833a12
commit a84c2a6f31
17 changed files with 313 additions and 56 deletions
@@ -74,8 +74,7 @@ import org.jetbrains.kotlin.resolve.calls.model.*;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.*;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluatorKt;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
@@ -826,14 +825,71 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@Override
public StackValue visitStringTemplateExpression(@NotNull KtStringTemplateExpression expression, StackValue receiver) {
StringBuilder constantValue = new StringBuilder("");
KtStringTemplateEntry[] entries = expression.getEntries();
List<StringTemplateEntry> entries = preprocessStringTemplate(expression);
if (entries.length == 1 && entries[0] instanceof KtStringTemplateEntryWithExpression) {
KtExpression expr = entries[0].getExpression();
return genToString(gen(expr), expressionType(expr));
if (entries.size() == 1) {
StringTemplateEntry entry = entries.get(0);
if (entry instanceof StringTemplateEntry.Expression) {
KtExpression expr = ((StringTemplateEntry.Expression) entry).expression;
return genToString(gen(expr), expressionType(expr));
}
else {
Type type = expressionType(expression);
return StackValue.constant(((StringTemplateEntry.Constant) entry).value, type);
}
}
return StackValue.operation(JAVA_STRING_TYPE, v -> {
genStringBuilderConstructor(v);
invokeAppendForEntries(v, entries);
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
return Unit.INSTANCE;
});
}
private void invokeAppendForEntries(InstructionAdapter v, List<StringTemplateEntry> entries) {
for (StringTemplateEntry entry : entries) {
if (entry instanceof StringTemplateEntry.Expression) {
invokeAppend(v, ((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);
}
else {
v.aconst(value);
genInvokeAppendMethod(v, JAVA_STRING_TYPE);
}
}
}
}
private static abstract class StringTemplateEntry {
static class Constant extends StringTemplateEntry {
final String value;
Constant(String value) {
this.value = value;
}
}
static class Expression extends StringTemplateEntry {
final KtExpression expression;
Expression(KtExpression expression) {
this.expression = expression;
}
}
}
private @NotNull List<StringTemplateEntry> preprocessStringTemplate(@NotNull KtStringTemplateExpression expression) {
KtStringTemplateEntry[] entries = expression.getEntries();
List<StringTemplateEntry> result = new ArrayList<>(entries.length);
StringBuilder constantValue = new StringBuilder("");
for (KtStringTemplateEntry entry : entries) {
if (entry instanceof KtLiteralStringTemplateEntry) {
constantValue.append(entry.getText());
@@ -841,36 +897,46 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
else if (entry instanceof KtEscapeStringTemplateEntry) {
constantValue.append(((KtEscapeStringTemplateEntry) entry).getUnescapedValue());
}
else if (entry instanceof KtStringTemplateEntryWithExpression) {
KtExpression entryExpression = entry.getExpression();
if (entryExpression == null) throw new AssertionError("No expression in " + entry);
ConstantValue<?> compileTimeConstant =
getPrimitiveOrStringCompileTimeConstant(entryExpression, bindingContext, state.getShouldInlineConstVals());
if (compileTimeConstant != null && isConstantValueInlinableInStringTemplate(compileTimeConstant)) {
constantValue.append(String.valueOf(compileTimeConstant.getValue()));
}
else {
result.add(new StringTemplateEntry.Constant(constantValue.toString()));
constantValue.setLength(0);
result.add(new StringTemplateEntry.Expression(entryExpression));
}
}
else {
constantValue = null;
break;
throw new AssertionError("Unexpected string template entry: " + entry);
}
}
if (constantValue != null) {
Type type = expressionType(expression);
return StackValue.constant(constantValue.toString(), type);
}
else {
return StackValue.operation(JAVA_STRING_TYPE, v -> {
genStringBuilderConstructor(v);
for (KtStringTemplateEntry entry : entries) {
if (entry instanceof KtStringTemplateEntryWithExpression) {
invokeAppend(entry.getExpression());
}
else {
String text = entry instanceof KtEscapeStringTemplateEntry
? ((KtEscapeStringTemplateEntry) entry).getUnescapedValue()
: entry.getText();
v.aconst(text);
genInvokeAppendMethod(v, JAVA_STRING_TYPE);
}
}
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
return Unit.INSTANCE;
});
String leftoverConstantValue = constantValue.toString();
if (leftoverConstantValue.length() > 0) {
result.add(new StringTemplateEntry.Constant(leftoverConstantValue));
}
return result;
}
private static boolean isConstantValueInlinableInStringTemplate(@NotNull ConstantValue<?> constant) {
return constant instanceof StringValue ||
constant instanceof BooleanValue ||
constant instanceof DoubleValue ||
constant instanceof FloatValue ||
constant instanceof IntegerValueConstant ||
constant instanceof NullValue;
}
@Override
public StackValue visitBlockExpression(@NotNull KtBlockExpression expression, StackValue receiver) {
return generateBlock(expression, false);
@@ -3250,22 +3316,31 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
}
public void invokeAppend(KtExpression expr) {
public void invokeAppend(InstructionAdapter v, KtExpression expr) {
expr = KtPsiUtil.safeDeparenthesize(expr);
ConstantValue<?> compileTimeConstant = getPrimitiveOrStringCompileTimeConstant(expr, bindingContext, state.getShouldInlineConstVals());
if (compileTimeConstant == null && expr instanceof KtBinaryExpression) {
KtBinaryExpression binaryExpression = (KtBinaryExpression) expr;
if (binaryExpression.getOperationToken() == KtTokens.PLUS) {
KtExpression left = binaryExpression.getLeft();
KtExpression right = binaryExpression.getRight();
Type leftType = expressionType(left);
if (compileTimeConstant == null) {
if (expr instanceof KtBinaryExpression) {
KtBinaryExpression binaryExpression = (KtBinaryExpression) expr;
if (binaryExpression.getOperationToken() == KtTokens.PLUS) {
KtExpression left = binaryExpression.getLeft();
KtExpression right = binaryExpression.getRight();
Type leftType = expressionType(left);
if (leftType.equals(JAVA_STRING_TYPE)) {
invokeAppend(left);
invokeAppend(right);
return;
if (leftType.equals(JAVA_STRING_TYPE)) {
invokeAppend(v, left);
invokeAppend(v, right);
return;
}
}
}
else if (expr instanceof KtStringTemplateExpression) {
List<StringTemplateEntry> entries = preprocessStringTemplate((KtStringTemplateExpression) expr);
invokeAppendForEntries(v, entries);
return;
}
}
Type exprType = expressionType(expr);
@@ -42,8 +42,8 @@ class Concat : IntrinsicMethod() {
if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) {
// LHS + RHS
genStringBuilderConstructor(v)
codegen.invokeAppend(element.left)
codegen.invokeAppend(element.right)
codegen.invokeAppend(v, element.left)
codegen.invokeAppend(v, element.right)
}
else {
// Explicit plus call LHS?.plus(RHS) or LHS.plus(RHS)
@@ -51,7 +51,7 @@ class Concat : IntrinsicMethod() {
genStringBuilderConstructor(v)
v.swap()
genInvokeAppendMethod(v, returnType)
codegen.invokeAppend(arguments[0])
codegen.invokeAppend(v, arguments[0])
}
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)