Dear List,
I wish to understand a crucial (and difficult) point of the Squeak exception handling mechanism. Basically, my question is: How a debugger gets opened when an exception is thrown? Clearly, the method Error>>defaultAction gets invoked somehow. By UndefinedObject>>handleSignal: when reaching the bottom of the method call stack I suspect. I suspect that this handleSignal: is called by a Exception>>pass. So, my question is where? in PasteUpMorph>>becomeActiveDuring: ? This is not easy to figure out since when a debugger gets opened, the part of the stack used to open it is discarded (how?). Cheers, Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;. |
> This is not easy to figure out since when a debugger gets opened, the part
> of the stack used to open it is discarded (how?). Error>>#defaultAction raises a new exception, the UnhandledError. UnhandledError has an instance-variable to remember the original exception. UnhandledError opens the debugger as part of the #defaultAction. The debugger uses the stack-frame of the original exception to display at the top level. That's why you don't see the handling code itself. Lukas -- Lukas Renggli http://www.lukas-renggli.ch |
Thanks Lukas!
Cheers, Alexandre On 6 Aug 2008, at 14:55, Lukas Renggli wrote: >> This is not easy to figure out since when a debugger gets opened, >> the part >> of the stack used to open it is discarded (how?). > > Error>>#defaultAction raises a new exception, the UnhandledError. > UnhandledError has an instance-variable to remember the original > exception. > > UnhandledError opens the debugger as part of the #defaultAction. The > debugger uses the stack-frame of the original exception to display at > the top level. That's why you don't see the handling code itself. > > Lukas > > -- > Lukas Renggli > http://www.lukas-renggli.ch > -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;. |
In reply to this post by Lukas Renggli
Lukas
This was not the exact question. We were discussing to know who is really calling defaultAction (I know how the debugger is raised). The question is who is calling defaultAction when the stack is empty. Is it the VM or something else. Now in the image the only sender is UndefinedObject>>handleSignal: exception "When no more handler (on:do:) context left in sender chain this gets called. Return from signal with default action." ^ exception resumeUnchecked: exception defaultAction Who is calling handleSignal: Exception>>pass "Yield control to the enclosing exception action for the receiver." handlerContext nextHandlerContext handleSignal: self Exception>>signal "Ask ContextHandlers in the sender chain to handle this signal. The default is to execute and return my defaultAction." signalContext := thisContext contextTag. ^ thisContext nextHandlerContext handleSignal: self ContextPart>>handleSignal: exception "Sent to handler (on:do:) contexts only. If my exception class (first arg) handles exception then execute my handle block (second arg), otherwise forward this message to the next handler context. If none left, execute exception's defaultAction (see nil>>handleSignal:)." | val | (((self tempAt: 1) handles: exception) and: [self tempAt: 3]) ifFalse: [ ^ self nextHandlerContext handleSignal: exception]. exception privHandlerContext: self contextTag. self tempAt: 3 put: false. "disable self while executing handle block" val := [(self tempAt: 2) valueWithPossibleArgs: {exception}] ensure: [self tempAt: 3 put: true]. self return: val. "return from self if not otherwise directed in handle block" So it seems that this is signal and when handleSignal: arrives on nil then the default action is fetch. Can somebody with a VM knowledge validate my hypothesis? Setf On Aug 6, 2008, at 2:55 PM, Lukas Renggli wrote: >> This is not easy to figure out since when a debugger gets opened, >> the part >> of the stack used to open it is discarded (how?). > > Error>>#defaultAction raises a new exception, the UnhandledError. > UnhandledError has an instance-variable to remember the original > exception. > > UnhandledError opens the debugger as part of the #defaultAction. The > debugger uses the stack-frame of the original exception to display at > the top level. That's why you don't see the handling code itself. > > Lukas > > -- > Lukas Renggli > http://www.lukas-renggli.ch > > |
Stef, defaultAction is called by either ContextPart>>handleSignal: and
UndefinedObject>>handleSignal: At the bottom of the method call stack (when looking for an appropriate handler), the latter gets invoked. This method is defined as: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-= UndefinedObject>>handleSignal: exception "When no more handler (on:do:) context left in sender chain this gets called. Return from signal with default action." ^ exception resumeUnchecked: exception defaultAction -=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The question is who call this handleSignal: ? It could either be Exception>>pass and Exception>>signal. But I feel this is Exception>>pass according to what I can find by inspecting the stack of a "self halt" in a workspace: PasteUpMorph>>becomeActiveDuring: Anyone can confirm or infirm that? Cheers, Alexandre On 7 Aug 2008, at 18:03, stephane ducasse wrote: > Lukas > > This was not the exact question. We were discussing to know who is > really calling defaultAction (I know how the debugger > is raised). The question is who is calling defaultAction when the > stack is empty. > Is it the VM or something else. > > Now in the image the only sender is > > UndefinedObject>>handleSignal: exception > "When no more handler (on:do:) context left in sender chain this > gets called. Return from signal with default action." > > ^ exception resumeUnchecked: exception defaultAction > > Who is calling handleSignal: > > Exception>>pass > "Yield control to the enclosing exception action for the receiver." > > handlerContext nextHandlerContext handleSignal: self > > Exception>>signal > "Ask ContextHandlers in the sender chain to handle this signal. > The default is to execute and return my defaultAction." > > signalContext := thisContext contextTag. > ^ thisContext nextHandlerContext handleSignal: self > > ContextPart>>handleSignal: exception > "Sent to handler (on:do:) contexts only. If my exception class > (first arg) handles exception then execute my handle block (second > arg), otherwise forward this message to the next handler context. > If none left, execute exception's defaultAction (see > nil>>handleSignal:)." > > | val | > (((self tempAt: 1) handles: exception) and: [self tempAt: 3]) > ifFalse: [ > ^ self nextHandlerContext handleSignal: exception]. > > exception privHandlerContext: self contextTag. > self tempAt: 3 put: false. "disable self while executing handle > block" > val := [(self tempAt: 2) valueWithPossibleArgs: {exception}] > ensure: [self tempAt: 3 put: true]. > self return: val. "return from self if not otherwise directed in > handle block" > > So it seems that this is signal and when handleSignal: arrives on > nil then the default action is fetch. > > Can somebody with a VM knowledge validate my hypothesis? > > Setf > > > > > > On Aug 6, 2008, at 2:55 PM, Lukas Renggli wrote: > >>> This is not easy to figure out since when a debugger gets opened, >>> the part >>> of the stack used to open it is discarded (how?). >> >> Error>>#defaultAction raises a new exception, the UnhandledError. >> UnhandledError has an instance-variable to remember the original >> exception. >> >> UnhandledError opens the debugger as part of the #defaultAction. The >> debugger uses the stack-frame of the original exception to display at >> the top level. That's why you don't see the handling code itself. >> >> Lukas >> >> -- >> Lukas Renggli >> http://www.lukas-renggli.ch >> >> > -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;. |
In reply to this post by stephane ducasse
2008/8/7 stephane ducasse <[hidden email]>:
> Lukas > > This was not the exact question. We were discussing to know who is really > calling defaultAction (I know how the debugger > is raised). The question is who is calling defaultAction when the stack is > empty. > Is it the VM or something else. > > Now in the image the only sender is > > UndefinedObject>>handleSignal: exception > "When no more handler (on:do:) context left in sender chain this gets > called. Return from signal with default action." > > ^ exception resumeUnchecked: exception defaultAction > > Who is calling handleSignal: > > Exception>>pass > "Yield control to the enclosing exception action for the receiver." > > handlerContext nextHandlerContext handleSignal: self > > Exception>>signal > "Ask ContextHandlers in the sender chain to handle this signal. The > default is to execute and return my defaultAction." > > signalContext := thisContext contextTag. > ^ thisContext nextHandlerContext handleSignal: self > > ContextPart>>handleSignal: exception > "Sent to handler (on:do:) contexts only. If my exception class > (first arg) handles exception then execute my handle block (second arg), > otherwise forward this message to the next handler context. If none left, > execute exception's defaultAction (see nil>>handleSignal:)." > > | val | > (((self tempAt: 1) handles: exception) and: [self tempAt: 3]) > ifFalse: [ > ^ self nextHandlerContext handleSignal: exception]. > > exception privHandlerContext: self contextTag. > self tempAt: 3 put: false. "disable self while executing handle > block" > val := [(self tempAt: 2) valueWithPossibleArgs: {exception}] > ensure: [self tempAt: 3 put: true]. > self return: val. "return from self if not otherwise directed in > handle block" > > So it seems that this is signal and when handleSignal: arrives on nil then > the default action is fetch. > > Can somebody with a VM knowledge validate my hypothesis? > The primitive, which sits in #findNextHandlerContextStarting which is get called from #nextHandlerContext Does following: primitiveFindHandlerContext "Primitive. Search up the context stack for the next method context marked for exception handling starting at the receiver. Return nil if none found" | thisCntx nilOop | thisCntx := self popStack. nilOop := nilObj. [(self isHandlerMarked: thisCntx) ifTrue:[ self push: thisCntx. ^nil]. thisCntx := self fetchPointer: SenderIndex ofObject: thisCntx. thisCntx = nilOop] whileFalse. ^self push: nilObj ---- This primitive is done just for speed reasons. You can see, that if primitive fails (not exists in VM), the method's code does exactly same: findNextHandlerContextStarting "Return the next handler marked context, returning nil if there is none. Search starts with self and proceeds up to nil." | ctx | <primitive: 197> ctx := self. [ctx isHandlerContext ifTrue:[^ctx]. (ctx := ctx sender) == nil ] whileFalse. ^nil > Setf > > > > > > On Aug 6, 2008, at 2:55 PM, Lukas Renggli wrote: > >>> This is not easy to figure out since when a debugger gets opened, the >>> part >>> of the stack used to open it is discarded (how?). >> >> Error>>#defaultAction raises a new exception, the UnhandledError. >> UnhandledError has an instance-variable to remember the original >> exception. >> >> UnhandledError opens the debugger as part of the #defaultAction. The >> debugger uses the stack-frame of the original exception to display at >> the top level. That's why you don't see the handling code itself. >> >> Lukas >> >> -- >> Lukas Renggli >> http://www.lukas-renggli.ch >> >> > > > -- Best regards, Igor Stasenko AKA sig. |
thanks igor.
I should really install VM Maker. Stef On Aug 7, 2008, at 6:36 PM, Igor Stasenko wrote: > 2008/8/7 stephane ducasse <[hidden email]>: >> Lukas >> >> This was not the exact question. We were discussing to know who is >> really >> calling defaultAction (I know how the debugger >> is raised). The question is who is calling defaultAction when the >> stack is >> empty. >> Is it the VM or something else. >> >> Now in the image the only sender is >> >> UndefinedObject>>handleSignal: exception >> "When no more handler (on:do:) context left in sender chain >> this gets >> called. Return from signal with default action." >> >> ^ exception resumeUnchecked: exception defaultAction >> >> Who is calling handleSignal: >> >> Exception>>pass >> "Yield control to the enclosing exception action for the >> receiver." >> >> handlerContext nextHandlerContext handleSignal: self >> >> Exception>>signal >> "Ask ContextHandlers in the sender chain to handle this >> signal. The >> default is to execute and return my defaultAction." >> >> signalContext := thisContext contextTag. >> ^ thisContext nextHandlerContext handleSignal: self >> >> ContextPart>>handleSignal: exception >> "Sent to handler (on:do:) contexts only. If my exception class >> (first arg) handles exception then execute my handle block (second >> arg), >> otherwise forward this message to the next handler context. If >> none left, >> execute exception's defaultAction (see nil>>handleSignal:)." >> >> | val | >> (((self tempAt: 1) handles: exception) and: [self tempAt: 3]) >> ifFalse: [ >> ^ self nextHandlerContext handleSignal: exception]. >> >> exception privHandlerContext: self contextTag. >> self tempAt: 3 put: false. "disable self while executing handle >> block" >> val := [(self tempAt: 2) valueWithPossibleArgs: {exception}] >> ensure: [self tempAt: 3 put: true]. >> self return: val. "return from self if not otherwise directed >> in >> handle block" >> >> So it seems that this is signal and when handleSignal: arrives on >> nil then >> the default action is fetch. >> >> Can somebody with a VM knowledge validate my hypothesis? >> > > The primitive, which sits in #findNextHandlerContextStarting which is > get called from #nextHandlerContext > Does following: > > primitiveFindHandlerContext > "Primitive. Search up the context stack for the next method context > marked for exception handling starting at the receiver. Return nil if > none found" > | thisCntx nilOop | > thisCntx := self popStack. > nilOop := nilObj. > > [(self isHandlerMarked: thisCntx) ifTrue:[ > self push: thisCntx. > ^nil]. > thisCntx := self fetchPointer: SenderIndex ofObject: thisCntx. > thisCntx = nilOop] whileFalse. > > ^self push: nilObj > ---- > > This primitive is done just for speed reasons. You can see, that if > primitive fails (not exists in VM), the method's code does exactly > same: > > findNextHandlerContextStarting > "Return the next handler marked context, returning nil if there is > none. Search starts with self and proceeds up to nil." > > | ctx | > <primitive: 197> > ctx := self. > [ctx isHandlerContext ifTrue:[^ctx]. > (ctx := ctx sender) == nil ] whileFalse. > ^nil > > >> Setf >> >> >> >> >> >> On Aug 6, 2008, at 2:55 PM, Lukas Renggli wrote: >> >>>> This is not easy to figure out since when a debugger gets opened, >>>> the >>>> part >>>> of the stack used to open it is discarded (how?). >>> >>> Error>>#defaultAction raises a new exception, the UnhandledError. >>> UnhandledError has an instance-variable to remember the original >>> exception. >>> >>> UnhandledError opens the debugger as part of the #defaultAction. The >>> debugger uses the stack-frame of the original exception to display >>> at >>> the top level. That's why you don't see the handling code itself. >>> >>> Lukas >>> >>> -- >>> Lukas Renggli >>> http://www.lukas-renggli.ch >>> >>> >> >> >> > > > > -- > Best regards, > Igor Stasenko AKA sig. > > |
Free forum by Nabble | Edit this page |