Reputation: 3244
When you start a code sending not implemented message, Pharo launches debugger.
As far as I understand it works through Object >> doesNotUnderstand
, which trigger exception, and this leads to debugger window.
The question is what exactly does Object >> doesNotUnderstand
do, and how is that different to other interactive helpers, like one starting on not existent variable?
Upvotes: 5
Views: 282
Reputation: 14868
Just to complement the excellent answer given by Adrei Chis, let me add that the message #doesNotUnderstad:
is a little bit different from other messages.
Every time a message is sent, the Runtime (usually in the Virtual Machine), finds the method that corresponds to the intended receiver for the selector of the message being sent.
It does this by looking at the receiver's behavior. If no method is found it goes to the inherited behavior (usually the one defined in the superclass), and continues this way until it finds the method or the inheritance chain is exhausted. This search is called method lookup.
In the second case (when no method exists in the behavior hierarchy for the object), the Runtime (1) reifies the message by creating a Message
object with the selector not found and the actual arguments (if any) and (2) sends the receiver a new message with selector #doesNotUnderstand:
and argument the message just reified.
The lookup process repeats, and (most likely) this time the selector #doesNotUnderstand:
is found for the receiver (which could have implemented its own version, or have inherited it from the top of the class hierarchy). At this point the steps described by Adrei follow.
If for any reason the receiver does not understand #doesNotUnderstand:
(pun intended), the Runtime cannot continue and closes the system (what else it could do?)
Note also that the lookup is a little bit different if the message was sent to super
. But that is another story and the basic ideas w.r.t. this question remain.
Upvotes: 5
Reputation: 723
The debugger is opened as a response to an unhandled exception. To better explain we can start from triggering an exception that is not caught anywhere in the system. For example, we can execute in a Playground Error signal: 'an error'.
(signal is throwing an error in Pharo). This opens the following debugger:
When an exception happens the system first tries to find an exception handler for that exception. If no exception handler is found then the system sends the message defaultAction
to the exception. This is implemented in the class Error
as:
Error>>#defaultAction
"No one has handled this error, but now give them a chance to decide how
to debug it. If none handle this either then open debugger
(see UnhandedError-defaultAction)"
UnhandledError signalForException: self
So the system responds to an unhandled exception by throwing another exception, UnhandledError
. Again if no exception handlers are found for the new exception the system sends the message defaultAction
to the exception object. In this case the exception is an instance of UnhandledError
and this class overriders defaultAction
with the following implementation:
UnhandledError>>#defaultAction
<reflective: #unhandledErrorDefaultAction:message:>
^ UIManager default unhandledErrorDefaultAction: self exception
The method unhandledErrorDefaultAction:
is quite simple and just sends the exception object the message debug
.
MorphicUIManager>>#unhandledErrorDefaultAction: anException
anException debug
The method debug is implemented in Exception
, the root class of all exceptions in Pharo as:
Exception>>#debug
"open a debugger on myself"
Processor activeProcess
debug: self signalerContext
title: self description
So this is what opens the debugger.
In the case of sending an unknown message to an object an exception of type MessageNotUnderstood
is thrown by the method Object>>#doesNotUnderstand:
. This exception follows the same chain as before and the system ends up sending the debug
message to the MessageNotUnderstood
exception which opens the debugger.
So in short:
#doesNotUnderstand:
;doesNotUnderstand:
raises an exception of type MessageNotUnderstood
;UnhandledError
is raised;UnhandledError
is not caught it asks the UI manager to handle this case;debug
to the initial exception, in this case the MessageNotUnderstood
exception (only MorphicUIManager
does this; other UI managers like CommandLineUIManager
do other actions like existing the image);debug
message opens the debuggerThe handler for non existent variables has a totally different implementation that is in the actual compiler. When code is compiled in a class that has unknown variables the compiler detects that and asks the user what to do: create a new instance variable or a local parameter.
If you try to execute code containing an unknown class, like UnnknowsClass new.
the compiler also detects this and asks the user what to do. There is no exception raised in this case.
There are other helpers that use the same mechanism as doesNotUnderstand:
for example raising notifications. If you execute Object new notify: 'a notification'
the method notify:
throws a Warning
exception that ends up opening the debugger.
Upvotes: 7