[self-interest] Block activation verification...
Jecel Assumpcao Jr
jecel at merlintec.com
Fri Jun 2 23:56:10 UTC 2000
Dru Nelson you wrote:
> In the Self tutorial/slides, it mentions that the blocks
> are compiled with some hidden slots.
True. This greatly offends some people who feel they are being cheated
by these tricks. This led me to take a look at some outliners for
methods (just choose "sprout" from the yellow menu while pointing to a
method) and noticed that Self 4.1.2 doesn't include the "pseudo slots"
like 4.0 does. In fact, this lastest version seems to have some real
problems with outliners and methods (it can't show the reflectee slot
in a method mirror outliner without getting into a loop generating
errors). Speaking of 4.1.2 UI problems, enumerators now start
evaluating themselves as soon as you call them up instead of when you
try to open them. This means you can never uncheck the "well known
only" box before doing the enumeration.
> Then when an expression that references a block is "evaluated",
> a clone is made with a hidden slot to the context.
Variable scoping in other languages is simulated in Self using object
inheritance. By making the implicit receiver be the current context and
having that point to the original receiver via a self* parent slot, you
can access both "local variables" and "instance variables" (and "class
variables! And "globals") in a uniform way.
When you send the 'value' message to a block, if the corresponding
methods acted in the normal way you would have a "self*" slot pointing
to the block itself and no way to access stuff in the context in which
( | a <- 3. b <- 4 | a < b ifTrue: [ ^ a*b ]. a+b )
This has a block object that looks like this:
( | parent* = traits block. value = ( ^ a*b ) | )
Now if this block object is sent a 'value' message and the resulting
context gets a 'self*' slot pointing to itself, then it will know about
'parent*' and 'value', but nothing about 'a' and 'b', right?
So we don't add that 'self*' slot at all, but a 'lexicalParent*' slot
pointing to the context which executed the "push literal: [ ^ a*b ]"
bytecode. But how can we find out that information when creating the
context for the 'value' method? By this time, we no longer remember
this (and it would be a good idea to know this anyway since otherwise
we can't execute the "non local return" bytecode, the "^").
> (3 < 4) ifTrue: [ ^nil ].
> How is that done?
Yet another trick - when the bytecode tells us to push a block on the
stack, we create a clone of it with an extra slot to point to the
currently executing context and push that instead. Note that you can't
see this in the debugger - it does every thing it can to make you think
that the original block was used, not its evil twin ;-)
Now when we send the 'value' message, it will go to the modified clone
instead of the block, and this clone does know about the context we are
interested in and so can fill in the 'lexicalParent*' slot correctly.
>From now on, everything works as claimed.
Don't worry too much about understanding all this... Self doesn't
really work this way at all under the hood!!
So David's answer was probably just what you needed :-)
More information about the Self-interest