<br><br><div class="gmail_quote">On Sat, May 18, 2013 at 3:49 PM, Lars <span dir="ltr"><<a href="mailto:lars.wassermann@googlemail.com" target="_blank">lars.wassermann@googlemail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
<div text="#000000" bgcolor="#FFFFFF">
Hi Eliot,<br>
thank you for the analysis.<br>
The question is why the code works with the VM, but not if we step
through it. What does the VM do that makes this behave correctly,
although the temps-array is removed from the stack prior to
returning from #contextOn:do:?<br></div></blockquote><div><br></div><div>the vm doesn't do anything special (* with one exception) because even though the image code pops off the temps array the return pushes it back almost immediately, before the temps array is used.</div>
<div><br></div><div>* the exception is that when a context is created for a stack frame the code miust handle the fact that the stack may be empty and so the proxy context must have a nil where the missing stack entry is, not an uninitialized field. See the comment in updateStateOfSpouseContextForFrame:WithSP:.</div>
<div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div text="#000000" bgcolor="#FFFFFF">I ask because I need to recreate that behaviour in case it also
appears in other places. The general project is creating a squeak
research VM. That is also why I try to refrain from changing the
image, retaining compatibility with Pharo/Squeak trunk.<br>
<br>
Thank you, Lars<br>
<br>
<div>2013/05/17 8:08 pm Eliot Miranda
<a href="mailto:eliot.miranda@gmail.com" target="_blank"><eliot.miranda@gmail.com></a>:<br>
</div>
<blockquote type="cite">
<pre> </pre>
<br>
<fieldset></fieldset>
<br>
Hi Lars,<br>
<br>
<div class="gmail_quote">On Fri, May 17, 2013 at 9:36 AM, Lars
Wassermann <span dir="ltr"><<a href="mailto:lars.wassermann@googlemail.com" target="_blank">lars.wassermann@googlemail.com</a>></span>
wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> <br>
Hello VM-dev,<br>
I habe problems understanding what exactly happens when I step
through:<br>
(ContextPart contextOn: Error do: []) halt<br>
in a workspace.<br>
When I step over the parenthesis in the debugger, everything
works kind of like expected (the context of contextOn:do:
misses one temporary but the returned value is what the
methods description promised). When I step into or through the
code, an error is raised when executing bytecode 54 of
#contextOn:do:, because the temps-array illegally has been
poped (by bytecode 53).<br>
<br>
My question now is:<br>
Why does the normal vm not only break when runing this code,
but also return the correct value (the first element of the
last temporary).<br>
I appended a graphic trying to visualize what happens up to
the miracle of three skipped bytecodes.<br>
</blockquote>
<div><br>
</div>
<div>Ugh. I hate this method. Actually I hate
ContextPart>>jump, which is the really evil method.</div>
<div><br>
</div>
<div>So let's take a look at the methods:</div>
<div><br>
</div>
<div>ContextPasrt class>>contextOn: exceptionClass do:
block</div>
<div><span style="white-space:pre-wrap"> </span>"Create
an #on:do: context that is ready to return from executing its
receiver"</div>
<div><br>
</div>
<div><span style="white-space:pre-wrap"> </span>|
ctxt chain |</div>
<div><span style="white-space:pre-wrap"> </span>ctxt
:= thisContext.</div>
<div><span style="white-space:pre-wrap"> </span>[chain
:= thisContext sender cut: ctxt. ctxt jump]</div>
<div><span style="white-space:pre-wrap"> </span><span style="white-space:pre-wrap"> </span>on:
exceptionClass</div>
<div><span style="white-space:pre-wrap"> </span><span style="white-space:pre-wrap"> </span>do:
block.</div>
<div><span style="white-space:pre-wrap"> </span>"jump
above will resume here without unwinding chain"</div>
<div><span style="white-space:pre-wrap"> </span>^
chain</div>
<div><br>
</div>
<div>answers a context that is an activation of on:do: ready to
catch the supplied exception. It is used by
ContextPart>>runUntilErrorOrReturnFrom: to insert an
exception handler for UnhandledError immediately beneath the
current context being executed by the debugger.</div>
<div><br>
</div>
<div>The on:do: argument is a little unclear. It is easier to
understand if it reads</div>
<div><br>
</div>
<div>
<div>ContextPasrt class>>contextOn: exceptionClass do:
block</div>
<div><span style="white-space:pre-wrap"> </span>"Create
an #on:do: context that is ready to return from executing
its receiver"</div>
<div><br>
</div>
<div><span style="white-space:pre-wrap"> </span>|
ctxt onDoContext |</div>
<div><span style="white-space:pre-wrap"> </span>ctxt
:= thisContext.</div>
<div><span><span style="white-space:pre-wrap"> </span>[</span>onDoContext<span> := thisContext sender.</span></div>
<div><span style="white-space:pre-wrap"> </span> onDoContext cut:
ctxt.</div>
<div><span style="white-space:pre-wrap"> </span> ctxt
jump]</div>
<div><span style="white-space:pre-wrap"> </span><span style="white-space:pre-wrap"> </span>on:
exceptionClass</div>
<div><span style="white-space:pre-wrap"> </span><span style="white-space:pre-wrap"> </span>do:
block.</div>
<div><span style="white-space:pre-wrap"> </span>"jump
above will resume here without unwinding chain"</div>
<div><span style="white-space:pre-wrap"> </span>^ onDoContext</div>
</div>
<div><br>
</div>
<div>So the idea is to run until in the on:do:'s receiver (the
[onDoContext := ...] block), grab the sender (the on:do:
activation), remove intervening contexts (onDoContext cut:
ctxt) so that on:do:'s handlerActive doesn't get set, resume
executing in the contextOn:do: context (ctxt jump) and answer
the onDoContext.</div>
<div><br>
</div>
<div>The horror is in ContextPart>>jump:</div>
<div><br>
</div>
<div>
<div>ContextPart>>jump</div>
<div><span style="white-space:pre-wrap"> </span>"Abandon
thisContext and resume self instead (using the same current
process). You may want to save thisContext's sender before
calling this so you can jump back to it.</div>
<div><span style="white-space:pre-wrap"> </span>Self
MUST BE a top context (ie. a suspended context or a
abandoned context that was jumped out of). A top context
already has its return value on its stack (see
Interpreter>>primitiveSuspend and other suspending
primitives).</div>
<div><span style="white-space:pre-wrap"> </span>thisContext's
sender is converted to a top context (by pushing a nil
return value on its stack) so it can be jump back to."</div>
<div><br>
</div>
<div>
<span style="white-space:pre-wrap"> </span>|
top |</div>
<div><span style="white-space:pre-wrap"> </span>"Make
abandoned context a top context (has return value (nil)) so
it can be jumped back to"</div>
<div><span style="white-space:pre-wrap"> </span>thisContext
sender push: nil.</div>
<div><br>
</div>
<div><span style="white-space:pre-wrap"> </span>"Pop
self return value then return it to self (since we jump to
self by returning to it)"</div>
<div><span style="white-space:pre-wrap"> </span>stackp
= 0 ifTrue: [self stepToSendOrReturn].</div>
<div><span style="white-space:pre-wrap"> </span>stackp
= 0 ifTrue: [self push: nil]. "must be quick return
self/constant"</div>
<div><span style="white-space:pre-wrap"> </span>top
:= self pop.</div>
<div><span style="white-space:pre-wrap"> </span>thisContext
privSender: self.</div>
<div><span style="white-space:pre-wrap"> </span>^
top</div>
</div>
<div><br>
</div>
<div>This works by returning to self (a MethodContext), but
return pushes a result. So the code pops the top thing off
the stack and returns it. This is evil. If the stack is
empty then this will pop the receiver, return it and push it
back in the return (!!). If the stack contains only temps
then the last temp is popped and pushed by the return (!!).
This is what you're seeing. The stack's last temp is an
indirection vector. It gets popped off the stack by</div>
<span style="white-space:pre-wrap"> </span>top
:= self pop.</div>
<div class="gmail_quote">at which point any attempt to look at
self's temps in a debugger will cause an error because the
indirection vector is no longer there. It then gets pushed back
by=</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>^ top</div>
<div class="gmail_quote"><br>
</div>
<div class="gmail_quote">Horrible. It all depends on being able
to create a temporary invalid execution state in a context.</div>
<div class="gmail_quote"><br>
</div>
<div class="gmail_quote">A better way to do this is by process
switch, avoiding the return. For example, in my basic block
profiler I handle an unknownBytecode error caused by executing
the unknownBytecode that overwrites the bytecode at the
beginning of each basic block, replaces the unknownBytecode with
the correct bytecode, and continues:</div>
<div class="gmail_quote"><br>
</div>
<div class="gmail_quote">
<div class="gmail_quote">ContextPart>>unusedBytecode</div>
<div class="gmail_quote">
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>"Handle unusedBytecode by
replacing the bytecode with the</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span> correct one found in the
coverage property and continuing.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span> Continue via wait/signal
since return would push a result."</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>| coverage semaphore
process |</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>self assert: (method at:
pc) = method encoderClass unusedBytecode.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>coverage := method
propertyValueAt: #coverage.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>self assert: coverage
notNil.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>self assert: (coverage
includesKey: pc).</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>semaphore := Semaphore
new.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>process := Processor
activeProcess.</div>
<div class="gmail_quote"><br>
</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>[method</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>at: pc</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>put: (coverage removeKey:
pc).</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span> process suspendedContext
unwindTo: self.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span> process
suspendedContext: self.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span> semaphore signal] fork.</div>
<div class="gmail_quote"><br>
</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>semaphore wait</div>
</div>
<div class="gmail_quote"><br>
</div>
<div class="gmail_quote">So that could become something like</div>
<div class="gmail_quote"><br>
</div>
<div class="gmail_quote">
<div class="gmail_quote">jump</div>
<div class="gmail_quote">
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>"Abandon thisContext
and resume self instead (using the same current process).
You may want to save thisContext's sender before calling
this so you can jump back to it.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>Self MUST BE a top
context (ie. a suspended context or a abandoned context
that was jumped out of). A top context already has its
return value on its stack (see
Interpreter>>primitiveSuspend and other suspending
primitives)."</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>| semaphore process |</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>semaphore := Semaphore
new.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>process := Processor
activeProcess.</div>
<div class="gmail_quote"><br>
</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>[process
suspendedContext unwindTo: self.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span> process
suspendedContext: self.</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span> semaphore signal]
fork.</div>
<div class="gmail_quote"><br>
</div>
<div class="gmail_quote"><span style="white-space:pre-wrap"> </span>semaphore wait</div>
<div><br>
</div>
<div>but perhaps the process switch will introduce other
problems. I don't know.</div>
<div><br>
</div>
<div>Hope this helps.</div>
<div><br>
</div>
</div>
</div>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
I replicated the behavior in Squeak 4.0 and 4.4 images on
interpreter VMs 3.7.7, 4.10, and a newer cog VM.<br>
<br>
<br>
A fix to the problem of stepping and comprehension as well as
the missing temp in the frame would be changing the method to:<br>
<br>
contextOn: exceptionClass do: block<br>
"Create an #on:do: context that is ready to return
from executing its receiver"<br>
<br>
| ctxt chain |<br>
ctxt := thisContext.<br>
[chain := thisContext sender cut: ctxt.<br>
ctxt push: nil. ctxt jump] on: exceptionClass do:
block.<br>
"jump above will resume here without unwinding chain"<br>
^ chain<br>
<br>
The difference is that after cutting off ctxt, we ensure that
it is a top context by pushing a meaningless value. But this
still doesn't help me understanding the original behaviour.<br>
<br>
Thank you and all the best<br>
Lars<br>
<br>
</blockquote>
</div>
<br>
-- <br>
best,
<div>Eliot</div></blockquote></div></blockquote></div>-- <br>best,<div>Eliot</div>