diff options
author | Lukas Rytz <lukas.rytz@typesafe.com> | 2015-05-04 11:17:59 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@typesafe.com> | 2015-05-04 11:17:59 +0200 |
commit | 6c75bc40c71d8688fb62759b6e1a23e5e0ba5e93 (patch) | |
tree | 2368ed2115504180cea8dd112bd73ec96e62d8af /src/compiler/scala/tools/nsc/transform/Delambdafy.scala | |
parent | 9e29061fb75a71c271c5e0a9824aabe93bc269fe (diff) | |
parent | 3bf208fd26e0ff272e9aaf9e35446daac4a99901 (diff) | |
download | scala-6c75bc40c71d8688fb62759b6e1a23e5e0ba5e93.tar.gz scala-6c75bc40c71d8688fb62759b6e1a23e5e0ba5e93.tar.bz2 scala-6c75bc40c71d8688fb62759b6e1a23e5e0ba5e93.zip |
Merge pull request #4463 from retronym/topic/indylambda-emit-indy
Use LambdaMetafactory where possible for lambda creation.
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/Delambdafy.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Delambdafy.scala | 103 |
1 files changed, 83 insertions, 20 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 45a89ac594..92db57c533 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -9,12 +9,17 @@ import scala.reflect.internal.Symbols import scala.collection.mutable.LinkedHashMap /** - * This transformer is responsible for turning lambdas into anonymous classes. + * This transformer is responsible for preparing lambdas for runtime, by either translating to anonymous classes + * or to a tree that will be convereted to invokedynamic by the JVM 1.8+ backend. + * * The main assumption it makes is that a lambda {args => body} has been turned into * {args => liftedBody()} where lifted body is a top level method that implements the body of the lambda. * Currently Uncurry is responsible for that transformation. * - * From a lambda, Delambdafy will create + * From a lambda, Delambdafy will create: + * + * Under -target:jvm-1.7 and below: + * * 1) a new top level class that a) has fields and a constructor taking the captured environment (including possibly the "this" * reference) @@ -22,9 +27,11 @@ import scala.collection.mutable.LinkedHashMap * c) if needed a bridge method for the apply method * 2) an instantiation of the newly created class which replaces the lambda * - * TODO the main work left to be done is to plug into specialization. Primarily that means choosing a - * specialized FunctionN trait instead of the generic FunctionN trait as a parent and creating the - * appropriately named applysp method + * Under -target:jvm-1.8 with GenBCode: + * + * 1) An application of the captured arguments to a fictional symbol representing the lambda factory. + * This will be translated by the backed into an invokedynamic using a bootstrap method in JDK8's `LambdaMetaFactory`. + * The captured arguments include `this` if `liftedBody` is unable to be made STATIC. */ abstract class Delambdafy extends Transform with TypingTransformers with ast.TreeDSL with TypeAdaptingTransformer { import global._ @@ -79,6 +86,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre sealed abstract class TransformedFunction // A class definition for the lambda, an expression instantiating the lambda class case class DelambdafyAnonClass(lambdaClassDef: ClassDef, newExpr: Tree) extends TransformedFunction + case class InvokeDynamicLambda(tree: Apply) extends TransformedFunction // here's the main entry point of the transform override def transform(tree: Tree): Tree = tree match { @@ -93,6 +101,9 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre lambdaClassDefs(pkg) = lambdaClassDef :: lambdaClassDefs(pkg) super.transform(newExpr) + case InvokeDynamicLambda(apply) => + // ... or an invokedynamic call + super.transform(apply) } case _ => super.transform(tree) } @@ -124,6 +135,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre if (!thisReferringMethods.contains(target)) target setFlag STATIC + val isStatic = target.hasFlag(STATIC) /** * Creates the apply method for the anonymous subclass of FunctionN @@ -199,7 +211,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre val abstractFunctionErasedType = AbstractFunctionClass(formals.length).tpe // anonymous subclass of FunctionN with an apply method - def makeAnonymousClass = { + def makeAnonymousClass: ClassDef = { val parents = addSerializable(abstractFunctionErasedType) val funOwner = originalFunction.symbol.owner @@ -232,7 +244,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre // the Optional proxy that will hold a reference to the 'this' // object used by the lambda, if any. NoSymbol if there is no this proxy val thisProxy = { - if (target.hasFlag(STATIC)) + if (isStatic) NoSymbol else { 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 val body = members ++ List(constr, applyMethodDef) ++ bridgeMethod // TODO if member fields are private this complains that they're not accessible - (localTyper.typedPos(decapturedFunction.pos)(ClassDef(lambdaClass, body)).asInstanceOf[ClassDef], thisProxy) + localTyper.typedPos(decapturedFunction.pos)(ClassDef(lambdaClass, body)).asInstanceOf[ClassDef] } - val (anonymousClassDef, thisProxy) = makeAnonymousClass - - pkg.info.decls enter anonymousClassDef.symbol - - val thisArg = optionSymbol(thisProxy) map (_ => gen.mkAttributedThis(oldClass) setPos originalFunction.pos) - val captureArgs = captures map (capture => Ident(capture) setPos originalFunction.pos) - - val newStat = - Typed(New(anonymousClassDef.symbol, (thisArg.toList ++ captureArgs): _*), TypeTree(abstractFunctionErasedType)) - - val typedNewStat = localTyper.typedPos(originalFunction.pos)(newStat) + val allCaptureArgs: List[Tree] = { + val thisArg = if (isStatic) Nil else (gen.mkAttributedThis(oldClass) setPos originalFunction.pos) :: Nil + val captureArgs = captures.iterator.map(capture => gen.mkAttributedRef(capture) setPos originalFunction.pos).toList + thisArg ::: captureArgs + } - DelambdafyAnonClass(anonymousClassDef, typedNewStat) + val functionalInterface = java8CompatFunctionalInterface(target, originalFunction.tpe) + if (functionalInterface.exists) { + // Create a symbol representing a fictional lambda factory method that accepts the captured + // arguments and returns a Function. + val msym = currentOwner.newMethod(nme.ANON_FUN_NAME, originalFunction.pos, ARTIFACT) + val argTypes: List[Type] = allCaptureArgs.map(_.tpe) + val params = msym.newSyntheticValueParams(argTypes) + msym.setInfo(MethodType(params, originalFunction.tpe)) + val arity = originalFunction.vparams.length + + // We then apply this symbol to the captures. + val apply = localTyper.typedPos(originalFunction.pos)(Apply(Ident(msym), allCaptureArgs)).asInstanceOf[Apply] + + // The backend needs to know the target of the lambda and the functional interface in order + // to emit the invokedynamic instruction. We pass this information as tree attachment. + apply.updateAttachment(LambdaMetaFactoryCapable(target, arity, functionalInterface)) + InvokeDynamicLambda(apply) + } else { + val anonymousClassDef = makeAnonymousClass + pkg.info.decls enter anonymousClassDef.symbol + val newStat = Typed(New(anonymousClassDef.symbol, allCaptureArgs: _*), TypeTree(abstractFunctionErasedType)) + val typedNewStat = localTyper.typedPos(originalFunction.pos)(newStat) + DelambdafyAnonClass(anonymousClassDef, typedNewStat) + } } /** @@ -436,4 +465,38 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre super.traverse(tree) } } + + final case class LambdaMetaFactoryCapable(target: Symbol, arity: Int, functionalInterface: Symbol) + + // The functional interface that can be used to adapt the lambda target method `target` to the + // given function type. Returns `NoSymbol` if the compiler settings are unsuitable, or `LambdaMetaFactory` + // would be unable to generate the correct implementation (e.g. functions referring to derived value classes) + private def java8CompatFunctionalInterface(target: Symbol, functionType: Type): Symbol = { + val canUseLambdaMetafactory: Boolean = { + val hasValueClass = exitingErasure { + val methodType: Type = target.info + methodType.exists(_.isInstanceOf[ErasedValueType]) + } + val isTarget18 = settings.target.value.contains("jvm-1.8") + settings.isBCodeActive && isTarget18 && !hasValueClass + } + + def functionalInterface: Symbol = { + val sym = functionType.typeSymbol + val pack = currentRun.runDefinitions.Scala_Java8_CompatPackage + val name1 = specializeTypes.specializedFunctionName(sym, functionType.typeArgs) + val paramTps :+ restpe = functionType.typeArgs + val arity = paramTps.length + if (name1.toTypeName == sym.name) { + val returnUnit = restpe.typeSymbol == UnitClass + val functionInterfaceArray = + if (returnUnit) currentRun.runDefinitions.Scala_Java8_CompatPackage_JProcedure + else currentRun.runDefinitions.Scala_Java8_CompatPackage_JFunction + functionInterfaceArray.apply(arity) + } else { + pack.info.decl(name1.toTypeName.prepend("J")) + } + } + if (canUseLambdaMetafactory) functionalInterface else NoSymbol + } } |