Smalltalk Blue Book表示编译器优化ifTrue:ifFalse:并使用特殊操作码的朋友,以避免必须创建闭包并对实际的布尔对象进行动态调度.
如果我理解正确的话,那么:
o ifTrue: [ ^ 'yes' ] ifElse: [ ^ 'no' ]
Run Code Online (Sandbox Code Playgroud)
...变得像:
push o
jump_if_false 1
return 'yes'
jump 2
1:
return 'no'
2:
Run Code Online (Sandbox Code Playgroud)
(我查了一下,GNU Smalltalk正是这样做的.)
如果o是a Boolean,这将工作正常,但我不明白如果不是会发生什么.毕竟,编译器不知道是什么类型o.它不能保证它是一个布尔值.它甚至不知道该ifTrue:ifFalse:方法是否具有传统的语义.
例如:
Fnord ifTrue: tb ifFalse: fb
"muhahaha"
tb value: self.
fb value: self and: 9.
^ 7
Run Code Online (Sandbox Code Playgroud)
要使此实现起作用,true和false块必须是闭包,分别取一个和两个参数.那么编译器如何知道生成闭包?或者编译器是否允许在这种情况下中断?
这是字节码(用我正在使用的方言)
1 frameless prolog
2 load R with instance o ; o is an ivar in my code
4 test jump false 13
7 load R with literal 'yes'
9 return
10 jump 16
13 load R with literal 'no'
15 return
16 return self
Run Code Online (Sandbox Code Playgroud)
这是本地化代码(简化):
cmp eax, false ; test jump false
jz @2
cmp eax, true
jz @1
call mustBeBoolean ; <--- here!
@1: mov eax, 'true'
ret
jmp @3 ; jump
@2: mov eax, 'no'
ret
@3: move eax, esi
ret
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,JIT 编译器(又名 nativizer)检查 的接收者是否#ifTrue:ifFalse:是布尔值。更准确地说,字节码的本地化test jump false会发出代码,mustBeBoolean如果接收者不是布尔值,则该代码将发送通知。
编辑
关于你的第二个问题,#ifTrue:ifFalse:如果发送者内联(显式)块参数,则在另一个类中的实现将不起作用,如下所示:
receiver ifTrue: ['yes'] ifFalse: ['no']
Run Code Online (Sandbox Code Playgroud)
这段代码将会失败,#mustBeBoolean因为 ifreceiver是一个实例Fnord(即使receiver理解#ifTrue:ifFalse:)。原因是,在这种情况下,编译器将#ifTrue:ifFalse:像我们上面看到的那样进行优化,而不是将其作为常规消息发送。
然而,表达式
receiver ifTrue: [:a | <whatever>] ifFalse: [:a :b | <your code>]
Run Code Online (Sandbox Code Playgroud)
和
fa := [:a | <whatever>].
fb := [:a :b | <yourcode>].
receiver ifTrue: fa ifFalse: fb
Run Code Online (Sandbox Code Playgroud)
不会被优化,这意味着#ifTrue:ifFalse:将被发送。