<body><div id="__MailbirdStyleContent" style="font-size: 10pt;font-family: Arial;color: #000000">
                                        <img src="cid:10854e18-41de-40a6-a844-d8dbdb7aab7e" width="auto"></img><div class="mb_sig"></div><blockquote class="history_container" type="cite" style="border-left-style:solid;border-width:1px; margin-top:20px; margin-left:0px;padding-left:10px;">
                        <p style="color: #AAAAAA; margin-top: 10px;">Am 04.09.2019 17:04:03 schrieb commits@source.squeak.org <commits@source.squeak.org>:</p><div style="font-family:Arial,Helvetica,sans-serif">A new version of Compiler was added to project The Inbox:<br>http://source.squeak.org/inbox/Compiler-mt.410.mcz<br><br>==================== Summary ====================<br><br>Name: Compiler-mt.410<br>Author: mt<br>Time: 4 September 2019, 5:03:52.834738 pm<br>UUID: 7ff9d1f8-5f7a-4077-b11b-ede80ada7d13<br>Ancestors: Compiler-TraitTest.409<br><br>Fixes ifNil:ifNotNil: decompilation. Please review.<br><br>- Only decompile ifNil:ifNotNil: if temps are not closured across nested blocks. This is the same behavior as #to:(by:)do:, which does not restore #to:(by:)do: if the 'var' or 'limit' are in an outer (outer?) scope. Only relevant if programmers type the optimized source code themselves.<br>- Note that I created a new method in DecompilerConstructor to pass 'tempReadCounts'. #to:(by:)do: is reconstructed in Decompiler, which already has access to 'tempReadCounts'. See Decompiler >> #jump:if: and #convertToDoLoop:.<br><br>=============== Diff against Compiler-TraitTest.409 ===============<br><br>Item was added:<br>+ ----- Method: AssignmentNode>>ifNilTemporary (in category 'private') -----<br>+ ifNilTemporary<br>+         "(temp := object) == nil ifTrue: [...] ifFalse: [...]"<br>+     <br>+     ^ self variable!<br><br>Item was added:<br>+ ----- Method: AssignmentNode>>ifNilValue (in category 'private') -----<br>+ ifNilValue<br>+    "(temp := object) == nil ifTrue: [...] ifFalse: [...]"<br>+     <br>+     ^ self value!<br><br>Item was changed:<br>  ----- Method: BlockNode>>printTemporaries:on:doPrior: (in category 'printing') -----<br>  printTemporaries: tempSequence on: aStream doPrior: aBlock<br>        "Print any in-scope temporaries.  If there are any evaluate aBlock<br>        prior to printing.  Answer whether any temporaries were printed."<br>       | tempStream seen |<br>   tempSequence ifNil:<br>           [^false].<br>     tempStream := (String new: 16) writeStream.<br>   "This is for the decompiler which canmot work out which optimized block a particular temp is<br>      local to and hence may produce diplicates as in<br>              expr ifTrue: [| aTemp | ...] ifFalse: [| aTemp | ...]"<br>   seen := Set new.<br>      tempSequence do:<br>              [:tempNode |<br>                  tempNode isIndirectTempVector<br>                         ifTrue:<br>                               [tempNode remoteTemps do:<br>                                     [:tempVariableNode|<br>                                    (tempVariableNode scope >= 0<br>+                                       and: [<br>+                                             "This is for the deocmpiler which may create a block arg when converting<br>+                                                a ifTrue:ifFalse: into a ifNil:ifNotNil: but won't remove it from temporaries"<br>+                                          tempVariableNode isBlockArg not<br>+                                        and: [(seen includes: tempNode key) not]]) ifTrue:<br>-                                           and: [(seen includes: tempNode key) not]) ifTrue:<br>                                           [tempStream space; nextPutAll: (seen add: tempVariableNode key)]]]<br>                    ifFalse:<br>                              [(tempNode scope >= -1<br>                               and: ["This is for the decompiler which may create a block arg when converting<br>+                                                a while into a to:do: but won't remove it from temporaries"<br>-                                             a while into a to:do: but won't remove it form temporaries"<br>                                         tempNode isBlockArg not<br>                              and: [(seen includes: tempNode key) not]]) ifTrue:<br>                                          [tempStream space; nextPutAll: (seen add: tempNode key)]]].<br>   tempStream position = 0 ifTrue:<br>               [^false].<br>     aBlock value.<br>         aStream nextPut: $|; nextPutAll: tempStream contents; space; nextPut: $|.<br>     ^true!<br><br>Item was changed:<br>  ----- Method: Decompiler>>jump:if: (in category 'instruction decoding') -----<br>  jump: dist if: condition<br>  <br>    | savePc sign elsePc elseStart end cond ifExpr thenBlock elseBlock<br>      thenJump elseJump condHasValue isIfNil saveStack |<br>          lastJumpIfPcStack addLast: lastPc.<br>    stack last == CascadeFlag ifTrue: [^ [self case: dist] ensure: [lastJumpIfPcStack removeLast]].<br>       elsePc := lastPc.<br>     elseStart := pc + dist.<br>       end := limit.<br>         "Check for bfp-jmp to invert condition.<br>          Don't be fooled by a loop with a null body."<br>     sign := condition.<br>    savePc := pc.<br>         self interpretJump ifNotNil:<br>                  [:elseDist|<br>            (elseDist >= 0 and: [elseStart = pc]) ifTrue:<br>                      [sign := sign not.  elseStart := pc + elseDist]].<br>    pc := savePc.<br>         ifExpr := stack removeLast.<br>   (isIfNil := stack size > 0 and: [stack last == IfNilFlag]) ifTrue:<br>                 [stack removeLast].<br>   saveStack := stack.<br>   stack := OrderedCollection new.<br>       thenBlock := self blockTo: elseStart.<br>         condHasValue := hasValue or: [isIfNil].<br>       "ensure jump is within block (in case thenExpr returns)"<br>    thenJump := exit <= end="" iftrue:="" [exit]="" iffalse:=""></=><br>          "if jump goes back, then it's a loop"<br>       thenJump <><br>             ifTrue:<br>                       [| blockBody blockArgs savedReadCounts blockBodyReadCounts selector |<br>                          "Must be a while loop...<br>                          thenJump will jump to the beginning of the while expr.  In the case of while's<br>                        with a block in the condition, the while expr should include more than just<br>                           the last expression: find all the statements needed by searching for the node<br>                         with the relevant pc."<br>                         stack := saveStack.<br>                   savedReadCounts := tempReadCounts copy.<br>                       pc := thenJump.<br>                       blockBody := self statementsTo: elsePc.<br>                       blockBodyReadCounts := tempReadCounts.<br>                        savedReadCounts keysAndValuesDo:<br>                              [:temp :count|<br>                                 blockBodyReadCounts at: temp put: (blockBodyReadCounts at: temp) - count].<br>                   tempReadCounts := savedReadCounts.<br>                    "discard unwanted statements from block"<br>                    blockBody size - 1 timesRepeat: [statements removeLast].<br>                      blockArgs := thenBlock statements = constructor codeEmptyBlock statements<br>                                                     ifTrue: [#()]<br>                                                         ifFalse: [{ thenBlock }].<br>                     selector := blockArgs isEmpty<br>                                                         ifTrue: [sign ifTrue: [#whileFalse] ifFalse: [#whileTrue]]<br>                                                    ifFalse: [sign ifTrue: [#whileFalse:] ifFalse: [#whileTrue:]].<br>                        statements addLast:<br>                           (constructor<br>                                          codeMessage: (constructor codeBlock: blockBody returns: false)<br>                                        selector: (constructor codeSelector: selector code: #macro)<br>                                   arguments: blockArgs).<br>                        pc := elseStart.<br>                      selector == #whileTrue: ifTrue:<br>                               [self convertToDoLoop: blockBodyReadCounts]]<br>                  ifFalse:<br>                      ["Must be a conditional..."<br>                         elseBlock := self blockTo: thenJump.<br>                          elseJump := exit.<br>                     "if elseJump is backwards, it is not part of the elseExpr"<br>                          elseJump < elsepc=""><br>                                 [pc := lastPc].<br>                       cond := isIfNil<br>                                               ifTrue:<br>                                                       [constructor<br>                                                                  codeMessage: ifExpr ifNilReceiver<br>                                                             selector: (constructor<br>                                                                                        codeSelector: (sign ifTrue: [#ifNotNil:] ifFalse: [#ifNil:])<br>                                                                                          code: #macro)<br>                                                                 arguments: (Array with: thenBlock)]<br>                                           ifFalse:<br>+                                                     [(sign ifTrue: [{elseBlock. thenBlock}] ifFalse: [{thenBlock. elseBlock}]) in: [:args |<br>+                                                              (constructor<br>+                                                                         decodeIfNilWithReceiver: ifExpr<br>+                                                                      selector: #ifTrue:ifFalse:<br>+                                                                   arguments: args<br>+                                                                      tempReadCounts: tempReadCounts) ifNil: [<br>+                                                                             constructor<br>+                                                                                  codeMessage: ifExpr<br>+                                                                                  selector: (constructor codeSelector: #ifTrue:ifFalse: code: #macro)<br>+                                                                                  arguments:       args]]].<br>-                                                    [constructor<br>-                                                                 codeMessage: ifExpr<br>-                                                          selector: (constructor codeSelector: #ifTrue:ifFalse: code: #macro)<br>-                                                          arguments:      (sign<br>-                                                                                                ifTrue: [{elseBlock. thenBlock}]<br>-                                                                                             ifFalse: [{thenBlock. elseBlock}])].<br>                          stack := saveStack.<br>                   condHasValue<br>                                  ifTrue: [stack addLast: cond]<br>                                 ifFalse: [statements addLast: cond]].<br>         lastJumpIfPcStack removeLast.!<br><br>Item was changed:<br>  ----- Method: DecompilerConstructor>>codeMessage:selector:arguments: (in category 'constructor') -----<br>  codeMessage: receiver selector: selector arguments: arguments<br>          | symbol |<br>    symbol := selector key.<br>       (self<br>                 decodeLiteralVariableValueDereferenceWithReceiver: receiver<br>           selector: symbol<br>              arguments: arguments) ifNotNil: [:node| ^node].<br>+ <br>-  (self decodeIfNilWithReceiver: receiver<br>-                      selector: symbol<br>-                     arguments: arguments) ifNotNil: [:node| ^node].<br>       ^MessageNode new<br>                      receiver: receiver selector: selector<br>                         arguments: arguments<br>                          precedence: symbol precedence!<br><br>Item was removed:<br>- ----- Method: DecompilerConstructor>>decodeIfNilWithReceiver:selector:arguments: (in category 'constructor') -----<br>- decodeIfNilWithReceiver: receiver selector: selector arguments: arguments<br>-         receiver ifNil: [ ^nil ].               "For instance, when cascading"<br>-     selector == #ifTrue:ifFalse:<br>-                 ifFalse: [^ nil].<br>-    (receiver isMessage: #==<br>-                             receiver: nil<br>-                                arguments: [:argNode | argNode == NodeNil])<br>-          ifFalse: [^ nil].<br>-    ^ (MessageNode new<br>-                   receiver: receiver<br>-                   selector: (SelectorNode new key: #ifTrue:ifFalse: code: #macro)<br>-                      arguments: arguments<br>-                         precedence: 3)<br>-               noteSpecialSelector: #ifNil:ifNotNil:!<br><br>Item was added:<br>+ ----- Method: DecompilerConstructor>>decodeIfNilWithReceiver:selector:arguments:tempReadCounts: (in category 'constructor') -----<br>+ decodeIfNilWithReceiver: receiver selector: selector arguments: arguments tempReadCounts: tempReadCounts<br>+     <br>+     | node temp |<br>+        receiver ifNil: [ ^nil ].               "For instance, when cascading"<br>+     selector == #ifTrue:ifFalse:<br>+                 ifFalse: [^ nil].<br>+                            <br>+     (receiver isMessage: #==<br>+                             receiver: nil<br>+                                arguments: [:argNode | argNode == NodeNil])<br>+          ifFalse: [^ nil].<br>+            <br>+     "Like #to:(by:)do:, support only local temps."<br>+     (((temp := receiver ifNilTemporary) isNil or: [tempReadCounts includesKey: temp]) or: [<br>+              "What about 'object ifNotNil: [:o | ]', which as not read the blockArg? Just check that there is no remote vector pointing to it."<br>+                 tempReadCounts keys noneSatisfy: [:otherTemp |<br>+                       otherTemp isIndirectTempVector<br>+                               ifTrue: [otherTemp remoteTemps anySatisfy: [:remoteTemp | remoteTemp name = temp name]]<br>+                              ifFalse: [otherTemp name = temp name]]<br>+                       ])<br>+           ifFalse: [^ nil].<br>+            <br>+     node := (MessageNode new<br>+                     receiver: receiver<br>+                   selector: (SelectorNode new key: #ifTrue:ifFalse: code: #macro)<br>+                      arguments: arguments<br>+                         precedence: 3).<br>+ <br>+  "Reconfigure the message node to #ifNil:ifNotNil:. Note that original* instance variables keep their optimized format. See MessageNode >> #printIfNilNotNil:indent:."   <br>+     node<br>+                 noteSpecialSelector: #ifNil:ifNotNil:;<br>+               selector: (SelectorNode new key: #ifNil:ifNotNil:).<br>+  <br>+     temp ifNil: [^ node].<br>+        temp isTemp ifFalse: [^ node].<br>+       <br>+     (arguments second isJust: NodeNil) not ifTrue: [<br>+                     temp beBlockArg.<br>+                     node arguments: {<br>+                            arguments first.<br>+                             arguments second copy arguments: { temp }; yourself } ].<br>+                             <br>+     ^ node!<br><br>Item was added:<br>+ ----- Method: MessageNode>>ifNilTemporary (in category 'private') -----<br>+ ifNilTemporary<br>+ <br>+    ^ self ifNilReceiver ifNilTemporary!<br><br>Item was changed:<br>  ----- Method: MessageNode>>printIfNilNotNil:indent: (in category 'printing') -----<br>  printIfNilNotNil: aStream indent: level<br>  <br>+         (arguments first isJust: NodeNil) ifTrue: [<br>+          self printReceiver: receiver ifNilReceiver ifNilValue on: aStream indent: level.<br>+             ^ self printKeywords: #ifNotNil:<br>-     self printReceiver: receiver ifNilReceiver on: aStream indent: level.<br>- <br>-    (arguments first isJust: NodeNil) ifTrue:<br>-            [^ self printKeywords: #ifNotNil:<br>                             arguments: { arguments second }<br>                               on: aStream indent: level].<br>+                  <br>+     (arguments second isJust: NodeNil) ifTrue: [<br>+                 self printReceiver: receiver ifNilReceiver on: aStream indent: level.<br>+                ^ self printKeywords: #ifNil:<br>-        (arguments second isJust: NodeNil) ifTrue:<br>-           [^ self printKeywords: #ifNil:<br>                                arguments: { arguments first }<br>                                on: aStream indent: level].<br>+  <br>+     self printReceiver: receiver ifNilReceiver ifNilValue on: aStream indent: level.<br>      ^ self printKeywords: #ifNil:ifNotNil:<br>                        arguments: arguments<br>                          on: aStream indent: level!<br><br>Item was changed:<br>  ----- Method: MessageNode>>printWithClosureAnalysisIfNilNotNil:indent: (in category 'printing') -----<br>  printWithClosureAnalysisIfNilNotNil: aStream indent: level<br>  <br>+     (arguments first isJust: NodeNil) ifTrue: [<br>+          self printWithClosureAnalysisReceiver: receiver ifNilReceiver ifNilValue on: aStream indent: level.<br>+          ^ self printWithClosureAnalysisKeywords: #ifNotNil:<br>-  self printWithClosureAnalysisReceiver: receiver ifNilReceiver on: aStream indent: level.<br>- <br>-         (arguments first isJust: NodeNil) ifTrue:<br>-            [^self printWithClosureAnalysisKeywords: #ifNotNil:<br>                           arguments: { arguments second }<br>                               on: aStream indent: level].<br>+                  <br>+     (arguments second isJust: NodeNil) ifTrue: [<br>+                 self printWithClosureAnalysisReceiver: receiver ifNilReceiver on: aStream indent: level.<br>+             ^ self printWithClosureAnalysisKeywords: #ifNil:<br>-     (arguments second isJust: NodeNil) ifTrue:<br>-           [^self printWithClosureAnalysisKeywords: #ifNil:<br>                              arguments: { arguments first }<br>                                on: aStream indent: level].<br>+                  <br>+     self printWithClosureAnalysisReceiver: receiver ifNilReceiver ifNilValue on: aStream indent: level.<br>+  ^ self printWithClosureAnalysisKeywords: #ifNil:ifNotNil:<br>-    ^self printWithClosureAnalysisKeywords: #ifNil:ifNotNil:<br>                      arguments: arguments<br>                          on: aStream indent: level!<br><br>Item was added:<br>+ ----- Method: ParseNode>>ifNilTemporary (in category 'private') -----<br>+ ifNilTemporary<br>+ <br>+   ^ nil!<br><br>Item was added:<br>+ ----- Method: ParseNode>>ifNilValue (in category 'private') -----<br>+ ifNilValue<br>+ <br>+       ^self!<br><br>Item was changed:<br>  ----- Method: Parser>>parseCue:noPattern:ifFail: (in category 'public access') -----<br>  parseCue: aCue noPattern: noPattern ifFail: aBlock <br>      "Answer a MethodNode for the argument, sourceStream, that is the root of<br>          a parse tree. Parsing is done with respect to the CompilationCue to <br>          resolve variables. Errors in parsing are reported to the cue's requestor; <br>    otherwise aBlock is evaluated. The argument noPattern is a Boolean that is<br>    true if the the sourceStream does not contain a method header (i.e., for DoIts)."<br>  <br>   | methNode repeatNeeded myStream s p subSelection |<br>   myStream := aCue sourceStream.<br>        [repeatNeeded := false.<br>        p := myStream position.<br>       s := myStream upToEnd.<br>        myStream position: p.<br>+ <br>+    doitFlag := noPattern.<br>+       failBlock:= aBlock.<br>          <br>       self encoder init: aCue notifying: self.<br>      self init: myStream cue: aCue failBlock: [^ aBlock value].<br>   <br>       subSelection := self interactive and: [cue requestor selectionInterval = (p + 1 to: p + s size)].<br>  <br>-        doitFlag := noPattern.<br>-       failBlock:= aBlock.<br>           [methNode := self method: noPattern context: cue context] <br>           on: ReparseAfterSourceEditing <br>                do:     [ :ex |<br>                       repeatNeeded := true.<br>                         properties := nil. "Avoid accumulating pragmas and primitives Number"<br>                       myStream := ex newSource <br>                             ifNil: [subSelection<br>                                                          ifTrue:<br>                                                               [ReadStream<br>                                                                   on: cue requestor text string<br>                                                                         from: cue requestor selectionInterval first<br>                                                                   to: cue requestor selectionInterval last]<br>                                                     ifFalse:<br>                                                              [ReadStream on: cue requestor text string]]<br>                           ifNotNil: [:src | myStream := src readStream]].<br>        repeatNeeded] whileTrue:<br>             [encoder := self encoder class new].<br>          methNode sourceText: s.<br>       ^methNode<br>  !<br><br><br></div></blockquote>
                                        </div></body>