當編譯器使用SSA形式表示代碼時,對局部變量的更新成爲新變量。但是,當變量處於封閉範圍內時,這並不總是有效的,例如, (使用JavaScript語法說明,可以在許多語言中出現的情況):在封閉範圍內更新變量的SSA表示
function f() {
var x = 1;
function g() {
x++;
}
...
}
什麼是代表這個通常的方法是什麼?
當編譯器使用SSA形式表示代碼時,對局部變量的更新成爲新變量。但是,當變量處於封閉範圍內時,這並不總是有效的,例如, (使用JavaScript語法說明,可以在許多語言中出現的情況):在封閉範圍內更新變量的SSA表示
function f() {
var x = 1;
function g() {
x++;
}
...
}
什麼是代表這個通常的方法是什麼?
封閉中使用的可變自由變量(在您的示例中爲x
)需要隱式「裝箱」。
有兩個問題需要考慮。首先,生命期:變量可能超過它創建的範圍。 (f
可能會返回g
,或將其存儲在持久性容器中。)其次,共享:可通過f
,g
或由f
創建的任何其他函數修改該變量。
最簡單的解決方案是將變量更改爲「框」(一個對象的容器,它是變量的值)。盒子本身是不可變的(也就是說,這個名字總是指向相同的盒子)。修改和引用變量的值成爲容器setter和getter方法。當然,該值的存儲必須在不再需要時動態分配和回收(與任何容器一樣)。
在某些情況下可以優化 - 甚至可能是大多數情況。首先,如果變量永遠不會被修改,則可能爲每個閉包賦予一個值的副本,而不是裝箱該值。
其次,如果變量是不共享 - 這是不是通過的f
執行創建的任何其他功能創造g
後關閉,它不被引用的f
- 變量可以簡單地轉移到g
「關閉。
實際上,在上述兩種情況下,閉包本身都變成了盒子,但這具有不需要單獨動態分配的優點。
證明這些優化在特定程序中的有效性需要良好的靜態分析。第一個很簡單,因爲修改很容易檢測句法,但第二個需要流程分析(至少)。
在上面,我對可能應用優化的情況進行了保守的描述,只需要簡單的流量分析;檢測其他可能的應用程序更爲複雜。