我正在摆弄 Lua 并使用 https://www.luac.nl/ 来看看它如何转换为字节码。我想看看像
if boolean_always_true then ... end
这样用 <const>
定义布尔值的模式是否会得到优化。与始终为 false 布尔值相同。
local always_true <const> = true
local always_false <const> = false
function foo()
if always_true then
print("Hello World!")
end
end
function bar()
if always_false then
print("Hello World!")
end
end
最终成为
function foo() --line 4 through 8
1 GETTABUP 0 0 0 ; _ENV "print"
2 LOADK 1 1 ; "Hello World!"
3 CALL 0 2 1 ; 1 in 0 out
4 RETURN0
function bar() --line 10 through 14
1 LOADFALSE 0
2 TEST 0 0
3 JMP 3 to pc 7
4 GETTABUP 0 0 0 ; _ENV "print"
5 LOADK 1 1 ; "Hello World!"
6 CALL 0 2 1 ; 1 in 0 out
7 RETURN0
为什么
always_true
情况得到优化,if
消失,而 always_false
情况却没有?
我尝试了不同的代码配置,并使用 https://www.luac.nl/ 查看生成的字节码。我原以为
bar
只是RETURN0
这与
function bar()
if false then
print("Hello World!")
end
end
您将看到优化的结果:
JMP 3 ;jump to return
GETGLOBAL 0 -1
LOADK 1 -2
CALL 0 2 1
RETURN 0 1
很好,对吧?但这里隐藏着一个问题。众所周知,Lua 中的逻辑运算符(
and
、or
)非常独特。其实它们和if-else
等一些语句的逻辑是一样的,直接跳起来会导致这样的bug:
--[[
<= 5.1.4: nil
>= 5.1.5: false
]]
print(((nil and nil) or false) and true)
由于最后一个
(nil and nil) or false
的左边表达式(and
)明显为假,所以lua跳过了对该表达式的值的求值,导致最终结果存在一些细微的偏差。所以lua的解决方案总是评估虚假表达式。
你可能会问有没有更好的解决方案,我想肯定有,但是你也要知道lua的开发者很少。