彭鑫,陳馳,林云
1. 復旦大學計算機科學技術學院,上海 200438;2. 上海市數(shù)據(jù)科學重點實驗室,上海 200438;3. 新加坡國立大學計算機學院,新加坡 117418
各種形式的代碼復用一直是軟件開發(fā)人員廣泛使用的一種高效的輔助開發(fā)手段,復用的對象包括相似功能模塊、代碼片段以及應用編程接口(application programming interface,API)等不同粒度的代碼單元。在傳統(tǒng)的代碼復用方式中,開發(fā)人員需要利用互聯(lián)網(wǎng)搜索引擎或企業(yè)代碼庫搜索等手段獲取特定領域的或與領域無關的可復用代碼單元,同時查找樣例代碼和文本解釋等幫助信息,在此基礎上選擇代碼單元,并完成修改和集成。這種代碼復用方式雖然有效,但依賴開發(fā)人員的經(jīng)驗,同時需要進行大量的信息查找和確認,因此復用效率不高,而且容易在代碼中引入缺陷。
開源和企業(yè)代碼庫以及軟件技術文檔、軟件開發(fā)問答等軟件開發(fā)資源的大量積累形成了代碼大數(shù)據(jù)。例如,代碼托管平臺GitHub上已經(jīng)聚集了超過2.7億個代碼倉庫,軟件開發(fā)問答網(wǎng)站Stack Overflow上已經(jīng)積累了超過1 700萬個軟件開發(fā)問題。軟件開發(fā)過程中很多時候遇到的是重復性的開發(fā)問題,如通用功能實現(xiàn)、通用API及其使用模式,因此通過代碼大數(shù)據(jù)分析、挖掘和學習可以實現(xiàn)智能化的代碼復用推薦。對于軟件開發(fā)人員而言,最有效的方式是基于代碼上下文的智能化代碼復用推薦,即根據(jù)集成化開發(fā)環(huán)境(integrated development environment,IDE)中當前開發(fā)任務已經(jīng)完成的代碼等上下文信息,針對性地推薦可用的代碼單元,同時輔助開發(fā)人員進行定制化的代碼修改和集成。
針對基于上下文的智能化代碼復用推薦,研究人員使用數(shù)據(jù)挖掘、統(tǒng)計模型、深度學習等各種技術手段開展了一系列研究和探索,包括基于上下文匹配的API使用模式推薦[1]、基于上下文概率模型的代碼補全[2]、基于上下文圖統(tǒng)計語言模型的API推薦[3]等。這些方法推薦的可復用代碼單元覆蓋了API使用模式、代碼片段、單個API和語句行等不同粒度,其有效性已經(jīng)在一定范圍內(nèi)得到了驗證。然而,現(xiàn)有的智能化代碼復用推薦還無法提供一種具有廣泛適用性以及能夠按需調(diào)整推薦粒度及內(nèi)容的系統(tǒng)性的智能輔助開發(fā)支持。
一般而言,軟件開發(fā)人員的代碼復用對象包括特定領域的共性代碼單元以及與領域無關的通用代碼單元。前者的復用范圍局限在特定領域內(nèi),但與核心業(yè)務關系更密切,例如以代碼片段或功能模塊的形式出現(xiàn)的相似業(yè)務功能的代碼實現(xiàn)變體。后者的復用范圍更廣,但與核心業(yè)務關系較弱,如通用API及其使用模式、通用算法與功能實現(xiàn)等。不同類型的代碼復用對象需要采用不同的智能化方法進行分析和推薦。本文圍繞基于上下文的智能化代碼復用推薦這一主題,從基于模板挖掘的代碼復用推薦和基于深度學習的代碼復用推薦兩個方面介紹筆者的研究工作。其中,基于模板挖掘的代碼復用推薦支持代碼片段和功能模塊兩個層次的復用推薦,同時所需的相似代碼副本數(shù)量較少,適用于面向特定領域的共性代碼復用推薦?;谏疃葘W習的代碼復用推薦支持單行及多行API調(diào)用代碼的推薦,所需的訓練數(shù)據(jù)較多,適用于與領域無關的通用代碼復用推薦。在此基礎上,本文還將針對基于上下文的智能化代碼復用推薦的未來發(fā)展方向進行展望。
軟件開發(fā)人員經(jīng)常會實現(xiàn)相似或相同的功能,相應的實現(xiàn)代碼也是相似的。這種代碼片段級別的復用行為在開源和企業(yè)軟件開發(fā)中十分普遍。在這種代碼復用中,開發(fā)人員經(jīng)常需要對所復用的代碼進行定制化修改。此時,開發(fā)人員可能因為對代碼本身以及不同部分之間的邏輯關聯(lián)不夠了解而造成遺漏修改或錯誤修改,進而導致缺陷的產(chǎn)生。此外,在功能模塊級別上,開發(fā)人員可能會通過代碼復制粘貼實現(xiàn)更大粒度的復用,其中隱含著對設計結(jié)構(gòu)的復用。通過代碼片段和功能模塊兩個層次的模板抽取,可以實現(xiàn)相應的代碼復用推薦。
針對代碼片段級別的復用,可以通過代碼克隆檢測發(fā)現(xiàn)當前所復用代碼片段在項目庫中的所有相似副本。這些相似副本被稱為克隆實例,它們構(gòu)成的集合被稱為克隆類。該方法的基本思想在于,項目庫中的每一個相似副本(克隆實例)都被視為一次基于代碼復制的復用的結(jié)果,而這些克隆實例之間的差異則被視為代碼復用過程中所做的修改。因此,開發(fā)人員通過代碼復制復用一個代碼片段時,可以找出所有與之相似的克隆實例,分析它們之間的差異,并從中抽取代碼模板以及其中蘊含的關聯(lián)關系用于復用推薦。
該方法的基本過程如圖1所示[4]。在針對當前所復制代碼片段的克隆檢測結(jié)果的基礎上,利用多段代碼之間的差異比較技術來檢測這些克隆實例之間的差異[5],并將每一個差異轉(zhuǎn)化為一個代碼可變點。在此基礎上,該方法通過基于歷史代碼信息(曾在可變點處出現(xiàn)過的代碼)和上下文代碼的分析,挖掘每個可變點上的內(nèi)容選項以及相互之間的關聯(lián)關系,并在開發(fā)人員的代碼編輯過程中以交互式推薦的方式輔助實現(xiàn)代碼復用。當開發(fā)人員在可變點中選擇了某些代碼內(nèi)容選項或輸入新的代碼內(nèi)容時,該方法可以基于挖掘的關聯(lián)規(guī)則動態(tài)調(diào)整其他相關可變點上的代碼內(nèi)容選項及其推薦排序。
基于該方法實現(xiàn)的Eclipse插件CCDeamon(code cloning deamon)如圖2所示。其中,代碼內(nèi)容上的方框表示所復制的代碼上經(jīng)過模板抽取后識別出的可變點,點擊后將顯示經(jīng)過排序的推薦選項列表。
圖1 代碼片段模板抽取與復用推薦方法[4]
圖2 CCDeamon工具插件[4]
針對功能模塊級別的復用,可以從相似副本中抽取出相似功能的設計和實現(xiàn)模板,并支持開發(fā)人員基于模板進行定制化的功能實現(xiàn)。該方法將抽取的抽象設計以類圖形式保存為模板,在此基礎上,支持開發(fā)人員基于模板自動生成骨架代碼以及骨架內(nèi)的部分實現(xiàn)代碼[6]。
該方法的基本過程如圖3所示,采用自底向上的聚合和抽象技術來完成代碼模板的抽取[6]。該方法首先從項目源代碼中逆向恢復出整個項目的靜態(tài)設計模型,其中包含整個項目中的類、接口、方法、屬性以及它們之間的關系。然后,該方法定義了模型內(nèi)程序元素(即類、接口、方法和屬性)的對應匹配方式,被匹配的程序元素被聚合成一個多重集。接著,該方法根據(jù)多重集間元素的關系(如繼承和調(diào)用關系等)將多個多重集進行拼接,并進一步對每個多重集內(nèi)部的程序元素進行抽象,形成模板程序元素;最后,該方法根據(jù)生成的模板支持開發(fā)人員通過定制生成新的應用程序骨架代碼以及骨架內(nèi)部代碼。
圖3 功能模塊實現(xiàn)模板抽取與復用推薦方法[6]
該技術已經(jīng)被實現(xiàn),稱為Eclipse插件MICoDe(mining implicit code design),如圖4所示。圖4右側(cè)的模板視圖列出了模板庫中所有抽取出的可復用模板,中間的模板編輯器支持開發(fā)人員對模板進行二次編輯。UML類圖可視化了一個模板中的類、接口、方法、屬性以及它們之間的關系(如繼承和調(diào)用關系等)。同時,工具底部的代碼差異視圖支持開發(fā)人員查看模板中某個元素(如方法)在項目中的多個實現(xiàn)示例,并高亮顯示出這些示例之間的差異。
圖4 MICoDe工具示意圖[6]
由于深度學習在自然語言處理領域表現(xiàn)出的優(yōu)越性,其被遷移到代碼復用推薦領域。相對于模板挖掘這種顯式的方式,深度學習通過隱式的方式學習代碼的使用模式?;谏疃葘W習的代碼復用推薦方法分為訓練階段和預測階段。在訓練階段,基于深度學習的代碼復用推薦方法通常將代碼解析為某種代碼表示形式(如代碼序列表示、代碼樹結(jié)構(gòu)表示以及代碼圖結(jié)構(gòu)表示等),設計或應用符合該代碼表示形式的深度學習網(wǎng)絡(如長短期記憶(long short-term memory,LSTM)網(wǎng)絡、Tree-LSTM網(wǎng)絡以及門控圖神經(jīng)網(wǎng)絡(gated graph sequence neural network,GG-NN)等)進行學習和訓練。在預測階段,基于深度學習的代碼復用推薦方法應用訓練好的模型對給定的帶有窟窿(即待完成的部分)的不完整代碼進行預測。
基于傳統(tǒng)統(tǒng)計學習的API推薦方法[2,7-11]以及基于深度學習的API推薦方法[11-15]將代碼解析為代碼序列,并利用傳統(tǒng)統(tǒng)計模型(如N-Gram模型等)或深度學習模型(如LSTM網(wǎng)絡等)進行學習和訓練。然而,這些研究方法沒有考慮代碼的結(jié)構(gòu)信息(如控制流和數(shù)據(jù)流),不能有效地捕獲相隔較遠的代碼之間的關系,并且沒有實例化API中的參數(shù)。
基于Tree-LSTM網(wǎng)絡的生成式API推薦方法DeepAPIRec如圖5所示[16]。在訓練階段,DeepAPIRec訓練了一個語句模型用于預測抽象API語句,并構(gòu)造了一個參數(shù)模型用于實例化抽象API語句中的參數(shù),從而形成實例化API語句。在預測階段,對于給定的帶有窟窿的程序,DeepAPIRec預測出實例化API語句供開發(fā)人員選擇。
圖5 基于Tree-LSTM網(wǎng)絡的生成式API推薦方法DeepAPIRec[16]
在語句模型訓練階段,DeepAPIRec將代碼解析為包含代碼控制流信息的代碼樹。代碼樹中的每一個結(jié)點表示抽象的API語句、變量聲明/賦值語句、控制結(jié)構(gòu)或代碼窟窿,邊表示它們之間的控制流關系。DeepAPIRec結(jié)合Tai K S等人[17]提出的Child-Sum Tree-LSTM網(wǎng)絡和N-ary Tree-LSTM網(wǎng)絡,作為深度學習網(wǎng)絡對代碼樹進行學習和訓練,從而有效地捕獲相隔較遠的代碼之間的關系以及代碼結(jié)構(gòu)信息的語義。
在參數(shù)模型構(gòu)建階段,DeepAPIRec在代碼樹上加入了數(shù)據(jù)依賴分析,從而引入數(shù)據(jù)流邊,構(gòu)成加入數(shù)據(jù)依賴關系的代碼樹。DeepAPIRec通過統(tǒng)計存在數(shù)據(jù)依賴關系的代碼樹中兩個結(jié)點之間在相應路徑上的數(shù)據(jù)依賴次數(shù)來構(gòu)造參數(shù)模型。給定一個待預測的API結(jié)點及對應的加入數(shù)據(jù)依賴關系的代碼樹,參數(shù)模型計算每個結(jié)點在所有可以到達待預測API結(jié)點且長度大于2的路徑上與待預測API結(jié)點產(chǎn)生數(shù)據(jù)依賴的次數(shù),次數(shù)越高表示數(shù)據(jù)依賴概率越大,即該結(jié)點表示的變量越有可能成為預測的API中的參數(shù)。
在代碼推薦階段,給定一段帶有窟窿的程序(如圖6所示),DeepAPIRec首先將其解析為代碼樹(如圖7所示),并輸入語句模型進行預測,得到抽象API語句;其次,DeepAPIRec將其解析為加入數(shù)據(jù)流的代碼樹(如圖8所示);最后,用抽象API語句結(jié)點替換hole結(jié)點,并通過參數(shù)模型進行參數(shù)實例化,以形成實例化的API語句供開發(fā)人員選擇。對于圖6的代碼示例,DeepAPIRec可以成功地推薦正確的API語句Signature signature = Signature.getInstance(signMode)。
代碼包含代碼結(jié)構(gòu)信息和代碼文本信息兩種核心信息。代碼結(jié)構(gòu)信息(如控制流和數(shù)據(jù)流)反映代碼的程序邏輯特性,代碼文本信息(如方法名、變量名等)反映代碼在自然語言中的語義?,F(xiàn)有的API推薦方法[2,7-15,18]要么將代碼按照文本處理的方式處理為代碼序列,要么只考慮代碼的結(jié)構(gòu)信息,并將代碼解析為樹或者圖表示。這些API推薦方法僅獨立建模代碼結(jié)構(gòu)信息或代碼文本信息,沒有將代碼結(jié)構(gòu)信息和文本信息進行結(jié)合。此外,在考慮代碼結(jié)構(gòu)信息時應考慮代碼的全局語義(即將代碼看作一個完整的圖表示),而非像GraLan[3]那樣只考慮代碼的局部語義(即將代碼處理為子圖進行推薦)。如圖9所示,GraLan只考慮局部語義,因此GraLan無法成功地預測出正確的API。如果考慮全局語義,那么可以得知hole處的語義為“對一個String類型的變量進行某種處理,以獲取一個int類型的值”。然而,只考慮代碼結(jié)構(gòu)信息無法得到hole處的準確語義,這是因為無法確定需要對String類型的變量進行何種處理。如圖10所示,其代碼結(jié)構(gòu)信息與圖9中的代碼非常相似,但是hole處的語義與圖9中的代碼不一樣。如果結(jié)合代碼文本信息進行考慮,就可以得知圖9中hole處的語義為“計算字符串的Hash值”,而圖10中hole處的語義為“將字符串直接轉(zhuǎn)為整數(shù)”。因此筆者團隊提出一種名為APIRec-CST的基于深度學習及代碼上下文結(jié)構(gòu)和文本信息的API推薦方法[19]。
圖6 利用Signature簽名加密的代碼示例
圖7 代碼樹示例
圖8 加入數(shù)據(jù)流的代碼樹示例
APIRec-CST將代碼解析為API上下文圖,以反映代碼中的結(jié)構(gòu)信息。API上下文圖中的每個結(jié)點表示抽象API方法調(diào)用、成員變量訪問、變量聲明/賦值、控制結(jié)構(gòu)或代碼窟窿,邊表示它們之間的控制流或數(shù)據(jù)流關系。APIRec-CST將代碼文本信息(如方法名、參數(shù)名和變量名)提取為代碼Token詞袋,并通過分詞、詞形還原、去重等方式處理得到的代碼Token詞袋。對于得到的API上下文圖及代碼Token詞袋,APIRec-CST通過圖11所示的網(wǎng)絡進行聯(lián)合學習和訓練。其中,APIRec-CST通過API上下文圖網(wǎng)絡學習API上下文圖的全局語義,通過代碼Token網(wǎng)絡學習代碼文本信息語義,并通過聯(lián)合層結(jié)合代碼結(jié)構(gòu)信息和文本信息。
對于一段給定的帶有窟窿的程序(如圖9、圖10所示),APIRec-CST將其解析為API上下文圖(如圖12所示)及代碼Token詞袋(compute、Hash、code、path、result、rd、br以及str),并分別輸入圖11所示的網(wǎng)絡中。最后,APIRec-CST能夠預測出正確的API推薦java.lang.String.HashCode()。
圖9 計算文件內(nèi)容哈希值的代碼示例
圖10 從文件獲取得分的代碼示例
圖11 APIRec-CST的網(wǎng)絡模型結(jié)構(gòu)[19]
各種智能化技術特別是深度學習技術的應用使得基于上下文的智能化代碼復用推薦成為可能。然而,由于軟件開發(fā)固有的復雜性和不確定性,在企業(yè)軟件開發(fā)中全面實現(xiàn)智能化代碼復用推薦還存在一系列技術挑戰(zhàn),主要包括:軟件需求和設計中蘊含的創(chuàng)造性和不確定性、各種軟件項目在業(yè)務和技術領域的多樣性和差異性、代碼及其他軟件開發(fā)資源的數(shù)據(jù)質(zhì)量問題[20]。
基于上下文的智能化代碼復用推薦是一種輔助開發(fā)手段,需要與開發(fā)人員的主觀經(jīng)驗、思考和判斷能力相結(jié)合。另外,理想的智能化代碼復用推薦應當提供一種集成化、系統(tǒng)化的智能開發(fā)輔助,實現(xiàn)通過單一的入口和渠道滿足開發(fā)人員對不同粒度(如功能模塊、代碼片段、單個API或單行代碼等)以及不同形態(tài)(參考代碼、原理解釋、解決方案描述等)的可復用資源和信息的需要。因此,未來有效的智能化代碼復用推薦應當以一種人機協(xié)作的智能化助手的形式來實現(xiàn)。這種智能化助手隱藏在IDE之中,持續(xù)觀察開發(fā)人員的行為以及代碼上下文的變化,及時發(fā)現(xiàn)問題,并在需要的時候提供所需的各種幫助,例如推薦API或代碼片段、提供代碼說明并指導代碼修改、給出解決方案建議、回答開發(fā)人員問題等。智能化助手的工作方式應當類似于結(jié)對編程,能夠與軟件開發(fā)人員持續(xù)交互和協(xié)作。為此,這種智能化助手應當具有以下多個方面的能力[20]。
● 交互式澄清與解釋。以交互式的方式澄清開發(fā)人員的意圖,同時對給出的推薦提供原理解釋和說明。
● 逐步細化解決方案建議。按照開發(fā)人員的思維方式,由粗到細逐步推薦和細化代碼復用及問題解決方案,而不是一開始就陷入細節(jié)。
● 獲取和利用技術和業(yè)務背景知識。具備編程語言、API、領域模型等方面的技術和業(yè)務背景知識,以此來支持與開發(fā)人員的高效交互和復用推薦。
● 按需提供不同粒度和不同形式的解決方案。根據(jù)開發(fā)人員的意圖和代碼上下文,按需提供文本方案描述、問題解答、功能模塊、代碼片段、API模式、單個API、代碼行及參數(shù)等不同粒度和形式的解決方案。
以代碼為核心的軟件開發(fā)大數(shù)據(jù)的積累以及各種智能化分析技術的發(fā)展,使得智能化的代碼復用推薦成為可能。通過代碼模板挖掘以及代碼深度學習等不同的智能化分析技術,可以實現(xiàn)面向特定領域以及與領域無關的智能化代碼復用推薦。然而,現(xiàn)有的方法和工具大部分只對與領域無關的通用代碼單元較為有效,如通用API使用模式、常用算法實現(xiàn)等。支持特定領域代碼復用的代碼模板挖掘等方法的適應性和可組合性都十分有限。上述問題導致現(xiàn)有的方法和工具僅能在簡單的編碼任務上提供一些有限的智能化支持,無法提供全面、系統(tǒng)化的智能化軟件開發(fā)支持。
未來有效的智能化代碼復用推薦應當以一種人機協(xié)作的智能化助手的形式來實現(xiàn),通過持續(xù)觀察開發(fā)人員的行為以及代碼上下文的變化,及時發(fā)現(xiàn)問題,并在需要的時候提供各種幫助。這種智能化開發(fā)助手需要與軟件開發(fā)人員持續(xù)交互和協(xié)作,并具備交互式澄清與解釋、逐步細化解決方案建議、獲取和利用技術和業(yè)務背景知識、按需提供不同粒度和不同形式的解決方案等方面的能力。
圖12 API上下文圖示例