Adding instVar to ClassDescription breaks ClassBuilder
Last updated at 12:52 am UTC on 17 January 2006
Adding an instVar to ClassDescription worked in Squeak3.2 – however in Squeak3.4 adding a instVar to ClassDescription breaks in a funky way. The system begins to re-compile all classes and runs into a DNU exception.
2/28/2003 A fix and unit-tests are available (scroll down) [bkv]
Andreas Raab looked into issues related to this bug last fall and concluded it had to do with MetaClass re-shaping issues.. [bkv]
Nathanael Scharli has run into this issue with his work on To Traits Or Not To Traits. [bkv]
Hannes Hirzel posted a SUnit test for this bug to the squeak-dev list on 2/24/2003. [bkv]
Here's the code, minus the funky bang characters, [bkv]:
From Squeak3.2 of 11 July 2002 [latest update: #4956] on 24 February
2003 at 4:22:07 pm'
TestCase subclass: #BCCMaddInstVar
BCCMaddInstVar methodsFor: 'as yet unclassified' stamp: 'hjh 2/24/2003
self assert: (ClassDescription isKindOf: ClassDescription).
self assert: (Object isKindOf: Object).
self deny: (OrderedCollection isKindOf: OrderedCollection).
ClassDescription addInstVarName: 'myAdditionalInstVar'.
ClassDescription removeInstVarName: 'myAdditionalInstVar'.
Andreas posted updated investigation notes to the squeak-dev list on 2/27/2003: [bkv]
Here's an update on this problem. I could trace it down to a very obscure breakage of the superclass/subclass invariant which can only happen when you recompile any of the essential behaviors (Behavior, ClassDescription, Class).
The short version of it is that ClassBuilder assumes it can see all the subclasses of some class (this is obviously required since if it doesn't then things will break horribly as instances of some classes are being reshaped and others aren't). 
However, during the "main mutation process" ClassBuilder removes the class to reshape from its (old) superclass and later puts back under the new superclass. So far so good. Except that when we recompile the meta classes, we actually use the non-metaclass hierarchy for determining the subclasses of some metaclass (e.g., the subclasses of meta class "Foo class" are the meta classes of Foo's subclasses "Foo subclasses collect:[:each| each class]").
This leads to an "interesting" effect when we recompile any of the
aforementioned behaviors. When we recompile (for example)ClassDescription we recompile all the subclasses of Class (the meta class hierarchy) as well.
However, at the time when we recompile "Behavior class" we don't see
"ClassDescription class" as one of its subclasses - because while
recompiling we have removed ClassDescription from its superclass (Behavior).
So I actually tried to simply "remove the removal" but apparently that's not quite enough. I haven't had the time to go beyound this but it is clear that the problem we are seeing is an (accidental) breaking of the superclass/subclass invariant (when you look at the errors real close you can see that it is caused by a meta class which wasn't reshaped).
 Because of the importance of the superclass/subclass invariant I
actually think that #addSubclass: and #removeSubclass: should really be private messages. To me these look "just like the others" but using those without knowing what you do means you're going to break the system a _long_ time after you did it. Similarly with "Class new" or any of these messages - they ought to be screened and either raise errors or do something sensible.
So at this point it is clear what is causing the problem and at least what part of the solution must be - namely _strict_ adherence to the
Andreas' description of how he debugged this, posted to the squeak-dev list on 2/27/2003. [bkv]
Well, I recreated the problem, saw that the error you get was a recursive one, deduced that it must be happening in ContextPart>>printOn: put an exception handler in there to simply print "" (this might actually be generally helpful) and then I could see that the problem originated from Metaclass>>name which apparently tried to concatenate a Dictionary with a
String. Evaluating "thisClass instVarAt: ..." in the debugger (since you can't inspect it) showed me that the values of every instVar were shifted by one from what it should be which is the exact effect you see when you "forget" to reshape a subclass of some other class. From there, you can start hacking ClassBuilder - I put a halt in there if some class' name wasn't a stringy object. That showed me the "first place where the problem occurs" - e.g., the last class after which it gets broken. In this halt you see that (and which) the meta classes are broken and the stack tells you the rest of the story.
Andreas posted a fix for this to the squeak-dev list on 2/27/2003, entitled "[FIX] ClassBuilder problem". [bkv]
Here's that fix as an installable SAR file: MetaClassBuilderFix.sar [bkv]
Andreas posted a set of SUNit tests for ClassBuilder to the squeak-dev list on 2/27/2003 [bkv]
3 out of 3 tests pass in a 3.4gamma image! [bkv]