<br><br><div class="gmail_quote">On Sun, Apr 22, 2012 at 11:48 AM, stephane ducasse <span dir="ltr">&lt;<a href="mailto:stephane.ducasse@gmail.com">stephane.ducasse@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Hi eliot<br>
<br>
I&#39;m rereading your blog. I understand that you create an array so that you are sure that<br>
local of the activation context of the method containing a block are not modified, just the<br>
array elements. so this means in particular that since the array is not allocated on the heap<br>
you guarantee that captured variables of the closure environment outlive the method activation.<br>
I hope I got it correctly.<br></blockquote><div><br></div><div>The array *is* allocated on the heap.  I don&#39;t guarantee that captured variables of the closure environment outlive the method activation.  That depends on whether a reference to the block is captured.  If some object takes a reference to the block (stores it in an inst var) then the block can outlive its enclosing method activation.  If this happens, if the variables closed-over by the block live on the stack of the method activation then the method activation must persist too.  And that&#39;s a problem if the activation has been mapped to a stack ftrame, because we&#39;ve exited the stack frame on returning from the method activation and so must update the state of the context from the stack frame at return time, and that&#39;s what we&#39;re trying to avoid. So instead any shared closed-over variables (variables that can&#39;t be copied) get allocated in a heap-allocated indirection vector (the array).</div>
<div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Now I have a question about the following snippet representing what the compiler is doing.<br>
Where indirectTempsCopy is initialized and to what?<br>
<br>
inject: thisValue into: binaryBlock<br>
        | indirectTemps |<br>
        indirectTemps := Array new: 1.<br>
        indirectTemps at: 1 put: thisValue.<br>
        self do: (thisContext<br>
                closureCopy:<br>
                        [:each |<br>
                                | binaryBlockCopy indirectTempsCopy |<br>
                                indirectTempsCopy<br>
                                        at:1<br>
                                        put: (binaryBlockCopy value: (indirectTempsCopy at: 1) value: each)]<br>
                                copiedValues: (Array with: binaryBlock with: indirectTemps)).<br>
        ^indirectTemps at: 1<br>
<br>
I thought that it should be like and I was wondering after if and why the indirectTemps should be copied.<br>
Because we create one context for each<br>
<br>
                [:each |<br>
                                | binaryBlockCopy indirectTempsCopy |<br>
                                indirectTempsCopy := indirectTemps<br>
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>
                                indirectTempsCopy<br>
                                        at:1<br>
                                        put: (binaryBlockCopy value: (indirectTempsCopy at: 1) value: each)]<br>
                                copiedValues: (Array with: binaryBlock with: indirectTemps)).<br>
        ^indirectTemps at: 1<br></blockquote><div><br></div><div>That&#39;s right.  But the indirectTempsCopy assignment happens in the block value primitive.  The first place the indirection vector ends up is on the stack frame of the method activation:</div>
<div><br></div><div>17 &lt;8A 01&gt; push: (Array new: 1)</div><div>19 &lt;6A&gt; popIntoTemp: 2</div><div><br></div><div>The second place it ends up is in the block:</div><div><br></div><div>25 &lt;11&gt; pushTemp: 1    &lt;&lt; pushes the binaryBlock argument to inject:into:</div>
<div>26 &lt;12&gt; pushTemp: 2    &lt;&lt; pushes the indirection vector</div><div>27 &lt;8F 21 00 0A&gt; closureNumCopied: 2 numArgs: 1 bytes 31 to 40  &lt;&lt;= creates the block within inject:into: that takes a copy of the two arguments on the stack</div>
<div><br></div><div>The third place it sends up is at stack offset 2 in the inner block.  In the inner block the argument nextValue is at offset 0, binaryBlock is at offset 1, and the indirection vector is at offset 2.  So the line &quot;<span class="Apple-style-span" style="border-collapse:collapse;font-family:arial,sans-serif;font-size:13px">indirectTempsCopy := indirectTemps</span>&quot; is implicit in the value:value: primitive that activates the context.</div>
<div><br></div><div>i.e. in the value:value: primitive you&#39;ll see a simulation of this which is disabled (in the false ifTrue: arm):</div><div><br></div><div><div>BlockClosure&gt;&gt;value: firstArg value: secondArg</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span>&quot;Activate the receiver, creating a closure activation (MethodContext)</div><div><span class="Apple-tab-span" style="white-space:pre">        </span> whose closure is the receiver and whose caller is the sender of this</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span> message. Supply the arguments and copied values to the activation</div><div><span class="Apple-tab-span" style="white-space:pre">        </span> as its arguments and copied temps. Primitive. Essential.&quot;</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span>&lt;primitive: 203&gt;</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>| newContext |</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>numArgs ~= 2 ifTrue:</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>[self numArgsError: 2].</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>false</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>ifTrue: &quot;Old code to simulate the closure value primitive on VMs that lack it.&quot;</div>
<div><span class="Apple-tab-span" style="white-space:pre">                        </span>[newContext := <b>self asContextWithSender: thisContext sender</b>.</div><div><span class="Apple-tab-span" style="white-space:pre">                        </span><b>newContext at: 1 put: firstArg.</b></div>
<div><b><span class="Apple-tab-span" style="white-space:pre">                        </span>newContext at: 2 put: secondArg.</b></div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>thisContext privSender: newContext]</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>ifFalse: [self primitiveFailed]</div></div><div> </div><div><div>BlockClosure&gt;&gt;asContextWithSender: aContext</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>&quot;Inner private support method for evaluation.  Do not use unless you know what you&#39;re doing.&quot;</div>
<div><br></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>^((MethodContext newForMethod: outerContext method)</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>setSender: aContext</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>receiver: outerContext receiver</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>method: outerContext method</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>closure: self</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>startpc: startpc) <b>privRefresh</b></div></div><div><br></div><div><div>MethodContext&gt;&gt;privRefresh</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>&quot;Reinitialize the receiver so that it is in the state it was at its creation.&quot;</div>
<div><br></div><div><span class="Apple-tab-span" style="white-space:pre">        </span><b>closureOrNil</b></div><div><span class="Apple-tab-span" style="white-space:pre">                </span>ifNotNil:</div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>[pc := closureOrNil startpc.</div>
<div><span class="Apple-tab-span" style="white-space:pre">                        </span>self stackp: <b>closureOrNil numArgs + closureOrNil numCopiedValues</b>.</div><div><span class="Apple-tab-span" style="white-space:pre">                        </span><b>1 to: closureOrNil numCopiedValues</b> do:</div>
<div><span class="Apple-tab-span" style="white-space:pre">                                </span>[:i | self tempAt: <b>closureOrNil numArgs + i</b> put: (closureOrNil at: i)]]</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>ifNil:</div>
<div><span class="Apple-tab-span" style="white-space:pre">                        </span>[pc := method initialPC.</div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>self stackp: method numTemps.</div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>method numArgs+1 to: method numTemps do:</div>
<div><span class="Apple-tab-span" style="white-space:pre">                                </span>[:i | self tempAt: i put: nil]]</div></div><div><br></div><div>Make sense now?</div></div><div><br></div>-- <br>best,<div>Eliot</div><br>