summaryrefslogtreecommitdiff
path: root/src/repl
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2015-04-17 14:55:04 +1000
committerJason Zaugg <jzaugg@gmail.com>2015-04-17 15:39:37 +1000
commit1aa97cb81b96cbf1e7b7b2cd42dd5bd3d8e5c6dd (patch)
treec8cbc6e20a8c453080a54ab583efb233cf0f8b5f /src/repl
parentb730937493f8f25c985b1a4cf5fcfb7fc16092a4 (diff)
downloadscala-1aa97cb81b96cbf1e7b7b2cd42dd5bd3d8e5c6dd.tar.gz
scala-1aa97cb81b96cbf1e7b7b2cd42dd5bd3d8e5c6dd.tar.bz2
scala-1aa97cb81b96cbf1e7b7b2cd42dd5bd3d8e5c6dd.zip
Make lambda body public rather than using static accessor
Under -Ydelambdafy:method (the basis of the upcoming "indylambda" translation for -target:jvm-1.8), an anonymous function is currently encoded as: 1. a private method containing the lambda's code 2. a public, static accessor method that allows access to 1 from other classes, namely: 3. an anonymous class capturing free variables and implementing the suitable FunctionN interface. In our prototypes of indylambda, we do away with 3, instead deferring creating of this class to JDK8's LambdaMetafactory by way of an invokedynamic instruction at the point of lambda capture. This facility can use a private method as the lambda target; access is mediated by the runtime-provided instance of `java.lang.invoke.MethodHandles.Lookup` that confers the privelages of the lambda capture call site to the generated implementation class. Indeed, The java compiler uses this to emit private lambda body methods. However, there are two Scala specific factors that make this a little troublesome. First, Scala's optimizer may want to inline the lambda capture call site into a new enclosing class. In general, this isn't a safe optimization, as `invokedynamic` instructions have call-site specific semantics. But we will rely the ability to inline like this in order to eliminate closures altogether where possible. Second, to support lambda deserialization, the Java compiler creates a synthetic method `$dersializeLamda$` in each class that captures them, just to be able to get the suitable access to spin up an anoymous class with access to the private method. It only needs to do this for functional interfaces that extends Serializable, which is the exception rather than the rule. But in Scala, *every* function must support serialization, so blindly copying the Java approach will lead to some code bloat. I have prototyped a hybrid approach to these challenges: use the private method directly where it is allowed, but fall back to using the accessor method in a generic lambda deserializer or in call sites that have been inlined into a new enclosing class. However, the most straight forward approach is to simply emit the lambda bodies as public (with an mangled name and with the SYHTNETIC flag) and use them in all cases. That is what is done in this commit. This does moves us one step backwards from the goals of SI-7085, but it doesn't seem sensible to incur the inconvenience from locking down one small part of our house when we don't have a plan or the budget to complete that job. The REPL has some fancy logic to decompile the bodys of lambdas (`:javap -fun C#m`) which needed tweaking to accomodate this change. I haven't tried to make this backwards compatible with the old encoding as `-Ydelambdafy:method` is still experimental.
Diffstat (limited to 'src/repl')
-rw-r--r--src/repl/scala/tools/nsc/interpreter/JavapClass.scala34
1 files changed, 11 insertions, 23 deletions
diff --git a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala
index c80b94bf89..1ccade2172 100644
--- a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala
+++ b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala
@@ -8,6 +8,7 @@ package tools.nsc
package interpreter
import java.lang.{ ClassLoader => JavaClassLoader, Iterable => JIterable }
+import scala.tools.asm.Opcodes
import scala.tools.nsc.util.ScalaClassLoader
import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, PrintWriter, StringWriter, Writer }
import java.util.{ Locale }
@@ -758,32 +759,19 @@ object JavapClass {
import scala.tools.asm.ClassReader
import scala.tools.asm.Opcodes.INVOKESTATIC
import scala.tools.asm.tree.{ ClassNode, MethodInsnNode }
- // the accessor methods invoked statically by the apply of the given closure class
- def accesses(s: String): Seq[(String, String)] = {
- val accessor = """accessor\$\d+""".r
+ def callees(s: String): List[(String, String)] = {
loader classReader s withMethods { ms =>
- ms filter (_.name == "apply") flatMap (_.instructions.toArray.collect {
- case i: MethodInsnNode if i.getOpcode == INVOKESTATIC && when(i.name) { case accessor(_*) => true } => (i.owner, i.name)
- })
+ val nonBridgeApplyMethods = ms filter (_.name == "apply") filter (n => (n.access & Opcodes.ACC_BRIDGE) == 0)
+ val instructions = nonBridgeApplyMethods flatMap (_.instructions.toArray)
+ instructions.collect {
+ case i: MethodInsnNode => (i.owner, i.name)
+ }.toList
}
}
- // get the k.$anonfun for the accessor k.m
- def anonOf(k: String, m: String): String = {
- val res =
- loader classReader k withMethods { ms =>
- ms filter (_.name == m) flatMap (_.instructions.toArray.collect {
- case i: MethodInsnNode if i.getOpcode == INVOKESTATIC && i.name.startsWith("$anonfun") => i.name
- })
- }
- assert(res.size == 1)
- res.head
- }
- // the lambdas invoke accessors that call the anonfuns of interest. Filter k on the k#$anonfuns.
- val ack = accesses(lambda)
- assert(ack.size == 1) // There can be only one.
- ack.head match {
- case (k, _) if target.isModule && !(k endsWith "$") => None
- case (k, m) => Some(s"${k}#${anonOf(k, m)}")
+ callees(lambda) match {
+ case (k, _) :: Nil if target.isModule && !(k endsWith "$") => None
+ case (k, m) :: _ => Some(s"${k}#${m}")
+ case _ => None
}
}
/** Translate the supplied targets to patterns for anonfuns.