From 7be7786a67323b5c138eab650c9bf0d23924a688 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 9 Mar 2015 14:20:17 +1000 Subject: SI-8359 Emit invokedynamic for lambdas Suitable lambdas are identified in Delambdafy and marked with such with a tree annotation that includes the data needed by the backend to emit an invokedynamic instruction. GenBCode to rewrite instantiation of such anonymous function classes with an invokedynamic instruction. At this stage, I don't plan to merge the support for this into GenASM. Between these points, the lambda capture is represented as an application of a dummy factory symbol: ``` (captures...) : FunctionN ``` Demo: ``` % wget http://central.maven.org/maven2/org/scala-lang/modules/scala-java8-compat_2.11/0.3.0/scala-java8-compat_2.11-0.3.0.jar % qscala -classpath scala-java8-compat_2.11-0.3.0.jar -Ydelambdafy:method -target:jvm-1.8 -Ybackend:GenBCode Welcome to Scala version 2.11.6-20150309-144147-c91c978c81 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25). Type in expressions to have them evaluated. Type :help for more information. scala> (() => "").getClass res0: Class[_ <: () => String] = class $$Lambda$1/871790326 ``` I have also corrected an error in a previous commit. The newly added symbol test, `isDelambdafyTarget`, needs to check for the `ARTIFACT` flag, as that is what is added to the method by `Uncurry`. --- .../tools/nsc/backend/jvm/BCodeBodyBuilder.scala | 39 +++++++++++++ .../tools/nsc/backend/jvm/BCodeIdiomatic.scala | 3 + .../scala/tools/nsc/transform/Delambdafy.scala | 65 ++++++++++++++++++---- 3 files changed, 96 insertions(+), 11 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 15b014bdd3..8ebe27e61b 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -10,6 +10,7 @@ package backend package jvm import scala.annotation.switch +import scala.reflect.internal.Flags import scala.tools.asm import GenBCode._ @@ -632,6 +633,10 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case _ => abort(s"Cannot instantiate $tpt of kind: $generatedType") } + case Apply(_, args) if app.hasAttachment[delambdafy.LambdaMetaFactoryCapable] => + val attachment = app.attachments.get[delambdafy.LambdaMetaFactoryCapable].get + genLoadArguments(args, paramTKs(app)) + genInvokeDynamicLambda(attachment.target, attachment.arity, attachment.functionalInterface) case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) => val nativeKind = tpeTK(expr) @@ -1280,6 +1285,40 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { def genSynchronized(tree: Apply, expectedType: BType): BType def genLoadTry(tree: Try): BType + def genInvokeDynamicLambda(lambdaTarget: Symbol, arity: Int, functionalInterface: Symbol) { + val isStaticMethod = lambdaTarget.hasFlag(Flags.STATIC) + + val targetHandle = + new asm.Handle(if (lambdaTarget.hasFlag(Flags.STATIC)) asm.Opcodes.H_INVOKESTATIC else asm.Opcodes.H_INVOKEVIRTUAL, + classBTypeFromSymbol(lambdaTarget.owner).internalName, + lambdaTarget.name.toString, + asmMethodType(lambdaTarget).descriptor) + val receiver = if (isStaticMethod) None else Some(lambdaTarget.owner) + val (capturedParams, lambdaParams) = lambdaTarget.paramss.head.splitAt(lambdaTarget.paramss.head.length - arity) + // Requires https://github.com/scala/scala-java8-compat on the runtime classpath + val returnUnit = lambdaTarget.info.resultType.typeSymbol == UnitClass + val functionalInterfaceDesc: String = classBTypeFromSymbol(functionalInterface).descriptor + val desc = (receiver.toList ::: capturedParams).map(sym => toTypeKind(sym.info)).mkString(("("), "", ")") + functionalInterfaceDesc + + // TODO specialization + val constrainedType = new MethodBType(lambdaParams.map(p => toTypeKind(p.tpe)), toTypeKind(lambdaTarget.tpe.resultType)).toASMType + val abstractMethod = functionalInterface.info.decls.find(_.isDeferred).getOrElse(functionalInterface.info.member(nme.apply)) + val methodName = abstractMethod.name.toString + val applyN = { + val mt = asmMethodType(abstractMethod) + mt.toASMType + } + + bc.jmethod.visitInvokeDynamicInsn(methodName, desc, lambdaMetaFactoryBootstrapHandle, + // boostrap args + applyN, targetHandle, constrainedType + ) + } } + val lambdaMetaFactoryBootstrapHandle = + new asm.Handle(asm.Opcodes.H_INVOKESTATIC, + "java/lang/invoke/LambdaMetafactory", "metafactory", + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;") + } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index 9993357eee..8f2a17a2bf 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -412,6 +412,9 @@ abstract class BCodeIdiomatic extends SubComponent { jmethod.instructions.add(node) if (settings.YoptInlinerEnabled) callsitePositions(node) = pos } + final def invokedynamic(owner: String, name: String, desc: String) { + jmethod.visitMethodInsn(Opcodes.INVOKEDYNAMIC, owner, name, desc) + } // can-multi-thread final def goTo(label: asm.Label) { jmethod.visitJumpInsn(Opcodes.GOTO, label) } diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 2d33b35241..315d96d2e9 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -79,6 +79,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre sealed abstract class TransformedFunction // A class definition for the lambda, an expression insantiating 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 +94,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 +128,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 +204,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 +237,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 +276,58 @@ 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 + val useLambdaMetafactory = { + val hasValueClass = exitingErasure { + val methodType: Type = targetMethod(originalFunction).info + methodType.exists(_.isInstanceOf[ErasedValueType]) + } + val isTarget18 = settings.target.value.contains("jvm-1.8") + settings.isBCodeActive && isTarget18 && !hasValueClass + } + + val thisArg = if (isStatic) Nil else (gen.mkAttributedThis(oldClass) setPos originalFunction.pos) :: Nil - pkg.info.decls enter anonymousClassDef.symbol + def anonClass: TransformedFunction = { + val anonymousClassDef = makeAnonymousClass + pkg.info.decls enter anonymousClassDef.symbol + val captureArgs = captures map (capture => Ident(capture) setPos originalFunction.pos) - 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 ++ captureArgs: _*), TypeTree(abstractFunctionErasedType)) - val newStat = - Typed(New(anonymousClassDef.symbol, (thisArg.toList ++ captureArgs): _*), TypeTree(abstractFunctionErasedType)) + val typedNewStat = localTyper.typedPos(originalFunction.pos)(newStat) - val typedNewStat = localTyper.typedPos(originalFunction.pos)(newStat) + DelambdafyAnonClass(anonymousClassDef, typedNewStat) + } - DelambdafyAnonClass(anonymousClassDef, typedNewStat) + if (useLambdaMetafactory) { + val arity = originalFunction.vparams.length + val functionalInterface: Symbol = { + val sym = originalFunction.tpe.typeSymbol + val pack = currentRun.runDefinitions.Scala_Java8_CompatPackage + 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) + } + if (functionalInterface.exists) { + val captureArgs = captures.iterator.map(capture => gen.mkAttributedRef(capture) setPos originalFunction.pos).toList + val allCaptureArgs = thisArg ++: captureArgs + + 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 tree = localTyper.typedPos(originalFunction.pos)(Apply(Ident(msym), allCaptureArgs)).asInstanceOf[Apply] + tree.updateAttachment(LambdaMetaFactoryCapable(targetMethod(originalFunction), arity, functionalInterface)) + InvokeDynamicLambda(tree) + } else anonClass + } else anonClass } /** @@ -436,4 +477,6 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre super.traverse(tree) } } + + final case class LambdaMetaFactoryCapable(target: Symbol, arity: Int, functionalInterface: Symbol) } -- cgit v1.2.3 From d12d59a178c4056fb8ea3cdf4eab5ef453c9a113 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 14 Jun 2014 11:22:29 +0200 Subject: Support specialized method-handle based lambdas ``` scala> (x: Int) => {??? : Int} res2: Int => Int = $$Lambda$1371/1961176822@6ed3ccb2 scala> res2(42) scala.NotImplementedError: an implementation is missing at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225) at .$anonfun$1(:8) at $$Lambda$1371/1961176822.apply$mcII$sp(Unknown Source) ... 33 elided scala> (x: Int, y: Long) => {??? : Int} res4: (Int, Long) => Int = $$Lambda$1382/1796047085@6f8e8894 scala> res4(0, 0L) scala.NotImplementedError: an implementation is missing at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225) at .$anonfun$1(:8) at $$Lambda$1382/1796047085.apply$mcIIJ$sp(Unknown Source) ... 33 elided ``` --- src/compiler/scala/tools/nsc/transform/Delambdafy.scala | 15 ++++++++++----- .../scala/tools/nsc/transform/SpecializeTypes.scala | 17 ++++++++++++++++- src/compiler/scala/tools/nsc/transform/UnCurry.scala | 2 +- 3 files changed, 27 insertions(+), 7 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 315d96d2e9..548f34d9b9 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -308,11 +308,16 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre val functionalInterface: Symbol = { val sym = originalFunction.tpe.typeSymbol val pack = currentRun.runDefinitions.Scala_Java8_CompatPackage - 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) + val name1 = specializeTypes.specializedFunctionName(sym, originalFunction.tpe.typeArgs) + 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 (functionalInterface.exists) { val captureArgs = captures.iterator.map(capture => gen.mkAttributedRef(capture) setPos originalFunction.pos).toList diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 086512677e..4f834d31ff 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -303,6 +303,17 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } + def specializedFunctionName(sym: Symbol, args: List[Type]) = exitingSpecialize { + require(isFunctionSymbol(sym), sym) + val env: TypeEnv = TypeEnv.fromSpecialization(sym, args) + specializedClass.get((sym, env)) match { + case Some(x) => + x.name + case None => + sym.name + } + } + /** Return the specialized name of 'sym' in the given environment. It * guarantees the same result regardless of the map order by sorting * type variables alphabetically. @@ -315,10 +326,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (sym.isClass) env.keySet else specializedTypeVars(sym).intersect(env.keySet) ) + specializedName(sym.name, tvars, env) + } + + private def specializedName(name: Name, tvars: immutable.Set[Symbol], env: TypeEnv): TermName = { val (methparams, others) = tvars.toList sortBy ("" + _.name) partition (_.owner.isMethod) // debuglog("specName(" + sym + ") env: " + env + " tvars: " + tvars) - specializedName(sym.name, methparams map env, others map env) + specializedName(name, methparams map env, others map env) } /** Specialize name for the two list of types. The first one denotes diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 6484d96a52..836ea808ac 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -237,7 +237,7 @@ abstract class UnCurry extends InfoTransform def canUseDelamdafyMethod = ( (inConstructorFlag == 0) // Avoiding synthesizing code prone to SI-6666, SI-8363 by using old-style lambda translation - && !isSpecialized // DelambdafyTransformer currently only emits generic FunctionN-s, use the old style in the meantime + && (!isSpecialized || (settings.target.value == "jvm-1.8")) // DelambdafyTransformer currently only emits generic FunctionN-s, use the old style in the meantime ) if (inlineFunctionExpansion || !canUseDelamdafyMethod) { val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe)) -- cgit v1.2.3 From 8a9efcc93a47fe647926065ab88962cb392a9f71 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 22 Apr 2015 12:37:25 +1000 Subject: Update internal documentation in Delambdafy phase --- src/compiler/scala/tools/nsc/transform/Delambdafy.scala | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 548f34d9b9..79bcf5c655 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._ -- cgit v1.2.3 From 3bf208fd26e0ff272e9aaf9e35446daac4a99901 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 22 Apr 2015 13:05:04 +1000 Subject: Small refactorings and additional comments in Delambdafy --- .../scala/tools/nsc/transform/Delambdafy.scala | 100 +++++++++++---------- 1 file changed, 54 insertions(+), 46 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 79bcf5c655..729fe76791 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -286,60 +286,36 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre localTyper.typedPos(decapturedFunction.pos)(ClassDef(lambdaClass, body)).asInstanceOf[ClassDef] } - val useLambdaMetafactory = { - val hasValueClass = exitingErasure { - val methodType: Type = targetMethod(originalFunction).info - methodType.exists(_.isInstanceOf[ErasedValueType]) - } - val isTarget18 = settings.target.value.contains("jvm-1.8") - settings.isBCodeActive && isTarget18 && !hasValueClass + 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 } - val thisArg = if (isStatic) Nil else (gen.mkAttributedThis(oldClass) setPos originalFunction.pos) :: Nil + 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] - def anonClass: TransformedFunction = { + // 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 captureArgs = captures map (capture => Ident(capture) setPos originalFunction.pos) - - val newStat = - Typed(New(anonymousClassDef.symbol, thisArg ++ captureArgs: _*), TypeTree(abstractFunctionErasedType)) - + val newStat = Typed(New(anonymousClassDef.symbol, allCaptureArgs: _*), TypeTree(abstractFunctionErasedType)) val typedNewStat = localTyper.typedPos(originalFunction.pos)(newStat) - DelambdafyAnonClass(anonymousClassDef, typedNewStat) } - - if (useLambdaMetafactory) { - val arity = originalFunction.vparams.length - val functionalInterface: Symbol = { - val sym = originalFunction.tpe.typeSymbol - val pack = currentRun.runDefinitions.Scala_Java8_CompatPackage - val name1 = specializeTypes.specializedFunctionName(sym, originalFunction.tpe.typeArgs) - 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 (functionalInterface.exists) { - val captureArgs = captures.iterator.map(capture => gen.mkAttributedRef(capture) setPos originalFunction.pos).toList - val allCaptureArgs = thisArg ++: captureArgs - - 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 tree = localTyper.typedPos(originalFunction.pos)(Apply(Ident(msym), allCaptureArgs)).asInstanceOf[Apply] - tree.updateAttachment(LambdaMetaFactoryCapable(targetMethod(originalFunction), arity, functionalInterface)) - InvokeDynamicLambda(tree) - } else anonClass - } else anonClass } /** @@ -491,4 +467,36 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre } 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 + } } -- cgit v1.2.3