Thanks eliot I'm refreshing my fuzzy understanding of closure implementation details (I would love to avoid to have students, kids, bosses and ⦠around to get some calm time :). I will start rereading your blog.
tempAt: in Method context uses <primitive: 210>, which is failing. Maybe there is a new primitive we are not aware of for Blocks...
No, 210 is what is needed. The issue is my implementation of temporary initialization in blocks, which simply uses pushNil to allocate and initialize each temp. So if one inspects [|a|a:=2] and sends it self method symbolic you get:
13 <8F 00 00 05> closureNumCopied: 0 numArgs: 0 bytes 17 to 21 17 <73> pushConstant: nil 18 <77> pushConstant: 2 19 <81 40> storeIntoTemp: 0 21 <7D> blockReturn 22 <7C> returnTop
And when you say self asContext pc you get 17, which is *before* the nil is pushed. So you'll want to modify tempNamed: to compare the index of the temporary with the stack pointer and answer nil if the index is beyond the stack pointer.
Is it correct? I think so but I'm learning ContextPart tempNamed: aName "Returns the value of the temporaries, aName." "Implementation notes: temporary initialization in blocks simply uses pushNil to allocate and initialize each temp. So if one inspects [|a|a:=2] and sends it self method symbolic you get: 13 <8F 00 00 05> closureNumCopied: 0 numArgs: 0 bytes 17 to 21 17 <73> pushConstant: nil 18 <77> pushConstant: 2 19 <81 40> storeIntoTemp: 0 21 <7D> blockReturn 22 <7C> returnTop And when we check self asContext pc we get 17, which is *before* the nil is pushed. Therefore we should pay attention when querying a temporary if the temporary allocation was executed." | index | index := (self tempNames indexOf: aName). ^ index >= stackp ifTrue: [ nil] ifFalse: [self tempAt: (self tempNames indexOf: aName)]
The same bug exists in Squeak, e.g. the below gave an error until I fixed DebuggerMethodMap>>namedTempAt:in:
I checked and this method is the same in pharo and latest squeak namedTempAt: index in: aContext "Answer the value of the temp at index in aContext where index is relative to the array of temp names answered by tempNamesForContext:" ^self privateTempAt: index in: aContext startpcsToBlockExtents: aContext method startpcsToBlockExtents
| b | b := [ |a| a:=2 ]. { b asContext tempNames. b method debuggerMap tempsAndValuesForContext: b asContext } #(#('b' 'a') 'b: [closure] in UndefinedObject>>DoIt a: nil ')
Why did I use pushNil to allocate temps? It was not a good choice; it means for example that to determine how many temps a block has one has to parse all the bytecodes and see where the stack pointer is at the end (since initial pushNils are ambiguous; an initial pushNil might be producing a parameter or result or initializing a temp). But not using pushNil would have meant a more complex bytecode for closure creation plus another inst var in BlockClosure. I made another similar misstep in not storing the method in a BlockClosure, hence preventing simple implementation of clean blocks (one needs to synthesize an outerContext also). We live and learn.
Do you mean that the closure cannot access directly method but should do go to its outer context to access the methods. When this is needed to set the instruction pointer? Another thoughts while writing the chapter on block execution.. Am I correct to say: we need contexts (to me contexts are activation objects by opposition to bindings which could be used to represent scope) to query local temps of a closure because they will be allocated at execution while the temporaries that are shared are defined in their defining environment (now the trick is that in Smalltalk environment is represented as a context). I get why I'm often slow because I did not implemented closure in Smalltalk only in lisp foo |b| b := 3. ^ [ |a | a :=2. a] In my stupid lisp implementation an environment is represented by bindings and closure points to their own environment so [ |a | a :=2. a] points to (b.3) (toplevel) (and not top-level) and for closure execution we just added temporaly a binding for a to the closure environment. eval a:=2. a in (a) (b.3) (toplevel)