diff options
author | Paul Phillips <paulp@improving.org> | 2012-09-24 20:09:03 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-09-24 20:09:03 -0700 |
commit | db8dea0055739ec39c8765565c5f479ab360acf9 (patch) | |
tree | a4d70326a55b7062536a34e1ec89b7216ec21138 /src/reflect | |
parent | ffaa3cb89edc1407c99c89f36248358c16887fe7 (diff) | |
parent | 759de8623923d17eaca373dcec14899681a52df4 (diff) | |
download | scala-db8dea0055739ec39c8765565c5f479ab360acf9.tar.gz scala-db8dea0055739ec39c8765565c5f479ab360acf9.tar.bz2 scala-db8dea0055739ec39c8765565c5f479ab360acf9.zip |
Merge pull request #1385 from scalamacros/ticket/6412
SI-6412 some fixes for reflection leaks
Diffstat (limited to 'src/reflect')
-rw-r--r-- | src/reflect/scala/reflect/internal/Importers.scala | 164 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Mirrors.scala | 12 | ||||
-rw-r--r-- | src/reflect/scala/reflect/runtime/JavaMirrors.scala | 13 | ||||
-rw-r--r-- | src/reflect/scala/reflect/runtime/SynchronizedTypes.scala | 24 |
4 files changed, 121 insertions, 92 deletions
diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala index c116928d37..02209d0712 100644 --- a/src/reflect/scala/reflect/internal/Importers.scala +++ b/src/reflect/scala/reflect/internal/Importers.scala @@ -1,6 +1,8 @@ package scala.reflect package internal + import scala.collection.mutable.WeakHashMap +import scala.ref.WeakReference // SI-6241: move importers to a mirror trait Importers extends api.Importers { self: SymbolTable => @@ -26,8 +28,20 @@ trait Importers extends api.Importers { self: SymbolTable => val from: SymbolTable - lazy val symMap: WeakHashMap[from.Symbol, Symbol] = new WeakHashMap - lazy val tpeMap: WeakHashMap[from.Type, Type] = new WeakHashMap + protected lazy val symMap = new Cache[from.Symbol, Symbol]() + protected lazy val tpeMap = new Cache[from.Type, Type]() + protected class Cache[K <: AnyRef, V <: AnyRef] extends WeakHashMap[K, WeakReference[V]] { + def weakGet(key: K): Option[V] = this get key flatMap WeakReference.unapply + def weakUpdate(key: K, value: V) = this.update(key, WeakReference(value)) + def weakGetOrElseUpdate(key: K)(value: => V): V = + weakGet(key) match { + case Some(result) => result + case None => + val result = value + this(key) = WeakReference(result) + result + } + } // fixups and maps prevent stackoverflows in importer var pendingSyms = 0 @@ -44,8 +58,10 @@ trait Importers extends api.Importers { self: SymbolTable => object reverse extends from.StandardImporter { val from: self.type = self - for ((fromsym, mysym) <- StandardImporter.this.symMap) symMap += ((mysym, fromsym)) - for ((fromtpe, mytpe) <- StandardImporter.this.tpeMap) tpeMap += ((mytpe, fromtpe)) + // FIXME this and reverse should be constantly kept in sync + // not just synced once upon the first usage of reverse + for ((fromsym, WeakReference(mysym)) <- StandardImporter.this.symMap) symMap += ((mysym, WeakReference(fromsym))) + for ((fromtpe, WeakReference(mytpe)) <- StandardImporter.this.tpeMap) tpeMap += ((mytpe, WeakReference(fromtpe))) } // todo. careful import of positions @@ -53,70 +69,70 @@ trait Importers extends api.Importers { self: SymbolTable => pos.asInstanceOf[Position] def importSymbol(sym0: from.Symbol): Symbol = { - def doImport(sym: from.Symbol): Symbol = { - if (symMap.contains(sym)) - return symMap(sym) - - val myowner = importSymbol(sym.owner) - val mypos = importPosition(sym.pos) - val myname = importName(sym.name).toTermName - val myflags = sym.flags - def linkReferenced(mysym: TermSymbol, x: from.TermSymbol, op: from.Symbol => Symbol): Symbol = { - symMap(x) = mysym - mysym.referenced = op(x.referenced) - mysym - } - val mysym = sym match { - case x: from.MethodSymbol => - linkReferenced(myowner.newMethod(myname, mypos, myflags), x, importSymbol) - case x: from.ModuleSymbol => - linkReferenced(myowner.newModuleSymbol(myname, mypos, myflags), x, importSymbol) - case x: from.FreeTermSymbol => - newFreeTermSymbol(importName(x.name).toTermName, x.value, x.flags, x.origin) setInfo importType(x.info) - case x: from.FreeTypeSymbol => - newFreeTypeSymbol(importName(x.name).toTypeName, x.flags, x.origin) - case x: from.TermSymbol => - linkReferenced(myowner.newValue(myname, mypos, myflags), x, importSymbol) - case x: from.TypeSkolem => - val origin = x.unpackLocation match { - case null => null - case y: from.Tree => importTree(y) - case y: from.Symbol => importSymbol(y) + def doImport(sym: from.Symbol): Symbol = + symMap weakGet sym match { + case Some(result) => result + case _ => + val myowner = importSymbol(sym.owner) + val mypos = importPosition(sym.pos) + val myname = importName(sym.name).toTermName + val myflags = sym.flags + def linkReferenced(mysym: TermSymbol, x: from.TermSymbol, op: from.Symbol => Symbol): Symbol = { + symMap.weakUpdate(x, mysym) + mysym.referenced = op(x.referenced) + mysym } - myowner.newTypeSkolemSymbol(myname.toTypeName, origin, mypos, myflags) - case x: from.ModuleClassSymbol => - val mysym = myowner.newModuleClass(myname.toTypeName, mypos, myflags) - symMap(x) = mysym - mysym.sourceModule = importSymbol(x.sourceModule) - mysym - case x: from.ClassSymbol => - val mysym = myowner.newClassSymbol(myname.toTypeName, mypos, myflags) - symMap(x) = mysym - if (sym.thisSym != sym) { - mysym.typeOfThis = importType(sym.typeOfThis) - mysym.thisSym setName importName(sym.thisSym.name) + val mysym = sym match { + case x: from.MethodSymbol => + linkReferenced(myowner.newMethod(myname, mypos, myflags), x, importSymbol) + case x: from.ModuleSymbol => + linkReferenced(myowner.newModuleSymbol(myname, mypos, myflags), x, importSymbol) + case x: from.FreeTermSymbol => + newFreeTermSymbol(importName(x.name).toTermName, x.value, x.flags, x.origin) setInfo importType(x.info) + case x: from.FreeTypeSymbol => + newFreeTypeSymbol(importName(x.name).toTypeName, x.flags, x.origin) + case x: from.TermSymbol => + linkReferenced(myowner.newValue(myname, mypos, myflags), x, importSymbol) + case x: from.TypeSkolem => + val origin = x.unpackLocation match { + case null => null + case y: from.Tree => importTree(y) + case y: from.Symbol => importSymbol(y) + } + myowner.newTypeSkolemSymbol(myname.toTypeName, origin, mypos, myflags) + case x: from.ModuleClassSymbol => + val mysym = myowner.newModuleClass(myname.toTypeName, mypos, myflags) + symMap.weakUpdate(x, mysym) + mysym.sourceModule = importSymbol(x.sourceModule) + mysym + case x: from.ClassSymbol => + val mysym = myowner.newClassSymbol(myname.toTypeName, mypos, myflags) + symMap.weakUpdate(x, mysym) + if (sym.thisSym != sym) { + mysym.typeOfThis = importType(sym.typeOfThis) + mysym.thisSym setName importName(sym.thisSym.name) + } + mysym + case x: from.TypeSymbol => + myowner.newTypeSymbol(myname.toTypeName, mypos, myflags) } - mysym - case x: from.TypeSymbol => - myowner.newTypeSymbol(myname.toTypeName, mypos, myflags) - } - symMap(sym) = mysym - mysym setFlag Flags.LOCKED - mysym setInfo { - val mytypeParams = sym.typeParams map importSymbol - new LazyPolyType(mytypeParams) { - override def complete(s: Symbol) { - val result = sym.info match { - case from.PolyType(_, res) => res - case result => result + symMap.weakUpdate(sym, mysym) + mysym setFlag Flags.LOCKED + mysym setInfo { + val mytypeParams = sym.typeParams map importSymbol + new LazyPolyType(mytypeParams) { + override def complete(s: Symbol) { + val result = sym.info match { + case from.PolyType(_, res) => res + case result => result + } + s setInfo GenPolyType(mytypeParams, importType(result)) + s setAnnotations (sym.annotations map importAnnotationInfo) + } } - s setInfo GenPolyType(mytypeParams, importType(result)) - s setAnnotations (sym.annotations map importAnnotationInfo) } - } - } - mysym resetFlag Flags.LOCKED - } // end doImport + mysym resetFlag Flags.LOCKED + } // end doImport def importOrRelink: Symbol = { val sym = sym0 // makes sym visible in the debugger @@ -186,14 +202,10 @@ trait Importers extends api.Importers { self: SymbolTable => } // end importOrRelink val sym = sym0 - if (symMap contains sym) { - symMap(sym) - } else { + symMap.weakGetOrElseUpdate(sym) { pendingSyms += 1 - - try { - symMap getOrElseUpdate (sym, importOrRelink) - } finally { + try importOrRelink + finally { pendingSyms -= 1 tryFixup() } @@ -258,14 +270,10 @@ trait Importers extends api.Importers { self: SymbolTable => def importOrRelink: Type = doImport(tpe) - if (tpeMap contains tpe) { - tpeMap(tpe) - } else { + tpeMap.weakGetOrElseUpdate(tpe) { pendingTpes += 1 - - try { - tpeMap getOrElseUpdate (tpe, importOrRelink) - } finally { + try importOrRelink + finally { pendingTpes -= 1 tryFixup() } diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala index bde7f7ac51..4836db5db0 100644 --- a/src/reflect/scala/reflect/internal/Mirrors.scala +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -10,10 +10,15 @@ package internal import Flags._ trait Mirrors extends api.Mirrors { - self: SymbolTable => + thisUniverse: SymbolTable => override type Mirror >: Null <: RootsBase + // root symbols hold a strong reference to the enclosing mirror + // this prevents the mirror from being collected + // if there are any symbols created by that mirror + trait RootSymbol extends Symbol { def mirror: Mirror } + abstract class RootsBase(rootOwner: Symbol) extends MirrorOf[Mirrors.this.type] { thisMirror => protected[scala] def rootLoader: LazyType @@ -70,7 +75,7 @@ trait Mirrors extends api.Mirrors { protected def mirrorMissingHook(owner: Symbol, name: Name): Symbol = NoSymbol - protected def universeMissingHook(owner: Symbol, name: Name): Symbol = self.missingHook(owner, name) + protected def universeMissingHook(owner: Symbol, name: Name): Symbol = thisUniverse.missingHook(owner, name) private[scala] def missingHook(owner: Symbol, name: Name): Symbol = mirrorMissingHook(owner, name) orElse universeMissingHook(owner, name) @@ -251,10 +256,11 @@ trait Mirrors extends api.Mirrors { } // Features common to RootClass and RootPackage, the roots of all // type and term symbols respectively. - sealed trait RootSymbol extends WellKnownSymbol { + sealed trait RootSymbol extends WellKnownSymbol with thisUniverse.RootSymbol { final override def isRootSymbol = true override def owner = rootOwner override def typeOfThis = thisSym.tpe + def mirror = thisMirror.asInstanceOf[Mirror] } // This is the package _root_. The actual root cannot be referenced at diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 0d9e90d3a6..2f12ba59a2 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -1236,16 +1236,9 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { thisUnive override def scopeTransform(owner: Symbol)(op: => Scope): Scope = if (owner.isPackageClass) owner.info.decls else op - private lazy val rootToLoader = new WeakHashMap[Symbol, ClassLoader] - - override def mirrorThatLoaded(sym: Symbol): Mirror = { - val root = sym.enclosingRootClass - def findLoader = { - val loaders = (mirrors collect { case (cl, ref) if ref.get.get.RootClass == root => cl }) - assert(loaders.nonEmpty, sym) - loaders.head - } - mirrors(rootToLoader getOrElseUpdate(root, findLoader)).get.get + override def mirrorThatLoaded(sym: Symbol): Mirror = sym.enclosingRootClass match { + case root: RootSymbol => root.mirror + case _ => abort(s"${sym}.enclosingRootClass = ${sym.enclosingRootClass}, which is not a RootSymbol") } private lazy val syntheticCoreClasses: Map[(String, Name), Symbol] = { diff --git a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala index de029ca658..b9b140a2fd 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala @@ -1,6 +1,9 @@ package scala.reflect package runtime +import scala.collection.mutable.WeakHashMap +import java.lang.ref.WeakReference + /** This trait overrides methods in reflect.internal, bracketing * them in synchronized { ... } to make them thread-safe */ @@ -11,7 +14,26 @@ trait SynchronizedTypes extends internal.Types { self: SymbolTable => private object uniqueLock - override def unique[T <: Type](tp: T): T = uniqueLock.synchronized { super.unique(tp) } + private val uniques = WeakHashMap[Type, WeakReference[Type]]() + override def unique[T <: Type](tp: T): T = uniqueLock.synchronized { + // we need to have weak uniques for runtime reflection + // because unlike the normal compiler universe, reflective universe isn't organized in runs + // therefore perRunCaches can grow infinitely large + // + // despite that toolbox universes are decorated, toolboxes are compilers, + // i.e. they have their caches cleaned up automatically on per-run basis, + // therefore they should use vanilla uniques, which are faster + if (!isCompilerUniverse) { + val result = if (uniques contains tp) uniques(tp).get else null + if (result ne null) result.asInstanceOf[T] + else { + uniques(tp) = new WeakReference(tp) + tp + } + } else { + super.unique(tp) + } + } class SynchronizedUndoLog extends UndoLog { private val actualLock = new java.util.concurrent.locks.ReentrantLock |