当消息未被明确时,Pharo如何启动调试器?

uhb*_*f19 5 debugging smalltalk pharo

当您启动代码发送未实现的消息时,Pharo启动调试器.

据我所知,它通过Object >> doesNotUnderstand,触发异常,这导致调试器窗口.

在此输入图像描述

问题是究竟做了Object >> doesNotUnderstand什么,以及与其他交互式帮助者有什么不同,比如从不存在的变量开始?

小智 6

调试器将作为对未处理异常的响应而打开.为了更好地解释,我们可以从触发系统中未捕获的异常开始.例如,我们可以在Playground中执行Error signal: 'an error'.(信号在Pharo中抛出错误).这将打开以下调试器:

在此输入图像描述

发生异常时,系统首先尝试为该异常查找异常处理程序.如果未找到异常处理程序,则系统将消息发送defaultAction到异常.这在类中实现Error为:

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
Run Code Online (Sandbox Code Playgroud)

因此,系统通过抛出另一个异常来响应未处理的异常UnhandledError.同样,如果没有为新异常找到异常处理程序,系统会将消息发送defaultAction到异常对象.在这种情况下,异常是一个实例,UnhandledError并且此类覆盖defaultAction具有以下实现:

UnhandledError>>#defaultAction
    <reflective: #unhandledErrorDefaultAction:message:>
    ^ UIManager default unhandledErrorDefaultAction: self exception
Run Code Online (Sandbox Code Playgroud)

该方法unhandledErrorDefaultAction:非常简单,只是向异常对象发送消息debug.

MorphicUIManager>>#unhandledErrorDefaultAction: anException
    anException debug
Run Code Online (Sandbox Code Playgroud)

方法debug Exception在Pharo中的所有异常的根类中实现为:

Exception>>#debug
    "open a debugger on myself"
    Processor activeProcess 
        debug: self signalerContext
        title: self description
Run Code Online (Sandbox Code Playgroud)

这就是打开调试器的原因.

在向对象发送未知消息的情况下MessageNotUnderstood,该方法抛出类型异常Object>>#doesNotUnderstand:.此异常遵循与之前相同的链,系统最终将debug消息发送到MessageNotUnderstood打开调试器的异常.

简而言之:

  • 当向对象发送未知消息时,系统向对象发送消息#doesNotUnderstand:;
  • doesNotUnderstand:提出类型的例外MessageNotUnderstood;
  • 如果未捕获此异常,则会引发另一个类型异常UnhandledError;
  • 如果UnhandledError没有捕获到它,它会要求UI管理器处理这种情况;
  • UI管理器将消息发送debug到初始异常,在这种情况下是MessageNotUnderstood异常(仅MorphicUIManager执行此操作;其他UI管理器喜欢CommandLineUIManager执行其他操作,如现有映像);
  • debug消息打开调试器

不存在的变量的处理程序具有与实际编译器完全不同的实现.当代码在具有未知变量的类中编译时,编译器会检测到该代码并询问用户要执行的操作:创建新的实例变量或本地参数.

如果您尝试执行包含未知类的代码,UnnknowsClass new.则编译器也会检测到此类并询问用户该怎么做.在这种情况下没有例外.

还有其他帮助程序使用相同的机制doesNotUnderstand:,例如引发通知.如果执行Object new notify: 'a notification'该方法,则notify:抛出Warning异常,最终打开调试器.

在此输入图像描述


Lea*_*lia 5

为了补充Adrei Chis给出的优秀答案,让我补充一点,该消息#doesNotUnderstad:与其他消息略有不同.

每次发送消息时,运行时(通常在虚拟机中)都会找到与正在发送的消息的选择器的预期接收者相对应的方法.

它通过查看接收者的行为来做到这一点.如果没有找到方法,则转到继承的行为(通常是超类中定义的行为),并继续这种方式,直到找到方法或继承链耗尽为止.此搜索称为方法查找.

在第二种情况下(当对象的行为层次结构中不存在任何方法时),Runtime(1)通过创建一个Message未找到选择器的对象和实际参数(如果有的话)和(2)发送接收器来重新启动消息带有选择器#doesNotUnderstand:和参数的新消息刚才具有的消息.

查找过程重复,并且(很可能)这次#doesNotUnderstand:为接收器找到了选择 器(可能已经实现了自己的版本,或者从类层次结构的顶部继承了它).此时,Adrei描述的步骤如下.

如果由于任何原因接收器不理解#doesNotUnderstand:(双关语),运行时无法继续并关闭系统(它还能做什么?)

另请注意,如果发送消息,查找会略有不同super.但这是另一个故事,而这个问题的基本思想仍然存在.