diff options
Diffstat (limited to 'src')
5 files changed, 101 insertions, 12 deletions
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) } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 756ed870ca..98fd8e2361 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -1513,6 +1513,10 @@ trait Definitions extends api.StandardDefinitions { def isPolymorphicSignature(sym: Symbol) = PolySigMethods(sym) private lazy val PolySigMethods: Set[Symbol] = Set[Symbol](MethodHandle.info.decl(sn.Invoke), MethodHandle.info.decl(sn.InvokeExact)).filter(_.exists) + + lazy val Scala_Java8_CompatPackage = rootMirror.getPackageIfDefined("scala.compat.java8") + lazy val Scala_Java8_CompatPackage_JFunction = (0 to MaxTupleArity).toArray map (i => getMemberIfDefined(Scala_Java8_CompatPackage.moduleClass, TypeName("JFunction" + i))) + lazy val Scala_Java8_CompatPackage_JProcedure = (0 to MaxTupleArity).toArray map (i => getMemberIfDefined(Scala_Java8_CompatPackage.moduleClass, TypeName("JProcedure" + i))) } } } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 4a39712ad7..a96948cb69 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -794,7 +794,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isAnonymousFunction = isSynthetic && (name containsName tpnme.ANON_FUN_NAME) final def isDelambdafyFunction = isSynthetic && (name containsName tpnme.DELAMBDAFY_LAMBDA_CLASS_NAME) - final def isDelambdafyTarget = isSynthetic && isMethod && (name containsName tpnme.ANON_FUN_NAME) + final def isDelambdafyTarget = isArtifact && isMethod && (name containsName tpnme.ANON_FUN_NAME) final def isDefinedInPackage = effectiveOwner.isPackageClass final def needsFlatClasses = phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass |