当消息不被理解时,Pharo 如何启动调试器?

How does Pharo starts debugger when message is not understanded?

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

据我所知,它通过 Object >> doesNotUnderstand 工作,这会触发异常,这会导致调试器 window。

问题是 Object >> doesNotUnderstand 到底做了什么,它与其他交互式助手有何不同,比如从不存在的变量开始?

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

当异常发生时,系统首先尝试为该异常寻找异常处理程序。如果未找到异常处理程序,则系统将消息 defaultAction 发送到异常。这在 class 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

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

UnhandledError>>#defaultAction
    <reflective: #unhandledErrorDefaultAction:message:>
    ^ UIManager default unhandledErrorDefaultAction: self exception

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

MorphicUIManager>>#unhandledErrorDefaultAction: anException
    anException debug

方法调试在Exception中实现,Pharo中所有异常的根源class为:

Exception>>#debug
    "open a debugger on myself"
    Processor activeProcess 
        debug: self signalerContext
        title: self description

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

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

简而言之:

  • 当向对象发送未知消息时,系统会向该对象发送消息 #doesNotUnderstand:
  • doesNotUnderstand: 引发 MessageNotUnderstood;
  • 类型的异常
  • 如果未捕获此异常,则会引发另一个 UnhandledError 类型的异常;
  • 如果这个新的 UnhandledError 没有被抓到,它会要求 UI 经理处理这个案例;
  • UI 管理器将消息 debug 发送到初始异常,在本例中为 MessageNotUnderstood 异常(只有 MorphicUIManager 这样做;其他 UI 像 CommandLineUIManager 这样的经理会执行其他操作,例如现有图像);
  • debug 消息打开调试器

不存在变量的处理程序与实际编译器中的实现完全不同。当代码在具有未知变量的 class 中编译时,编译器会检测到并询问用户该怎么做:创建一个新的实例变量或局部参数。

如果您尝试执行包含未知 class 的代码,例如 UnnknowsClass new.,编译器也会检测到这一点并询问用户该怎么做。在这种情况下没有引发异常。

还有其他助手使用与 doesNotUnderstand: 相同的机制,例如发出通知。如果你执行 Object new notify: 'a notification' 方法 notify: 抛出一个 Warning 异常,最终打开调试器。

为了补充 Adrei Chis 给出的出色答案,让我补充一下消息 #doesNotUnderstad: 与其他消息略有不同。

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

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

在第二种情况下(当对象的行为层次结构中不存在任何方法时),运行时 (1) 通过创建 Message 未找到选择器的对象和实际参数 (如果有的话)和(2)向接收者发送一条带有选择器 #doesNotUnderstand: 的新消息,并且参数是刚刚具体化的消息。

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

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

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