From 5dbc37dfbe0e9a039da6744e45012abc3034cdf5 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 23 Aug 2013 14:23:48 +0200 Subject: SI-7779 Account for class name compactification in reflection We have to assume that the classes we are reflecting on were compiled with the default value for -Xmax-classfile-name (255). With this assumption, we can apply the same name compactification as done in the regular compiler. The REPL is particularly prone to generating long class names with the '$iw' prefixes, so this is an important fix for runtime reflection. Also adds support for getting the runtime class of `O.type` if `O` is a module. --- bincompat-backward.whitelist.conf | 4 ++ bincompat-forward.whitelist.conf | 12 ++++ src/reflect/scala/reflect/internal/StdNames.scala | 1 + .../scala/reflect/runtime/JavaMirrors.scala | 14 +++++ test/files/run/t7779.scala | 67 ++++++++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 test/files/run/t7779.scala diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index 08d972eee1..35b67a13ee 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -267,6 +267,10 @@ filter { { matchName="scala.reflect.internal.SymbolTable.scala$reflect$internal$Trees$$duplicator" problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.StdNames.compactifyName" + problemName=MissingMethodProblem } ] } diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index 7fdd4329ea..20e5e30b68 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -571,6 +571,18 @@ filter { { matchName="scala.reflect.internal.SymbolTable.scala$reflect$internal$Trees$$duplicator" problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.runtime.JavaMirrors#JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$PackageAndClassPattern" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.SymbolTable.compactifyName" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.StdNames.compactifyName" + problemName=MissingMethodProblem } ] } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index de7af4340d..c3b7f24a9d 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -44,6 +44,7 @@ trait StdNames { } } + private[reflect] def compactifyName(orig: String): String = compactify(orig) private final object compactify extends (String => String) { val md5 = MessageDigest.getInstance("MD5") diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 22fe7f098c..6fdb238462 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -1178,6 +1178,17 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni var fullNameOfJavaClass = ownerClazz.getName if (childOfClass || childOfTopLevel) fullNameOfJavaClass += "$" fullNameOfJavaClass += clazz.name + + // compactify (see SI-7779) + fullNameOfJavaClass = fullNameOfJavaClass match { + case PackageAndClassPattern(pack, clazzName) => + // in a package + pack + compactifyName(clazzName) + case _ => + // in the empty package + compactifyName(fullNameOfJavaClass) + } + if (clazz.isModuleClass) fullNameOfJavaClass += "$" // println(s"ownerChildren = ${ownerChildren.toList}") @@ -1187,6 +1198,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni noClass } + private val PackageAndClassPattern = """(.*\.)(.*)$""".r + private def expandedName(sym: Symbol): String = if (sym.isPrivate) nme.expandedName(sym.name.toTermName, sym.owner).toString else sym.name.toString @@ -1241,6 +1254,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case TypeRef(_, ArrayClass, List(elemtpe)) => jArrayClass(typeToJavaClass(elemtpe)) case TypeRef(_, sym: ClassSymbol, _) => classToJava(sym.asClass) case tpe @ TypeRef(_, sym: AliasTypeSymbol, _) => typeToJavaClass(tpe.dealias) + case SingleType(_, sym: ModuleSymbol) => classToJava(sym.moduleClass.asClass) case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") } } diff --git a/test/files/run/t7779.scala b/test/files/run/t7779.scala new file mode 100644 index 0000000000..db32cb751f --- /dev/null +++ b/test/files/run/t7779.scala @@ -0,0 +1,67 @@ +// -Xmax-classfile-length doesn't compress top-level classes. +// class ::::::::::::::::::::::::::::::::::::::::::::::::: + +trait Marker + +class Short extends Marker + +// We just test with member classes +object O { + object ::::::::::::::::::::::::::::::::::::::::::::::::: extends Marker +} +class C { + class D { + class ::::::::::::::::::::::::::::::::::::::::::::::::: extends Marker + } +} + +package pack { + // abbreviates to: $colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon to $read$$iw$$iw$$colon$colon$colon$colon$colon$colon$colon$colon$$$$c39b3f245029fbed9732fc888d44231b$$$$on$colon$colon$colon$colon$colon$colon$colon$colon$colon$colon + // class ::::::::::::::::::::::::::::::::::::::::::::::::: + + class Short extends Marker + + // We just test with member classes + object O { + object ::::::::::::::::::::::::::::::::::::::::::::::::: extends Marker + } + class C { + class D { + class ::::::::::::::::::::::::::::::::::::::::::::::::: extends Marker + } + } + package p2 { + class Short extends Marker + + object O { + object ::::::::::::::::::::::::::::::::::::::::::::::::: extends Marker + } + class C { + class D { + class ::::::::::::::::::::::::::::::::::::::::::::::::: extends Marker + } + } + } +} + + +object Test extends App { + import reflect.runtime.universe._ + def test[T: TypeTag] = { + val tt = typeTag[T] + val clz = tt.mirror.runtimeClass(tt.tpe) + assert(classOf[Marker].isAssignableFrom(clz), clz.toString) + } + + test[Short] + test[O.:::::::::::::::::::::::::::::::::::::::::::::::::.type] + test[C#D#`:::::::::::::::::::::::::::::::::::::::::::::::::`] + + test[pack.Short] + test[pack.O.:::::::::::::::::::::::::::::::::::::::::::::::::.type] + test[pack.C#D#`:::::::::::::::::::::::::::::::::::::::::::::::::`] + + test[pack.p2.Short] + test[pack.p2.O.:::::::::::::::::::::::::::::::::::::::::::::::::.type] + test[pack.p2.C#D#`:::::::::::::::::::::::::::::::::::::::::::::::::`] +} -- cgit v1.2.3 From 9d5ed33ef9b503f20506dbe3e410960069a99d0a Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 23 Aug 2013 09:29:56 +0200 Subject: SI-7775 Harden against the shifting sands of System.getProperties If another thread writes a new system property (which can happen in pretty innocuous code such as `new Date`!), the compiler startup could fail with a `ConcurrentModificationException` as it iterated all bindings in the properties map in search of a boot classpath property for esoteric JVMs. This commit uses `Properties#getStringProperties` to get a snapshot of the keys that isn't backed by the live map, and iterates these instead. That method will also limit us to bindings with String values, which is all that we expect. --- .../scala/tools/reflect/WrappedProperties.scala | 8 ++++++-- test/files/run/t7775.scala | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 test/files/run/t7775.scala diff --git a/src/compiler/scala/tools/reflect/WrappedProperties.scala b/src/compiler/scala/tools/reflect/WrappedProperties.scala index c34edb8ba0..7ce0171c0b 100644 --- a/src/compiler/scala/tools/reflect/WrappedProperties.scala +++ b/src/compiler/scala/tools/reflect/WrappedProperties.scala @@ -26,9 +26,13 @@ trait WrappedProperties extends PropertiesTrait { override def envOrElse(name: String, alt: String) = wrap(super.envOrElse(name, alt)) getOrElse alt override def envOrNone(name: String) = wrap(super.envOrNone(name)).flatten - def systemProperties: Iterator[(String, String)] = { + def systemProperties: List[(String, String)] = { import scala.collection.JavaConverters._ - wrap(System.getProperties.asScala.iterator) getOrElse Iterator.empty + wrap { + val props = System.getProperties + // SI-7269 Be careful to avoid `ConcurrentModificationException` if another thread modifies the properties map + props.stringPropertyNames().asScala.toList.map(k => (k, props.get(k).asInstanceOf[String])) + } getOrElse Nil } } diff --git a/test/files/run/t7775.scala b/test/files/run/t7775.scala new file mode 100644 index 0000000000..5fb0327611 --- /dev/null +++ b/test/files/run/t7775.scala @@ -0,0 +1,17 @@ +import scala.concurrent.{duration, future, Await, ExecutionContext} +import scala.tools.nsc.Settings +import ExecutionContext.Implicits.global + +// Was failing pretty regularly with a ConcurrentModificationException as +// WrappedProperties#systemProperties iterated directly over the mutable +// global system properties map. +object Test { + def main(args: Array[String]) { + val tries = 1000 // YMMV + val compiler = future { + for(_ <- 1 to tries) new Settings(_ => {}) + } + for(i <- 1 to tries * 10) System.setProperty(s"foo$i", i.toString) + Await.result(compiler, duration.Duration.Inf) + } +} -- cgit v1.2.3 From bce786f070afe383d203c4d809ef69803330b340 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 26 Aug 2013 12:30:21 +0200 Subject: SI-7782 Derive type skolems at the ground level Rather than at the current value of `skolemizationLevel`, which could be influenced by an in-flight existential subtype computation. This method is called in `PolyTypeCompleter`, which could be constructed by the lazy type completer of the enclosing class. So currently it is closing over a mutable variable; hence the Heisenbug. This issue was exposed by the changes in b74c33eb860, which was introduced in Scala 2.10.1. --- .../reflect/internal/ExistentialsAndSkolems.scala | 9 +++++++- test/files/pos/t7782.scala | 25 ++++++++++++++++++++++ test/files/pos/t7782b.scala | 25 ++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/t7782.scala create mode 100644 test/files/pos/t7782b.scala diff --git a/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala index 3bcb793926..2c2ed351c9 100644 --- a/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala +++ b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala @@ -19,6 +19,9 @@ trait ExistentialsAndSkolems { * can be deskolemized to the original type parameter. (A skolem is a * representation of a bound variable when viewed inside its scope.) * !!!Adriaan: this does not work for hk types. + * + * Skolems will be created at level 0, rather than the current value + * of `skolemizationLevel`. (See SI-7782) */ def deriveFreshSkolems(tparams: List[Symbol]): List[Symbol] = { class Deskolemizer extends LazyType { @@ -30,7 +33,11 @@ trait ExistentialsAndSkolems { sym setInfo sym.deSkolemize.info.substSym(typeParams, typeSkolems) } } - (new Deskolemizer).typeSkolems + + val saved = skolemizationLevel + skolemizationLevel = 0 + try new Deskolemizer().typeSkolems + finally skolemizationLevel = saved } def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type? diff --git a/test/files/pos/t7782.scala b/test/files/pos/t7782.scala new file mode 100644 index 0000000000..037bdad673 --- /dev/null +++ b/test/files/pos/t7782.scala @@ -0,0 +1,25 @@ +package pack + +object Test { + import O.empty + empty // this will trigger completion of `test` + // with skolemizationLevel = 1 +} + +object O { + // order matters (!!!) + + // this order breaks under 2.10.x + def empty[E]: C[E] = ??? + def empty(implicit a: Any): Any = ??? +} + +abstract class C[E] { + def foo[BB](f: BB) + def test[B](f: B): Any = foo(f) + // error: no type parameters for method foo: ( f: BB)scala.this.Unit exist so that it can be applied to arguments (B&1) + // --- because --- + // argument expression's type is not compatible with formal parameter type; + // found : B&1 + // required: ?BB +} diff --git a/test/files/pos/t7782b.scala b/test/files/pos/t7782b.scala new file mode 100644 index 0000000000..09da4a5c5b --- /dev/null +++ b/test/files/pos/t7782b.scala @@ -0,0 +1,25 @@ +package pack + +object Test { + import O.empty + empty // this will trigger completion of `test` + // with skolemizationLevel = 1 +} + +object O { + // order matters (!!!) + + // this order breaks under 2.11.x + def empty(implicit a: Any): Any = ??? + def empty[E]: C[E] = ??? +} + +abstract class C[E] { + def foo[BB](f: BB) + def test[B](f: B): Any = foo(f) + // error: no type parameters for method foo: ( f: BB)scala.this.Unit exist so that it can be applied to arguments (B&1) + // --- because --- + // argument expression's type is not compatible with formal parameter type; + // found : B&1 + // required: ?BB +} -- cgit v1.2.3 From 9772ec8b2f15fd8c2971413d90006beb8bf0d2b7 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 29 Aug 2013 08:29:26 +0200 Subject: typedAnnotated no longer emits nulls Adds a null-check in original synthesis for the result of typedAnnotated. Previously it was possible for the aforementioned result to look like TypeTree() setOriginal Annotated(..., null). Not anymore. --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 +- test/files/run/typed-annotated.check | 1 + test/files/run/typed-annotated/Macros_1.scala | 17 +++++++++++++++++ test/files/run/typed-annotated/Test_2.scala | 3 +++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 test/files/run/typed-annotated.check create mode 100644 test/files/run/typed-annotated/Macros_1.scala create mode 100644 test/files/run/typed-annotated/Test_2.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index bf2170310f..e27f540a7d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4107,7 +4107,7 @@ trait Typers extends Modes with Adaptations with Tags { // we need symbol-ful originals for reification // hence we go the extra mile to hand-craft tis guy val original = arg1 match { - case tt @ TypeTree() => Annotated(ann, tt.original) + case tt @ TypeTree() if tt.original != null => Annotated(ann, tt.original) // this clause is needed to correctly compile stuff like "new C @D" or "@(inline @getter)" case _ => Annotated(ann, arg1) } diff --git a/test/files/run/typed-annotated.check b/test/files/run/typed-annotated.check new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/test/files/run/typed-annotated.check @@ -0,0 +1 @@ +42 diff --git a/test/files/run/typed-annotated/Macros_1.scala b/test/files/run/typed-annotated/Macros_1.scala new file mode 100644 index 0000000000..dd18c63d90 --- /dev/null +++ b/test/files/run/typed-annotated/Macros_1.scala @@ -0,0 +1,17 @@ +import scala.reflect.macros.Context +import language.experimental.macros + +class ann extends scala.annotation.StaticAnnotation + +object Macros { + def impl(c: Context) = { + import c.universe._ + // val tpt = Annotated(Apply(Select(New(Ident(newTypeName("ann"))), nme.CONSTRUCTOR), List()), Ident(newTypeName("Int"))) + val tpt = Annotated(Apply(Select(New(Ident(newTypeName("ann"))), nme.CONSTRUCTOR), List()), TypeTree(weakTypeOf[Int])) + c.Expr[Unit](Block( + List(ValDef(Modifiers(), newTermName("x"), tpt, Literal(Constant(42)))), + Apply(Ident(newTermName("println")), List(Ident(newTermName("x")))))) + } + + def foo = macro impl +} \ No newline at end of file diff --git a/test/files/run/typed-annotated/Test_2.scala b/test/files/run/typed-annotated/Test_2.scala new file mode 100644 index 0000000000..acfddae942 --- /dev/null +++ b/test/files/run/typed-annotated/Test_2.scala @@ -0,0 +1,3 @@ +object Test extends App { + Macros.foo +} \ No newline at end of file -- cgit v1.2.3 From cb9f2b9a9a97f2ae2f37d65a1125ee238bc036ac Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 29 Aug 2013 06:49:55 -0700 Subject: [nomaster] SI-7790 No ScriptEngine in 2.10 build The service entry was inadvertendly added in `e3b5e0ba40447970d621cfeed5cc1770df69884f`, "Sanity for build.xml: exscriptus&positus delendus est," which is ant latin for: "Reduced copy/pasting to the best of my antabilities." With that degree of hubris, it was inevitable that the commit introduced a copy/paste bug. What is the Attic Greek for copy/paste? I'm pretty sure that in fifth century Athens they would just pound one inscription to rubble using a bigger inscription. They had slaves for that kind of work. You never hear about Socrates tweaking the build script. That's the reason he never wrote anything down. --- build.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.xml b/build.xml index d8d0ca3bab..317898f6db 100644 --- a/build.xml +++ b/build.xml @@ -1240,9 +1240,11 @@ TODO: + -- cgit v1.2.3 From 133b5c0750259260bfc0b6f354757c3d79bb2c55 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 15 Jul 2013 10:17:26 +1000 Subject: Commit .gitignore directly Rather than relying on the cloner to copy the provided gitignore.SAMPLE files. This finishes the job started in c48509598, mostly by reverting that commit and moving the two existing SAMPLE files to the final destinations. Use `.git/info/exclude` to augment the list of patterns with entries specific to your workflow. (cherry picked from commit b51cb581270da7021b2ea122dc059847101d56a7) ============================================== Paring back the scope of our shared .gitignore Importantly, limit the exclusion of build.properties to the file in the root directory, paving the way for the return of an SBT build. - Unignores .bak, .jar, and ~ - limit ignorance of qbin to the root directory .log files, generated by partest, are still ignored. To see ignored files in your workspace, try: git ls-files --others --ignored --exclude-standard -- test | grep log git status --ignored -- test (cherry picked from commit f0bbd2ca32acb40be37dc382c1f95081deca3f22) --- .gitignore | 48 +++++++++++++++++++++++++++++++++++++++++++++ gitignore.SAMPLE | 35 --------------------------------- test/files/.gitignore | 2 ++ test/files/gitignore.SAMPLE | 2 -- 4 files changed, 50 insertions(+), 37 deletions(-) create mode 100644 .gitignore delete mode 100644 gitignore.SAMPLE create mode 100644 test/files/.gitignore delete mode 100644 test/files/gitignore.SAMPLE diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..f90835d970 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +# +# Are you tempted to edit this file? +# +# First consider if the changes make sense for all, +# or if they are specific to your workflow/system. +# If it is the latter, you can augment this list with +# entries in .git/info/excludes +# +# see also test/files/.gitignore +# + +# +# JARs aren't checked in, they are fetched by Ant / pull_binary_libs.sh +# +# We could be more concise with /lib/**/*.jar but that assumes +# a late-model git. +# +/lib/ant/*.jar +/lib/*.jar +/test/files/codelib/*.jar +/test/files/lib/*.jar +/test/files/speclib/instrumented.jar +/tools/*.jar + +# Developer specific Ant properties +/build.properties + +# target directories for ant build +/build/ +/dists/ + +# other +/out/ +/bin/ +/sandbox/ + +# eclipse, intellij +/.classpath +/.project +/src/intellij/*.iml +/src/intellij/*.ipr +/src/intellij/*.iws +/.cache +/.idea +/.settings + +# Standard symbolic link to build/quick/bin +/qbin diff --git a/gitignore.SAMPLE b/gitignore.SAMPLE deleted file mode 100644 index 483ad4caca..0000000000 --- a/gitignore.SAMPLE +++ /dev/null @@ -1,35 +0,0 @@ -# see also test/files/.gitignore -/.gitignore -/test/files/.gitignore - -*.jar -*~ - -#sbt -/project/target/ -/project/project/target - -/target/ -/src/jline/target/ - -# target directories for ant build -/build/ -/dists/ - -# other -/out/ -/bin/ -/sandbox/ - -# eclipse, intellij -/.classpath -/.project -/src/intellij/*.iml -/src/intellij/*.ipr -/src/intellij/*.iws -/.cache -/.idea -/.settings - -# bak files produced by ./cleanup-commit -*.bak diff --git a/test/files/.gitignore b/test/files/.gitignore new file mode 100644 index 0000000000..161be5b55f --- /dev/null +++ b/test/files/.gitignore @@ -0,0 +1,2 @@ +*.log +*.obj/ diff --git a/test/files/gitignore.SAMPLE b/test/files/gitignore.SAMPLE deleted file mode 100644 index 161be5b55f..0000000000 --- a/test/files/gitignore.SAMPLE +++ /dev/null @@ -1,2 +0,0 @@ -*.log -*.obj/ -- cgit v1.2.3 From 27d61a2ef6c3059fd56a36fc18886fc53bb354a8 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 30 Aug 2013 07:12:03 -0700 Subject: SI-4760 Parser handles block-ending import Don't molest the RBRACE. Updated with additional parse tests suggested by @retronym. "What are you lazy?" Yes, I must be lazy. Can't ScalaCheck or Par-Test generate these tests automatically? That seems like a reasonable expectation. --- .../scala/tools/nsc/ast/parser/Parsers.scala | 2 +- test/files/pos/t4760.scala | 34 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/t4760.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 50398824e1..5476afa75e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -3067,7 +3067,7 @@ self => while (!isStatSeqEnd && in.token != CASE) { if (in.token == IMPORT) { stats ++= importClause() - acceptStatSep() + acceptStatSepOpt() } else if (isExprIntro) { stats += statement(InBlock) diff --git a/test/files/pos/t4760.scala b/test/files/pos/t4760.scala new file mode 100644 index 0000000000..767e3847f4 --- /dev/null +++ b/test/files/pos/t4760.scala @@ -0,0 +1,34 @@ + +class Test { + // parses + def f1 = { + import scala._; + } + // b.scala:7: error: ';' expected but '}' found. + // } + // ^ + // one error found + def f2 = { + import scala._ + } + def f2b = { + import scala.collection.mutable.{ Map => MMap } + } + def f(): Unit = { + locally { + import scala.util.Properties.lineSeparator + } + } + + // parses + def f3 = { + import scala._ + 5 + } + locally { (x: Int) => + import scala.util._ + } + 1 match { + case 1 => import scala.concurrent._ + } +} -- cgit v1.2.3 From 3ada7038877928711176da6ebde68528ab5fc39c Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Tue, 3 Sep 2013 17:47:29 -0700 Subject: 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 +++++++++++++++++++++ .../scala/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 35b67a13ee..c016b52241 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 20e5e30b68..d1a19534db 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 9882aad5e5..fc12e31360 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 0000000000..3c8f380150 --- /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 + } + } +} -- cgit v1.2.3 From 989c3f85d95a22c95bc7ce936c4bd57ff0608bcd Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Tue, 3 Sep 2013 18:12:31 -0700 Subject: 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 ++++++++++++++++++++++++ src/reflect/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 c016b52241..38d26c7fb7 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 d1a19534db..a64eb0ba5d 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 cd9f3d23c9..cfa6f927b5 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 } -- cgit v1.2.3 From a78dddd73c0fb26d9c5ef2af3d592ff5f278b237 Mon Sep 17 00:00:00 2001 From: James Iry Date: Fri, 17 May 2013 14:00:57 -0700 Subject: 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. --- src/reflect/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 5ccf81b4b5..6ca8900d7c 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]()) -- cgit v1.2.3