【編者按】Servo是一個實驗性質的網頁瀏覽器排版引擎(類似于Gecko),由Mozilla聯合三星進行開發,采用Mozilla新推出的Rust語言編寫。Servo的愿景是盡可能地發揮多核處理器的“多核”功能,而不是讓它成為硬件商的噱頭,與三星強強聯合是希望Servo和Rust最終在Android平臺和ARM設備上開花結果。
背景介紹
一個Web瀏覽器的宗旨是幫助用戶和程序間建立起友好的溝通橋梁。用戶希望瀏覽器是反應迅速的,因而瀏覽器的布局和渲染算法通常是采用底層本機代碼(Native Code)來實現的。同時,通過DOM,我們可以使用JavaScript來重構整個HTML文檔。因此,使用瀏覽器來呈現頁面時,實質上是使用了一個多語言的數據結構,使得低級本機代碼和高級語言如JavaScript之間可以無縫交流。在Servo項目中,我們會讓這個溝通的藝術變得更美好,我們有一個新的DOM內存管理方法還使用了一些Rust的功能,如:自動生成域遍歷(Auto-generating field traversals),生命周期檢驗(Lifetime checking),自定義靜態分析插件(custom static analysis plugins)。
DOM內存管理
一個還將使用的DOM一般不會被馬上銷毀,但這通常會引起use-after-free 漏洞。要解決這個問題,很多瀏覽器都使用引用計數(reference counting即內存回收)來追蹤不同底層DOM對象的指針。當用JS獲取一個DOM對象時(如getElementById),瀏覽器會在JVM中生成一個反射對象來指向一個底層對象。如果JS的垃圾回收器判斷出一個反射對象不再使用,它會銷毀這個對象并相應調減引用計數,當計數為0時,垃圾收集器會釋放引用次數為0的值所占內存。
這個辦法的確能解決use-after-free問題,但如果瀏覽器內存占用可以更少,用戶會更滿意的。因此,無用對象的銷毀是越快越好。然而,跨語言的反射對象體系會是一個障礙,出現循環引用問題。
例如下面代碼的一個C++ Element對象,它有一個Event的引用計數指針:
假設我們在JS中為element對象添加一個事件:
代碼執行時,event的屬性會回指Element,這就是一個跨語言循環引用例子。C++的回收器不會銷毀這個循環,JS的垃圾收集器追蹤不到C++指針,因此引用計數無法減為0,這些對象將永遠不會被銷毀。盡管存在一些解決方法,但是可能會導致其它諸如內存泄漏,NULL指針的問題。
有鑒于此,我們在Servo嘗試了新的思路―不再對DOM對象進行引用計數。取而代之的,我們賦予了JS垃圾回收器全權來管理這些底層DOM對象。要實現它,Servo的Rust代碼與SpiderMonkey的垃圾回收器之間需要做一個相當繁復的互動,還好Rust一些炫酷的特性會帶來極大的幫助。
自動生成字段遍歷(Auto-generating field traversals)
Rust中有一個traits(特性)的概念,類似于Haskell語言中的類型類或面向對象語言中的接口。
例如Collection trait類型:
這個集合類型描述的是任何元素的類型集合,這里的len方法是用于獲取集合的長度。
還有一個是Endodable trait類型,用于序列化。
任何可序列化的類型都有一個泛型的encode方法。Encodable trait是一個特殊的類型,因為編譯器能自行來實現它。
我們來看看DOM的Document文檔接口在Servo中的實現:
deriving屬性會讓編譯器遞歸地對node,window等字段進行encode處理。如果往Document中添加了一個非Endoable的字段,編譯器會報錯,這就確保了在編譯時進行全字段跟蹤。
生存期檢查(Lifetime checking)
在Servo中,需要用Rust來傳遞一個DOM對象指針作為函數參數,在本地變量中存儲DOM對象指針等等。這些額外的臨時引用,需要在垃圾回收器中進行可到達分析時,以roots的形式來登記。否則,會引致 use-after-free 問題。為了解決該問題,Rust中引入了編譯時生存期檢查器,它會使編譯器識別并拒絕use-after-free或其它的危險錯誤。
例如:
這個語法的意思是:
1. <’a>: “任何值為’a生存期”
2. (&'a self):“一個在’a有效生存期內的Root引用”
3. ->JSRef<'a, T>:“返回一個生存期參數為’a的JSRef”
如果我們嘗試執行下面的代碼:
我們會收到這樣的錯誤提示:
有關該語法中有關概念的詳細解釋,請點擊這里進行了解。
自定義靜態分析 (custom static analysis)
Rust的編譯器能夠載入“lint”插件進行自定義靜態分析,從字面來看,”lint”的宗旨是采取低開銷戰略來捕捉常見錯誤。我們希望其與生存期檢查器一起為開發者帶來更安全可靠的瀏覽器引擎。
寫在最后
Rust語言(最新版本0.11.0)和Servo還處于實驗階段,仍需各方協力來不斷完善,如果想進一步了解Rust或Servo,請嘗試訪問下面的鏈接。
更多詳細內容:mozilla.org