[comp.lang.smalltalk] Adding instance variables to Sm/V classes

markm@cs.qmc.ac.uk (Mark Magennis) (08/02/89)

Here is a method for adding instance variables to classes which
have instances. I have successfully used it with Smalltalk/V
Mac but there are a few problems with it.

1. Why does it not work with Form class?
I postulate that it may be to do with the system expecting certain
instances of Form or its subclasses which it keeps (eg Display,
Screen) to be a particular size.

2. Are there any other classes for which it won't work?
I've tried it on a few classes including DemoClass and a class of
my own called Foo with a subclass, SubFoo and an instance of each
in the global variables AFoo and ANewFoo.

3. Is there an easy way of changing the superclass of a class?
Class has this instance method:
subclass:instanceVariableNames:classVariableNames:poolDictionaries:
which the handbook claims can be used to 'Create or modify the
class [specified in subclass field] to be a subclass of the
reciever...'. When I try this I get a 'Cannot change
superclass' notifier.

4. If anyone has any comments or sugestions I'd be pleased to
hear them.

***************************************************************
This code may be used to add instance variables to a class
which has instances. I have used it with classes of my own and
with DemoClass and it works well, the new instance variables
being available for the use of the existing instances.

WARNING: THIS WILL NOT WORK FOR ALL CLASSES!

In particular it will cause Smalltalk/V to fall over if
attempted with class Form. There may be other classes for which
it will not work too, particularly 'low level' classes which
the system accesses in its own mysterious but probably
efficient way.

This particular code is for classes with NO subclasses. If
subclasses exist then these should be dealt with in the same
way unless you can write some code for changing a classes
superclass.

FIRST STEP:
Create the class templates for the temporary class NewFoo and
its subclasses by editting those for Foo and its subclasses,
altering the class and subclass names and adding whatever new
instance variables you want.

THEN EXECUTE THE FOLLOWING CODE FOR THE CLASS AND EACH SUBCLASS:

|copy|

"change instances of Foo into instances of NewFoo"
Foo allInstances do: [:anInstance |
    copy := NewFoo new.
    1 to: Foo instSize do: [:index |
        copy instVarAt: index
                   put: (anInstance instVarAt: index)].
    anInstance become: copy].

"copy instances from Foo to NewFoo"
Foo allInstances do: [:anInstance |
    copy := NewFoo new.
    1 to: Foo instSize do: [:index |
        copy instVarAt: index
                   put: (anInstance instVarAt: index)].
    anInstance become: copy].

"copy instance methods of Foo into NewFoo"
Foo selectors do: [ :each |
    NewFoo addSelector: each
            withMethod: (Foo compiledMethodAt: each)].

"copy class methods of Foo into NewFoo"
Foo class selectors do: [ :each |
    NewFoo class addSelector: each
                  withMethod: (Foo class compiledMethodAt: each)].

"copy values of Foo class variables to class variables of NewFoo"
Foo classVarNames do: [ :aClassVar |
    NewFoo classPool at: aClassVar
                    put: (Foo classPool at: aClassVar)].


THEN DO THE FOLLOWING:

Foo removeFromSystem.  "remove class Foo and rename NewFoo"
NewFoo rename: 'Foo'.

Also rename the temporary subclasses.
***************************************************************







-- 
UUCP:      markm@qmc-cs.uucp                       | Computer Science Dept
ARPA:      markm%cs.qmc.ac.uk@nsfnet-relay.ac.uk   | Queen Mary College
JANET:     markm@uk.ac.qmc.cs                      | Mile End Road
Voice:     +44 1 975 5241 (Direct Dial)            | London E1 4NS

markm@cs.qmc.ac.uk (Mark Magennis) (08/02/89)

 Whoops! The first section of that code accidentally crept in
twice. You can see the two bits which are identical quite
easily (to do with swopping instances from one class to
another), just remove one. Such are the problems of cutting and
pasting between Sm/V Mac and ded, a dubious endeavour which
it's probably best to avoid completely.
-- 
UUCP:      markm@qmc-cs.uucp                       | Computer Science Dept
ARPA:      markm%cs.qmc.ac.uk@nsfnet-relay.ac.uk   | Queen Mary College
JANET:     markm@uk.ac.qmc.cs                      | Mile End Road
Voice:     +44 1 975 5241 (Direct Dial)            | London E1 4NS

markm@cs.qmc.ac.uk (Mark Magennis) (08/03/89)

How embarrasing, sorry.

I'm afraid the previous message I sent on this subject was
completely wrong. I copied an older and non-working method. I
can't even delete it either, it tells me that it's someone
elses message. In that case I wont take the blame :-)

Here is code that definitely works for Menu class among others,
SO FAR. That means my image hasn't fallen over yet, after a
lot of menu work.

If you want to try it please work on a spare image until you
are absolutely sure it is safe with whatever class you wish to
use it.

Again, this is for a class (Menu) without subclasses.

***************************************************************

The new method in Class, removeAndBecome: aClass is a copy of
removeFromSystem but with the two occurences of DeletedClass
changed to aClass.

|newInstance|

"Change instances of Menu into instances of NewMenu"

Menu allInstances do: [:anInstance |
    newInstance := NewMenu new.
    1 to: Menu instSize do: [:index |
        newInstance instVarAt: index
                   put: (anInstance instVarAt: index)].
    anInstance become: newInstance].

"Copy instance methods from Menu to NewMenu "

Menu selectors do: [ :each |
    NewMenu addSelector: each withMethod: (Menu compiledMethodAt: each)].

"Copy class methods from Menu to NewMenu"

Menu class selectors do: [ :each |
    NewMenu class addSelector: each
                   withMethod: (Menu class compiledMethodAt: each)].

"Copy class variable values from Menu to NewMenu"

Menu classVarNames do: [ :aClassVar |
    NewMenu classPool at: aClassVar put: (Menu classPool at: aClassVar)].

"Set the instance methods' class field to NewMenu"

Menu selectors do: [ :each |
    (Menu compiledMethodAt: each) classField: #NewMenu].

"Set the class methods' class field to NewMenu"

Menu class selectors do: [ :each |
    (Menu class compiledMethodAt: each) classField: NewMenu class].

"Copy the structure variable"

NewMenu structure: Menu structure.
NewMenu class structure: Menu class structure.

"Remove Menu, change references to become NewMenu and rename
NewMenu"

Menu removeAndBecome: NewMenu. "*A new method in Class*"
NewMenu rename: #Menu.

"Reset the instance methods' class field to Menu"

Menu selectors do: [ :each |
    (Menu compiledMethodAt: each) classField: #Menu].

"Reset the class methods' class field to Menu"

Menu class selectors do: [ :each |
    (Menu class compiledMethodAt: each) classField: Menu class].

***************************************************************

-- 
UUCP:      markm@qmc-cs.uucp                       | Computer Science Dept
ARPA:      markm%cs.qmc.ac.uk@nsfnet-relay.ac.uk   | Queen Mary College
JANET:     markm@uk.ac.qmc.cs                      | Mile End Road
Voice:     +44 1 975 5241 (Direct Dial)            | London E1 4NS