8000 Fix lambda deserialization in classes with 252+ lambdas · scala/scala@20587e9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 20587e9

Browse files
committed
Fix lambda deserialization in classes with 252+ lambdas
Create a lambda deserializer per group of target methods, and call these sequentially trapping the particular pattern of exception that is thrown when a target method is absent from the map. Fixes scala/bug#10232 ``` // access flags 0x100A private static synthetic $deserializeLambda$(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object; TRYCATCHBLOCK L0 L1 L1 java/lang/IllegalArgumentException L0 ALOAD 0 INVOKEDYNAMIC lambdaDeserialize(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object; [ // handle kind 0x6 : INVOKESTATIC scala/runtime/LambdaDeserialize.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite; // arguments: // handle kind 0x6 : INVOKESTATIC Test$.$anonfun$main$1$adapted(Lscala/Function1;)Ljava/lang/Object;, // handle kind 0x6 : INVOKESTATIC Test$.$anonfun$lambdas$1(Ljava/lang/Object;)Ljava/lang/String;, ... Test$.$anonfun$lambdas$249(Ljava/lang/Object;)Ljava/lang/String;, // handle kind 0x6 : INVOKESTATIC Test$.$anonfun$lambdas$250(Ljava/lang/Object;)Ljava/lang/String; ] ARETURN L1 ALOAD 0 INVOKEDYNAMIC lambdaDeserialize(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object; [ // handle kind 0x6 : INVOKESTATIC scala/runtime/LambdaDeserialize.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite; // arguments: // handle kind 0x6 : INVOKESTATIC Test$.$anonfun$lambdas$251(Ljava/lang/Object;)Ljava/lang/String;, // handle kind 0x6 : INVOKESTATIC Test$.$anonfun$lambdas$252(Ljava/lang/Object;)Ljava/lang/String;, ... // handle kind 0x6 : INVOKESTATIC Test$.$anonfun$lambdas$256(Ljava/lang/Object;)Ljava/lang/String; ] ARETURN MAXSTACK = 2 MAXLOCALS = 1 ```
1 parent d9343a7 commit 20587e9

File tree

2 files changed

+313
-7
lines changed

2 files changed

+313
-7
lines changed

src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,27 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
6666

6767
class NonLubbingTypeFlowAnalyzer(val methodNode: MethodNode, classInternalName: InternalName) extends AsmAnalyzer(methodNode, classInternalName, new Analyzer(new NonLubbingTypeFlowInterpreter))
6868

69-
/**
69+
/*
7070
* Add:
71+
*
7172
* private static Object $deserializeLambda$(SerializedLambda l) {
72-
* return indy[scala.runtime.LambdaDeserialize.bootstrap](l)
73+
* <FOR N in 0..NUM_GROUPS-2>
74+
* try {
75+
* return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup$N](l)
76+
* } catch {
77+
* case i: IllegalArgumentException => // swallow
78+
* }
79+
* </FOR>
80+
* return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup${NUM_GROUPS-1}](l)
7381
* }
7482
*
7583
* We use invokedynamic here to enable caching within the deserializer without needing to
7684
* host a static field in the enclosing class. This allows us to add this method to interfaces
7785
* that define lambdas in default methods.
86+
*
87+
* SI-10232 we can't pass arbitrary number of method handles to the final varargs parameter of the bootstrap
88+
* method due to a limitation in the JVM. Instead, we emit a separate invokedynamic bytecode for each group of target
89+
* methods.
7890
*/
7991
def addLambdaDeserialize(classNode: ClassNode, implMethods: Iterable[Handle]): Unit = {
8092
val cw = classNode
@@ -87,15 +99,34 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
8799

88100
val nilLookupDesc = MethodBType(Nil, jliMethodHandlesLookupRef).descriptor
89101
val serlamObjDesc = MethodBType(jliSerializedLambdaRef :: Nil, ObjectRef).descriptor
102+
val implMethodsArray = implMethods.toArray
90103

91-
{
92-
val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", serlamObjDesc, null, null)
93-
mv.visitCode()
104+
val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", serlamObjDesc, null, null)
105+
def emitLambdaDeserializeIndy(targetMethods: Seq[Handle]) {
94106
mv.visitVarInsn(ALOAD, 0)
95-
mv.visitInvokeDynamicInsn("lambdaDeserialize", serlamObjDesc, lambdaDeserializeBootstrapHandle, implMethods.toArray: _*)
107+
mv.visitInvokeDynamicInsn("lambdaDeserialize", serlamObjDesc, lambdaDeserializeBootstrapHandle, targetMethods: _*)
108+
}
109+
110+
val targetMethodGroupLimit = 255 - 1 - 3 // JVM limit. See See MAX_MH_ARITY in CallSite.java
111+
val groups: Array[Array[Handle]] = implMethodsArray.grouped(targetMethodGroupLimit).toArray
112+
val numGroups = groups.length
113+
114+
import scala.tools.asm.Label
115+
val initialLabels = Array.fill(numGroups - 1)(new Label())
116+
val terminalLabel = new Label
117+
def nextLabel(i: Int) = if (i == numGroups - 2) terminalLabel else initialLabels(i + 1)
118+
119+
for ((label, i) <- initialLabels.iterator.zipWithIndex) {
120+
mv.visitTryCatchBlock(label, nextLabel(i), nextLabel(i), "java/lang/IllegalArgumentException")
121+
}
122+
for ((label, i) <- initialLabels.iterator.zipWithIndex) {
123+
mv.visitLabel(label)
124+
emitLambdaDeserializeIndy(groups(i))
96125
mv.visitInsn(ARETURN)
97-
mv.visitEnd()
98126
}
127+
mv.visitLabel(terminalLabel)
128+
emitLambdaDeserializeIndy(groups(numGroups - 1))
129+
mv.visitInsn(ARETURN)
99130
}
100131

101132
/**

test/files/run/t10232.scala

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, ObjectInputStream, ObjectOutputStream}
2+
3+
object Test {
4+
val lambdas: List[Any => String] = List(
5+
{ (t: Any) => "ab" },
6+
{ (t: Any) => "ab" },
7+
{ (t: Any) => "ab" },
8+
{ (t: Any) => "ab" },
9+
{ (t: Any) => "ab" },
10+
{ (t: Any) => "ab" },
11+
{ (t: Any) => "ab" },
12+
{ (t: Any) => "ab" },
13+
{ (t: Any) => "ab" },
14+
{ (t: Any) => "ab" },
15+
{ (t: Any) => "ab" },
16+
{ (t: Any) => "ab" },
17+
{ (t: Any) => "ab" },
18+
{ (t: Any) => "ab" },
19+
{ (t: Any) => "ab" },
20+
{ (t: Any) => "ab" },
21+
{ (t: Any) => "ab" },
22+
{ (t: Any) => "ab" },
23+
{ (t: Any) => "ab" },
24+
{ (t: Any) => "ab" },
25+
{ (t: Any) => "ab" },
26+
{ (t: Any) => "ab" },
27+
{ (t: Any) => "ab" },
28+
{ (t: Any) => "ab" },
29+
{ (t: Any) => "ab" },
30+
{ (t: Any) => "ab" },
31+
{ (t: Any) => "ab" },
32+
{ (t: Any) => "ab" },
33+
{ (t: Any) => "ab" },
34+
{ (t: Any) => "ab" },
35+
{ (t: Any) => "ab" },
36+
{ (t: Any) => "ab" },
37+
{ (t: Any) => "ab" },
38+
{ (t: Any) => "ab" },
39+
{ (t: Any) => "ab" },
40+
{ (t: Any) => "ab" },
41+
{ (t: Any) => "ab" },
42+
{ (t: Any) => "ab" },
43+
{ (t: Any) => "ab" },
44+
{ (t: Any) => "ab" },
45+
{ (t: Any) => "ab" },
46+
{ (t: Any) => "ab" },
47+
{ (t: Any) => "ab" },
48+
{ (t: Any) => "ab" },
49+
{ (t: Any) => "ab" },
50+
{ (t: Any) => "ab" },
51+
{ (t: Any) => "ab" },
52+
{ (t: Any) => "ab" },
53+
{ (t: Any) => "ab" },
54+
{ (t: Any) => "ab" },
55+
{ (t: Any) => "ab" },
56+
{ (t: Any) => "ab" },
57+
{ (t: Any) => "ab" },
58+
{ (t: Any) => "ab" },
59+
{ (t: Any) => "ab" },
60+
{ (t: Any) => "ab" },
61+
{ (t: Any) => "ab" },
62+
{ (t: Any) => "ab" },
63+
{ (t: Any) => "ab" },
64+
{ (t: Any) => "ab" },
65+
{ (t: Any) => "ab" },
66+
{ (t: Any) => "ab" },
67+
{ (t: Any) => "ab" },
68+
{ (t: Any) => "ab" },
69+
{ (t: Any) => "ab" },
70+
{ (t: Any) => "ab" },
71+
{ (t: Any) => "ab" },
72+
{ (t: Any) => "ab" },
73+
{ (t: Any) => "ab" },
74+
{ (t: Any) => "ab" },
75+
{ (t: Any) => "ab" },
76+
{ (t: Any) => "ab" },
77+
{ (t: Any) => "ab" },
78+
{ (t: Any) => "ab" },
79+
{ (t: Any) => "ab" },
80+
{ (t: Any) => "ab" },
81+
{ (t: Any) => "ab" },
82+
{ (t: Any) => "ab" },
83+
{ (t: Any) => "ab" },
84+
{ (t: Any) => "ab" },
85+
{ (t: Any) => "ab" },
86+
{ (t: Any) => "ab" },
87+
{ (t: Any) => "ab" },
88+
{ (t: Any) => "ab" },
89+
{ (t: Any) => "ab" },
90+
{ (t: Any) => "ab" },
91+
{ (t: Any) => "ab" },
92+
{ (t: Any) => "ab" },
93+
{ (t: Any) => "ab" },
94+
{ (t: Any) => "ab" },
95+
{ (t: Any) => "ab" },
96+
{ (t: Any) => "ab" },
97+
{ (t: Any) => "ab" },
98+
{ (t: Any) => "ab" },
99+
{ (t: Any) => "ab" },
100+
{ (t: Any) => "ab" },
101+
{ (t: Any) => "ab" },
102+
{ (t: Any) => "ab" },
103+
{ (t: Any) => "ab" },
104+
{ (t: Any) => "ab" },
105+
{ (t: Any) => "ab" },
106+
{ (t: Any) => "ab" },
107+
{ (t: Any) => "ab" },
108+
{ (t: Any) => "ab" },
109+
{ (t: Any) => "ab" },
110+
{ (t: Any) => "ab" },
111+
{ (t: Any) => "ab" },
112+
{ (t: Any) => "ab" },
113+
{ (t: Any) => "ab" },
114+
{ (t: Any) => "ab" },
115+
{ (t: Any) => "ab" },
116+
{ (t: Any) => "ab" },
117+
{ (t: Any) => "ab" },
118+
{ (t: Any) => "ab" },
119+
{ (t: Any) => "ab" },
120+
{ (t: Any) => "ab" },
121+
{ (t: Any) => "ab" },
122+
{ (t: Any) => "ab" },
123+
{ (t: Any) => "ab" },
124+
{ (t: Any) => "ab" },
125+
{ (t: Any) => "ab" },
126+
{ (t: Any) => "ab" },
127+
{ (t: Any) => "ab" },
128+
{ (t: Any) => "ab" },
129+
{ (t: Any) => "ab" },
130+
{ (t: Any) => "ab" },
131+
{ (t: Any) => "ab" },
132+
{ (t: Any) => "ab" },
133+
{ (t: Any) => "ab" },
134+
{ (t: Any) => "ab" },
135+
{ (t: Any) => "ab" },
136+
{ (t: Any) => "ab" },
137+
{ (t: Any) => "ab" },
138+
{ (t: Any) => "ab" },
139+
{ (t: Any) => "ab" },
140+
{ (t: Any) => "ab" },
141+
{ (t: Any) => "ab" },
142+
{ (t: Any) => "ab" },
143+
{ (t: Any) => "ab" },
144+
{ (t: Any) => "ab" },
145+
{ (t: Any) => "ab" },
146+
{ (t: Any) => "ab" },
147+
{ (t: Any) => "ab" },
148+
{ (t: Any) => "ab" },
149+
{ (t: Any) => "ab" },
150+
{ (t: Any) => "ab" },
151+
{ (t: Any) => "ab" },
152+
{ (t: Any) => "ab" },
153+
{ (t: Any) => "ab" },
154+
{ (t: Any) => "ab" },
155+
{ (t: Any) => "ab" },
156+
{ (t: Any) => "ab" },
157+
{ (t: Any) => "ab" },
158+
{ (t: Any) => "ab" },
159+
{ (t: Any) => "ab" },
160+
{ (t: Any) => "ab" },
161+
{ (t: Any) => "ab" },
162+
{ (t: Any) => "ab" },
163+
{ (t: Any) => "ab" },
164+
{ (t: Any) => "ab" },
165+
{ (t: Any) => "ab" },
166+
{ (t: Any) => "ab" },
167+
{ (t: Any) => "ab" },
168+
{ (t: Any) => "ab" },
169+
{ (t: Any) => "ab" },
170+
{ (t: Any) => "ab" },
171+
{ (t: Any) => "ab" },
172+
{ (t: Any) => "ab" },
173+
{ (t: Any) => "ab" },
174+
{ (t: Any) => "ab" },
175+
{ (t: Any) => "ab" },
176+
{ (t: Any) => "ab" },
177+
{ (t: Any) => "ab" },
178+
{ (t: Any) => "ab" },
179+
{ (t: Any) => "ab" },
180+
{ (t: Any) => "ab" },
181+
{ (t: Any) => "ab" },
182+
{ (t: Any) => "ab" },
183+
{ (t: Any) => "ab" },
184+
{ (t: Any) => "ab" },
185+
{ (t: Any) => "ab" },
186+
{ (t: Any) => "ab" },
187+
{ (t: Any) => "ab" },
188+
{ (t: Any) => "ab" },
189+
{ (t: Any) => "ab" },
190+
{ (t: Any) => "ab" },
191+
{ (t: Any) => "ab" },
192+
{ (t: Any) => "ab" },
193+
{ (t: Any) => "ab" },
194+
{ (t: Any) => "ab" },
195+
{ (t: Any) => "ab" },
196+
{ (t: Any) => "ab" },
197+
{ (t: Any) => "ab" },
198+
{ (t: Any) => "ab" },
199+
{ (t: Any) => "ab" },
200+
{ (t: Any) => "ab" },
201+
{ (t: Any) => "ab" },
202+
{ (t: Any) => "ab" },
203+
{ (t: Any) => "ab" },
204+
{ (t: Any) => "ab" },
205+
{ (t: Any) => "ab" },
206+
{ (t: Any) => "ab" },
207+
{ (t: Any) => "ab" },
208+
{ (t: Any) => "ab" },
209+
{ (t: Any) => "ab" },
210+
{ (t: Any) => "ab" },
211+
{ (t: Any) => "ab" },
212+
{ (t: Any) => "ab" },
213+
{ (t: Any) => "ab" },
214+
{ (t: Any) => "ab" },
215+
{ (t: Any) => "ab" },
216+
{ (t: Any) => "ab" },
217+
{ (t: Any) => "ab" },
218+
{ (t: Any) => "ab" },
219+
{ (t: Any) => "ab" },
220+
{ (t: Any) => "ab" },
221+
{ (t: Any) => "ab" },
222+
{ (t: Any) => "ab" },
223+
{ (t: Any) => "ab" },
224+
{ (t: Any) => "ab" },
225+
{ (t: Any) => "ab" },
226+
{ (t: Any) => "ab" },
227+
{ (t: Any) => "ab" },
228+
{ (t: Any) => "ab" },
229+
{ (t: Any) => "ab" },
230+
{ (t: Any) => "ab" },
231+
{ (t: Any) => "ab" },
232+
{ (t: Any) => "ab" },
233+
{ (t: Any) => "ab" },
234+
{ (t: Any) => "ab" },
235+
{ (t: Any) => "ab" },
236+
{ (t: Any) => "ab" },
237+
{ (t: Any) => "ab" },
238+
{ (t: Any) => "ab" },
239+
{ (t: Any) => "ab" },
240+
{ (t: Any) => "ab" },
241+
{ (t: Any) => "ab" },
242+
{ (t: Any) => "ab" },
243+
{ (t: Any) => "ab" },
244+
{ (t: Any) => "ab" },
245+
{ (t: Any) => "ab" },
246+
{ (t: Any) => "ab" },
247+
{ (t: Any) => "ab" },
248+
{ (t: Any) => "ab" },
249+
{ (t: Any) => "ab" },
250+
{ (t: Any) => "ab" },
251+
{ (t: Any) => "ab" },
252+
{ (t: Any) => "ab" },
253+
{ (t: Any) => "ab" },
254+
{ (t: Any) => "ab" },
255+
{ (t: Any) => "ab" },
256+
{ (t: Any) => "ab" },
257+
{ (t: Any) => "ab" },
258+
{ (t: Any) => "ab" },
259+
{ (t: Any) => "ab" },
260+
{ (t: Any) => "ab" }
261+
)
262+
263+
def main(args: Array[String]): Unit = {
264+
for (lambda <- lambdas) {
265+
val outStream = new ByteArrayOutputStream
266+
val oo = new ObjectOutputStream(outStream)
267+
oo.writeObject(lambda)
268+
269+
val inStream = new ByteArrayInputStream(outStream.toByteArray)
270+
val oi = new ObjectInputStream(inStream)
271+
val lambda2 = oi.readObject().asInstanceOf[Any => String]
272+
assert(lambda2(1) == "ab")
273+
}
274+
}
275+
}

0 commit comments

Comments
 (0)
0