diff options
Diffstat (limited to 'src/scalap/scala/tools/scalap/scalasig/ScalaSigPrinter.scala')
-rw-r--r-- | src/scalap/scala/tools/scalap/scalasig/ScalaSigPrinter.scala | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/src/scalap/scala/tools/scalap/scalasig/ScalaSigPrinter.scala b/src/scalap/scala/tools/scalap/scalasig/ScalaSigPrinter.scala new file mode 100644 index 0000000000..01bef65fbe --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalasig/ScalaSigPrinter.scala @@ -0,0 +1,434 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2013, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ http://scala-lang.org/ +** +*/ + +package scala.tools.scalap.scalasig + +import scala.language.implicitConversions + +import java.io.{PrintStream, ByteArrayOutputStream} +import java.util.regex.Pattern + +import scala.tools.scalap.rules.~ + +class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { + import stream._ + + val CONSTRUCTOR_NAME = "<init>" + + case class TypeFlags(printRep: Boolean) + + def printSymbol(symbol: Symbol) {printSymbol(0, symbol)} + + def printSymbolAttributes(s: Symbol, onNewLine: Boolean, indent: => Unit) = s match { + case t: SymbolInfoSymbol => { + for (a <- t.attributes) { + indent; print(toString(a)) + if (onNewLine) print("\n") else print(" ") + } + } + case _ => + } + + def printSymbol(level: Int, symbol: Symbol) { + if (!symbol.isLocal && + !(symbol.isPrivate && !printPrivates)) { + def indent() {for (i <- 1 to level) print(" ")} + + printSymbolAttributes(symbol, true, indent) + symbol match { + case o: ObjectSymbol => + if (!isCaseClassObject(o)) { + indent + if (o.name == "package") { + // print package object + printPackageObject(level, o) + } else { + printObject(level, o) + } + } + case c: ClassSymbol if !refinementClass(c) && !c.isModule => + indent + printClass(level, c) + case m: MethodSymbol => + printMethod(level, m, indent) + case a: AliasSymbol => + indent + printAlias(level, a) + case t: TypeSymbol if !t.isParam && !t.name.matches("_\\$\\d+")=> + indent + printTypeSymbol(level, t) + case s => + } + } + } + + def isCaseClassObject(o: ObjectSymbol): Boolean = { + val TypeRefType(_, classSymbol: ClassSymbol, _) = o.infoType + o.isFinal && (classSymbol.children.find(x => x.isCase && x.isInstanceOf[MethodSymbol]) match { + case Some(_) => true + case None => false + }) + } + + private def underCaseClass(m: MethodSymbol) = m.parent match { + case Some(c: ClassSymbol) => c.isCase + case _ => false + } + + + private def printChildren(level: Int, symbol: Symbol) { + for (child <- symbol.children) printSymbol(level + 1, child) + } + + def printWithIndent(level: Int, s: String) { + def indent() {for (i <- 1 to level) print(" ")} + indent; + print(s) + } + + def printModifiers(symbol: Symbol) { + // print private access modifier + if (symbol.isPrivate) print("private ") + else if (symbol.isProtected) print("protected ") + else symbol match { + case sym: SymbolInfoSymbol => sym.symbolInfo.privateWithin match { + case Some(t: Symbol) => print("private[" + t.name +"] ") + case _ => + } + case _ => + } + + if (symbol.isSealed) print("sealed ") + if (symbol.isImplicit) print("implicit ") + if (symbol.isFinal && !symbol.isInstanceOf[ObjectSymbol]) print("final ") + if (symbol.isOverride) print("override ") + if (symbol.isAbstract) symbol match { + case c@(_: ClassSymbol | _: ObjectSymbol) if !c.isTrait => print("abstract ") + case _ => () + } + if (symbol.isCase && !symbol.isMethod) print("case ") + } + + private def refinementClass(c: ClassSymbol) = c.name == "<refinement>" + + def printClass(level: Int, c: ClassSymbol) { + if (c.name == "<local child>" /*scala.tools.nsc.symtab.StdNames.LOCAL_CHILD.toString()*/ ) { + print("\n") + } else { + printModifiers(c) + val defaultConstructor = if (c.isCase) getPrinterByConstructor(c) else "" + if (c.isTrait) print("trait ") else print("class ") + print(processName(c.name)) + val it = c.infoType + val classType = it match { + case PolyType(typeRef, symbols) => PolyTypeWithCons(typeRef, symbols, defaultConstructor) + case ClassInfoType(a, b) if c.isCase => ClassInfoTypeWithCons(a, b, defaultConstructor) + case _ => it + } + printType(classType) + print(" {") + //Print class selftype + c.selfType match { + case Some(t: Type) => print("\n"); print(" this : " + toString(t) + " =>") + case None => + } + print("\n") + printChildren(level, c) + printWithIndent(level, "}\n") + } + } + + def getPrinterByConstructor(c: ClassSymbol) = { + c.children.find { + case m: MethodSymbol if m.name == CONSTRUCTOR_NAME => true + case _ => false + } match { + case Some(m: MethodSymbol) => + val baos = new ByteArrayOutputStream + val stream = new PrintStream(baos) + val printer = new ScalaSigPrinter(stream, printPrivates) + printer.printMethodType(m.infoType, false)(()) + baos.toString + case _ => + "" + } + } + + def printPackageObject(level: Int, o: ObjectSymbol) { + printModifiers(o) + print("package ") + print("object ") + val poName = o.symbolInfo.owner.name + print(processName(poName)) + val TypeRefType(_, classSymbol: ClassSymbol, _) = o.infoType + printType(classSymbol) + print(" {\n") + printChildren(level, classSymbol) + printWithIndent(level, "}\n") + + } + + def printObject(level: Int, o: ObjectSymbol) { + printModifiers(o) + print("object ") + print(processName(o.name)) + val TypeRefType(_, classSymbol: ClassSymbol, _) = o.infoType + printType(classSymbol) + print(" {\n") + printChildren(level, classSymbol) + printWithIndent(level, "}\n") + } + + def printMethodType(t: Type, printResult: Boolean)(cont: => Unit): Unit = { + + def _pmt(mt: MethodType) = { + + val paramEntries = mt.paramSymbols.map({ + case ms: MethodSymbol => ms.name + " : " + toString(ms.infoType)(TypeFlags(true)) + case _ => "^___^" + }) + val implicitWord = mt.paramSymbols.headOption match { + case Some(p) if p.isImplicit => "implicit " + case _ => "" + } + + // Print parameter clauses + print(paramEntries.mkString("(" + implicitWord, ", ", ")")) + + // Print result type + mt.resultType match { + case mt: MethodType => printMethodType(mt, printResult)({}) + case x => if (printResult) { + print(" : "); + printType(x) + } + } + } + + t match { + case NullaryMethodType(resType) => if (printResult) { print(" : "); printType(resType) } + case mt@MethodType(resType, paramSymbols) => _pmt(mt) + case pt@PolyType(mt, typeParams) => { + print(typeParamString(typeParams)) + printMethodType(mt, printResult)({}) + } + //todo consider another method types + case x => print(" : "); printType(x) + } + + // Print rest of the symbol output + cont + } + + def printMethod(level: Int, m: MethodSymbol, indent: () => Unit) { + def cont() = print(" = { /* compiled code */ }") + + val n = m.name + if (underCaseClass(m) && n == CONSTRUCTOR_NAME) return + if (n.matches(".+\\$default\\$\\d+")) return // skip default function parameters + if (n.startsWith("super$")) return // do not print auxiliary qualified super accessors + if (m.isAccessor && n.endsWith("_$eq")) return + indent() + printModifiers(m) + if (m.isAccessor) { + val indexOfSetter = m.parent.get.children.indexWhere(x => x.isInstanceOf[MethodSymbol] && + x.asInstanceOf[MethodSymbol].name == n + "_$eq") + print(if (indexOfSetter > 0) "var " else "val ") + } else { + print("def ") + } + n match { + case CONSTRUCTOR_NAME => + print("this") + printMethodType(m.infoType, false)(cont) + case name => + val nn = processName(name) + print(nn) + printMethodType(m.infoType, true)( + {if (!m.isDeferred) print(" = { /* compiled code */ }" /* Print body only for non-abstract methods */ )} + ) + } + print("\n") + } + + def printAlias(level: Int, a: AliasSymbol) { + print("type ") + print(processName(a.name)) + printType(a.infoType, " = ") + print("\n") + printChildren(level, a) + } + + def printTypeSymbol(level: Int, t: TypeSymbol) { + print("type ") + print(processName(t.name)) + printType(t.infoType) + print("\n") + } + + def toString(attrib: AttributeInfo): String = { + val buffer = new StringBuffer + buffer.append(toString(attrib.typeRef, "@")) + if (attrib.value.isDefined) { + buffer.append("(") + val value = attrib.value.get + val stringVal = value.isInstanceOf[String] + if (stringVal) buffer.append("\"") + val stringValue = valueToString(value) + val isMultiline = stringVal && (stringValue.contains("\n") + || stringValue.contains("\r")) + if (isMultiline) buffer.append("\"\"") + buffer.append(valueToString(value)) + if (isMultiline) buffer.append("\"\"") + if (stringVal) buffer.append("\"") + buffer.append(")") + } + if (!attrib.values.isEmpty) { + buffer.append(" {") + for (name ~ value <- attrib.values) { + buffer.append(" val ") + buffer.append(processName(name)) + buffer.append(" = ") + buffer.append(valueToString(value)) + } + buffer.append(valueToString(attrib.value)) + buffer.append(" }") + } + buffer.toString + } + + def valueToString(value: Any): String = value match { + case t: Type => toString(t) + // TODO string, char, float, etc. + case _ => value.toString + } + + implicit object _tf extends TypeFlags(false) + + def printType(sym: SymbolInfoSymbol)(implicit flags: TypeFlags): Unit = printType(sym.infoType)(flags) + + def printType(t: Type)(implicit flags: TypeFlags): Unit = print(toString(t)(flags)) + + def printType(t: Type, sep: String)(implicit flags: TypeFlags): Unit = print(toString(t, sep)(flags)) + + def toString(t: Type)(implicit flags: TypeFlags): String = toString(t, "")(flags) + + def toString(t: Type, sep: String)(implicit flags: TypeFlags): String = { + // print type itself + t match { + case ThisType(symbol) => sep + processName(symbol.path) + ".type" + case SingleType(typeRef, symbol) => sep + processName(symbol.path) + ".type" + case ConstantType(constant) => sep + (constant match { + case null => "scala.Null" + case _: Unit => "scala.Unit" + case _: Boolean => "scala.Boolean" + case _: Byte => "scala.Byte" + case _: Char => "scala.Char" + case _: Short => "scala.Short" + case _: Int => "scala.Int" + case _: Long => "scala.Long" + case _: Float => "scala.Float" + case _: Double => "scala.Double" + case _: String => "java.lang.String" + case c: Class[_] => "java.lang.Class[" + c.getComponentType.getCanonicalName.replace("$", ".") + "]" + }) + case TypeRefType(prefix, symbol, typeArgs) => sep + (symbol.path match { + case "scala.<repeated>" => flags match { + case TypeFlags(true) => toString(typeArgs.head) + "*" + case _ => "scala.Seq" + typeArgString(typeArgs) + } + case "scala.<byname>" => "=> " + toString(typeArgs.head) + case _ => { + val path = cutSubstring(symbol.path)(".package") //remove package object reference + trimStart(processName(path) + typeArgString(typeArgs), "<empty>.") + } + }) + case TypeBoundsType(lower, upper) => { + val lb = toString(lower) + val ub = toString(upper) + val lbs = if (!lb.equals("scala.Nothing")) " >: " + lb else "" + val ubs = if (!ub.equals("scala.Any")) " <: " + ub else "" + lbs + ubs + } + case RefinedType(classSym, typeRefs) => sep + typeRefs.map(toString).mkString("", " with ", "") + case ClassInfoType(symbol, typeRefs) => sep + typeRefs.map(toString).mkString(" extends ", " with ", "") + case ClassInfoTypeWithCons(symbol, typeRefs, cons) => sep + typeRefs.map(toString). + mkString(cons + " extends ", " with ", "") + + case MethodType(resultType, _) => toString(resultType, sep) + case NullaryMethodType(resultType) => toString(resultType, sep) + + case PolyType(typeRef, symbols) => typeParamString(symbols) + toString(typeRef, sep) + case PolyTypeWithCons(typeRef, symbols, cons) => typeParamString(symbols) + processName(cons) + toString(typeRef, sep) + case AnnotatedType(typeRef, attribTreeRefs) => { + toString(typeRef, sep) + } + case AnnotatedWithSelfType(typeRef, symbol, attribTreeRefs) => toString(typeRef, sep) + case ExistentialType(typeRef, symbols) => { + val refs = symbols.map(toString _).filter(!_.startsWith("_")).map("type " + _) + toString(typeRef, sep) + (if (refs.size > 0) refs.mkString(" forSome {", "; ", "}") else "") + } + case _ => sep + t.toString + } + } + + def getVariance(t: TypeSymbol) = if (t.isCovariant) "+" else if (t.isContravariant) "-" else "" + + def toString(symbol: Symbol): String = symbol match { + case symbol: TypeSymbol => { + val attrs = (for (a <- symbol.attributes) yield toString(a)).mkString(" ") + val atrs = if (attrs.length > 0) attrs.trim + " " else "" + atrs + getVariance(symbol) + processName(symbol.name) + toString(symbol.infoType) + } + case s => symbol.toString + } + + def typeArgString(typeArgs: Seq[Type]): String = + if (typeArgs.isEmpty) "" + else typeArgs.map(toString).map(trimStart(_, "=> ")).mkString("[", ", ", "]") + + def typeParamString(params: Seq[Symbol]): String = + if (params.isEmpty) "" + else params.map(toString).mkString("[", ", ", "]") + + val _syms = Map("\\$bar" -> "|", "\\$tilde" -> "~", + "\\$bang" -> "!", "\\$up" -> "^", "\\$plus" -> "+", + "\\$minus" -> "-", "\\$eq" -> "=", "\\$less" -> "<", + "\\$times" -> "*", "\\$div" -> "/", "\\$bslash" -> "\\\\", + "\\$greater" -> ">", "\\$qmark" -> "?", "\\$percent" -> "%", + "\\$amp" -> "&", "\\$colon" -> ":", "\\$u2192" -> "→", + "\\$hash" -> "#") + val pattern = Pattern.compile(_syms.keys.foldLeft("")((x, y) => if (x == "") y else x + "|" + y)) + val placeholderPattern = "_\\$(\\d)+" + + private def stripPrivatePrefix(name: String) = { + val i = name.lastIndexOf("$$") + if (i > 0) name.substring(i + 2) else name + } + + private def processName(name: String) = { + val stripped = stripPrivatePrefix(name) + val m = pattern.matcher(stripped) + var temp = stripped + while (m.find) { + val key = m.group + val re = "\\" + key + temp = temp.replaceAll(re, _syms(re)) + } + val result = temp.replaceAll(placeholderPattern, "_") + scala.reflect.NameTransformer.decode(result) + } + + private def trimStart(s: String, prefix: String) = + if (s != null && s.startsWith(prefix)) s.substring(prefix.length) else s + + private def decapitalize(s: String) = + java.beans.Introspector.decapitalize(s) + + private def cutSubstring(dom: String)(s: String) = + if (dom != null && s != null) dom.replace(s, "") else dom +} |