summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-02-02 08:23:36 +0000
committerPaul Phillips <paulp@improving.org>2011-02-02 08:23:36 +0000
commit7aca20d8d31790569557c87406a74c0c7377de79 (patch)
tree1301f492080eaec81af6d0280e6dee47c117df09 /src
parentad7fd95c8f6366cc03f5a573b4115641b986803d (diff)
downloadscala-7aca20d8d31790569557c87406a74c0c7377de79.tar.gz
scala-7aca20d8d31790569557c87406a74c0c7377de79.tar.bz2
scala-7aca20d8d31790569557c87406a74c0c7377de79.zip
Added javap to the repl.
so those hypothetical people who use non-sun jvms won't be thrown off kilter. You can give it a file argument or a class name, including classes you just defined in the repl, and it will clean up the repl mangling on the way out. scala> class Bippy { def hackenkraks(x: Float, y: Double) = 5 } defined class Bippy scala> :javap Bippy Compiled from "<console>" public class Bippy extends java.lang.Object implements scala.ScalaObject [...] public int hackenkraks(float, double); Signature: (FD)I Code: Stack=1, Locals=4, Args_size=3 0: iconst_5 1: ireturn scala> :javap scala.collection.Map public interface scala.collection.Map extends scala.collection.Iterable,scala.collection.MapLike,scala.ScalaObject [...] const #7 = Asciz ScalaSig; const #8 = Asciz <A:Ljava/lang/Object;B:Ljava/lang/Object;>Ljava/lang/Object;Lscala/collection/Iterable<Lscala/Tuple2<TA;TB;>;>;Lscala/collection/MapLike<TA;TB;Lscala/collection/Map<TA;TB;>;>;Lscala/ScalaObject;; const #9 = Asciz Lscala/reflect/ScalaSignature;; const #10 = Asciz bytes; const #11 = Asciz [line elided for control chars: possibly a scala signature] scala> :javap java.util.Enumeration Compiled from "Enumeration.java" public interface java.util.Enumeration [...] scala> :javap /scala/trunk/build/quick/classes/compiler/scala/tools/util/Javap.class Compiled from "Javap.scala" public class scala.tools.util.Javap extends java.lang.Object implements scala.ScalaObject [...] No review.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala24
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoop.scala33
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala199
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/package.scala11
-rw-r--r--src/compiler/scala/tools/nsc/io/NullPrintStream.scala12
-rw-r--r--src/compiler/scala/tools/nsc/io/Streamable.scala8
-rw-r--r--src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala2
-rw-r--r--src/compiler/scala/tools/util/Javap.scala104
8 files changed, 280 insertions, 113 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
index 955e558461..39c27cfbb1 100644
--- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
@@ -17,20 +17,28 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader)
extends ClassLoader(parent)
with ScalaClassLoader
{
- def getBytesForClass(name: String): Array[Byte] = {
- def onull[T](x: T): T = if (x == null) throw new ClassNotFoundException(name) else x
+ private def findBytes(name: String, onError: => Array[Byte]): Array[Byte] = {
var file: AbstractFile = root
- val pathParts = name.split("[./]").toList
+ val pathParts = name.split("[./]").toList
- for (dirPart <- pathParts.init)
- file = onull(file.lookupName(dirPart, true))
+ for (dirPart <- pathParts.init) {
+ file = file.lookupName(dirPart, true)
+ if (file == null)
+ return onError
+ }
- file = onull(file.lookupName(pathParts.last+".class", false))
- file.toByteArray
+ file.lookupName(pathParts.last+".class", false) match {
+ case null => onError
+ case file => file.toByteArray
+ }
}
+ override def findBytesForClassName(name: String): Array[Byte] =
+ findBytes(name, super.findBytesForClassName(name))
+
override def findClass(name: String): JClass = {
- val bytes = getBytesForClass(name)
+ val bytes = findBytes(name, throw new ClassNotFoundException(name))
defineClass(name, bytes, 0, bytes.length)
}
}
+
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
index 4c6e14ed6f..a075fe5bb0 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
@@ -8,11 +8,10 @@ package interpreter
import Predef.{ println => _, _ }
import java.io.{ BufferedReader, FileReader, PrintWriter }
-import java.io.IOException
import scala.sys.process.Process
import scala.tools.nsc.interpreter.{ Results => IR }
-import scala.tools.util.SignalManager
+import scala.tools.util.{ SignalManager, Javap }
import scala.annotation.tailrec
import scala.util.control.Exception.{ ignoring }
import scala.collection.mutable.ListBuffer
@@ -197,12 +196,13 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
NoArgs("help", "print this help message", printHelp),
VarArgs("history", "show the history (optional arg: lines to show)", printHistory),
LineArg("h?", "search the history", searchHistory),
+ LineArg("javap", "disassemble a file or class name", javapCommand),
LineArg("keybindings", "show how ctrl-[A-Z] and other keys are bound", keybindingsCommand),
OneArg("load", "load and interpret a Scala file", load),
NoArgs("power", "enable power user mode", powerCmd),
NoArgs("quit", "exit the interpreter", () => Result(false, None)),
NoArgs("replay", "reset execution and replay all previous commands", replay),
- LineArg("sh", "fork a shell and run a command", runShellCmd),
+ LineArg("sh", "fork a shell and run a command", shCommand),
NoArgs("silent", "disable/enable automatic printing of results", verbosity)
)
}
@@ -216,6 +216,18 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
LineArg("wrap", "code to wrap around all executions", wrapCommand)
)
}
+ private def javapCommand(line: String): Result = {
+ if (line == "")
+ return ":javap <filename or classname>"
+ val javap = new Javap(intp.classLoader) {
+ override def defaultPrintWriter = new IMain.ReplStrippingWriter(intp)
+ }
+ val path = intp.pathToFlatName(line)
+ val result = javap guess path
+
+ if (result.isError) "Failed: " + result.value
+ else result.show()
+ }
private def keybindingsCommand(line: String): Result = {
if (in.keyBindings.isEmpty) "Key bindings unavailable."
else {
@@ -336,11 +348,16 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
}
/** fork a shell and run a command */
- def runShellCmd(cmd: String) {
- intp.beQuietDuring { intp.interpret("import _root_.scala.sys.process._") }
- val xs = Process(cmd).lines
- if (xs.nonEmpty)
- intp.bind("stdout", "scala.Stream[String]", xs)
+ def shCommand(cmd: String): Result = {
+ if (cmd == "")
+ return "Usage: sh <command line>"
+
+ intp quietRun "import _root_.scala.sys.process._"
+ val pb = Process(cmd)
+ intp.bind("builder", pb)
+ val stdout = Process(cmd).lines
+ intp.bind("stdout", stdout)
+ ()
}
def withFile(filename: String)(action: File => Unit) {
diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
index 492b79cb84..3c54c18fe1 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -25,45 +25,37 @@ import scala.util.control.Exception.{ ultimately }
import scala.reflect.NameTransformer
import IMain._
-/** <p>
- * An interpreter for Scala code.
- * </p>
- * <p>
- * The main public entry points are <code>compile()</code>,
- * <code>interpret()</code>, and <code>bind()</code>.
- * The <code>compile()</code> method loads a
- * complete Scala file. The <code>interpret()</code> method executes one
- * line of Scala code at the request of the user. The <code>bind()</code>
- * method binds an object to a variable that can then be used by later
- * interpreted code.
- * </p>
- * <p>
- * The overall approach is based on compiling the requested code and then
- * using a Java classloader and Java reflection to run the code
- * and access its results.
- * </p>
- * <p>
- * In more detail, a single compiler instance is used
- * to accumulate all successfully compiled or interpreted Scala code. To
- * "interpret" a line of code, the compiler generates a fresh object that
- * includes the line of code and which has public member(s) to export
- * all variables defined by that code. To extract the result of an
- * interpreted line to show the user, a second "result object" is created
- * which imports the variables exported by the above object and then
- * exports a single member named "$export". To accomodate user expressions
- * that read from variables or methods defined in previous statements, "import"
- * statements are used.
- * </p>
- * <p>
- * This interpreter shares the strengths and weaknesses of using the
- * full compiler-to-Java. The main strength is that interpreted code
- * behaves exactly as does compiled code, including running at full speed.
- * The main weakness is that redefining classes and methods is not handled
- * properly, because rebinding at the Java level is technically difficult.
- * </p>
+/** An interpreter for Scala code.
*
- * @author Moez A. Abdel-Gawad
- * @author Lex Spoon
+ * The main public entry points are compile(), interpret(), and bind().
+ * The compile() method loads a complete Scala file. The interpret() method
+ * executes one line of Scala code at the request of the user. The bind()
+ * method binds an object to a variable that can then be used by later
+ * interpreted code.
+ *
+ * The overall approach is based on compiling the requested code and then
+ * using a Java classloader and Java reflection to run the code
+ * and access its results.
+ *
+ * In more detail, a single compiler instance is used
+ * to accumulate all successfully compiled or interpreted Scala code. To
+ * "interpret" a line of code, the compiler generates a fresh object that
+ * includes the line of code and which has public member(s) to export
+ * all variables defined by that code. To extract the result of an
+ * interpreted line to show the user, a second "result object" is created
+ * which imports the variables exported by the above object and then
+ * exports a single member named "$export". To accomodate user expressions
+ * that read from variables or methods defined in previous statements, "import"
+ * statements are used.
+ *
+ * This interpreter shares the strengths and weaknesses of using the
+ * full compiler-to-Java. The main strength is that interpreted code
+ * behaves exactly as does compiled code, including running at full speed.
+ * The main weakness is that redefining classes and methods is not handled
+ * properly, because rebinding at the Java level is technically difficult.
+ *
+ * @author Moez A. Abdel-Gawad
+ * @author Lex Spoon
*/
class IMain(val settings: Settings, protected val out: PrintWriter) {
intp =>
@@ -275,6 +267,17 @@ class IMain(val settings: Settings, protected val out: PrintWriter) {
private def allReqAndHandlers = prevRequests.toList flatMap (req => req.handlers map (req -> _))
private def importHandlers = allHandlers collect { case x: ImportHandler => x }
+ /** Given a simple repl-defined name, returns the real name of
+ * the class representing it, e.g. for "Bippy" it may return
+ *
+ * $line19.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$Bippy
+ */
+ def pathToFlatName(id: String): String = {
+ requestForIdent(id) match {
+ case Some(req) => req fullFlatName id
+ case _ => id
+ }
+ }
def allDefinedNames = definedNameMap.keys.toList sortBy (_.toString)
def pathToType(id: String): String = pathToName(newTypeName(id))
def pathToTerm(id: String): String = pathToName(newTermName(id))
@@ -484,8 +487,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) {
compileSources(new BatchSourceFile("<script>", code))
- /** Build a request from the user. <code>trees</code> is <code>line</code>
- * after being parsed.
+ /** Build a request from the user. `trees` is `line` after being parsed.
*/
private def buildRequest(line: String, trees: List[Tree]): Request = new Request(line, trees)
@@ -515,16 +517,16 @@ class IMain(val settings: Settings, protected val out: PrintWriter) {
Right(buildRequest(line, trees))
}
- /** <p>
+ /**
* Interpret one line of input. All feedback, including parse errors
* and evaluation results, are printed via the supplied compiler's
* reporter. Values defined are available for future interpreted
* strings.
- * </p>
- * <p>
+ *
+ *
* The return value is whether the line was interpreter successfully,
* e.g. that there were no parse errors.
- * </p>
+ *
*
* @param line ...
* @return ...
@@ -585,8 +587,10 @@ class IMain(val settings: Settings, protected val out: PrintWriter) {
|}
""".stripMargin.format(bindRep.evalName, boundType, boundType)
)
- bindRep.call("set", value)
- interpret("val %s = %s.value".format(name, bindRep.evalPath))
+ bindRep.callOpt("set", value) match {
+ case Some(_) => interpret("val %s = %s.value".format(name, bindRep.evalPath))
+ case _ => DBG("Set failed in bind(%s, %s, %s)".format(name, boundType, value)) ; IR.Error
+ }
}
def quietBind(p: NamedParam): IR.Result = beQuietDuring(bind(p))
@@ -637,12 +641,19 @@ class IMain(val settings: Settings, protected val out: PrintWriter) {
def evalPath = pathTo(evalName)
def printPath = pathTo(printName)
- def call(name: String, args: Any*) =
+ def call(name: String, args: Any*): AnyRef =
evalMethod(name).invoke(evalClass, args.map(_.asInstanceOf[AnyRef]): _*)
+ def callOpt(name: String, args: Any*): Option[AnyRef] =
+ try Some(call(name, args: _*))
+ catch { case ex: Exception =>
+ quietBind("lastException", ex)
+ None
+ }
+
lazy val readRoot = definitions.getModule(readPath) // the outermost wrapper
lazy val evalClass = loadByName(evalPath)
- lazy val evalValue = try Some(call(valueMethod)) catch { case ex: Exception => None }
+ lazy val evalValue = callOpt(valueMethod)
def compile(source: String): Boolean = compileAndSaveRun("<console>", source)
def afterTyper[T](op: => T): T = {
@@ -703,6 +714,12 @@ class IMain(val settings: Settings, protected val out: PrintWriter) {
def fullPath(vname: String) = (
lineRep.readPath + accessPath + ".`%s`".format(vname)
)
+ /** Same as fullpath, but after it has been flattened, so:
+ * $line5.$iw.$iw.$iw.Bippy // fullPath
+ * $line5.$iw$$iw$$iw$Bippy // fullFlatName
+ */
+ def fullFlatName(name: String) =
+ lineRep.readPath + accessPath.replace('.', '$') + "$" + name
/** Code to access a variable with the specified name */
def fullPath(vname: Name): String = fullPath(vname.toString)
@@ -849,7 +866,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) {
}
try {
- val execution = lineManager.set(originalLine)(lineRep call "$export")
+ val execution = lineManager.set(originalLine) {
+ lineRep call "$export"
+ }
execution.await()
execution.state match {
@@ -1054,43 +1073,61 @@ object IMain {
code println postamble
}
}
- class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, null, intp.out) {
- import intp._
-
- /** Truncate a string if it is longer than isettings.maxPrintString */
- private def truncPrintString(str: String): String = {
- val maxpr = isettings.maxPrintString
- val trailer = "..."
- if (!truncationOK || maxpr <= 0 || str.length <= maxpr) str
- else (str take maxpr-3) + trailer
+ trait StrippingWriter {
+ def isStripping: Boolean
+ def stripImpl(str: String): String
+ def strip(str: String): String = if (isStripping) stripImpl(str) else str
+ }
+ trait TruncatingWriter {
+ def maxStringLength: Int
+ def isTruncating: Boolean
+ def truncate(str: String): String = {
+ if (isTruncating && str.length > maxStringLength)
+ (str take maxStringLength - 3) + "..."
+ else str
+ }
+ }
+ abstract class StrippingTruncatingWriter(out: PrintWriter)
+ extends PrintWriter(out)
+ with StrippingWriter
+ with TruncatingWriter {
+ self =>
+
+ def clean(str: String): String = truncate(strip(str))
+ override def write(str: String) = super.write(clean(str))
+ }
+ class ReplStrippingWriter(intp: IMain) extends StrippingTruncatingWriter(intp.out) {
+ import intp._
+ def maxStringLength = isettings.maxPrintString
+ def isStripping = isettings.unwrapStrings
+ def isTruncating = reporter.truncationOK
+
+ def stripImpl(str: String): String = {
+ val cleaned = removeIWPackages(removeLineWrapper(str))
+ var ctrlChars = 0
+ cleaned map { ch =>
+ if (ch.isControl && !ch.isWhitespace) {
+ ctrlChars += 1
+ if (ctrlChars > 5) return "[line elided for control chars: possibly a scala signature]"
+ else '?'
+ }
+ else ch
+ }
}
- /** Clean up a string for output */
- private def clean(str: String) = truncPrintString(
- if (isettings.unwrapStrings) stripWrapperGunk(str)
- else str
- )
+ // The two name forms this is catching are the two sides of this assignment:
+ //
+ // $line3.$read.$iw.$iw.Bippy =
+ // $line3.$read$$iw$$iw$Bippy@4a6a00ca
+ private def removeLineWrapper(s: String) = s.replaceAll("""\$line\d+[./]\$(read|eval|print)[$.]""", "")
+ private def removeIWPackages(s: String) = s.replaceAll("""\$iw[$.]""", "")
+ }
+ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, null, new ReplStrippingWriter(intp)) {
override def printMessage(msg: String) {
- if (totalSilence)
- return
-
- out println clean(msg)
- out.flush()
+ if (intp.totalSilence) ()
+ else super.printMessage(msg)
}
}
-
- import scala.collection.generic.CanBuildFrom
- def partialFlatMap[A, B, CC[X] <: Traversable[X]]
- (coll: CC[A])
- (pf: PartialFunction[A, CC[B]])
- (implicit bf: CanBuildFrom[CC[A], B, CC[B]]) =
- {
- val b = bf(coll)
- for (x <- coll collect pf)
- b ++= x
-
- b.result
- }
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala
index 2847dc53d0..80040be8d0 100644
--- a/src/compiler/scala/tools/nsc/interpreter/package.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/package.scala
@@ -45,17 +45,6 @@ package object interpreter {
private[nsc] def isQuoted(s: String) =
(s.length >= 2) && (s.head == s.last) && ("\"'" contains s.head)
- // The two name forms this is catching are the two sides of this assignment:
- //
- // $line3.$read.$iw.$iw.Bippy = $line3.$read$$iw$$iw$Bippy@4a6a00ca
- private def removeLineWrapper(s: String) = s.replaceAll("""\$line\d+\.\$(read|eval|print)[$.]""", "")
- private def removeIWPackages(s: String) = s.replaceAll("""\$iw[$.]""", "")
-
- /** Heuristically strip interpreter wrapper prefixes
- * from an interpreter output string.
- */
- def stripWrapperGunk(str: String) = removeIWPackages(removeLineWrapper(str))
-
/** Class objects */
def classForName(name: String): Option[JClass] =
try Some(Class forName name)
diff --git a/src/compiler/scala/tools/nsc/io/NullPrintStream.scala b/src/compiler/scala/tools/nsc/io/NullPrintStream.scala
index 5bbdf97f5e..52c7ddc74b 100644
--- a/src/compiler/scala/tools/nsc/io/NullPrintStream.scala
+++ b/src/compiler/scala/tools/nsc/io/NullPrintStream.scala
@@ -22,4 +22,16 @@ object NullPrintStream extends NullPrintStream {
body
}
}
+
+ def sinkingSystemOutAndErr[T](body: => T): T = {
+ val savedOut = System.out
+ val savedErr = System.err
+ System setOut NullPrintStream
+ System setErr NullPrintStream
+ try body
+ finally {
+ System setOut savedOut
+ System setErr savedErr
+ }
+ }
}
diff --git a/src/compiler/scala/tools/nsc/io/Streamable.scala b/src/compiler/scala/tools/nsc/io/Streamable.scala
index c472c82e20..6edca13b42 100644
--- a/src/compiler/scala/tools/nsc/io/Streamable.scala
+++ b/src/compiler/scala/tools/nsc/io/Streamable.scala
@@ -111,11 +111,11 @@ object Streamable {
try f(stream)
finally stream.close()
- def bytes(is: InputStream): Array[Byte] =
- new Bytes { val inputStream = is } toByteArray
+ def bytes(is: => InputStream): Array[Byte] =
+ new Bytes { def inputStream() = is } toByteArray
- def slurp(is: InputStream)(implicit codec: Codec): String =
- new Chars { val inputStream = is } slurp codec
+ def slurp(is: => InputStream)(implicit codec: Codec): String =
+ new Chars { def inputStream() = is } slurp codec
def slurp(url: URL)(implicit codec: Codec): String =
slurp(url.openStream())
diff --git a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
index 3691870179..ee71d04827 100644
--- a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
+++ b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
@@ -65,7 +65,7 @@ trait ScalaClassLoader extends JavaClassLoader {
val url = this.getResource(name)
if (url == null) Array()
- else new io.Streamable.Bytes { def inputStream() = url.openStream } . toByteArray()
+ else io.Streamable.bytes(url.openStream)
}
/** Run the main method of a class to be loaded by this classloader */
diff --git a/src/compiler/scala/tools/util/Javap.scala b/src/compiler/scala/tools/util/Javap.scala
new file mode 100644
index 0000000000..b31683b1f7
--- /dev/null
+++ b/src/compiler/scala/tools/util/Javap.scala
@@ -0,0 +1,104 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package util
+
+import java.io.{ InputStream, PrintWriter, ByteArrayInputStream, FileNotFoundException }
+import java.lang.reflect.{ GenericSignatureFormatError, Method, Constructor }
+import java.lang.{ ClassLoader => JavaClassLoader }
+import scala.tools.nsc.util.ScalaClassLoader
+import scala.tools.nsc.io.{ File, NullPrintStream }
+
+trait JavapResult {
+ type ResultType
+ def isError: Boolean
+ def value: ResultType
+ def show(): Unit
+ // todo
+ // def header(): String
+ // def fields(): List[String]
+ // def methods(): List[String]
+ // def signatures(): List[String]
+}
+class JavapError(msg: String) extends JavapResult {
+ type ResultType = String
+ def isError = true
+ def value = msg
+ def show() = println(msg)
+}
+class JavapSuccess(val value: AnyRef) extends JavapResult {
+ type ResultType = AnyRef
+ def isError = false
+ def show() = value.asInstanceOf[{ def print(): Unit }].print()
+}
+
+class Javap(val loader: ScalaClassLoader) {
+ def this() = this(ScalaClassLoader.getSystemLoader())
+ def defaultPrintWriter = new PrintWriter(System.out, true)
+
+ private val envFieldsToSet = List[(String, Any)](
+ // "showLineAndLocal" -> true,
+ "showDisassembled" -> true,
+ "showVerbose" -> true,
+ "showInternalSigs" -> true
+ )
+ val Env = "sun.tools.javap.JavapEnvironment"
+ val Printer = "sun.tools.javap.JavapPrinter"
+
+ val EnvClass = loader.tryToInitializeClass[AnyRef](Env).orNull
+ val PrinterClass = loader.tryToInitializeClass[AnyRef](Printer).orNull
+ val EnvCtr = EnvClass.getConstructor(List[Class[_]](): _*)
+ val PrinterCtr = PrinterClass.getConstructor(classOf[InputStream], classOf[PrintWriter], EnvClass)
+
+ /** Assume the string is a path and try to find the classfile
+ * it represents.
+ */
+ def tryFile(path: String): Option[Array[Byte]] = {
+ val file = File(
+ if (path.endsWith(".class")) path
+ else path.replace('.', '/') + ".class"
+ )
+ if (!file.exists) None
+ else try Some(file.toByteArray) catch { case x: Exception => None }
+ }
+ /** Assume the string is a fully qualified class name and try to
+ * find the class object it represents.
+ */
+ def tryClass(path: String): Array[Byte] = {
+ val extName = (
+ if (path endsWith ".class") (path dropRight 6).replace('/', '.')
+ else path
+ )
+ loader.findBytesForClassName(extName)
+ }
+
+ def newEnv(): AnyRef = {
+ val env = EnvClass.newInstance()
+ envFieldsToSet foreach { case (name, value) =>
+ val x = EnvClass getDeclaredField name
+ x setAccessible true
+ x.set(env, value.asInstanceOf[AnyRef])
+ }
+ env
+ }
+ def newPrinter(
+ in: InputStream,
+ pw: PrintWriter = defaultPrintWriter,
+ env: AnyRef = newEnv()
+ ): AnyRef = {
+ PrinterCtr.newInstance(in, pw, env)
+ }
+
+ def guess(path: String): JavapResult = {
+ val bytes = tryFile(path) getOrElse tryClass(path)
+ if (bytes.length > 0) new JavapSuccess(newPrinter(new ByteArrayInputStream(bytes)))
+ else new JavapError("Could not find class bytes for '%s'".format(path))
+ }
+}
+
+object Javap extends Javap(ScalaClassLoader.getSystemLoader()) {
+ def apply(path: String): AnyRef = guess(path)
+}