JavaClassReloader.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Fri, 24 May 2013 17:55:42 +0100
branchbuiltin-class-support
changeset 2629 cedb88626902
parent 2529 764ab6925cf5
child 2651 5363bd0388c3
permissions -rw-r--r--
Closing branch.

"
 COPYRIGHT (c) 1996-2011 by Claus Gittinger

 New code and modifications done at SWING Research Group [1]:

 COPYRIGHT (c) 2010-2011 by Jan Vrany, Jan Kurs and Marcel Hlopko
                            SWING Research Group, Czech Technical University in Prague

 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.

 [1] Code written at SWING Research Group contains a signature
     of one of the above copright owners. For exact set of such code,
     see the differences between this version and version stx:libjava
     as of 1.9.2010
"
"{ Package: 'stx:libjava' }"

Object subclass:#JavaClassReloader
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	category:'Languages-Java-Support'
!

Object subclass:#SingleClassReloader
	instanceVariableNames:'oldClass newClass mustMigrateInstances mustMigrateClass
		instFieldMapping staticFieldMapping'
	classVariableNames:''
	poolDictionaries:''
	privateIn:JavaClassReloader
!

Object subclass:#FieldMapping
	instanceVariableNames:'old new'
	classVariableNames:''
	poolDictionaries:''
	privateIn:JavaClassReloader::SingleClassReloader
!

!JavaClassReloader class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1996-2011 by Claus Gittinger

 New code and modifications done at SWING Research Group [1]:

 COPYRIGHT (c) 2010-2011 by Jan Vrany, Jan Kurs and Marcel Hlopko
                            SWING Research Group, Czech Technical University in Prague

 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.

 [1] Code written at SWING Research Group contains a signature
     of one of the above copright owners. For exact set of such code,
     see the differences between this version and version stx:libjava
     as of 1.9.2010

"
!

documentation
"
    A main workhorse for reloading (updating) java classes
    in running system.

    [author:]
        Jan Vrany <jan.vrany@fit.cvut.cz>

    [instance variables:]

    [class variables:]

    [see also:]

"
! !

!JavaClassReloader class methodsFor:'reloading'!

reload: oldClass with: newClass
    ^ self new reload: oldClass with: newClass

    "Created: / 20-02-2012 / 23:29:01 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaClassReloader methodsFor:'reloading'!

reload: oldClass with: newClass

    | newClassToInstall |

    newClassToInstall := SingleClassReloader new reload: oldClass with: newClass.
    "Also, reload all subclasses - fields may have changed!!"
    newClassToInstall ~~ oldClass ifTrue: [
        oldClass mergeVersionsWith: newClassToInstall.
        oldClass subclassesDo:[:oldSubclass|
            | newSubclass |

            newSubclass := oldSubclass copy.
            newSubclass setSuperclass: newClassToInstall.
            newSubclass instSize: (newClassToInstall instSize + oldSubclass fields size).
            JavaVM registry registerClass: newSubclass.
        ].
    ].
    ^newClassToInstall.

    "Created: / 04-04-2012 / 01:32:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 14-04-2013 / 12:34:18 / Marcel Hlopko <marcel.hlopko@fit.cvut.cz>"
    "Modified (format): / 14-04-2013 / 14:27:20 / Marcel Hlopko <marcel.hlopko@fit.cvut.cz>"
! !

!JavaClassReloader::SingleClassReloader methodsFor:'private'!

invalidate

    | anyInvalidated |

    anyInvalidated := false.
    JavaVM registry classesDo:[:class|
        anyInvalidated := anyInvalidated | (self invalidateClass: class).
    ].
    anyInvalidated ifTrue:[ObjectMemory flushCaches].

    "Created: / 21-02-2012 / 09:47:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

invalidateClass: javaClass

    ^javaClass constantPool invalidateForClass: oldClass name

    "Created: / 21-02-2012 / 09:58:40 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

migrate
    "Possibly migrate instances and class. Return the class that should
     be installed in registry once reloader finishes his job.
     
     At this point, all references are already invalidated (see #reload)
     "

    mustMigrateInstances ifTrue:[
        self migrateInstances.
        mustMigrateClass ifTrue:[
            self migrateClass
        ].
        ^newClass.
    ].

    mustMigrateClass ifTrue:[
        self migrateClass.
        ^newClass.
    ].
    self updateOldClass.
    ^oldClass.

    "Created: / 20-02-2012 / 23:40:03 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (comment): / 16-12-2012 / 17:39:26 / Marcel Hlopko <marcel.hlopko@fit.cvut.cz>"
!

migrateClass

    "/self error:'Not yet supported'

    "Created: / 21-02-2012 / 11:04:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

migrateInstance: object

    self assert: object class == oldClass.

    "Created: / 30-03-2012 / 19:42:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

migrateInstances

    oldClass allInstancesDo:[:i|
        self migrateInstance:i.
    ].

    "Created: / 21-02-2012 / 11:04:47 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

prepare
    "Analyze and prepare data for reloading" 

    self prepareInstFieldMapping.
    self prepareStaticFieldMapping.

    "Created: / 20-02-2012 / 23:40:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

prepareFieldMap: fields
    | map |

    map := Dictionary new.
    fields do:[ :field | map at: field name put: field ].
    ^map

    "Created: / 21-02-2012 / 09:42:17 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

prepareInstFieldMapping
    "Scans both old and new class inst fields and create a mapping.
     Sets mustMigrateInstances"

    | newFields |

    mustMigrateInstances := false.
    instFieldMapping := Set new.
    newFields := self prepareFieldMap: newClass allFields.
    oldClass fields do:[:old|
        | new mapping |

        new := newFields at: old name ifAbsent:[nil].
        new notNil ifTrue:[ newFields removeKey: old name ].
        mapping := FieldMapping old: old new: new.
        mustMigrateInstances := mustMigrateInstances or:[mapping mustMigrate].
        instFieldMapping add: mapping.
    ].
    "Remaining fields are new, i.e., does not exist in
     old class. Add them to the mapping"
    newFields do:[:new|
        instFieldMapping add: (FieldMapping old: nil new: new).
        mustMigrateInstances := true
    ].

    "Created: / 21-02-2012 / 09:32:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

prepareStaticFieldMapping
    "Scans both old and new class inst fields and create a mapping.
     Sets mustMigrateInstances"

    | newFields |

    mustMigrateClass := false.
    staticFieldMapping := Set new.
    newFields := self prepareFieldMap: newClass allStaticFields.
    oldClass staticFields do:[:old|
        | new mapping |

        new := newFields at: old name ifAbsent:[nil].
        new notNil ifTrue:[ newFields removeKey: old name ].
        mapping := FieldMapping old: old new: new.
        mustMigrateClass:= mustMigrateClass or:[mapping mustMigrate].
        staticFieldMapping add: mapping.
    ].
    "Remaining fields are new, i.e., does not exist in
     old class. Add them to the mapping"
    newFields do:[:new|
        staticFieldMapping add: (FieldMapping old: nil new: new).
        mustMigrateClass:= true
    ].

    "Created: / 21-02-2012 / 09:45:19 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

updateOldClass
    "at this point we know that both classes have same layout. So we can
     simply copy methods and other info from new class to old one. 
     References must be flushed anyway!!"

    | oldMethods newMethods problems |

    oldMethods := oldClass methodDictionary.
    newMethods := newClass methodDictionary copy.

    newMethods keysDo:[:selector|
        | oldM newM |

        oldM := oldMethods at: selector ifAbsent:[nil].
        oldM notNil ifTrue:[
            newM := newMethods at: selector.
            (oldM canBeUpdatedFrom: newM) ifTrue:[
                oldM updateFrom: newM.
                newMethods at: selector put: oldM.
            ]
        ]
    ].
    newMethods do:[:m|m setJavaClass: oldClass].
    oldClass setMethodDictionary: newMethods.
    oldClass setConstantPool: newClass constantPool.
    oldClass setSource: newClass sourceString.

    "Also, transfer all problems of newClass to newClass so
     the highligher shows them..."
    JavaCompiler notNil ifTrue:[
        problems := JavaCompiler problems at: newClass ifAbsent:[nil].
        problems notNil ifTrue:[
            JavaCompiler problems removeKey: newClass.
            JavaCompiler problems at: oldClass put: problems.
        ]
    ].

    "Created: / 16-12-2012 / 17:36:52 / Marcel Hlopko <marcel.hlopko@fit.cvut.cz>"
    "Modified: / 19-04-2013 / 09:44:54 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaClassReloader::SingleClassReloader methodsFor:'reloading'!

reload

    self prepare.
    self invalidate.
    ^self migrate.

    "Created: / 20-02-2012 / 23:29:37 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

reload: oldClassA with: newClassA

    oldClass := oldClassA.
    newClass := newClassA.
    ^ self reload.

    "Created: / 20-02-2012 / 23:29:37 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaClassReloader::SingleClassReloader::FieldMapping class methodsFor:'instance creation'!

old: old new:new

    ^self new old: old new: new.

    "Created: / 21-02-2012 / 09:20:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaClassReloader::SingleClassReloader::FieldMapping methodsFor:'accessing'!

new
    ^ new
!

new:something
    new := something.
!

old
    ^ old
!

old:something
    old := something.
!

old:oldArg new:newArg 
    old := oldArg.
    new := newArg.
! !

!JavaClassReloader::SingleClassReloader::FieldMapping methodsFor:'queries'!

mustMigrate
    "Returns true if the field must be migrated, false otherwise"

    | oldD newD |

    (old isNil or:[new isNil]) ifTrue:[ 
        ^ true "Either one is missing, must migrate"
    ].

    old index ~~ new index ifTrue:[
        ^true "Offsets changed, must migrate"
    ].

    oldD := old descriptor.
    newD := new descriptor.

    oldD = newD ifTrue:[
        ^false"Same descriptor, the easy case"
    ].

    (oldD first == $L and: [newD first == $L]) ifTrue:[
        ^false"Both object types, who cares about type safety in Smalltalk?"
    ].

    1 to: (oldD size min: newD size) do:[:i|
        ((oldD at: i) == $L and: [ (newD at: i) == $L ]) ifTrue:[
            ^false"Both object types, who cares about type safety in Smalltalk?"
        ].
        ((oldD at: i) ~~ $[ or: [ (newD at: i) ~~ $[ ]) ifTrue:[
            ^true"Different primitive/array types, must migrate"
        ].
    ].

    ^false

    "Created: / 21-02-2012 / 10:57:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaClassReloader class methodsFor:'documentation'!

version_CVS
    ^ '$Header: /cvs/stx/stx/libjava/JavaClassReloader.st,v 1.2 2013-02-25 11:15:31 vrany Exp $'
!

version_HG

    ^ '$Changeset: <not expanded> $'
!

version_SVN
    ^ '§Id§'
! !