1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
package scala.runtime
import java.lang.invoke._
/**
* This class is only intended to be called by synthetic `$deserializeLambda$` method that the Scala 2.12
* compiler will add to classes hosting lambdas.
*
* It is not intended to be consumed directly.
*/
object LambdaDeserializer {
/**
* Deserialize a lambda by calling `LambdaMetafactory.altMetafactory` to spin up a lambda class
* and instantiating this class with the captured arguments.
*
* A cache may be provided to ensure that subsequent deserialization of the same lambda expression
* is cheap, it amounts to a reflective call to the constructor of the previously created class.
* However, deserialization of the same lambda expression is not guaranteed to use the same class,
* concurrent deserialization of the same lambda expression may spin up more than one class.
*
* Assumptions:
* - No additional marker interfaces are required beyond `{java.io,scala.}Serializable`. These are
* not stored in `SerializedLambda`, so we can't reconstitute them.
* - No additional bridge methods are passed to `altMetafactory`. Again, these are not stored.
*
* @param lookup The factory for method handles. Must have access to the implementation method, the
* functional interface class, and `java.io.Serializable` or `scala.Serializable` as
* required.
* @param cache A cache used to avoid spinning up a class for each deserialization of a given lambda. May be `null`
* @param serialized The lambda to deserialize. Note that this is typically created by the `readResolve`
* member of the anonymous class created by `LambdaMetaFactory`.
* @return An instance of the functional interface
*/
def deserializeLambda(lookup: MethodHandles.Lookup, cache: java.util.Map[String, MethodHandle],
targetMethodMap: java.util.Map[String, MethodHandle], serialized: SerializedLambda): AnyRef = {
assert(targetMethodMap != null)
def slashDot(name: String) = name.replaceAll("/", ".")
val loader = lookup.lookupClass().getClassLoader
val implClass = loader.loadClass(slashDot(serialized.getImplClass))
val key = LambdaDeserialize.nameAndDescriptorKey(serialized.getImplMethodName, serialized.getImplMethodSignature)
def makeCallSite: CallSite = {
import serialized._
def parseDescriptor(s: String) =
MethodType.fromMethodDescriptorString(s, loader)
val funcInterfaceSignature = parseDescriptor(getFunctionalInterfaceMethodSignature)
val instantiated = parseDescriptor(getInstantiatedMethodType)
val functionalInterfaceClass = loader.loadClass(slashDot(getFunctionalInterfaceClass))
val implMethodSig = parseDescriptor(getImplMethodSignature)
// Construct the invoked type from the impl method type. This is the type of a factory
// that will be generated by the meta-factory. It is a method type, with param types
// coming form the types of the captures, and return type being the functional interface.
val invokedType: MethodType = {
// 1. Add receiver for non-static impl methods
val withReceiver = getImplMethodKind match {
case MethodHandleInfo.REF_invokeStatic | MethodHandleInfo.REF_newInvokeSpecial =>
implMethodSig
case _ =>
implMethodSig.insertParameterTypes(0, implClass)
}
// 2. Remove lambda parameters, leaving only captures. Note: the receiver may be a lambda parameter,
// such as in `Function<Object, String> s = Object::toString`
val lambdaArity = funcInterfaceSignature.parameterCount()
val from = withReceiver.parameterCount() - lambdaArity
val to = withReceiver.parameterCount()
// 3. Drop the lambda return type and replace with the functional interface.
withReceiver.dropParameterTypes(from, to).changeReturnType(functionalInterfaceClass)
}
// Lookup the implementation method
val implMethod: MethodHandle = try {
if (targetMethodMap.containsKey(key)) {
targetMethodMap.get(key)
} else {
throw new IllegalArgumentException("Illegal lambda deserialization")
}
} catch {
case e: ReflectiveOperationException => throw new IllegalArgumentException("Illegal lambda deserialization", e)
}
val flags: Int = LambdaMetafactory.FLAG_SERIALIZABLE | LambdaMetafactory.FLAG_MARKERS
val isScalaFunction = functionalInterfaceClass.getName.startsWith("scala.Function")
val markerInterface: Class[_] = loader.loadClass(if (isScalaFunction) ScalaSerializable else JavaIOSerializable)
LambdaMetafactory.altMetafactory(
lookup, getFunctionalInterfaceMethodName, invokedType,
/* samMethodType = */ funcInterfaceSignature,
/* implMethod = */ implMethod,
/* instantiatedMethodType = */ instantiated,
/* flags = */ flags.asInstanceOf[AnyRef],
/* markerInterfaceCount = */ 1.asInstanceOf[AnyRef],
/* markerInterfaces[0] = */ markerInterface,
/* bridgeCount = */ 0.asInstanceOf[AnyRef]
)
}
val factory: MethodHandle = if (cache == null) {
makeCallSite.getTarget
} else cache.synchronized{
cache.get(key) match {
case null =>
val callSite = makeCallSite
val temp = callSite.getTarget
cache.put(key, temp)
temp
case target => target
}
}
val captures = Array.tabulate(serialized.getCapturedArgCount)(n => serialized.getCapturedArg(n))
factory.invokeWithArguments(captures: _*)
}
private val ScalaSerializable = "scala.Serializable"
private val JavaIOSerializable = {
// We could actually omit this marker interface as LambdaMetaFactory will add it if
// the FLAG_SERIALIZABLE is set and of the provided markers extend it. But the code
// is cleaner if we uniformly add a single marker, so I'm leaving it in place.
"java.io.Serializable"
}
}
|