summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2015-07-07 14:34:11 +0200
committerLukas Rytz <lukas.rytz@gmail.com>2015-07-23 15:02:24 +0200
commitfc1cda21183436181effc87e2df176ddc2d65be9 (patch)
treeb91dc7f9824e10646c0c20175c0bdd199733a2c4 /src/compiler
parent8f272c0ad2d1435b4032188eb2e4cba641605dd4 (diff)
downloadscala-fc1cda21183436181effc87e2df176ddc2d65be9.tar.gz
scala-fc1cda21183436181effc87e2df176ddc2d65be9.tar.bz2
scala-fc1cda21183436181effc87e2df176ddc2d65be9.zip
[backport] Small refactoring to the closure optimizer
Introduces an extractor `LMFInvokeDynamic` that matches InvokeDynamic instructions that are LambdaMetaFactory calls. The case class `LambdaMetaFactoryCall` holds such an InvokeDynamic instruction. It also holds the bootstrap arguments (samMethodType, implMethod, instantiatedMethodType) so that they can be accessed without casting the indy.bsmArgs. The `closureInstantiations` map in the call graph now stores ClosureInstantiation objects instead of a tuple. This simplifies some code and gets rid of a few casts.
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala101
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala158
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala31
3 files changed, 151 insertions, 139 deletions
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 8abecdb261..8c1673b4f2 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
@@ -9,7 +9,7 @@ package opt
import scala.reflect.internal.util.{NoPosition, Position}
import scala.tools.asm.tree.analysis.{Value, Analyzer, BasicInterpreter}
-import scala.tools.asm.{Opcodes, Type}
+import scala.tools.asm.{Opcodes, Type, Handle}
import scala.tools.asm.tree._
import scala.collection.concurrent
import scala.collection.convert.decorateAsScala._
@@ -24,7 +24,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
val callsites: concurrent.Map[MethodInsnNode, Callsite] = recordPerRunCache(concurrent.TrieMap.empty)
- val closureInstantiations: concurrent.Map[InvokeDynamicInsnNode, (MethodNode, ClassBType)] = recordPerRunCache(concurrent.TrieMap.empty)
+ val closureInstantiations: concurrent.Map[InvokeDynamicInsnNode, ClosureInstantiation] = recordPerRunCache(concurrent.TrieMap.empty)
def addClass(classNode: ClassNode): Unit = {
val classType = classBTypeFromClassNode(classNode)
@@ -33,14 +33,14 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
(calls, closureInits) = analyzeCallsites(m, classType)
} {
calls foreach (callsite => callsites(callsite.callsiteInstruction) = callsite)
- closureInits foreach (indy => closureInstantiations(indy) = (m, classType))
+ closureInits foreach (lmf => closureInstantiations(lmf.indy) = ClosureInstantiation(lmf, m, classType))
}
}
/**
* Returns a list of callsites in the method, plus a list of closure instantiation indy instructions.
*/
- def analyzeCallsites(methodNode: MethodNode, definingClass: ClassBType): (List[Callsite], List[InvokeDynamicInsnNode]) = {
+ def analyzeCallsites(methodNode: MethodNode, definingClass: ClassBType): (List[Callsite], List[LambdaMetaFactoryCall]) = {
case class CallsiteInfo(safeToInline: Boolean, safeToRewrite: Boolean,
annotatedInline: Boolean, annotatedNoInline: Boolean,
@@ -129,7 +129,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
}
val callsites = new collection.mutable.ListBuffer[Callsite]
- val closureInstantiations = new collection.mutable.ListBuffer[InvokeDynamicInsnNode]
+ val closureInstantiations = new collection.mutable.ListBuffer[LambdaMetaFactoryCall]
methodNode.instructions.iterator.asScala foreach {
case call: MethodInsnNode =>
@@ -173,8 +173,8 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
callsitePosition = callsitePositions.getOrElse(call, NoPosition)
)
- case indy: InvokeDynamicInsnNode =>
- if (closureOptimizer.isClosureInstantiation(indy)) closureInstantiations += indy
+ case LMFInvokeDynamic(lmf) =>
+ closureInstantiations += lmf
case _ =>
}
@@ -236,4 +236,91 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
calleeInfoWarning: Option[CalleeInfoWarning]) {
assert(!(safeToInline && safeToRewrite), s"A callee of ${callee.name} can be either safeToInline or safeToRewrite, but not both.")
}
+
+ final case class ClosureInstantiation(lambdaMetaFactoryCall: LambdaMetaFactoryCall, ownerMethod: MethodNode, ownerClass: ClassBType) {
+ override def toString = s"ClosureInstantiation($lambdaMetaFactoryCall, ${ownerMethod.name + ownerMethod.desc}, $ownerClass)"
+ }
+ final case class LambdaMetaFactoryCall(indy: InvokeDynamicInsnNode, samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type)
+
+ object LMFInvokeDynamic {
+ private val lambdaMetaFactoryInternalName: InternalName = "java/lang/invoke/LambdaMetafactory"
+
+ private val metafactoryHandle = {
+ val metafactoryMethodName: String = "metafactory"
+ val metafactoryDesc: String = "(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;"
+ new Handle(Opcodes.H_INVOKESTATIC, lambdaMetaFactoryInternalName, metafactoryMethodName, metafactoryDesc)
+ }
+
+ private val altMetafactoryHandle = {
+ val altMetafactoryMethodName: String = "altMetafactory"
+ val altMetafactoryDesc: String = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"
+ 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)
+ }
+ }
+
+ if (isIndyLambda) Some(LambdaMetaFactoryCall(indy, samMethodType, implMethod, instantiatedMethodType))
+ else None
+
+ case _ => None
+ }
+ else None
+ }
+
+ def unapply(insn: AbstractInsnNode): Option[LambdaMetaFactoryCall] = insn match {
+ case indy: InvokeDynamicInsnNode => extractLambdaMetaFactoryCall(indy)
+ case _ => None
+ }
+ }
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
index 8da209b269..86536ff0d2 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
@@ -8,6 +8,7 @@ package backend.jvm
package opt
import scala.annotation.switch
+import scala.collection.mutable
import scala.reflect.internal.util.NoPosition
import scala.tools.asm.{Handle, Type, Opcodes}
import scala.tools.asm.tree._
@@ -24,106 +25,28 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
import callGraph._
def rewriteClosureApplyInvocations(): Unit = {
- implicit object closureInitOrdering extends Ordering[(InvokeDynamicInsnNode, MethodNode, ClassBType)] {
- // Note: this code is cleaned up in a future commit, no more tuples.
- override def compare(x: (InvokeDynamicInsnNode, MethodNode, ClassBType), y: (InvokeDynamicInsnNode, MethodNode, ClassBType)): Int = {
- val cls = x._3.internalName compareTo y._3.internalName
+ implicit object closureInitOrdering extends Ordering[ClosureInstantiation] {
+ override def compare(x: ClosureInstantiation, y: ClosureInstantiation): Int = {
+ val cls = x.ownerClass.internalName compareTo y.ownerClass.internalName
if (cls != 0) return cls
- val mName = x._2.name compareTo y._2.name
+ val mName = x.ownerMethod.name compareTo y.ownerMethod.name
if (mName != 0) return mName
- val mDesc = x._2.desc compareTo y._2.desc
+ val mDesc = x.ownerMethod.desc compareTo y.ownerMethod.desc
if (mDesc != 0) return mDesc
- def pos(indy: InvokeDynamicInsnNode) = x._2.instructions.indexOf(indy)
- pos(x._1) - pos(y._1)
+ def pos(inst: ClosureInstantiation) = inst.ownerMethod.instructions.indexOf(inst.lambdaMetaFactoryCall.indy)
+ pos(x) - pos(y)
}
}
- val sorted = closureInstantiations.iterator.map({
- case (indy, (methodNode, ownerClass)) => (indy, methodNode, ownerClass)
- }).to[collection.immutable.TreeSet]
+ val sorted = mutable.TreeSet.empty[ClosureInstantiation]
+ sorted ++= closureInstantiations.values
- sorted foreach {
- case (indy, methodNode, ownerClass) =>
- val warnings = rewriteClosureApplyInvocations(indy, methodNode, ownerClass)
- warnings.foreach(w => backendReporting.inlinerWarning(w.pos, w.toString))
- }
- }
-
- private val lambdaMetaFactoryInternalName: InternalName = "java/lang/invoke/LambdaMetafactory"
-
- private val metafactoryHandle = {
- val metafactoryMethodName: String = "metafactory"
- val metafactoryDesc: String = "(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;"
- new Handle(H_INVOKESTATIC, lambdaMetaFactoryInternalName, metafactoryMethodName, metafactoryDesc)
- }
-
- private val altMetafactoryHandle = {
- val altMetafactoryMethodName: String = "altMetafactory"
- val altMetafactoryDesc: String = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"
- new Handle(H_INVOKESTATIC, lambdaMetaFactoryInternalName, altMetafactoryMethodName, altMetafactoryDesc)
- }
-
- def isClosureInstantiation(indy: InvokeDynamicInsnNode): Boolean = {
- (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 == 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: _*)
- }
-
- {
- 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)
- }
- }
-
- case _ =>
- false
- }
+ for (closureInst <- sorted) {
+ val warnings = rewriteClosureApplyInvocations(closureInst)
+ warnings.foreach(w => backendReporting.inlinerWarning(w.pos, w.toString))
}
}
@@ -152,9 +75,10 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
* Stores the values captured by a closure creation into fresh local variables.
* Returns the list of locals holding the captured values.
*/
- private def storeCaptures(indy: InvokeDynamicInsnNode, methodNode: MethodNode): LocalsList = {
+ private def storeCaptures(closureInit: ClosureInstantiation): LocalsList = {
+ val indy = closureInit.lambdaMetaFactoryCall.indy
val capturedTypes = Type.getArgumentTypes(indy.desc)
- val firstCaptureLocal = methodNode.maxLocals
+ val firstCaptureLocal = closureInit.ownerMethod.maxLocals
// This could be optimized: in many cases the captured values are produced by LOAD instructions.
// If the variable is not modified within the method, we could avoid introducing yet another
@@ -165,10 +89,10 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
// This is checked in `isClosureInstantiation`: the types of the captured variables in the indy
// instruction match exactly the corresponding parameter types in the body method.
val localsForCaptures = LocalsList.fromTypes(firstCaptureLocal, capturedTypes, castLoadTypes = _ => None)
- methodNode.maxLocals = firstCaptureLocal + localsForCaptures.size
+ closureInit.ownerMethod.maxLocals = firstCaptureLocal + localsForCaptures.size
- insertStoreOps(indy, methodNode, localsForCaptures)
- insertLoadOps(indy, methodNode, localsForCaptures)
+ insertStoreOps(indy, closureInit.ownerMethod, localsForCaptures)
+ insertLoadOps(indy, closureInit.ownerMethod, localsForCaptures)
localsForCaptures
}
@@ -205,22 +129,24 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
}
}
- def rewriteClosureApplyInvocations(indy: InvokeDynamicInsnNode, methodNode: MethodNode, ownerClass: ClassBType): List[RewriteClosureApplyToClosureBodyFailed] = {
- val lambdaBodyHandle = indy.bsmArgs(1).asInstanceOf[Handle] // safe, checked in isClosureInstantiation
+ def rewriteClosureApplyInvocations(closureInit: ClosureInstantiation): List[RewriteClosureApplyToClosureBodyFailed] = {
+ val lambdaBodyHandle = closureInit.lambdaMetaFactoryCall.implMethod
+ val ownerMethod = closureInit.ownerMethod
+ val ownerClass = closureInit.ownerClass
// Kept as a lazy val to make sure the analysis is only computed if it's actually needed.
// ProdCons is used to identify closure body invocations (see isSamInvocation), but only if the
// callsite has the right name and signature. If the method has no invcation instruction with
// the right name and signature, the analysis is not executed.
- lazy val prodCons = new ProdConsAnalyzer(methodNode, ownerClass.internalName)
+ lazy val prodCons = new ProdConsAnalyzer(ownerMethod, ownerClass.internalName)
// First collect all callsites without modifying the instructions list yet.
// Once we start modifying the instruction list, prodCons becomes unusable.
// A list of callsites and stack heights. If the invocation cannot be rewritten, a warning
// message is stored in the stack height value.
- val invocationsToRewrite: List[(MethodInsnNode, Either[RewriteClosureApplyToClosureBodyFailed, Int])] = methodNode.instructions.iterator.asScala.collect({
- case invocation: MethodInsnNode if isSamInvocation(invocation, indy, prodCons) =>
+ val invocationsToRewrite: List[(MethodInsnNode, Either[RewriteClosureApplyToClosureBodyFailed, Int])] = ownerMethod.instructions.iterator.asScala.collect({
+ case invocation: MethodInsnNode if isSamInvocation(invocation, closureInit.lambdaMetaFactoryCall.indy, prodCons) =>
val bodyAccessible: Either[OptimizerWarning, Boolean] = for {
(bodyMethodNode, declClass) <- byteCodeRepository.methodNode(lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc): Either[OptimizerWarning, (MethodNode, InternalName)]
isAccessible <- inliner.memberIsAccessible(bodyMethodNode.access, classBTypeFromParsedClassfile(declClass), classBTypeFromParsedClassfile(lambdaBodyHandle.getOwner), ownerClass)
@@ -243,17 +169,17 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
// lazy val to make sure locals for captures and arguments are only allocated if there's
// effectively a callsite to rewrite.
lazy val (localsForCapturedValues, argumentLocalsList) = {
- val captureLocals = storeCaptures(indy, methodNode)
+ val captureLocals = storeCaptures(closureInit)
// allocate locals for storing the arguments of the closure apply callsites.
// if there are multiple callsites, the same locals are re-used.
- val argTypes = indy.bsmArgs(0).asInstanceOf[Type].getArgumentTypes // safe, checked in isClosureInstantiation
- val firstArgLocal = methodNode.maxLocals
+ val argTypes = closureInit.lambdaMetaFactoryCall.samMethodType.getArgumentTypes
+ val firstArgLocal = ownerMethod.maxLocals
// The comment in `isClosureInstantiation` explains why we have to introduce casts for
// arguments that have different types in samMethodType and instantiatedMethodType.
val castLoadTypes = {
- val instantiatedMethodType = indy.bsmArgs(2).asInstanceOf[Type]
+ val instantiatedMethodType = closureInit.lambdaMetaFactoryCall.instantiatedMethodType
(argTypes, instantiatedMethodType.getArgumentTypes).zipped map {
case (samArgType, instantiatedArgType) if samArgType != instantiatedArgType =>
// isClosureInstantiation ensures that the two types are reference types, so we don't
@@ -264,7 +190,7 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
}
}
val argLocals = LocalsList.fromTypes(firstArgLocal, argTypes, castLoadTypes)
- methodNode.maxLocals = firstArgLocal + argLocals.size
+ ownerMethod.maxLocals = firstArgLocal + argLocals.size
(captureLocals, argLocals)
}
@@ -274,20 +200,20 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
case (invocation, Right(stackHeight)) =>
// store arguments
- insertStoreOps(invocation, methodNode, argumentLocalsList)
+ insertStoreOps(invocation, ownerMethod, argumentLocalsList)
// drop the closure from the stack
- methodNode.instructions.insertBefore(invocation, new InsnNode(POP))
+ ownerMethod.instructions.insertBefore(invocation, new InsnNode(POP))
// load captured values and arguments
- insertLoadOps(invocation, methodNode, localsForCapturedValues)
- insertLoadOps(invocation, methodNode, argumentLocalsList)
+ insertLoadOps(invocation, ownerMethod, localsForCapturedValues)
+ insertLoadOps(invocation, ownerMethod, argumentLocalsList)
// update maxStack
val capturesStackSize = localsForCapturedValues.size
val invocationStackHeight = stackHeight + capturesStackSize - 1 // -1 because the closure is gone
- if (invocationStackHeight > methodNode.maxStack)
- methodNode.maxStack = invocationStackHeight
+ if (invocationStackHeight > ownerMethod.maxStack)
+ ownerMethod.maxStack = invocationStackHeight
// replace the callsite with a new call to the body method
val bodyOpcode = (lambdaBodyHandle.getTag: @switch) match {
@@ -296,19 +222,19 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
case H_INVOKESPECIAL => INVOKESPECIAL
case H_INVOKEINTERFACE => INVOKEINTERFACE
case H_NEWINVOKESPECIAL =>
- val insns = methodNode.instructions
+ val insns = ownerMethod.instructions
insns.insertBefore(invocation, new TypeInsnNode(NEW, lambdaBodyHandle.getOwner))
insns.insertBefore(invocation, new InsnNode(DUP))
INVOKESPECIAL
}
val isInterface = bodyOpcode == INVOKEINTERFACE
val bodyInvocation = new MethodInsnNode(bodyOpcode, lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc, isInterface)
- methodNode.instructions.insertBefore(invocation, bodyInvocation)
+ ownerMethod.instructions.insertBefore(invocation, bodyInvocation)
val returnType = Type.getReturnType(lambdaBodyHandle.getDesc)
- fixLoadedNothingOrNullValue(returnType, bodyInvocation, methodNode, btypes) // see comment of that method
+ fixLoadedNothingOrNullValue(returnType, bodyInvocation, ownerMethod, btypes) // see comment of that method
- methodNode.instructions.remove(invocation)
+ ownerMethod.instructions.remove(invocation)
// update the call graph
val originalCallsite = callGraph.callsites.remove(invocation)
@@ -318,7 +244,7 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
def bodyMethodIsBeingCompiled = byteCodeRepository.classNodeAndSource(lambdaBodyHandle.getOwner).map(_._2 == CompilationUnit).getOrElse(false)
val bodyMethodCallsite = Callsite(
callsiteInstruction = bodyInvocation,
- callsiteMethod = methodNode,
+ callsiteMethod = ownerMethod,
callsiteClass = ownerClass,
callee = bodyMethod.map({
case (bodyMethodNode, bodyMethodDeclClass) => Callee(
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 4a022b7bc4..3794ee9950 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -456,9 +456,9 @@ class Inliner[BT <: BTypes](val btypes: BT) {
case indy: InvokeDynamicInsnNode =>
callGraph.closureInstantiations.get(indy) match {
- case Some((methodNode, ownerClass)) =>
+ case Some(closureInit) =>
val newIndy = instructionMap(indy).asInstanceOf[InvokeDynamicInsnNode]
- callGraph.closureInstantiations(newIndy) = (callsiteMethod, callsiteClass)
+ callGraph.closureInstantiations(newIndy) = ClosureInstantiation(closureInit.lambdaMetaFactoryCall.copy(indy = newIndy), callsiteMethod, callsiteClass)
case None =>
}
@@ -688,7 +688,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
}
}
- case indy: InvokeDynamicInsnNode =>
+ case LMFInvokeDynamic(lmf) =>
// an indy instr points to a "call site specifier" (CSP) [1]
// - a reference to a bootstrap method [2]
// - bootstrap method name
@@ -734,21 +734,20 @@ 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.
- if (destinationClass == calleeDeclarationClass) {
+ val methodRefClass = classBTypeFromParsedClassfile(lmf.implMethod.getOwner)
+ for {
+ (methodNode, methodDeclClassNode) <- byteCodeRepository.methodNode(methodRefClass.internalName, lmf.implMethod.getName, lmf.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 if (closureOptimizer.isClosureInstantiation(indy)) {
- val implMethod = indy.bsmArgs(1).asInstanceOf[Handle] // safe, checked in isClosureInstantiation
- val methodRefClass = classBTypeFromParsedClassfile(implMethod.getOwner)
- for {
- (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
- }
- } else {
+ else
Left(UnknownInvokeDynamicInstruction)
- }
case ci: LdcInsnNode => ci.cst match {
case t: asm.Type => classIsAccessible(bTypeForDescriptorOrInternalNameFromClassfile(t.getInternalName), destinationClass)