Hi,
I was looking for loop patterns that can be rewritten to use #repeat:, because it became an inlined message (thanks Nicolas). I decided to use ParseNodeEnumerator, the swiss army knife, suitable for these kind of tasks. As a first step I tried to find method with the following pattern: [ ... true ] whileTrue. To my surprise, my code didn't find any methods with it. Since I knew that there are methods with this pattern, I created the following "test case" which shows the problem:
| shouldSelect visitor | shouldSelect := false. visitor := ParseNodeEnumerator ofBlock: [ :node | shouldSelect := shouldSelect or: [ node isMessageNode and: [ node selector key == #whileTrue and: [ node receiver isBlockNode and: [ node receiver statements last isVariableNode and: [ node receiver statements last key = 'true' ] ] ] ] ] ]. shouldSelect := false. [ [ true ] whileTrue ] decompile accept: visitor. shouldSelect.
It should return true, but it returns false. What's even worse is that if you insert a #halt, like in the following example and press proceed when the debugger shows up, then the result will be true.
... node isMessageNode and: [ self halt. node selector key == #whileTrue and: [ ...
The cause of the problem is that the decompiler creates the MessageNode with #whileTrue: as selector instead of #whileTrue, but MessageNode >> #printWhileOn:indent: or MessageNode >> #printWithClosureAnalysisWhileOn:indent: replaces the selector to #whileTrue. Since the debugger triggers one of these methods, the code will pass in the second case (#halt). The same happens if you open an explorer on the MessageNode.
Shouldn't the change be done by the decompiler, instead of some method used for printing?
Cheers, Levente
P.S.: I finished the script and rewrote most methods with these patterns in the Trunk. I'll push them soon. Here's the final snippet in case you would like to check your own code:
| shouldSelect | shouldSelect := false. visitor := ParseNodeEnumerator ofBlock: [ :node | shouldSelect := shouldSelect or: [ node isMessageNode and: [ (#(whileTrue whileTrue: whileFalse whileFalse:) instVarsInclude: node selector key) and: [ | receiver | (receiver := node receiver) isBlockNode and: [ | lastStatement | (lastStatement := receiver statements last) isVariableNode and: [ #('true' 'false') includes: lastStatement key ] ] ] ] ] ]. SystemNavigation default browseAllSelect: [ :method | shouldSelect := false. method decompile accept: visitor. shouldSelect ]
Oh bad bad behaved Nodes, 'hello, I will change when you will debug me' I guess you must have lost a few hairs on this one.
Of course, it should better be Decompiler responsibility to trigger this mutation.
Nicolas
2011/2/20 Levente Uzonyi leves@elte.hu:
Hi,
I was looking for loop patterns that can be rewritten to use #repeat:, because it became an inlined message (thanks Nicolas). I decided to use ParseNodeEnumerator, the swiss army knife, suitable for these kind of tasks. As a first step I tried to find method with the following pattern: [ ... true ] whileTrue. To my surprise, my code didn't find any methods with it. Since I knew that there are methods with this pattern, I created the following "test case" which shows the problem:
| shouldSelect visitor | shouldSelect := false. visitor := ParseNodeEnumerator ofBlock: [ :node | shouldSelect := shouldSelect or: [ node isMessageNode and: [ node selector key == #whileTrue and: [ node receiver isBlockNode and: [ node receiver statements last isVariableNode and: [ node receiver statements last key = 'true' ] ] ] ] ] ]. shouldSelect := false. [ [ true ] whileTrue ] decompile accept: visitor. shouldSelect.
It should return true, but it returns false. What's even worse is that if you insert a #halt, like in the following example and press proceed when the debugger shows up, then the result will be true.
... node isMessageNode and: [ self halt. node selector key == #whileTrue and: [ ...
The cause of the problem is that the decompiler creates the MessageNode with #whileTrue: as selector instead of #whileTrue, but MessageNode >> #printWhileOn:indent: or MessageNode >> #printWithClosureAnalysisWhileOn:indent: replaces the selector to #whileTrue. Since the debugger triggers one of these methods, the code will pass in the second case (#halt). The same happens if you open an explorer on the MessageNode.
Shouldn't the change be done by the decompiler, instead of some method used for printing?
Cheers, Levente
P.S.: I finished the script and rewrote most methods with these patterns in the Trunk. I'll push them soon. Here's the final snippet in case you would like to check your own code:
| shouldSelect | shouldSelect := false. visitor := ParseNodeEnumerator ofBlock: [ :node | shouldSelect := shouldSelect or: [ node isMessageNode and: [ (#(whileTrue whileTrue: whileFalse whileFalse:) instVarsInclude: node selector key) and: [ | receiver | (receiver := node receiver) isBlockNode and: [ | lastStatement | (lastStatement := receiver statements last) isVariableNode and: [ #('true' 'false') includes: lastStatement key ] ] ] ] ] ]. SystemNavigation default browseAllSelect: [ :method | shouldSelect := false. method decompile accept: visitor. shouldSelect ]
squeak-dev@lists.squeakfoundation.org