JSScript serialisable#2403
Conversation
gbrail
left a comment
There was a problem hiding this comment.
Thanks for working on this!
| */ | ||
| public class JSScript implements Script, ScriptOrFn<JSScript> { | ||
| public class JSScript implements Script, ScriptOrFn<JSScript>, Serializable { | ||
| @Serial private static final long serialVersionUID = 1L; |
There was a problem hiding this comment.
Since have another PR open related to serialVersionUID in general, please assign a real one here, generated by the "serialver" tool or your IDE, not generated by AI ;-)
There was a problem hiding this comment.
The value 1 is as good as any other. This class was not serialisable before, so there's no old value that it must be different from. The other PR #2249 does not affect JSScript. Or is there something I'm missing?
| } | ||
|
|
||
| @Serial | ||
| protected Object writeReplace() { |
There was a problem hiding this comment.
Back in the day when I dealt with serialization all the time, we used to implement Externalizable which let us take control over serialization without all this stuff -- might be worth considering that too.
There was a problem hiding this comment.
Externalizable is good if you want to do it completely by yourself. Here I do less:
- for interpreted mode: JSScript is serialised and deserialised normally.
- for compiled mode: JSScript is not serialised; rather it is substituted by a CompiledScriptProxy (that has the JavaScript source). The proxy is serialised in the normal way. At the other end, the proxy is deserialised in the normal way (it's almost just a String), and then CompiledScriptProxy.readResolve() substitutes it back to a JSScript with compiled code (by compiling again).
| /** recompiles from source */ | ||
| @Serial | ||
| private Object readResolve() { | ||
| Context cx = Context.enter(); |
There was a problem hiding this comment.
I may not be following, but won't this result in all interpreted scripts being deserialized into compiled scripts?
Also, we (perhaps controversially) made Context Closeable so you can just use a try-with-resources here.
There was a problem hiding this comment.
No, an interpreted JSScript goes in and comes out in the normal way. A compiled JSScript doesn't go in. JSScript.writeReplace() substitutes it by a CompiledScriptProxy on the way in, and CompiledScriptProxy.readResolve() substitutes it back to a compiled JSScript after it came out.
There was a problem hiding this comment.
Yes, I'll do try-with-resource.
…ion as interpreted JSScript, and compiled comes back as compiled.
|
It looks like we could use a rebase here, since the test262 directory seems to be at a different place in your branch versus the one that we're testing against. |
|
I think this is a laudable effort, but there are some things that we should think about if we want to actually support serialisation for continuations, and there are likely some other bugs which would also need to be fixed. Since continuations only work under interpreted mode I'm not sure if it's worth the complexity of handling compiled functions, it might be wiser to simply throw an error if the script is compiled, but I think there is more we need to consider even among interpreted scripts. If we round trip a script are we also round tripping the entire scope and context from which it was reachable? I.e. does it matter if cached template literals and symbols do not maintain their identity? I think our template literals may already be utterly broken because I think we're caching In preparation for introducing a new interpreter we've also changed some things around. It might be best to add something to descriptor to return if it is simply serialisable or requires recompilation which would then ask that question of both the If we do want to support serialisation of compiled scripts should we not have similar functionality for functions? I don't know how easy it is to generate a compiled function which would not also cause its script to remain reachable, but I can believe it's possible. Finally, you might also want to rebase your changes rather than a merge down as this tends to make PRs much easier to manage and understand. I did this for you at https://github.com/aardvark179/rhino/tree/aardvark179-jsscript-rebase, if that helps, and removed the (accidental?) update to test262 commit. |
|
After thinking about it more while on on my lunchtime walk I was going to post some more concerns around the order in which objects might be encountered during serialisation and what the effects of that might be, but I think these problem might be avoidable by moving where we perform the write replacement. If we were to move the problem to |
This is for #2402
It makes JSScript serialisable.
There are two cases to consider:
For interpreted mode, there is not much to fix: just JSScript implements Serializable
In compiled mode, the JavaScript is compiled to a class in Java bytecode. For serialization I considered putting the bytes of the class into serialisation. But that became too complicated and unclear. A much simpler solution is: I don't need the compiled class, because all the information is in the JavaScript program text. So at serialisation, I forget the compiled stuff (JSScript is substituted by CompiledScriptProxy), and at deserialisation, from the JavaScript text, I compile it again. The JavaScript text is available in JSDescriptor.
Compiling again is a slow step, I know, but maybe it's done later or in a different place, so anyway it seems a clean solution to me. Anyway, implementing serialisation for compiled mode this way makes something slow that before was impossible, so it's not making any use case worse.