summaryrefslogtreecommitdiff
path: root/src/compiler/scala
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-11-11 05:21:37 +0000
committerPaul Phillips <paulp@improving.org>2010-11-11 05:21:37 +0000
commit19064bad63aff0d3386bf7d5fb154b14c345c418 (patch)
treeea794f756a9df3b2928d48bebafb650912ae60cb /src/compiler/scala
parentb95246f152347fad32630ad7dc251750b3c4ae47 (diff)
downloadscala-19064bad63aff0d3386bf7d5fb154b14c345c418.tar.gz
scala-19064bad63aff0d3386bf7d5fb154b14c345c418.tar.bz2
scala-19064bad63aff0d3386bf7d5fb154b14c345c418.zip
Some more work on options related to showing co...
Some more work on options related to showing compiler structures during compilation. The pickler knew more than was healthy about things like compiler settings, so I let the pickler worry about pickling and moved that logic somewhere closer to the surface. Some convenience oriented tweaks to command line phase parsing. The end result is as follows (some output trimmed for brevity.) // dippy.scala class Dippy { def f[T <: Dippy](x: T) = (x, x) object DingusDippy extends util.Random { } } // className@phaseString should be reliably unambiguous % scalac -Xshow-class Dippy@typer,erasure,jvm dippy.scala <<-- class Dippy after phase 'typer' -->> Members (excluding Any/AnyRef unless overridden): final object DingusDippy extends object Dippy.this.DingusDippy def f[T <: Dippy](x: T): (T, T) <<-- class Dippy after phase 'erasure' -->> Members (excluding Any/AnyRef unless overridden): private lazy var DingusDippy$module: object Dippy#DingusDippy lazy val DingusDippy(): object Dippy#DingusDippy def f(x: Dippy): Tuple2 <<-- class Dippy after phase 'jvm' -->> Members (excluding Any/AnyRef unless overridden): protected var bitmap$0: Int private lazy var DingusDippy$module: object Dippy$DingusDippy lazy val DingusDippy(): object Dippy$DingusDippy def f(x: Dippy): Tuple2 No review.
Diffstat (limited to 'src/compiler/scala')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala131
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/StdNames.scala36
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolTable.scala2
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala78
-rw-r--r--src/compiler/scala/tools/nsc/util/ShowPickled.scala7
6 files changed, 144 insertions, 111 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index f3d6b9f41d..c2136edbb6 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -12,8 +12,8 @@ import compat.Platform.currentTime
import scala.collection.{ mutable, immutable }
import io.{ SourceReader, AbstractFile, Path }
import reporters.{ Reporter, ConsoleReporter }
-import util.{ ClassPath, SourceFile, Statistics, BatchSourceFile, ScriptSourceFile, returning }
-import reflect.generic.{ PickleBuffer }
+import util.{ ClassPath, SourceFile, Statistics, BatchSourceFile, ScriptSourceFile, ShowPickled, returning }
+import reflect.generic.{ PickleBuffer, PickleFormat }
import symtab.{ Flags, SymbolTable, SymbolLoaders }
import symtab.classfile.Pickler
@@ -202,8 +202,20 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
def optSetting[T](s: Settings#Setting): Option[T] =
if (s.isDefault) None else Some(s.value.asInstanceOf[T])
- def showClass = optSetting[String](settings.Xshowcls)
- def showObject = optSetting[String](settings.Xshowobj)
+ // Allows for syntax like scalac -Xshow-class Random@erasure,typer
+ private def splitClassAndPhase(str: String, term: Boolean): Name = {
+ def mkName(s: String) = if (term) newTermName(s) else newTypeName(s)
+ (str indexOf '@') match {
+ case -1 => mkName(str)
+ case idx =>
+ val phasePart = str drop (idx + 1)
+ settings.Yshow.tryToSetColon(phasePart split ',' toList)
+ mkName(str take idx)
+ }
+ }
+
+ val showClass = optSetting[String](settings.Xshowcls) map (x => splitClassAndPhase(x, false))
+ val showObject = optSetting[String](settings.Xshowobj) map (x => splitClassAndPhase(x, true))
def script = optSetting[String](settings.script)
def encoding = optSetting[String](settings.encoding)
def sourceReader = optSetting[String](settings.sourceReader)
@@ -220,15 +232,17 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
def unchecked = settings.unchecked.value
def verbose = settings.verbose.value
def writeICode = settings.writeICode.value
+ def declsOnly = false
/** Flags as applied to the current or previous phase */
def browsePhase = isActive(settings.browse)
+ def checkPhase = wasActive(settings.check)
def logPhase = isActive(settings.log)
def printPhase = isActive(settings.Xprint)
-
- def checkPhase = wasActive(settings.check)
+ def showPhase = isActive(settings.Yshow)
/** Derived values */
+ def showNames = List(showClass, showObject).flatten
def jvm = target startsWith "jvm"
def msil = target == "msil"
def verboseDebug = debug && verbose
@@ -755,6 +769,15 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
/** A map from compiled top-level symbols to their picklers */
val symData = new mutable.HashMap[Symbol, PickleBuffer]
+ def registerPickle(sym: Symbol): Unit = {
+ // Convert all names to the type name: objects don't store pickled data
+ if (opt.showPhase && (opt.showNames exists (x => findNamedMember(x.toTypeName, sym) != NoSymbol))) {
+ symData get sym foreach { pickle =>
+ ShowPickled.show("\n<<-- " + sym.fullName + " -->>\n", pickle, false)
+ }
+ }
+ }
+
/** does this run compile given class, module, or case factory? */
def compiles(sym: Symbol): Boolean =
if (sym == NoSymbol) false
@@ -785,6 +808,9 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
}
}
+ private def showMembers() =
+ opt.showNames foreach (x => showDef(x, opt.declsOnly, globalPhase))
+
/** Compile list of source files */
def compileSources(_sources: List[SourceFile]) {
val depSources = dependencyAnalysis.calculateFiles(_sources.distinct) // bug #1268, scalac confused by duplicated filenames
@@ -807,9 +833,13 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
writeICode()
// print trees
- if (opt.printPhase || opt.printLate && runIsAt(cleanupPhase))
+ if (opt.printPhase || opt.printLate && runIsAt(cleanupPhase)) {
if (opt.showTrees) nodePrinters.printAll()
else printAllUnits()
+ }
+ // print members
+ if (opt.showPhase)
+ showMembers()
// browse trees with swing tree viewer
if (opt.browsePhase)
@@ -830,9 +860,9 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
advancePhase
}
- // print class and object definitions
- opt.showClass foreach (x => showDef(newTypeName(x)))
- opt.showObject foreach (x => showDef(newTermName(x)))
+ // If no phase was specified for -Xshow-class/object, show it now.
+ if (settings.Yshow.isDefault)
+ showMembers()
if (reporter.hasErrors) {
for ((sym, file) <- symSource.iterator) {
@@ -973,55 +1003,50 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
atPhase(phase.next) { currentRun.units foreach treePrinter.print }
}
+ private def findMemberFromRoot(fullName: Name): Symbol = {
+ val segs = nme.segments(fullName.toString, fullName.isTermName)
+ if (segs.isEmpty) NoSymbol
+ else findNamedMember(segs.tail, definitions.RootClass.info member segs.head)
+ }
+
+ private def findNamedMember(fullName: Name, root: Symbol): Symbol = {
+ val segs = nme.segments(fullName.toString, fullName.isTermName)
+ if (segs.isEmpty || segs.head != root.simpleName) NoSymbol
+ else findNamedMember(segs.tail, root)
+ }
+ private def findNamedMember(segs: List[Name], root: Symbol): Symbol =
+ if (segs.isEmpty) root
+ else findNamedMember(segs.tail, root.info member segs.head)
+
/** We resolve the class/object ambiguity by passing a type/term name.
*/
- def showDef(fullName: Name, ph: Phase = currentRun.typerPhase.next) = {
- def phased[T](body: => T): T = atPhase(ph)(body)
-
- def walker(sym: Symbol, n: Name) = sym.info member n
- def walk(root: Symbol, segs: List[Name]) = phased(segs.foldLeft(root)(walker))
- def defs(sym: Symbol) = phased(sym.info.members map (x => if (x.isTerm) x.defString else x.toString))
- def bases(sym: Symbol) = phased(sym.info.baseClasses map (_.fullName))
- def mkName(str: String, term: Boolean) =
- if (term) newTermName(str)
- else newTypeName(str)
-
- def nameSegments(name: String): List[Name] = {
- name.indexWhere(ch => ch == '.' || ch == '#') match {
- // it's the last segment: the argument to showDef tells us whether type or term
- case -1 => if (name == "") Nil else List(mkName(name, fullName.isTermName))
- // otherwise, we can tell based on whether '#' or '.' is the following char.
- case idx =>
- val (id, div, rest) = (name take idx, name charAt idx, name drop (idx + 1))
- mkName(id, div == '.') :: nameSegments(rest)
- }
- }
-
- val syms = {
- // creates a list of simple type and term names.
- val segments = nameSegments(fullName.toString)
-
- // make the type/term selections walking from the root.
- walk(definitions.RootClass, segments) match {
- // The name as given was not found, so we'll sift through every symbol in
- // the run looking for plausible matches.
- case NoSymbol => phased {
- currentRun.symSource.keys.toList .
- filter (_.simpleName == segments.head) .
- map (sym => walk(sym, segments.tail)) .
- filterNot (_ == NoSymbol)
- }
- // The name as given matched, so show only that.
- case sym => List(sym)
- }
+ def showDef(fullName: Name, declsOnly: Boolean, ph: Phase) = {
+ val boringOwners = Set(definitions.AnyClass, definitions.AnyRefClass, definitions.ObjectClass)
+ def phased[T](body: => T): T = afterPhase(ph)(body)
+ def boringMember(sym: Symbol) = boringOwners(sym.owner)
+ def symString(sym: Symbol) = if (sym.isTerm) sym.defString else sym.toString
+
+ def members(sym: Symbol) = phased(sym.info.members filterNot boringMember map symString)
+ def decls(sym: Symbol) = phased(sym.info.decls.toList map symString)
+ def bases(sym: Symbol) = phased(sym.info.baseClasses map (x => x.kindString + " " + x.fullName))
+
+ // make the type/term selections walking from the root.
+ val syms = findMemberFromRoot(fullName) match {
+ // The name as given was not found, so we'll sift through every symbol in
+ // the run looking for plausible matches.
+ case NoSymbol => phased(currentRun.symSource.keys map (sym => findNamedMember(fullName, sym)) filterNot (_ == NoSymbol) toList)
+ // The name as given matched, so show only that.
+ case sym => List(sym)
}
syms foreach { sym =>
- val name = phased("\n<<-- " + sym.kindString + " " + sym.fullName + " -->>\n")
- val baseClasses = bases(sym).mkString("Base classes:\n ", "\n ", "\n")
- val members = defs(sym).mkString("Members after phase typer:\n ", "\n ", "\n")
+ val name = "\n<<-- %s %s after phase '%s' -->>".format(sym.kindString, sym.fullName, ph.name)
+ val baseClasses = bases(sym).mkString("Base classes:\n ", "\n ", "")
+ val contents =
+ if (declsOnly) decls(sym).mkString("Declarations:\n ", "\n ", "")
+ else members(sym).mkString("Members (excluding Any/AnyRef unless overridden):\n ", "\n ", "")
- inform(List(name, baseClasses, members) mkString "\n")
+ inform(List(name, baseClasses, contents) mkString "\n\n")
}
}
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index be6d886537..98e2f4aacf 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -106,6 +106,7 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings {
val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options")
val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after")
val check = PhasesSetting ("-Ycheck", "Check the tree at the end of")
+ val Yshow = PhasesSetting ("-Yshow", "Specifies show phases in conjunction with -Xshow-class or -Xshow-object")
val Xcloselim = BooleanSetting ("-Yclosure-elim", "Perform closure elimination")
val Ycompacttrees = BooleanSetting ("-Ycompact-trees", "Use compact tree printer when displaying trees")
val noCompletion = BooleanSetting ("-Yno-completion", "Disable tab-completion in the REPL")
diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
index 98582cff03..1e348f5d68 100644
--- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala
+++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
@@ -12,6 +12,42 @@ trait StdNames extends reflect.generic.StdNames with NameManglers {
self: SymbolTable =>
object nme extends StandardNames with NameMangling {
+ /** Translate a String into a list of simple TypeNames and TermNames.
+ * In all segments before the last, type/term is determined by whether
+ * the following separator char is '.' or '#'. In the last segment,
+ * the argument "assumeTerm" determines it. Examples:
+ *
+ * package foo {
+ * object Lorax { object Wog ; class Wog }
+ * class Lorax { object Zax ; class Zax }
+ * }
+ *
+ * f("foo.Lorax", true) == List("foo": Term, "Lorax": Term) // object Lorax
+ * f("foo.Lorax", false) == List("foo": Term, "Lorax": Type) // class Lorax
+ * f("Lorax.Wog", true) == List("Lorax": Term, "Wog": Term) // object Wog
+ * f("Lorax.Wog", false) == List("Lorax": Term, "Wog": Type) // class Wog
+ * f("Lorax#Zax", true) == List("Lorax": Type, "Zax": Term) // object Zax
+ * f("Lorax#Zax", false) == List("Lorax": Type, "Zax": Type) // class Zax
+ *
+ * Note that in actual scala syntax you cannot refer to object Zax without an
+ * instance of Lorax, so Lorax#Zax could only mean the type. One might think
+ * that Lorax#Zax.type would work, but this is not accepted by the parser.
+ * For the purposes of referencing that object, the syntax is allowed.
+ */
+ def segments(name: String, assumeTerm: Boolean): List[Name] = {
+ def mkName(str: String, term: Boolean): Name =
+ if (term) newTermName(str) else newTypeName(str)
+
+ name.indexWhere(ch => ch == '.' || ch == '#') match {
+ // it's the last segment: the parameter tells us whether type or term
+ case -1 => if (name == "") scala.Nil else scala.List(mkName(name, assumeTerm))
+ // otherwise, we can tell based on whether '#' or '.' is the following char.
+ case idx =>
+ val (simple, div, rest) = (name take idx, name charAt idx, name drop (idx + 1))
+ mkName(simple, div == '.') :: segments(rest, assumeTerm)
+ }
+ }
+
// Scala keywords
val ABSTRACTkw = newTermName("abstract")
val CASEkw = newTermName("case")
diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala
index b410572f7b..281a3174f9 100644
--- a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala
+++ b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala
@@ -106,6 +106,8 @@ abstract class SymbolTable extends reflect.generic.Universe
phase = current
}
}
+ final def afterPhase[T](ph: Phase)(op: => T): T =
+ atPhase(ph.next)(op)
/** Break into repl debugger if assertion is true */
// def breakIf(assertion: => Boolean, args: Any*): Unit =
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
index 2acfbaf098..7db6e72a6c 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
@@ -9,15 +9,15 @@ package classfile
import java.lang.Float.floatToIntBits
import java.lang.Double.doubleToLongBits
-import util.{ ShowPickled }
import reflect.generic.{ PickleBuffer, PickleFormat }
+import scala.collection.mutable.LinkedHashMap
import PickleFormat._
import Flags._
/**
* Serialize a top-level module and/or class.
*
- * @see <code>EntryTags.scala</code> for symbol table attribute format.
+ * @see EntryTags.scala for symbol table attribute format.
*
* @author Martin Odersky
* @version 1.0
@@ -34,11 +34,10 @@ abstract class Pickler extends SubComponent {
class PicklePhase(prev: Phase) extends StdPhase(prev) {
def apply(unit: CompilationUnit) {
def pickle(tree: Tree) {
-
def add(sym: Symbol, pickle: Pickle) = {
if (currentRun.compiles(sym) && !currentRun.symData.contains(sym)) {
if (settings.debug.value) log("pickling " + sym)
- pickle.putSymbol(sym)
+ pickle putSymbol sym
currentRun.symData(sym) = pickle
}
}
@@ -48,10 +47,11 @@ abstract class Pickler extends SubComponent {
stats foreach pickle
case ClassDef(_, _, _, _) | ModuleDef(_, _, _) =>
val sym = tree.symbol
- val pickle = new Pickle(sym, sym.name.toTermName, sym.owner)
+ val pickle = new Pickle(sym)
add(sym, pickle)
add(sym.companionSymbol, pickle)
- pickle.finish
+ pickle.writeArray()
+ currentRun registerPickle sym
case _ =>
}
}
@@ -59,17 +59,12 @@ abstract class Pickler extends SubComponent {
}
}
- private class Pickle(root: Symbol, rootName: Name, rootOwner: Symbol)
- extends PickleBuffer(new Array[Byte](4096), -1, 0) {
- import scala.collection.mutable.LinkedHashMap
- private var entries = new Array[AnyRef](256)
- private var ep = 0
- private val index = new LinkedHashMap[AnyRef, Int]
-
- // collect higher-order type params
- //private var locals: Set[Symbol] = Set()
-
-// private var boundSyms: List[Symbol] = Nil
+ private class Pickle(root: Symbol) extends PickleBuffer(new Array[Byte](4096), -1, 0) {
+ private val rootName = root.name.toTermName
+ private val rootOwner = root.owner
+ private var entries = new Array[AnyRef](256)
+ private var ep = 0
+ private val index = new LinkedHashMap[AnyRef, Int]
private def isRootSym(sym: Symbol) =
sym.name.toTermName == rootName && sym.owner == rootOwner
@@ -100,11 +95,10 @@ abstract class Pickler extends SubComponent {
// Phase 1 methods: Populate entries/index ------------------------------------
- /** Store entry <code>e</code> in index at next available position unless
+ /** Store entry e in index at next available position unless
* it is already there.
*
- * @param entry ...
- * @return <code>true</code> iff entry is new.
+ * @return true iff entry is new.
*/
private def putEntry(entry: AnyRef): Boolean = index.get(entry) match {
case Some(_) => false
@@ -120,8 +114,7 @@ abstract class Pickler extends SubComponent {
true
}
- /** Store symbol in <code>index</code>. If symbol is local, also store
- * everything it refers to.
+ /** Store symbol in index. If symbol is local, also store everything it references.
*
* @param sym ...
*/
@@ -158,9 +151,7 @@ abstract class Pickler extends SubComponent {
private def putSymbols(syms: List[Symbol]) =
syms foreach putSymbol
- /** Store type and everything it refers to in map <code>index</code>.
- *
- * @param tp ...
+ /** Store type and everything it refers to in map index.
*/
private def putType(tp: Type): Unit = if (putEntry(tp)) {
tp match {
@@ -416,8 +407,7 @@ abstract class Pickler extends SubComponent {
putEntry(privateWithin)
}
- /** Store a constant in map <code>index</code> along with
- * anything it references.
+ /** Store a constant in map index, along with anything it references.
*/
private def putConstant(c: Constant) {
if (putEntry(c)) {
@@ -479,10 +469,7 @@ abstract class Pickler extends SubComponent {
// Phase 2 methods: Write all entries to byte array ------------------------------
- /** Write a reference to object, i.e., the object's number in the map
- * <code>index</code>.
- *
- * @param ref ...
+ /** Write a reference to object, i.e., the object's number in the map index.
*/
private def writeRef(ref: AnyRef) { writeNat(index(ref)) }
private def writeRefs(refs: List[AnyRef]) { refs foreach writeRef }
@@ -979,7 +966,8 @@ abstract class Pickler extends SubComponent {
}
/** Print entry for diagnostics */
- private def printEntry(entry: AnyRef) {
+ def printEntryAtIndex(idx: Int) = printEntry(entries(idx))
+ def printEntry(entry: AnyRef) {
def printRef(ref: AnyRef) {
print(index(ref)+
(if (ref.isInstanceOf[Name]) "("+ref+") " else " "))
@@ -1088,35 +1076,13 @@ abstract class Pickler extends SubComponent {
}
/** Write byte array */
- def finish {
+ def writeArray() {
assert(writeIndex == 0)
writeNat(MajorVersion)
writeNat(MinorVersion)
writeNat(ep)
- def matchesRoot(name: String) = (
- rootName.toString == (
- if (name contains '.') name.split('.').last
- else name
- )
- )
- val showClass = opt.showClass exists matchesRoot
- def versionString = "V" + MajorVersion + "." + MinorVersion
-
- if (showSig)
- println("Pickled info for " + rootName + " in " + rootOwner.fullName + " " + versionString)
-
- for (i <- 0 until ep) {
- if (showSig) {
- print((i formatted "%3d: ")+(writeIndex formatted "%5d: "))
- printEntry(entries(i))
- }
- writeEntry(entries(i))
- }
- if (showClass) {
- readIndex = 0
- ShowPickled.printFile(this, Console.out)
- }
+ entries take ep foreach writeEntry
}
override def toString = "" + rootName + " in " + rootOwner
diff --git a/src/compiler/scala/tools/nsc/util/ShowPickled.scala b/src/compiler/scala/tools/nsc/util/ShowPickled.scala
index 458116845d..58abd721c5 100644
--- a/src/compiler/scala/tools/nsc/util/ShowPickled.scala
+++ b/src/compiler/scala/tools/nsc/util/ShowPickled.scala
@@ -284,8 +284,11 @@ object ShowPickled extends Names {
catch { case _: Exception => None }
def show(what: String, pickle: PickleBuffer, bare: Boolean) = {
- Console.println(what + ": ")
+ Console.println(what)
+ val saved = pickle.readIndex
+ pickle.readIndex = 0
printFile(pickle, Console.out, bare)
+ pickle.readIndex = saved
}
private lazy val ShowPickledSpec =
@@ -304,7 +307,7 @@ object ShowPickled extends Names {
residualArgs foreach { arg =>
(fromFile(arg) orElse fromName(arg)) match {
- case Some(pb) => show(arg, pb, parsed isSet "--bare")
+ case Some(pb) => show(arg + ":", pb, parsed isSet "--bare")
case _ => Console.println("Cannot read " + arg)
}
}