The Trunk: TemporaryVariableScopeEditor-eem.1.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

The Trunk: TemporaryVariableScopeEditor-eem.1.mcz

commits-2
Eliot Miranda uploaded a new version of TemporaryVariableScopeEditor to project The Trunk:
http://source.squeak.org/trunk/TemporaryVariableScopeEditor-eem.1.mcz

==================== Summary ====================

Name: TemporaryVariableScopeEditor-eem.1
Author: eem
Time: 5 April 2012, 9:57:16.567 am
UUID: 3d601f26-29f5-4223-bd26-e890d5b765f0
Ancestors:

A useful utility to move method-level temps used in blocks
into their smallest enclosing block scope.  See the class side
of TempScopeEditor.  Extracted from the Cog package.

==================== Snapshot ====================

SystemOrganization addCategory: #TemporaryVariableScopeEditor!

ParseNodeVisitor subclass: #BlockNodeCollectingVisitor
        instanceVariableNames: 'blockNodes'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TemporaryVariableScopeEditor'!

!BlockNodeCollectingVisitor commentStamp: 'eem 4/3/2012 09:41' prior: 0!
A BlockNodeCollectingVisitor answers the BlockNodes in a parse tree in depth-first order.

Instance Variables
        blockNodes: <OrderedCollection | nil>

blockNodes
        - the sequence of block nodes in depth-first order.
!

----- Method: BlockNodeCollectingVisitor>>blockNodes (in category 'accessing') -----
blockNodes
        ^blockNodes!

----- Method: BlockNodeCollectingVisitor>>visitBlockNode: (in category 'visiting') -----
visitBlockNode: aBlockNode
        (blockNodes ifNil: [blockNodes := OrderedCollection new]) addLast: aBlockNode.
        super visitBlockNode: aBlockNode!

ParseNodeVisitor subclass: #ReadBeforeWrittenVisitor
        instanceVariableNames: 'readBeforeWritten written'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TemporaryVariableScopeEditor'!

!ReadBeforeWrittenVisitor commentStamp: '<historical>' prior: 0!
Answer the set of temporary variables that are read before they are written.!

----- Method: ReadBeforeWrittenVisitor>>readBeforeWritten (in category 'accessing') -----
readBeforeWritten
        ^readBeforeWritten ifNil: [IdentitySet new]!

----- Method: ReadBeforeWrittenVisitor>>visitAssignmentNode: (in category 'visiting') -----
visitAssignmentNode: anAssignmentNode
        anAssignmentNode value accept: self.
        anAssignmentNode variable isTemp
                ifTrue:
                        [written ifNil: [written := IdentitySet new].
                         written add: anAssignmentNode variable]
                ifFalse:
                        [anAssignmentNode variable accept: self]!

----- Method: ReadBeforeWrittenVisitor>>visitBlockNode: (in category 'visiting') -----
visitBlockNode: aBlockNode
        | savedWritten |
        savedWritten := written copy.
        super visitBlockNode: aBlockNode.
        written := savedWritten!

----- Method: ReadBeforeWrittenVisitor>>visitTempVariableNode: (in category 'visiting') -----
visitTempVariableNode: aTempVariableNode
        (aTempVariableNode isArg
         or: [written notNil
                and: [written includes: aTempVariableNode]]) ifTrue:
                [^self].
        readBeforeWritten ifNil:
                [readBeforeWritten := IdentitySet new].
        readBeforeWritten add: aTempVariableNode!

Object subclass: #TempScopeEditor
        instanceVariableNames: 'method methodNode out tempMap blockNodes sourceString soFar'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TemporaryVariableScopeEditor'!

!TempScopeEditor commentStamp: 'eem 4/5/2012 09:55' prior: 0!
A TempScopeEditor is a utility that makes a pass over code looking for temporaries that are declared at method level that could be declared at block level, i.e. variables that are only used within a block, and are assigned-to before they are read.  It rewrites any methods it finds fitting this pattern.  See the class-side methods edit and editPackage:.

Instance Variables
        blockNodes: <(SequenceableCollection of: BlockNode) | nil>
        method: <CompiledMethod>
        methodNode: <MethodNode>
        out: <WriteStream on: String>
        soFar: <Integer>
        sourceString: <String>
        tempMap: <IdentityDictionary of: TempVariableNode ->BlockNode>

blockNodes
        - the block nodes in the current method

method
        - the current method

methodNode
        - the current method's parse tree

out
        - the edited code stream

soFar
        - the point in the sourceString from which code has been copied to out

sourceString
        - the method's source code

tempMap
        - a map from indirect temps to their smallest enclosing scope
!

----- Method: TempScopeEditor class>>edit (in category 'as yet unclassified') -----
edit
        "Trawl the system for methods containing misdeclared temps and correct these methods."
        SystemNavigation default allSelect:
                [:m| | scanner |
                (m isQuick not
                and: [(scanner := InstructionStream on: m) scanFor:
                                [:b| b = 143 and: [scanner followingByte >= 16]]]) ifTrue:
                        [(self new forMethod: m) edit].
                false]!

----- Method: TempScopeEditor class>>editPackage: (in category 'as yet unclassified') -----
editPackage: packageInfoOrName
        "Trawl the given package for methods containing misdeclared temps and correct these methods."
        "self editPackage: #Cassowary"
        SystemNavigation new
                allMethodsSelect:
                        [:m| | scanner |
                        (m isQuick not
                        and: [(scanner := InstructionStream on: m) scanFor:
                                        [:b| b = 143 and: [scanner followingByte >= 16]]]) ifTrue:
                                [(self new forMethod: m) edit].
                        false]
                localToPackage: packageInfoOrName!

----- Method: TempScopeEditor>>anyScopes:outsideExtent: (in category 'editing') -----
anyScopes: referenceScopeDict outsideExtent: blockExtent
        ^referenceScopeDict notNil
           and: [referenceScopeDict notEmpty
           and: [referenceScopeDict anySatisfy:
                        [:set|
                        set anySatisfy: [:location| (blockExtent rangeIncludes: location) not]]]]!

----- Method: TempScopeEditor>>blockNode:isEnclosingScopeFor: (in category 'editing') -----
blockNode: aBlockNode isEnclosingScopeFor: aTempVariableNode
        ^((self
                anyScopes: (aTempVariableNode instVarNamed: 'readingScopes')
                outsideExtent: aBlockNode blockExtent)
        or: [self
                anyScopes: (aTempVariableNode instVarNamed: 'writingScopes')
                outsideExtent: aBlockNode blockExtent]) not!

----- Method: TempScopeEditor>>buildTempMap (in category 'editing') -----
buildTempMap
        "Build the map for moving remote temps. Each remote temp
         that should be moved is entered into the map referencing its
         smallest enclosing scope.  This may seem backwards but it
         means that the map is one-to-one, not one-to-many."
        | readBeforeWritten |
        readBeforeWritten := (methodNode accept: ReadBeforeWrittenVisitor new) readBeforeWritten.
        blockNodes do:
                [:blockNode|
                (blockNode temporaries notEmpty
                 and: [blockNode temporaries last isIndirectTempVector]) ifTrue:
                        [blockNode temporaries last remoteTemps do:
                                [:remoteTemp| | enclosingScopes smallestEnclosingBlockScope |
                                 (readBeforeWritten includes: remoteTemp) ifFalse:
                                  [enclosingScopes := blockNodes select: [:blockScope|
                                                                                                                        self blockNode: blockScope
                                                                                                                                isEnclosingScopeFor: remoteTemp].
                                         enclosingScopes notEmpty ifTrue:
                                                [smallestEnclosingBlockScope := enclosingScopes last.
                                                 smallestEnclosingBlockScope ~~ blockNode ifTrue:
                                                         [tempMap at: remoteTemp put: smallestEnclosingBlockScope]]]]]]!

----- Method: TempScopeEditor>>copyMethodMovingTemps (in category 'editing') -----
copyMethodMovingTemps
        | methodBodyStart tempsToKeep tempsStart tempsEnd |
        methodBodyStart := method methodClass parserClass new
                                                        parseMethodComment: sourceString setPattern: [:ignored|];
                                                        startOfNextToken.
        tempsStart := sourceString indexOf: $| startingAt: methodBodyStart.
        tempsEnd := sourceString indexOf: $| startingAt: tempsStart + 1.
        (tempsToKeep := self tempsToKeepAtMethodLevel) isEmpty
                ifTrue:
                        [| startOfFirstBlock |
                         startOfFirstBlock := (methodNode encoder sourceRangeFor: blockNodes second closureCreationNode) first.
                         tempsStart < startOfFirstBlock
                                ifTrue:
                                         [out next: tempsStart - 1 putAll: sourceString.
                                          soFar := tempsEnd + 1]
                                ifFalse:
                                        [soFar := 1]]
                ifFalse:
                        [out next: tempsStart putAll: sourceString.
                         tempsToKeep do: [:t| out space; nextPutAll: t name].
                         soFar := tempsEnd.
                         (sourceString at: soFar - 1) isSeparator ifTrue:
                                [soFar := soFar - 1]].
        blockNodes allButFirst do:
                [:blockNode|
                self processBlockNode: blockNode].
        out next: sourceString size - soFar + 1 putAll: sourceString startingAt: soFar!

----- Method: TempScopeEditor>>edit (in category 'editing') -----
edit
        self buildTempMap.
        tempMap notEmpty ifTrue:
                [| mr |
                 mr := method methodReference.
                 self copyMethodMovingTemps.
                 Transcript cr; show: mr actualClass name, ' >> ', mr methodSymbol.
                 method methodClass compile: out contents classified: mr category]!

----- Method: TempScopeEditor>>editNoCompile (in category 'editing') -----
editNoCompile
        self buildTempMap.
        ^tempMap isEmpty ifFalse:
                [self copyMethodMovingTemps.
                 out contents]!

----- Method: TempScopeEditor>>forMethod: (in category 'initialize-release') -----
forMethod: aCompiledMethod
        method := aCompiledMethod.
        sourceString := aCompiledMethod getSourceFromFile asString.
        methodNode := method methodClass parserClass new
                                                parse: sourceString
                                                class: method methodClass.
        methodNode ensureClosureAnalysisDone.
        blockNodes := (methodNode accept: BlockNodeCollectingVisitor new)
                                                blockNodes reject: [:bn| bn optimized].
        out := (String new: sourceString size) writeStream.
        tempMap := IdentityDictionary new


        "(TempScopeEditor new forMethod: SARInstaller class>>#ensurePackageWithId:) edit"!

----- Method: TempScopeEditor>>processBlockNode: (in category 'editing') -----
processBlockNode: blockNode
        | tempsToMoveHere startOfBlock endOfArgs maybeBlockTempsStart blockTempsInSource |
        tempsToMoveHere := (tempMap select: [:aBlockNode| aBlockNode == blockNode]) keys.
        tempsToMoveHere isEmpty ifTrue: [^self].
        startOfBlock := (methodNode encoder sourceRangeFor: blockNode closureCreationNode) first.
        endOfArgs := blockNode arguments isEmpty
                                        ifTrue: [startOfBlock]
                                        ifFalse: [sourceString indexOf: $| startingAt: startOfBlock].
        out next: endOfArgs - soFar + 1 putAll: sourceString startingAt: soFar.
        maybeBlockTempsStart := sourceString indexOf: $| startingAt: endOfArgs + 1 ifAbsent: sourceString size + 1.
        blockTempsInSource := (sourceString copyFrom: endOfArgs + 1 to: maybeBlockTempsStart - 1) allSatisfy:
                                                                [:c| c isSeparator].
        blockTempsInSource
                ifTrue:
                        [out next: maybeBlockTempsStart - endOfArgs putAll: sourceString startingAt: endOfArgs + 1.
                         (self tempsToKeepFor: blockNode) do:
                                [:tempNode| out space; nextPutAll: tempNode name].
                         tempsToMoveHere do: [:t| out space; nextPutAll: t name].
                         soFar := sourceString indexOf: $| startingAt: maybeBlockTempsStart + 1.
                         (sourceString at: soFar - 1) isSeparator ifTrue:
                                [soFar := soFar - 1]]
                ifFalse:
                        [out space; nextPut: $|.
                         tempsToMoveHere do: [:t| out space; nextPutAll: t name].
                         out space; nextPut: $|.
                         soFar := endOfArgs + 1]!

----- Method: TempScopeEditor>>tempsToKeepAtMethodLevel (in category 'editing') -----
tempsToKeepAtMethodLevel
        ^(self tempsToKeepFor: blockNodes first) select:
                [:t|t scope >= 0]!

----- Method: TempScopeEditor>>tempsToKeepFor: (in category 'editing') -----
tempsToKeepFor: blockNode
        | tempsToKeep |
        tempsToKeep := OrderedCollection new.
        blockNode temporaries do:
                [:t|
                t isIndirectTempVector
                        ifTrue:
                                [t remoteTemps do:
                                        [:rt|
                                        (tempMap includesKey: rt) ifFalse:
                                                [tempsToKeep addLast: rt]]]
                        ifFalse:
                                [tempsToKeep addLast: t]].
        ^tempsToKeep!