summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2015-09-24 12:54:19 +1000
committerJason Zaugg <jzaugg@gmail.com>2015-09-24 12:54:19 +1000
commit58a5c5a57c1ada086c4e728586537d64dca765ef (patch)
tree54bf3c3c75a1911f7a570ab16831d71c3fbd4ef4
parentdf704ef7389ff6265afc1278783489ee97ee4ce7 (diff)
parentb0b5b09f90b84696695538a42e7b7ff36555c0f9 (diff)
downloadscala-58a5c5a57c1ada086c4e728586537d64dca765ef.tar.gz
scala-58a5c5a57c1ada086c4e728586537d64dca765ef.tar.bz2
scala-58a5c5a57c1ada086c4e728586537d64dca765ef.zip
Merge pull request #4763 from lrytz/inlineDeserializeLambda
Add $deserializeLambda$ when inlining an indyLambda into a class
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala5
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala54
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala26
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala14
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/analysis/Analyzers.scala48
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala136
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala18
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala9
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala2
-rw-r--r--test/files/run/inlineAddDeserializeLambda.scala20
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala2
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala2
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala2
17 files changed, 203 insertions, 151 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
index e5eb0b79d5..a42332f7f2 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
@@ -11,6 +11,7 @@ package jvm
import scala.annotation.switch
import scala.reflect.internal.Flags
+import java.lang.invoke.LambdaMetafactory
import scala.tools.asm
import GenBCode._
@@ -1303,7 +1304,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val samName = sam.name.toString
val samMethodType = asmMethodType(sam).toASMType
- val flags = 3 // TODO 2.12.x Replace with LambdaMetafactory.FLAG_SERIALIZABLE | LambdaMetafactory.FLAG_MARKERS
+ val flags = LambdaMetafactory.FLAG_SERIALIZABLE | LambdaMetafactory.FLAG_MARKERS
val ScalaSerializable = classBTypeFromSymbol(definitions.SerializableClass).toASMType
bc.jmethod.visitInvokeDynamicInsn(samName, invokedType, lambdaMetaFactoryBootstrapHandle,
@@ -1315,7 +1316,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
/* markerInterfaces[0] = */ ScalaSerializable,
/* bridgeCount = */ 0.asInstanceOf[AnyRef]
)
- indyLambdaHosts += this.claszSymbol
+ indyLambdaHosts += cnode.name
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index 0f381a4325..bc3bdfc6ba 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -683,60 +683,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
new java.lang.Long(id)
).visitEnd()
}
-
- /**
- * Add:
- * private static java.util.Map $deserializeLambdaCache$ = null
- * private static Object $deserializeLambda$(SerializedLambda l) {
- * var cache = $deserializeLambdaCache$
- * if (cache eq null) {
- * cache = new java.util.HashMap()
- * $deserializeLambdaCache$ = cache
- * }
- * return scala.runtime.LambdaDeserializer.deserializeLambda(MethodHandles.lookup(), cache, l);
- * }
- */
- def addLambdaDeserialize(clazz: Symbol, jclass: asm.ClassVisitor): Unit = {
- val cw = jclass
- import scala.tools.asm.Opcodes._
-
- // Need to force creation of BTypes for these as `getCommonSuperClass` is called on
- // automatically computing the max stack size (`visitMaxs`) during method writing.
- javaUtilHashMapReference
- javaUtilMapReference
-
- cw.visitInnerClass("java/lang/invoke/MethodHandles$Lookup", "java/lang/invoke/MethodHandles", "Lookup", ACC_PUBLIC + ACC_FINAL + ACC_STATIC)
-
- {
- val fv = cw.visitField(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambdaCache$", "Ljava/util/Map;", null, null)
- fv.visitEnd()
- }
-
- {
- val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", "(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;", null, null)
- mv.visitCode()
- // javaBinaryName returns the internal name of a class. Also used in BTypesFromsymbols.classBTypeFromSymbol.
- mv.visitFieldInsn(GETSTATIC, clazz.javaBinaryName.toString, "$deserializeLambdaCache$", "Ljava/util/Map;")
- mv.visitVarInsn(ASTORE, 1)
- mv.visitVarInsn(ALOAD, 1)
- val l0 = new asm.Label()
- mv.visitJumpInsn(IFNONNULL, l0)
- mv.visitTypeInsn(NEW, "java/util/HashMap")
- mv.visitInsn(DUP)
- mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V", false)
- mv.visitVarInsn(ASTORE, 1)
- mv.visitVarInsn(ALOAD, 1)
- mv.visitFieldInsn(PUTSTATIC, clazz.javaBinaryName.toString, "$deserializeLambdaCache$", "Ljava/util/Map;")
- mv.visitLabel(l0)
- mv.visitFieldInsn(GETSTATIC, "scala/runtime/LambdaDeserializer$", "MODULE$", "Lscala/runtime/LambdaDeserializer$;")
- mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false)
- mv.visitVarInsn(ALOAD, 1)
- mv.visitVarInsn(ALOAD, 0)
- mv.visitMethodInsn(INVOKEVIRTUAL, "scala/runtime/LambdaDeserializer$", "deserializeLambda", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/util/Map;Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;", false)
- mv.visitInsn(ARETURN)
- mv.visitEnd()
- }
- }
} // end of trait BCClassGen
/* functionality for building plain and mirror classes */
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index 9875ade113..b22fdf7c2c 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -67,8 +67,6 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
var isCZStaticModule = false
var isCZRemote = false
- protected val indyLambdaHosts = collection.mutable.Set[Symbol]()
-
/* ---------------- idiomatic way to ask questions to typer ---------------- */
def paramTKs(app: Apply): List[BType] = {
@@ -127,10 +125,10 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
val shouldAddLambdaDeserialize = (
settings.target.value == "jvm-1.8"
&& settings.Ydelambdafy.value == "method"
- && indyLambdaHosts.contains(claszSymbol))
+ && indyLambdaHosts.contains(cnode.name))
if (shouldAddLambdaDeserialize)
- addLambdaDeserialize(claszSymbol, cnode)
+ backendUtils.addLambdaDeserialize(cnode)
addInnerClassesASM(cnode, innerClassBufferASM.toList)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index aff2d2d8c9..f1b515910f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -7,6 +7,7 @@ package scala.tools.nsc
package backend.jvm
import scala.annotation.switch
+import scala.collection.{mutable, concurrent}
import scala.collection.concurrent.TrieMap
import scala.reflect.internal.util.Position
import scala.tools.asm
@@ -14,7 +15,7 @@ import asm.Opcodes
import scala.tools.asm.tree._
import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo}
import scala.tools.nsc.backend.jvm.BackendReporting._
-import scala.tools.nsc.backend.jvm.analysis.Analyzers
+import scala.tools.nsc.backend.jvm.analysis.BackendUtils
import scala.tools.nsc.backend.jvm.opt._
import scala.collection.convert.decorateAsScala._
import scala.tools.nsc.settings.ScalaSettings
@@ -30,6 +31,8 @@ import scala.tools.nsc.settings.ScalaSettings
abstract class BTypes {
import BTypes.InternalName
+ val backendUtils: BackendUtils[this.type]
+
// Some core BTypes are required here, in class BType, where no Global instance is available.
// The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual
// implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol.
@@ -51,8 +54,6 @@ abstract class BTypes {
val callGraph: CallGraph[this.type]
- val analyzers: Analyzers[this.type]
-
val backendReporting: BackendReporting
// Allows to define per-run caches here and in the CallGraph component, which don't have a global
@@ -72,19 +73,19 @@ abstract class BTypes {
* Concurrent because stack map frames are computed when in the class writer, which might run
* on multiple classes concurrently.
*/
- val classBTypeFromInternalName: collection.concurrent.Map[InternalName, ClassBType] = recordPerRunCache(TrieMap.empty)
+ val classBTypeFromInternalName: concurrent.Map[InternalName, ClassBType] = recordPerRunCache(TrieMap.empty)
/**
* Store the position of every MethodInsnNode during code generation. This allows each callsite
* in the call graph to remember its source position, which is required for inliner warnings.
*/
- val callsitePositions: collection.concurrent.Map[MethodInsnNode, Position] = recordPerRunCache(TrieMap.empty)
+ val callsitePositions: concurrent.Map[MethodInsnNode, Position] = recordPerRunCache(TrieMap.empty)
/**
* Contains the internal names of all classes that are defined in Java source files of the current
* compilation run (mixed compilation). Used for more detailed error reporting.
*/
- val javaDefinedClasses: collection.mutable.Set[InternalName] = recordPerRunCache(collection.mutable.Set.empty)
+ val javaDefinedClasses: mutable.Set[InternalName] = recordPerRunCache(mutable.Set.empty)
/**
* Cache, contains methods whose unreachable instructions are eliminated.
@@ -96,14 +97,23 @@ abstract class BTypes {
* This cache allows running dead code elimination whenever an analyzer is used. If the method
* is already optimized, DCE can return early.
*/
- val unreachableCodeEliminated: collection.mutable.Set[MethodNode] = recordPerRunCache(collection.mutable.Set.empty)
+ val unreachableCodeEliminated: mutable.Set[MethodNode] = recordPerRunCache(mutable.Set.empty)
/**
* Cache of methods which have correct `maxLocals` / `maxStack` values assigned. This allows
* invoking `computeMaxLocalsMaxStack` whenever running an analyzer but performing the actual
* computation only when necessary.
*/
- val maxLocalsMaxStackComputed: collection.mutable.Set[MethodNode] = recordPerRunCache(collection.mutable.Set.empty)
+ val maxLocalsMaxStackComputed: mutable.Set[MethodNode] = recordPerRunCache(mutable.Set.empty)
+
+ /**
+ * Classes with indyLambda closure instantiations where the SAM type is serializable (e.g. Scala's
+ * FunctionN) need a `$deserializeLambda$` method. This map contains classes for which such a
+ * method has been generated. It is used during ordinary code generation, as well as during
+ * inlining: when inlining an indyLambda instruction into a class, we need to make sure the class
+ * has the method.
+ */
+ val indyLambdaHosts: mutable.Set[InternalName] = recordPerRunCache(mutable.Set.empty)
/**
* Obtain the BType for a type descriptor or internal name. For class descriptors, the ClassBType
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index df34326bbf..79b5e6a2fb 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -7,7 +7,7 @@ package scala.tools.nsc
package backend.jvm
import scala.tools.asm
-import scala.tools.nsc.backend.jvm.analysis.Analyzers
+import scala.tools.nsc.backend.jvm.analysis.BackendUtils
import scala.tools.nsc.backend.jvm.opt._
import scala.tools.nsc.backend.jvm.BTypes._
import BackendReporting._
@@ -33,6 +33,8 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
val bCodeAsmCommon: BCodeAsmCommon[global.type] = new BCodeAsmCommon(global)
import bCodeAsmCommon._
+ val backendUtils: BackendUtils[this.type] = new BackendUtils(this)
+
// Why the proxy, see documentation of class [[CoreBTypes]].
val coreBTypes = new CoreBTypesProxy[this.type](this)
import coreBTypes._
@@ -49,8 +51,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
val callGraph: CallGraph[this.type] = new CallGraph(this)
- val analyzers: Analyzers[this.type] = new Analyzers(this)
-
val backendReporting: BackendReporting = new BackendReportingImpl(global)
final def initializeCoreBTypes(): Unit = {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
index 00ca096e59..48e43c74f4 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
@@ -206,12 +206,14 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] {
def boxedClasses: Set[ClassBType]
- def RT_NOTHING : ClassBType
- def RT_NULL : ClassBType
-
- def ObjectReference : ClassBType
- def jlCloneableReference : ClassBType
- def jioSerializableReference : ClassBType
+ def RT_NOTHING: ClassBType
+ def RT_NULL : ClassBType
+
+ def ObjectReference : ClassBType
+ def jlCloneableReference : ClassBType
+ def jioSerializableReference: ClassBType
+ def javaUtilHashMapReference: ClassBType
+ def javaUtilMapReference : ClassBType
}
/**
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/Analyzers.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/Analyzers.scala
deleted file mode 100644
index bb5c6e3820..0000000000
--- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/Analyzers.scala
+++ /dev/null
@@ -1,48 +0,0 @@
-package scala.tools.nsc
-package backend.jvm
-package analysis
-
-import scala.tools.asm.tree.{AbstractInsnNode, MethodNode}
-import scala.tools.asm.tree.analysis.{Frame, BasicInterpreter, Analyzer, Value}
-import scala.tools.nsc.backend.jvm.BTypes._
-import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._
-
-/**
- * This component hosts tools for running ASM analyzers that require access to a `BTypes` instance.
- * In particular, the AsmAnalyzer class runs `computeMaxLocalsMaxStack` on the methodNode to be
- * analyzed. This method in turn lives inside the BTypes assembly because it queries the per-run
- * cache `maxLocalsMaxStackComputed` defined in there.
- */
-class Analyzers[BT <: BTypes](val btypes: BT) {
- import btypes._
-
- /**
- * A wrapper to make ASM's Analyzer a bit easier to use.
- */
- class AsmAnalyzer[V <: Value](methodNode: MethodNode, classInternalName: InternalName, val analyzer: Analyzer[V] = new Analyzer(new BasicInterpreter)) {
- localOpt.computeMaxLocalsMaxStack(methodNode)
- analyzer.analyze(classInternalName, methodNode)
- def frameAt(instruction: AbstractInsnNode): Frame[V] = analyzer.frameAt(instruction, methodNode)
- }
-
- /**
- * See the doc comment on package object `analysis` for a discussion on performance.
- */
- object AsmAnalyzer {
- // jvm limit is 65535 for both number of instructions and number of locals
-
- private def size(method: MethodNode) = method.instructions.size.toLong * method.maxLocals * method.maxLocals
-
- // with the limits below, analysis should not take more than one second
-
- private val nullnessSizeLimit = 5000l * 600l * 600l // 5000 insns, 600 locals
- private val basicValueSizeLimit = 9000l * 1000l * 1000l
- private val sourceValueSizeLimit = 8000l * 950l * 950l
-
- def sizeOKForNullness(method: MethodNode): Boolean = size(method) < nullnessSizeLimit
- def sizeOKForBasicValue(method: MethodNode): Boolean = size(method) < basicValueSizeLimit
- def sizeOKForSourceValue(method: MethodNode): Boolean = size(method) < sourceValueSizeLimit
- }
-
- class ProdConsAnalyzer(val methodNode: MethodNode, classInternalName: InternalName) extends AsmAnalyzer(methodNode, classInternalName, new Analyzer(new InitialProducerSourceInterpreter)) with ProdConsAnalyzerImpl
-}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
new file mode 100644
index 0000000000..793235c131
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
@@ -0,0 +1,136 @@
+package scala.tools.nsc
+package backend.jvm
+package analysis
+
+import scala.tools.asm.Label
+import scala.tools.asm.tree._
+import scala.tools.asm.tree.analysis.{Frame, BasicInterpreter, Analyzer, Value}
+import scala.tools.nsc.backend.jvm.BTypes._
+import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._
+import java.lang.invoke.LambdaMetafactory
+import scala.collection.convert.decorateAsJava._
+import scala.collection.convert.decorateAsScala._
+
+/**
+ * This component hosts tools and utilities used in the backend that require access to a `BTypes`
+ * instance.
+ *
+ * One example is the AsmAnalyzer class, which runs `computeMaxLocalsMaxStack` on the methodNode to
+ * be analyzed. This method in turn lives inside the BTypes assembly because it queries the per-run
+ * cache `maxLocalsMaxStackComputed` defined in there.
+ */
+class BackendUtils[BT <: BTypes](val btypes: BT) {
+ import btypes._
+
+ /**
+ * A wrapper to make ASM's Analyzer a bit easier to use.
+ */
+ class AsmAnalyzer[V <: Value](methodNode: MethodNode, classInternalName: InternalName, val analyzer: Analyzer[V] = new Analyzer(new BasicInterpreter)) {
+ localOpt.computeMaxLocalsMaxStack(methodNode)
+ analyzer.analyze(classInternalName, methodNode)
+ def frameAt(instruction: AbstractInsnNode): Frame[V] = analyzer.frameAt(instruction, methodNode)
+ }
+
+ /**
+ * See the doc comment on package object `analysis` for a discussion on performance.
+ */
+ object AsmAnalyzer {
+ // jvm limit is 65535 for both number of instructions and number of locals
+
+ private def size(method: MethodNode) = method.instructions.size.toLong * method.maxLocals * method.maxLocals
+
+ // with the limits below, analysis should not take more than one second
+
+ private val nullnessSizeLimit = 5000l * 600l * 600l // 5000 insns, 600 locals
+ private val basicValueSizeLimit = 9000l * 1000l * 1000l
+ private val sourceValueSizeLimit = 8000l * 950l * 950l
+
+ def sizeOKForNullness(method: MethodNode): Boolean = size(method) < nullnessSizeLimit
+ def sizeOKForBasicValue(method: MethodNode): Boolean = size(method) < basicValueSizeLimit
+ def sizeOKForSourceValue(method: MethodNode): Boolean = size(method) < sourceValueSizeLimit
+ }
+
+ class ProdConsAnalyzer(val methodNode: MethodNode, classInternalName: InternalName) extends AsmAnalyzer(methodNode, classInternalName, new Analyzer(new InitialProducerSourceInterpreter)) with ProdConsAnalyzerImpl
+
+ /**
+ * Add:
+ * private static java.util.Map $deserializeLambdaCache$ = null
+ * private static Object $deserializeLambda$(SerializedLambda l) {
+ * var cache = $deserializeLambdaCache$
+ * if (cache eq null) {
+ * cache = new java.util.HashMap()
+ * $deserializeLambdaCache$ = cache
+ * }
+ * return scala.runtime.LambdaDeserializer.deserializeLambda(MethodHandles.lookup(), cache, l);
+ * }
+ */
+ def addLambdaDeserialize(classNode: ClassNode): Unit = {
+ val cw = classNode
+ import scala.tools.asm.Opcodes._
+
+ // Need to force creation of BTypes for these as `getCommonSuperClass` is called on
+ // automatically computing the max stack size (`visitMaxs`) during method writing.
+ btypes.coreBTypes.javaUtilHashMapReference
+ btypes.coreBTypes.javaUtilMapReference
+
+ // This is fine, even if `visitInnerClass` was called before for MethodHandles.Lookup: duplicates are not emitted.
+ cw.visitInnerClass("java/lang/invoke/MethodHandles$Lookup", "java/lang/invoke/MethodHandles", "Lookup", ACC_PUBLIC + ACC_FINAL + ACC_STATIC)
+
+ {
+ val fv = cw.visitField(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambdaCache$", "Ljava/util/Map;", null, null)
+ fv.visitEnd()
+ }
+
+ {
+ val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", "(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;", null, null)
+ mv.visitCode()
+ // javaBinaryName returns the internal name of a class. Also used in BTypesFromsymbols.classBTypeFromSymbol.
+ mv.visitFieldInsn(GETSTATIC, classNode.name, "$deserializeLambdaCache$", "Ljava/util/Map;")
+ mv.visitVarInsn(ASTORE, 1)
+ mv.visitVarInsn(ALOAD, 1)
+ val l0 = new Label()
+ mv.visitJumpInsn(IFNONNULL, l0)
+ mv.visitTypeInsn(NEW, "java/util/HashMap")
+ mv.visitInsn(DUP)
+ mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V", false)
+ mv.visitVarInsn(ASTORE, 1)
+ mv.visitVarInsn(ALOAD, 1)
+ mv.visitFieldInsn(PUTSTATIC, classNode.name, "$deserializeLambdaCache$", "Ljava/util/Map;")
+ mv.visitLabel(l0)
+ mv.visitFieldInsn(GETSTATIC, "scala/runtime/LambdaDeserializer$", "MODULE$", "Lscala/runtime/LambdaDeserializer$;")
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false)
+ mv.visitVarInsn(ALOAD, 1)
+ mv.visitVarInsn(ALOAD, 0)
+ mv.visitMethodInsn(INVOKEVIRTUAL, "scala/runtime/LambdaDeserializer$", "deserializeLambda", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/util/Map;Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;", false)
+ mv.visitInsn(ARETURN)
+ mv.visitEnd()
+ }
+ }
+
+ /**
+ * Clone the instructions in `methodNode` into a new [[InsnList]], mapping labels according to
+ * the `labelMap`. Returns the new instruction list and a map from old to new instructions, and
+ * a boolean indicating if the instruction list contains an instantiation of a serializable SAM
+ * type.
+ */
+ def cloneInstructions(methodNode: MethodNode, labelMap: Map[LabelNode, LabelNode]): (InsnList, Map[AbstractInsnNode, AbstractInsnNode], Boolean) = {
+ val javaLabelMap = labelMap.asJava
+ val result = new InsnList
+ var map = Map.empty[AbstractInsnNode, AbstractInsnNode]
+ var hasSerializableClosureInstantiation = false
+ for (ins <- methodNode.instructions.iterator.asScala) {
+ if (!hasSerializableClosureInstantiation) ins match {
+ case callGraph.LambdaMetaFactoryCall(indy, _, _, _) => indy.bsmArgs match {
+ case Array(_, _, _, flags: Integer, xs@_*) if (flags.intValue & LambdaMetafactory.FLAG_SERIALIZABLE) != 0 =>
+ hasSerializableClosureInstantiation = true
+ case _ =>
+ }
+ case _ =>
+ }
+ val cloned = ins.clone(javaLabelMap)
+ result add cloned
+ map += ((ins, cloned))
+ }
+ (result, map, hasSerializableClosureInstantiation)
+ }
+}
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 ea186f9a1b..5ff356b704 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
@@ -16,8 +16,6 @@ import scala.tools.asm.{MethodWriter, ClassWriter, Label, Opcodes, Type}
import scala.tools.asm.tree._
import GenBCode._
import scala.collection.convert.decorateAsScala._
-import scala.collection.convert.decorateAsJava._
-import scala.tools.nsc.backend.jvm.BTypes._
object BytecodeUtils {
@@ -271,22 +269,6 @@ object BytecodeUtils {
}
/**
- * Clone the instructions in `methodNode` into a new [[InsnList]], mapping labels according to
- * the `labelMap`. Returns the new instruction list and a map from old to new instructions.
- */
- def cloneInstructions(methodNode: MethodNode, labelMap: Map[LabelNode, LabelNode]): (InsnList, Map[AbstractInsnNode, AbstractInsnNode]) = {
- val javaLabelMap = labelMap.asJava
- val result = new InsnList
- var map = Map.empty[AbstractInsnNode, AbstractInsnNode]
- for (ins <- methodNode.instructions.iterator.asScala) {
- val cloned = ins.clone(javaLabelMap)
- result add cloned
- map += ((ins, cloned))
- }
- (result, map)
- }
-
- /**
* Clone the local variable descriptors of `methodNode` and map their `start` and `end` labels
* according to the `labelMap`.
*/
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 b9788c3f56..32eaf07080 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
@@ -21,7 +21,7 @@ import BytecodeUtils._
class CallGraph[BT <: BTypes](val btypes: BT) {
import btypes._
- import analyzers._
+ import backendUtils._
/**
* The call graph contains the callsites in the program being compiled.
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 fb7dd16909..a7c4c27a97 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
@@ -23,7 +23,7 @@ import scala.collection.convert.decorateAsScala._
class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
import btypes._
import callGraph._
- import analyzers._
+ import backendUtils._
/**
* If a closure is allocated and invoked within the same method, re-write the invocation to the
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 baa747492f..f88c131e8d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -24,7 +24,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
import btypes._
import callGraph._
import inlinerHeuristics._
- import analyzers._
+ import backendUtils._
def eliminateUnreachableCodeAndUpdateCallGraph(methodNode: MethodNode, definingClass: InternalName): Unit = {
localOpt.minimalRemoveUnreachableCode(methodNode, definingClass) foreach {
@@ -299,7 +299,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
// New labels for the cloned instructions
val labelsMap = cloneLabels(callee)
- val (clonedInstructions, instructionMap) = cloneInstructions(callee, labelsMap)
+ val (clonedInstructions, instructionMap, hasSerializableClosureInstantiation) = cloneInstructions(callee, labelsMap)
val keepLineNumbers = callsiteClass == calleeDeclarationClass
if (!keepLineNumbers) {
removeLineNumberNodes(clonedInstructions)
@@ -431,6 +431,11 @@ class Inliner[BT <: BTypes](val btypes: BT) {
callsiteMethod.maxStack = math.max(callsiteMethod.maxStack, math.max(stackHeightAtNullCheck, maxStackOfInlinedCode))
+ if (hasSerializableClosureInstantiation && !indyLambdaHosts(callsiteClass.internalName)) {
+ indyLambdaHosts += callsiteClass.internalName
+ addLambdaDeserialize(byteCodeRepository.classNode(callsiteClass.internalName).get)
+ }
+
callGraph.addIfMissing(callee, calleeDeclarationClass)
def mapArgInfo(argInfo: (Int, ArgInfo)): Option[(Int, ArgInfo)] = argInfo match {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
index 1e7b46012e..38f3c51892 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
@@ -48,7 +48,7 @@ import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._
class LocalOpt[BT <: BTypes](val btypes: BT) {
import LocalOptImpls._
import btypes._
- import analyzers._
+ import backendUtils._
/**
* In order to run an Analyzer, the maxLocals / maxStack fields need to be available. The ASM
diff --git a/test/files/run/inlineAddDeserializeLambda.scala b/test/files/run/inlineAddDeserializeLambda.scala
new file mode 100644
index 0000000000..a6bafd0f49
--- /dev/null
+++ b/test/files/run/inlineAddDeserializeLambda.scala
@@ -0,0 +1,20 @@
+class C { @inline final def f: Int => Int = (x: Int) => x + 1 }
+
+object Test extends App {
+ import java.io._
+
+ def serialize(obj: AnyRef): Array[Byte] = {
+ val buffer = new ByteArrayOutputStream
+ val out = new ObjectOutputStream(buffer)
+ out.writeObject(obj)
+ buffer.toByteArray
+ }
+ def deserialize(a: Array[Byte]): AnyRef = {
+ val in = new ObjectInputStream(new ByteArrayInputStream(a))
+ in.readObject
+ }
+
+ def serializeDeserialize[T <: AnyRef](obj: T) = deserialize(serialize(obj)).asInstanceOf[T]
+
+ assert(serializeDeserialize((new C).f).isInstanceOf[Function1[_, _]])
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala
index f78d450db1..a7d1dc168a 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala
@@ -31,7 +31,7 @@ object NullnessAnalyzerTest extends ClearAfterClass.Clearable {
class NullnessAnalyzerTest extends ClearAfterClass {
ClearAfterClass.stateToClear = NullnessAnalyzerTest
val noOptCompiler = NullnessAnalyzerTest.noOptCompiler
- import noOptCompiler.genBCode.bTypes.analyzers._
+ import noOptCompiler.genBCode.bTypes.backendUtils._
def newNullnessAnalyzer(methodNode: MethodNode, classInternalName: InternalName = "C") = new AsmAnalyzer(methodNode, classInternalName, new NullnessAnalyzer)
diff --git a/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala
index 155e0a6017..4835eb3cdf 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala
@@ -26,7 +26,7 @@ object ProdConsAnalyzerTest extends ClearAfterClass.Clearable {
class ProdConsAnalyzerTest extends ClearAfterClass {
ClearAfterClass.stateToClear = ProdConsAnalyzerTest
val noOptCompiler = ProdConsAnalyzerTest.noOptCompiler
- import noOptCompiler.genBCode.bTypes.analyzers._
+ import noOptCompiler.genBCode.bTypes.backendUtils._
def prodToString(producer: AbstractInsnNode) = producer match {
case p: InitialProducer => p.toString
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
index 8429a583b5..1108a37266 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
@@ -68,7 +68,7 @@ class InlinerTest extends ClearAfterClass {
val compiler = InlinerTest.compiler
import compiler.genBCode.bTypes._
- import compiler.genBCode.bTypes.analyzers._
+ import compiler.genBCode.bTypes.backendUtils._
def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] = {
InlinerTest.notPerRun.foreach(_.clear())