Skip to content

Commit c5da7a0

Browse files
authored
Merge pull request scala#6757 from szeiger/issue/10932
Remove reflection-based class name computation for collections
2 parents ab8dcc2 + 9990df7 commit c5da7a0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+140
-200
lines changed

src/library/scala/collection/ArrayOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ object ArrayOps {
3939
private class ArrayView[A](xs: Array[A]) extends IndexedSeqView[A] {
4040
def length = xs.length
4141
def apply(n: Int) = xs(n)
42-
override def className = "ArrayView"
42+
override protected[this] def className = "ArrayView"
4343
}
4444

4545
/** A lazy filtered array. No filtering is applied until one of `foreach`, `map` or `flatMap` is called. */

src/library/scala/collection/BitSet.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ trait BitSet extends SortedSet[Int] with BitSetOps[BitSet] {
2323
override protected def fromSpecificIterable(coll: Iterable[Int]): BitSetC = bitSetFactory.fromSpecific(coll)
2424
override protected def newSpecificBuilder: Builder[Int, BitSetC] = bitSetFactory.newBuilder
2525
override def empty: BitSetC = bitSetFactory.empty
26+
override protected[this] def stringPrefix = "BitSet"
2627
}
2728

2829
@SerialVersionUID(3L)

src/library/scala/collection/IndexedSeq.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import scala.math.Ordering
77
import Searching.{SearchResult, Found, InsertionPoint}
88

99
/** Base trait for indexed sequences that have efficient `apply` and `length` */
10-
trait IndexedSeq[+A] extends Seq[A] with IndexedSeqOps[A, IndexedSeq, IndexedSeq[A]]
10+
trait IndexedSeq[+A] extends Seq[A] with IndexedSeqOps[A, IndexedSeq, IndexedSeq[A]] {
11+
override protected[this] def stringPrefix: String = "IndexedSeq"
12+
}
1113

1214
@SerialVersionUID(3L)
1315
object IndexedSeq extends SeqFactory.Delegate[IndexedSeq](immutable.IndexedSeq)

src/library/scala/collection/Iterable.scala

Lines changed: 37 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,43 @@ trait Iterable[+A] extends IterableOnce[A] with IterableOps[A, Iterable, Iterabl
4646
* to be preserved or a more efficient implementation is available. Override to return `this` in order to
4747
* use self-serialization instead of a proxy. */
4848
protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(iterableFactory.iterableFactory, this)
49+
50+
/** Defines the prefix of this object's `toString` representation.
51+
*
52+
* It is recommended to return the name of the concrete collection type, but
53+
* not implementation subclasses. For example, for `ListMap` this method should
54+
* return `"ListMap"`, not `"Map"` (the supertype) or `"Node"` (an implementation
55+
* subclass).
56+
*
57+
* The default implementation returns "Iterable". It is overridden for the basic
58+
* collection kinds "Seq", "IndexedSeq", "LinearSeq", "Buffer", "Set", "Map",
59+
* "SortedSet", "SortedMap" and "View".
60+
*
61+
* @return a string representation which starts the result of `toString`
62+
* applied to this $coll. By default the string prefix is the
63+
* simple name of the collection class $coll.
64+
*/
65+
protected[this] def className: String = stringPrefix
66+
67+
/** Forwarder to `className` for use in `scala.runtime.ScalaRunTime`.
68+
*
69+
* This allows the proper visibility for `className` to be
70+
* published, but provides the exclusive access needed by
71+
* `scala.runtime.ScalaRunTime.stringOf` (and a few tests in
72+
* the test suite).
73+
*/
74+
private[scala] final def collectionClassName: String = className
75+
76+
@deprecatedOverriding("Override className instead", "2.13.0")
77+
protected[this] def stringPrefix: String = "Iterable"
78+
79+
/** Converts this $coll to a string.
80+
*
81+
* @return a string representation of this collection. By default this
82+
* string consists of the `className` of this $coll, followed
83+
* by all elements separated by commas and enclosed in parentheses.
84+
*/
85+
override def toString = mkString(className + "(", ", ", ")")
4986
}
5087

5188
/** Base trait for Iterable operations
@@ -187,105 +224,6 @@ trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] with Iterable
187224
@deprecated("Use .view.slice(from, until) instead of .view(from, until)", "2.13.0")
188225
@`inline` final def view(from: Int, until: Int): View[A] = view.slice(from, until)
189226

190-
/** Defines the prefix of this object's `toString` representation.
191-
*
192-
* It is recommended to return the name of the concrete collection type, but
193-
* not implementation subclasses. For example, for `ListMap` this method should
194-
* return `"ListMap"`, not `"Map"` (the supertype) or `"Node"` (an implementation
195-
* subclass).
196-
*
197-
* It is recommended to overwrite this method even if the default implementation
198-
* returns the correct name, to avoid the implementation using reflection.
199-
*
200-
* @return a string representation which starts the result of `toString`
201-
* applied to this $coll. By default the string prefix is the
202-
* simple name of the collection class $coll.
203-
*/
204-
@`inline` protected[this] def className: String = stringPrefix
205-
206-
/** Forwarder to `className` for use in `scala.runtime.ScalaRunTime`.
207-
*
208-
* This allows the proper visibility for `className` to be
209-
* published, but provides the exclusive access needed by
210-
* `scala.runtime.ScalaRunTime.stringOf` (and a few tests in
211-
* the test suite).
212-
*/
213-
private[scala] final def collectionClassName: String = className
214-
215-
@deprecatedOverriding("Override className instead", "2.13.0")
216-
protected[this] def stringPrefix: String = {
217-
/* This method is written in a style that avoids calling `String.split()`
218-
* as well as methods of java.lang.Character that require the Unicode
219-
* database information. This is mostly important for Scala.js, so that
220-
* using the collection library does automatically bring java.util.regex.*
221-
* and the Unicode database in the generated code.
222-
*
223-
* This algorithm has the additional benefit that it won't allocate
224-
* anything except the result String in the common case, where the class
225-
* is not an inner class (i.e., when the result contains no '.').
226-
*/
227-
val fqn = toIterable.getClass.getName
228-
var pos: Int = fqn.length - 1
229-
230-
// Skip trailing $'s
231-
while (pos != -1 && fqn.charAt(pos) == '$') {
232-
pos -= 1
233-
}
234-
if (pos == -1 || fqn.charAt(pos) == '.') {
235-
return ""
236-
}
237-
238-
var result: String = ""
239-
while (true) {
240-
// Invariant: if we enter the loop, there is a non-empty part
241-
242-
// Look for the beginning of the part, remembering where was the last non-digit
243-
val partEnd = pos + 1
244-
while (pos != -1 && fqn.charAt(pos) <= '9' && fqn.charAt(pos) >= '0') {
245-
pos -= 1
246-
}
247-
val lastNonDigit = pos
248-
while (pos != -1 && fqn.charAt(pos) != '$' && fqn.charAt(pos) != '.') {
249-
pos -= 1
250-
}
251-
val partStart = pos + 1
252-
253-
// A non-last part which contains only digits marks a method-local part -> drop the prefix
254-
if (pos == lastNonDigit && partEnd != fqn.length) {
255-
return result
256-
}
257-
258-
// Skip to the next part, and determine whether we are the end
259-
while (pos != -1 && fqn.charAt(pos) == '$') {
260-
pos -= 1
261-
}
262-
val atEnd = pos == -1 || fqn.charAt(pos) == '.'
263-
264-
// Handle the actual content of the part (we ignore parts that are likely synthetic)
265-
def isPartLikelySynthetic = {
266-
val firstChar = fqn.charAt(partStart)
267-
(firstChar > 'Z' && firstChar < 0x7f) || (firstChar < 'A')
268-
}
269-
if (atEnd || !isPartLikelySynthetic) {
270-
val part = fqn.substring(partStart, partEnd)
271-
result = if (result.isEmpty) part else part + '.' + result
272-
if (atEnd)
273-
return result
274-
}
275-
}
276-
277-
// dead code
278-
result
279-
}
280-
281-
/** Converts this $coll to a string.
282-
*
283-
* @return a string representation of this collection. By default this
284-
* string consists of the `className` of this $coll, followed
285-
* by all elements separated by commas and enclosed in parentheses.
286-
*/
287-
override def toString = mkString(className + "(", ", ", ")")
288-
289227
//TODO Can there be a useful lazy implementation of this method? Otherwise mark it as being always strict
290228
/** Transposes this $coll of iterable collections into
291229
* a $coll of ${coll}s.

src/library/scala/collection/LinearSeq.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import scala.language.higherKinds
88
* `tail` operations.
99
* Known subclasses: List, LazyList
1010
*/
11-
trait LinearSeq[+A] extends Seq[A] with LinearSeqOps[A, LinearSeq, LinearSeq[A]]
11+
trait LinearSeq[+A] extends Seq[A] with LinearSeqOps[A, LinearSeq, LinearSeq[A]] {
12+
override protected[this] def stringPrefix: String = "LinearSeq"
13+
}
1214

1315
@SerialVersionUID(3L)
1416
object LinearSeq extends SeqFactory.Delegate[LinearSeq](immutable.List)

src/library/scala/collection/Map.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ trait Map[K, +V]
5454
def - (key: K): Map[K, V]
5555
@deprecated("Use -- or removeAll on an immutable Map", "2.13.0")
5656
def - (key1: K, key2: K, keys: K*): Map[K, V]
57+
58+
override protected[this] def stringPrefix: String = "Map"
59+
60+
override def toString(): String = super[Iterable].toString() // Because `Function1` overrides `toString` too
5761
}
5862

5963
/** Base Map implementation type
@@ -278,8 +282,6 @@ trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C]
278282
/** Alias for `concat` */
279283
/*@`inline` final*/ def ++ [V2 >: V](xs: collection.Iterable[(K, V2)]): CC[K, V2] = concat(xs)
280284

281-
override def toString(): String = super[IterableOps].toString()
282-
283285
override def mkString(start: String, sep: String, end: String): String =
284286
iterator.map { case (k, v) => s"$k -> $v" }.mkString(start, sep, end)
285287

src/library/scala/collection/Seq.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ trait Seq[+A]
3939

4040
override def toString(): String = super[Iterable].toString()
4141

42+
override protected[this] def stringPrefix: String = "Seq"
4243
}
4344

4445
/**

src/library/scala/collection/Set.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ trait Set[A]
3030
override def iterableFactory: IterableFactory[IterableCC] = Set
3131

3232
def empty: IterableCC[A] = iterableFactory.empty
33+
34+
override protected[this] def stringPrefix: String = "Set"
35+
36+
override def toString(): String = super[Iterable].toString() // Because `Function1` overrides `toString` too
3337
}
3438

3539
/** Base trait for set operations
@@ -128,8 +132,6 @@ trait SetOps[A, +CC[_], +C <: SetOps[A, CC, C]]
128132
}
129133
}
130134

131-
override def toString(): String = super[IterableOps].toString() // Because `Function1` overrides `toString` too
132-
133135
/** Computes the intersection between this set and another set.
134136
*
135137
* @param that the set to intersect with.

src/library/scala/collection/SortedMap.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ trait SortedMap[K, +V]
3030
override def empty: SortedMapCC[K, V] @uncheckedVariance = sortedMapFactory.empty
3131

3232
override protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(sortedMapFactory.sortedMapFactory[K, V], this)
33+
34+
override protected[this] def stringPrefix: String = "SortedMap"
3335
}
3436

3537
trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]]
@@ -181,7 +183,6 @@ trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _],
181183
override def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CC[K, V1] = sortedMapFactory.from(new View.Concat(new View.Appended(new View.Appended(toIterable, elem1), elem2), elems))
182184

183185
// TODO Also override mapValues
184-
185186
}
186187

187188
object SortedMapOps {

src/library/scala/collection/SortedSet.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ trait SortedSet[A] extends Set[A] with SortedSetOps[A, SortedSet, SortedSet[A]]
2424
override def empty: SortedIterableCC[A] = sortedIterableFactory.empty
2525

2626
override protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(sortedIterableFactory.evidenceIterableFactory[A], this)
27+
28+
override protected[this] def stringPrefix: String = "SortedSet"
2729
}
2830

2931
trait SortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, CC, C]]

src/library/scala/collection/StringOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1409,5 +1409,5 @@ case class StringView(s: String) extends AbstractIndexedSeqView[Char] {
14091409
def length = s.length
14101410
@throws[StringIndexOutOfBoundsException]
14111411
def apply(n: Int) = s.charAt(n)
1412-
override def className = "StringView"
1412+
override protected[this] def className = "StringView"
14131413
}

src/library/scala/collection/View.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ trait View[+A] extends Iterable[A] with IterableOps[A, View, View[A]] {
1818

1919
override def iterableFactory = View
2020

21-
override def toString = "View(?)"
21+
override def toString = stringPrefix + "(?)"
2222

23-
override def className = "View"
23+
override protected[this] def stringPrefix = "View"
2424

2525
@deprecated("Views no longer know about their underlying collection type; .force always returns an IndexedSeq", "2.13.0")
2626
@`inline` def force: IndexedSeq[A] = toIndexedSeq

src/library/scala/collection/concurrent/TrieMap.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,7 @@ final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater
971971
if (nonReadOnly) readOnlySnapshot().size
972972
else cachedSize()
973973

974-
override def className = "TrieMap"
974+
override protected[this] def className = "TrieMap"
975975

976976
}
977977

src/library/scala/collection/immutable/ArraySeq.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ sealed abstract class ArraySeq[+A]
131131

132132
override def reverse: ArraySeq[A] = ArraySeq.unsafeWrapArray(new ArrayOps(unsafeArray).reverse).asInstanceOf[ArraySeq[A]]
133133

134-
override def className = "ArraySeq"
134+
override protected[this] def className = "ArraySeq"
135135

136136
override def copyToArray[B >: A](xs: Array[B], start: Int = 0): xs.type = copyToArray[B](xs, start, length)
137137

src/library/scala/collection/immutable/BitSet.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ sealed abstract class BitSet
2424
with collection.BitSetOps[BitSet]
2525
with StrictOptimizedIterableOps[Int, Set, BitSet] {
2626

27-
override def className: String = "BitSet"
28-
2927
def bitSetFactory = BitSet
3028

3129
protected[collection] def fromBitMaskNoCopy(elems: Array[Long]): BitSet = BitSet.fromBitMaskNoCopy(elems)

src/library/scala/collection/immutable/ChampHashMap.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ final class ChampHashMap[K, +V] private[immutable] (val rootNode: MapNode[K, V],
102102
case _ => super.equals(that)
103103
}
104104

105+
106+
override protected[this] def className = "ChampHashMap"
105107
}
106108

107109
private[immutable] object MapNode {

src/library/scala/collection/immutable/ChampHashSet.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ final class ChampHashSet[A] private[immutable] (val rootNode: SetNode[A], val ca
8484
case _ => super.equals(that)
8585
}
8686

87+
override protected[this] def className = "ChampHashSet"
8788
}
8889

8990
private[immutable] final object SetNode {

src/library/scala/collection/immutable/HashMap.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ sealed abstract class HashMap[K, +V]
9090
nullToEmpty(filter0(pred, negate = true, 0, buffer, 0))
9191
}
9292

93-
override def className: String = "HashMap"
93+
override protected[this] def className: String = "HashMap"
9494

9595
}
9696

src/library/scala/collection/immutable/HashSet.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ sealed abstract class HashSet[A]
3030

3131
import HashSet.{bufferSize, LeafHashSet, nullToEmpty}
3232

33-
override def className: String = "HashSet"
33+
override protected[this] def className: String = "HashSet"
3434

3535
override def iterableFactory = HashSet
3636

src/library/scala/collection/immutable/IntMap.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ sealed abstract class IntMap[+T] extends AbstractMap[Int, T]
258258
case IntMap.Nil =>
259259
}
260260

261-
override def className = "IntMap"
261+
override protected[this] def className = "IntMap"
262262

263263
override def isEmpty = this == IntMap.Nil
264264

src/library/scala/collection/immutable/LazyList.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ sealed abstract class LazyList[+A] extends AbstractSeq[A] with LinearSeq[A] with
225225

226226
override protected[this] def writeReplace(): AnyRef =
227227
if(headDefined && tailDefined) new LazyListOps.SerializationProxy[A, LazyList](this) else this
228+
229+
override protected[this] def className = "LazyList"
228230
}
229231

230232
sealed private[immutable] trait LazyListOps[+A, +CC[+X] <: LinearSeq[X] with LazyListOps[X, CC, CC[X]], +C <: CC[A] with LazyListOps[A, CC, C]]
@@ -255,8 +257,6 @@ sealed private[immutable] trait LazyListOps[+A, +CC[+X] <: LinearSeq[X] with Laz
255257
def lazyAppendedAll[B >: A](suffix: => collection.IterableOnce[B]): CC[B] =
256258
if (isEmpty) iterableFactory.from(suffix) else cons[B](head, tail.lazyAppendedAll(suffix))
257259

258-
override def className = "LazyList"
259-
260260
override def equals(that: Any): Boolean =
261261
if (this eq that.asInstanceOf[AnyRef]) true else super.equals(that)
262262

@@ -454,6 +454,8 @@ sealed private[immutable] trait LazyListOps[+A, +CC[+X] <: LinearSeq[X] with Laz
454454
override def mkString(sep: String): String = super.mkString(sep)
455455
override def mkString: String = super.mkString
456456

457+
protected[this] def className: String
458+
457459
/**
458460
* @return a string representation of this collection. Undefined elements are
459461
* represented with `"_"`, an undefined tail is represented with `"?"`,
@@ -714,7 +716,7 @@ object LazyList extends LazyListFactory[LazyList] {
714716
sealed abstract class Stream[+A] extends AbstractSeq[A] with LinearSeq[A] with LazyListOps[A, Stream, Stream[A]] {
715717
override def iterableFactory: LazyListFactory[Stream] = Stream
716718

717-
override def className: String = "Stream"
719+
override protected[this] def className: String = "Stream"
718720

719721
protected def cons[T](hd: => T, tl: => Stream[T]): Stream[T] = new Stream.Cons(hd, tl)
720722

src/library/scala/collection/immutable/List.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ sealed abstract class List[+A]
382382
None
383383
}
384384

385-
override def className = "List"
385+
override protected[this] def className = "List"
386386

387387
/** Builds a new list by applying a function to all elements of this list.
388388
* Like `xs map f`, but returns `xs` unchanged if function

0 commit comments

Comments
 (0)