summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2015-07-07 16:02:56 -0700
committerLukas Rytz <lukas.rytz@gmail.com>2015-07-23 15:02:24 +0200
commit41b99e25317cc50a6ae6afa1a5527694236528ff (patch)
treeb52851162621679641c9e1c0132fe3e989978521
parentfc1cda21183436181effc87e2df176ddc2d65be9 (diff)
downloadscala-41b99e25317cc50a6ae6afa1a5527694236528ff.tar.gz
scala-41b99e25317cc50a6ae6afa1a5527694236528ff.tar.bz2
scala-41b99e25317cc50a6ae6afa1a5527694236528ff.zip
[backport] Integrate the LMFInvokeDynamic extractor into LambdaMetaFactoryCall
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala115
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala17
3 files changed, 64 insertions, 70 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
index cd36fd8bba..df8dcc690a 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
@@ -104,6 +104,8 @@ object BytecodeUtils {
def isStrictfpMethod(methodNode: MethodNode): Boolean = (methodNode.access & Opcodes.ACC_STRICT) != 0
+ def isReference(t: Type) = t.getSort == Type.OBJECT || t.getSort == Type.ARRAY
+
def nextExecutableInstruction(instruction: AbstractInsnNode, alsoKeep: AbstractInsnNode => Boolean = Set()): Option[AbstractInsnNode] = {
var result = instruction
do { result = result.getNext }
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
index 8c1673b4f2..96455c0e38 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
@@ -173,8 +173,8 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
callsitePosition = callsitePositions.getOrElse(call, NoPosition)
)
- case LMFInvokeDynamic(lmf) =>
- closureInstantiations += lmf
+ case LambdaMetaFactoryCall(indy, samMethodType, implMethod, instantiatedMethodType) =>
+ closureInstantiations += LambdaMetaFactoryCall(indy, samMethodType, implMethod, instantiatedMethodType)
case _ =>
}
@@ -242,7 +242,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
}
final case class LambdaMetaFactoryCall(indy: InvokeDynamicInsnNode, samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type)
- object LMFInvokeDynamic {
+ object LambdaMetaFactoryCall {
private val lambdaMetaFactoryInternalName: InternalName = "java/lang/invoke/LambdaMetafactory"
private val metafactoryHandle = {
@@ -257,69 +257,60 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
new Handle(Opcodes.H_INVOKESTATIC, lambdaMetaFactoryInternalName, altMetafactoryMethodName, altMetafactoryDesc)
}
- private def extractLambdaMetaFactoryCall(indy: InvokeDynamicInsnNode) = {
- if (indy.bsm == metafactoryHandle || indy.bsm == altMetafactoryHandle) indy.bsmArgs match {
- case Array(samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type, xs@_*) =>
- // LambdaMetaFactory performs a number of automatic adaptations when invoking the lambda
- // implementation method (casting, boxing, unboxing, and primitive widening, see Javadoc).
- //
- // The closure optimizer supports only one of those adaptations: it will cast arguments
- // to the correct type when re-writing a closure call to the body method. Example:
- //
- // val fun: String => String = l => l
- // val l = List("")
- // fun(l.head)
- //
- // The samMethodType of Function1 is `(Object)Object`, while the instantiatedMethodType
- // is `(String)String`. The return type of `List.head` is `Object`.
- //
- // The implMethod has the signature `C$anonfun(String)String`.
- //
- // At the closure callsite, we have an `INVOKEINTERFACE Function1.apply (Object)Object`,
- // so the object returned by `List.head` can be directly passed into the call (no cast).
- //
- // The closure object will cast the object to String before passing it to the implMethod.
- //
- // When re-writing the closure callsite to the implMethod, we have to insert a cast.
- //
- // The check below ensures that
- // (1) the implMethod type has the expected singature (captured types plus argument types
- // from instantiatedMethodType)
- // (2) the receiver of the implMethod matches the first captured type
- // (3) all parameters that are not the same in samMethodType and instantiatedMethodType
- // are reference types, so that we can insert casts to perform the same adaptation
- // that the closure object would.
-
- val isStatic = implMethod.getTag == Opcodes.H_INVOKESTATIC
- val indyParamTypes = Type.getArgumentTypes(indy.desc)
- val instantiatedMethodArgTypes = instantiatedMethodType.getArgumentTypes
- val expectedImplMethodType = {
- val paramTypes = (if (isStatic) indyParamTypes else indyParamTypes.tail) ++ instantiatedMethodArgTypes
- Type.getMethodType(instantiatedMethodType.getReturnType, paramTypes: _*)
- }
-
- val isIndyLambda = {
- Type.getType(implMethod.getDesc) == expectedImplMethodType // (1)
- } && {
- isStatic || implMethod.getOwner == indyParamTypes(0).getInternalName // (2)
- } && {
- def isReference(t: Type) = t.getSort == Type.OBJECT || t.getSort == Type.ARRAY
- (samMethodType.getArgumentTypes, instantiatedMethodArgTypes).zipped forall {
- case (samArgType, instArgType) =>
- samArgType == instArgType || isReference(samArgType) && isReference(instArgType) // (3)
+ def unapply(insn: AbstractInsnNode): Option[(InvokeDynamicInsnNode, Type, Handle, Type)] = insn match {
+ case indy: InvokeDynamicInsnNode if indy.bsm == metafactoryHandle || indy.bsm == altMetafactoryHandle =>
+ indy.bsmArgs match {
+ case Array(samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type, xs@_*) => // xs binding because IntelliJ gets confused about _@_*
+ // LambdaMetaFactory performs a number of automatic adaptations when invoking the lambda
+ // implementation method (casting, boxing, unboxing, and primitive widening, see Javadoc).
+ //
+ // The closure optimizer supports only one of those adaptations: it will cast arguments
+ // to the correct type when re-writing a closure call to the body method. Example:
+ //
+ // val fun: String => String = l => l
+ // val l = List("")
+ // fun(l.head)
+ //
+ // The samMethodType of Function1 is `(Object)Object`, while the instantiatedMethodType
+ // is `(String)String`. The return type of `List.head` is `Object`.
+ //
+ // The implMethod has the signature `C$anonfun(String)String`.
+ //
+ // At the closure callsite, we have an `INVOKEINTERFACE Function1.apply (Object)Object`,
+ // so the object returned by `List.head` can be directly passed into the call (no cast).
+ //
+ // The closure object will cast the object to String before passing it to the implMethod.
+ //
+ // When re-writing the closure callsite to the implMethod, we have to insert a cast.
+ //
+ // The check below ensures that
+ // (1) the implMethod type has the expected singature (captured types plus argument types
+ // from instantiatedMethodType)
+ // (2) the receiver of the implMethod matches the first captured type
+ // (3) all parameters that are not the same in samMethodType and instantiatedMethodType
+ // are reference types, so that we can insert casts to perform the same adaptation
+ // that the closure object would.
+
+ val isStatic = implMethod.getTag == Opcodes.H_INVOKESTATIC
+ val indyParamTypes = Type.getArgumentTypes(indy.desc)
+ val instantiatedMethodArgTypes = instantiatedMethodType.getArgumentTypes
+ val expectedImplMethodType = {
+ val paramTypes = (if (isStatic) indyParamTypes else indyParamTypes.tail) ++ instantiatedMethodArgTypes
+ Type.getMethodType(instantiatedMethodType.getReturnType, paramTypes: _*)
}
- }
- if (isIndyLambda) Some(LambdaMetaFactoryCall(indy, samMethodType, implMethod, instantiatedMethodType))
- else None
+ val isIndyLambda = (
+ Type.getType(implMethod.getDesc) == expectedImplMethodType // (1)
+ && (isStatic || implMethod.getOwner == indyParamTypes(0).getInternalName) // (2)
+ && samMethodType.getArgumentTypes.corresponds(instantiatedMethodArgTypes)((samArgType, instArgType) =>
+ samArgType == instArgType || isReference(samArgType) && isReference(instArgType)) // (3)
+ )
- case _ => None
- }
- else None
- }
+ if (isIndyLambda) Some((indy, samMethodType, implMethod, instantiatedMethodType))
+ else None
- def unapply(insn: AbstractInsnNode): Option[LambdaMetaFactoryCall] = insn match {
- case indy: InvokeDynamicInsnNode => extractLambdaMetaFactoryCall(indy)
+ case _ => None
+ }
case _ => None
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
index 3794ee9950..8477f5461a 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -688,7 +688,12 @@ class Inliner[BT <: BTypes](val btypes: BT) {
}
}
- case LMFInvokeDynamic(lmf) =>
+ case _: InvokeDynamicInsnNode if destinationClass == calleeDeclarationClass =>
+ // within the same class, any indy instruction can be inlined
+ Right(true)
+
+ // does the InvokeDynamicInsnNode call LambdaMetaFactory?
+ case LambdaMetaFactoryCall(_, _, implMethod, _) =>
// an indy instr points to a "call site specifier" (CSP) [1]
// - a reference to a bootstrap method [2]
// - bootstrap method name
@@ -734,20 +739,16 @@ class Inliner[BT <: BTypes](val btypes: BT) {
// the implMethod is public, lambdaMetaFactory doesn't use the Lookup object's extended
// capability, and we can safely inline the instruction into a different class.
- val methodRefClass = classBTypeFromParsedClassfile(lmf.implMethod.getOwner)
+ val methodRefClass = classBTypeFromParsedClassfile(implMethod.getOwner)
for {
- (methodNode, methodDeclClassNode) <- byteCodeRepository.methodNode(methodRefClass.internalName, lmf.implMethod.getName, lmf.implMethod.getDesc): Either[OptimizerWarning, (MethodNode, InternalName)]
+ (methodNode, methodDeclClassNode) <- byteCodeRepository.methodNode(methodRefClass.internalName, implMethod.getName, implMethod.getDesc): Either[OptimizerWarning, (MethodNode, InternalName)]
methodDeclClass = classBTypeFromParsedClassfile(methodDeclClassNode)
res <- memberIsAccessible(methodNode.access, methodDeclClass, methodRefClass, destinationClass)
} yield {
res
}
- case indy: InvokeDynamicInsnNode =>
- if (destinationClass == calleeDeclarationClass)
- Right(true) // within the same class, any indy instruction can be inlined
- else
- Left(UnknownInvokeDynamicInstruction)
+ case _: InvokeDynamicInsnNode => Left(UnknownInvokeDynamicInstruction)
case ci: LdcInsnNode => ci.cst match {
case t: asm.Type => classIsAccessible(bTypeForDescriptorOrInternalNameFromClassfile(t.getInternalName), destinationClass)