胡營營,趙逢禹
(上海理工大學 光電信息與計算機工程學院,上海 200093)
為了快速開發Web應用系統,開發人員常常復用已有系統的框架或成熟項目中現有的代碼,然后在此基礎上進行完善與修改。這種代碼與框架復用能夠提高開發效率,節省開發時間,但同時也帶來了諸多的副作用。一是代碼冗余增加,即項目中存在冗余的頁面代碼、JavaScript代碼、CSS代碼、處理方法、控制類、支持類等;二是導致源代碼臃腫,可讀性差,增加了維護難度。在某些情況下,Web應用系統中的冗余代碼還會隱藏軟件缺陷與安全漏洞[1-3],最近一項對9 300名開發人員進行的調查結果顯示將冗余代碼檢測和代碼拆分評為最高級別功能請求。
冗余檢測一直受到國內外學者的關注。王偉使用Lex和YACC分別對C語言代碼進行詞法和語法分析,通過語法樹檢測代碼中的冪等冗余、變量冗余和死代碼冗余[4];蘇小紅等人利用TOKEN序列建立復合語句控制結構信息表,設計了基于控制結構的冗余代碼檢測模型,能對C語言中冪等、隱冪等、私有變量、條件冗余、死代碼和冗余傳參進行檢測[5-6];壽能為了揭示冗余與軟件缺陷的關系,在冗余分類的基礎上,研究了冗余特征與軟件缺陷的關聯關系,使用NRefactory設計了冗余檢測算法,實現了一個冗余檢測與缺陷提示的檢測器[7];Wang Xing結合靜態切片和動態切片分析方法提出了一種基于程序切片的死代碼檢測方法,并在LLVM基礎上設計了死代碼檢測框架[8];Leitao通過對C語言代碼構建抽象語法樹,設計了Retargetable Redundancy and Deceit Detector (R2D2),通過分析語法樹中的子節點來檢測冗余代碼[9]。
Niels針對Web中代碼復用帶來瀏覽器解析多余JavaScript死代碼造成Web應用系統的整體性能下降問題,提出了一種基于靜態和動態分析的JavaScript死代碼消除方法Lacuna,在執行時間和分析精度方面取得了可喜的成果[10]。雖然Niels針對JavaScript中的冗余代碼進行了檢測,但Web應用系統中還包括多個層面的代碼如瀏覽器端的表現層代碼HTML和CSS、服務器端處理與控制類代碼、數據庫操作與服務支持代碼等[11],Niels的研究尚無法推廣到對整個Web應用系統的冗余檢測。而實際上,前后臺交互的業務處理類與處理方法是Web應用系統的核心邏輯,對該類代碼進行冗余檢測在提高系統的可讀性、可維護性以及性能上有重要作用,因此文中把研究的重點集中在前后臺交互的業務處理類與處理方法冗余檢測方面。
Web應用系統開發中常用前端開發語言有HTML、Javascript、CSS、C#、Jquery和Bootstrap等;常用后端開發語言為Java、ASP.NET、PHP、Python和Ruby等。盡管一個Web應用系統可能由不同語言甚至多種語言組合開發,但前后臺仍然是通過業務處理類與處理方法進行交互,對該類冗余的檢測仍然適用于所有Web應用系統,因此文中以廣泛使用的HTML、Javascript、CSS和Java語言為例,給出了Web應用系統中基于源代碼分析的冗余代碼檢測(redundant code detection for web application,RCDWA)方法。
在RCDWA方法中,需要獲取Web應用中前后臺交互的業務處理類與處理方法,即表現層和業務邏輯層功能節點業務跳轉的關聯關系,這需要用到抽象語法樹以實現對相關節點的提取和源代碼的解析。
抽象語法樹[12](abstract syntax tree,AST)是源代碼的抽象語法結構的樹狀表現形式。樹上的每個節點都表示源代碼中的一種結構。之所以說語法是“抽象”的,是因為這里的語法并不會表示出真實語法中出現的每個細節,一些語句被隱含在樹的結構中,并沒有以節點的形式呈現。
采用Eclipse JDT中的靜態解析技術將源代碼文件轉化為抽象語法樹,通過操縱抽象語法樹,一方面可以精確地獲得Web應用源代碼中的相關節點,進而實現對代碼的解析和利用;另一方面在語法分析過程中編譯器會對文法進行等價的轉換如消除左遞歸、回溯、二義性等[13],這樣會給文法引入多余的成分,抽象語法樹的結構采用的是上下文無關文法即不依賴于源語言的文法,因此選用抽象語法樹可以避免干擾因素,更好地對處理類與處理方法節點進行提取。
一個Web應用系統是由頁面、后臺處理邏輯、數據庫系統組成的有聯系的代碼集合。Web應用從入口頁面即主頁面開始執行并根據用戶交互情況動態生成不同的顯示層頁面[14]。代碼1和代碼2中的代碼分別為Web應用系統前臺HTML、Javascript、CSS代碼和后臺的Java代碼舉例。代碼1中超鏈接的href會鏈接到頁面next.jsp,Javascript代碼的myfunction函數調用代碼2中的業務處理方法function1,代碼2中的Controller和Redundant為業務處理類。從程序入口開始遍歷所有源代碼文件,遍歷時被調用到的頁面、被調用的處理類與方法就是Web應用中有效的代碼集。遍歷完整個Web應用系統,如果某些頁面、類與方法都沒有被調用,那它們就是冗余頁面、冗余業務處理類與處理方法。在代碼1和代碼2給出的例子中,代碼2中Redundant、function2和function3都沒有被調用,就是冗余類或冗余方法。
代碼1:Web應用系統的入口文件index.jsp。
1
2
5
6
7
8
9
10 function myfunction(){
11 formObject.action="URL";}
12
13
14
代碼2:Web后臺的Java代碼Controller.java。
1 public class Controller{
2 @RequestMapping(value ="/ URL")
3 public ModelAndView function1 (request){
4 …
5 }
6 public ModelAndView function2 (request){
7 …
8 }
9 }
10 class Redundant {
11 public ModelAndView function3 (request){
12 …
13 }
14 }
為了分析Web應用系統中業務處理類與處理方法的冗余問題,需要分析前后臺交互的業務處理類與處理方法。圖1給出了該冗余檢測問題的整體流程框架。

圖1 RCDWA方法流程框架
(1)提取Web應用中的頁面文件名,得到頁面文件名集合PageSet。在應用系統頁面文件目錄下新建一個.bat文件,并且以編輯形式打開并輸入代碼@ECHO OFF回車>tree /F >頁面文件名集合.txt,即可得到PageSet集合。
(2)提取后臺文件中類和方法得到節點集合AllSet={AST1,AST2,…,ASTi…},其中節點ASTi=
(3)搜索Web應用入口頁面中對其他頁面的調用或對后臺類中方法的調用,遞歸構建Web應用調用樹(application call tree,ACT)。算法2給出了構建ACT的詳細過程。
(4)冗余檢測。將集合PageSet、AllSet集合和Web應用調用樹ACT作為輸入進行冗余代碼檢測,輸出冗余頁面、冗余處理類和處理方法。算法3給出了冗余檢測的具體過程。
在RCDWA方法中核心算法有節點構建算法、Web應用調用樹構建算法和冗余檢測算法。節點構建算法利用源代碼的抽象語法樹查找Classi中的方法,得到節點ASTi;調用樹構建算法是為了構造出Web應用系統頁面間、方法間、頁面與方法間的調用關系;冗余檢測算法利用Web應用調用樹得到Web應用中有效的頁面、處理類和處理方法節點集ActivePage和ActiveSet,將有效節點集與Web應用中總的節點集AllSet對比來檢測冗余代碼。
>TYPES(2)
>TypeDeclaration[158+99]
>type binding:cn.usst.market.controller.Controller
>BODY_DECLARATIONS(2)
>MethodDeclaration[186+65]
>method binding:Controller.function1()
>MethodDeclaration[254+30]
>method binding:Controller.function2()
>TypeDeclaration[290+49]
>type binding:cn.usst.market.controller.Redundant
>BODY_DECLARATIONS(1)
>MethodDeclaration[310+26]
>method binding:Redundant.function3()
以上為Controller.java的抽象語法樹,TYPES為抽象語法樹的根節點,TYPES(2)表示該屬性下有兩個TypeDeclaration子節點,TypeDeclaration表示類聲明或接口聲明,在該樹中表示類Controller和Redundant;類聲明的子節點BodyDeclaration表示類主體,即類大括號中的內容;BodyDeclaration的子節點MethodDeclaration表示方法聲明或構造器聲明,在該樹中代表方法function1、function2和function3。將程序源碼解析為抽象語法樹AST,生成的語法樹可以方便地查找類與類中的方法,下面給出類與方法節點ASTi構建算法。
算法1:節點構建算法。
輸入:源碼code;
輸出:AllSet。
處理:
(1)獲取Web應用所有后臺源代碼文件。
(2)利用Eclipse JDT中的靜態解析技術構建源代碼文件類的抽象語法樹Treei。
(3)根據抽象語法樹Treei,查找類中的方法,并形成節點ASTi=
(4)返回節點ASTi,并將節點添加到節點集合AllSet中。
利用節點構建算法處理后臺源代碼文件Controller.java可以得到集合AST1=
為了檢測Web系統中從未調用的頁面、處理類與處理方法,需要建立代碼之間的邏輯調用關系,并基于代碼間的調用關系構建Web應用調用樹ACT。Web應用的主頁面為ACT的根節點,主頁面調用的其他頁面page(頁面文件名)、調用的后臺類中方法Class.Method都是該根節點的子節點。從程序入口頁面開始將節點按調用關系逐步構建Web應用調用樹,以便后文利用Web應用調用樹來識別有效的頁面、處理類與處理方法。
算法2:Web應用調用樹構建算法。
輸入:Web應用入口頁面文件;
輸出:Web應用調用樹ACT。
處理:
(1)將Web應用入口頁面文件名作為樹的根節點Root,并標記該節點為“未訪問”,得到初始Web應用調用樹ACT(每個Web應用系統有唯一入口頁面)。
(2)采用廣度優先算法,從ACT中獲取第一個未訪問的節點Node,如果該節點是一個頁面文件名,轉步驟3,如果該節點是一個方法,轉步驟4,如果該節點為Null,則轉步驟5。
(3)搜索該頁面中標簽屬性為href和action的節點作為Node節點的孩子節點,并將該Node節點標記為“已訪問”,然后轉步驟2。
(4)利用抽象語法樹查找Node節點調用的頁面page(頁面文件名)與調用的方法Class.Method,把這些節點作為該Node節點的孩子節點,并將該Node節點標記為“已訪問”,然后轉步驟2。
(5)返回構建好的ACT,結束算法。
Web應用調用樹是由href鏈接到的page和action邏輯跳轉到的Class.Method 2種節點組成,如圖2所示為前文代碼1和代碼2的Web應用調用樹,根節點為入口文件index.jsp,頁面index.jsp中標簽屬性為href和action的節點分別調用頁面next.jsp和后臺方法function1,依次遞歸直至構建為完整的Web應用調用樹。

圖2 代碼示例的Web應用調用樹
將源程序中所有頁面文件名存入集合PageSet中,到目前為止,已經獲取集合PageSet、集合AllSet和Web應用調用樹ACT,集合PageSet和AllSet中包含Web應用所有的頁面文件、類和方法,ACT中的節點為有效的頁面文件、處理類和處理方法,通過計算可以得出冗余頁面、冗余處理類與處理方法。
算法3:冗余代碼檢測算法。
輸入:PageSet、AllSet、ACT;
輸出:冗余代碼。
處理:
(1)設置有效的頁面集合ActivePage與有效的方法集合ActiveSet為Null。
(2)獲取ACT中未訪問過的節點Node,如果Node為空,則轉步驟4。
(3)如果該節點是一個頁面文件則存入集合ActivePage中,如果該節點是一個方法則存入集合ActiveSet中,然后把Node節點標記為“已訪問”。轉步驟2。
(4)計算冗余頁面集合RedundantPage=PageSet- ActivePage,集合RedundantPage中節點對應的頁面就是冗余頁面。
(5)計算冗余方法集合RedundantSet=AllSet-ActiveSet,集合RedundantSet中節點對應的方法就是冗余方法。
(6)如果一個類中的所有方法都是冗余方法,則該類記為冗余類
(7)輸出代碼冗余結果。
為了評估RCDWA方法的有效性,包括誤檢率和漏檢率,對兩個Web應用系統進行了冗余檢測,并對兩個程序集分別進行不同數量的人工注入冗余實驗。
采用UsstMarket和MovieBoot兩個Web應用進行實驗分析,其中UsstMarket為筆者所在實驗室開發的大型模擬創業網站,網站總共包括6個季度,模擬了現實世界創業中可能遇到的各種流程,目的是給各大高校學生當作一門創業課。MovieBoot為github上的開源項目,是一個集電影、音樂和書籍于一體的JavaWeb應用,github上顯示最近一次代碼更新是1個月前。兩個Web應用都是由HTML、JavaScript、CSS和Java語言開發。表1為兩個應用程序的基本信息。

表1 程序集信息
實驗1:誤檢率檢測。
利用RCDWA方法對UsstMarket和MovieBoot進行冗余檢測,獲取Web應用中的頁面文件名集合,獲取后臺源代碼文件并解析為抽象語法樹得到類與方法節點集,然后構建Web應用調用樹,進行冗余檢測。
實驗過程中兩個程序集前臺頁面文件數、生成的抽象語法樹AST個數、處理類與處理方法個數如表2所示。對檢測出的頁面冗余、類冗余和方法冗余進行了人工審查,發現確實是冗余代碼,其中UsstMarket中的49個方法冗余是因為復用其它功能代碼后需求改變所導致的冗余,2個頁面冗余是開發新頁面時復用其它頁面的代碼導致的。

表2 誤檢率檢測結果
實驗2:漏檢率檢測。
為了統計漏檢率,本文對兩個Web應用進行了人工注入冗余實驗,人工注入冗余時將冗余類和方法盡量分散到了不同文件里,表3列出了人工注入冗余頁面、冗余處理類與處理方法的數量,注入冗余后利用RCDWA方法和文獻[6][8]中的冗余代碼檢測方法對兩個應用進行冗余檢測,檢測結果如表3所示,對RCDWA方法檢測出的冗余進行人工審查,發現檢測出總的冗余是人工注入冗余和實驗1檢測冗余結果之和。

表3 漏檢率檢測結果
通過表2和表3可以看出,RCDWA方法對兩個應用程序冗余檢測的誤檢率為0%,對人工注入冗余的漏檢率為0%。針對實驗2進行了多次試驗,每次注入若干個頁面、處理類與處理方法冗余,漏檢率和誤檢率都為0%。RC-Finder和DCDPS方法對頁面冗余檢測數為0是因為文獻[6]和文獻[8]并未對頁面冗余進行研究;對冗余類和冗余方法漏檢率較高是因為Web應用使用面向對象語言開發,引入了封裝、繼承、多態等特性,文獻[6]中RC-Finder方法不能完全適用于面向對象語言,文獻[8]中DCDPS方法添加了動態分析技術,但由于動態程序切片的效率低導致只能分析較小的程序。從兩個實驗結果來看,文中提出的冗余代碼檢測方法針對Web應用系統中的頁面冗余、處理類與處理方法冗余可以達到較高的檢測效率。
冗余檢測對Web應用系統的缺陷排除、系統維護有重要意義[15]。提出的基于源代碼分析的冗余代碼檢測方法,從應用程序入口開始,根據代碼之間的邏輯調用關系構建Web應用調用樹,進而得到有效頁面、類與方法節點集;然后將有效節點集與Web應用總的節點集根據冗余檢測算法檢測出冗余頁面、冗余業務處理類與處理方法。基于該方法,對UsstMarket和MovieBoot兩個中型Web應用進行了冗余檢測實驗,具有一定的代表性。盡管實驗是針對JavaWeb應用系統進行的,但RCDWA方法完全適用于其他語言開發的Web應用。
文中重點對Web應用系統中基于前后臺交互的業務處理類、處理方法和頁面進行冗余檢測,對于Web應用系統中的其他冗余,如數據庫操作冗余、CSS冗余、Javascript冗余等,并沒有進行深入的研究,因此在后續工作中將進一步針對Web應用中的其他冗余進行研究。