Stability of lambda serialization
David M. Lloyd
david.lloyd at redhat.com
Tue Jul 30 06:15:57 PDT 2013
I realize I was ambiguous:
On 07/29/2013 07:40 PM, David M. Lloyd wrote:
> As before, I continue to strongly disagree with this so-called "de
> facto" constraint. In reality, we see real users who rely heavily on
> the *real* constraints which are outlined in the specification of
> serialization. I think that real-world users are going to encounter
> bizarre issues at best, and security vulnerabilities at worst, due to
> the behavior of capture.
...due to the behavior of capture with respect to serialization.
> As I always do when this particular truism is brought up, I will counter
> your "the perfect is the enemy of the good" with "the good is the enemy
> of the correct". A clever saying is never enough to justify incorrect
> behavior. I think it's perfectly reasonable to capture "this", at most,
> but beyond that I think there is no benefit to having capture, at all,
> in the face of its instability.
...having capture in the context of serialization...
> I think the "equivalent stability" argument is also a bit fallacious.
> It's saying "anonymous classes are bad, so that's a free pass to do this
> similarly badly as well, in whatever way we want". To be "as good as
> anonymous classes", we'd have to have capture which is stable by name at
> least.
...capture in the context of serialization...
> But I personally I think this criteria is arbitrary and unnecessary in
> any case. I think that we can easily guarantee stability of both
> captured variables and of method resolution by only allowing
> serialization of named method references and forbidding capture (other
> than "this").
...forbidding capture in serializable lambdas...
> On 07/27/2013 12:18 PM, Brian Goetz wrote:
>> Yes, when this came up the first several times, the consensus of the EG
>> was that we would build on the de-facto, practical constraint that
>> people have learned to live with for years when dealing with
>> serialization in the presence of inner classes: make sure the same
>> classfile bits are on both sides of the pipe.
>>
>> Inner classes have a naming instability, where you have a stable method
>> name but an unstable class name. Lambdas have the opposite, but
>> effectively equivalent instability: a stable class name but an unstable
>> method name. People have learned to use serialization with inner
>> classes by the technique of keeping the classfile bits the same on both
>> sides of the wire. The exact same technique enables serialization of
>> lambdas.
>>
>> In particular, the EG rejected the extremes of "make it perfect" and
>> "don't allow it at all" as being "the perfect is the enemy of the good."
>> Inner classes have been exactly this hostile to serialization for 15
>> years, but people who want to use serialization have learned coping
>> techniques to do so. The compromises in the middle are squishy but
>> better than either extreme.
>>
>> Of course, if we can make it better without exposing additional
>> complexity, so much the better. For example, we did a few rounds of
>> this by encoding the hash of the method name and signature so that
>> simply reordering the methods in a class did not trigger such
>> instabilities. But the goal was never perfect stability; it was just
>> "at least as good as inner classes." I believe we've reached that goal.
>>
>> Do you have a concrete proposal for how we would improve lambda
>> serialization to be robust in the face of changes in order of capture?
>> On the surface it looks entirely possible.
>>
>> On 7/27/2013 12:52 PM, Sam Pullara wrote:
>>> I think the requirement should be that you have the same classes on each
>>> side of the wire.
>>>
>>> Sam
>>>
>>>
>>> On Tue, Jul 23, 2013 at 10:35 AM, Scott Stark <sstark at redhat.com
>>> <mailto:sstark at redhat.com>> wrote:
>>>
>>> Red Hat has a concern regarding how fragile the default
>>> serialization behavior of lambda expressions is in the current
>>> reference implementation, currently:
>>> ironmaiden:OpenJDK starksm$
>>>
>>> /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/bin/java
>>> -version
>>> java version "1.8.0-ea"
>>> Java(TM) SE Runtime Environment (build 1.8.0-ea-b98)
>>> Java HotSpot(TM) 64-Bit Server VM (build 25.0-b40, mixed mode)
>>>
>>> The problem is that the serialized form of a lambda expression
>>> depends on the order in which captured arguments are declared. The
>>> attached simple example demonstrates how easy it is for a trivial
>>> reordering of the lambda code block to result in an inability to
>>> deserialize a previously saved expression.
>>>
>>> To produce this exception:
>>> 1. Run the serialization.AuthenticationContext.testWriteLambda
>>> method with the lambda expression written as:
>>> Authenticator a = (Authenticator & Serializable) (String
>>> principal, char[] pass) -> {
>>> // Run with p declared first when writing out the
>>> /tmp/testWriteLambda.bin, then switch
>>> // to declare u first when running testReadLambda
>>> String p = "-> Password " + password + " <-";
>>> String u = "-> User " + user + " <-";
>>> return u + " " + p;
>>> };
>>> 2. Change the lambda expression to:
>>> Authenticator a = (Authenticator & Serializable) (String
>>> principal, char[] pass) -> {
>>> // Run with p declared first when writing out the
>>> /tmp/testWriteLambda.bin, then switch
>>> // to declare u first when running testReadLambda
>>> String u = "-> User " + user + " <-";
>>> String p = "-> Password " + password + " <-";
>>> return u + " " + p;
>>> };
>>>
>>> Recompile and run serialization.AuthenticationContext.testReadLambda
>>> to produce:
>>>
>>> java.io.IOException: unexpected exception type
>>> at
>>>
>>> java.io.ObjectStreamClass.throwMiscException(ObjectStreamClass.java:1538)
>>>
>>> at
>>>
>>> java.io.ObjectStreamClass.invokeReadResolve(ObjectStreamClass.java:1110)
>>> at
>>>
>>> java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1807)
>>>
>>> at
>>> java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
>>> at
>>> java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
>>> at
>>>
>>> serialization.AuthenticationContext.testReadLambda(AuthenticationContext.java:34)
>>>
>>>
>>> ...
>>> Caused by: java.lang.reflect.InvocationTargetException
>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native
>>> Method)
>>> at
>>>
>>> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
>>>
>>>
>>> at
>>>
>>> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>>>
>>>
>>> at
>>>
>>> java.lang.invoke.SerializedLambda.readResolve(SerializedLambda.java:222)
>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native
>>> Method)
>>> at
>>>
>>> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
>>>
>>>
>>> at
>>>
>>> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>>>
>>>
>>> at
>>>
>>> java.io.ObjectStreamClass.invokeReadResolve(ObjectStreamClass.java:1104)
>>> ... 30 more
>>> Caused by: java.lang.IllegalArgumentException: Invalid lambda
>>> deserialization
>>> at
>>>
>>> serialization.AuthenticationContext.$deserializeLambda$(AuthenticationContext.java:1)
>>>
>>>
>>> ... 40 more
>>>
>>> One does not see the same level of sensitivity to the ordering of
>>> the serialization fields in a POJO as demonstrated by the
>>> serialization.AuthenticationContext.testWritePOJO/testReadPOJO cases
>>> where one can reorder the TestPOJO.{user,password} fields without
>>> having serialization fail.
>>>
>>> We would like to see at least that level of stability of the
>>> serialized form of lambda expressions.
>>>
>>>
>
>
--
- DML
More information about the lambda-spec-observers
mailing list