-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix lambda deserialization in classes with 252+ lambdas #5857
Conversation
Unfortunately, this change uses exceptions as control flow for lambda#252 and above, but that seems to be the best way around the compatibility constraint that the newly generated code for I've prototyped a change to avoid the overhead of filling in the stack trace of the |
mv.visitVarInsn(ASTORE, 1) | ||
mv.visitVarInsn(ALOAD, 1) | ||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/IllegalArgumentException", "getCause", "()Ljava/lang/Throwable;", false) | ||
mv.visitJumpInsn(IFNULL, nextLabel(i)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should there be an ALOAD 1; ATHROW
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I've just noticed that the second throw IllegalArgumentException
in LambdaDeserializer
is detritus that should have been removed in 131402f.
I'll remove that throw
and then simplify this PR (no need to check getCause == null
here, just swallow any IAE
unconditionally in all but the terminal indy
call.
Looks good otherwise. I guess for 2.13 we can have a solution that doesn't use exceptional control flow? You could mark the commit |
I should have removed the try/catch when I removed the code path that could throw that exception in 131402f.
Pushed the discussed simplification.
I'd rather merge this into 2.13.x and then adapt the result, I've opened a ticket to track that: https://github.com/scala/scala-dev/issues/373 |
Sure, that's fine too. |
def nextLabel(i: Int) = if (i == numGroups - 2) terminalLabel else initialLabels(i + 1) | ||
|
||
for ((label, i) <- initialLabels.iterator.zipWithIndex) { | ||
mv.visitTryCatchBlock(label, nextLabel(i), nextLabel(i), "java/lang/IllegalArgumentException") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add jlIllegalArgumentExceptionRef
to CoreBTypes
, like jlClassCastExceptionRef
, then use .internalName
? This ensures that the ClassBType
is part of the classBTypeFromInternalName
map, which is excpected for computing stack map frames during class writing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
* case i: IllegalArgumentException => // swallow | ||
* } | ||
* </FOR> | ||
* return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup${NUM_GROUPS-1}](l) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the generated structure is more like
try return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup$0](l)
catch {
case i: IllegalArgumentException =>
try return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup$1](l)
catch {
case i: IllegalArgumentException =>
...
return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup${NUM_GROUPS-1}](l)
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a difference? In any case, your version is clearer, so I'll use that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a detail to make the comment align better with the implementation.
@@ -103,6 +103,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { | |||
lazy val jlCloneableRef : ClassBType = classBTypeFromSymbol(JavaCloneableClass) // java/lang/Cloneable | |||
lazy val jiSerializableRef : ClassBType = classBTypeFromSymbol(JavaSerializableClass) // java/io/Serializable | |||
lazy val jlClassCastExceptionRef : ClassBType = classBTypeFromSymbol(ClassCastExceptionClass) // java/lang/ClassCastException | |||
lazy val jlIllegalArgExceptionRef : ClassBType = classBTypeFromSymbol(IllegalArgExceptionClass) // java/lang/ClassCastException |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment is out of synch..
def nextLabel(i: Int) = if (i == numGroups - 2) terminalLabel else initialLabels(i + 1) | ||
|
||
for ((label, i) <- initialLabels.iterator.zipWithIndex) { | ||
mv.visitTryCatchBlock(label, nextLabel(i), nextLabel(i), coreBTypes.jlIllegalArgExceptionRef.internalName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
coreBTypes._
is already imported
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 ```
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