On Mar 6, 2012, at 4:06 AM, Martin Dias wrote: > Hello, > > I'd like to discuss about a problem found when loading classes with Fuel (for > people who don't know it: http://rmod.lille.inria.fr/web/pier/software/Fuel). > > Fuel can serialize a class either as a global or as a "regular" object. In > the latter case, it saves the class with its compiled methods, including > their bytecodes. (This was demonstrated in > http://marianopeck.wordpress.com/2011/09/24/importing-and-exporting-packages-with-fuel/) > > Well, the issue I want to talk about happens when loading a class with a > method that uses an inherited instance variable. In a short example: > > - define class A with instVars 'x' > - define class B with a method that uses 'x'. > - serialize B in a file > - remove B from system > - redefine A with instVars 'y x' > - materialize B from the file > > -----> the bytecodes of B's method, that should refer to 'x', instead refer > to 'y' (because inst vars are encoded in bytecodes just as indexes) > > Of course, there are variations of this problem: the variable was whether > removed or renamed. > > > Some solutions: > 1) detect the change in a superclass and then: > a) just raise an error > b) compile based on attached source > c) decompile parse tree from bytecodes, fix it, and generate the method. > 2) serialize the parse tree *instead* of bytecodes, and always generate the > method when materializing. > > I am thinking on the use case of package sharing, like Parcels. Note that > since Fuel supports multiple strategies for serializing the same object, it > is not necessary to choose just one option to fit in every use case. > > - I'm not sure, but I guess Parcels makes something like 1c). > - The alternative 1a) doesn't require any compiler (in a bootstrap image is > good) > - I am tempted to explore 2), but not sure if that makes sense. > > I am curious about your opinions. > > Best regards, > Martín >
Parcels - Check a hash based on class layout vs equivalent hash for the stored method's class (stored with the method). If different: 1) check for existence of a backwards-compatability reader method, hand the old instance to it, and expect the old instance to be #become'd into something current, 2) raise an error if no such method exists. Now, the main problem with this scheme is it's left as an exercise to the user to come up with a procedure to ensure said backwards-compat reader method stays up to date as additional changes are made. Is it a big problem? Depends on your tests, and programmer diligence at any given day. Added inst vars are not a big deal, as if you forget them, there will usually be a nil #DNU somewhere down the line. (or you use lazy initialization, and everything works as expected even for existing instances) Removals/reorderings are a bigger issue, as the detection of failure (inst vars in wrong slots) are often far removed from the source of problems (forgetting to update the reader method), If seldomly used, it may even go unnoticed until the instance is next saved, at which time you're in real trouble (especially if saved alongside newly created ones, in which case there is no consistency). In general I think it's an ok scheme, but would like to see a solution more resilient to user-error. How to achieve that is an interesting topic, which I haven't yet found time to think through as thoroughly as I had wished/intended some months ago. :( Cheers, Henry
