@@ -9,22 +9,29 @@ import scala.reflect.internal.Symbols
99import scala .collection .mutable .LinkedHashMap
1010
1111/**
12- * This transformer is responsible for turning lambdas into anonymous classes.
12+ * This transformer is responsible for preparing lambdas for runtime, by either translating to anonymous classes
13+ * or to a tree that will be convereted to invokedynamic by the JVM 1.8+ backend.
14+ *
1315 * The main assumption it makes is that a lambda {args => body} has been turned into
1416 * {args => liftedBody()} where lifted body is a top level method that implements the body of the lambda.
1517 * Currently Uncurry is responsible for that transformation.
1618 *
17- * From a lambda, Delambdafy will create
19+ * From a lambda, Delambdafy will create:
20+ *
21+ * Under -target:jvm-1.7 and below:
22+ *
1823 * 1) a new top level class that
1924 a) has fields and a constructor taking the captured environment (including possibly the "this"
2025 * reference)
2126 * b) an apply method that calls the target method
2227 * c) if needed a bridge method for the apply method
2328 * 2) an instantiation of the newly created class which replaces the lambda
2429 *
25- * TODO the main work left to be done is to plug into specialization. Primarily that means choosing a
26- * specialized FunctionN trait instead of the generic FunctionN trait as a parent and creating the
27- * appropriately named applysp method
30+ * Under -target:jvm-1.8 with GenBCode:
31+ *
32+ * 1) An application of the captured arguments to a fictional symbol representing the lambda factory.
33+ * This will be translated by the backed into an invokedynamic using a bootstrap method in JDK8's `LambdaMetaFactory`.
34+ * The captured arguments include `this` if `liftedBody` is unable to be made STATIC.
2835 */
2936abstract class Delambdafy extends Transform with TypingTransformers with ast.TreeDSL with TypeAdaptingTransformer {
3037 import global ._
@@ -79,6 +86,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
7986 sealed abstract class TransformedFunction
8087 // A class definition for the lambda, an expression instantiating the lambda class
8188 case class DelambdafyAnonClass (lambdaClassDef : ClassDef , newExpr : Tree ) extends TransformedFunction
89+ case class InvokeDynamicLambda (tree : Apply ) extends TransformedFunction
8290
8391 // here's the main entry point of the transform
8492 override def transform (tree : Tree ): Tree = tree match {
@@ -93,6 +101,9 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
93101 lambdaClassDefs(pkg) = lambdaClassDef :: lambdaClassDefs(pkg)
94102
95103 super .transform(newExpr)
104+ case InvokeDynamicLambda (apply) =>
105+ // ... or an invokedynamic call
106+ super .transform(apply)
96107 }
97108 case _ => super .transform(tree)
98109 }
@@ -124,6 +135,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
124135 if (! thisReferringMethods.contains(target))
125136 target setFlag STATIC
126137
138+ val isStatic = target.hasFlag(STATIC )
127139
128140 /**
129141 * Creates the apply method for the anonymous subclass of FunctionN
@@ -199,7 +211,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
199211 val abstractFunctionErasedType = AbstractFunctionClass (formals.length).tpe
200212
201213 // anonymous subclass of FunctionN with an apply method
202- def makeAnonymousClass = {
214+ def makeAnonymousClass : ClassDef = {
203215 val parents = addSerializable(abstractFunctionErasedType)
204216 val funOwner = originalFunction.symbol.owner
205217
@@ -232,7 +244,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
232244 // the Optional proxy that will hold a reference to the 'this'
233245 // object used by the lambda, if any. NoSymbol if there is no this proxy
234246 val thisProxy = {
235- if (target.hasFlag( STATIC ) )
247+ if (isStatic )
236248 NoSymbol
237249 else {
238250 val sym = lambdaClass.newVariable(nme.FAKE_LOCAL_THIS , originalFunction.pos, SYNTHETIC )
@@ -271,22 +283,39 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
271283 val body = members ++ List (constr, applyMethodDef) ++ bridgeMethod
272284
273285 // TODO if member fields are private this complains that they're not accessible
274- ( localTyper.typedPos(decapturedFunction.pos)(ClassDef (lambdaClass, body)).asInstanceOf [ClassDef ], thisProxy)
286+ localTyper.typedPos(decapturedFunction.pos)(ClassDef (lambdaClass, body)).asInstanceOf [ClassDef ]
275287 }
276288
277- val (anonymousClassDef, thisProxy) = makeAnonymousClass
278-
279- pkg.info.decls enter anonymousClassDef.symbol
280-
281- val thisArg = optionSymbol(thisProxy) map (_ => gen.mkAttributedThis(oldClass) setPos originalFunction.pos)
282- val captureArgs = captures map (capture => Ident (capture) setPos originalFunction.pos)
283-
284- val newStat =
285- Typed (New (anonymousClassDef.symbol, (thisArg.toList ++ captureArgs): _* ), TypeTree (abstractFunctionErasedType))
286-
287- val typedNewStat = localTyper.typedPos(originalFunction.pos)(newStat)
289+ val allCaptureArgs : List [Tree ] = {
290+ val thisArg = if (isStatic) Nil else (gen.mkAttributedThis(oldClass) setPos originalFunction.pos) :: Nil
291+ val captureArgs = captures.iterator.map(capture => gen.mkAttributedRef(capture) setPos originalFunction.pos).toList
292+ thisArg ::: captureArgs
293+ }
288294
289- DelambdafyAnonClass (anonymousClassDef, typedNewStat)
295+ val functionalInterface = java8CompatFunctionalInterface(target, originalFunction.tpe)
296+ if (functionalInterface.exists) {
297+ // Create a symbol representing a fictional lambda factory method that accepts the captured
298+ // arguments and returns a Function.
299+ val msym = currentOwner.newMethod(nme.ANON_FUN_NAME , originalFunction.pos, ARTIFACT )
300+ val argTypes : List [Type ] = allCaptureArgs.map(_.tpe)
301+ val params = msym.newSyntheticValueParams(argTypes)
302+ msym.setInfo(MethodType (params, originalFunction.tpe))
303+ val arity = originalFunction.vparams.length
304+
305+ // We then apply this symbol to the captures.
306+ val apply = localTyper.typedPos(originalFunction.pos)(Apply (Ident (msym), allCaptureArgs)).asInstanceOf [Apply ]
307+
308+ // The backend needs to know the target of the lambda and the functional interface in order
309+ // to emit the invokedynamic instruction. We pass this information as tree attachment.
310+ apply.updateAttachment(LambdaMetaFactoryCapable (target, arity, functionalInterface))
311+ InvokeDynamicLambda (apply)
312+ } else {
313+ val anonymousClassDef = makeAnonymousClass
314+ pkg.info.decls enter anonymousClassDef.symbol
315+ val newStat = Typed (New (anonymousClassDef.symbol, allCaptureArgs : _* ), TypeTree (abstractFunctionErasedType))
316+ val typedNewStat = localTyper.typedPos(originalFunction.pos)(newStat)
317+ DelambdafyAnonClass (anonymousClassDef, typedNewStat)
318+ }
290319 }
291320
292321 /**
@@ -436,4 +465,38 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
436465 super .traverse(tree)
437466 }
438467 }
468+
469+ final case class LambdaMetaFactoryCapable (target : Symbol , arity : Int , functionalInterface : Symbol )
470+
471+ // The functional interface that can be used to adapt the lambda target method `target` to the
472+ // given function type. Returns `NoSymbol` if the compiler settings are unsuitable, or `LambdaMetaFactory`
473+ // would be unable to generate the correct implementation (e.g. functions referring to derived value classes)
474+ private def java8CompatFunctionalInterface (target : Symbol , functionType : Type ): Symbol = {
475+ val canUseLambdaMetafactory : Boolean = {
476+ val hasValueClass = exitingErasure {
477+ val methodType : Type = target.info
478+ methodType.exists(_.isInstanceOf [ErasedValueType ])
479+ }
480+ val isTarget18 = settings.target.value.contains(" jvm-1.8" )
481+ settings.isBCodeActive && isTarget18 && ! hasValueClass
482+ }
483+
484+ def functionalInterface : Symbol = {
485+ val sym = functionType.typeSymbol
486+ val pack = currentRun.runDefinitions.Scala_Java8_CompatPackage
487+ val name1 = specializeTypes.specializedFunctionName(sym, functionType.typeArgs)
488+ val paramTps :+ restpe = functionType.typeArgs
489+ val arity = paramTps.length
490+ if (name1.toTypeName == sym.name) {
491+ val returnUnit = restpe.typeSymbol == UnitClass
492+ val functionInterfaceArray =
493+ if (returnUnit) currentRun.runDefinitions.Scala_Java8_CompatPackage_JProcedure
494+ else currentRun.runDefinitions.Scala_Java8_CompatPackage_JFunction
495+ functionInterfaceArray.apply(arity)
496+ } else {
497+ pack.info.decl(name1.toTypeName.prepend(" J" ))
498+ }
499+ }
500+ if (canUseLambdaMetafactory) functionalInterface else NoSymbol
501+ }
439502}
0 commit comments