From 3ada7038877928711176da6ebde68528ab5fc39c Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Tue, 3 Sep 2013 17:47:29 -0700 Subject: [PATCH 1/3] SI-7150 Replace scala.reflect.internal.WeakHashSet Replaces scala.reflect.internal.WeakHashSet with a version that * extends the mutable.Set trait * doesn't leak WeakReferences * is unit tested --- bincompat-backward.whitelist.conf | 8 + bincompat-forward.whitelist.conf | 800 ++++++++++++++++++ .../reflect/internal/util/WeakHashSet.scala | 453 +++++++++- test/files/run/WeakHashSetTest.scala | 174 ++++ 4 files changed, 1393 insertions(+), 42 deletions(-) create mode 100644 test/files/run/WeakHashSetTest.scala diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index 35b67a13ee62..c016b5224177 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -271,6 +271,14 @@ filter { { matchName="scala.reflect.internal.StdNames.compactifyName" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet" + problemName=FinalClassProblem + }, + { + matchName="scala.reflect.internal.util.WeakReferenceWithEquals" + problemName=MissingClassProblem } ] } diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index 20e5e30b6854..d1a19534dbc2 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -583,6 +583,806 @@ filter { { matchName="scala.reflect.internal.StdNames.compactifyName" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet$Diagnostics" + problemName=MissingClassProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet$Entry" + problemName=MissingClassProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet$" + problemName=MissingClassProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet" + problemName=MissingTypesProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.newBuilder" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.min" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.initialCapacity" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.foldLeft" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toIterable" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toIterable" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.union" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.union" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.groupBy" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.groupBy" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toSet" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toSet" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toSeq" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toSeq" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toIndexedSeq" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.unzip3" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.nonEmpty" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.slice" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.max" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.addString" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.addString" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.addString" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.subsetOf" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.fold" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toIterator" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.foreach" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.flatten" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.headOption" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.mapResult" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.scala$reflect$internal$util$WeakHashSet$$bucketFor" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toTraversable" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toTraversable" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.filter" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.tails" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.last" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.collect" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.takeRight" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.lastOption" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.reduceRightOption" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.+" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.+" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.+" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.+" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.+" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.+" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.take" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.zipWithIndex" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.foldRight" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.hasDefiniteSize" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.<<" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.sliceWithKnownBound" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.to" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.result" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.result" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.isTraversableAgain" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.add" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.partition" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toBuffer" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.update" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.view" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.view" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.view" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.view" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.tail" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.zipAll" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.retain" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.find" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.withFilter" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.init" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.findEntryOrUpdate" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.diagnostics" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.zip" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.drop" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.:\\" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.companion" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toMap" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toMap" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.genericBuilder" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.unzip" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.seq" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.seq" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.seq" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.seq" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.seq" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.seq" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.seq" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.&~" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toStream" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.-=" + problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.-=" + problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.-=" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.splitAt" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.addEntry" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.aggregate" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.parCombiner" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.maxBy" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.sliding" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.sliding" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.repr" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.repr" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.scan" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.span" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toArray" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.findEntry" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toVector" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.scala$collection$SetLike$$super$map" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.dropWhile" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.forall" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.reduce" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.intersect" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.this" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.--=" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.loadFactor" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.copyToArray" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.copyToArray" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.copyToArray" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.canEqual" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.inits" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.sliceWithKnownDelta" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.grouped" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.minBy" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet./:" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.--" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.--" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.sameElements" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.par" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.equals" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.sizeHint" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.sizeHint" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.sizeHint" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet./:\\" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.threshhold_=" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.++" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.++" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.++" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.map" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.clone" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.clone" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.diff" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.diff" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.isEmpty" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.&" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.head" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toCollection" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toCollection" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.++:" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.++:" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.mkString" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.mkString" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.mkString" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.threshhold" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.iterator" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.toList" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.-" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.-" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.-" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.-" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.-" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.-" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.-" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.++=" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.takeWhile" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.exists" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.scanRight" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.transpose" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.sizeHintBounded" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.hashCode" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.scala$collection$mutable$Cloneable$$super$clone" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.remove" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.|" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.reduceLeft" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.count" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.scanLeft" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.reduceLeftOption" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.dropRight" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.collectFirst" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.flatMap" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.+=" + problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.+=" + problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.+=" + problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.+=" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.reversed" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.sum" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.filterNot" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.product" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.thisCollection" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.thisCollection" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.copyToBuffer" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.subsets" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.subsets" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.reduceRight" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.empty" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.empty" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.empty" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.stringPrefix" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.util.WeakHashSet.reduceOption" + problemName=MissingMethodProblem } ] } diff --git a/src/reflect/scala/reflect/internal/util/WeakHashSet.scala b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala index 9882aad5e5da..fc12e313602d 100644 --- a/src/reflect/scala/reflect/internal/util/WeakHashSet.scala +++ b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala @@ -1,61 +1,430 @@ -package scala.reflect.internal.util +package scala +package reflect.internal.util -import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer -import scala.collection.mutable.Builder -import scala.collection.mutable.SetBuilder +import java.lang.ref.{WeakReference, ReferenceQueue} +import scala.annotation.tailrec import scala.collection.generic.Clearable -import scala.runtime.AbstractFunction1 +import scala.collection.mutable.{Set => mSet} -/** A bare-bones implementation of a mutable `Set` that uses weak references - * to hold the elements. +/** + * A HashSet where the elements are stored weakly. Elements in this set are elligible for GC if no other + * hard references are associated with them. Its primary use case is as a canonical reference + * identity holder (aka "hash-consing") via findEntryOrUpdate * - * This implementation offers only add/remove/test operations, - * therefore it does not fulfill the contract of Scala collection sets. + * This Set implementation cannot hold null. Any attempt to put a null in it will result in a NullPointerException + * + * This set implmeentation is not in general thread safe without external concurrency control. However it behaves + * properly when GC concurrently collects elements in this set. */ -class WeakHashSet[T <: AnyRef] extends AbstractFunction1[T, Boolean] with Clearable { - private val underlying = mutable.HashSet[WeakReferenceWithEquals[T]]() +final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: Double) extends Set[A] with Function1[A, Boolean] with mSet[A] { + + import WeakHashSet._ + + def this() = this(initialCapacity = WeakHashSet.defaultInitialCapacity, loadFactor = WeakHashSet.defaultLoadFactor) + + type This = WeakHashSet[A] + + /** + * queue of Entries that hold elements scheduled for GC + * the removeStaleEntries() method works through the queue to remeove + * stale entries from the table + */ + private[this] val queue = new ReferenceQueue[A] + + /** + * the number of elements in this set + */ + private[this] var count = 0 + + /** + * from a specified initial capacity compute the capacity we'll use as being the next + * power of two equal to or greater than the specified initial capacity + */ + private def computeCapacity = { + if (initialCapacity < 0) throw new IllegalArgumentException("initial capacity cannot be less than 0"); + var candidate = 1 + while (candidate < initialCapacity) { + candidate *= 2 + } + candidate + } + + /** + * the underlying table of entries which is an array of Entry linked lists + */ + private[this] var table = new Array[Entry[A]](computeCapacity) + + /** + * the limit at which we'll increase the size of the hash table + */ + var threshhold = computeThreshHold + + private[this] def computeThreshHold: Int = (table.size * loadFactor).ceil.toInt - /** Add the given element to this set. */ - def +=(elem: T): this.type = { - underlying += new WeakReferenceWithEquals(elem) - this + /** + * find the bucket associated with an elements's hash code + */ + private[this] def bucketFor(hash: Int): Int = { + // spread the bits around to try to avoid accidental collisions using the + // same algorithm as java.util.HashMap + var h = hash + h ^= h >>> 20 ^ h >>> 12 + h ^= h >>> 7 ^ h >>> 4 + + // this is finding h % table.length, but takes advantage of the + // fact that table length is a power of 2, + // if you don't do bit flipping in your head, if table.length + // is binary 100000.. (with n 0s) then table.length - 1 + // is 1111.. with n 1's. + // In other words this masks on the last n bits in the hash + h & (table.length - 1) } - /** Remove the given element from this set. */ - def -=(elem: T): this.type = { - underlying -= new WeakReferenceWithEquals(elem) - this + /** + * remove a single entry from a linked list in a given bucket + */ + private[this] def remove(bucket: Int, prevEntry: Entry[A], entry: Entry[A]) { + prevEntry match { + case null => table(bucket) = entry.tail + case _ => prevEntry.tail = entry.tail + } + count -= 1 } - /** Does the given element belong to this set? */ - def contains(elem: T): Boolean = - underlying.contains(new WeakReferenceWithEquals(elem)) + /** + * remove entries associated with elements that have been gc'ed + */ + private[this] def removeStaleEntries() { + def poll(): Entry[A] = queue.poll().asInstanceOf[Entry[A]] - /** Does the given element belong to this set? */ - def apply(elem: T): Boolean = contains(elem) + @tailrec + def queueLoop { + val stale = poll() + if (stale != null) { + val bucket = bucketFor(stale.hash) - /** Return the number of elements in this set, including reclaimed elements. */ - def size = underlying.size + @tailrec + def linkedListLoop(prevEntry: Entry[A], entry: Entry[A]): Unit = if (stale eq entry) remove(bucket, prevEntry, entry) + else if (entry != null) linkedListLoop(entry, entry.tail) - /** Remove all elements in this set. */ - def clear() = underlying.clear() -} + linkedListLoop(null, table(bucket)) -/** A WeakReference implementation that implements equals and hashCode by - * delegating to the referent. - */ -class WeakReferenceWithEquals[T <: AnyRef](ref: T) { - def get(): T = underlying.get() + queueLoop + } + } + + queueLoop + } + + /** + * Double the size of the internal table + */ + private[this] def resize() { + val oldTable = table + table = new Array[Entry[A]](oldTable.size * 2) + threshhold = computeThreshHold + + @tailrec + def tableLoop(oldBucket: Int): Unit = if (oldBucket < oldTable.size) { + @tailrec + def linkedListLoop(entry: Entry[A]): Unit = entry match { + case null => () + case _ => { + val bucket = bucketFor(entry.hash) + val oldNext = entry.tail + entry.tail = table(bucket) + table(bucket) = entry + linkedListLoop(oldNext) + } + } + linkedListLoop(oldTable(oldBucket)) + + tableLoop(oldBucket + 1) + } + tableLoop(0) + } + + // from scala.reflect.internal.Set, find an element or null if it isn't contained + override def findEntry(elem: A): A = elem match { + case null => throw new NullPointerException("WeakHashSet cannot hold nulls") + case _ => { + removeStaleEntries() + val hash = elem.hashCode + val bucket = bucketFor(hash) + + @tailrec + def linkedListLoop(entry: Entry[A]): A = entry match { + case null => null.asInstanceOf[A] + case _ => { + val entryElem = entry.get + if (elem == entryElem) entryElem + else linkedListLoop(entry.tail) + } + } + + linkedListLoop(table(bucket)) + } + } + // add an element to this set unless it's already in there and return the element + def findEntryOrUpdate(elem: A): A = elem match { + case null => throw new NullPointerException("WeakHashSet cannot hold nulls") + case _ => { + removeStaleEntries() + val hash = elem.hashCode + val bucket = bucketFor(hash) + val oldHead = table(bucket) + + def add() = { + table(bucket) = new Entry(elem, hash, oldHead, queue) + count += 1 + if (count > threshhold) resize() + elem + } + + @tailrec + def linkedListLoop(entry: Entry[A]): A = entry match { + case null => add() + case _ => { + val entryElem = entry.get + if (elem == entryElem) entryElem + else linkedListLoop(entry.tail) + } + } + + linkedListLoop(oldHead) + } + } + + // add an element to this set unless it's already in there and return this set + override def +(elem: A): this.type = elem match { + case null => throw new NullPointerException("WeakHashSet cannot hold nulls") + case _ => { + removeStaleEntries() + val hash = elem.hashCode + val bucket = bucketFor(hash) + val oldHead = table(bucket) + + def add() { + table(bucket) = new Entry(elem, hash, oldHead, queue) + count += 1 + if (count > threshhold) resize() + } + + @tailrec + def linkedListLoop(entry: Entry[A]): Unit = entry match { + case null => add() + case _ if (elem == entry.get) => () + case _ => linkedListLoop(entry.tail) + } + + linkedListLoop(oldHead) + this + } + } + + def +=(elem: A) = this + elem + + // from scala.reflect.interanl.Set + override def addEntry(x: A) { this += x } + + // remove an element from this set and return this set + override def -(elem: A): this.type = elem match { + case null => this + case _ => { + removeStaleEntries() + val bucket = bucketFor(elem.hashCode) - override val hashCode = ref.hashCode - override def equals(other: Any): Boolean = other match { - case wf: WeakReferenceWithEquals[_] => - underlying.get() == wf.get() - case _ => - false + + @tailrec + def linkedListLoop(prevEntry: Entry[A], entry: Entry[A]): Unit = entry match { + case null => () + case _ if (elem == entry.get) => remove(bucket, prevEntry, entry) + case _ => linkedListLoop(entry, entry.tail) + } + + linkedListLoop(null, table(bucket)) + this + } } - private val underlying = new java.lang.ref.WeakReference(ref) + def -=(elem: A) = this - elem + + // empty this set + override def clear(): Unit = { + table = new Array[Entry[A]](table.size) + threshhold = computeThreshHold + count = 0 + + // drain the queue - doesn't do anything because we're throwing away all the values anyway + @tailrec def queueLoop(): Unit = if (queue.poll() != null) queueLoop() + queueLoop() + } + + // true if this set is empty + override def empty: This = new WeakHashSet[A](initialCapacity, loadFactor) + + // the number of elements in this set + override def size: Int = { + removeStaleEntries() + count + } + + override def apply(x: A): Boolean = this contains x + + override def foreach[U](f: A => U): Unit = iterator foreach f + + override def toList(): List[A] = iterator.toList + + // Iterator over all the elements in this set in no particular order + override def iterator: Iterator[A] = { + removeStaleEntries() + + new Iterator[A] { + + /** + * the bucket currently being examined. Initially it's set past the last bucket and will be decremented + */ + private[this] var currentBucket: Int = table.size + + /** + * the entry that was last examined + */ + private[this] var entry: Entry[A] = null + + /** + * the element that will be the result of the next call to next() + */ + private[this] var lookaheadelement: A = null.asInstanceOf[A] + + @tailrec + def hasNext: Boolean = { + while (entry == null && currentBucket > 0) { + currentBucket -= 1 + entry = table(currentBucket) + } + + if (entry == null) false + else { + lookaheadelement = entry.get + if (lookaheadelement == null) { + // element null means the weakref has been cleared since we last did a removeStaleEntries(), move to the next entry + entry = entry.tail + hasNext + } else { + true + } + } + } + + def next(): A = if (lookaheadelement == null) + throw new IndexOutOfBoundsException("next on an empty iterator") + else { + val result = lookaheadelement + lookaheadelement = null.asInstanceOf[A] + entry = entry.tail + result + } + } + } + + /** + * Diagnostic information about the internals of this set. Not normally + * needed by ordinary code, but may be useful for diagnosing performance problems + */ + private[util] class Diagnostics { + /** + * Verify that the internal structure of this hash set is fully consistent. + * Throws an assertion error on any problem. In order for it to be reliable + * the entries must be stable. If any are garbage collected during validation + * then an assertion may inappropriately fire. + */ + def fullyValidate { + var computedCount = 0 + var bucket = 0 + while (bucket < table.size) { + var entry = table(bucket) + while (entry != null) { + assert(entry.get != null, s"$entry had a null value indicated that gc activity was happening during diagnostic validation or that a null value was inserted") + computedCount += 1 + val cachedHash = entry.hash + val realHash = entry.get.hashCode + assert(cachedHash == realHash, s"for $entry cached hash was $cachedHash but should have been $realHash") + val computedBucket = bucketFor(realHash) + assert(computedBucket == bucket, s"for $entry the computed bucket was $computedBucket but should have been $bucket") + + entry = entry.tail + } + + bucket += 1 + } + + assert(computedCount == count, s"The computed count was $computedCount but should have been $count") + } + + /** + * Produces a diagnostic dump of the table that underlies this hash set. + */ + def dump = table.deep + + /** + * Number of buckets that hold collisions. Useful for diagnosing performance issues. + */ + def collisionBucketsCount: Int = + (table filter (entry => entry != null && entry.tail != null)).size + + /** + * Number of buckets that are occupied in this hash table. + */ + def fullBucketsCount: Int = + (table filter (entry => entry != null)).size + + /** + * Number of buckets in the table + */ + def bucketsCount: Int = table.size + + /** + * Number of buckets that don't hold anything + */ + def emptyBucketsCount = bucketsCount - fullBucketsCount + + /** + * Number of elements that are in collision. Useful for diagnosing performance issues. + */ + def collisionsCount = size - (fullBucketsCount - collisionBucketsCount) + + /** + * A map from a count of elements to the number of buckets with that count + */ + def elementCountDistribution = table map linkedListSize groupBy identity map {case (size, list) => (size, list.size)} + + private def linkedListSize(entry: Entry[A]) = { + var e = entry + var count = 0 + while (e != null) { + count += 1 + e = e.tail + } + count + } + } + + private[util] def diagnostics = new Diagnostics +} + +/** + * Companion object for WeakHashSet + */ +object WeakHashSet { + /** + * A single entry in a WeakHashSet. It's a WeakReference plus a cached hash code and + * a link to the next Entry in the same bucket + */ + private class Entry[A](element: A, val hash:Int, var tail: Entry[A], queue: ReferenceQueue[A]) extends WeakReference[A](element, queue) + + val defaultInitialCapacity = 16 + val defaultLoadFactor = .75 + + def apply[A <: AnyRef](initialCapacity: Int = WeakHashSet.defaultInitialCapacity, loadFactor: Double = WeakHashSet.defaultLoadFactor) = new WeakHashSet[A](initialCapacity, defaultLoadFactor) } diff --git a/test/files/run/WeakHashSetTest.scala b/test/files/run/WeakHashSetTest.scala new file mode 100644 index 000000000000..3c8f380150b1 --- /dev/null +++ b/test/files/run/WeakHashSetTest.scala @@ -0,0 +1,174 @@ +object Test { + def main(args: Array[String]) { + val test = scala.reflect.internal.util.WeakHashSetTest + test.checkEmpty + test.checkPlusEquals + test.checkPlusEqualsCollisions + test.checkRehashing + test.checkRehashCollisions + test.checkFindOrUpdate + test.checkMinusEquals + test.checkMinusEqualsCollisions + test.checkClear + test.checkIterator + test.checkIteratorCollisions + + // This test is commented out because it relies on gc behavior which isn't reliable enough in an automated environment + // test.checkRemoveUnreferencedObjects + } +} + +// put the main test object in the same package as WeakHashSet because +// it uses the package private "diagnostics" method +package scala.reflect.internal.util { + + object WeakHashSetTest { + // a class guaranteed to provide hash collisions + case class Collider(x : String) extends Comparable[Collider] with Serializable { + override def hashCode = 0 + def compareTo(y : Collider) = this.x compareTo y.x + } + + // basic emptiness check + def checkEmpty { + val hs = new WeakHashSet[String]() + assert(hs.size == 0) + hs.diagnostics.fullyValidate + } + + // make sure += works + def checkPlusEquals { + val hs = new WeakHashSet[String]() + val elements = List("hello", "goodbye") + elements foreach (hs += _) + assert(hs.size == 2) + assert(hs contains "hello") + assert(hs contains "goodbye") + hs.diagnostics.fullyValidate + } + + // make sure += works when there are collisions + def checkPlusEqualsCollisions { + val hs = new WeakHashSet[Collider]() + val elements = List("hello", "goodbye") map Collider + elements foreach (hs += _) + assert(hs.size == 2) + assert(hs contains Collider("hello")) + assert(hs contains Collider("goodbye")) + hs.diagnostics.fullyValidate + } + + // add a large number of elements to force rehashing and then validate + def checkRehashing { + val size = 200 + val hs = new WeakHashSet[String]() + val elements = (0 until size).toList map ("a" + _) + elements foreach (hs += _) + elements foreach {i => assert(hs contains i)} + hs.diagnostics.fullyValidate + } + + // make sure rehashing works properly when the set is rehashed + def checkRehashCollisions { + val size = 200 + val hs = new WeakHashSet[Collider]() + val elements = (0 until size).toList map {x => Collider("a" + x)} + elements foreach (hs += _) + elements foreach {i => assert(hs contains i)} + hs.diagnostics.fullyValidate + } + + // test that unreferenced objects are removed + // not run in an automated environment because gc behavior can't be relied on + def checkRemoveUnreferencedObjects { + val size = 200 + val hs = new WeakHashSet[Collider]() + val elements = (0 until size).toList map {x => Collider("a" + x)} + elements foreach (hs += _) + // don't throw the following into a retained collection so gc + // can remove them + for (i <- 0 until size) { + hs += Collider("b" + i) + } + System.gc() + Thread.sleep(1000) + assert(hs.size == 200) + elements foreach {i => assert(hs contains i)} + for (i <- 0 until size) { + assert(!(hs contains Collider("b" + i))) + } + hs.diagnostics.fullyValidate + } + + // make sure findOrUpdate returns the originally entered element + def checkFindOrUpdate { + val size = 200 + val hs = new WeakHashSet[Collider]() + val elements = (0 until size).toList map {x => Collider("a" + x)} + elements foreach {x => assert(hs findEntryOrUpdate x eq x)} + for (i <- 0 until size) { + // when we do a lookup the result should be the same reference we + // original put in + assert(hs findEntryOrUpdate(Collider("a" + i)) eq elements(i)) + } + hs.diagnostics.fullyValidate + } + + // check -= functionality + def checkMinusEquals { + val hs = new WeakHashSet[String]() + val elements = List("hello", "goodbye") + elements foreach (hs += _) + hs -= "goodbye" + assert(hs.size == 1) + assert(hs contains "hello") + assert(!(hs contains "goodbye")) + hs.diagnostics.fullyValidate + } + + // check -= when there are collisions + def checkMinusEqualsCollisions { + val hs = new WeakHashSet[Collider] + val elements = List(Collider("hello"), Collider("goodbye")) + elements foreach (hs += _) + hs -= Collider("goodbye") + assert(hs.size == 1) + assert(hs contains Collider("hello")) + assert(!(hs contains Collider("goodbye"))) + hs -= Collider("hello") + assert(hs.size == 0) + assert(!(hs contains Collider("hello"))) + hs.diagnostics.fullyValidate + } + + // check that the clear method actually cleans everything + def checkClear { + val size = 200 + val hs = new WeakHashSet[String]() + val elements = (0 until size).toList map ("a" + _) + elements foreach (hs += _) + hs.clear() + assert(hs.size == 0) + elements foreach {i => assert(!(hs contains i))} + hs.diagnostics.fullyValidate + } + + // check that the iterator covers all the contents + def checkIterator { + val hs = new WeakHashSet[String]() + val elements = (0 until 20).toList map ("a" + _) + elements foreach (hs += _) + assert(elements.iterator.toList.sorted == elements.sorted) + hs.diagnostics.fullyValidate + } + + // check that the iterator covers all the contents even when there is a collision + def checkIteratorCollisions { + val hs = new WeakHashSet[Collider] + val elements = (0 until 20).toList map {x => Collider("a" + x)} + elements foreach (hs += _) + assert(elements.iterator.toList.sorted == elements.sorted) + hs.diagnostics.fullyValidate + } + } +} From 989c3f85d95a22c95bc7ce936c4bd57ff0608bcd Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Tue, 3 Sep 2013 18:12:31 -0700 Subject: [PATCH 2/3] SI-7149 Use a WeakHashSet for type uniqueness Currently type uniqueness is done via a HashSet[Type], but that means the Types live through an entire compile session, even ones that are used once. The result is a huge amount of unnecessarily retained memory. This commit uses a WeakHashSet instead so that Types and their WeakReferences are cleaned up when no longer in use. --- bincompat-backward.whitelist.conf | 24 +++++++++++++++++++ bincompat-forward.whitelist.conf | 24 +++++++++++++++++++ .../scala/reflect/internal/Types.scala | 4 ++-- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index c016b5224177..38d26c7fb74b 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -279,6 +279,30 @@ filter { { matchName="scala.reflect.internal.util.WeakReferenceWithEquals" problemName=MissingClassProblem + }, + { + matchName="scala.reflect.internal.SymbolTable.scala$reflect$internal$Types$$uniques" + problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.SymbolTable.scala$reflect$internal$Types$$uniques_=" + problemName=IncompatibleMethTypeProblem + }, + { + matchName="scala.reflect.internal.Types.scala$reflect$internal$Types$$uniques" + problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.Types.scala$reflect$internal$Types$$uniques_=" + problemName=IncompatibleMethTypeProblem + }, + { + matchName="scala.reflect.internal.Types.scala$reflect$internal$Types$$uniques" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Types.scala$reflect$internal$Types$$uniques_=" + problemName=MissingMethodProblem } ] } diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index d1a19534dbc2..a64eb0ba5ded 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -1383,6 +1383,30 @@ filter { { matchName="scala.reflect.internal.util.WeakHashSet.reduceOption" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.SymbolTable.scala$reflect$internal$Types$$uniques" + problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.SymbolTable.scala$reflect$internal$Types$$uniques_=" + problemName=IncompatibleMethTypeProblem + }, + { + matchName="scala.reflect.internal.Types.scala$reflect$internal$Types$$uniques" + problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.Types.scala$reflect$internal$Types$$uniques_=" + problemName=IncompatibleMethTypeProblem + }, + { + matchName="scala.reflect.internal.Types.scala$reflect$internal$Types$$uniques" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Types.scala$reflect$internal$Types$$uniques_=" + problemName=MissingMethodProblem } ] } diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index cd9f3d23c919..cfa6f927b572 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -3935,13 +3935,13 @@ trait Types extends api.Types { self: SymbolTable => // Hash consing -------------------------------------------------------------- private val initialUniquesCapacity = 4096 - private var uniques: util.HashSet[Type] = _ + private var uniques: util.WeakHashSet[Type] = _ private var uniqueRunId = NoRunId protected def unique[T <: Type](tp: T): T = { if (Statistics.canEnable) Statistics.incCounter(rawTypeCount) if (uniqueRunId != currentRunId) { - uniques = util.HashSet[Type]("uniques", initialUniquesCapacity) + uniques = util.WeakHashSet[Type](initialUniquesCapacity) perRunCaches.recordCache(uniques) uniqueRunId = currentRunId } From a78dddd73c0fb26d9c5ef2af3d592ff5f278b237 Mon Sep 17 00:00:00 2001 From: James Iry Date: Fri, 17 May 2013 14:00:57 -0700 Subject: [PATCH 3/3] Modify perRunCaches to not leak WeakReferences perRunCaches was using a HashMap of WeakReferences which meant it would accumulate WeakReferences over time. This commit uses a WeakHashSet instead so that the references are cleaned up. --- .../scala/reflect/internal/SymbolTable.scala | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 5ccf81b4b50e..6ca8900d7c56 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -302,28 +302,21 @@ abstract class SymbolTable extends macros.Universe } object perRunCaches { - import java.lang.ref.WeakReference import scala.runtime.ScalaRunTime.stringOf import scala.collection.generic.Clearable // Weak references so the garbage collector will take care of // letting us know when a cache is really out of commission. - private val caches = mutable.HashSet[WeakReference[Clearable]]() + private val caches = WeakHashSet[Clearable]() def recordCache[T <: Clearable](cache: T): T = { - caches += new WeakReference(cache) + caches += cache cache } def clearAll() = { debuglog("Clearing " + caches.size + " caches.") - caches foreach { ref => - val cache = ref.get() - if (cache == null) - caches -= ref - else - cache.clear() - } + caches foreach (_.clear) } def newWeakMap[K, V]() = recordCache(mutable.WeakHashMap[K, V]())