Metaclass.st
author claus
Fri, 16 Jul 1993 11:39:45 +0200
changeset 1 a27a279701f8
child 2 6526dde5f3ac
permissions -rw-r--r--
Initial revision

"
 COPYRIGHT (c) 1988-93 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Class subclass:#Metaclass
       instanceVariableNames:''
       classVariableNames:''
       poolDictionaries:''
       category:'Kernel-Classes'
!

Metaclass comment:'

COPYRIGHT (c) 1988-93 by Claus Gittinger
              All Rights Reserved

every class-class is a subclass of Metaclass
- this adds support for creating new subclasses or changing the definition
of an already existing class.

%W% %E%
'!

!Metaclass methodsFor:'creating classes'!

name:newName inEnvironment:aSystemDictionary
             subclassOf:aClass
             instanceVariableNames:stringOfInstVarNames
             variable:variableBoolean
             words:wordsBoolean
             pointers:pointersBoolean
             classVariableNames:stringOfClassVarNames
             poolDictionaries:stringOfPoolNames
             category:categoryString
             comment:commentString
             changed:changed

    |newClass newMetaclass nInstVars nameString classSymbol oldClass 
     allSubclasses classVarChange instVarChange superClassChange newComment
     upd|

    nInstVars := stringOfInstVarNames countWords.
    nameString := newName asString.
    classSymbol := nameString asSymbol.
    newComment := commentString.

    (aSystemDictionary includesKey:classSymbol) ifTrue:[
        oldClass := aSystemDictionary at:classSymbol.
        (newComment isNil and:[oldClass isBehavior "isKindOf:Class"]) ifTrue:[
            newComment := oldClass comment
        ]
    ].

    "create the metaclass first"
    newMetaclass := Metaclass new.
    newMetaclass setSuperclass:(aClass class).
    newMetaclass instSize:(aClass class instSize).
    newMetaclass flags:0.            "not indexed"
    newMetaclass setName:(nameString , 'class').
    newMetaclass classVariableString:'' "stringOfClassVarNames".
    newMetaclass setComment:newComment category:categoryString.

    newClass := newMetaclass new.
    newClass setSuperclass:aClass.
    newClass instSize:(aClass instSize + nInstVars).

    (variableBoolean == true) ifTrue:[
        pointersBoolean ifTrue:[
            newClass flags:4         "pointerarray"
        ] ifFalse:[
            wordsBoolean ifTrue:[
                newClass flags:2     "wordarray"
            ] ifFalse:[
                newClass flags:1     "bytearray"
            ]
        ]
    ] ifFalse:[
        "this is a backward compatible hack"

        (variableBoolean == #float) ifTrue:[
            newClass flags:6         "float array"
        ] ifFalse:[
            (variableBoolean == #double) ifTrue:[
                newClass flags:7     "double array"
            ] ifFalse:[
                (variableBoolean == #long) ifTrue:[
                    newClass flags:3     "long array"
                ] ifFalse:[
                    newClass flags:0   
                ]
            ]
        ].
    ].

    newClass setName:nameString.
    (nInstVars ~~ 0) ifTrue:[
        newClass instanceVariableString:stringOfInstVarNames
    ].
    oldClass notNil ifTrue:[
        "setting first will make new class clear obsolete classvars"
        newClass setClassVariableString:(oldClass classVariableString)
    ].
    newClass classVariableString:stringOfClassVarNames.

    oldClass notNil ifTrue:[
        "dont have to flush if class is brand-new"

        ObjectMemory flushCaches.
    ].

    aSystemDictionary at:classSymbol put:newClass.

    self addChangeRecordForClass:newClass.

    oldClass isNil ifTrue:[
        commentString notNil ifTrue:[
            newClass comment:commentString
        ]
    ] ifFalse:[
        "if only category/comment has changed, do not recompile .."

        (oldClass superclass == newClass superclass) ifTrue:[
          (oldClass instSize == newClass instSize) ifTrue:[
            (oldClass flags == newClass flags) ifTrue:[
              (oldClass name = newClass name) ifTrue:[
                (oldClass instanceVariableString = newClass instanceVariableString) ifTrue:[
                  (oldClass classVariableString = newClass classVariableString) ifTrue:[
                    (newComment ~= oldClass comment) ifTrue:[
                        oldClass comment:newComment
                    ]. 
                    oldClass category:categoryString. 
                    aSystemDictionary at:classSymbol put:oldClass.
                    oldClass changed.
                    ^ oldClass
                  ]
                ]
              ]
            ]
          ]
        ].

        (newComment ~= oldClass comment) ifTrue:[
            newClass comment:newComment
        ].

        upd := Class updateChanges:false.

        superClassChange := oldClass superclass ~~ newClass superclass.

        classVarChange := oldClass classVariableString ~= newClass classVariableString.

        classVarChange ifTrue:[
            " no need to recompile if classvars are added "
            classVarChange := (newClass classVariableString startsWith: oldClass classVariableString) not
        ].
        classVarChange := classVarChange or:[superClassChange].
        classVarChange := classVarChange or:[self anyInvalidatedMethodsIn: oldClass class].

        classVarChange ifTrue:[
            "must recompile class-methods"
            self copyInvalidatedMethodsFrom:(oldClass class) for:newMetaclass.
            newMetaclass recompile
        ] ifFalse:[
            "class methods still work"
            self copyMethodsFrom:(oldClass class) for:newMetaclass
        ].

        instVarChange := oldClass instanceVariableString ~= newClass instanceVariableString.
        instVarChange ifTrue:[
            " no need to recompile if instvars are added "
            instVarChange := (newClass instanceVariableString startsWith: oldClass instanceVariableString) not
        ].
        instVarChange := instVarChange or:[superClassChange].
        instVarChange := instVarChange or:[self anyInvalidatedMethodsIn: oldClass].

        (instVarChange or:[classVarChange]) ifTrue:[
            "must recompile instance-methods"
            self copyInvalidatedMethodsFrom:oldClass for:newClass.
            newClass recompile
        ] ifFalse:[
            "instance methods still work"
            self copyMethodsFrom:oldClass for:newClass
        ].

        "get list of all subclasses - do before superclass is changed"

        allSubclasses := oldClass allSubclasses.

        "update superclass of immediate subclasses"

        oldClass subclassesDo:[:aClass |
            aClass superclass:newClass
        ].

        "update instSizes and recompile all subclasses if needed"

        "for subclasses we must be strict"
        classVarChange := oldClass classVariableString ~= newClass classVariableString.
        classVarChange := classVarChange or:[superClassChange].

        "for subclasses we must be strict since offsets change"
        instVarChange := oldClass instanceVariableString ~= newClass instanceVariableString.
        instVarChange := instVarChange or:[superClassChange].

        allSubclasses do:[:aClass |
            aClass instSize:(aClass instSize + (newClass instSize - oldClass instSize)).
            (classVarChange or:[self anyInvalidatedMethodsIn:aClass class]) ifTrue:[
                aClass class recompileAll
            ].
            (classVarChange or:[instVarChange or:[self anyInvalidatedMethodsIn: aClass]]) ifTrue:[
                aClass recompileAll
            ]
        ].

        ObjectMemory flushCaches.
        Class updateChanges:upd
    ].
    oldClass isNil ifTrue:[
        Smalltalk changed
    ] ifFalse:[
        oldClass setName:(oldClass name , '-old')
    ].
    ^ newClass
!

new
    "returs a new class class"
    |newClass|

    newClass := self basicNew.
    newClass setSuperclass:(Object class)
                 selectors:(Array new:0)
                   methods:(Array new:0)
                  instSize:0
                     flags:0.
    newClass setComment:(self comment) category:(self category).
    ^ newClass
! !

!Metaclass methodsFor:'class instance variables'!

instanceVariableNames:aString
    "changing / adding class-inst vars -
     this actually creates a new metaclass and class"

    |newClass newMetaclass nClassInstVars oldClass 
     allSubclasses upd t oldVars sizeChange|

    oldVars := self instanceVariableString.
    aString = oldVars ifTrue:[^ self].

    nClassInstVars := aString countWords.
    sizeChange := nClassInstVars ~~ oldVars countWords.

    "create the new metaclass"
    newMetaclass := Metaclass new.
    newMetaclass setSuperclass:superclass.
    newMetaclass instSize:(superclass instSize + nClassInstVars).
    (nClassInstVars ~~ 0) ifTrue:[
        newMetaclass instanceVariableString:aString
    ].
    newMetaclass flags:0.            "not indexed"
    newMetaclass setName:name.
    newMetaclass classVariableString:classvars.
    newMetaclass category:category.
    newMetaclass setComment:comment.

    "find the class which is my sole instance"

    t := Smalltalk allClasses select:[:element | element class == self].
    (t size ~~ 1) ifTrue:[
        self error:'oops - I should have exactly one instance'.
        ^ nil
    ].
    oldClass := t anElement.

    "create a new class"
    newClass := newMetaclass new.
    newClass setSuperclass:(oldClass superclass).
    newClass instSize:(oldClass instSize).
    newClass flags:(oldClass flags).
    newClass setName:(oldClass name).
    newClass instanceVariableString:(oldClass instanceVariableString).
    newClass classVariableString:(oldClass classVariableString).
    newClass comment:(oldClass comment).
    newClass category:(oldClass category).

    ObjectMemory flushCaches.

    Smalltalk at:(oldClass name asSymbol) put:newClass.

    upd := Class updateChanges:false.

    (oldVars isBlank 
    or:[aString startsWith:oldVars]) ifTrue:[
        "there where none before or a new var has been added
         - methods still work"
        self copyMethodsFrom:self for:newMetaclass.
        self copyMethodsFrom:oldClass for:newClass
    ] ifFalse:[
        "recompile class-methods"
        self copyInvalidatedMethodsFrom:self for:newMetaclass.
        newMetaclass recompile.

        "recompile instance-methods"
        self copyInvalidatedMethodsFrom:oldClass for:newClass.
        newClass recompile
    ].

    "get list of all subclasses - do before superclass is changed"

    allSubclasses := oldClass allSubclasses.

    "update superclass of immediate subclasses"

    oldClass subclassesDo:[:aClass |
        aClass superclass:newClass
    ].

    "update instSizes and recompile all subclasses if needed"

    allSubclasses do:[:aClass |
        aClass class recompileAll.
        aClass recompileAll
    ].

    ObjectMemory flushCaches.
    Class updateChanges:upd.
    ^ newMetaclass
! !

!Metaclass methodsFor:'queries'!

isMeta
    "return true, if the receiver is some kind of metaclass;
     true is returned here. Redefines isMeta in Object"

    ^ true
! !

!Metaclass methodsFor:'private'!

copyMethodsFrom:oldClass for:newClass
    "when a class has changed, but metaclass is unaffected (i.e. classVars
     have not changed) there is no need to recompile them"

    newClass selectors:(oldClass selectors) methods:(oldClass methodDictionary)
!

copyInvalidatedMethodsFrom:oldClass for:newClass
    "when a class has been changed, copy all old methods into the new class
     - changing code to a trap method giving an error message;
     this allows us to keep the source while trapping uncompilable (due to
     now undefined instvars) methods"

    |trap trapCode trapByteCode|

    trap := Method compiledMethodAt:#invalidMethod.
    trapCode := trap code.
    trapByteCode := trap byteCode.

    newClass selectors:(oldClass selectors) methods:(oldClass methodDictionary).
    newClass methodDictionary do:[:aMethod |
        aMethod code:trapCode.
        aMethod literals:nil.
        aMethod byteCode:trapByteCode
    ]
!

anyInvalidatedMethodsIn:aClass
    "return true, if aClass has any invalidated methods in it"

    |trap trapCode trapByteCode|

    trap := Method compiledMethodAt:#invalidMethod.
    trapCode := trap code.
    trapByteCode := trap byteCode.

    aClass methodDictionary do:[:aMethod |
        trapCode notNil ifTrue:[
            (aMethod code == trapCode) ifTrue:[^ true]
        ].
        trapByteCode notNil ifTrue:[
            (aMethod byteCode == trapByteCode) ifTrue:[^ true]
        ]
    ].
    ^ false
! !