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:
@@ -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.CallMaker;
|
||||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
|
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
|
||||||
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
|
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
|
||||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
import org.jetbrains.kotlin.resolve.constants.*;
|
||||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluatorKt;
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluatorKt;
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||||
@@ -826,14 +825,71 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StackValue visitStringTemplateExpression(@NotNull KtStringTemplateExpression expression, StackValue receiver) {
|
public StackValue visitStringTemplateExpression(@NotNull KtStringTemplateExpression expression, StackValue receiver) {
|
||||||
StringBuilder constantValue = new StringBuilder("");
|
List<StringTemplateEntry> entries = preprocessStringTemplate(expression);
|
||||||
KtStringTemplateEntry[] entries = expression.getEntries();
|
|
||||||
|
|
||||||
if (entries.length == 1 && entries[0] instanceof KtStringTemplateEntryWithExpression) {
|
if (entries.size() == 1) {
|
||||||
KtExpression expr = entries[0].getExpression();
|
StringTemplateEntry entry = entries.get(0);
|
||||||
return genToString(gen(expr), expressionType(expr));
|
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) {
|
for (KtStringTemplateEntry entry : entries) {
|
||||||
if (entry instanceof KtLiteralStringTemplateEntry) {
|
if (entry instanceof KtLiteralStringTemplateEntry) {
|
||||||
constantValue.append(entry.getText());
|
constantValue.append(entry.getText());
|
||||||
@@ -841,36 +897,46 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
else if (entry instanceof KtEscapeStringTemplateEntry) {
|
else if (entry instanceof KtEscapeStringTemplateEntry) {
|
||||||
constantValue.append(((KtEscapeStringTemplateEntry) entry).getUnescapedValue());
|
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 {
|
else {
|
||||||
constantValue = null;
|
throw new AssertionError("Unexpected string template entry: " + entry);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (constantValue != null) {
|
|
||||||
Type type = expressionType(expression);
|
String leftoverConstantValue = constantValue.toString();
|
||||||
return StackValue.constant(constantValue.toString(), type);
|
if (leftoverConstantValue.length() > 0) {
|
||||||
}
|
result.add(new StringTemplateEntry.Constant(leftoverConstantValue));
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
public StackValue visitBlockExpression(@NotNull KtBlockExpression expression, StackValue receiver) {
|
public StackValue visitBlockExpression(@NotNull KtBlockExpression expression, StackValue receiver) {
|
||||||
return generateBlock(expression, false);
|
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());
|
ConstantValue<?> compileTimeConstant = getPrimitiveOrStringCompileTimeConstant(expr, bindingContext, state.getShouldInlineConstVals());
|
||||||
|
|
||||||
if (compileTimeConstant == null && expr instanceof KtBinaryExpression) {
|
if (compileTimeConstant == null) {
|
||||||
KtBinaryExpression binaryExpression = (KtBinaryExpression) expr;
|
if (expr instanceof KtBinaryExpression) {
|
||||||
if (binaryExpression.getOperationToken() == KtTokens.PLUS) {
|
KtBinaryExpression binaryExpression = (KtBinaryExpression) expr;
|
||||||
KtExpression left = binaryExpression.getLeft();
|
if (binaryExpression.getOperationToken() == KtTokens.PLUS) {
|
||||||
KtExpression right = binaryExpression.getRight();
|
KtExpression left = binaryExpression.getLeft();
|
||||||
Type leftType = expressionType(left);
|
KtExpression right = binaryExpression.getRight();
|
||||||
|
Type leftType = expressionType(left);
|
||||||
|
|
||||||
if (leftType.equals(JAVA_STRING_TYPE)) {
|
if (leftType.equals(JAVA_STRING_TYPE)) {
|
||||||
invokeAppend(left);
|
invokeAppend(v, left);
|
||||||
invokeAppend(right);
|
invokeAppend(v, right);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (expr instanceof KtStringTemplateExpression) {
|
||||||
|
List<StringTemplateEntry> entries = preprocessStringTemplate((KtStringTemplateExpression) expr);
|
||||||
|
invokeAppendForEntries(v, entries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Type exprType = expressionType(expr);
|
Type exprType = expressionType(expr);
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ class Concat : IntrinsicMethod() {
|
|||||||
if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) {
|
if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) {
|
||||||
// LHS + RHS
|
// LHS + RHS
|
||||||
genStringBuilderConstructor(v)
|
genStringBuilderConstructor(v)
|
||||||
codegen.invokeAppend(element.left)
|
codegen.invokeAppend(v, element.left)
|
||||||
codegen.invokeAppend(element.right)
|
codegen.invokeAppend(v, element.right)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Explicit plus call LHS?.plus(RHS) or LHS.plus(RHS)
|
// Explicit plus call LHS?.plus(RHS) or LHS.plus(RHS)
|
||||||
@@ -51,7 +51,7 @@ class Concat : IntrinsicMethod() {
|
|||||||
genStringBuilderConstructor(v)
|
genStringBuilderConstructor(v)
|
||||||
v.swap()
|
v.swap()
|
||||||
genInvokeAppendMethod(v, returnType)
|
genInvokeAppendMethod(v, returnType)
|
||||||
codegen.invokeAppend(arguments[0])
|
codegen.invokeAppend(v, arguments[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
|
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ class Concat : IntrinsicMethod() {
|
|||||||
if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) {
|
if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) {
|
||||||
// LHS + RHS
|
// LHS + RHS
|
||||||
genStringBuilderConstructor(v)
|
genStringBuilderConstructor(v)
|
||||||
codegen.invokeAppend(element.left)
|
codegen.invokeAppend(v, element.left)
|
||||||
codegen.invokeAppend(element.right)
|
codegen.invokeAppend(v, element.right)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// LHS?.plus(RHS)
|
// LHS?.plus(RHS)
|
||||||
@@ -57,7 +57,7 @@ class Concat : IntrinsicMethod() {
|
|||||||
genStringBuilderConstructor(v)
|
genStringBuilderConstructor(v)
|
||||||
v.swap()
|
v.swap()
|
||||||
genInvokeAppendMethod(v, returnType)
|
genInvokeAppendMethod(v, returnType)
|
||||||
codegen.invokeAppend(arguments.get(0))
|
codegen.invokeAppend(v, arguments.get(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
|
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
|
||||||
|
|||||||
+4
-5
@@ -1,5 +1,4 @@
|
|||||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||||
// IGNORE_BACKEND: JS
|
|
||||||
|
|
||||||
object A {
|
object A {
|
||||||
const val a: String = "$"
|
const val a: String = "$"
|
||||||
@@ -7,7 +6,7 @@ object A {
|
|||||||
const val c = 10000
|
const val c = 10000
|
||||||
|
|
||||||
val bNonConst = "1234$a"
|
val bNonConst = "1234$a"
|
||||||
val bNullable: String = "1234$a"
|
val bNullable: String? = "1234$a"
|
||||||
}
|
}
|
||||||
|
|
||||||
object B {
|
object B {
|
||||||
@@ -16,7 +15,7 @@ object B {
|
|||||||
const val c = 10000
|
const val c = 10000
|
||||||
|
|
||||||
val bNonConst = "1234$a"
|
val bNonConst = "1234$a"
|
||||||
val bNullable: String = "1234$a"
|
val bNullable: String? = "1234$a"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
@@ -26,8 +25,8 @@ fun box(): String {
|
|||||||
|
|
||||||
if (A.c !== B.c) return "Fail 3: A.c !== B.c"
|
if (A.c !== B.c) return "Fail 3: A.c !== B.c"
|
||||||
|
|
||||||
if (A.bNonConst === B.bNonConst) return "Fail 5: A.bNonConst == B.bNonConst"
|
if (A.bNonConst !== B.bNonConst) return "Fail 4: A.bNonConst !== B.bNonConst"
|
||||||
if (A.bNullable === B.bNullable) return "Fail 6: A.bNullable == B.bNullable"
|
if (A.bNullable !== B.bNullable) return "Fail 5: A.bNullable !== B.bNullable"
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||||
|
// IGNORE_BACKEND: JS
|
||||||
|
// LANGUAGE_VERSION: 1.0
|
||||||
|
|
||||||
|
object A {
|
||||||
|
const val a: String = "$"
|
||||||
|
const val b = "1234$a"
|
||||||
|
const val c = 10000
|
||||||
|
|
||||||
|
val bNonConst = "1234$a"
|
||||||
|
val bNullable: String? = "1234$a"
|
||||||
|
}
|
||||||
|
|
||||||
|
object B {
|
||||||
|
const val a: String = "$"
|
||||||
|
const val b = "1234$a"
|
||||||
|
const val c = 10000
|
||||||
|
|
||||||
|
val bNonConst = "1234$a"
|
||||||
|
val bNullable: String? = "1234$a"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
if (A.a !== B.a) return "Fail 1: A.a !== B.a"
|
||||||
|
|
||||||
|
if (A.b !== B.b) return "Fail 2: A.b !== B.b"
|
||||||
|
|
||||||
|
if (A.c !== B.c) return "Fail 3: A.c !== B.c"
|
||||||
|
|
||||||
|
if (A.bNonConst === B.bNonConst) return "Fail 4: A.bNonConst === B.bNonConst"
|
||||||
|
if (A.bNullable === B.bNullable) return "Fail 5: A.bNullable === B.bNullable"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
const val constTrue = true
|
||||||
|
const val const42 = 42
|
||||||
|
const val constPiF = 3.14F
|
||||||
|
const val constPi = 3.1415926358
|
||||||
|
const val constString = "string"
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("true", "$constTrue")
|
||||||
|
assertEquals("42", "$const42")
|
||||||
|
assertEquals("3.14", "$constPiF")
|
||||||
|
assertEquals("3.1415926358", "$constPi")
|
||||||
|
assertEquals("string", "$constString")
|
||||||
|
|
||||||
|
assertEquals(constPi.toString(), "$constPi")
|
||||||
|
assertEquals((constPi * constPi).toString(), "${constPi * constPi}")
|
||||||
|
|
||||||
|
assertEquals("null", "${null}")
|
||||||
|
assertEquals("42", "${42}")
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
fun test1(s1: String, s2: String, s3: String) =
|
||||||
|
(s1 + s2) + s3
|
||||||
|
|
||||||
|
fun test2(s1: String, s2: String, s3: String) =
|
||||||
|
s1 + (s2 + s3)
|
||||||
|
|
||||||
|
fun test3(s1: String, s2: String, s3: String) =
|
||||||
|
"s1: $s1; " +
|
||||||
|
"s2: $s2; " +
|
||||||
|
"s3: $s3"
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("123", test1("1", "2", "3"))
|
||||||
|
assertEquals("123", test2("1", "2", "3"))
|
||||||
|
assertEquals("s1: 1; s2: 2; s3: 3", test3("1", "2", "3"))
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+1
-1
@@ -6,5 +6,5 @@ class kt5016 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
// 0 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
||||||
// 3 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
// 2 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
||||||
|
|||||||
+1
-1
@@ -6,6 +6,6 @@ class kt5016int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
// 0 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
||||||
// 2 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(I\)Ljava/lang/StringBuilder
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(I\)Ljava/lang/StringBuilder
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ class kt5016intOrNull {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
||||||
// 2 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
val c = 1
|
||||||
|
fun foo() = "a\"($c)\"d"
|
||||||
|
|
||||||
|
// 3 INVOKEVIRTUAL java/lang/StringBuilder.append
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
fun test1(s1: String, s2: String, s3: String) =
|
||||||
|
(s1 + s2) + s3
|
||||||
|
|
||||||
|
fun test2(s1: String, s2: String, s3: String) =
|
||||||
|
s1 + (s2 + s3)
|
||||||
|
|
||||||
|
fun test3(s1: String, s2: String, s3: String, s4: String) =
|
||||||
|
((s1 + s2) + ((s3 + s4)))
|
||||||
|
|
||||||
|
fun test4(s1: String, s2: String, s3: String) =
|
||||||
|
"s1: $s1; " +
|
||||||
|
"s2: $s2; " +
|
||||||
|
"s3: $s3"
|
||||||
|
|
||||||
|
// 4 NEW java/lang/StringBuilder
|
||||||
+18
@@ -4157,6 +4157,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt9532_lv10.kt")
|
||||||
|
public void testKt9532_lv10() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/kt9532_lv10.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("long.kt")
|
@TestMetadata("long.kt")
|
||||||
public void testLong() throws Exception {
|
public void testLong() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/long.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/long.kt");
|
||||||
@@ -17510,6 +17516,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/strings"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/strings"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("constInStringTemplate.kt")
|
||||||
|
public void testConstInStringTemplate() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/constInStringTemplate.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ea35743.kt")
|
@TestMetadata("ea35743.kt")
|
||||||
public void testEa35743() throws Exception {
|
public void testEa35743() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/ea35743.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/ea35743.kt");
|
||||||
@@ -17582,6 +17594,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedConcat.kt")
|
||||||
|
public void testNestedConcat() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/nestedConcat.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("rawStrings.kt")
|
@TestMetadata("rawStrings.kt")
|
||||||
public void testRawStrings() throws Exception {
|
public void testRawStrings() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/rawStrings.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/rawStrings.kt");
|
||||||
|
|||||||
@@ -4157,6 +4157,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt9532_lv10.kt")
|
||||||
|
public void testKt9532_lv10() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/kt9532_lv10.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("long.kt")
|
@TestMetadata("long.kt")
|
||||||
public void testLong() throws Exception {
|
public void testLong() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/long.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/long.kt");
|
||||||
@@ -17510,6 +17516,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/strings"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/strings"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("constInStringTemplate.kt")
|
||||||
|
public void testConstInStringTemplate() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/constInStringTemplate.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ea35743.kt")
|
@TestMetadata("ea35743.kt")
|
||||||
public void testEa35743() throws Exception {
|
public void testEa35743() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/ea35743.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/ea35743.kt");
|
||||||
@@ -17582,6 +17594,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedConcat.kt")
|
||||||
|
public void testNestedConcat() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/nestedConcat.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("rawStrings.kt")
|
@TestMetadata("rawStrings.kt")
|
||||||
public void testRawStrings() throws Exception {
|
public void testRawStrings() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/rawStrings.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/rawStrings.kt");
|
||||||
|
|||||||
@@ -1988,6 +1988,18 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt15235.kt")
|
||||||
|
public void testKt15235() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/stringOperations/kt15235.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedConcat.kt")
|
||||||
|
public void testNestedConcat() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/stringOperations/nestedConcat.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nonNullableStringPlus.kt")
|
@TestMetadata("nonNullableStringPlus.kt")
|
||||||
public void testNonNullableStringPlus() throws Exception {
|
public void testNonNullableStringPlus() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/stringOperations/nonNullableStringPlus.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/stringOperations/nonNullableStringPlus.kt");
|
||||||
|
|||||||
@@ -4157,6 +4157,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt9532_lv10.kt")
|
||||||
|
public void testKt9532_lv10() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/kt9532_lv10.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("long.kt")
|
@TestMetadata("long.kt")
|
||||||
public void testLong() throws Exception {
|
public void testLong() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/long.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/long.kt");
|
||||||
@@ -17510,6 +17516,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/strings"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/strings"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("constInStringTemplate.kt")
|
||||||
|
public void testConstInStringTemplate() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/constInStringTemplate.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ea35743.kt")
|
@TestMetadata("ea35743.kt")
|
||||||
public void testEa35743() throws Exception {
|
public void testEa35743() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/ea35743.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/ea35743.kt");
|
||||||
@@ -17582,6 +17594,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedConcat.kt")
|
||||||
|
public void testNestedConcat() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/nestedConcat.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("rawStrings.kt")
|
@TestMetadata("rawStrings.kt")
|
||||||
public void testRawStrings() throws Exception {
|
public void testRawStrings() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/rawStrings.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/rawStrings.kt");
|
||||||
|
|||||||
+18
@@ -4796,6 +4796,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
@TestMetadata("kt9532.kt")
|
@TestMetadata("kt9532.kt")
|
||||||
public void testKt9532() throws Exception {
|
public void testKt9532() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/kt9532.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/kt9532.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt9532_lv10.kt")
|
||||||
|
public void testKt9532_lv10() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constants/kt9532_lv10.kt");
|
||||||
try {
|
try {
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
@@ -21512,6 +21518,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/strings"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/strings"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("constInStringTemplate.kt")
|
||||||
|
public void testConstInStringTemplate() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/constInStringTemplate.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ea35743.kt")
|
@TestMetadata("ea35743.kt")
|
||||||
public void testEa35743() throws Exception {
|
public void testEa35743() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/ea35743.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/ea35743.kt");
|
||||||
@@ -21590,6 +21602,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedConcat.kt")
|
||||||
|
public void testNestedConcat() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/nestedConcat.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("rawStrings.kt")
|
@TestMetadata("rawStrings.kt")
|
||||||
public void testRawStrings() throws Exception {
|
public void testRawStrings() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/rawStrings.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/rawStrings.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user