diff options
Diffstat (limited to 'src')
21 files changed, 359 insertions, 261 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index 4a10756468..328a8187c8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -40,7 +40,7 @@ abstract class BCodeIdiomatic extends SubComponent { if (emitStackMapFrame) asm.ClassWriter.COMPUTE_FRAMES else 0 ) - val StringBuilderClassName = "scala/collection/mutable/StringBuilder" + lazy val JavaStringBuilderClassName = jlStringBuilderRef.internalName val EMPTY_STRING_ARRAY = Array.empty[String] val EMPTY_INT_ARRAY = Array.empty[Int] @@ -184,10 +184,10 @@ abstract class BCodeIdiomatic extends SubComponent { * can-multi-thread */ final def genStartConcat(pos: Position): Unit = { - jmethod.visitTypeInsn(Opcodes.NEW, StringBuilderClassName) + jmethod.visitTypeInsn(Opcodes.NEW, JavaStringBuilderClassName) jmethod.visitInsn(Opcodes.DUP) invokespecial( - StringBuilderClassName, + JavaStringBuilderClassName, INSTANCE_CONSTRUCTOR_NAME, "()V", pos @@ -198,21 +198,23 @@ abstract class BCodeIdiomatic extends SubComponent { * can-multi-thread */ final def genStringConcat(el: BType, pos: Position): Unit = { - - val jtype = - if (el.isArray || el.isClass) ObjectRef - else el + val jtype = el 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 + } val bt = MethodBType(List(jtype), jlStringBuilderRef) - - invokevirtual(StringBuilderClassName, "append", bt.descriptor, pos) + invokevirtual(JavaStringBuilderClassName, "append", bt.descriptor, pos) } /* * can-multi-thread */ final def genEndConcat(pos: Position): Unit = { - invokevirtual(StringBuilderClassName, "toString", "()Ljava/lang/String;", pos) + invokevirtual(JavaStringBuilderClassName, "toString", "()Ljava/lang/String;", pos) } /* diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala index 8bb71a386f..6d322d1a34 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala @@ -95,7 +95,9 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { lazy val ObjectRef : ClassBType = classBTypeFromSymbol(ObjectClass) lazy val StringRef : ClassBType = classBTypeFromSymbol(StringClass) lazy val PredefRef : ClassBType = classBTypeFromSymbol(PredefModule.moduleClass) - lazy val jlStringBuilderRef : ClassBType = classBTypeFromSymbol(StringBuilderClass) + lazy val jlStringBuilderRef : ClassBType = classBTypeFromSymbol(JavaStringBuilderClass) + lazy val jlStringBufferRef : ClassBType = classBTypeFromSymbol(JavaStringBufferClass) + lazy val jlCharSequenceRef : ClassBType = classBTypeFromSymbol(JavaCharSequenceClass) lazy val jlThrowableRef : ClassBType = classBTypeFromSymbol(ThrowableClass) lazy val jlCloneableRef : ClassBType = classBTypeFromSymbol(JavaCloneableClass) // java/lang/Cloneable lazy val jiSerializableRef : ClassBType = classBTypeFromSymbol(JavaSerializableClass) // java/io/Serializable @@ -343,6 +345,8 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: def StringRef : ClassBType = _coreBTypes.StringRef def PredefRef : ClassBType = _coreBTypes.PredefRef def jlStringBuilderRef : ClassBType = _coreBTypes.jlStringBuilderRef + def jlStringBufferRef : ClassBType = _coreBTypes.jlStringBufferRef + def jlCharSequenceRef : ClassBType = _coreBTypes.jlCharSequenceRef def jlThrowableRef : ClassBType = _coreBTypes.jlThrowableRef def jlCloneableRef : ClassBType = _coreBTypes.jlCloneableRef def jiSerializableRef : ClassBType = _coreBTypes.jiSerializableRef diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala index 4ecaf17a10..16fe2e5cff 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala @@ -21,7 +21,7 @@ class BoxUnbox[BT <: BTypes](val btypes: BT) { import backendUtils._ /** - * Eliminate box-unbox paris within `method`. Such appear commonly after closure elimination: + * Eliminate box-unbox pairs within `method`. Such appear commonly after closure elimination: * * def t2 = { * val f = (b: Byte, i: Int) => i + b // no specialized variant for this function type @@ -767,7 +767,7 @@ class BoxUnbox[BT <: BTypes](val btypes: BT) { private def isSpecializedTupleClass(tupleClass: InternalName) = specializedTupleClassR.pattern.matcher(tupleClass).matches private val specializedTupleGetterR = "_[12]\\$mc[IJDCZ]\\$sp".r - private def isSpecializedTupleGetter(mi: MethodInsnNode) = specializedTupleGetterR.pattern.matcher(mi.name)matches + private def isSpecializedTupleGetter(mi: MethodInsnNode) = specializedTupleGetterR.pattern.matcher(mi.name).matches private val tupleGetterR = "_\\d\\d?".r private def isTupleGetter(mi: MethodInsnNode) = tupleGetterR.pattern.matcher(mi.name).matches diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala index c1fdb7eb59..f486bb0cb9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala @@ -771,154 +771,173 @@ object LocalOptImpls { // A set of all exception handlers that guard the current instruction, required for simplifyGotoReturn var activeHandlers = Set.empty[TryCatchBlockNode] - // Instructions that need to be removed. simplifyBranchOverGoto returns an instruction to be - // removed. It cannot remove it itself because the instruction may be the successor of the current - // instruction of the iterator, which is not supported in ASM. - var instructionsToRemove = Set.empty[AbstractInsnNode] + val jumpInsns = mutable.LinkedHashMap.empty[JumpInsnNode, Boolean] - val iterator = method.instructions.iterator() - while (iterator.hasNext) { - val instruction = iterator.next() + for (insn <- method.instructions.iterator().asScala) insn match { + case l: LabelNode => + activeHandlers ++= allHandlers.filter(_.start == l) + activeHandlers = activeHandlers.filter(_.end != l) - instruction match { - case l: LabelNode => - activeHandlers ++= allHandlers.filter(_.start == l) - activeHandlers = activeHandlers.filter(_.end != l) - case _ => + case ji: JumpInsnNode => + jumpInsns(ji) = activeHandlers.nonEmpty + + case _ => + } + + var _jumpTargets: Set[AbstractInsnNode] = null + def jumpTargets = { + if (_jumpTargets == null) { + _jumpTargets = jumpInsns.keysIterator.map(_.label).toSet } + _jumpTargets + } - if (instructionsToRemove(instruction)) { - iterator.remove() - instructionsToRemove -= instruction - } else if (isJumpNonJsr(instruction)) { // fast path - all of the below only treat jumps - var jumpRemoved = simplifyThenElseSameTarget(method, instruction) + def removeJumpFromMap(jump: JumpInsnNode) = { + jumpInsns.remove(jump) + _jumpTargets = null + } - if (!jumpRemoved) { - changed = collapseJumpChains(instruction) || changed - jumpRemoved = removeJumpToSuccessor(method, instruction) + def replaceJumpByPop(jump: JumpInsnNode) = { + removeJumpAndAdjustStack(method, jump) + removeJumpFromMap(jump) + } - if (!jumpRemoved) { - val staleGoto = simplifyBranchOverGoto(method, instruction) - instructionsToRemove ++= staleGoto - changed ||= staleGoto.nonEmpty - changed = simplifyGotoReturn(method, instruction, inTryBlock = activeHandlers.nonEmpty) || changed - } + /** + * Removes a conditional jump if it is followed by a GOTO to the same destination. + * + * CondJump l; [nops]; GOTO l; [...] + * POP*; [nops]; GOTO l; [...] + * + * Introduces 1 or 2 POP instructions, depending on the number of values consumed by the CondJump. + */ + def simplifyThenElseSameTarget(insn: AbstractInsnNode): Boolean = insn match { + case ConditionalJump(jump) => + nextExecutableInstruction(insn) match { + case Some(Goto(elseJump)) if sameTargetExecutableInstruction(jump, elseJump) => + replaceJumpByPop(jump) + true + + case _ => false } - changed ||= jumpRemoved - } + + case _ => false } - assert(instructionsToRemove.isEmpty, "some optimization required removing a previously traversed instruction. add `instructionsToRemove.foreach(method.instructions.remove)`") - changed - } - /** - * Removes a conditional jump if it is followed by a GOTO to the same destination. - * - * CondJump l; [nops]; GOTO l; [...] - * POP*; [nops]; GOTO l; [...] - * - * Introduces 1 or 2 POP instructions, depending on the number of values consumed by the CondJump. - */ - private def simplifyThenElseSameTarget(method: MethodNode, instruction: AbstractInsnNode): Boolean = instruction match { - case ConditionalJump(jump) => - nextExecutableInstruction(instruction) match { - case Some(Goto(elseJump)) if sameTargetExecutableInstruction(jump, elseJump) => - removeJumpAndAdjustStack(method, jump) + /** + * Replace jumps to a sequence of GOTO instructions by a jump to the final destination. + * + * Jump l; [any ops]; l: GOTO m; [any ops]; m: GOTO n; [any ops]; n: NotGOTO; [...] + * => Jump n; [rest unchanged] + * + * If there's a loop of GOTOs, the initial jump is replaced by one of the labels in the loop. + */ + def collapseJumpChains(insn: AbstractInsnNode): Boolean = insn match { + case JumpNonJsr(jump) => + val target = finalJumpTarget(jump) + if (jump.label == target) false else { + jump.label = target + _jumpTargets = null true + } - case _ => false - } - case _ => false - } + case _ => false + } - /** - * Replace jumps to a sequence of GOTO instructions by a jump to the final destination. - * - * Jump l; [any ops]; l: GOTO m; [any ops]; m: GOTO n; [any ops]; n: NotGOTO; [...] - * => Jump n; [rest unchanged] - * - * If there's a loop of GOTOs, the initial jump is replaced by one of the labels in the loop. - */ - private def collapseJumpChains(instruction: AbstractInsnNode): Boolean = instruction match { - case JumpNonJsr(jump) => - val target = finalJumpTarget(jump) - if (jump.label == target) false else { - jump.label = target + /** + * Eliminates unnecessary jump instructions + * + * Jump l; [nops]; l: [...] + * => POP*; [nops]; l: [...] + * + * Introduces 0, 1 or 2 POP instructions, depending on the number of values consumed by the Jump. + */ + def removeJumpToSuccessor(insn: AbstractInsnNode): Boolean = insn match { + case JumpNonJsr(jump) if nextExecutableInstruction(jump, alsoKeep = Set(jump.label)) contains jump.label => + replaceJumpByPop(jump) true - } - case _ => false - } + case _ => false + } - /** - * Eliminates unnecessary jump instructions - * - * Jump l; [nops]; l: [...] - * => POP*; [nops]; l: [...] - * - * Introduces 0, 1 or 2 POP instructions, depending on the number of values consumed by the Jump. - */ - private def removeJumpToSuccessor(method: MethodNode, instruction: AbstractInsnNode) = instruction match { - case JumpNonJsr(jump) if nextExecutableInstruction(jump, alsoKeep = Set(jump.label)) == Some(jump.label) => - removeJumpAndAdjustStack(method, jump) - true - case _ => false - } + /** + * If the "else" part of a conditional branch is a simple GOTO, negates the conditional branch + * and eliminates the GOTO. + * + * CondJump l; [nops, no jump targets]; GOTO m; [nops]; l: [...] + * => NegatedCondJump m; [nops, no jump targets]; [nops]; l: [...] + * + * Note that no jump targets are allowed in the first [nops] section. Otherwise, there could + * be some other jump to the GOTO, and eliminating it would change behavior. + */ + def simplifyBranchOverGoto(insn: AbstractInsnNode, inTryBlock: Boolean): Boolean = insn match { + case ConditionalJump(jump) => + // don't skip over jump targets, see doc comment + nextExecutableInstruction(jump, alsoKeep = jumpTargets) match { + case Some(Goto(goto)) => + if (nextExecutableInstruction(goto, alsoKeep = Set(jump.label)) contains jump.label) { + val newJump = new JumpInsnNode(negateJumpOpcode(jump.getOpcode), goto.label) + method.instructions.set(jump, newJump) + removeJumpFromMap(jump) + jumpInsns(newJump) = inTryBlock + replaceJumpByPop(goto) + true + } else false + + case _ => false + } + case _ => false + } - /** - * If the "else" part of a conditional branch is a simple GOTO, negates the conditional branch - * and eliminates the GOTO. - * - * CondJump l; [nops, no labels]; GOTO m; [nops]; l: [...] - * => NegatedCondJump m; [nops, no labels]; [nops]; l: [...] - * - * Note that no label definitions are allowed in the first [nops] section. Otherwise, there could - * be some other jump to the GOTO, and eliminating it would change behavior. - * - * For technical reasons, we cannot remove the GOTO here (*).Instead this method returns an Option - * containing the GOTO that needs to be eliminated. - * - * (*) The ASM instruction iterator (used in the caller [[simplifyJumps]]) has an undefined - * behavior if the successor of the current instruction is removed, which may be the case here - */ - private def simplifyBranchOverGoto(method: MethodNode, instruction: AbstractInsnNode): Option[JumpInsnNode] = instruction match { - case ConditionalJump(jump) => - // don't skip over labels, see doc comment - nextExecutableInstructionOrLabel(jump) match { - case Some(Goto(goto)) => - if (nextExecutableInstruction(goto, alsoKeep = Set(jump.label)) == Some(jump.label)) { - val newJump = new JumpInsnNode(negateJumpOpcode(jump.getOpcode), goto.label) - method.instructions.set(jump, newJump) - Some(goto) - } else None - - case _ => None - } - case _ => None - } + /** + * Inlines xRETURN and ATHROW + * + * GOTO l; [any ops]; l: xRETURN/ATHROW + * => xRETURN/ATHROW; [any ops]; l: xRETURN/ATHROW + * + * inlining is only done if the GOTO instruction is not part of a try block, otherwise the + * rewrite might change the behavior. For xRETURN, the reason is that return instructions may throw + * an IllegalMonitorStateException, as described here: + * http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.return + */ + def simplifyGotoReturn(instruction: AbstractInsnNode, inTryBlock: Boolean): Boolean = !inTryBlock && (instruction match { + case Goto(jump) => + nextExecutableInstruction(jump.label) match { + case Some(target) => + if (isReturn(target) || target.getOpcode == ATHROW) { + method.instructions.set(jump, target.clone(null)) + removeJumpFromMap(jump) + true + } else false + + case _ => false + } + case _ => false + }) - /** - * Inlines xRETURN and ATHROW - * - * GOTO l; [any ops]; l: xRETURN/ATHROW - * => xRETURN/ATHROW; [any ops]; l: xRETURN/ATHROW - * - * inlining is only done if the GOTO instruction is not part of a try block, otherwise the - * rewrite might change the behavior. For xRETURN, the reason is that return instructions may throw - * an IllegalMonitorStateException, as described here: - * http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.return - */ - private def simplifyGotoReturn(method: MethodNode, instruction: AbstractInsnNode, inTryBlock: Boolean): Boolean = !inTryBlock && (instruction match { - case Goto(jump) => - nextExecutableInstruction(jump.label) match { - case Some(target) => - if (isReturn(target) || target.getOpcode == ATHROW) { - method.instructions.set(jump, target.clone(null)) - true - } else false + def run(): Boolean = { + var changed = false + + // `.toList` because we're modifying the map while iterating over it + for ((jumpInsn, inTryBlock) <- jumpInsns.toList if jumpInsns.contains(jumpInsn) && isJumpNonJsr(jumpInsn)) { + var jumpRemoved = simplifyThenElseSameTarget(jumpInsn) - case _ => false + if (!jumpRemoved) { + changed = collapseJumpChains(jumpInsn) || changed + jumpRemoved = removeJumpToSuccessor(jumpInsn) + + if (!jumpRemoved) { + changed = simplifyBranchOverGoto(jumpInsn, inTryBlock) || changed + changed = simplifyGotoReturn(jumpInsn, inTryBlock) || changed + } + } + + changed ||= jumpRemoved } - case _ => false - }) + + if (changed) run() + changed + } + + run() + } } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 20e2c4346f..1446d22217 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -319,7 +319,7 @@ trait ScalaSettings extends AbsScalaSettings def YoptWarningNoInlineMissingBytecode = YoptWarnings.contains(YoptWarningsChoices.noInlineMissingBytecode) def YoptWarningNoInlineMissingScalaInlineInfoAttr = YoptWarnings.contains(YoptWarningsChoices.noInlineMissingScalaInlineInfoAttr) - val YoptTrace = StringSetting("-YoptTrace", "package/Class.method", "Trace the optimizer progress for a specific method.", "") + val YoptTrace = StringSetting("-Yopt-trace", "package/Class.method", "Trace the optimizer progress for a specific method.", "") private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug." diff --git a/src/library/scala/collection/immutable/ListSet.scala b/src/library/scala/collection/immutable/ListSet.scala index a65e25ed6e..98b91f7c84 100644 --- a/src/library/scala/collection/immutable/ListSet.scala +++ b/src/library/scala/collection/immutable/ListSet.scala @@ -12,7 +12,7 @@ package immutable import generic._ import scala.annotation.tailrec -import mutable.Builder +import mutable.{Builder, ReusableBuilder} /** $factoryInfo * @define Coll immutable.ListSet @@ -32,8 +32,10 @@ object ListSet extends ImmutableSetFactory[ListSet] { * a time to a list backed set puts the "squared" in N^2. There is a * temporary space cost, but it's improbable a list backed set could * become large enough for this to matter given its pricy element lookup. + * + * This builder is reusable. */ - class ListSetBuilder[Elem](initial: ListSet[Elem]) extends Builder[Elem, ListSet[Elem]] { + class ListSetBuilder[Elem](initial: ListSet[Elem]) extends ReusableBuilder[Elem, ListSet[Elem]] { def this() = this(empty[Elem]) protected val elems = (new mutable.ListBuffer[Elem] ++= initial).reverse protected val seen = new mutable.HashSet[Elem] ++= initial diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 5a9734a99e..539ae9c387 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -13,7 +13,7 @@ package immutable import scala.annotation.unchecked.uncheckedVariance import scala.compat.Platform import scala.collection.generic._ -import scala.collection.mutable.Builder +import scala.collection.mutable.{Builder, ReusableBuilder} import scala.collection.parallel.immutable.ParVector /** Companion object to the Vector class @@ -704,8 +704,8 @@ extends AbstractIterator[A] } } - -final class VectorBuilder[A]() extends Builder[A,Vector[A]] with VectorPointer[A @uncheckedVariance] { +/** A class to build instances of `Vector`. This builder is reusable. */ +final class VectorBuilder[A]() extends ReusableBuilder[A,Vector[A]] with VectorPointer[A @uncheckedVariance] { // possible alternative: start with display0 = null, blockIndex = -32, lo = 32 // to avoid allocating initial array if the result will be empty anyways diff --git a/src/library/scala/collection/mutable/AnyRefMap.scala b/src/library/scala/collection/mutable/AnyRefMap.scala index af7600ad3d..2ed5bbea60 100644 --- a/src/library/scala/collection/mutable/AnyRefMap.scala +++ b/src/library/scala/collection/mutable/AnyRefMap.scala @@ -427,7 +427,11 @@ object AnyRefMap { def apply(): AnyRefMapBuilder[J, U] = new AnyRefMapBuilder[J, U] } - final class AnyRefMapBuilder[K <: AnyRef, V] extends Builder[(K, V), AnyRefMap[K, V]] { + /** A builder for instances of `AnyRefMap`. + * + * This builder can be reused to create multiple instances. + */ + final class AnyRefMapBuilder[K <: AnyRef, V] extends ReusableBuilder[(K, V), AnyRefMap[K, V]] { private[collection] var elems: AnyRefMap[K, V] = new AnyRefMap[K, V] def +=(entry: (K, V)): this.type = { elems += entry diff --git a/src/library/scala/collection/mutable/ArrayBuilder.scala b/src/library/scala/collection/mutable/ArrayBuilder.scala index ac78ab823b..549ffef565 100644 --- a/src/library/scala/collection/mutable/ArrayBuilder.scala +++ b/src/library/scala/collection/mutable/ArrayBuilder.scala @@ -18,7 +18,7 @@ import scala.reflect.ClassTag * * @tparam T the type of the elements for the builder. */ -abstract class ArrayBuilder[T] extends Builder[T, Array[T]] with Serializable +abstract class ArrayBuilder[T] extends ReusableBuilder[T, Array[T]] with Serializable /** A companion object for array builders. * @@ -49,6 +49,8 @@ object ArrayBuilder { /** A class for array builders for arrays of reference types. * + * This builder can be reused. + * * @tparam T type of elements for the array builder, subtype of `AnyRef` with a `ClassTag` context bound. */ @deprecatedInheritance("ArrayBuilder.ofRef is an internal implementation not intended for subclassing.", "2.11.0") @@ -98,12 +100,13 @@ object ArrayBuilder { super.++=(xs) } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -115,7 +118,7 @@ object ArrayBuilder { override def toString = "ArrayBuilder.ofRef" } - /** A class for array builders for arrays of `byte`s. */ + /** A class for array builders for arrays of `byte`s. It can be reused. */ @deprecatedInheritance("ArrayBuilder.ofByte is an internal implementation not intended for subclassing.", "2.11.0") class ofByte extends ArrayBuilder[Byte] { @@ -163,12 +166,13 @@ object ArrayBuilder { super.++=(xs) } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -180,7 +184,7 @@ object ArrayBuilder { override def toString = "ArrayBuilder.ofByte" } - /** A class for array builders for arrays of `short`s. */ + /** A class for array builders for arrays of `short`s. It can be reused. */ @deprecatedInheritance("ArrayBuilder.ofShort is an internal implementation not intended for subclassing.", "2.11.0") class ofShort extends ArrayBuilder[Short] { @@ -228,12 +232,13 @@ object ArrayBuilder { super.++=(xs) } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -245,7 +250,7 @@ object ArrayBuilder { override def toString = "ArrayBuilder.ofShort" } - /** A class for array builders for arrays of `char`s. */ + /** A class for array builders for arrays of `char`s. It can be reused. */ @deprecatedInheritance("ArrayBuilder.ofChar is an internal implementation not intended for subclassing.", "2.11.0") class ofChar extends ArrayBuilder[Char] { @@ -293,12 +298,13 @@ object ArrayBuilder { super.++=(xs) } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -310,7 +316,7 @@ object ArrayBuilder { override def toString = "ArrayBuilder.ofChar" } - /** A class for array builders for arrays of `int`s. */ + /** A class for array builders for arrays of `int`s. It can be reused. */ @deprecatedInheritance("ArrayBuilder.ofInt is an internal implementation not intended for subclassing.", "2.11.0") class ofInt extends ArrayBuilder[Int] { @@ -358,12 +364,13 @@ object ArrayBuilder { super.++=(xs) } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -375,7 +382,7 @@ object ArrayBuilder { override def toString = "ArrayBuilder.ofInt" } - /** A class for array builders for arrays of `long`s. */ + /** A class for array builders for arrays of `long`s. It can be reused. */ @deprecatedInheritance("ArrayBuilder.ofLong is an internal implementation not intended for subclassing.", "2.11.0") class ofLong extends ArrayBuilder[Long] { @@ -423,12 +430,13 @@ object ArrayBuilder { super.++=(xs) } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -440,7 +448,7 @@ object ArrayBuilder { override def toString = "ArrayBuilder.ofLong" } - /** A class for array builders for arrays of `float`s. */ + /** A class for array builders for arrays of `float`s. It can be reused. */ @deprecatedInheritance("ArrayBuilder.ofFloat is an internal implementation not intended for subclassing.", "2.11.0") class ofFloat extends ArrayBuilder[Float] { @@ -488,12 +496,13 @@ object ArrayBuilder { super.++=(xs) } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -505,7 +514,7 @@ object ArrayBuilder { override def toString = "ArrayBuilder.ofFloat" } - /** A class for array builders for arrays of `double`s. */ + /** A class for array builders for arrays of `double`s. It can be reused. */ @deprecatedInheritance("ArrayBuilder.ofDouble is an internal implementation not intended for subclassing.", "2.11.0") class ofDouble extends ArrayBuilder[Double] { @@ -553,12 +562,13 @@ object ArrayBuilder { super.++=(xs) } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -570,7 +580,7 @@ object ArrayBuilder { override def toString = "ArrayBuilder.ofDouble" } - /** A class for array builders for arrays of `boolean`s. */ + /** A class for array builders for arrays of `boolean`s. It can be reused. */ class ofBoolean extends ArrayBuilder[Boolean] { private var elems: Array[Boolean] = _ @@ -617,12 +627,13 @@ object ArrayBuilder { super.++=(xs) } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -634,65 +645,33 @@ object ArrayBuilder { override def toString = "ArrayBuilder.ofBoolean" } - /** A class for array builders for arrays of `Unit` type. */ + /** A class for array builders for arrays of `Unit` type. It can be reused. */ @deprecatedInheritance("ArrayBuilder.ofUnit is an internal implementation not intended for subclassing.", "2.11.0") class ofUnit extends ArrayBuilder[Unit] { - private var elems: Array[Unit] = _ - private var capacity: Int = 0 private var size: Int = 0 - private def mkArray(size: Int): Array[Unit] = { - val newelems = new Array[Unit](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - def +=(elem: Unit): this.type = { - ensureSize(size + 1) - elems(size) = elem size += 1 this } - override def ++=(xs: TraversableOnce[Unit]): this.type = xs match { - case xs: WrappedArray.ofUnit => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) + override def ++=(xs: TraversableOnce[Unit]): this.type = { + size += xs.size + this } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems - else mkArray(size) + val ans = new Array[Unit](size) + var i = 0 + while (i < size) { ans(i) = (); i += 1 } + ans } override def equals(other: Any): Boolean = other match { - case x: ofUnit => (size == x.size) && (elems == x.elems) + case x: ofUnit => (size == x.size) case _ => false } diff --git a/src/library/scala/collection/mutable/Builder.scala b/src/library/scala/collection/mutable/Builder.scala index 75560580cc..8d6a0ec69d 100644 --- a/src/library/scala/collection/mutable/Builder.scala +++ b/src/library/scala/collection/mutable/Builder.scala @@ -18,6 +18,14 @@ import generic._ * elements to the builder with `+=` and then converting to the required * collection type with `result`. * + * One cannot assume that a single `Builder` can build more than one + * instance of the desired collection. Particular subclasses may allow + * such behavior. Otherwise, `result` should be treated as a terminal + * operation: after it is called, no further methods should be called on + * the builder. Extend the [[collection.mutable.ReusableBuilder]] trait + * instead of `Builder` for builders that may be reused to build multiple + * instances. + * * @tparam Elem the type of elements that get added to the builder. * @tparam To the type of collection that it produced. * @@ -36,8 +44,10 @@ trait Builder[-Elem, +To] extends Growable[Elem] { */ def clear() - /** Produces a collection from the added elements. - * The builder's contents are undefined after this operation. + /** Produces a collection from the added elements. This is a terminal operation: + * the builder's contents are undefined after this operation, and no further + * methods should be called. + * * @return a collection containing the elements added to this builder. */ def result(): To @@ -112,6 +122,8 @@ trait Builder[-Elem, +To] extends Growable[Elem] { * @tparam NewTo the type of collection returned by `f`. * @return a new builder which is the same as the current builder except * that a transformation function is applied to this builder's result. + * + * @note The original builder should no longer be used after `mapResult` is called. */ def mapResult[NewTo](f: To => NewTo): Builder[Elem, NewTo] = new Builder[Elem, NewTo] with Proxy { diff --git a/src/library/scala/collection/mutable/GrowingBuilder.scala b/src/library/scala/collection/mutable/GrowingBuilder.scala index c4b5e546aa..27d554d98e 100644 --- a/src/library/scala/collection/mutable/GrowingBuilder.scala +++ b/src/library/scala/collection/mutable/GrowingBuilder.scala @@ -15,6 +15,8 @@ import generic._ /** The canonical builder for collections that are growable, i.e. that support an * efficient `+=` method which adds an element to the collection. * + * GrowableBuilders can produce only a single instance of the collection they are growing. + * * @author Paul Phillips * @version 2.8 * @since 2.8 @@ -25,6 +27,6 @@ import generic._ class GrowingBuilder[Elem, To <: Growable[Elem]](empty: To) extends Builder[Elem, To] { protected var elems: To = empty def +=(x: Elem): this.type = { elems += x; this } - def clear() { elems = empty } + def clear() { empty.clear } def result: To = elems } diff --git a/src/library/scala/collection/mutable/LazyBuilder.scala b/src/library/scala/collection/mutable/LazyBuilder.scala index ebee38b77f..f0a5e6971a 100644 --- a/src/library/scala/collection/mutable/LazyBuilder.scala +++ b/src/library/scala/collection/mutable/LazyBuilder.scala @@ -13,12 +13,14 @@ package mutable /** A builder that constructs its result lazily. Iterators or iterables to * be added to this builder with `++=` are not evaluated until `result` is called. * + * This builder can be reused. + * * @since 2.8 * * @tparam Elem type of the elements for this builder. * @tparam To type of the collection this builder builds. */ -abstract class LazyBuilder[Elem, +To] extends Builder[Elem, To] { +abstract class LazyBuilder[Elem, +To] extends ReusableBuilder[Elem, To] { /** The different segments of elements to be added to the builder, represented as iterators */ protected var parts = new ListBuffer[TraversableOnce[Elem]] def +=(x: Elem): this.type = { parts += List(x); this } diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala index 0a483ceb86..02fcced3ac 100644 --- a/src/library/scala/collection/mutable/ListBuffer.scala +++ b/src/library/scala/collection/mutable/ListBuffer.scala @@ -46,7 +46,7 @@ final class ListBuffer[A] with Buffer[A] with GenericTraversableTemplate[A, ListBuffer] with BufferLike[A, ListBuffer[A]] - with Builder[A, List[A]] + with ReusableBuilder[A, List[A]] with SeqForwarder[A] with Serializable { @@ -297,6 +297,10 @@ final class ListBuffer[A] // Implementation of abstract method in Builder + /** Returns the accumulated `List`. + * + * This method may be called multiple times to obtain snapshots of the list in different stages of construction. + */ def result: List[A] = toList /** Converts this buffer to a list. Takes constant time. The buffer is diff --git a/src/library/scala/collection/mutable/LongMap.scala b/src/library/scala/collection/mutable/LongMap.scala index f39a6ba634..ecbb1952af 100644 --- a/src/library/scala/collection/mutable/LongMap.scala +++ b/src/library/scala/collection/mutable/LongMap.scala @@ -519,7 +519,11 @@ object LongMap { def apply(): LongMapBuilder[U] = new LongMapBuilder[U] } - final class LongMapBuilder[V] extends Builder[(Long, V), LongMap[V]] { + /** A builder for instances of `LongMap`. + * + * This builder can be reused to create multiple instances. + */ + final class LongMapBuilder[V] extends ReusableBuilder[(Long, V), LongMap[V]] { private[collection] var elems: LongMap[V] = new LongMap[V] def +=(entry: (Long, V)): this.type = { elems += entry diff --git a/src/library/scala/collection/mutable/MapBuilder.scala b/src/library/scala/collection/mutable/MapBuilder.scala index a5a6b12ea9..cfc3079f41 100644 --- a/src/library/scala/collection/mutable/MapBuilder.scala +++ b/src/library/scala/collection/mutable/MapBuilder.scala @@ -23,7 +23,7 @@ package mutable * @since 2.8 */ class MapBuilder[A, B, Coll <: scala.collection.GenMap[A, B] with scala.collection.GenMapLike[A, B, Coll]](empty: Coll) -extends Builder[(A, B), Coll] { +extends ReusableBuilder[(A, B), Coll] { protected var elems: Coll = empty def +=(x: (A, B)): this.type = { elems = (elems + x).asInstanceOf[Coll] diff --git a/src/library/scala/collection/mutable/ReusableBuilder.scala b/src/library/scala/collection/mutable/ReusableBuilder.scala new file mode 100644 index 0000000000..caab3071b6 --- /dev/null +++ b/src/library/scala/collection/mutable/ReusableBuilder.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2016, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package collection +package mutable + +import generic._ + +/** `ReusableBuilder` is a marker trait that indicates that a `Builder` + * can be reused to build more than one instance of a collection. In + * particular, calling `result` followed by `clear` will produce a + * collection and reset the builder to begin building a new collection + * of the same type. + * + * It is up to subclasses to implement this behavior, and to document any + * other behavior that varies from standard `ReusableBuilder` usage + * (e.g. operations being well-defined after a call to `result`, or allowing + * multiple calls to result to obtain different snapshots of a collection under + * construction). + * + * @tparam Elem the type of elements that get added to the builder. + * @tparam To the type of collection that it produced. + * + * @since 2.12 + */ +trait ReusableBuilder[-Elem, +To] extends Builder[Elem, To] { + /** Clears the contents of this builder. + * After execution of this method, the builder will contain no elements. + * + * If executed immediately after a call to `result`, this allows a new + * instance of the same type of collection to be built. + */ + override def clear(): Unit // Note: overriding for scaladoc only! + + /** Produces a collection from the added elements. + * + * After a call to `result`, the behavior of all other methods is undefined + * save for `clear`. If `clear` is called, then the builder is reset and + * may be used to build another instance. + * + * @return a collection containing the elements added to this builder. + */ + override def result(): To // Note: overriding for scaladoc only! +} diff --git a/src/library/scala/collection/mutable/SetBuilder.scala b/src/library/scala/collection/mutable/SetBuilder.scala index 01bfdc96ed..5d1e9ffc3a 100644 --- a/src/library/scala/collection/mutable/SetBuilder.scala +++ b/src/library/scala/collection/mutable/SetBuilder.scala @@ -17,7 +17,9 @@ package mutable * @param empty The empty element of the collection. * @since 2.8 */ -class SetBuilder[A, Coll <: scala.collection.Set[A] with scala.collection.SetLike[A, Coll]](empty: Coll) extends Builder[A, Coll] { +class SetBuilder[A, Coll <: scala.collection.Set[A] +with scala.collection.SetLike[A, Coll]](empty: Coll) +extends ReusableBuilder[A, Coll] { protected var elems: Coll = empty def +=(x: A): this.type = { elems = elems + x; this } def clear() { elems = empty } diff --git a/src/library/scala/collection/mutable/StringBuilder.scala b/src/library/scala/collection/mutable/StringBuilder.scala index c56d40786e..b5b9498374 100644 --- a/src/library/scala/collection/mutable/StringBuilder.scala +++ b/src/library/scala/collection/mutable/StringBuilder.scala @@ -33,7 +33,7 @@ final class StringBuilder(private val underlying: JavaStringBuilder) with java.lang.CharSequence with IndexedSeq[Char] with StringLike[StringBuilder] - with Builder[Char, String] + with ReusableBuilder[Char, String] with Serializable { override protected[this] def thisCollection: StringBuilder = this @@ -435,7 +435,11 @@ final class StringBuilder(private val underlying: JavaStringBuilder) */ override def mkString = toString - /** Returns the result of this Builder (a String) + /** Returns the result of this Builder (a String). + * + * If this method is called multiple times, each call will result in a snapshot of the buffer at that point in time. + * In particular, a `StringBuilder` can be used to build multiple independent strings by emptying the buffer with `clear` + * after each call to `result`. * * @return the string assembled by this StringBuilder */ diff --git a/src/library/scala/collection/mutable/WrappedArrayBuilder.scala b/src/library/scala/collection/mutable/WrappedArrayBuilder.scala index bfe95a11ab..c4781321d7 100644 --- a/src/library/scala/collection/mutable/WrappedArrayBuilder.scala +++ b/src/library/scala/collection/mutable/WrappedArrayBuilder.scala @@ -17,12 +17,14 @@ import scala.runtime.ScalaRunTime._ /** A builder class for arrays. * + * This builder can be reused. + * * @tparam A type of elements that can be added to this builder. * @param tag class tag for objects of type `A`. * * @since 2.8 */ -class WrappedArrayBuilder[A](tag: ClassTag[A]) extends Builder[A, WrappedArray[A]] { +class WrappedArrayBuilder[A](tag: ClassTag[A]) extends ReusableBuilder[A, WrappedArray[A]] { @deprecated("use tag instead", "2.10.0") val manifest: ClassTag[A] = tag @@ -73,12 +75,13 @@ class WrappedArrayBuilder[A](tag: ClassTag[A]) extends Builder[A, WrappedArray[A this } - def clear() { - size = 0 - } + def clear() { size = 0 } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index ba6c363918..a025407672 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -428,13 +428,15 @@ trait Definitions extends api.StandardDefinitions { def elementType(container: Symbol, tp: Type): Type = elementExtract(container, tp) // collections classes - lazy val ConsClass = requiredClass[scala.collection.immutable.::[_]] - lazy val IteratorClass = requiredClass[scala.collection.Iterator[_]] - lazy val IterableClass = requiredClass[scala.collection.Iterable[_]] - lazy val ListClass = requiredClass[scala.collection.immutable.List[_]] - lazy val SeqClass = requiredClass[scala.collection.Seq[_]] - lazy val StringBuilderClass = requiredClass[scala.collection.mutable.StringBuilder] - lazy val TraversableClass = requiredClass[scala.collection.Traversable[_]] + lazy val ConsClass = requiredClass[scala.collection.immutable.::[_]] + lazy val IteratorClass = requiredClass[scala.collection.Iterator[_]] + lazy val IterableClass = requiredClass[scala.collection.Iterable[_]] + lazy val ListClass = requiredClass[scala.collection.immutable.List[_]] + lazy val SeqClass = requiredClass[scala.collection.Seq[_]] + lazy val JavaStringBuilderClass = requiredClass[java.lang.StringBuilder] + lazy val JavaStringBufferClass = requiredClass[java.lang.StringBuffer] + lazy val JavaCharSequenceClass = requiredClass[java.lang.CharSequence] + lazy val TraversableClass = requiredClass[scala.collection.Traversable[_]] lazy val ListModule = requiredModule[scala.collection.immutable.List.type] def List_apply = getMemberMethod(ListModule, nme.apply) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index d6b611a3f4..e5f77c322d 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -266,7 +266,9 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.IterableClass definitions.ListClass definitions.SeqClass - definitions.StringBuilderClass + definitions.JavaStringBuilderClass + definitions.JavaStringBufferClass + definitions.JavaCharSequenceClass definitions.TraversableClass definitions.ListModule definitions.NilModule |