Compilation 浏览「;“发件人”;in Finder还返回不包含';t包含消息I';我在法罗寻找什么?
打开新的Pharo 5图像,打开Finder并搜索+选择器。然后选择+并单击发件人。得到了大约5000个结果,其中大多数看起来不错,但是经过仔细检查,有些结果似乎遗漏了我发送的消息的任何参考 我要补充的不是几个,其中大约1700个似乎是错误的,并且没有提及任何这样的内容。你认为问题出在哪里?这里有一个例子: 更新 搜索通过文本源代码和字节码进行;因此它向您显示了该方法,因为它在字节码中找到了Compilation 浏览「;“发件人”;in Finder还返回不包含';t包含消息I';我在法罗寻找什么?,compilation,smalltalk,pharo,Compilation,Smalltalk,Pharo,打开新的Pharo 5图像,打开Finder并搜索+选择器。然后选择+并单击发件人。得到了大约5000个结果,其中大多数看起来不错,但是经过仔细检查,有些结果似乎遗漏了我发送的消息的任何参考 我要补充的不是几个,其中大约1700个似乎是错误的,并且没有提及任何这样的内容。你认为问题出在哪里?这里有一个例子: 更新 搜索通过文本源代码和字节码进行;因此它向您显示了该方法,因为它在字节码中找到了#+,但浏览器太有限,无法同时显示文本和字节,因此它只显示文本(即使匹配是在字节码上完成的) 至于字节码本
#+
,但浏览器太有限,无法同时显示文本和字节,因此它只显示文本(即使匹配是在字节码上完成的)
至于字节码本身,Pharo(re)每次保存时都会编译一个方法;例如,当您保存以下方法时
Something>>loop
1 to: 10 do: [ :each | ]
系统将编译它,当您检查该方法并查看字节码时,您将看到
我相信你也可以手工编写字节码
原始答案 因为对于一些特殊的选择器,它也会查看字节码,当您检查方法本身时可以看到字节码 在不到一分钟的时间内,通过查看Pharo本身的代码可以很容易地发现这一点(一旦您对Pharo更熟悉):
提供选择器的发送者列表#+发送者
- 因此,您可以查看
→ <代码>选民所指的全部内容:发件人的实现,并浏览可用的调用链(您可以使用鼠标或双击并按住ctrl+n键选择选择器,以显示发件人浏览器)
→ <代码>所有发件人类别:发件人
只是想澄清一下@Peter的回答: 迭代
1 to: 10 do: [:each | dictionary at: each put: each]
每次迭代块时,向块变量发送+1
我们在源代码中没有看到+1
,但它实际上发生在幕后。之所以会发现这样一个隐藏的发送,是因为Smalltalk不分析源代码,而是分析CompiledMethod
,而是通过查看文本帧和字节码(正如Peter所说)来分析源代码
例如,通过编译和检查方法
m
1 to: 10 do: [:i | i foo]
我们可以看到中间表示Ir选项卡的内容如下:
1. label: 1
2. pushLiteral: 1
3. popIntoTemp: #i
4. goto: 2
5. label: 2
6. pushTemp: #i
7. pushLiteral: 10
8. send: #'<='
9. if: false goto: 4 else: 3
10. label: 3
11. pushTemp: #i
12. send: #foo
13. popTop
14. pushTemp: #i
15. pushLiteral: 1
16. send: #+ "Here we sum 1 to i"
17. popIntoTemp: #i
18. goto: 2
19. label: 4
20. returnReceiver
您的方法有4次发送:foo
,发送到:do:
,foo
(再次)和条
。但是,编译器将只生成3次发送foo
、foo
和bar
,并将to:do:
的字节码包含在适当的位置
假设to:do:
需要对块参数i
求和1
,则实际发送的send+
会被检测为在方法中发生(因为它确实发生了)
但是,如果将该方法重写为
m
| block |
block := [:i | i foo].
self foo.
1 to: 10 do: block
self bar
编译器将拒绝内联到:do:
,并将其作为常规消息发送。因此,您的方法将不再是+
的发送者
如果未发送to:do:,为什么我的方法是它的发送者?
这是一个更微妙的问题。正如我们所看到的,当to:do:
内联时,它不会被发送。那么,我的方法如何可能被识别为to:do:
的发送方
原因是编译器无论如何都会将符号#to:do:
添加到方法的文本框架中。它会这样做,只是为了允许您的方法作为to:do:
的发送者被找到。不管您的方法是否真的将发送到:do:
,内联技术只是一种优化。在更高的抽象层次上,我们都希望看到我们的方法是to:do:
的“发送者”,因此需要将其添加到文本框架中的“技巧”。您的答案是信息性的,但我仍然不明白。看到我上传的图片中的代码,我没有看到任何对+或#+的调用。当我没有实际编写字节码时,+的调用是通过什么机制进入字节码的。仅仅编译就可以做到这一点吗?查看BytecodeEncoder>>#如果:isSpecialLiteralForPush:从本质上看,系统决定在哪里包含此发送:+bytecode?@Niki编译器进行了大量优化,您有时还可以在方法中看到
杂注(使用Finder并选择“杂注”搜索),这也会触发VM的不同行为,我现在明白了。可能是出于性能原因,在幕后有人这样做。我想我在编码器中找到了发生这种情况的地方。它在做一些“断言”,最后是“流下一个输出:specialSelectorIndex+175”,我不明白这是如何生成发送的:+它背后的逻辑是什么?@Niki-seeOCASTTranslator>>emitToDo:step:
;编译器执行语义分析,并将转换为:do:
调用为特定字节码。您还可以手动执行编译(Someclass compile:'myCode
)并使用调试器跟踪执行情况。我现在明白了为什么会找到并显示它,基本上字节码中有+,但finder看不到字节码,所以您会看到实际上不包含+的文本。smalltalk决定在何处添加o消息,它是如何工作的?太棒了!我现在明白了!
self foo.
1 to: 10 do: [:i | i foo].
self bar
m
| block |
block := [:i | i foo].
self foo.
1 to: 10 do: block
self bar