在 Squeak 中,我在哪里可以找到消息处理算法的代码?
当向 Squeak 中的对象发送消息时,运行时调用算法类似于
- curr <- 接收者的类
- Repeat while curr 不为 nil
- 在该类的方法中搜索选择器;如果存在,则调用它并返回
- curr <- curr 的超类
- 在
self
上调用doesNotUnderstand:
现在,respondsTo:
使用了一个非常相似的算法code> 方法,通过检查 respondsTo:
的代码确实可以看到。 我试图找到的是用于调用的上述算法的代码的位置。
我知道 perform:
做了类似的事情,但我相信它不用于常规方法调用,而仅作为类似反射的方法调用机制(例如,当程序员直到运行时才知道方法名称时) 。
如果上面的代码也隐藏为原始指令,那么我在哪里可以找到原始调用?如果不是,我在哪里可以找到代码本身?
When sending a message to an object in Squeak, the runtime invocation algorithm is something like
- curr <- the receiver's class
- Repeat while curr isn't nil
- Search for the selector in that class's methods; if it's there, invoke it and return
- curr <- curr's superclass
- Call
doesNotUnderstand:
onself
Now, a very similar algorithm is used for the respondsTo:
method, and indeed it can be seen by inspecting respondsTo:
's code. What I'm trying to find is the location of the code for the above algorithm used for invocation.
I know perform:
does something similar but I believe it's not used for regular method invocation but only as a reflection-like method calling mechanism (e.g. when the method name is not known to the programmer until the runtime).
If the code above is also hidden as a primitive directive, where would I find the primitive call? If it isn't, where would I find the code itself?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您可能想查看VMMaker。它的 Interpreter 类是执行 CompiledMethod 字节码的人,并且实际上会将消息发送到您的对象。
例如,如果您查看 Object>>respondsTo 的字节码:您将看到
解释器读取字节码,在其 BytecodeTable 中查找该字节码(在 Interpreter 类>>initialiseBytecodeTable 中初始化)并执行适当的方法。所以<70> (#pushReceiverByteCode) 将 self 推送到解释器的内部堆栈上。然后 (#bytecodePrimClass) 归结为“找到自己的类”。 <10> (#pushTemporaryVariableBytecode) 将 #respondsTo: 的参数推入堆栈。有趣的部分发生在 (#sendLiteralSelectorBytecode),它调用
self normalSend
。 #normalSend 依次找出接收者的类(在本例中为 self class ),然后调用 self commonSend ,它会找到我们要运行的实际方法,并且然后运行它。我是一个虚拟机新手;上面可能不是查看算法实际运行等的绝对最佳位置(甚至不是最好的解释),但我希望这是一个很好的起点。
虚拟机实际发送消息所使用的算法正如您在问题中概述的那样。该算法的实际实现在
Interpreter>>commonSend
中定义。查找算法位于Interpreter>>lookupMethodInClass:
中,执行算法位于Interpreter>>internalExecuteNewMethod
中。前者的工作方式与您所描述的非常相似:
后者的工作方式如下:
You'd probably want to look at VMMaker. Its Interpreter class is the guy that executes a CompiledMethod's bytecodes, and will actually send the messages to your objects.
For instance, if you look at the bytecodes for Object>>respondsTo: you'll see
The Interpreter reads in a bytecode, looks up that bytecode in its BytecodeTable (initialised in Interpreter class>>initialiseBytecodeTable) and executes the appropriate method. So <70> (#pushReceiverByteCode) pushes self onto the Interpreter's internal stack. Then (#bytecodePrimClass) boils down to "find self's class". <10> (#pushTemporaryVariableBytecode) pushes the argument to #respondsTo: onto the stack. The interesting part happens with (#sendLiteralSelectorBytecode), which calls
self normalSend
. #normalSend in turn figures out the class of the receiver (self class
in this case), and then callsself commonSend
, which finds the actual method we seek to run, and then runs it.I'm a VM newbie; the above might not be the absolute best place to see the algorithm in action, etc., (or even the best explanation) but I hope it's a good place to start.
The algorithm used by the VM to actually send a message is as you outline in your question. The actual implementation of that algorithm's defined in
Interpreter>>commonSend
. The lookup algorithm's inInterpreter>>lookupMethodInClass:
and the execution algorithm's inInterpreter>>internalExecuteNewMethod
.The former works much as you describe:
The latter works like this:
进一步挖掘,
ContextPart
类是一个能够运行字节码的解释器。根据其文档:如果我们检查它如何解释字节码,
interpret
方法会为每条指令调用它的interpretNextInstructionFor:
。interpretNextInstructionFor:
调用send:super:numArgs:
。send:super:numArgs:
调用send:to:with:super:
(假设它不是原始消息)。send:to:with:super:
使用Behavior
的lookupSelector:
来定位要使用的正确选择器。Behavior
的lookupSelector:
负责问题中出现的算法中的超类循环。所以这不是我正在寻找的实际实现(因此这不是真正的答案),但我想它可以帮助理解精确算法的细微差别。
Digging some more, the
ContextPart
class is an interpreter capable of running bytecode. According to its documentation:If we check how it interprets bytecode,
interpret
method calls itsinterpretNextInstructionFor:
for each instruction.interpretNextInstructionFor:
callssend:super:numArgs:
when a send instruction is encountered.send:super:numArgs:
callssend:to:with:super:
(assuming it is not a primitive message).send:to:with:super:
usesBehavior
'slookupSelector:
to locate the correct selector to use.Behavior
'slookupSelector:
is the one responsible for the superclass loop in the algorithm appearing in the question.So this is not the actual implementation I was looking for (and thus this is not really an answer), but I guess that it can help with understanding the nuances of the precise algorithm.
要理解 Franks 的响应,您需要一些背景信息:
编译器生成一个“发送字节码”,稍后由 VM 的字节码解释器执行(或 jitted,但语义相同)。因此,您不会期望在任何类中找到实现,而是在虚拟机中找到。
大多数其他 VM 都是用 C、汇编程序或其他语言编写的...
但是:squeak VM 是用 Smalltalk 编写的,并由“Smalltalk 到 C 编译器的子集”(所谓的“俚语”)编译为 C ,因为它没有涵盖完整的 Smalltalk 语义)。
该虚拟机是用 Smalltalk 编写的,当然可以在 Squeak 中进行开发、调试和测试(通过在镜像中运行镜像上的俚语解释器)。这就是为什么您可以在 Interpreter 中找到 Frank 所描述的实现。
To understand Franks response, you need some background info:
the compiler generates a "send bytecode", which is later executed by the VM's bytecode interpreter (or jitted, but the semantics is the same). Thus, you would not expect to find the implementation in any class, but in the VM.
Most other VM's are written in C, Assembler or what else...
However: the squeak VM is written in Smalltalk, and compiled to C by a "Subset-of-Smalltalk-to-C-Compiler" (so called "Slang", because it does not cover the full Smalltalk semantics).
Being written in Smalltalk, that VM can of course be developed, debugged and tested from within Squeak (by running the Slang-interpreter on the image from within the image). That is why you can find the implementation in Interpreter as described by Frank.