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 --- .../scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala | 14 +++++++++++--- .../scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala | 15 +++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 59d584c370..1170e60d85 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -999,9 +999,17 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case concatenations => bc.genStartConcat(tree.pos) for (elem <- concatenations) { - val kind = tpeTK(elem) - genLoad(elem, kind) - bc.genStringConcat(kind, elem.pos) + val loadedElem = elem match { + case Apply(boxOp, value :: Nil) if currentRun.runDefinitions.isBox(boxOp.symbol) => + // Eliminate boxing of primitive values. Boxing is introduced by erasure because + // there's only a single synthetic `+` method "added" to the string class. + value + + case _ => elem + } + val elemType = tpeTK(loadedElem) + genLoad(loadedElem, elemType) + bc.genConcat(elemType, loadedElem.pos) } bc.genEndConcat(tree.pos) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index 328a8187c8..0a95bc5e39 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -197,16 +197,19 @@ abstract class BCodeIdiomatic extends SubComponent { /* * can-multi-thread */ - final def genStringConcat(el: BType, pos: Position): Unit = { - val jtype = el match { + def genConcat(elemType: BType, pos: Position): Unit = { + val paramType = elemType match { case ct: ClassBType if ct.isSubtypeOf(StringRef).get => StringRef case ct: ClassBType if ct.isSubtypeOf(jlStringBufferRef).get => jlStringBufferRef case ct: ClassBType if ct.isSubtypeOf(jlCharSequenceRef).get => jlCharSequenceRef - case rt: RefBType => ObjectRef - case pt: PrimitiveBType => pt // Currently this ends up being boxed in erasure + // Don't match for `ArrayBType(CHAR)`, even though StringBuilder has such an overload: + // `"a" + Array('b')` should NOT be "ab", but "a[C@...". + case _: RefBType => ObjectRef + // jlStringBuilder does not have overloads for byte and short, but we can just use the int version + case BYTE | SHORT => INT + case pt: PrimitiveBType => pt } - - val bt = MethodBType(List(jtype), jlStringBuilderRef) + val bt = MethodBType(List(paramType), jlStringBuilderRef) invokevirtual(JavaStringBuilderClassName, "append", bt.descriptor, pos) } -- cgit v1.2.3