I’m not sure if this is intended behavior, but let’s take the following Kotlin sample:
fun main(args: Array<String>) {
val sb = StringBuilder() // first half
sb.append("strings are cool ${sb.length}")
val sb2 = StringBuilder() // second half
sb2.append("strings are cool ").append(sb2.length)
}
The following bytecode for the JVM is then generated.
// access flags 0x19
public final static main([Ljava/lang/String;)V
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC "args"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1 // start of the first half
LINENUMBER 4 L1
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ASTORE 1
L2
LINENUMBER 5 L2
ALOAD 1
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
LDC "strings are cool "
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.length ()I
INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
POP
L3 // start of the second half
LINENUMBER 7 L3
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ASTORE 2
L4
LINENUMBER 8 L4
ALOAD 2
LDC "strings are cool "
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.length ()I
INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
POP
L5
LINENUMBER 9 L5
RETURN
L6
LOCALVARIABLE sb2 Ljava/lang/StringBuilder; L4 L6 2
LOCALVARIABLE sb Ljava/lang/StringBuilder; L2 L6 1
LOCALVARIABLE args [Ljava/lang/String; L0 L6 0
MAXSTACK = 3
MAXLOCALS = 3
According to the bytecode, it looks like the first StringBuilder appending a string template ends up having to instantiate a new StringBuilder to process the template and then add it to the sb
StringBuilder. While the 2nd example, after sb2
was made, shows no instantiation for another StringBuilder as expected.
Is this intended behavior? Could the compiler be written to optimize the first example into the second? IntelliJ also detects this inspection in Java, but not Kotlin.