【问题标题】:Interception messages in SqueakSqueak 中的拦截消息
【发布时间】:2013-04-05 05:10:42
【问题描述】:

我正在尝试更好地理解 Smalltalk 中的反射。我正在使用最新版本的 Squeak (v4.3)。我想拦截发送到我的一个类的实例的每条消息。我假设我可以覆盖方法 ProtoObject>>withArgs:executeMethod 但 Stéphane Ducasse 向我解释说,出于性能原因,不使用此方法(这是我自己对他的回答的总结)。我应该覆盖哪种方法/如何拦截发送的消息?

这是我尝试的代码:

Object subclass: #C
    instanceVariableNames: 'i'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'CSE3009'.

C class compile: 'newWithi: anInt
    ^(self new) i: anInt ; yourself.'.

C compile: 'withArgs: someArgs executeMethod: aMethod
    Transcript show: ''Caught: ''.
    ^ super withArgs: someArgs executeMethod aMethod.'.

C compile: 'foo: aText
    Transcript show: aText.
    Transcript show: i.
    Transcript cr.'.

C compile: 'i: anInt
    i := anInt.'.

o := C newWithi: 42.
o foo: 'This is foo: '.

执行这整段代码会产生:

This is foo: 42

当我想要:

Caught: This is foo: 42

【问题讨论】:

  • Squeak 的最新发布版本是 4.4。

标签: reflection smalltalk metaclass squeak objc-message-send


【解决方案1】:

没有内置的方法可以拦截此类对象的消息。我们通常使用两种方法来实现这种技巧。

首先,您可以创建一个响应doesNotUnderstand: 的包装器对象。该对象的超类通常为 nil,因此它不会从 Object 继承任何实例方法。 doesNotUnderstand: 处理程序会将其所有消息委托给目标对象。它可以选择在调用之前和之后执行代码。所有对原始对象的引用现在都将指向新的“代理”对象。发给 self 的消息不会被拦截,代理需要测试返回 self 的对象并将返回的对象更改为代理。

第二种方法是使用一种称为 Method Wrappers 的机制。方法包装器允许您将一组类中的所有方法替换为在调用原始方法之前和之后执行一些其他操作的方法。这种方法可以提供相当流畅的结果并拦截所有消息,包括发送给自己的消息。

MethodWrappers 可用于 VisualWorks 和 VASmalltalk。我相信 Squeak 和 Pharo 也可以使用它,但我并不肯定。

【讨论】:

  • Squeak 或 Pharo 有几个 MethodWrapper 实现
  • 谢谢你的回答,但我有点惊讶:我不应该能够通过反射改变方法调用的语义吗? (这就是为什么我天真地认为我可以覆盖ProtoObject>>withArgs:executeMethod:。我的意思是,类似于我可以覆盖新类方法的方式。
  • 在我看来,使用doesNotUnderstand: 的解决方案之所以有效,是因为该语言是无类型的,并且MethodWrapper(和其他ObjectTracer)是使用doesNotUnderstand: 的解决方案的实现。
  • Smalltalk 是后期绑定的,因此在实际呼叫发生时确定接收者。 Smalltalk 也不会在接收者上调用方法,但它会向它发送一个更松耦合的消息。 MethodWrappers 与doesNotUnderstand 无关。这是两个不同的东西。使用doesNotUnderstand,您可以构建一个类的替代品,而MethodWrappers 是一种替代CompiledMethod 的机制。此处使用的方法称为 run:with:in 替换对象实现
  • 如果调用方法的唯一方法是调用另一个方法,我们将永远无法调用方法。 Smalltalk 虚拟机了解如何在方法字典中查找方法并调用它们。这是在 VM 级别完成的,并且是调用方法的默认机制。这不仅仅是一个性能问题。这绝对需要由 VM 完成,否则您将根本无法完成。
【解决方案2】:

三种主要技术是:

  • 动态代理
  • 方法包装器
  • 字节码检测

要对所有可能的方法进行很好的比较,请查看 Stephane Ducasse 的“Evaluating Message Passing Control Techniques in Smalltalk”(显然你已经认识他了)。

有趣的是 F. Rivard 的“Smalltalk: A Reflective Langauge”,它展示了如何使用字节码重写来实现前置条件和后置条件。这也是一种拦截形式。

【讨论】:

  • 谢谢@ewernli,我实际上已经阅读了它们,但它们并没有直接解决我的问题......
猜你喜欢
  • 2022-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-19
  • 2012-03-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多