summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/ScriptRunner.scala10
-rw-r--r--src/compiler/scala/tools/nsc/util/Origins.scala105
-rw-r--r--src/compiler/scala/tools/nsc/util/package.scala11
-rw-r--r--src/library/scala/util/DynamicVariable.scala65
-rw-r--r--test/files/run/origins.check6
-rw-r--r--test/files/run/origins.scala21
6 files changed, 169 insertions, 49 deletions
diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala
index 6f5f2936a8..c432e1c9f0 100644
--- a/src/compiler/scala/tools/nsc/ScriptRunner.scala
+++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala
@@ -16,7 +16,7 @@ import io.{ Directory, File, Path, PlainFile }
import java.net.URL
import java.util.jar.{ JarEntry, JarOutputStream }
-import util.waitingForThreads
+import util.{ waitingForThreads, addShutdownHook }
import scala.tools.util.PathResolver
import scala.tools.nsc.reporters.{Reporter,ConsoleReporter}
@@ -56,14 +56,6 @@ object ScriptRunner {
/** Default name to use for the wrapped script */
val defaultScriptMain = "Main"
- /** Must be a daemon thread else scripts won't shut down: ticket #3678 */
- private def addShutdownHook(body: => Unit) =
- Runtime.getRuntime addShutdownHook {
- val t = new Thread { override def run { body } }
- t setDaemon true
- t
- }
-
/** Pick a main object name from the specified settings */
def scriptMain(settings: Settings) = settings.script.value match {
case "" => defaultScriptMain
diff --git a/src/compiler/scala/tools/nsc/util/Origins.scala b/src/compiler/scala/tools/nsc/util/Origins.scala
new file mode 100644
index 0000000000..a95558272d
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/util/Origins.scala
@@ -0,0 +1,105 @@
+/* NSC -- new scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package util
+
+/** A debugging class for logging from whence a method is being called.
+ * Say you wanted to discover who was calling phase_= in SymbolTable.
+ * You could do this:
+ *
+ * {{{
+ * private lazy val origins = Origins[SymbolTable]("phase_=")
+ * // Commented out original enclosed for contrast
+ * // final def phase_=(p: Phase): Unit = {
+ * final def phase_=(p: Phase): Unit = origins {
+ * }}}
+ *
+ * And that's it. When the JVM exits it would issue a report something like this:
+ {{{
+ >> Origins scala.tools.nsc.symtab.SymbolTable.phase_= logged 145585 calls from 51 distinguished sources.
+
+ 71114 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:862)
+ 16584 scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:757)
+ 15411 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:869)
+ 11507 scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:770)
+ 10285 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:864)
+ 6860 scala.tools.nsc.transform.SpecializeTypes.specializedTypeVars(SpecializeTypes.scala:304)
+ ...
+ }}}
+ *
+ */
+
+import scala.collection.{ mutable, immutable }
+import Origins._
+
+abstract class Origins {
+ type Rep
+ def newRep(xs: StackSlice): Rep
+ def repString(rep: Rep): String
+ def originClass: String
+
+ private var _tag: String = null
+ def tag: String = _tag
+ def setTag(tag: String): this.type = {
+ _tag = tag
+ this
+ }
+
+ private val origins = new mutable.HashMap[Rep, Int] withDefaultValue 0
+ private def add(xs: Rep) = origins(xs) += 1
+ private def total = origins.values.foldLeft(0L)(_ + _)
+
+ // We find the right line by dropping any from around here and any
+ // from the method's origin class.
+ private def dropStackElement(cn: String) =
+ (cn startsWith OriginsName) || (cn startsWith originClass)
+
+ // Create a stack and whittle it down to the interesting part.
+ private def readStack(): Array[StackTraceElement] =
+ (new Throwable).getStackTrace dropWhile (el => dropStackElement(el.getClassName))
+
+ def apply[T](body: => T): T = {
+ add(newRep(readStack()))
+ body
+ }
+ def clear() = origins.clear()
+ def show() = {
+ println("\n>> Origins %s.%s logged %s calls from %s distinguished sources.\n".format(originClass, tag, total, origins.keys.size))
+ origins.toList sortBy (-_._2) foreach {
+ case (k, v) => println("%7s %s".format(v, repString(k)))
+ }
+ }
+ def purge() = {
+ show()
+ clear()
+ }
+}
+
+object Origins {
+ private type StackSlice = Array[StackTraceElement]
+ private val OriginsName = classOf[Origins].getName
+ private val counters = new mutable.HashSet[Origins]
+
+ {
+ // Console.println("\nOrigins loaded: registering shutdown hook to display results.")
+ addShutdownHook(counters foreach (_.purge()))
+ }
+
+ def apply[T: Manifest](tag: String): Origins = apply(tag, manifest[T].erasure)
+ def apply(tag: String, clazz: Class[_]): Origins = apply(tag, new OneLine(clazz))
+ def apply(tag: String, orElse: => Origins): Origins = {
+ counters find (_.tag == tag) getOrElse {
+ returning(orElse setTag tag)(counters += _)
+ }
+ }
+
+ class OneLine(clazz: Class[_]) extends Origins {
+ type Rep = StackTraceElement
+ val originClass = clazz.getName stripSuffix "$"
+ def newRep(xs: StackSlice): Rep = xs(0)
+ def repString(rep: Rep) = " " + rep
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/util/package.scala b/src/compiler/scala/tools/nsc/util/package.scala
index c38b2c5031..427e385aca 100644
--- a/src/compiler/scala/tools/nsc/util/package.scala
+++ b/src/compiler/scala/tools/nsc/util/package.scala
@@ -11,6 +11,17 @@ package object util {
/** Apply a function and return the passed value */
def returning[T](x: T)(f: T => Unit): T = { f(x) ; x }
+ /** Register a shutdown hook to be run when the jvm exits.
+ * Marks it as daemon so it doesn't interfere with shutdown,
+ * but the thread is returned so it can be modified.
+ */
+ def addShutdownHook(body: => Unit) = {
+ returning(new Thread { override def run { body } }) { t =>
+ t setDaemon true
+ Runtime.getRuntime addShutdownHook t
+ }
+ }
+
/** All living threads. */
def allThreads(): List[Thread] = {
val num = Thread.activeCount()
diff --git a/src/library/scala/util/DynamicVariable.scala b/src/library/scala/util/DynamicVariable.scala
index 52d1763123..c42c3baaf8 100644
--- a/src/library/scala/util/DynamicVariable.scala
+++ b/src/library/scala/util/DynamicVariable.scala
@@ -6,45 +6,32 @@
** |/ **
\* */
-
-
package scala.util
-
import java.lang.InheritableThreadLocal
-/** <p>
- * DynamicVariables provide a binding mechanism where the current
- * value is found through <em>dynamic scope</em>, but where
- * access to the variable itself is resolved through <em>static
- * scope</em>.
- * </p>
- * <p>
- * The current value can be retrieved with the
- * <code>value</code> method. New values should be
- * pushed using the <code>withValue</code> method.
- * Values pushed via <code>withValue</code> only
- * stay valid while the <code>withValue</code>'s
- * <em>second</em> argument, a parameterless closure,
- * executes. When the second argument finishes,
- * the variable reverts to the previous value.
- * </p>
- * <p>
- * Usage of <code>withValue</code> looks like this:
- * </p>
- * <blockquote><pre>
+/** DynamicVariables provide a binding mechanism where the current
+ * value is found through dynamic scope, but where access to the
+ * variable itself is resolved through static scope.
+ *
+ * The current value can be retrieved with the value method. New values
+ * should be pushed using the withValue method. Values pushed via
+ * withValue only stay valid while the withValue's second argument, a
+ * parameterless closure, executes. When the second argument finishes,
+ * the variable reverts to the previous value.
+ *
+ * {{{
* someDynamicVariable.withValue(newValue) {
* // ... code called in here that calls value ...
* // ... will be given back the newValue ...
* }
- * </pre></blockquote>
- * <p>
- * Each thread gets its own stack of bindings. When a
- * new thread is created, the DynamicVariable gets a copy
- * of the stack of bindings from the parent thread, and
- * from then on the bindings for the new thread
- * are independent of those for the original thread.
- * </p>
+ * }}}
+ *
+ * Each thread gets its own stack of bindings. When a
+ * new thread is created, the DynamicVariable gets a copy
+ * of the stack of bindings from the parent thread, and
+ * from then on the bindings for the new thread
+ * are independent of those for the original thread.
*
* @author Lex Spoon
* @version 1.1, 2007-5-21
@@ -57,26 +44,24 @@ class DynamicVariable[T](init: T) {
/** Retrieve the current value */
def value: T = tl.get.asInstanceOf[T]
-
/** Set the value of the variable while executing the specified
* thunk.
*
* @param newval The value to which to set the variable
* @param thunk The code to evaluate under the new setting
*/
- def withValue[S](newval: T)(thunk: =>S): S = {
+ def withValue[S](newval: T)(thunk: => S): S = {
val oldval = value
- tl.set(newval)
+ tl set newval
- try { thunk } finally {
- tl.set(oldval)
- }
+ try thunk
+ finally tl set oldval
}
/** Change the currently bound value, discarding the old value.
- * Usually <code>withValue()</code> gives better semantics.
+ * Usually withValue() gives better semantics.
*/
- def value_=(newval: T) = { tl.set(newval) }
+ def value_=(newval: T) = tl set newval
- override def toString: String = "DynamicVariable(" + value +")"
+ override def toString: String = "DynamicVariable(" + value + ")"
}
diff --git a/test/files/run/origins.check b/test/files/run/origins.check
new file mode 100644
index 0000000000..c088ea05c2
--- /dev/null
+++ b/test/files/run/origins.check
@@ -0,0 +1,6 @@
+
+>> Origins goxbox.Socks.boop logged 65 calls from 3 distinguished sources.
+
+ 50 Test$$anonfun$f3$1.apply$mcII$sp(origins.scala:16)
+ 10 Test$$anonfun$f2$1.apply$mcII$sp(origins.scala:15)
+ 5 Test$$anonfun$f1$1.apply$mcII$sp(origins.scala:14)
diff --git a/test/files/run/origins.scala b/test/files/run/origins.scala
new file mode 100644
index 0000000000..4c98e7a66c
--- /dev/null
+++ b/test/files/run/origins.scala
@@ -0,0 +1,21 @@
+import scala.tools.nsc.util.Origins
+
+package goxbox {
+ object Socks {
+ val origins = Origins[Socks.type]("boop")
+
+ def boop(x: Int): Int = origins { 5 }
+ }
+}
+
+object Test {
+ import goxbox.Socks.boop
+
+ def f1() = 1 to 5 map boop
+ def f2() = 1 to 10 map boop
+ def f3() = 1 to 50 map boop
+
+ def main(args: Array[String]): Unit = {
+ f1() ; f2() ; f3()
+ }
+}