浏览"发件人"在Finder中还会返回不包含我在Pharo中搜索的消息的代码吗?

时间:2017-01-21 20:22:41

标签: compilation smalltalk pharo

打开一个新的Pharo 5图像,打开Finder并搜索+选择器。然后选择+并单击发件人。得到了大约5000个结果,其中大多数似乎都很好,但是仔细观察后,一些结果似乎缺少我发送的任何参考信息。

我可能会补充一点,其中约有1700个似乎是错误的,并且没有任何参考+什么。您认为这个问题是什么?这是一个例子:

Example

2 个答案:

答案 0 :(得分:6)

更新

搜索通过文本源代码和字节码进行;所以它显示了方法,因为它在字节码中找到了#+,但是浏览器太有限了,无法同时显示文本和字节,所以它只显示文本(即使匹配是在字节码上完成的)

至于字节码本身,Pharo(re)每次保存时都会编译一个方法;例如,当您保存以下方法时

Something>>loop
    1 to: 10 do: [ :each | ]

系统会编译它,当你检查方法并查看字节码时,你会看到这个

enter image description here

我相信你也可以手工编写字节码。

原始答案

因为对于某些特殊选择器,它还会查看字节码,您可以在检查方法本身时看到它。

enter image description here

通过查看Pharo本身的代码(一旦你对Pharo更熟悉),可以在不到一分钟的时间内轻松发现这一点:

  • #+ senders为您提供选择器的发件人列表
  • 所以你看一下senders的实现并通过可用的调用链(你可以用鼠标选择选择器或双击和ctrl + n来显示发件人浏览器)
    • sendersallSendersOf:thoroughWhichSelectorsReferTo:
  • 你看到了

thoroughWhichSelectorsReferTo: literal
    "Answer a set of selectors whose methods access the argument as a 
    literal. Dives into the compact literal notation, making it slow but 
    thorough "
    | selectors special byte |
    "for speed we check the special selectors here once per class"
    special := Smalltalk
        hasSpecialSelector: literal
        ifTrueSetByte: [ :value | byte := value ].
    selectors := OrderedCollection new.
    self selectorsAndMethodsDo: [ :sel :method |
            ((method refersToLiteral: literal) or: [special and: [method scanFor: byte]]) ifTrue: [selectors add: sel]].
    ^ selectors

答案 1 :(得分:4)

只是为了澄清@彼得的答案:

迭代

  1 to: 10 do: [:each | dictionary at: each put: each]
每次迭代块时,

+ 1发送到块变量each

我们在源代码中看不到+ 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

第15行和第16行代表实际发生的+ 1。请注意,符号#+不在文字框架中(检查器的“原始”选项卡)。它不在AST中,因为源代码中没有+

为什么会这样?

有一些特殊消息,#to:do:是其中之一,当您的代码发送时,内联。内联方法意味着在发送方中包含其字节码,而不是实际发送它们。

例如,如果您的代码为

self foo.
1 to: 10 do: [:i | i foo].
self bar

您的方法有4个发送:footo:do:foo(再次)和bar。但是,编译器将只生成3个发送foofoobar,并将包含to:do:的字节码。

鉴于to:do:需要将1加到块参数i,实际发送的send +将被检测为在您的方法中发生(因为事实上,确实如此)。

但是,如果您将方法重写为

m
  | block |
  block := [:i | i foo].
  self foo.
  1 to: 10 do: block
  self bar

编译器将拒绝内联to:do:并将其作为常规消息发送。因此,您的方法将不再是+的发件人。

如果:执行:未发送,为什么我的方法是发件人?

这是一个更微妙的问题。正如我们所看到的,当内联to:do:时,它不会被发送。那么我的方法如何被识别为to:do:的发件人?

嗯,原因是编译器无论如何都会将符号#to:do:添加到方法的文字框架中。它只是为了让你的方法被发现为to:do:的发送者。您的方法是否实际发送to:do:并不重要,内联技术只是一种优化。在更高的抽象层次上,我们都希望看到我们的方法是一个&#34;发送者&#34; to:do:,因此&#34;技巧&#34;将其添加到文字框架中。