diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2013-07-25 14:01:34 -0700 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2013-07-25 14:01:34 -0700 |
commit | f593364d7b6ebcd7050d7987dc7a5f2d4962f78a (patch) | |
tree | d67ebb99acd30dda80f66b30954fa4add1e5bcf5 /src | |
parent | 56b7c0f8fa6c1037905867aec88520476dbd3baa (diff) | |
parent | 1010a324f20dfa1221f506d67700d748e6ed79d4 (diff) | |
download | scala-f593364d7b6ebcd7050d7987dc7a5f2d4962f78a.tar.gz scala-f593364d7b6ebcd7050d7987dc7a5f2d4962f78a.tar.bz2 scala-f593364d7b6ebcd7050d7987dc7a5f2d4962f78a.zip |
Merge pull request #2736 from som-snytt/issue/7488-chase-delayed-init
SI-7488 REPL javap finds new style delayedEndpoint
Diffstat (limited to 'src')
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/JavapClass.scala | 82 |
1 files changed, 64 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 { |