/* NSC -- new Scala compiler * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky */ // todo. we need to unify this prettyprinter with NodePrinters package scala.reflect package internal import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } import Flags._ import scala.compat.Platform.EOL trait Printers extends api.Printers { self: SymbolTable => //nsc import treeInfo.{ IsTrue, IsFalse } final val showOuterTests = false /** Adds backticks if the name is a scala keyword. */ def quotedName(name: Name, decode: Boolean): String = { val s = if (decode) name.decode else name.toString val term = name.toTermName if (nme.keywords(term) && term != nme.USCOREkw) "`%s`" format s else s } def quotedName(name: Name): String = quotedName(name, false) def quotedName(name: String): String = quotedName(newTermName(name), false) private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { val sym = tree.symbol if (sym.name.toString == nme.ERROR.toString) { "<" + quotedName(name, decoded) + ": error>" } else if (sym != null && sym != NoSymbol) { val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" var suffix = "" if (settings.uniqid.value) suffix += ("#" + sym.id) if (settings.Yshowsymkinds.value) suffix += ("#" + sym.abbreviatedKindString) prefix + quotedName(tree.symbol.decodedName) + suffix } else { quotedName(name, decoded) } } def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) /** Turns a path into a String, introducing backquotes * as necessary. */ def backquotedPath(t: Tree): String = { t match { case Select(qual, name) if name.isTermName => "%s.%s".format(backquotedPath(qual), symName(t, name)) case Select(qual, name) if name.isTypeName => "%s#%s".format(backquotedPath(qual), symName(t, name)) case Ident(name) => symName(t, name) case _ => t.toString } } class TreePrinter(out: PrintWriter) extends super.TreePrinter { protected var indentMargin = 0 protected val indentStep = 2 protected var indentString = " " // 40 printTypes = settings.printtypes.value printIds = settings.uniqid.value printKinds = settings.Yshowsymkinds.value printMirrors = false // typically there's no point to print mirrors inside the compiler, as there is only one mirror there protected def doPrintPositions = settings.Xprintpos.value def indent() = indentMargin += indentStep def undent() = indentMargin -= indentStep def printPosition(tree: Tree) = if (doPrintPositions) print(tree.pos.show) def println() { out.println() while (indentMargin > indentString.length()) indentString += indentString if (indentMargin > 0) out.write(indentString, 0, indentMargin) } def printSeq[a](ls: List[a])(printelem: a => Unit)(printsep: => Unit) { ls match { case List() => case List(x) => printelem(x) case x :: rest => printelem(x); printsep; printSeq(rest)(printelem)(printsep) } } def printColumn(ts: List[Tree], start: String, sep: String, end: String) { print(start); indent; println() printSeq(ts){print(_)}{print(sep); println()}; undent; println(); print(end) } def printRow(ts: List[Tree], start: String, sep: String, end: String) { print(start); printSeq(ts){print(_)}{print(sep)}; print(end) } def printRow(ts: List[Tree], sep: String) { printRow(ts, "", sep, "") } def printTypeParams(ts: List[TypeDef]) { if (!ts.isEmpty) { print("["); printSeq(ts){ t => printAnnotations(t) printParam(t) }{print(", ")}; print("]") } } def printLabelParams(ps: List[Ident]) { print("(") printSeq(ps){printLabelParam}{print(", ")} print(")") } def printLabelParam(p: Ident) { print(symName(p, p.name)); printOpt(": ", TypeTree() setType p.tpe) } def printValueParams(ts: List[ValDef]) { print("(") if (!ts.isEmpty) printFlags(ts.head.mods.flags & IMPLICIT, "") printSeq(ts){printParam}{print(", ")} print(")") } def printParam(tree: Tree) { tree match { case ValDef(mods, name, tp, rhs) => printPosition(tree) printAnnotations(tree) print(symName(tree, name)); printOpt(": ", tp); printOpt(" = ", rhs) case TypeDef(mods, name, tparams, rhs) => printPosition(tree) print(symName(tree, name)) printTypeParams(tparams); print(rhs) } } def printBlock(tree: Tree) { tree match { case Block(_, _) => print(tree) case _ => printColumn(List(tree), "{", ";", "}") } } private def symFn[T](tree: Tree, f: Symbol => T, orElse: => T): T = tree.symbol match { case null | NoSymbol => orElse case sym => f(sym) } private def ifSym(tree: Tree, p: Symbol => Boolean) = symFn(tree, p, false) def printOpt(prefix: String, tree: Tree) { if (!tree.isEmpty) { print(prefix, tree) } } def printModifiers(tree: Tree, mods: Modifiers): Unit = printFlags( if (tree.symbol == NoSymbol) mods.flags else tree.symbol.flags, "" + ( if (tree.symbol == NoSymbol) mods.privateWithin else if (tree.symbol.hasAccessBoundary) tree.symbol.privateWithin.name else "" ) ) def printFlags(flags: Long, privateWithin: String) { var mask: Long = if (settings.debug.value) -1L else PrintableFlags val s = flagsToString(flags & mask, privateWithin) if (s != "") print(s + " ") } def printAnnotations(tree: Tree) { // SI-5885: by default this won't print annotations of not yet initialized symbols val annots = tree.symbol.annotations match { case Nil => tree.asInstanceOf[MemberDef].mods.annotations case anns => anns } annots foreach (annot => print("@"+annot+" ")) } private var currentOwner: Symbol = NoSymbol private var selectorType: Type = NoType def printTree(tree: Tree) { tree match { case EmptyTree => print("<empty>") case ClassDef(mods, name, tparams, impl) => printAnnotations(tree) printModifiers(tree, mods) val word = if (mods.isTrait) "trait" else if (ifSym(tree, _.isModuleClass)) "object" else "class" print(word, " ", symName(tree, name)) printTypeParams(tparams) print(if (mods.isDeferred) " <: " else " extends ", impl) case PackageDef(packaged, stats) => printAnnotations(tree) print("package ", packaged); printColumn(stats, " {", ";", "}") case ModuleDef(mods, name, impl) => printAnnotations(tree) printModifiers(tree, mods); print("object " + symName(tree, name), " extends ", impl) case ValDef(mods, name, tp, rhs) => printAnnotations(tree) printModifiers(tree, mods) print(if (mods.isMutable) "var " else "val ", symName(tree, name)) printOpt(": ", tp) if (!mods.isDeferred) print(" = ", if (rhs.isEmpty) "_" else rhs) case DefDef(mods, name, tparams, vparamss, tp, rhs) => printAnnotations(tree) printModifiers(tree, mods) print("def " + symName(tree, name)) printTypeParams(tparams); vparamss foreach printValueParams printOpt(": ", tp); printOpt(" = ", rhs) case TypeDef(mods, name, tparams, rhs) => if (mods hasFlag (PARAM | DEFERRED)) { printAnnotations(tree) printModifiers(tree, mods); print("type "); printParam(tree) } else { printAnnotations(tree) printModifiers(tree, mods); print("type " + symName(tree, name)) printTypeParams(tparams); printOpt(" = ", rhs) } case LabelDef(name, params, rhs) => print(symName(tree, name)); printLabelParams(params); printBlock(rhs) case Import(expr, selectors) => // Is this selector remapping a name (i.e, {name1 => name2}) def isNotRemap(s: ImportSelector) : Boolean = (s.name == nme.WILDCARD || s.name == s.rename) def selectorToString(s: ImportSelector): String = { val from = quotedName(s.name) if (isNotRemap(s)) from else from + "=>" + quotedName(s.rename) } print("import ", backquotedPath(expr), ".") selectors match { case List(s) => // If there is just one selector and it is not remapping a name, no braces are needed if (isNotRemap(s)) print(selectorToString(s)) else print("{", selectorToString(s), "}") // If there is more than one selector braces are always needed case many => print(many.map(selectorToString).mkString("{", ", ", "}")) } case Template(parents, self, body) => val currentOwner1 = currentOwner if (tree.symbol != NoSymbol) currentOwner = tree.symbol.owner // if (parents exists isReferenceToAnyVal) { // print("AnyVal") // } // else { printRow(parents, " with ") if (!body.isEmpty) { if (self.name != nme.WILDCARD) { print(" { ", self.name); printOpt(": ", self.tpt); print(" => ") } else if (!self.tpt.isEmpty) { print(" { _ : ", self.tpt, " => ") } else { print(" {") } printColumn(body, "", ";", "}") } // } currentOwner = currentOwner1 case Block(stats, expr) => printColumn(stats ::: List(expr), "{", ";", "}") case Match(selector, cases) => val selectorType1 = selectorType selectorType = selector.tpe print(selector); printColumn(cases, " match {", "", "}") selectorType = selectorType1 case CaseDef(pat, guard, body) => print("case ") def patConstr(pat: Tree): Tree = pat match { case Apply(fn, args) => patConstr(fn) case _ => pat } if (showOuterTests && needsOuterTest( patConstr(pat).tpe.finalResultType, selectorType, currentOwner)) print("???") print(pat); printOpt(" if ", guard) print(" => ", body) case Alternative(trees) => printRow(trees, "(", "| ", ")") case Star(elem) => print("(", elem, ")*") case Bind(name, t) => print("(", symName(tree, name), " @ ", t, ")") case UnApply(fun, args) => print(fun, " <unapply> "); printRow(args, "(", ", ", ")") case ArrayValue(elemtpt, trees) => print("Array[", elemtpt); printRow(trees, "]{", ", ", "}") case Function(vparams, body) => print("("); printValueParams(vparams); print(" => ", body, ")") if (printIds && tree.symbol != null) print("#"+tree.symbol.id) case Assign(lhs, rhs) => print(lhs, " = ", rhs) case AssignOrNamedArg(lhs, rhs) => print(lhs, " = ", rhs) case If(cond, thenp, elsep) => print("if (", cond, ")"); indent; println() print(thenp); undent if (!elsep.isEmpty) { println(); print("else"); indent; println(); print(elsep); undent } case Return(expr) => print("return ", expr) case Try(block, catches, finalizer) => print("try "); printBlock(block) if (!catches.isEmpty) printColumn(catches, " catch {", "", "}") printOpt(" finally ", finalizer) case Throw(expr) => print("throw ", expr) case New(tpe) => print("new ", tpe) case Typed(expr, tp) => print("(", expr, ": ", tp, ")") case TypeApply(fun, targs) => print(fun); printRow(targs, "[", ", ", "]") case Apply(fun, vargs) => print(fun); printRow(vargs, "(", ", ", ")") case ApplyDynamic(qual, vargs) => print("<apply-dynamic>(", qual, "#", tree.symbol.nameString) printRow(vargs, ", (", ", ", "))") case Super(This(qual), mix) => if (!qual.isEmpty || tree.symbol != NoSymbol) print(symName(tree, qual) + ".") print("super") if (!mix.isEmpty) print("[" + mix + "]") case Super(qual, mix) => print(qual, ".super") if (!mix.isEmpty) print("[" + mix + "]") case This(qual) => if (!qual.isEmpty) print(symName(tree, qual) + ".") print("this") case Select(qual @ New(tpe), name) if (!settings.debug.value) => print(qual) case Select(qualifier, name) => print(backquotedPath(qualifier), ".", symName(tree, name)) case id @ Ident(name) => val str = symName(tree, name) print( if (id.isBackquoted) "`" + str + "`" else str ) case Literal(x) => print(x.escapedStringValue) case tt: TypeTree => if ((tree.tpe eq null) || (doPrintPositions && tt.original != null)) { if (tt.original != null) print("<type: ", tt.original, ">") else print("<type ?>") } else if ((tree.tpe.typeSymbol ne null) && tree.tpe.typeSymbol.isAnonymousClass) { print(tree.tpe.typeSymbol.toString) } else { print(tree.tpe.toString) } case Annotated(Apply(Select(New(tpt), nme.CONSTRUCTOR), args), tree) => def printAnnot() { print("@", tpt) if (!args.isEmpty) printRow(args, "(", ",", ")") } print(tree, if (tree.isType) " " else ": ") printAnnot() case SingletonTypeTree(ref) => print(ref, ".type") case SelectFromTypeTree(qualifier, selector) => print(qualifier, "#", symName(tree, selector)) case CompoundTypeTree(templ) => print(templ) case AppliedTypeTree(tp, args) => print(tp); printRow(args, "[", ", ", "]") case TypeBoundsTree(lo, hi) => printOpt(" >: ", lo); printOpt(" <: ", hi) case ExistentialTypeTree(tpt, whereClauses) => print(tpt); printColumn(whereClauses, " forSome { ", ";", "}") // SelectFromArray is no longer visible in reflect.internal. // eliminated until we figure out what we will do with both Printers and // SelectFromArray. // case SelectFromArray(qualifier, name, _) => // print(qualifier); print(".<arr>"); print(symName(tree, name)) case tree => xprintTree(this, tree) } if (printTypes && tree.isTerm && !tree.isEmpty) { print("{", if (tree.tpe eq null) "<null>" else tree.tpe.toString, "}") } } def print(args: Any*): Unit = args foreach { case tree: Tree => printPosition(tree) printTree(tree) case name: Name => print(quotedName(name)) case arg => out.print(if (arg == null) "null" else arg.toString) } } /** Hook for extensions */ def xprintTree(treePrinter: TreePrinter, tree: Tree) = treePrinter.print(tree.productPrefix+tree.productIterator.mkString("(", ", ", ")")) def newTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) /** A writer that writes to the current Console and * is sensitive to replacement of the Console's * output stream. */ object ConsoleWriter extends Writer { override def write(str: String) { Console.print(str) } def write(cbuf: Array[Char], off: Int, len: Int) { write(new String(cbuf, off, len)) } def close = { /* do nothing */ } def flush = { /* do nothing */ } } def newRawTreePrinter(writer: PrintWriter): RawTreePrinter = new RawTreePrinter(writer) def newRawTreePrinter(stream: OutputStream): RawTreePrinter = newRawTreePrinter(new PrintWriter(stream)) def newRawTreePrinter(): RawTreePrinter = newRawTreePrinter(new PrintWriter(ConsoleWriter)) // provides footnotes for types and mirrors import scala.collection.mutable.{Map, WeakHashMap, SortedSet} private val footnoteIndex = new FootnoteIndex private class FootnoteIndex { private val index = Map[Class[_], WeakHashMap[Any, Int]]() private def classIndex[T: ClassTag] = index.getOrElseUpdate(classTag[T].runtimeClass, WeakHashMap[Any, Int]()) private val counters = Map[Class[_], Int]() private def nextCounter[T: ClassTag] = { val clazz = classTag[T].runtimeClass counters.getOrElseUpdate(clazz, 0) counters(clazz) = counters(clazz) + 1 counters(clazz) } def mkFootnotes() = new Footnotes class Footnotes { private val footnotes = Map[Class[_], SortedSet[Int]]() private def classFootnotes[T: ClassTag] = footnotes.getOrElseUpdate(classTag[T].runtimeClass, SortedSet[Int]()) def put[T: ClassTag](any: T): Int = { val index = classIndex[T].getOrElseUpdate(any, nextCounter[T]) classFootnotes[T] += index index } def get[T: ClassTag]: List[(Int, Any)] = classFootnotes[T].toList map (fi => (fi, classIndex[T].find{ case (any, ii) => ii == fi }.get._1)) def print[T: ClassTag](printer: Printers.super.TreePrinter): Unit = { val footnotes = get[T] if (footnotes.nonEmpty) { printer.print(EOL) footnotes.zipWithIndex foreach { case ((fi, any), ii) => printer.print("[", fi, "] ", any) if (ii < footnotes.length - 1) printer.print(EOL) } } } } } // emits more or less verbatim representation of the provided tree class RawTreePrinter(out: PrintWriter) extends super.TreePrinter { private var depth = 0 private var printTypesInFootnotes = true private var printingFootnotes = false private var footnotes = footnoteIndex.mkFootnotes() def print(args: Any*): Unit = { // don't print type footnotes if the argument is a mere type if (depth == 0 && args.length == 1 && args(0) != null && args(0).isInstanceOf[Type]) printTypesInFootnotes = false depth += 1 args foreach { case expr: Expr[_] => print("Expr") if (printTypes) print(expr.staticType) print("(") print(expr.tree) print(")") case EmptyTree => print("EmptyTree") case emptyValDef: AnyRef if emptyValDef eq self.emptyValDef => print("emptyValDef") case tree: Tree => val hasSymbol = tree.hasSymbol && tree.symbol != NoSymbol val isError = hasSymbol && tree.symbol.name.toString == nme.ERROR.toString printProduct( tree, preamble = _ => { print(tree.productPrefix) if (printTypes && tree.tpe != null) print(tree.tpe) }, body = { case name: Name => if (isError) { if (isError) print("<") print(name) if (isError) print(": error>") } else if (hasSymbol) { tree match { case _: Ident | _: Select | _: SelectFromTypeTree => print(tree.symbol) case _ => print(tree.symbol.name) } } else { print(name) } case Constant(s: String) => print("Constant(\"" + s + "\")") case Constant(null) => print("Constant(null)") case Constant(value) => print("Constant(" + value + ")") case arg => print(arg) }, postamble = { case tree @ TypeTree() if tree.original != null => print(".setOriginal(", tree.original, ")") case _ => // do nothing }) case sym: Symbol => if (sym == NoSymbol) print("NoSymbol") else if (sym.isStatic && (sym.isClass || sym.isModule)) print(sym.fullName) else print(sym.name) if (printIds) print("#", sym.id) if (printKinds) print("#", sym.abbreviatedKindString) if (printMirrors) print("%M", footnotes.put[scala.reflect.api.Mirror[_]](mirrorThatLoaded(sym))) case tag: TypeTag[_] => print("TypeTag(", tag.tpe, ")") case tag: WeakTypeTag[_] => print("WeakTypeTag(", tag.tpe, ")") case tpe: Type => val defer = printTypesInFootnotes && !printingFootnotes if (defer) print("[", footnotes.put(tpe), "]") else tpe match { case NoType => print("NoType") case NoPrefix => print("NoPrefix") case _ => printProduct(tpe.asInstanceOf[Product]) } case mods: Modifiers => print("Modifiers(") if (mods.flags != NoFlags || mods.privateWithin != tpnme.EMPTY || mods.annotations.nonEmpty) print(show(mods.flags)) if (mods.privateWithin != tpnme.EMPTY || mods.annotations.nonEmpty) { print(", "); print(mods.privateWithin) } if (mods.annotations.nonEmpty) { print(", "); print(mods.annotations); } print(")") case name: Name => print(show(name)) case scope: Scope => print("Scope") printIterable(scope.toList) case list: List[_] => print("List") printIterable(list) case product: Product => printProduct(product) case arg => out.print(arg) } depth -= 1 if (depth == 0 && !printingFootnotes) { printingFootnotes = true footnotes.print[Type](this) footnotes.print[scala.reflect.api.Mirror[_]](this) printingFootnotes = false } } def printProduct( p: Product, preamble: Product => Unit = p => print(p.productPrefix), body: Any => Unit = print(_), postamble: Product => Unit = p => print("")): Unit = { preamble(p) printIterable(p.productIterator.toList, body = body) postamble(p) } def printIterable( iterable: List[_], preamble: => Unit = print(""), body: Any => Unit = print(_), postamble: => Unit = print("")): Unit = { preamble print("(") val it = iterable.iterator while (it.hasNext) { body(it.next) print(if (it.hasNext) ", " else "") } print(")") postamble } } def show(name: Name): String = name match { case tpnme.WILDCARD => "tpnme.WILDCARD" case tpnme.EMPTY => "tpnme.EMPTY" case tpnme.ERROR => "tpnme.ERROR" case tpnme.PACKAGE => "tpnme.PACKAGE" case tpnme.WILDCARD_STAR => "tpnme.WILDCARD_STAR" case nme.WILDCARD => "nme.WILDCARD" case nme.EMPTY => "nme.EMPTY" case nme.ERROR => "tpnme.ERROR" case nme.PACKAGE => "nme.PACKAGE" case nme.CONSTRUCTOR => "nme.CONSTRUCTOR" case nme.ROOTPKG => "nme.ROOTPKG" case _ => val prefix = if (name.isTermName) "newTermName(\"" else "newTypeName(\"" prefix + name.toString + "\")" } def show(flags: FlagSet): String = { if (flags == NoFlags) nme.NoFlags.toString else { val s_flags = new scala.collection.mutable.ListBuffer[String] def hasFlag(left: Long, right: Long): Boolean = (left & right) != 0 for (i <- 0 to 63 if hasFlag(flags, 1L << i)) s_flags += flagToString(1L << i).replace("<", "").replace(">", "").toUpperCase s_flags mkString " | " } } }