From cb68d9c1868e9fbb3e58cdfd606274e070db5b31 Mon Sep 17 00:00:00 2001 From: Marko Elezovic Date: Fri, 27 Nov 2015 03:36:27 +0100 Subject: SI-9571 Avoid boxing primitives in string concatenation --- test/files/specialized/fft.check | 2 +- .../tools/nsc/backend/jvm/StringConcatTest.scala | 135 +++++++++++++++------ 2 files changed, 100 insertions(+), 37 deletions(-) (limited to 'test') diff --git a/test/files/specialized/fft.check b/test/files/specialized/fft.check index 74cb9bb3b5..5283c6cbe2 100644 --- a/test/files/specialized/fft.check +++ b/test/files/specialized/fft.check @@ -1,4 +1,4 @@ Processing 65536 items Boxed doubles: 0 -Boxed ints: 2 +Boxed ints: 0 Boxed longs: 1179811 diff --git a/test/junit/scala/tools/nsc/backend/jvm/StringConcatTest.scala b/test/junit/scala/tools/nsc/backend/jvm/StringConcatTest.scala index 80cde6c9a9..2a9b8f7198 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/StringConcatTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/StringConcatTest.scala @@ -24,47 +24,110 @@ class StringConcatTest extends ClearAfterClass { ClearAfterClass.stateToClear = StringConcatTest val compiler = StringConcatTest.compiler - val commonPreInstructions = List(Label(0), LineNumber(1, Label(0)), TypeOp(NEW, "java/lang/StringBuilder"), Op(DUP), Invoke(INVOKESPECIAL, "java/lang/StringBuilder", "", "()V", false), Ldc(LDC, "abc"), Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false), VarOp(ALOAD, 0)) - - val commonPostInstructions = List(Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false), Op(ARETURN), Label(12)) - - def instructionsWithCommonParts(instructions: List[Instruction]) = commonPreInstructions ++ instructions ++ commonPostInstructions - - def instructionsForResultMethod(code: String): List[Instruction] = { - val methods = compileMethods(compiler)(code) - val resultMethod = methods.find(_.name == "result").get - instructionsFromMethod(resultMethod) - } - @Test - def concatStringToStringBuilder: Unit = { - val code = """ def string = "def"; def result = "abc" + string """ - val actualInstructions = instructionsForResultMethod(code) - val expectedInstructions = instructionsWithCommonParts(List(Invoke(INVOKEVIRTUAL, "C", "string", "()Ljava/lang/String;", false), Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false))) - assertSameCode(actualInstructions, expectedInstructions) - } + def appendOverloadNoBoxing(): Unit = { + val code = + """class C { + | def t1( + | v: Unit, + | z: Boolean, + | c: Char, + | b: Byte, + | s: Short, + | i: Int, + | l: Long, + | f: Float, + | d: Double, + | str: String, + | sbuf: java.lang.StringBuffer, + | chsq: java.lang.CharSequence, + | chrs: Array[Char]) = str + this + v + z + c + b + s + i + f + l + d + sbuf + chsq + chrs + | + | // similar, but starting off with any2stringadd + | def t2( + | v: Unit, + | z: Boolean, + | c: Char, + | b: Byte, + | s: Short, + | i: Int, + | l: Long, + | f: Float, + | d: Double, + | str: String, + | sbuf: java.lang.StringBuffer, + | chsq: java.lang.CharSequence, + | chrs: Array[Char]) = this + str + v + z + c + b + s + i + f + l + d + sbuf + chsq + chrs + |} + """.stripMargin + val List(c) = compileClasses(compiler)(code) - @Test - def concatStringBufferToStringBuilder: Unit = { - val code = """ def stringBuffer = new java.lang.StringBuffer("def"); def result = "abc" + stringBuffer """ - val actualInstructions = instructionsForResultMethod(code) - val expectedInstructions = instructionsWithCommonParts(List(Invoke(INVOKEVIRTUAL, "C", "stringBuffer", "()Ljava/lang/StringBuffer;", false), Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/StringBuffer;)Ljava/lang/StringBuilder;", false))) - assertSameCode(actualInstructions, expectedInstructions) - } + def invokeNameDesc(m: String): List[String] = getSingleMethod(c, m).instructions collect { + case Invoke(_, _, name, desc, _) => name + desc + } + assertEquals(invokeNameDesc("t1"), List( + "()V", + "append(Ljava/lang/String;)Ljava/lang/StringBuilder;", + "append(Ljava/lang/Object;)Ljava/lang/StringBuilder;", + "append(Ljava/lang/Object;)Ljava/lang/StringBuilder;", + "append(Z)Ljava/lang/StringBuilder;", + "append(C)Ljava/lang/StringBuilder;", + "append(I)Ljava/lang/StringBuilder;", + "append(I)Ljava/lang/StringBuilder;", + "append(I)Ljava/lang/StringBuilder;", + "append(F)Ljava/lang/StringBuilder;", + "append(J)Ljava/lang/StringBuilder;", + "append(D)Ljava/lang/StringBuilder;", + "append(Ljava/lang/StringBuffer;)Ljava/lang/StringBuilder;", + "append(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;", + "append(Ljava/lang/Object;)Ljava/lang/StringBuilder;", // test that we're not using the [C overload + "toString()Ljava/lang/String;")) - @Test - def concatCharSequenceToStringBuilder: Unit = { - val code = """ def charSequence: CharSequence = "def"; def result = "abc" + charSequence """ - val actualInstructions = instructionsForResultMethod(code) - val expectedInstructions = instructionsWithCommonParts(List(Invoke(INVOKEVIRTUAL, "C", "charSequence", "()Ljava/lang/CharSequence;", false), Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;", false))) - assertSameCode(actualInstructions, expectedInstructions) + assertEquals(invokeNameDesc("t2"), List( + "()V", + "any2stringadd(Ljava/lang/Object;)Ljava/lang/Object;", + "$plus$extension(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String;", + "append(Ljava/lang/String;)Ljava/lang/StringBuilder;", + "append(Ljava/lang/Object;)Ljava/lang/StringBuilder;", + "append(Z)Ljava/lang/StringBuilder;", + "append(C)Ljava/lang/StringBuilder;", + "append(I)Ljava/lang/StringBuilder;", + "append(I)Ljava/lang/StringBuilder;", + "append(I)Ljava/lang/StringBuilder;", + "append(F)Ljava/lang/StringBuilder;", + "append(J)Ljava/lang/StringBuilder;", + "append(D)Ljava/lang/StringBuilder;", + "append(Ljava/lang/StringBuffer;)Ljava/lang/StringBuilder;", + "append(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;", + "append(Ljava/lang/Object;)Ljava/lang/StringBuilder;", // test that we're not using the [C overload + "toString()Ljava/lang/String;")) } @Test - def concatIntToStringBuilder: Unit = { - val code = """ def int = 123; def result = "abc" + int """ - val actualInstructions = instructionsForResultMethod(code) - val expectedInstructions = instructionsWithCommonParts(List(Invoke(INVOKEVIRTUAL, "C", "int", "()I", false), Invoke(INVOKESTATIC, "scala/runtime/BoxesRunTime", "boxToInteger", "(I)Ljava/lang/Integer;", false), Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;", false))) - assertSameCode(actualInstructions, expectedInstructions) + def concatPrimitiveCorrectness(): Unit = { + val obj: Object = new { override def toString = "TTT" } + def t( + v: Unit, + z: Boolean, + c: Char, + b: Byte, + s: Short, + i: Int, + l: Long, + f: Float, + d: Double, + str: String, + sbuf: java.lang.StringBuffer, + chsq: java.lang.CharSequence, + chrs: Array[Char]) = { + val s1 = str + obj + v + z + c + b + s + i + f + l + d + sbuf + chsq + chrs + val s2 = obj + str + v + z + c + b + s + i + f + l + d + sbuf + chsq + chrs + s1 + "//" + s2 + } + def sbuf = { val r = new java.lang.StringBuffer(); r.append("sbuf"); r } + def chsq: java.lang.CharSequence = "chsq" + val s = t((), true, 'd', 3: Byte, 12: Short, 3, -32l, 12.3f, -4.2d, "me", sbuf, chsq, Array('a', 'b')) + val r = s.replaceAll("""\[C@\w+""", "") + assertEquals(r, "meTTT()trued312312.3-32-4.2sbufchsq//TTTme()trued312312.3-32-4.2sbufchsq") } } -- cgit v1.2.3