On Sun, May 16, 2010 at 3:32 PM, Andreas Raab
<andreas.raab@gmx.de> wrote:
Hi Ken -
Very interesting. Looks like an issue with OutOfScopeNotification. The problem appears to be that this code:
� � � �"..." [ :a | a ].
� � � �"..." a.
generates an OutOfScopeNotification that is being suppressed in workspaces (I'm not exactly sure why that is). This is of course is interesting because of the highly unusual situation of a temp shadowing a global instead of another temp / ivar. For example, this wouldn't compile:
�| a |
�[ :a | a ].
But stranglely, this does:
�[ :Object | Object ].
so your example code is roughly equivalent to running:
� � � �b := [ :Object | Object ].
� � � �c := b value: Object.
When you run this line by line it works 'as expected' and when you run it as a single doIt you get the result of c being a "[closure] in UndefinedObject>>DoIt" due to the OutOfScopeNotification.
Hope this explains the issue - as for how to fix it, I have no clue :-)
Yes, the bug is that Encoder>>encodeVariable:sourceRange:ifUnknown: doesn't continue from a caught OutOfScopeNotification by answering the ifUnknown: action. �Instead it returns the out-of-scope block temp. �This is a rather serious compiler bug that I'd left unfixed because it only bites in workspaces (apologies) and I didn't have a small example to analyse before (thanks Ken!). �The code generated is actually equivalent to
�� � ��b := [ :a | a ].
� � � �c := b value: <temp 0>.
Since there aren't any temps, temp 0 is top of stack, which just happens to be the closure just created by the preceding bytecode. �e.g. look at bytecode 49 in the following:
a := 4. b := [ :a | a ]. c := b value: a. { a. b. c } thisContext method symbolic
The fix is simple. �When an OutOfScopeNotification is caught the Encoder should still answer what ever it should for an out of scope variable. � In Encoder>>encodeVariable:sourceRange:ifUnknown: the statements
(varNode isTemp and: [varNode scope < 0]) ifTrue: [
OutOfScopeNotification signal ifFalse: [ ^self notify: 'out of scope'].
].
^ varNode
should read
(varNode isTemp and: [varNode scope < 0]) ifTrue:
[^OutOfScopeNotification signal
ifTrue: [action value]
ifFalse: [self notify: 'out of scope']].
^varNode
Find attached:
!Encoder methodsFor: 'encoding' stamp: 'eem 5/16/2010 17:33'!
encodeVariable: name sourceRange: range ifUnknown: action
| varNode |
varNode := scopeTable
at: name
ifAbsent:�
[(self lookupInPools: name�
ifFound: [:assoc | varNode := self global: assoc name: name])
ifTrue: [varNode]
ifFalse: [^action value]].
range ifNotNil:
[name first canBeGlobalVarInitial ifTrue:
[globalSourceRanges addLast: { name. range. false }]].
(varNode isTemp and: [varNode scope < 0]) ifTrue:
[^OutOfScopeNotification signal
ifTrue: [action value]
ifFalse: [self notify: 'out of scope']].
^varNode! !
P.S. this is probably applicable to any Squeak bytecode compiler (including eToys). �The only change in my closure compiler from Andreas' 2003 version was changing "name first isUppercase" to "name first canBeGlobalVarInitial".
P.P.S. �Again Ken, thanks for a comprehensible example. �It always bit me in huge doits I was using to analyse the entire system's compiled methods, and invariably crashed the VM. �I never took the time to isolate the bug, I just fixed the doit and continued. �Turns out to be very simple.
best
Eliot
Cheers,
�- Andreas
On 5/16/2010 12:35 PM, Ken Causey wrote:
Well, it's nothing new but this one has stumped me:
http://bugs.squeak.org/view.php?id=7532
Initially I (and Frank) thought the reporter was mistaken until we
understood that the problem shows up when you execute the main code all
in one do-it. �I've since modified the original report to make this
clearer.
So here it is:
a := 4.
b := [ :a | a ].
c := b value: a.
If you SELECT ALL OF THIS AND EXECUTE IT ALL AT ONE TIME (crucial
detail). The result is that a is 4, b is a BlockClosure, and c is a
BlockClosure not 4 as expected. Execute each statement separately and c
is 4.
Alternately, from a suggestion from jmckeon, if you specify a different
symbol for the block argument:
a := 4.
b := [ :d | d ].
c := b value: a.
when executed all at one time works as you would expect: a is 4, b is a
BlockClosure, and c is 4. (and d is nil)
So what's up?
Ken