scala.tools.scalap.Main.scala Maven / Gradle / Ivy
/* ___ ____ ___ __ ___ ___
** / _// __// _ | / / / _ | / _ \ Scala classfile decoder
** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2013, LAMP/EPFL
** /____/\___/_/ |_/____/_/ |_/_/ http://scala-lang.org/
**
*/
package scala.tools.scalap
import java.io.{ PrintStream, OutputStreamWriter, ByteArrayOutputStream }
import scala.reflect.NameTransformer
import scalax.rules.scalasig._
import scala.tools.nsc.util.{ ClassPath, JavaClassPath }
import scala.tools.util.PathResolver
import ClassPath.DefaultJavaContext
import scala.tools.nsc.io.{ PlainFile, AbstractFile }
/**The main object used to execute scalap on the command-line.
*
* @author Matthias Zenger, Stephane Micheloud, Burak Emir, Ilya Sergey
*/
class Main {
val SCALA_SIG = "ScalaSig"
val SCALA_SIG_ANNOTATION = "Lscala/reflect/ScalaSignature;"
val BYTES_VALUE = "bytes"
val versionMsg = "Scala classfile decoder %s -- %s\n".format(Properties.versionString, Properties.copyrightString)
/**Verbose program run?
*/
var verbose = false
var printPrivates = false
def isScalaFile(bytes: Array[Byte]): Boolean = {
val byteCode = ByteCode(bytes)
val classFile = ClassFileParser.parse(byteCode)
classFile.attribute("ScalaSig").isDefined
}
/**Processes the given Java class file.
*
* @param clazz the class file to be processed.
*/
def processJavaClassFile(clazz: Classfile) {
// construct a new output stream writer
val out = new OutputStreamWriter(Console.out)
val writer = new JavaWriter(clazz, out)
// print the class
writer.printClass
out.flush()
}
def isPackageObjectFile(s: String) = s != null && (s.endsWith(".package") || s == "package")
def parseScalaSignature(scalaSig: ScalaSig, isPackageObject: Boolean) = {
val baos = new ByteArrayOutputStream
val stream = new PrintStream(baos)
val syms = scalaSig.topLevelClasses ++ scalaSig.topLevelObjects
syms.head.parent match {
// Partial match
case Some(p) if (p.name != "") => {
val path = p.path
if (!isPackageObject) {
stream.print("package ");
stream.print(path);
stream.print("\n")
} else {
val i = path.lastIndexOf(".")
if (i > 0) {
stream.print("package ");
stream.print(path.substring(0, i))
stream.print("\n")
}
}
}
case _ =>
}
// Print classes
val printer = new ScalaSigPrinter(stream, printPrivates)
syms foreach (printer printSymbol _)
baos.toString
}
def decompileScala(bytes: Array[Byte], isPackageObject: Boolean): String = {
val byteCode = ByteCode(bytes)
val classFile = ClassFileParser.parse(byteCode)
ScalaSigParser.parse(classFile) match {
case Some(scalaSig) => parseScalaSignature(scalaSig, isPackageObject)
case None => ""
}
}
/** Executes scalap with the given arguments and classpath for the
* class denoted by `classname`.
*/
def process(args: Arguments, path: ClassPath[AbstractFile])(classname: String): Unit = {
// find the classfile
val encName = classname match {
case "scala.AnyRef" => "java.lang.Object"
case _ =>
// we have to encode every fragment of a name separately, otherwise the NameTransformer
// will encode using unicode escaping dot separators as well
// we can afford allocations because this is not a performance critical code
classname.split('.').map(NameTransformer.encode).mkString(".")
}
val cls = path.findClass(encName)
if (cls.isDefined && cls.get.binary.isDefined) {
val cfile = cls.get.binary.get
if (verbose) {
Console.println(Console.BOLD + "FILENAME" + Console.RESET + " = " + cfile.path)
}
val bytes = cfile.toByteArray
if (isScalaFile(bytes)) {
Console.println(decompileScala(bytes, isPackageObjectFile(encName)))
} else {
// construct a reader for the classfile content
val reader = new ByteArrayReader(cfile.toByteArray)
// parse the classfile
val clazz = new Classfile(reader)
processJavaClassFile(clazz)
}
// if the class corresponds to the artificial class scala.Any.
// (see member list in class scala.tool.nsc.symtab.Definitions)
}
else
Console.println("class/object " + classname + " not found.")
}
object EmptyClasspath extends ClassPath[AbstractFile] {
/**
* The short name of the package (without prefix)
*/
def name = ""
def asURLs = Nil
def asClasspathString = ""
val context = DefaultJavaContext
val classes = IndexedSeq()
val packages = IndexedSeq()
val sourcepaths = IndexedSeq()
}
}
object Main extends Main {
/** Prints usage information for scalap. */
def usage() {
Console println """
|Usage: scalap {