Well. My final proposal is in: http://code.google.com/p/pharo/issues/detail?id=5223 With comments and some small refactors. As said, I would love if someone can take a view before integrating.
Cheers On Thu, Jan 26, 2012 at 7:25 PM, Eliot Miranda <[email protected]>wrote: > > > > On Thu, Jan 26, 2012 at 6:00 AM, Igor Stasenko <[email protected]> wrote: > >> >> On 26 January 2012 14:42, Mariano Martinez Peck <[email protected]> >> wrote: >> > >> > >> > >> > On Thu, Jan 26, 2012 at 11:38 AM, Igor Stasenko <[email protected]> >> wrote: >> >> >> >> >> >> On 26 January 2012 11:25, stephane ducasse <[email protected]> >> wrote: >> >> > >> >> >> phew... done reading through overquoting :) >> >> >> >> >> >> +1000 to removing tryXYZprimitive: >> >> >> >> >> >> I was always wondering what those methods for, until i met a need to >> >> >> support debugging when invoking nativeboost-prim methods, >> >> >> because it needs special handing when invoking methods with native >> >> >> code via debugger. >> >> >> >> >> >> Then i understood that this mechanism is necessary.. yet a bit >> awkward.. >> >> >> >> >> >> Funny. Even after implementing the fix, I still do not understand >> why all these is needed. Can someone explain to a newbie why invoking >> primitives (whether they are normal primitives, named primitives or NB >> primitives) from the debugger is different than invoking them normally (as >> when they are invoked by normal code) >> >> > >> >> > Yes I want to understand too. >> >> > >> >> >> >> Here the hint for you: >> >> - what should happen when you doing 'step in' on method, which has a >> primitive? >> >> >> >> Apparently, it should invoke that primitive , otherwise you will have >> >> difference between running and debugging modes, >> >> and will have different results, which makes debugger useless. >> >> So debugger should detect "somehow" if primitive was failed, and then >> >> step in into given method, >> >> or if its not, then step in = step over. >> >> >> >> And these 'tryXYZ ... ' is exactly for solving this dilemma. >> > >> > >> > Thanks Igor. So I wrote what I understood. Problem is that I always >> write for newbies (like me) so if it is too obvios or too long to put it as >> comment, let me know. >> > I would appreaciate if someone can validate what I wrote. >> > Today I will create a slice with mentioned solution + comments + >> removal of all those tryNamedPrimitive*. >> > >> > ---- >> > >> > "When using the debugger we want to run a method step by step. But what >> happens when we do a step into a CompiledMethod which has a primitive? If >> such a method is executed form outside the Debugger (normal scenario) the >> VM knows that such CompiledMethod has a primitive declaration and hence >> executes it. If it fails, then it continues executing all the bytecodes of >> the method. Otherwise, it just returns. >> > >> > Now, what is the problem with the Debugger? The problem is that if the >> primitive fail, we don't want that the VM directly executes all the >> remaining bytecodes of the method. Instead, we would like to go step by >> step with he Debugger, just as happens with normal methods. >> > >> > To solve the mentioned problem, we use the following trick: We have the >> original compiled method (the one that has a primitive invocation), the >> receiver and the arguments. So the idea is to use a template compiled >> method that ONLY contains the primitive declaration (it doesn't include all >> the original smalltalk code after the primitive). >> #tryNamedPrimitiveTemplateMethod answers such a template method which looks >> like: >> > >> > tryNamedPrimitive >> > <primitive:'to be set later' module:'to be ser later'> >> > ^ ContextPart primitiveFailToken' >> > >> > Since this method does not change its bytecodes for every invocation, >> we can reuse it for all methods with primitives. There are only 2 things we >> have to change in the template: the number of arguments and the primitive >> declaration (to use the correct primitive name and module name). >> > >> > Then what we do is to run that compiled method with the receiver and >> arguments we have. The result is that we will be invoking almost the same >> original method but a slightly different version that does not have the >> smalltalk part after the primitive and that in contrast is sends >> #primitiveFailToken (which tells the Debugger what to do after). If this >> method invocation does not fail, then the Debugger continues debugging the >> sender of the primitive method. In this case, the step in is the same as >> step over. If the primitive fails, then the debugger continues executing >> the smalltalk part after the primitive method. In this case, step in is a >> real step in. " >> > >> > >> There's another problem with using "tryXYZ" when primitive doing >> something weird with contexts (like block's #value) >> because then debugger cannot intercept switching contexts so easily, >> when primitive doing manipulation with contexts, >> because debugger have no idea where to put his next "break point" to >> do step-by-step evaluation. >> >> I thinking that there should be special primitive which: >> - takes a context, a receiver, a method and arguments (or just a >> context, if debugger ensures to pass it in prepared state i.e. >> receiver and args already on stack) >> - invokes method's primitive >> - answers a nil if primitive failed or a new context object, which >> holds an updated context state after primitive possibly manipulated >> with context(s) >> > > No, not needed. Manipulation of contexts does not require primitives > since contexts are first-class, and the effect of these primitives can be > simulated in Smalltalk (just as the effect of executing bytecodes can be > simulated). Look at the caller of tryPrimitive:withArgs: and > tryNamedPrimitiveIn:for:withArgs:, namely > ContextPart>doPrimitive:method:receiver:args:. It handles all these > primitives such as perform:[*], value[:value:*], withArgs:executeMethod:. > > > >> so, then debugger will use it like: >> >> contextOrNil := self invokeMethodPrimitive: method context: >> currentContext receiver: recvr arguments: args >> contextOrNil ifNil: [ primitive failed .. ] >> ifNotNil: [ >> currentContext := contextOrNil >> ]. >> >> -- >> Best regards, >> Igor Stasenko. >> > > > > -- > best, > Eliot > > > -- Mariano http://marianopeck.wordpress.com
