summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSom Snytt <som.snytt@gmail.com>2013-07-15 17:51:44 -0700
committerSom Snytt <som.snytt@gmail.com>2013-07-15 22:37:41 -0700
commit1010a324f20dfa1221f506d67700d748e6ed79d4 (patch)
treee04d13aa2a3a9de997f0c7045cd81bd046500ba9
parent11dcf82910b388046e675d76d277b09b931d5363 (diff)
downloadscala-1010a324f20dfa1221f506d67700d748e6ed79d4.tar.gz
scala-1010a324f20dfa1221f506d67700d748e6ed79d4.tar.bz2
scala-1010a324f20dfa1221f506d67700d748e6ed79d4.zip
SI-7488 REPL javap finds new style delayedEndpoint
The REPL :java -app command is a convenience to locate the body of DelayedInit code. Now it will look for new style delayedEndpoints on the class before it falls back to showing the apply method of the delayedInit$body closure. ``` apm@mara:~/tmp$ skala Welcome to Scala version 2.11.0-20130711-153246-eb1c3137f5 (OpenJDK 64-Bit Server VM, Java 1.7.0_21). Type in expressions to have them evaluated. Type :help for more information. scala> :javap -pv -app delayed.C public final void delayedEndpoint$delayed$C$1(); flags: ACC_PUBLIC, ACC_FINAL Code: stack=2, locals=1, args_size=1 0: getstatic #29 // Field scala/Predef$.MODULE$:Lscala/Predef$; 3: ldc #31 // String this is the initialization code of C 5: invokevirtual #35 // Method scala/Predef$.println:(Ljava/lang/Object;)V 8: return LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Ldelayed/C; LineNumberTable: line 11: 0 scala> :q apm@mara:~/tmp$ rm delayed/*.class apm@mara:~/tmp$ scalac delayed.scala apm@mara:~/tmp$ skala Welcome to Scala version 2.11.0-20130711-153246-eb1c3137f5 (OpenJDK 64-Bit Server VM, Java 1.7.0_21). Type in expressions to have them evaluated. Type :help for more information. scala> :javap -pv -app delayed.C public final java.lang.Object apply(); flags: ACC_PUBLIC, ACC_FINAL Code: stack=2, locals=1, args_size=1 0: getstatic #13 // Field scala/Predef$.MODULE$:Lscala/Predef$; 3: ldc #15 // String this is the initialization code of C 5: invokevirtual #19 // Method scala/Predef$.println:(Ljava/lang/Object;)V 8: getstatic #25 // Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit; 11: areturn LocalVariableTable: Start Length Slot Name Signature 0 12 0 this Ldelayed/C$delayedInit$body; LineNumberTable: line 11: 0 line 10: 8 ```
-rw-r--r--src/repl/scala/tools/nsc/interpreter/JavapClass.scala82
-rw-r--r--test/files/run/repl-javap-app.check39
-rw-r--r--test/files/run/repl-javap-app.scala10
3 files changed, 113 insertions, 18 deletions
diff --git a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala
index ef6f4c2920..49bdd69a8b 100644
--- a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala
+++ b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala
@@ -44,33 +44,78 @@ class JavapClass(
val (flags, upgraded) = upgrade(options)
import flags.{ app, fun, help, raw }
val targets = if (fun && !help) FunFinder(loader, intp).funs(claases) else claases
- if (help || claases.isEmpty) List(JpResult(JavapTool.helper(printWriter)))
- else if (targets.isEmpty) List(JpResult("No anonfuns found."))
- else tool(raw, upgraded)(targets map (claas => claas -> bytesFor(claas, app)))
+ if (help || claases.isEmpty)
+ List(JpResult(JavapTool.helper(printWriter)))
+ else if (targets.isEmpty)
+ List(JpResult("No anonfuns found."))
+ else
+ tool(raw, upgraded)(targets map (claas => targeted(claas, app)))
}
/** Cull our tool options. */
- private def upgrade(options: Seq[String]): (ToolArgs, Seq[String]) = ToolArgs fromArgs options match {
- case (t,s) if s.nonEmpty => (t,s)
- case (t,s) => (t, JavapTool.DefaultOptions)
- }
+ private def upgrade(options: Seq[String]): (ToolArgs, Seq[String]) =
+ ToolArgs fromArgs options match {
+ case (t, s) if s.nonEmpty => (t, s)
+ case (t, s) => (t, JavapTool.DefaultOptions)
+ }
+
+ /** Associate the requested path with a possibly failed or empty array of bytes. */
+ private def targeted(path: String, app: Boolean): (String, Try[Array[Byte]]) =
+ bytesFor(path, app) match {
+ case Success((target, bytes)) => (target, Try(bytes))
+ case f: Failure[_] => (path, Failure(f.exception))
+ }
/** Find bytes. Handle "-", "-app", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). */
private def bytesFor(path: String, app: Boolean) = Try {
def last = intp.get.mostRecentVar // fail if no intp
- def req = if (path == "-") last else {
- val s = path.splitHashMember
- if (s._1.nonEmpty) s._1
- else s._2 getOrElse "#"
+ def req = path match {
+ case "-" => last
+ case HashSplit(prefix, member) =>
+ if (prefix != null) prefix
+ else if (member != null) member
+ else "#"
+ }
+ val targetedBytes = if (app) findAppBody(req) else (req, findBytes(req))
+ if (targetedBytes._2.isEmpty) throw new FileNotFoundException(s"Could not find class bytes for '$path'")
+ targetedBytes
+ }
+
+ private def findAppBody(path: String): (String, Array[Byte]) = {
+ // is this new style delayedEndpoint? then find it.
+ // the name test is naive. could add $mangled path.
+ // assumes only the first match is of interest (because only one endpoint is generated).
+ def findNewStyle(bytes: Array[Byte]) = {
+ import scala.tools.asm.ClassReader
+ import scala.tools.asm.tree.ClassNode
+ import PartialFunction.cond
+ import JavaConverters._
+ val rdr = new ClassReader(bytes)
+ val nod = new ClassNode
+ rdr.accept(nod, 0)
+ //foo/Bar.delayedEndpoint$foo$Bar$1
+ val endpoint = "delayedEndpoint".r.unanchored
+ def isEndPoint(s: String) = (s contains '$') && cond(s) { case endpoint() => true }
+ nod.methods.asScala collectFirst { case m if isEndPoint(m.name) => m.name }
}
- def asAppBody(s: String) = {
- val (cls, fix) = s.splitSuffix
- s"${cls}$$delayedInit$$body${fix}"
+ // try new style, and add foo#delayedEndpoint$bar$1 to filter on the endpoint
+ def asNewStyle(bytes: Array[Byte]) = Some(bytes) filter (_.nonEmpty) flatMap { bs =>
+ findNewStyle(bs) map (n => (s"$path#$n", bs))
}
- def todo = if (app) asAppBody(req) else req
- val bytes = findBytes(todo)
- if (bytes.isEmpty) throw new FileNotFoundException(s"Could not find class bytes for '${path}'")
- else bytes
+ // use old style, and add foo# to filter on apply method
+ def asOldStyle = {
+ def asAppBody(s: String) = {
+ val (cls, fix) = s.splitSuffix
+ s"${cls}$$delayedInit$$body${fix}"
+ }
+ val oldStyle = asAppBody(path)
+ val oldBytes = findBytes(oldStyle)
+ if (oldBytes.nonEmpty) (s"$oldStyle#", oldBytes)
+ else (path, oldBytes)
+ }
+
+ val pathBytes = findBytes(path)
+ asNewStyle(pathBytes) getOrElse asOldStyle
}
def findBytes(path: String): Array[Byte] = tryFile(path) getOrElse tryClass(path)
@@ -496,6 +541,7 @@ object JavapClass {
intp: Option[IMain] = None
) = new JavapClass(loader, printWriter, intp)
+ val HashSplit = "(.*?)(?:#([^#]*))?".r
// We enjoy flexibility in specifying either a fully-qualified class name com.acme.Widget
// or a resource path com/acme/Widget.class; but not widget.out
implicit class MaybeClassLike(val s: String) extends AnyVal {
diff --git a/test/files/run/repl-javap-app.check b/test/files/run/repl-javap-app.check
new file mode 100644
index 0000000000..db1f09b977
--- /dev/null
+++ b/test/files/run/repl-javap-app.check
@@ -0,0 +1,39 @@
+#partest java6
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala> :javap -app MyApp$
+public final void delayedEndpoint$MyApp$1();
+ Code:
+ Stack=2, Locals=1, Args_size=1
+ 0: getstatic #61; //Field scala/Console$.MODULE$:Lscala/Console$;
+ 3: ldc #63; //String Hello, delayed world.
+ 5: invokevirtual #67; //Method scala/Console$.println:(Ljava/lang/Object;)V
+ 8: return
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 9 0 this LMyApp$;
+}
+
+scala>
+#partest !java6
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala> :javap -app MyApp$
+ public final void delayedEndpoint$MyApp$1();
+ flags: ACC_PUBLIC, ACC_FINAL
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: getstatic #61 // Field scala/Console$.MODULE$:Lscala/Console$;
+ 3: ldc #63 // String Hello, delayed world.
+ 5: invokevirtual #67 // Method scala/Console$.println:(Ljava/lang/Object;)V
+ 8: return
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 9 0 this LMyApp$;
+ LineNumberTable:
+ line 5: 0
+}
+
+scala>
diff --git a/test/files/run/repl-javap-app.scala b/test/files/run/repl-javap-app.scala
new file mode 100644
index 0000000000..be04920be1
--- /dev/null
+++ b/test/files/run/repl-javap-app.scala
@@ -0,0 +1,10 @@
+
+import scala.tools.partest.ReplTest
+
+object MyApp extends App {
+ Console println "Hello, delayed world."
+}
+
+object Test extends ReplTest {
+ def code = ":javap -app MyApp$"
+}