Serializable lambda and bytecode rewriters (original) (raw)
Remi Forax forax at univ-mlv.fr
Sun Jul 13 12:15:38 UTC 2014
- Previous message: RFR (S): 8050114: Expose Integer/Long formatUnsigned methods internally
- Next message: RFR [9]: 8050142: Optimize java.util.Formatter
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Hi guys, there is, i think, a serious issue in the way the deserialization of lambda is done by javac (or ecj which uses the same scheme), exactly in the code of the generated method deserializeLambdadeserializeLambdadeserializeLambda. Bytecode rewriting tools like jarjar or proguard works by rewriting all reference to types in class files so they need to distinguish if a reference is let say a String of a method descriptor. The actual code of deserializeLambdadeserializeLambdadeserializeLambda generated by javac breaks that assumption, all parameters of a deserialized lambda are checked as String thus are not rewritten correctly by the bytecode rewriters.
So currently, it's no possible to use a bytecode rewriting tools with the jdk8 if there is a serializable lambda somewhere in the code. My bad on that, I should have figure out that before the release of 8.
To fix this issue, the parameters that reference a lambda inside deserializeLambdadeserializeLambdadeserializeLambda should be typed as constant method handles and constant method types but given that the serializable form a lambda is a bunch of strings, doing the mapping between the two forms is not that obvious. Moreover, there is maybe a performance problem because
- the switch on the String that represent the name of the implementation method can not be used because it encodes the hashCode of a name that should be rewritten by the static tools.
- constant method handles and constant method types are not required to be interned by the VM thus may be re-created each time deserializeLambdadeserializeLambdadeserializeLambda is called if the code use ldc.
The invokedynamic evangelist in me think that deserializeLambdadeserializeLambdadeserializeLambda can be implemented as a unique invokedynamic call taking a SerializedLambda as parameter with all the parameters of all the serializable lambdas declared in the current class as bootstrap arguments. The bootstrap method will create a HashMap of a all methods names, and at runtime check if a serialized lambda is valid or not.
Something like: static class LambdaParameters { ... public boolean verify(SerializedLambda serializedLambda) { // check all parameters } }
public static CallSite deserializeLambda(Lookup lookup, String name, MethodType type, Object[] lambdaParameters) { HashMap<String, LambdaParameters> map = new HashMap<>(); for(int i=0; i < lambdaParameters.length; i+=3) { ... MethodHandle mh = (MethodHandle) lambdaParameters[i + 1]; String name = lookup.revealDirect(mh).getName(); ... map.put(name, new LambdaParameters(...)); } return new ConstantCallSite(CHECK.bindTo(map)); } private static final MethodHandle = ::check;
private static boolean check(HashMap<String, LambdaParameters> map, SerializedLambda serializedLambda) { LambdaParameters parameters = map.get(serializedLambda.getImplMethodName()); return parameters != null && parameters.verify(serializedLambda); }
A bootstrap method can not have more than 252 arguments, so you can not encode more than 84 (252 / 3) serializable lambdas, but I think that current scheme because it generated a lot of bytecodes will hit the 65 535 bytecodes near that number too. Anyway, if there is more than 84 serializable lambdas, deserializableLambdadeserializableLambdadeserializableLambda can call more than one invokedynamic in chain.
As you can see, the solution seems complex but may be somebody has a better solution :)
regards, Rémi
- Previous message: RFR (S): 8050114: Expose Integer/Long formatUnsigned methods internally
- Next message: RFR [9]: 8050142: Optimize java.util.Formatter
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]