diff options
Diffstat (limited to 'src/scalap')
12 files changed, 304 insertions, 141 deletions
diff --git a/src/scalap/scala/tools/scalap/Classfile.scala b/src/scalap/scala/tools/scalap/Classfile.scala index 44f687bd85..c4f273c5aa 100644 --- a/src/scalap/scala/tools/scalap/Classfile.scala +++ b/src/scalap/scala/tools/scalap/Classfile.scala @@ -26,6 +26,7 @@ class Classfile(in: ByteArrayReader) { val fields = readMembers(true) val methods = readMembers(false) val attribs = readAttribs + def scalaSigAttribute = attribs find (_.toString == Main.SCALA_SIG) def readAttribs = { val n = in.nextChar diff --git a/src/scalap/scala/tools/scalap/Decode.scala b/src/scalap/scala/tools/scalap/Decode.scala index d859ac5766..e9f9a390c5 100644 --- a/src/scalap/scala/tools/scalap/Decode.scala +++ b/src/scalap/scala/tools/scalap/Decode.scala @@ -10,7 +10,9 @@ package scala.tools.scalap import scala.tools.scalap.scalax.rules.scalasig._ +import scala.tools.nsc.util.ScalaClassLoader import scala.tools.nsc.util.ScalaClassLoader.getSystemLoader +import Main.SCALA_SIG /** Temporary decoder. This would be better off in the scala.tools.nsc * but right now the compiler won't acknowledge scala.tools.scalap @@ -23,15 +25,52 @@ object Decode { case _ => NoSymbol } + /** Return the classfile bytes representing the scala sig attribute. + */ + def scalaSigBytes(name: String): Option[Array[Byte]] = scalaSigBytes(name, getSystemLoader()) + def scalaSigBytes(name: String, classLoader: ScalaClassLoader): Option[Array[Byte]] = { + val bytes = classLoader.findBytesForClassName(name) + val reader = new ByteArrayReader(bytes) + val cf = new Classfile(reader) + cf.scalaSigAttribute map (_.data) + } + + /** private[scala] so nobody gets the idea this is a supported interface. + */ + private[scala] def caseParamNames(path: String): Option[List[String]] = { + val (outer, inner) = (path indexOf '$') match { + case -1 => (path, "") + case x => (path take x, path drop (x + 1)) + } + + for { + clazz <- getSystemLoader.tryToLoadClass[AnyRef](outer) + ssig <- ScalaSigParser.parse(clazz) + } + yield { + val f: PartialFunction[Symbol, List[String]] = + if (inner.isEmpty) { + case x: MethodSymbol if x.isCaseAccessor && (x.name endsWith " ") => List(x.name dropRight 1) + } + else { + case x: ClassSymbol if x.name == inner => + val xs = x.children filter (child => child.isCaseAccessor && (child.name endsWith " ")) + xs.toList map (_.name dropRight 1) + } + + (ssig.symbols collect f).flatten toList + } + } + /** Returns a map of Alias -> Type for the given package. */ - def typeAliases(pkg: String) = { + private[scala] def typeAliases(pkg: String) = { for { clazz <- getSystemLoader.tryToLoadClass[AnyRef](pkg + ".package") ssig <- ScalaSigParser.parse(clazz) } yield { - val typeAliases = ssig.symbols partialMap { case x: AliasSymbol => x } + val typeAliases = ssig.symbols collect { case x: AliasSymbol => x } Map(typeAliases map (x => (x.name, getAliasSymbol(x.infoType).path)): _*) } } diff --git a/src/scalap/scala/tools/scalap/Main.scala b/src/scalap/scala/tools/scalap/Main.scala index 59c46df25f..69a91dafce 100644 --- a/src/scalap/scala/tools/scalap/Main.scala +++ b/src/scalap/scala/tools/scalap/Main.scala @@ -9,10 +9,14 @@ package scala.tools.scalap -import java.io.{File, PrintStream, OutputStreamWriter, ByteArrayOutputStream} +import java.io.{PrintStream, OutputStreamWriter, ByteArrayOutputStream} import scalax.rules.scalasig._ -import tools.nsc.io.AbstractFile -import tools.nsc.util.{ClassPath, JavaClassPath} +import scalax.rules.scalasig.ClassFileParser.{ConstValueIndex, Annotation} +import tools.nsc.util.{ ClassPath } +import tools.util.PathResolver +import ClassPath.DefaultJavaContext +import tools.nsc.io.{PlainFile, AbstractFile} +import scala.reflect.generic.ByteCodecs /**The main object used to execute scalap on the command-line. * @@ -20,6 +24,9 @@ import tools.nsc.util.{ClassPath, JavaClassPath} */ object Main { val SCALA_SIG = "ScalaSig" + val SCALA_SIG_ANNOTATION = "Lscala/reflect/ScalaSignature;" + val BYTES_VALUE = "bytes" + val versionMsg = "Scala classfile decoder " + Properties.versionString + " -- " + Properties.copyrightString + "\n" @@ -33,7 +40,8 @@ object Main { */ def usage { Console.println("usage: scalap {<option>} <name>") - Console.println("where <option> is") + Console.println("where <name> is fully-qualified class name or <package_name>.package for package objects") + Console.println("and <option> is") Console.println(" -private print private definitions") Console.println(" -verbose print out additional information") Console.println(" -version print out the version number of scalap") @@ -94,17 +102,34 @@ object Main { baos.toString } - - def decompileScala(bytes: Array[Byte], isPackageObject: Boolean) = { + def decompileScala(bytes: Array[Byte], isPackageObject: Boolean): String = { val byteCode = ByteCode(bytes) val classFile = ClassFileParser.parse(byteCode) classFile.attribute(SCALA_SIG).map(_.byteCode).map(ScalaSigAttributeParsers.parse) match { - case Some(scalaSig) => Console.println(parseScalaSignature(scalaSig, isPackageObject)) - case None => //Do nothing + // No entries in ScalaSig attribute implies that the signature is stored in the annotation + case Some(ScalaSig(_, _, entries)) if entries.length == 0 => unpickleFromAnnotation(classFile, isPackageObject) + case Some(scalaSig) => parseScalaSignature(scalaSig, isPackageObject) + case None => "" + } + } + + def unpickleFromAnnotation(classFile: ClassFile, isPackageObject: Boolean): String = { + import classFile._ + classFile.annotation(SCALA_SIG_ANNOTATION) match { + case None => "" + case Some(Annotation(_, elements)) => + val bytesElem = elements.find(elem => constant(elem.elementNameIndex) == BYTES_VALUE).get + val bytes = ((bytesElem.elementValue match {case ConstValueIndex(index) => constantWrapped(index)}) + .asInstanceOf[StringBytesPair].bytes) + val length = ByteCodecs.decode(bytes) + val scalaSig = ScalaSigAttributeParsers.parse(ByteCode(bytes.take(length))) + parseScalaSignature(scalaSig, isPackageObject) } } + + /**Executes scalap with the given arguments and classpath for the * class denoted by <code>classname</code>. * @@ -125,7 +150,7 @@ object Main { } val bytes = cfile.toByteArray if (isScalaFile(bytes)) { - decompileScala(bytes, isPackageObjectFile(encName)) + Console.println(decompileScala(bytes, isPackageObjectFile(encName))) } else { // construct a reader for the classfile content val reader = new ByteArrayReader(cfile.toByteArray) @@ -262,13 +287,8 @@ object Main { verbose = arguments contains "-verbose" printPrivates = arguments contains "-private" // construct a custom class path - val path = arguments.getArgument("-classpath") match { - case None => arguments.getArgument("-cp") match { - case None => EmptyClasspath - case Some(path) => new JavaClassPath("", "", path, "", "") - } - case Some(path) => new JavaClassPath("", "", path, "", "") - } + def cparg = List("-classpath", "-cp") map (arguments getArgument _) reduceLeft (_ orElse _) + val path = cparg map (PathResolver fromPathString _) getOrElse EmptyClasspath // print the classpath if output is verbose if (verbose) { Console.println(Console.BOLD + "CLASSPATH" + Console.RESET + " = " + path) @@ -279,12 +299,14 @@ object Main { } object EmptyClasspath extends ClassPath[AbstractFile] { - import tools.nsc.util.ClassRep /** * The short name of the package (without prefix) */ def name: String = "" - val classes: List[ClassRep[AbstractFile]] = Nil + def asURLs = Nil + def asClasspathString = "" + val context = DefaultJavaContext + val classes: List[ClassRep] = Nil val packages: List[ClassPath[AbstractFile]] = Nil val sourcepaths: List[AbstractFile] = Nil } diff --git a/src/scalap/scala/tools/scalap/Properties.scala b/src/scalap/scala/tools/scalap/Properties.scala index f433737896..315b81cb3e 100644 --- a/src/scalap/scala/tools/scalap/Properties.scala +++ b/src/scalap/scala/tools/scalap/Properties.scala @@ -14,5 +14,4 @@ object Properties extends scala.util.PropertiesTrait { protected def propCategory = "decoder" protected def pickJarBasedOn = classOf[Classfile] - val cmdName = scala.tools.nsc.Properties.cmdName } diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Functors.scala b/src/scalap/scala/tools/scalap/scalax/rules/Functors.scala index aa95b48d44..aa852c1e63 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/Functors.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/Functors.scala @@ -60,7 +60,7 @@ trait Functors { } } -/** One of the 'unit' definitions must be overriden in concrete subclasses */ +/** One of the 'unit' definitions must be overridden in concrete subclasses */ trait UnitFunctors extends Units with Functors { def unit : M[Unit] = unit(()) def unit[A](a : => A) : M[A] = unit map { Unit => a } @@ -73,7 +73,7 @@ trait Monoidals extends UnitFunctors { implicit def app[A, B](fab : M[A => B]) = (fa : M[A]) => fa applyTo fab implicit def appUnit[A, B](a2b : A => B) = app(unit(a2b)) - /** One of 'and' and 'applyTo' definitions must be overriden in concrete subclasses */ + /** One of 'and' and 'applyTo' definitions must be overridden in concrete subclasses */ trait Monoidal[+A] extends Functor[A] { self : M[A] => def and[B](fb : => M[B]) : M[(A, B)] = ((a : A) => (b : B) => (a, b))(this)(fb) def applyTo[B](fab : M[A => B]) : M[B] = fab and this map { case (f, a) => f(a) } diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Memoisable.scala b/src/scalap/scala/tools/scalap/scalax/rules/Memoisable.scala index 1324ea695a..827c2dfff7 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/Memoisable.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/Memoisable.scala @@ -44,7 +44,7 @@ trait DefaultMemoisable extends Memoisable { map.getOrElseUpdate(key, compute(key, a)).asInstanceOf[A] } - protected def compute[A](key : AnyRef, a : => A) = a match { + protected def compute[A](key : AnyRef, a : => A): Any = a match { case success : Success[_, _] => onSuccess(key, success); success case other => if(DefaultMemoisable.debug) println(key + " -> " + other) diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Result.scala b/src/scalap/scala/tools/scalap/scalax/rules/Result.scala index 6befbb83c8..17ad4bd053 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/Result.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/Result.scala @@ -42,11 +42,11 @@ case class Success[+Out, +A](out : Out, value : A) extends Result[Out, A, Nothin def toOption = Some(value) - def map[B](f : A => B) = Success(out, f(value)) - def mapOut[Out2](f : Out => Out2) = Success(f(out), value) - def map[Out2, B](f : (Out, A) => (Out2, B)) = f(out, value) match { case (out2, b) => Success(out2, b) } - def flatMap[Out2, B](f : (Out, A) => Result[Out2, B, Nothing]) = f(out, value) - def orElse[Out2 >: Out, B >: A](other : => Result[Out2, B, Nothing]) = this + def map[B](f : A => B) : Result[Out, B, Nothing] = Success(out, f(value)) + def mapOut[Out2](f : Out => Out2) : Result[Out2, A, Nothing] = Success(f(out), value) + def map[Out2, B](f : (Out, A) => (Out2, B)) : Success[Out2, B] = f(out, value) match { case (out2, b) => Success(out2, b) } + def flatMap[Out2, B](f : (Out, A) => Result[Out2, B, Nothing]) : Result[Out2, B, Nothing]= f(out, value) + def orElse[Out2 >: Out, B >: A](other : => Result[Out2, B, Nothing]) : Result[Out2, B, Nothing] = this } sealed abstract class NoSuccess[+X] extends Result[Nothing, Nothing, X] { diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala b/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala index 4e8ddc8dbe..43f9c20b1d 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala @@ -98,7 +98,7 @@ trait StateRules { def nil = unit(Nil) def none = unit(None) - /** Create a rule that suceeds if f(in) is true. */ + /** Create a rule that identities if f(in) is true. */ def cond(f : S => Boolean) = get filter f /** Create a rule that succeeds if all of the given rules succeed. diff --git a/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala b/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala index 54f2c70bdc..34f52a1e19 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala @@ -24,7 +24,7 @@ class InRule[In, +Out, +A, +X](rule : Rule[In, Out, A, X]) { in : In => f(rule(in))(in) } - /** Creates a rule that suceeds only if the original rule would fail on the given context. */ + /** Creates a rule that succeeds only if the original rule would fail on the given context. */ def unary_! : Rule[In, In, Unit, Nothing] = mapRule { case Success(_, _) => in : In => Failure case _ => in : In => Success(in, ()) @@ -82,7 +82,7 @@ class SeqRule[S, +A, +X](rule : Rule[S, S, A, X]) { /** Repeats this rule num times */ def times(num : Int) : Rule[S, S, Seq[A], X] = from[S] { - val result = new collection.mutable.GenericArray[A](num) + val result = new collection.mutable.ArraySeq[A](num) // more compact using HoF but written this way so it's tail-recursive def rep(i : Int, in : S) : Result[S, Seq[A], X] = { if (i == num) Success(in, result) diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala index 37bfa9cfea..01652a50b9 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala @@ -9,8 +9,6 @@ import java.io.IOException import scala._ import scala.Predef._ -import scalax.rules.Error - object ByteCode { def apply(bytes : Array[Byte]) = new ByteCode(bytes, 0, bytes.length) @@ -62,11 +60,23 @@ class ByteCode(val bytes : Array[Byte], val pos : Int, val length : Int) { def toInt = fold(0) { (x, b) => (x << 8) + (b & 0xFF)} def toLong = fold(0L) { (x, b) => (x << 8) + (b & 0xFF)} - def toUTF8String = io.Codec toUTF8 (bytes drop pos take length) mkString + /** + * Transforms array subsequence of the current buffer into the UTF8 String and + * stores and array of bytes for the decompiler + */ + def toUTF8StringAndBytes = { + val chunk: Array[Byte] = bytes drop pos take length + StringBytesPair(io.Codec.toUTF8(chunk).mkString, chunk) + } def byte(i : Int) = bytes(pos) & 0xFF } +/** + * The wrapper for decode UTF-8 string + */ +case class StringBytesPair(string: String, bytes: Array[Byte]) + /** Provides rules for parsing byte-code. */ trait ByteCodeReader extends RulesWithState { @@ -84,6 +94,7 @@ trait ByteCodeReader extends RulesWithState { object ClassFileParser extends ByteCodeReader { def parse(byteCode : ByteCode) = expect(classFile)(byteCode) + def parseAnnotations(byteCode: ByteCode) = expect(annotations)(byteCode) val magicNumber = (u4 filter (_ == 0xCAFEBABE)) | error("Not a valid class file") val version = u2 ~ u2 ^^ { case minor ~ major => (major, minor) } @@ -91,7 +102,7 @@ object ClassFileParser extends ByteCodeReader { // NOTE currently most constants just evaluate to a string description // TODO evaluate to useful values - val utf8String = (u2 >> bytes) ^^ add1 { raw => pool => raw.toUTF8String } + val utf8String = (u2 >> bytes) ^^ add1 { raw => pool => raw.toUTF8StringAndBytes } val intConstant = u4 ^^ add1 { x => pool => x } val floatConstant = bytes(4) ^^ add1 { raw => pool => "Float: TODO" } val longConstant = bytes(8) ^^ add2 { raw => pool => raw.toLong } @@ -119,9 +130,32 @@ object ClassFileParser extends ByteCodeReader { val interfaces = u2 >> u2.times + // bytes are parametrizes by the length, declared in u4 section val attribute = u2 ~ (u4 >> bytes) ^~^ Attribute + // parse attributes u2 times val attributes = u2 >> attribute.times + // parse runtime-visible annotations + abstract class ElementValue + case class AnnotationElement(elementNameIndex: Int, elementValue: ElementValue) + case class ConstValueIndex(index: Int) extends ElementValue + case class EnumConstValue(typeNameIndex: Int, constNameIndex: Int) extends ElementValue + case class ClassInfoIndex(index: Int) extends ElementValue + case class Annotation(typeIndex: Int, elementValuePairs: Seq[AnnotationElement]) extends ElementValue + case class ArrayValue(values: Seq[ElementValue]) extends ElementValue + + def element_value: Parser[ElementValue] = u1 >> { + case 'B'|'C'|'D'|'F'|'I'|'J'|'S'|'Z'|'s' => u2 ^^ ConstValueIndex + case 'e' => u2 ~ u2 ^~^ EnumConstValue + case 'c' => u2 ^^ ClassInfoIndex + case '@' => annotation //nested annotation + case '[' => u2 >> element_value.times ^^ ArrayValue + } + + val element_value_pair = u2 ~ element_value ^~^ AnnotationElement + val annotation: Parser[Annotation] = u2 ~ (u2 >> element_value_pair.times) ^~^ Annotation + val annotations = u2 >> annotation.times + val field = u2 ~ u2 ~ u2 ~ attributes ^~~~^ Field val fields = u2 >> field.times @@ -153,9 +187,20 @@ case class ClassFile( def superClass = constant(header.superClassIndex) def interfaces = header.interfaces.map(constant) - def constant(index : Int) = header.constants(index) + def constant(index : Int) = header.constants(index) match { + case StringBytesPair(str, _) => str + case z => z + } + + def constantWrapped(index: Int) = header.constants(index) + + def attribute(name : String) = attributes.find {attrib => constant(attrib.nameIndex) == name } + + val RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations" + def annotations = (attributes.find(attr => constant(attr.nameIndex) == RUNTIME_VISIBLE_ANNOTATIONS) + .map(attr => ClassFileParser.parseAnnotations(attr.byteCode))) - def attribute(name : String) = attributes.find { attrib => constant(attrib.nameIndex) == name } + def annotation(name: String) = annotations.flatMap(seq => seq.find(annot => constant(annot.typeIndex) == name)) } case class Attribute(nameIndex : Int, byteCode : ByteCode) diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala index a97494ed6d..e0f95c8bbb 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala @@ -55,7 +55,7 @@ object ScalaSigAttributeParsers extends ByteCodeReader { val symtab = nat >> entry.times val scalaSig = nat ~ nat ~ symtab ^~~^ ScalaSig - val utf8 = read(_ toUTF8String) + val utf8 = read(x => x.toUTF8StringAndBytes.string) val longValue = read(_ toLong) } @@ -164,21 +164,21 @@ object ScalaSigEntryParsers extends RulesWithState with MemoisableRules { * | 5 ALIASsym len_Nat SymbolInfo * | 6 CLASSsym len_Nat SymbolInfo [thistype_Ref] * | 7 MODULEsym len_Nat SymbolInfo - * | 8 VALsym len_Nat [defaultGetter_Ref] SymbolInfo [alias_Ref] + * | 8 VALsym len_Nat [defaultGetter_Ref /* no longer needed*/] SymbolInfo [alias_Ref] * | 9 EXTref len_Nat name_Ref [owner_Ref] * | 10 EXTMODCLASSref len_Nat name_Ref [owner_Ref] * | 11 NOtpe len_Nat * | 12 NOPREFIXtpe len_Nat * | 13 THIStpe len_Nat sym_Ref * | 14 SINGLEtpe len_Nat type_Ref sym_Ref - * | 15 CONSTANTtpe len_Nat type_Ref constant_Ref + * | 15 CONSTANTtpe len_Nat constant_Ref * | 16 TYPEREFtpe len_Nat type_Ref sym_Ref {targ_Ref} * | 17 TYPEBOUNDStpe len_Nat tpe_Ref tpe_Ref * | 18 REFINEDtpe len_Nat classsym_Ref {tpe_Ref} * | 19 CLASSINFOtpe len_Nat classsym_Ref {tpe_Ref} * | 20 METHODtpe len_Nat tpe_Ref {sym_Ref} * | 21 POLYTtpe len_Nat tpe_Ref {sym_Ref} - * | 22 IMPLICITMETHODtpe len_Nat tpe_Ref {tpe_Ref} + * | 22 IMPLICITMETHODtpe len_Nat tpe_Ref {sym_Ref} /* no longer needed */ * | 52 SUPERtpe len_Nat tpe_Ref tpe_Ref * | 24 LITERALunit len_Nat * | 25 LITERALboolean len_Nat value_Long @@ -195,13 +195,12 @@ object ScalaSigEntryParsers extends RulesWithState with MemoisableRules { * | 36 LITERALenum len_Nat sym_Ref * | 40 SYMANNOT len_Nat sym_Ref AnnotInfoBody * | 41 CHILDREN len_Nat sym_Ref {sym_Ref} - * | 42 ANNOTATEDtpe len_Nat [sym_Ref] tpe_Ref {annotinfo_Ref} + * | 42 ANNOTATEDtpe len_Nat [sym_Ref /* no longer needed */] tpe_Ref {annotinfo_Ref} * | 43 ANNOTINFO len_Nat AnnotInfoBody * | 44 ANNOTARGARRAY len_Nat {constAnnotArg_Ref} * | 47 DEBRUIJNINDEXtpe len_Nat level_Nat index_Nat * | 48 EXISTENTIALtpe len_Nat type_Ref {symbol_Ref} */ - val noSymbol = 3 -^ NoSymbol val typeSymbol = symbolEntry(4) ^^ TypeSymbol as "typeSymbol" val aliasSymbol = symbolEntry(5) ^^ AliasSymbol as "alias" diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala index da268f4e44..26b01634f5 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala @@ -16,6 +16,7 @@ import java.io.{PrintStream, ByteArrayOutputStream} import java.util.regex.Pattern import scala.tools.scalap.scalax.util.StringUtil +import reflect.NameTransformer class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { import stream._ @@ -24,13 +25,24 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { case class TypeFlags(printRep: Boolean) - def printSymbol(symbol: Symbol) { printSymbol(0, symbol) } + 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)) { + !(symbol.isPrivate && !printPrivates)) { def indent() {for (i <- 1 to level) print(" ")} + printSymbolAttributes(symbol, true, indent) symbol match { case o: ObjectSymbol => if (!isCaseClassObject(o)) { @@ -50,8 +62,9 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { case a: AliasSymbol => indent printAlias(level, a) - case t: TypeSymbol => - () + case t: TypeSymbol if !t.isParam && !t.name.matches("_\\$\\d+")=> + indent + printTypeSymbol(level, t) case s => } } @@ -82,11 +95,20 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { } 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.isPrivate) print("private ") - else if (symbol.isProtected) print("protected ") if (symbol.isOverride) print("override ") if (symbol.isAbstract) symbol match { case c@(_: ClassSymbol | _: ObjectSymbol) if !c.isTrait => print("abstract ") @@ -98,30 +120,34 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { private def refinementClass(c: ClassSymbol) = c.name == "<refinement>" def printClass(level: Int, c: ClassSymbol) { - 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 _ => it - } - printType(classType) - print(" {") - //Print class selftype - c.selfType match { - case Some(t: Type) => print("\n"); print(" this : " + toString(t) + " =>") - case None => + if (c.name == "<local child>" /*scala.tools.nsc.symtab.StdNames.LOCALCHILD.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 _ => 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") } - print("\n") - printChildren(level, c) - printWithIndent(level, "}\n") } def getPrinterByConstructor(c: ClassSymbol) = { - c.children.find{ - case m : MethodSymbol if m.name == CONSTRUCTOR_NAME => true + c.children.find { + case m: MethodSymbol if m.name == CONSTRUCTOR_NAME => true case _ => false } match { case Some(m: MethodSymbol) => @@ -170,7 +196,7 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { if (res.length > 1) StringUtil.decapitalize(res.substring(0, 1)) else res.toLowerCase }) - def printMethodType(t: Type, printResult: Boolean)(implicit cont : => Unit): Unit = { + def printMethodType(t: Type, printResult: Boolean)(cont: => Unit): Unit = { def _pmt(mt: Type {def resultType: Type; def paramSymbols: Seq[Symbol]}) = { @@ -179,9 +205,9 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { case _ => "^___^" }) - // Printe parameter clauses + // Print parameter clauses print(paramEntries.mkString( - "(" + (mt match {case _ : ImplicitMethodType => "implicit "; case _ => ""}) + "(" + (mt match {case _: ImplicitMethodType => "implicit "; case _ => ""}) , ", ", ")")) // Print result type @@ -215,13 +241,14 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { 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") + x.asInstanceOf[MethodSymbol].name == n + "_$eq") print(if (indexOfSetter > 0) "var " else "val ") } else { print("def ") @@ -234,7 +261,7 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { val nn = processName(name) print(nn) printMethodType(m.infoType, true)( - {if (!m.isDeferred) print(" = { /* compiled code */ }" /* Print body only for non-abstract metods */ )} + {if (!m.isDeferred) print(" = { /* compiled code */ }" /* Print body only for non-abstract methods */ )} ) } print("\n") @@ -248,35 +275,43 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { printChildren(level, a) } - def printAttributes(sym: SymbolInfoSymbol) { - for (attrib <- sym.attributes) printAttribute(attrib) + def printTypeSymbol(level: Int, t: TypeSymbol) { + print("type ") + print(processName(t.name)) + printType(t.infoType) + print("\n") } - def printAttribute(attrib: AttributeInfo) { - printType(attrib.typeRef, "@") + def toString(attrib: AttributeInfo): String = { + val buffer = new StringBuffer + buffer.append(toString(attrib.typeRef, "@")) if (attrib.value.isDefined) { - print("(") - printValue(attrib.value.get) - print(")") + buffer.append("(") + val value = attrib.value.get + val stringVal = value.isInstanceOf[String] + if (stringVal) buffer.append("\"") + buffer.append(valueToString(value)) + if (stringVal) buffer.append("\"") + buffer.append(")") } if (!attrib.values.isEmpty) { - print(" {") + buffer.append(" {") for (name ~ value <- attrib.values) { - print(" val ") - print(processName(name)) - print(" = ") - printValue(value) + buffer.append(" val ") + buffer.append(processName(name)) + buffer.append(" = ") + buffer.append(valueToString(value)) } - printValue(attrib.value) - print(" }") + buffer.append(valueToString(attrib.value)) + buffer.append(" }") } - print(" ") + buffer.toString } - def printValue(value: Any): Unit = value match { - case t: Type => printType(t) + def valueToString(value: Any): String = value match { + case t: Type => toString(t) // TODO string, char, float, etc. - case _ => print(value) + case _ => value.toString } implicit object _tf extends TypeFlags(false) @@ -289,57 +324,72 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { def toString(t: Type)(implicit flags: TypeFlags): String = toString(t, "")(flags) - def toString(t: Type, sep: String)(implicit flags: TypeFlags): String = t match { - case ThisType(symbol) => sep + symbol.path + ".type" - case SingleType(typeRef, symbol) => sep + 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) + 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 = StringUtil.cutSubstring(symbol.path)(".package") //remove package object reference + StringUtil.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 "scala.<byname>" => "=> " + toString(typeArgs.head) - case _ => { - val path = StringUtil.cutSubstring(symbol.path)(".package") //remove package object reference - StringUtil.trimStart(processName(path) + typeArgString(typeArgs), "<empty>.") + case RefinedType(classSym, typeRefs) => sep + typeRefs.map(toString).mkString("", " with ", "") + case ClassInfoType(symbol, typeRefs) => sep + typeRefs.map(toString).mkString(" extends ", " with ", "") + + case ImplicitMethodType(resultType, _) => toString(resultType, sep) + case MethodType(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 TypeBoundsType(lower, upper) => " >: " + toString(lower) + " <: " + toString(upper) - case RefinedType(classSym, typeRefs) => sep + typeRefs.map(toString).mkString("", " with ", "") - case ClassInfoType(symbol, typeRefs) => sep + typeRefs.map(toString).mkString(" extends ", " with ", "") - - case ImplicitMethodType(resultType, _) => toString(resultType, sep) - case MethodType(resultType, _) => toString(resultType, sep) - - case PolyType(typeRef, symbols) => typeParamString(symbols) + toString(typeRef, sep) - case PolyTypeWithCons(typeRef, symbols, cons) => typeParamString(symbols) + cons + toString(typeRef, sep) - case AnnotatedType(typeRef, attribTreeRefs) => toString(typeRef, sep) - case AnnotatedWithSelfType(typeRef, symbol, attribTreeRefs) => toString(typeRef, sep) - //case DeBruijnIndexType(typeLevel, typeIndex) => - 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 AnnotatedWithSelfType(typeRef, symbol, attribTreeRefs) => toString(typeRef, sep) + //case DeBruijnIndexType(typeLevel, typeIndex) => + 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 } - 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 => getVariance(symbol) + processName(symbol.name) + toString(symbol.infoType) + 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 } @@ -356,19 +406,27 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { "\\$minus" -> "-", "\\$eq" -> "=", "\\$less" -> "<", "\\$times" -> "*", "\\$div" -> "/", "\\$bslash" -> "\\\\", "\\$greater" -> ">", "\\$qmark" -> "?", "\\$percent" -> "%", - "\\$amp" -> "&", "\\$colon" -> ":", "\\$u2192" -> "→") - val pattern = Pattern.compile(_syms.keysIterator.foldLeft("")((x, y) => if (x == "") y else x + "|" + y)) + "\\$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 + } + def processName(name: String) = { - val m = pattern.matcher(name) - var temp = name + 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)) } - temp.replaceAll(placeholderPattern, "_") + val result = temp.replaceAll(placeholderPattern, "_") + NameTransformer.decode(result) } } |