diff --git a/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinPlugin.java b/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinPlugin.java index 7e79998ae4..fc7122dc49 100644 --- a/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinPlugin.java +++ b/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinPlugin.java @@ -82,6 +82,7 @@ private static Pass makePass() { // TODO: preference for this pass .addPass("ResugarMethods", new ResugarKotlinMethodsPass()) .addPass("ReplaceContinue", ctx -> LabelHelper.replaceContinueWithBreak(ctx.getRoot())) + .addPass("CollapseStringConcat", new CollapseStringConcatPass()) .build(); } diff --git a/plugins/kotlin/src/main/java/org/vineflower/kotlin/expr/KFunctionExprent.java b/plugins/kotlin/src/main/java/org/vineflower/kotlin/expr/KFunctionExprent.java index e1e5873404..d638fd4af9 100644 --- a/plugins/kotlin/src/main/java/org/vineflower/kotlin/expr/KFunctionExprent.java +++ b/plugins/kotlin/src/main/java/org/vineflower/kotlin/expr/KFunctionExprent.java @@ -1,5 +1,7 @@ package org.vineflower.kotlin.expr; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.gen.CodeType; @@ -196,6 +198,22 @@ public TextBuffer toJava(int indent) { .append(wrapOperandString(lstOperands.get(1), true, indent)); return buf; } + case STR_CONCAT -> { + buf.append('"'); + for (Exprent expr : lstOperands) { + if (expr instanceof ConstExprent constExpr && VarType.VARTYPE_STRING.equals(constExpr.getExprType())) { + boolean ascii = DecompilerContext.getOption(IFernflowerPreferences.ASCII_STRING_CHARACTERS); + String value = ConstExprent.convertStringToJava((String) constExpr.getValue(), ascii); + buf.append(value.replace("$", "\\$")); + } else if (expr instanceof VarExprent var) { + buf.append("$").append(var.toJava(indent)); + } else { + buf.append("${").append(expr.toJava(indent)).append("}"); + } + } + buf.append('"'); + return buf; + } } return buf.append(super.toJava(indent)); diff --git a/plugins/kotlin/src/main/java/org/vineflower/kotlin/pass/CollapseStringConcatPass.java b/plugins/kotlin/src/main/java/org/vineflower/kotlin/pass/CollapseStringConcatPass.java new file mode 100644 index 0000000000..4c02ebddb9 --- /dev/null +++ b/plugins/kotlin/src/main/java/org/vineflower/kotlin/pass/CollapseStringConcatPass.java @@ -0,0 +1,63 @@ +package org.vineflower.kotlin.pass; + +import org.jetbrains.java.decompiler.api.plugin.pass.Pass; +import org.jetbrains.java.decompiler.api.plugin.pass.PassContext; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.vineflower.kotlin.expr.KFunctionExprent; + +import java.util.ArrayList; +import java.util.List; + +public class CollapseStringConcatPass implements Pass { + @Override + public boolean run(PassContext ctx) { + return run(ctx.getRoot()); + } + + private static boolean run(Statement stat) { + boolean res = false; + + for (Statement st : stat.getStats()) { + res |= run(st); + } + + List exprs = List.of(); + if (stat instanceof BasicBlockStatement) { + exprs = stat.getExprents(); + } else if (stat instanceof IfStatement) { + exprs = ((IfStatement)stat).getHeadexprentList(); + } + + for (Exprent ex : exprs) { + res |= run(ex); + } + + return res; + } + + private static boolean run(Exprent ex) { + boolean res = false; + + for (Exprent e : ex.getAllExprents()) { + res |= run(e); + } + + if (ex instanceof KFunctionExprent kex && kex.getFuncType() == FunctionExprent.FunctionType.STR_CONCAT) { + List operands = new ArrayList<>(kex.getLstOperands()); + List lstOperands = kex.getLstOperands(); + for (Exprent child : operands) { + if (child instanceof KFunctionExprent childKex && childKex.getFuncType() == FunctionExprent.FunctionType.STR_CONCAT) { + lstOperands.addAll(lstOperands.indexOf(child), childKex.getLstOperands()); + lstOperands.remove(child); + res = true; + } + } + } + + return res; + } +} diff --git a/plugins/kotlin/src/test/java/org/vineflower/kotlin/KotlinTests.java b/plugins/kotlin/src/test/java/org/vineflower/kotlin/KotlinTests.java index cb6d438fa3..af2e758f0e 100644 --- a/plugins/kotlin/src/test/java/org/vineflower/kotlin/KotlinTests.java +++ b/plugins/kotlin/src/test/java/org/vineflower/kotlin/KotlinTests.java @@ -85,5 +85,6 @@ private void registerKotlinTests() { register(KOTLIN, "TestReflection"); register(KOTLIN, "TestConstructors"); register(KOTLIN, "TestContracts"); + register(KOTLIN, "TestStringInterpolation"); } } diff --git a/plugins/kotlin/testData/results/pkg/TestConstructors.dec b/plugins/kotlin/testData/results/pkg/TestConstructors.dec index 734acf7b17..9e096bc606 100644 --- a/plugins/kotlin/testData/results/pkg/TestConstructors.dec +++ b/plugins/kotlin/testData/results/pkg/TestConstructors.dec @@ -2,11 +2,11 @@ package pkg class TestConstructors private constructor() { public constructor(a: Int) : this() {// 4 - System.out.println("a = " + a);// 5 + System.out.println("a = $a");// 5 }// 6 public constructor(a: Int, b: Int) : this(a) {// 8 - System.out.println("b = " + b);// 9 + System.out.println("b = $b");// 9 }// 10 } diff --git a/plugins/kotlin/testData/results/pkg/TestDataClass.dec b/plugins/kotlin/testData/results/pkg/TestDataClass.dec index c183ebb686..fc504d576e 100644 --- a/plugins/kotlin/testData/results/pkg/TestDataClass.dec +++ b/plugins/kotlin/testData/results/pkg/TestDataClass.dec @@ -61,15 +61,7 @@ data class TestDataClass(dataClassVal: Regex, variableWithVeryLongName: Int, req } public open fun toString(): String { - return "TestDataClass(dataClassVal=" - + this.dataClassVal - + ", variableWithVeryLongName=" - + this.variableWithVeryLongName - + ", requestLineWrapsIfTheParamListIsTooLong=" - + this.requestLineWrapsIfTheParamListIsTooLong - + ", nullability=" - + this.nullability - + ")"; + return "TestDataClass(dataClassVal=${this.dataClassVal}, variableWithVeryLongName=${this.variableWithVeryLongName}, requestLineWrapsIfTheParamListIsTooLong=${this.requestLineWrapsIfTheParamListIsTooLong}, nullability=${this.nullability})"; } public open fun hashCode(): Int { @@ -246,22 +238,22 @@ class 'pkg/TestDataClass' { } method 'toString ()Ljava/lang/String;' { - 0 64 - 1 64 - 2 64 - 3 64 - 4 66 - 5 66 - 6 66 - 7 66 - 8 68 - 9 68 - a 68 - b 68 - c 70 - d 70 - e 70 - f 70 + 0 63 + 1 63 + 2 63 + 3 63 + 4 63 + 5 63 + 6 63 + 7 63 + 8 63 + 9 63 + a 63 + b 63 + c 63 + d 63 + e 63 + f 63 10 63 11 63 12 63 @@ -271,129 +263,129 @@ class 'pkg/TestDataClass' { } method 'hashCode ()I' { + 0 68 + 1 68 + 2 68 + 3 68 + 4 68 + 5 68 + 6 68 + 8 68 + 9 68 + a 68 + b 68 + c 68 + d 68 + e 68 + f 68 + 10 68 + 11 68 + 12 68 + 13 68 + 15 68 + 16 68 + 17 68 + 18 68 + 19 69 + 1a 69 + 1b 69 + 1c 69 + 1d 69 + 1e 69 + 1f 69 + 20 68 + 22 68 + 23 71 + 24 71 + 25 67 + 26 72 + 27 72 + 28 72 + 29 72 + 2a 72 + 2b 72 + 2c 72 + 2d 72 + 31 72 + 32 72 + 33 72 + 34 72 + 35 72 + 36 72 + 37 72 + 38 67 + 3b 67 + } + + method 'equals (Ljava/lang/Object;)Z' { 0 76 1 76 2 76 3 76 4 76 - 5 76 - 6 76 - 8 76 - 9 76 - a 76 - b 76 - c 76 - d 76 - e 76 - f 76 - 10 76 - 11 76 - 12 76 - 13 76 - 15 76 - 16 76 - 17 76 - 18 76 - 19 77 - 1a 77 - 1b 77 - 1c 77 - 1d 77 - 1e 77 - 1f 77 - 20 76 - 22 76 - 23 79 - 24 79 - 25 75 - 26 80 - 27 80 - 28 80 - 29 80 - 2a 80 - 2b 80 - 2c 80 - 2d 80 - 31 80 - 32 80 - 33 80 - 34 80 - 35 80 - 36 80 - 37 80 - 38 75 - 3b 75 - } - - method 'equals (Ljava/lang/Object;)Z' { - 0 84 - 1 84 - 2 84 - 3 84 - 4 84 - 5 85 - 6 85 - 7 86 - b 86 - c 86 - d 86 - e 87 - f 87 - 10 89 - 11 89 - 12 89 - 13 89 - 14 89 - 15 90 - 16 90 - 17 90 - 18 90 - 19 90 - 1a 90 - 1b 90 - 1c 90 - 20 90 - 21 90 - 22 90 - 23 91 - 24 91 - 25 92 - 26 92 - 27 92 - 28 92 - 29 92 - 2a 92 - 2b 92 - 2c 92 - 2d 92 - 2e 92 - 2f 92 - 30 93 - 31 93 - 32 94 - 33 94 - 34 94 - 35 94 - 36 94 - 37 94 - 38 94 - 39 94 - 3d 94 - 3e 94 - 3f 94 - 40 95 - 41 95 - 42 97 - 43 97 - 44 97 - 45 97 - 46 97 - 47 97 - 48 97 - 49 97 - 4d 97 - 4e 97 - 4f 97 + 5 77 + 6 77 + 7 78 + b 78 + c 78 + d 78 + e 79 + f 79 + 10 81 + 11 81 + 12 81 + 13 81 + 14 81 + 15 82 + 16 82 + 17 82 + 18 82 + 19 82 + 1a 82 + 1b 82 + 1c 82 + 20 82 + 21 82 + 22 82 + 23 83 + 24 83 + 25 84 + 26 84 + 27 84 + 28 84 + 29 84 + 2a 84 + 2b 84 + 2c 84 + 2d 84 + 2e 84 + 2f 84 + 30 85 + 31 85 + 32 86 + 33 86 + 34 86 + 35 86 + 36 86 + 37 86 + 38 86 + 39 86 + 3d 86 + 3e 86 + 3f 86 + 40 87 + 41 87 + 42 89 + 43 89 + 44 89 + 45 89 + 46 89 + 47 89 + 48 89 + 49 89 + 4d 89 + 4e 89 + 4f 89 } } diff --git a/plugins/kotlin/testData/results/pkg/TestDestructors.dec b/plugins/kotlin/testData/results/pkg/TestDestructors.dec index 201601657b..4e98bb8d84 100644 --- a/plugins/kotlin/testData/results/pkg/TestDestructors.dec +++ b/plugins/kotlin/testData/results/pkg/TestDestructors.dec @@ -6,21 +6,21 @@ import kotlin.jvm.internal.SourceDebugExtension @SourceDebugExtension(["SMAP\nTestDestructors.kt\nKotlin\n*S Kotlin\n*F\n+ 1 TestDestructors.kt\npkg/TestDestructors\n*L\n1#1,71:1\n68#1,3:72\n68#1,3:75\n*S KotlinDebug\n*F\n+ 1 TestDestructors.kt\npkg/TestDestructors\n*L\n49#1:72,3\n54#1:75,3\n*E\n"]) class TestDestructors { public fun destructDataClasses(x: Pair, y: Triple) { - System.out.println(x.component1() as java.lang.String + " " + x.component2() as Integer);// 8 9 - System.out.println(y.component1() as java.lang.Number + " " + y.component2() as java.lang.Boolean + " " + y.component3() as java.lang.String);// 11 12 + System.out.println("${x.component1() as java.lang.String} ${x.component2() as Integer}");// 8 9 + System.out.println("${y.component1() as java.lang.Number} ${y.component2() as java.lang.Boolean} ${y.component3() as java.lang.String}");// 11 12 }// 13 public fun destructDataClassesSpecial(x: Pair, y: Triple, Nothing?, Unit>) { - System.out.println((x.component1() as java.lang.Number).intValue() + " " + x.component2() as java.lang.String);// 19 20 + System.out.println("${(x.component1() as java.lang.Number).intValue()} ${x.component2() as java.lang.String}");// 19 20 var c: java.util.List = y.component1() as java.util.List;// 22 var d: Void = y.component2() as Void; y.component3(); - System.out.println(c + " " + d + " " + Unit.INSTANCE);// 23 + System.out.println("$c $d ${Unit.INSTANCE}");// 23 }// 24 public fun destructDataClassesSkip(x: Triple, y: Triple) { System.out.println(x.component2() as Integer);// 30 31 - System.out.println(y.component1() as java.lang.Number + " " + y.component3() as java.lang.String);// 33 34 + System.out.println("${y.component1() as java.lang.Number} ${y.component3() as java.lang.String}");// 33 34 }// 35 public fun destructorImpossible(x: Pair): String { @@ -30,16 +30,15 @@ class TestDestructors { } public fun destructExtensionFunction(x: Int) { - System.out.println("" + this.component1(x) + this.component2(x) + this.component3(x));// 44 45 + System.out.println("${this.component1(x)}${this.component2(x)}${this.component3(x)}");// 44 45 }// 46 public fun destructInlineLambdaNoInline(x: () -> Int) { System.out .println( - "" - + this.component1((x.invoke() as java.lang.Number).intValue())// 49 50 72 - + this.component2((x.invoke() as java.lang.Number).intValue())// 73 - + this.component3((x.invoke() as java.lang.Number).intValue())// 74 + "${this.component1((x.invoke() as java.lang.Number).intValue())}${this.component2((x.invoke() as java.lang.Number).intValue())}${this.component3(// 49 50 72 73 74 + (x.invoke() as java.lang.Number).intValue() + )}" ); }// 51 @@ -47,10 +46,9 @@ class TestDestructors { var var2: Function0 = TestDestructors::destructLambdaInline$lambda$0; System.out .println( - "" - + this.component1((var2.invoke() as java.lang.Number).intValue())// 55 75 - + this.component2((var2.invoke() as java.lang.Number).intValue())// 76 - + this.component3((var2.invoke() as java.lang.Number).intValue())// 77 + "${this.component1((var2.invoke() as java.lang.Number).intValue())}${this.component2((var2.invoke() as java.lang.Number).intValue())}${this.component3(// 55 75 76 77 + (var2.invoke() as java.lang.Number).intValue() + )}" ); }// 56 @@ -330,69 +328,69 @@ class 'pkg/TestDestructors' { } method 'destructInlineLambdaNoInline (Lkotlin/jvm/functions/Function0;)V' { - 6 39 - 8 39 - e 39 - f 39 - 10 39 - 11 39 - 12 39 - 13 39 - 14 39 - 15 39 - 16 39 - 17 39 - 18 39 - 19 39 - 1a 39 - 1b 39 - 1c 39 - 1d 39 - 1e 39 - 20 40 - 23 40 - 29 40 - 2a 40 - 2b 40 - 2c 40 - 2d 40 - 2e 40 - 2f 40 - 30 40 - 31 40 - 32 40 - 33 40 - 34 40 - 35 40 - 36 40 - 37 40 - 38 40 - 39 40 - 3a 40 - 3c 41 - 3f 41 - 45 41 - 46 41 - 47 41 - 48 41 - 49 41 - 4a 41 - 4b 41 - 4c 41 - 4d 41 - 4e 41 - 4f 41 - 50 41 - 51 41 - 52 41 - 53 41 - 54 41 - 55 41 - 56 41 - 59 39 - 5a 40 - 5b 41 - 5c 41 + 6 38 + 8 38 + e 38 + f 38 + 10 38 + 11 38 + 12 38 + 13 38 + 14 38 + 15 38 + 16 38 + 17 38 + 18 38 + 19 38 + 1a 38 + 1b 38 + 1c 38 + 1d 38 + 1e 38 + 20 38 + 23 38 + 29 38 + 2a 38 + 2b 38 + 2c 38 + 2d 38 + 2e 38 + 2f 38 + 30 38 + 31 38 + 32 38 + 33 38 + 34 38 + 35 38 + 36 38 + 37 38 + 38 38 + 39 38 + 3a 38 + 3c 38 + 3f 39 + 45 38 + 46 38 + 47 39 + 48 39 + 49 39 + 4a 39 + 4b 39 + 4c 39 + 4d 39 + 4e 39 + 4f 39 + 50 39 + 51 39 + 52 39 + 53 39 + 54 38 + 55 38 + 56 38 + 59 38 + 5a 38 + 5b 38 + 5c 38 5d 38 5e 38 5f 38 @@ -404,198 +402,198 @@ class 'pkg/TestDestructors' { 66 37 67 37 68 37 - 69 43 + 69 42 } method 'destructLambdaInline (I)V' { - 6 46 - 7 50 - a 50 - 10 50 - 11 50 - 12 50 - 13 50 - 14 50 - 15 50 - 16 50 - 17 50 - 18 50 - 19 50 - 1a 50 - 1b 50 - 1c 50 - 1d 50 - 1e 50 - 1f 50 - 20 50 - 21 50 - 23 51 - 26 51 - 2c 51 - 2d 51 - 2e 51 - 2f 51 - 30 51 - 31 51 - 32 51 - 33 51 - 34 51 - 35 51 - 36 51 - 37 51 - 38 51 - 39 51 - 3a 51 - 3b 51 - 3c 51 - 3d 51 - 40 52 - 43 52 - 49 52 - 4a 52 - 4b 52 - 4c 52 - 4d 52 - 4e 52 - 4f 52 - 50 52 - 51 52 - 52 52 - 53 52 - 54 52 - 55 52 - 56 52 - 57 52 - 58 52 - 59 52 - 5a 52 - 5d 50 - 5e 51 - 5f 51 - 60 52 - 61 52 - 62 49 - 63 49 - 64 49 - 65 49 - 66 49 - 67 47 - 68 47 - 69 47 - 6b 48 - 6c 48 - 6d 48 - 6e 54 + 6 45 + 7 48 + a 48 + 10 48 + 11 48 + 12 48 + 13 48 + 14 48 + 15 48 + 16 48 + 17 48 + 18 48 + 19 48 + 1a 48 + 1b 48 + 1c 48 + 1d 48 + 1e 48 + 1f 48 + 20 48 + 21 48 + 23 48 + 26 48 + 2c 48 + 2d 48 + 2e 48 + 2f 48 + 30 48 + 31 48 + 32 48 + 33 48 + 34 48 + 35 48 + 36 48 + 37 48 + 38 48 + 39 48 + 3a 48 + 3b 48 + 3c 48 + 3d 48 + 40 48 + 43 49 + 49 48 + 4a 48 + 4b 49 + 4c 49 + 4d 49 + 4e 49 + 4f 49 + 50 49 + 51 49 + 52 49 + 53 49 + 54 49 + 55 49 + 56 49 + 57 49 + 58 48 + 59 48 + 5a 48 + 5d 48 + 5e 48 + 5f 48 + 60 48 + 61 48 + 62 48 + 63 48 + 64 48 + 65 48 + 66 48 + 67 46 + 68 46 + 69 46 + 6b 47 + 6c 47 + 6d 47 + 6e 52 } method 'component1 (I)I' { - 0 57 - 1 57 - 2 57 - 3 57 - 4 57 - 5 57 - 6 57 - 7 57 - 8 57 - 9 57 - a 57 - b 57 + 0 55 + 1 55 + 2 55 + 3 55 + 4 55 + 5 55 + 6 55 + 7 55 + 8 55 + 9 55 + a 55 + b 55 } method 'component2 (I)I' { - 0 61 - 1 61 - 2 61 - 3 61 - 4 61 - 5 61 - 6 61 - 7 61 - 8 61 - 9 61 - a 61 - b 61 + 0 59 + 1 59 + 2 59 + 3 59 + 4 59 + 5 59 + 6 59 + 7 59 + 8 59 + 9 59 + a 59 + b 59 } method 'component3 (I)I' { - 0 65 - 1 65 - 2 65 - 3 65 - 4 65 - 5 65 - 6 65 - 7 65 - 8 65 - 9 65 - a 65 - b 65 + 0 63 + 1 63 + 2 63 + 3 63 + 4 63 + 5 63 + 6 63 + 7 63 + 8 63 + 9 63 + a 63 + b 63 } method 'component1 (Lkotlin/jvm/functions/Function0;)I' { - 8 69 - 9 69 - a 69 - b 69 - c 69 - d 69 - e 69 - f 69 - 10 69 - 11 69 - 12 69 - 13 69 - 14 69 - 15 69 - 16 69 - 17 69 - 18 69 + 8 67 + 9 67 + a 67 + b 67 + c 67 + d 67 + e 67 + f 67 + 10 67 + 11 67 + 12 67 + 13 67 + 14 67 + 15 67 + 16 67 + 17 67 + 18 67 } method 'component2 (Lkotlin/jvm/functions/Function0;)I' { - 8 73 - 9 73 - a 73 - b 73 - c 73 - d 73 - e 73 - f 73 - 10 73 - 11 73 - 12 73 - 13 73 - 14 73 - 15 73 - 16 73 - 17 73 - 18 73 + 8 71 + 9 71 + a 71 + b 71 + c 71 + d 71 + e 71 + f 71 + 10 71 + 11 71 + 12 71 + 13 71 + 14 71 + 15 71 + 16 71 + 17 71 + 18 71 } method 'component3 (Lkotlin/jvm/functions/Function0;)I' { - 8 77 - 9 77 - a 77 - b 77 - c 77 - d 77 - e 77 - f 77 - 10 77 - 11 77 - 12 77 - 13 77 - 14 77 - 15 77 - 16 77 - 17 77 - 18 77 + 8 75 + 9 75 + a 75 + b 75 + c 75 + d 75 + e 75 + f 75 + 10 75 + 11 75 + 12 75 + 13 75 + 14 75 + 15 75 + 16 75 + 17 75 + 18 75 } method 'destructLambdaInline$lambda$0 (I)I' { - 0 82 - 1 82 + 0 80 + 1 80 } } @@ -619,21 +617,21 @@ Lines mapping: 44 <-> 33 45 <-> 33 46 <-> 34 -49 <-> 40 -50 <-> 40 -51 <-> 44 -54 <-> 83 -55 <-> 51 -56 <-> 55 -64 <-> 58 -65 <-> 62 -66 <-> 66 -68 <-> 70 -69 <-> 74 -70 <-> 78 -72 <-> 40 -73 <-> 41 -74 <-> 42 -75 <-> 51 -76 <-> 52 -77 <-> 53 +49 <-> 39 +50 <-> 39 +51 <-> 43 +54 <-> 81 +55 <-> 49 +56 <-> 53 +64 <-> 56 +65 <-> 60 +66 <-> 64 +68 <-> 68 +69 <-> 72 +70 <-> 76 +72 <-> 39 +73 <-> 39 +74 <-> 39 +75 <-> 49 +76 <-> 49 +77 <-> 49 diff --git a/plugins/kotlin/testData/results/pkg/TestForRange.dec b/plugins/kotlin/testData/results/pkg/TestForRange.dec index 04ed20c1ef..de741b0665 100644 --- a/plugins/kotlin/testData/results/pkg/TestForRange.dec +++ b/plugins/kotlin/testData/results/pkg/TestForRange.dec @@ -32,7 +32,7 @@ class TestForRange { public fun testIntStepX(x: Int) { if (x <= 0) { - throw new IllegalArgumentException("Step must be positive, was: " + x + "."); + throw new IllegalArgumentException("Step must be positive, was: $x."); } else { var i: Int = 1; var var3: Int = ProgressionUtilKt.getProgressionLastElement(1, 100, x); @@ -72,7 +72,7 @@ class TestForRange { public fun testIntDownToStepX(x: Int) { if (x <= 0) { - throw new IllegalArgumentException("Step must be positive, was: " + x + "."); + throw new IllegalArgumentException("Step must be positive, was: $x."); } else { var var2: Int = -x; var i: Int = 100; @@ -161,7 +161,7 @@ class TestForRange { public fun testIntYStepX(x: Int, y: Int, z: Int) { if (z <= 0) { - throw new IllegalArgumentException("Step must be positive, was: " + z + "."); + throw new IllegalArgumentException("Step must be positive, was: $z."); } else { var i: Int = x; var var5: Int = ProgressionUtilKt.getProgressionLastElement(x, y, z); diff --git a/plugins/kotlin/testData/results/pkg/TestLabeledJumps.dec b/plugins/kotlin/testData/results/pkg/TestLabeledJumps.dec index bf01a116ed..2b3aceb78b 100644 --- a/plugins/kotlin/testData/results/pkg/TestLabeledJumps.dec +++ b/plugins/kotlin/testData/results/pkg/TestLabeledJumps.dec @@ -9,7 +9,7 @@ class TestLabeledJumps { continue label24; } - System.out.println(j + " " + i);// 11 + System.out.println("$j $i");// 11 } System.out.println("loop");// 14 @@ -24,7 +24,7 @@ class TestLabeledJumps { break label22; } - System.out.println(j + " " + i);// 25 + System.out.println("$j $i");// 25 } } diff --git a/plugins/kotlin/testData/results/pkg/TestNonInlineLambda.dec b/plugins/kotlin/testData/results/pkg/TestNonInlineLambda.dec index 54df97d19e..65159bd073 100644 --- a/plugins/kotlin/testData/results/pkg/TestNonInlineLambda.dec +++ b/plugins/kotlin/testData/results/pkg/TestNonInlineLambda.dec @@ -52,13 +52,13 @@ open class TestNonInlineLambda { var y: ObjectRef = new ObjectRef();// 60 y.element = x; this.execute(TestNonInlineLambda::testCaptureMutableObject$lambda$9);// 61 - y.element = y.element + "!!";// 64 + y.element = "${y.element}!!";// 64 this.execute(TestNonInlineLambda::testCaptureMutableObject$lambda$10);// 65 - y.element = "" + y.element + y.element + y.element;// 68 + y.element = "${y.element}${y.element}${y.element}";// 68 this.execute(TestNonInlineLambda::testCaptureMutableObject$lambda$11);// 69 y.element = "Hello: ";// 72 this.execute(TestNonInlineLambda::testCaptureMutableObject$lambda$12);// 73 - y.element = y.element + x;// 76 + y.element = "${y.element}$x";// 76 this.execute(TestNonInlineLambda::testCaptureMutableObject$lambda$13);// 77 }// 80 @@ -73,7 +73,7 @@ open class TestNonInlineLambda { var y: ObjectRef = new ObjectRef();// 98 y.element = ""; this.execute(TestNonInlineLambda::testCaptureAndMutateString$lambda$16);// 99 - y.element = "Hello: " + x;// 105 + y.element = "Hello: $x";// 105 this.execute(TestNonInlineLambda::testCaptureAndMutateString$lambda$17);// 106 }// 112 @@ -203,7 +203,7 @@ open class TestNonInlineLambda { @JvmStatic fun `testCaptureAndMutateString$lambda$16`(`$y`: ObjectRef): Unit { while (((java.lang.String)$y.element).length() < 10) {// 100 - `$y`.element = " " + `$y`.element;// 101 + `$y`.element = " ${`$y`.element}";// 101 System.out.println(`$y`.element);// 102 } @@ -228,7 +228,7 @@ open class TestNonInlineLambda { @JvmStatic fun `testCapturePublicMutableStringField$lambda$19`(`this$0`: TestNonInlineLambda): Unit { - `this$0`.stringField = `this$0`.stringField + "!";// 123 + `this$0`.stringField = "${`this$0`.stringField}!";// 123 return Unit.INSTANCE; } @@ -240,7 +240,7 @@ open class TestNonInlineLambda { @JvmStatic fun `testCapturePrivateMutableStringField$lambda$21`(`this$0`: TestNonInlineLambda): Unit { - `this$0`.privateStringField = `this$0`.privateStringField + "!";// 135 + `this$0`.privateStringField = "${`this$0`.privateStringField}!";// 135 return Unit.INSTANCE; } } diff --git a/plugins/kotlin/testData/results/pkg/TestParams.dec b/plugins/kotlin/testData/results/pkg/TestParams.dec index 76d7822a36..56f50eee33 100644 --- a/plugins/kotlin/testData/results/pkg/TestParams.dec +++ b/plugins/kotlin/testData/results/pkg/TestParams.dec @@ -18,7 +18,7 @@ class TestParams { } public fun printMessageWithPrefix(message: String, prefix: String = "Info") { - System.out.println("[" + prefix + "] " + message);// 19 + System.out.println("[$prefix] $message");// 19 }// 20 @JvmStatic diff --git a/plugins/kotlin/testData/results/pkg/TestSmartCasts.dec b/plugins/kotlin/testData/results/pkg/TestSmartCasts.dec index 79b4ba3c08..ec5d10e7e6 100644 --- a/plugins/kotlin/testData/results/pkg/TestSmartCasts.dec +++ b/plugins/kotlin/testData/results/pkg/TestSmartCasts.dec @@ -6,21 +6,21 @@ class TestSmartCasts { return o as java.lang.String;// 21 } else { if (o is TestSmartCasts.A.B) {// 24 - System.out.println("B: " + o); + System.out.println("B: $o"); } else { if (o !is TestSmartCasts.A.C) {// 25 if (o is Pair) {// 26 - return "<" + this.testWhen((o as Pair).getFirst()) + ", " + this.testWhen((o as Pair).getSecond()) + ">"; + return "<${this.testWhen((o as Pair).getFirst())}, ${this.testWhen((o as Pair).getSecond())}>"; } if (o == null) {// 27 return "null"; } - return "else: " + o;// 28 + return "else: $o";// 28 } - System.out.println("C: " + o); + System.out.println("C: $o"); } return (o as TestSmartCasts.A).test();// 31 @@ -28,7 +28,7 @@ class TestSmartCasts { } public fun testIf(a: Any?): String { - return if (a !is TestSmartCasts.A.B && a !is TestSmartCasts.A.C) "else: " + a else (a as TestSmartCasts.A).test();// 35 36 39 + return if (a !is TestSmartCasts.A.B && a !is TestSmartCasts.A.C) "else: $a" else (a as TestSmartCasts.A).test();// 35 36 39 } public fun testIf2(a: Any?): String { @@ -49,7 +49,7 @@ class TestSmartCasts { } } - return "else: " + a;// 60 + return "else: $a";// 60 } public fun testCast(a: Any?) { diff --git a/plugins/kotlin/testData/results/pkg/TestStringInterpolation.dec b/plugins/kotlin/testData/results/pkg/TestStringInterpolation.dec new file mode 100644 index 0000000000..ab25b196c5 --- /dev/null +++ b/plugins/kotlin/testData/results/pkg/TestStringInterpolation.dec @@ -0,0 +1,128 @@ +package pkg + +class TestStringInterpolation { + public final val x: Int = 5 + + + public fun stringInterpolation(x: Int, y: String) { + System.out.println("$x $y");// 5 + }// 6 + + public fun testConstant(x: Int) { + System.out.println("$x 5");// 9 + }// 10 + + public fun testExpression(x: Int) { + System.out.println("$x ${x + 1}");// 13 + }// 14 + + public fun testProperty() { + System.out.println("${this.x}!");// 18 + }// 19 + + public fun testLiteralClass() { + System.out.println("${TestStringInterpolation::class.java}!");// 22 + }// 23 +} + +class 'pkg/TestStringInterpolation' { + method 'stringInterpolation (ILjava/lang/String;)V' { + 6 7 + 7 7 + 8 7 + 9 7 + a 7 + b 7 + c 7 + d 7 + e 7 + f 7 + 11 7 + 12 7 + 13 7 + 14 8 + } + + method 'testConstant (I)V' { + 0 11 + 1 11 + 2 11 + 3 11 + 4 11 + 5 11 + 6 11 + 7 11 + 8 11 + a 11 + b 11 + c 11 + d 12 + } + + method 'testExpression (I)V' { + 0 15 + 1 15 + 2 15 + 3 15 + 4 15 + 5 15 + 6 15 + 7 15 + 8 15 + 9 15 + a 15 + b 15 + d 15 + e 15 + f 15 + 10 16 + } + + method 'testProperty ()V' { + 0 19 + 1 19 + 2 19 + 3 19 + 4 19 + 5 19 + 6 19 + 7 19 + 8 19 + 9 19 + a 19 + b 19 + d 19 + e 19 + f 19 + 10 20 + } + + method 'testLiteralClass ()V' { + 0 23 + 1 23 + 2 23 + 3 23 + 4 23 + 5 23 + 6 23 + 7 23 + 8 23 + 9 23 + b 23 + c 23 + d 23 + e 24 + } +} + +Lines mapping: +5 <-> 8 +6 <-> 9 +9 <-> 12 +10 <-> 13 +13 <-> 16 +14 <-> 17 +18 <-> 20 +19 <-> 21 +22 <-> 24 +23 <-> 25 diff --git a/plugins/kotlin/testData/results/pkg/TestTopLevelKt.dec b/plugins/kotlin/testData/results/pkg/TestTopLevelKt.dec index caac445fe1..a15caa44aa 100644 --- a/plugins/kotlin/testData/results/pkg/TestTopLevelKt.dec +++ b/plugins/kotlin/testData/results/pkg/TestTopLevelKt.dec @@ -8,14 +8,48 @@ public const val topLevelConstVal: Int = 926 public fun topLevelFun() { }// 5 +public fun interpolateTopLevel() { + System.out.println("topLevelVar = ${topLevelVar}");// 14 + System.out.println("topLevelConstVal = 926");// 15 +}// 16 + class 'pkg/TestTopLevelKt' { method 'topLevelFun ()V' { 2 8 } + + method 'interpolateTopLevel ()V' { + 0 11 + 1 11 + 2 11 + 3 11 + 4 11 + 5 11 + 6 11 + 7 11 + 8 11 + 9 11 + a 11 + c 11 + d 11 + e 11 + f 12 + 10 12 + 11 12 + 12 12 + 13 12 + 15 12 + 16 12 + 17 12 + 18 13 + } } Lines mapping: 5 <-> 9 +14 <-> 12 +15 <-> 13 +16 <-> 14 Not mapped: 4 diff --git a/plugins/kotlin/testData/results/pkg/TestTryCatchExpressions.dec b/plugins/kotlin/testData/results/pkg/TestTryCatchExpressions.dec index f89af1f5b5..ccdb2b3e82 100644 --- a/plugins/kotlin/testData/results/pkg/TestTryCatchExpressions.dec +++ b/plugins/kotlin/testData/results/pkg/TestTryCatchExpressions.dec @@ -60,7 +60,7 @@ class TestTryCatchExpressions { var4 = var10002; } - var10000.test0(var13 + var4);// 18 + var10000.test0("$var13$var4");// 18 }// 30 public fun test2(a: String, b: String) { @@ -108,7 +108,7 @@ class TestTryCatchExpressions { var10003 = ""; } - var4 = var15 + "!!" + var10003;// 56 + var4 = "$var15!!$var10003";// 56 } var10000.test1(var10001, var4);// 34 diff --git a/plugins/kotlin/testData/src/kt/pkg/TestStringInterpolation.kt b/plugins/kotlin/testData/src/kt/pkg/TestStringInterpolation.kt new file mode 100644 index 0000000000..6e0a3dbbec --- /dev/null +++ b/plugins/kotlin/testData/src/kt/pkg/TestStringInterpolation.kt @@ -0,0 +1,24 @@ +package pkg + +class TestStringInterpolation { + fun stringInterpolation(x: Int, y: String) { + println("$x $y") + } + + fun testConstant(x: Int) { + println("$x ${5}") + } + + fun testExpression(x: Int) { + println("$x ${x + 1}") + } + + val x = 5 + fun testProperty() { + println("$x!") + } + + fun testLiteralClass() { + println("${TestStringInterpolation::class.java}!") + } +} diff --git a/plugins/kotlin/testData/src/kt/pkg/TestTopLevel.kt b/plugins/kotlin/testData/src/kt/pkg/TestTopLevel.kt index 74b94d49f6..94a0ff6793 100644 --- a/plugins/kotlin/testData/src/kt/pkg/TestTopLevel.kt +++ b/plugins/kotlin/testData/src/kt/pkg/TestTopLevel.kt @@ -9,3 +9,8 @@ var topLevelVar = 42 val topLevelVal = Regex("") const val topLevelConstVal = 926 + +fun interpolateTopLevel() { + println("topLevelVar = $topLevelVar") + println("topLevelConstVal = $topLevelConstVal") +}