[Pharo-project] Fuel - class loading issue when superclass changed inst vars

Eliot Miranda eliot.miranda at gmail.com
Tue Mar 6 17:22:06 EST 2012


Hi Frank,

On Tue, Mar 6, 2012 at 1:49 PM, Frank Shearar <frank.shearar at gmail.com>wrote:

> On 6 March 2012 18:27, Eliot Miranda <eliot.miranda at gmail.com> wrote:
> >
> >
> > On Tue, Mar 6, 2012 at 3:05 AM, Henrik Johansen
> > <henrik.s.johansen at veloxit.no> wrote:
> >>
> >>
> >> On Mar 6, 2012, at 12:03 PM, Henrik Johansen wrote:
> >>
> >> >>
> >> >
> >> > 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. :(
> >>
> >> Errr, BOSS, not Parcels.
> >
> >
> > Right. What Parcels do is shape-change instances *and* rescan methods to
> fix
> > up inst var offsets.  When a parcel is saved the "signature" of classes
> of
> > instances there-in are saved so that the parcel contains all the inst var
> > names of the class and its superclass.  If on load the superclass chain
> has
> > a different set of inst vars then lost inst vars are omitted and added
> inst
> > vars are nil in the materialized instances.  If a Parcel contains a full
> > class (not just instances) that has methods, and the superclass chains
> inst
> > vars have changed  then these methods are "rescanned" (disassembled, and
> > reassembled, not using the compiler) and inst var offsets are fixed up,
> > missing inst vars getting changed into undeclared variable references (I
> > think; at least this is what *should* happen; its what happens when one
> > removes an inst var that is still referenced from methods).
> >
> > What's missing in the parcel scheme is any hook to allow the user to
> process
> > shape-changing instances with the values of lost inst vars in hand.
> >  Presumably there will be cases when those values are essential to any
> > schema migration.  Personally I would want to decouple schema migration
> and
> > add it as a post-processing step, which would imply that the materializer
> > would offer a service (materialize in a special mode) where it
> constructed a
> > dictionary from materialized and shape-changed instance to in parcel
> state
> > (e.g. an Array of inst var values as they occurred in the parcel).  Then
> the
> > materialized instance could be migrated after the fact, with the default
> > behaviour being analogous to what the system does now on class
> redefinition
> > (values of deleted inst vars are lost, aded inst vars are nil).
>
> This sounds a bit like CLOS'  UPDATE-INSTANCE-FOR-REDEFINED-CLASS [1],
> which is a generic function (for our purposes just think "something a
> bit like a method") that you define and is invoked by the machinery
> involved in changing/redefining classes.
>
> A colleague told me about this part of CLOS the other day when
> discussing class-based OO: it's the sort of thing we need in at least
> the Browser, so one can change the shape of existing instances and
> ensure the new class invariants are maintained.


But Smalltalk has done without this for 40 years.  It has supported
existing instance redefinition but no-one has felt the need to provide a
scheme for redefinition in the IDE.  In any case it is relatively trivial
to script it, e.g.:

| instanceState |
instanceState := IdentityDictionary new.
myClass allInstancesDo: [:i| instanceState at: i put: ((1 to: myClass instSize)
collect: [:ivi| i instVarAt: ivi])].
myClass superclass subclass: myClass name instanceVariableNames: myClass
instVarNamesString, ' theExtraInstVar' [...].
myClass compile: sourceForupdateToNewFormatFrom.
myClass allInstancesDo: [:i| i updateToNewFormatFrom: (instanceState at: i]

So if and when you need it you can roll your own (and I've never needed it;
but I have wanted the refactoring browser to preserve inst vars on push
up/push dwn).

However, collections of persistent objects are a different kettle of fish.
 It is much more likely to need instance migration on load, especially for
library objects that the user is merely using and whose implementation has
evolved over time (e.g. UI elements such as morphs).


> (For instance, for
> "purely functional" objects, you really don't want to use reflection
> to initialise new instvars from outside the object.)


But the class builder uses an unprotected interface (instVarAt: and
instVarAt:put:).  So this turns out not to be an issue.



> So you'd change
> the class definition, supply a way of handling the schema migration,
> and then the image would change the class definition and run the
> migration.
>
> frank
>
> [1] http://clhs.lisp.se/Body/f_upda_1.htm
>
> > This reminds me of a bug with the ClassBuilder/RefactoringBrowser
> > combination.  If one refactors pushing an inst var up to a superclass or
> > down to subclasses, the values of te inst var in instances are lost
> because
> > the class change is in fact done as two changes, a deletion followed by
> an
> > addition.  This again could be fixed in a wrapper, building a dictionary
> > from instance to values, performing the set of class changes, and then
> > restoring the inst var state from the dictionary.
> >
> >
> >>
> >> My bad.
> >>
> >> Cheers,
> >> Henry
> >>
> >>
> >
> >
> >
> > --
> > best,
> > Eliot
> >
>
>


-- 
best,
Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.pharo.org/pipermail/pharo-dev_lists.pharo.org/attachments/20120306/fe4bb530/attachment-0001.html>


More information about the Pharo-dev mailing list