一、靜態程式碼分析
靜態程式碼分析是一種通過檢查程式碼而不是執行程式來發現原始碼中錯誤的手段。通常可以幫助我們發現常見的編碼錯誤,例如:
-
語法錯誤
-
違反制定的標準編碼
-
未定義的變數
-
安全性問題
靜態程式碼分析可以通過評估編寫的程式碼來提高程式碼質量;可以穩定的執行且可以輕鬆自動化;增加了在原始碼中發現漏洞的可能性,從而提高應用安全;由於是針對原始碼掃描可以在離線的開發環境中完成。但是靜態程式碼分析並不能完全保證編寫的程式碼沒有Bug,它也有一些缺點,例如:
-
誤報問題,發現了一個不是錯誤的錯誤。
-
靜態分析的規則需要特定維護,並非總是適用。
-
系統和第三方庫可能無法分析。
二、為何需對Lua程式碼進行靜態分析
Lua指令碼語言在效能方面非常出色、語法簡單且容易上手。目前有相當多的遊戲伺服器和客戶端程式都是使用它來開發功能業務。Lua屬於解釋性語言,編寫的邏輯程式碼只有在程式執行的過程才會發現邏輯錯誤,而一些較隱藏的分支可能要經過很長時間的執行才能觸發錯誤。未發現的錯誤如果在正式環境中觸發將會產生不可預估的損失。
如何發現更多的錯誤,除了通過更加詳細的測試外,還可以利用工具來彌補,下面列舉了一些平時常見的錯誤問題,如果使用靜態掃描分析工具是很容易發現的。
1. 變數的作用域問題[1],定義的變數在超出了作用域外使用。
1 function demo(param) 2 if param then 3 local var = param.smobj:get_var() 4 -- do_something() 5 end 6 -- 宣告的`var`變數在超出作用域後訪問 7 local new_var = var + 3 8 end
2. 變數的作用域問題[2],超出作用域變數作為條件判斷
1 function demo(param) 2 if param then 3 local var = param.smobj:get_var() 4 -- do_something() 5 end 6 if var then 7 -- 永遠無法執行到,但不會報錯 8 local new_var = var + 10 9 -- do_something() 10 end 11 end
3. 引數未定義,經常出現在程式碼複製-貼上的時候,未修改完全
1 function Player:set_world_pos(x, y) 2 do_something(x,y) 3 if condition_false then 4 log("set_world_pos x:%s, y:%s", x, y) 5 end 6 end 7 function Player:set_pos(cx, cy) 8 do_something(cx, cy) 9 if condition_false then 10 -- 這裡是從上面拷貝,但是傳參沒有修改 11 -- 程式碼不會報錯但行為已經錯了。 12 log("set_pos x:%s , y:%s", x, y) 13 end 14 end
4. 變數拼寫錯誤,很常見但不易察覺。
1 -- 由於拼寫錯誤,這個只有在過載檔案或者熱更檔案出現 2 -- 出現就會導致資料丟失,所謂一個粗心導致的大錯 3 g_player_mng = g_plater_mng or {} 4 -- do_something
5. 判空邏輯問題[1],先使用變數,後進行空值判斷
1 function demo() 2 local var = self:get_value() 3 local count = #var 4 -- 先拿變數進行了操作,然後才判斷空值情況 5 if var then 6 self:do_something() 7 end 8 end
6. 判空邏輯問題[2],判空的覆蓋不全,後面繼續使用了空值變數。
1 function demo() 2 local var = self:get_value() 3 if var then 4 count = #var 5 end 6 local r, b = self:get_data() 7 if b then 8 -- 雖然前面對var判空了 9 -- 但是沒有進行處理,這裡就繼續使用了。 10 table.insert(var, r) 11 end 12 end
7. 類成員方法宣告寫錯,“.”、“:” 經常寫錯。
1 -- 寫類的成員方法的時候漏寫了self 或者 :寫成了. 2 function Player.set_hp(var) 3 if self.mHp > var then 4 self.mHp = var 5 end 6 end
8. 方法呼叫時,“.”、“:” 寫錯。
1 function Player:demo2(param) 2 -- 少傳了引數self 3 self.demo() 4 -- 多傳入了引數self.dos 5 self.dos:demoe(self.dos) 6 end
上面這些錯誤來自平時專案中血與淚的教訓,如果能夠通過靜態程式碼掃描工具提前發現這些錯誤,那對提高程式碼的質量是非常有幫助和有價值的事情。目前市面上可以使用騰訊的TscanCode來對程式碼掃描,支援的語言也挺多的。
我也嘗試花幾篇文章來介紹一下如何編寫一個簡單的Lua程式碼掃描工具,主要介紹大體編寫程式碼的邏輯流程。
文章來自我的公眾號,大家如果有興趣可以關注,具體掃描關注下圖。