diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 131 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/StdNames.scala | 36 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/SymbolTable.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala | 78 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/util/ShowPickled.scala | 7 | ||||
-rwxr-xr-x | src/library/scala/reflect/generic/PickleFormat.scala | 1 | ||||
-rw-r--r-- | test/files/run/global-showdef.check | 14 | ||||
-rw-r--r-- | test/files/run/global-showdef.scala | 4 |
9 files changed, 154 insertions, 120 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) } } diff --git a/src/library/scala/reflect/generic/PickleFormat.scala b/src/library/scala/reflect/generic/PickleFormat.scala index d1e884f513..a1f988a22d 100755 --- a/src/library/scala/reflect/generic/PickleFormat.scala +++ b/src/library/scala/reflect/generic/PickleFormat.scala @@ -114,6 +114,7 @@ object PickleFormat { */ val MajorVersion = 5 val MinorVersion = 0 + def VersionString = "V" + MajorVersion + "." + MinorVersion final val TERMname = 1 final val TYPEname = 2 diff --git a/test/files/run/global-showdef.check b/test/files/run/global-showdef.check index 8f67253ced..36d33f6fdf 100644 --- a/test/files/run/global-showdef.check +++ b/test/files/run/global-showdef.check @@ -1,14 +1,14 @@ -<<-- class foo.bar.Bippy -->> +<<-- class foo.bar.Bippy after phase 'typer' -->> def showdefTestMemberClass1: Int -<<-- type foo.bar.Bippy.BippyType -->> +<<-- type foo.bar.Bippy.BippyType after phase 'typer' -->> def showdefTestMemberType1: Unit -<<-- type foo.bar.Bippy.BippyType -->> +<<-- type foo.bar.Bippy.BippyType after phase 'typer' -->> def showdefTestMemberType2: Unit -<<-- class foo.bar.Bippy.Boppity -->> +<<-- class foo.bar.Bippy.Boppity after phase 'typer' -->> def showdefTestMemberClass2: Int -<<-- class foo.bar.Bippy.Boppity.Boo -->> +<<-- class foo.bar.Bippy.Boppity.Boo after phase 'typer' -->> def showdefTestMemberClass3: Int -<<-- object foo.bar.Bippy -->> +<<-- object foo.bar.Bippy after phase 'typer' -->> def showdefTestMemberObject2: java.lang.String -<<-- object foo.bar.Bippy.Boppity.Boo -->> +<<-- object foo.bar.Bippy.Boppity.Boo after phase 'typer' -->> def showdefTestMemberObject1: java.lang.String diff --git a/test/files/run/global-showdef.scala b/test/files/run/global-showdef.scala index 0b34fc4548..e17f438247 100644 --- a/test/files/run/global-showdef.scala +++ b/test/files/run/global-showdef.scala @@ -55,8 +55,8 @@ object Bippy { } output split "\\n" toList } - def showClass(name: String) = lines("-Xshow-class", name) - def showObject(name: String) = lines("-Xshow-object", name) + def showClass(name: String) = lines("-Yshow:typer", "-Xshow-class", name) + def showObject(name: String) = lines("-Yshow:typer", "-Xshow-object", name) def show(xs: List[String]) = { xs filter (x => (x contains "def showdefTestMember") || (x startsWith "<<-- ")) foreach println |