謝春麗,梁 瑤,王 霞
江蘇師范大學 計算機科學與技術學院,江蘇 徐州 221116
GitHub、Stack Overflow等平臺存在著大量的開源項目和源代碼片段,開發(fā)人員如果能有效地將功能相似的代碼引入到自己的項目中,可以大幅度提高軟件開發(fā)效率。那么,如何在海量源碼中快速而準確地找到滿足功能和性能需求的源碼成為軟件工程領域關注的主要任務。除此以外,代碼克隆檢測、故障定位、代碼注釋、代碼摘要生成、命名推薦等問題也是該領域的研究熱點。這幾個任務都有一個相同的關鍵問題,即需要對源代碼進行一定的預處理,從中抽取特征進行源代碼表征。如圖1所示,源代碼表征是解決各類問題的第一步,在此基礎上設計各類算法,從而完成代碼克隆檢測、故障定位、摘要生成或其他任務。源代碼的表征方式決定了對源代碼特征抽取的程度,進而影響后續(xù)任務所能檢測的精度。

圖1 基于深度學習的代碼表征框架Fig.1 Code representation framework based on deep learning
根據對源代碼信息的利用程度,代碼表征方法可以分為基于文本、詞匯、語法、語義四個層面:基于文本的表征方式不經任何修飾直接把源代碼看作文本/字符;基于詞匯的表征方式首先對源代碼詞法分析,然后把源代碼符號序列化;基于語法的表征方式往往從源代碼的語法樹或者抽象語法樹抽取代碼語法信息;基于語義的表征方式則進一步從源代碼的控制流和數據流等信息中提取隱含的語義信息。這幾種不同層次的表征方式各有自己的特征與優(yōu)缺點,基于文本的表征最直接、最簡單,基于詞匯的表征相比基于文本的方式具有一定的抽象能力,基于語法的以及基于語義的表征抽象程度更高,但需要把源代碼轉換為樹型或者圖型結構等額外的預處理技術。根據源代碼表征的技術,可以分為三大類:傳統(tǒng)方法、基于機器學習的方法和基于深度學習的方法。傳統(tǒng)方法手工提取源碼中操作符、操作數、代碼行數、函數、數據類型、常量等特征[1],利用統(tǒng)計學原理表征源碼。由于,源碼和自然語言有諸多相似之處,隨后,Hindle等人提出了代碼的自然性假設,為源代碼表征提供了新的思路,大量的機器學習算法,例如N-Gram,被運用到代碼表征中,但此時機器學習方法仍然需要從源代碼中手工提取代碼特征[2]。近年來,由于深度學習在自然語言中的出色表現,逐漸有學者開始把循環(huán)神經網絡(Recurrent Neural Network,RNN)、卷積神經網絡(Convolutional Neural Networks,CNN)、圖神經網絡(Graph Neural Networks,GNN)等模型引入到源代碼表征中,試圖挖掘隱藏在源代碼中的深層次復雜特征,進一步提高代碼表征的能力[3]。
目前尚無工作單純對源代碼表征方式的研究進展進行梳理和歸納的研究工作。鑒于此,本文擬針對當前源代碼的表征方式的研究進展,進行歸納和總結,討論當前該領域存在的關鍵問題、解決思路與研究發(fā)展趨勢。
為分析源代碼表征技術的研究現狀,本文選用ACM、IEEEXplore、SpingerLink電子文獻數據庫、中國知網數據庫以及百度學術搜索對公開發(fā)布的期刊論文、會議論文進行搜索,檢索的關鍵字包括code representations、code similarity、code clone等,檢索范圍包括主題、關鍵詞、篇名,從中檢索在代碼表征方法中提出新模型、新方法的文獻,檢索時間從2019年開始。同時對中國計算機學會推薦的軟件工程/系統(tǒng)軟件/程序語言領域中的TSE、ASE、TOSEM等國際學術期刊和FSE/ESEC、ICSE、ASE、MSR、SANER等國際學術會議進行檢索,另外,有部分相關文獻可能出現在人工智能領域,所以對人工智能領域的頂級會議AAAI、NIPS、ICLR等也進行了相關檢索。并在閱讀文獻的過程中,把文獻中提供的經典算法的參考文獻也作為研究對象。經過檢索和篩選,去除一些綜述文獻,本文共對54篇文獻進行了研究,圖2顯示了這54篇文獻的期刊或會議出版情況。

圖2 相關論文發(fā)表情況Fig.2 Statistics of papers published
如圖3所示,對源代碼特征的抽取,根據粒度大小,可分為詞匯級、語句級、函數級。詞匯是表達源碼的最小粒度,但是很難表達詞和詞之間的關系,語句級介于詞匯級和函數級之間,能結合上下文表達源碼,函數級的抽象層次比前兩個要高,相對而言能更好地表達語義信息,但是復雜度提高,除此之外,還有文件級、項目級的表達,在此不再描述。

圖3 代碼表征分類Fig.3 Code representation classification
(1)詞匯級:源代碼本身就是詞匯序列,或者經過詞法分析,轉換為標識符、關鍵字、數值、字符串、注釋、特殊符號和運算符等各類標識符(Token)組成的序列。采用Token表達源代碼可以去除空白、制表符、注釋等無關信息,同時還可以消除由變量命名不同所導致的不同語義[4]。著名的CCFinder、CP-Miner等克隆檢測工具都是基于Token級的,可以很好地檢測完全相同的代碼對以及參數化后的代碼對克隆問題[5-6]。Nguyen等人在代碼的Token序列上利用概率統(tǒng)計模型,預測下一個可能的Token,完成代碼詞匯級的補全[7]。Dam等人對代碼的Token構建了端對端的長短期記憶神經網絡(Long-Short Term Memory,LSTM)模型,用于建模軟件及其開發(fā)過程,解決代碼推薦和預測等任務[8]。
(2)語句級:代碼是由各種不同類型的語句構成的,語句是代碼片段的基本構成,代碼的執(zhí)行是按照語句逐條進行的。但是不同的程序語言一行代碼能夠表達的內容是不一致,有可能是一個簡單的運算,也可能是一個算法。因此語句級的劃分上,主要采用抽象語法樹(Abstract Syntax Tree,AST)、數據流圖(Data Flow Graph,DFG)等不依賴具體語言的表達方式。Ben-Nun等人提出了Inst2Vec模型,首先把源代碼片段轉成一種中間語言,并進行預處理,去除了注釋等無關信息,將變量和常量用統(tǒng)一的符號替換,同時提出了一套生成語句上下文的方法,結合上下文關系將代碼語句映射到向量空間,使得具有相同上下文的代碼語句具有相似的含義[9],該方法充分考慮了語句的數據依賴關系和執(zhí)行特性。由于代碼的AST一般規(guī)模都比較大,代碼表征過程中很容易產生梯度消失問題,Zhang等人提出了一種針對語句級的方法,將一個代碼片段的AST分割成多個語句級的子樹,產生語句級向量,和完整的AST相比,語句級嵌入在樹的大小和語法特征抽取有一個較好的折中,有效緩解了梯度消失問題[10]。
(3)函數級:直接對函數進行表征的研究很少,大部分方法都是把函數分解為Token序列,對Token向量進行一定的加權組合來表達函數。Mou等人轉換成函數的AST,對AST中結點,采用自下向上的方法,通過孩子結點的加權組合學習父結點的表達,最頂層根結點的表征則是該函數的表征向量[11]。DeFreez等人提出了Fun2Vec方法,解決代碼的路徑爆炸問題,該方法采用隨機游走算法,隨機選擇部分執(zhí)行路徑,捕獲程序的層級結構,每條執(zhí)行路徑轉換為一個標簽序列,借助Word2Vec方法,把標簽映射為連續(xù)實值向量,并通過神經網絡訓練函數的嵌入向量[12]。
如圖2所示,根據源代碼的抽象層次不同,代碼表征有文本級、結構級、語義級、功能級四個層次。層次越高,抽象程度越高,能夠提取的信息就越多。
(1)文本級:自從自然語言模型出現之后,任意文本都可以轉為詞序列,并映射為詞向量,源代碼的詞匯級表征分為兩種,一種是文本形式的詞匯,即源代碼不經過詞法解析直接分解為詞序列,利用Word2Vec把各個詞轉換成向量,向量的加權平均用來表達文本[13]。另一種常見形式是先對源碼詞法解析,得到一個詞匯序列,也叫Token序列,并過濾掉無用的字符、空格、注釋等。Token序列提高了代碼的抽象表示,該方法通常對具有較小更改的代碼段,例如代碼間格式、間距變化或重命名等的檢測能力更強,更具有魯棒性,廣泛用于源代碼相似性測量,并用于CCFinder、CP-Miner、SourceCC等克隆檢測工具中[5-6,14],這種方法的缺點在于沒有考慮代碼行的順序,忽略了代碼中的結構信息。
(2)語法級:也稱為結構級,如果對代碼只修改了變量名稱、數據類型和注釋信息,改變書寫代碼風格、增加或刪除一些無關緊要的語句,而不改變程序執(zhí)行結構,那么這兩段代碼從結構上是一樣的,則表征的結果也是一樣的?;谡Z法樹的代碼表征是典型的結構級表征,通過語法解析將源程序轉換為解析樹或AST,AST可以避免格式化和詞匯差異問題,而關注兩個程序之間的結構信息。然而,基于樹的表征具有較高的計算復雜度,兩個具有n個結點的AST相似性比較的時間復雜度為O(n3)[15]。Baxter等人把基于樹的代碼表征應用到克隆檢測任務,把源代碼轉成帶注釋的解析樹,然后將子樹分組到桶中,僅在同一個桶中的子樹通過樹匹配算法相互比較[16]。Jiang等人把AST子樹中相關結點出現的次數作為特征值,抓取AST的結構信息,映射為特征向量,采用局部敏感散列(Locality-Sensitive Hashing,LSH)來聚類相似的向量,增加了優(yōu)化機制降低時間復雜度[1]。為了更準確地抽取AST的結構信息,Mou等人構建基于樹型結構的卷積神經網絡模型,用孩子結點的加權組合學習父結點的表達,提高了語法信息的抽取能力[11]。
(3)語義級:代碼不僅具有靜態(tài)結構信息,同時還是可執(zhí)行的,結構相同的代碼,執(zhí)行路徑可能不同,代碼語義也不同。基于圖的方法是主要的語義級表征,通過程序的數據流圖、控制流圖(Control Flow Graph,CFG)、程序依賴圖(Program Dependence Graph,PDG)等方式來提供比簡單語法相似性更精確的信息。例如,程序依賴圖中結點代表表達式和語句,邊表示控制依賴關系和數據依賴關系,代碼相似問題就變成了同構子圖的問題。Komondoor和Horwitz提出了一種基于程序依賴圖的方法,該方法能計算程序兩個版本之間的語法和語義相似,并使用程序切片技術找到同構子圖[17]。其后也有研究者陸續(xù)提出基于圖的檢測方法[18],但子圖同構問題的計算開銷非常大,可擴展性差,因此國內外相關研究較少。隨著圖嵌入技術的出現,語義級表征的問題逐漸成為新的研究熱點。Alon等人提出了Code2Vec模型,把AST中的路徑上下文作為神經網絡模型的嵌入層,源代碼表征為路徑上下文向量表征的加權組合,該模型可以提高函數名推薦的精度[19]。
(4)功能級:有些代碼片段即使語法和語義不同,但是功能是相同的,DeFreez等人提出Func2Vec方法,其基本思想是首先抽取源碼的控制流,把控制流圖中的路徑上的結點加上標簽,這些標簽包含指令類標簽、結構體類標簽、函數標簽、錯誤類標簽,然后采用隨機游走算法從眾多執(zhí)行路徑中選擇部分路徑,即得到一系列標簽序列,這些標簽序列作為訓練的語料庫,用Word2Vec訓練每個標簽的向量表征。采用隨機游走算法解決了路徑爆炸問題,并在路徑生成過程中捕獲了程序的層級結構特征[20]。
通過對選定文獻的表征粒度、表征層次和應用場景進行分析。其中不同表征粒度的分布如圖4(a)所示,從圖中可以看出目前已有文獻主要集中在詞匯粒度的表征,不管是傳統(tǒng)的方法,還是基于統(tǒng)計學原理的語言模型或是深度學習,詞匯是基本的研究對象。如圖4(b)所示,由于選擇的文獻絕大多數是近幾年的方法,所以多數以研究代碼語義為主,這也體現了新的研究趨勢。受自然語言、圖神經網絡等技術的快速發(fā)展的影響,各種深度學習代碼表征模型紛紛出現,這也引起了代碼表征的研究熱潮。圖4(c)表明了代碼表征通用技術已經成為新的研究熱點,目前,普遍的方法是在大規(guī)模無監(jiān)督數據集上進行預訓練,當應用到具體下游任務時,只需在預訓練好的模型上進行微調,即可獲得較好的性能。

圖4 文獻分布圖Fig.4 Distribution of papers
2016年,Hindle在論文中假設代碼和自然語言一樣具有一定的統(tǒng)計規(guī)律[21],緊隨其后,2017年,Vincent等人從詞匯方面分析了源代碼和自然語言的不同特征,指出自然語言的詞匯表非常有限且更新速度較慢,而代碼的詞匯表更新速度太快,新詞匯層出不窮,且是動態(tài)增長的,給數據訓練帶來很大的困難。在實驗中,通過調整傳統(tǒng)N-Gram模型,產生的結果比RNN和LSTM的效果還要好[22]。2018年,Allamanis等人再次提出,程序應該具有自然屬性,軟件語料和自然語言語料具有相似的統(tǒng)計特性,語料越大包含的模式更豐富,并將機器學習中的概率模型引入到代碼表征領域,以學習和研究代碼的特性,此后各種不同的編程語言模型紛紛被提出,并迅速應用到Bug修復、代碼推薦、克隆檢測、注釋自動生成等不同任務上[23-24]。為解決代碼的那些不在詞表中詞的問題(Out of Vocabulary,OOV),2019年,Karampatsis等人提出了一個基于開放詞匯的神經語言模型,該模型不限于固定的標識符詞匯表,采用Subword技術處理未知或罕見詞匯問題,取得了較好的效果[25]。
雖然源代碼具有自然性,但由于源代碼的詞匯可以任意創(chuàng)建,使得代碼語料庫非常復雜,語料庫的構建直接影響神經網絡模型的性能。2019年,Babii等人對如何更好地構建源代碼語料庫進行了探討,從代碼的非英文單詞的處理、字面常量的處理、注釋的處理、空白符/空白行的處理、分詞和大小寫問題的處理以及SubWord分詞的處理六個決策方面全面探討了各種選擇對語料庫和模型性能的影響,列出了源代碼語料庫的重要特征,并在含有10 106個項目的大型語料庫中做了驗證,證明了決策選擇對快速訓練精確的神經語言模型具有決定性的作用[26]。標識符是源代碼最基礎的組成元素,據調查,標識符約占據源代碼總量的70%,標識符包括變量名、函數名、類名、域等,其語義對源碼具有重要的作用。Wainakh等人通過500名開發(fā)者對標識符相關性、相似性和上下文相關性的調查問卷,構建了標識符嵌入的基準,提供了一個標識符的IdBench[27]。隨后,Wang等人構建了COSET作為程序語義嵌入的基準框架,COSET有多種不同類型的源代碼的數據集組成,由專家對程序屬性進行手工標注,并對TreeLSTM,門控圖神經網絡(Gated Graph Neural Network,GGNN),ASTPath神經網絡等模型進行了初步研究,該框架對發(fā)現模型的優(yōu)勢、局限性很有作用[28]。
因此,隨著機器學習研究的最新進展,軟件維護從符號形式方法轉向數據驅動方法。在這種情況下,隱藏在源代碼標識符中的豐富語義為構建代碼的語義表示提供了機會,Efstathiou等人為六種流行的編程語言預訓練了向量空間模型,提供了分布式代碼表示并指出自然語言和源代碼之間的差異[29]。
N-Gram模型是代碼表征最經典的機器學習方法,它是一種基于概率的語言模型(Language Model,LM),根據單詞的順序序列,預測下一個輸出序列的概率,即這些單詞的聯(lián)合概率。N-Gram引入到程序分析領域后,被應用到代碼推薦、代碼維護、代碼遷移、錯位定位以及函數名/類名推薦領域,獲得了很好的效果。Hindle等人構建了N-Gram模型用來預測下一個Token,該模型包含前N-1個詞所能提供的全部信息,必須要相當規(guī)模的訓練文本來確定模型的參數,當N很大時,模型的參數空間過大,容易發(fā)生數據稀疏問題,進而導致數據平滑問題[21]。
Nguyen等人為了提高N-Gram預測的性能,把數據類型、主題等信息加入到N-Gram模型中[30-33],Tu等人增加了緩存機制獲取本地文件中的Tokens[34-35]。Allamanis等人利用N-Gram預測下一個API調用[36-38]。N-Gram模型只適合處理較短的文本,Yang等人把Tokens根據詞匯和變量相關性分組,利用PCC優(yōu)化Token級別的N-Gram模型,使之可以處理語句級的長文本推薦[39]。Nguyen等人提出AUTOSC模型框架結合程序分析和自然語言的概率統(tǒng)計方法進行代碼補全,采用N-Gram模型在大型代碼語料庫上對源代碼的詞法進行訓練,訓練出能產生候選語句的語言模型。然后,使用程序分析從候選集中篩選合乎語法的抽象語句,并將其具體化為有效的候選語句并進行排序。實驗驗證AUTOSC的性能超過現存最好的方法[7]。Karnalim等人針對樹型結構相似性檢測的計算量大的問題,提出一個啟發(fā)式規(guī)則把樹型結構線性化為結點序列,采用N-Gram模型計算代碼相似度值[40]。如表1中所示,N-Gram技術主要應用于代碼補全等生成式模型中。
傳統(tǒng)語言模型雖然容易訓練,但是缺乏上下文泛化能力且平滑技術復雜。隨著深度學習在自然語言和圖像識別領域的成功應用,各種神經語言模型被提出,并應用到軟件工程領域。目前軟件工程領域的神經語言模型主要有循環(huán)神經網絡(RNN)、卷積神經網絡(CNN)、seq2seq模型以及圖神經網絡(GNN)。深度學習的一般過程為通常將代碼及類別標簽作為數據輸入,利用深度學習模型學習代碼中隱藏的深層次特征,把代碼從高維稀疏表示映射為低維空間連續(xù)向量,并利用這種向量表征,完成各類下游任務。一個基本的模型結構如圖5所示,包含輸入層、模型層、輸出層和應用層。輸入層的數據主要有組成代碼的詞匯序列、詞法分析的Token序列、語法分析的抽象語法樹以及上下文環(huán)境的控制流圖、數據流圖、程序依賴圖等。模型層可以是各類深度學習模型,包括循環(huán)神經網絡、卷積神經網絡、深度神經網絡、編碼-解碼器等,輸出層得到代碼的向量表示,并通過連接一些激活函數、相似性計算函數、全連接層完成具體應用,表1中列出了近幾年基于深度學習的代碼表征模型及其特征。

圖5 基于深度學習的代碼表征模型Fig.5 Code representation based on deep learning

表1 基于深度學習的源碼表征模型的現有工作Table 1 Existing studies of code representation framework based on deep learning
一般的神經網絡是從輸入層到隱藏層再到輸出層的結構,層與層之間是全連接的,層內結點是無連接的,在處理序列化數據時,顯得無能為力。循環(huán)神經網絡將層內結點通過時序連接起來,能夠對序列數據之間的依賴關系建模,被應用在機器翻譯、語音識別等領域。RNN具有較好的處理序列數據的能力,其一般結構如圖6所示,網絡接收輸入序列作為輸入,產生固定大小的向量作為序列的表示,其輸出可以作為其他網絡結構的輸入,應用于分類、預測、翻譯等問題。White等人第一個把RNN引入到代碼推薦系統(tǒng),結合循環(huán)神經網絡和遞歸神經網絡,提取代碼的詞匯信息和結構信息,賦予代碼表征以豐富的深層次信息,通過實驗驗證了RNN模型在代碼推薦、代碼克隆檢測任務中有著遠遠超過傳統(tǒng)N-Gram方法的性能[42,49]。Dam等人提出了DeepSoft模型,構建了一個基于LSTM的端到端的軟件分析框架,學習軟件建模中的長期依賴性,在代碼推薦等任務取得了較好的效果[8]。

圖6 RNN模型結構Fig.6 Structure of RNN model
Zhang等人提出了語句級別的向量嵌入,如圖7所示,首先將一個代碼片段的AST分割成多個小粒度的語句樹,編碼器遍歷語句樹,并對所有的語句樹執(zhí)行基于樹的嵌入,產生語句級的向量,把語句級向量序列送入雙向RNN模型,捕捉語句以及語句之間的序列依賴關系,進而生成代碼片段向量,有效捕獲了代碼的語法和語義信息[10]。Ben-Nun等人針對代碼本身具有的一些結構化的特性,提出了Inst2vec模型,該模型首先把源代碼轉換成一種新的中間語言,基于這種中間語言,構建基于上下文流動圖,送入RNN模型訓練出語句級的向量表征,該模型在代碼分類、性能預測等多種任務中得到了很好的效果[9]。

圖7 語句級RNN模型Fig.7 Structure of RNN model on sentence level
RNN模型的主要用途是處理和預測序列數據,因此在代碼翻譯、代碼推薦等必須要考慮上下文相關信息的任務上,表現出較好的性能。但是當上下文序列很長的時候,容易有梯度消失時的問題。
卷積神經網絡是一種特殊的前饋神經網絡結構,為減少網絡中參數個數,用卷積層來代替?zhèn)鹘y(tǒng)的全連接層,提高神經網絡的訓練效率,卷積神經網絡可以提取信息最多的數據特征,生成一個固定大小的向量表示結構。
Mou等人[43]設計了一個樹形結構的卷積層,AST中上層結點的詞向量由它的孩子結點表示。如圖8所示,對于每個非葉結點p以及它的孩子c1,c2,…,cn,具有表達式:

圖8 CNN模型結構Fig.8 Structure of CNN model

其中,vec(p),vec(c1),vec(c2),…,vec(cn)是結點p以及孩子結點的向量表示,w i是結點c i的權重矩陣,b是偏置項。根結點的向量即為整個AST的表示,用來表示函數的特征,該方法在代碼分類和搜索任務中有較好的表現[46]。
CNN模型適用于樹、圖等具有局部空間相關性的數據,主要用于代碼的AST結構上。AST的葉子結點能夠表示Token級別的語義信息,CNN網絡可以提取代碼的語法結構信息,因此,CNN模型能夠挖掘深層次的語法和語義信息,在代碼分類、代碼克隆、函數命名等任務有較好的性能表現,但同樣在深層次模型中存在梯度消失現象。
由于LM模型的限制,Nguyen等人在前期工作的基礎上提出了深度神經網絡模型Dnn4C,如圖9所示,其基本思想是設置三類映射通路,通過深度學習模型將三類信息實體映射到同一個隱空間,這三類信息包括源代碼的詞匯序列,對解析樹的非終端結點抽取并按照一定規(guī)則轉換成的語法符號序列,以及類型轉換序列,源代碼的表征即為三類信息隱藏向量的融合[45]。

圖9 DNN模型結構Fig.9 Structure of DNN model
為解決子圖匹配計算量的問題,Zhao等人[46]提出一個新型的編碼方法,如圖10所示,對代碼中的變量特征、基本塊特征、變量和基本塊之間的關系特征進行編碼,將代碼的控制流和數據流編碼成一個壓縮的語義矩陣,矩陣的每個元素都是一個高維稀疏的二進制向量,代表了該元素的控制流和數據流信息?;谶@個語義矩陣,設計一個DNN模型把代碼相似性問題轉化為一個二元分類的問題,進而學習相似代碼的模式。與傳統(tǒng)的CFG和DFG相比,語義特征矩陣減少了尋找同構子圖以檢測矩陣中相似模式的問題,并且在下游任務很容易使用。Wang等解析程序源代碼中的抽象語法樹,利用深度置信網絡從中抽取語義特征,利用這些特征進行軟件缺陷檢測[50]。

圖10 新型編碼模型Fig.10 New coding model
DNN模型就是多層神經網絡,在一維數據上具有較好的表現,可通過各種組合形式,把不同類型數據融合一起,但實際應用中采用較少的模型。
編碼-解碼模型是一個解決Seq2Seq問題的模型,就是根據一個輸入序列X,來生成另一個輸出序列Y,通過一個編碼和一個解碼過程來重構輸入數據,學習數據的隱層表示?;镜木幋a-解碼模型,結構如圖11所示,所謂編碼,就是將輸入序列轉化成一個固定長度的向量;解碼,就是將之前生成的固定向量再轉化成輸出序列,其中編碼器和解碼器可以是任何一種深度神經網絡模型,例如CNN/RNN/GRU/LSTM等等。Alon等人基于編碼-解碼器提出了Code2Seq模型,從源碼的AST中分別提取葉子結點和非葉子結點,AST的葉結點代表變量名和變量類型名,非葉結點代表程序的結構集,比如循環(huán)、變量聲明、表達式等。編碼器為雙向LSTM,輸入為AST中任意一條路徑上的結點的嵌入向量的全連接,解碼器采用單向LSTM,初始狀態(tài)為各條路徑編碼的加權平均。該模型和基于Token的方法的區(qū)別是Encoder的輸入不是Token句子序列,而是由Encoder為AST中的每條路徑分別創(chuàng)建一個向量表示。Decoder過程處理這些AST路徑編碼[41]。

圖11 Encoder-Decoder模型結構Fig.11 Structure of Encoder-Decoder model
編碼-解碼模型是一個通用的模型,Encoder、Decoder可以是任何類型的數據,針對不同的任務,可以構建CNN、DNN、LSTM等任意深度學習模型。因為是一個端到端的學習算法,在代碼翻譯領域應用較多。
源代碼分析的最大難點在于程序語言是一種高度結構化的語言,現有方法大多只考慮了源代碼的淺層特征,如方法名、Token分詞,但忽略了抽象語法樹中的語義信息和控制流圖的結構化特征。其次,盡管基于深度學習的方法在源代碼的表示上表現良好,但缺乏可解釋性,幾乎不可能理解源代碼的哪些特征對最終結果貢獻更多。為了解決上述兩個問題,Wan等人提出了一種用于語義源代碼檢索的新型多模態(tài)注意力機制網絡,開發(fā)了一種全面的多模態(tài)表示,用于表示源代碼的非結構化和結構化特征,其中LSTM用于表示代碼的分詞,Tree-LSTM用于表示代碼AST,GGNN用于表示代碼的CFG。該模型在代碼檢索領域獲得了最好的性能[47]。
把代碼表達為文本序列或者Token序列,甚至解析為AST,即使和深度學習結合,也更多只是抓住了代碼淺層的文本結構信息。錯失了代碼豐富的語義信息。為了彌補這一問題,Allamanis等人提出圖神經網絡抓取代碼的語法和語義特征,圖神經網絡通過消息傳遞迭代更新所有結點的隱藏狀態(tài),每個結點既接受相鄰結點的信息,又向相鄰結點發(fā)送信息。圖神經網絡結構如圖12所示,首先把源代碼解析為AST,在AST中通過增加數據流和類型層級兩種信息,將程序編碼成圖,圖由語法結點即語法中的非終結符和語法標記即終端結點組成,圖的邊代表Token之間的語法和語義關系,直接將這些語義輸入到機器學習模型中以減少對訓練數據量的要求。結點的初始狀態(tài)為Token的文本表示與它的類型的結合,通過多次迭代更新得到每個結點的最終狀態(tài)[24,51-52]。

圖12 GNN模型結構Fig.12 Structure of GNN model
GNN網絡用于學習非歐式空間數據的信息,GNN既可以學習圖中Token結點信息,也可以學習邊的預測,同時還可以學習CFG、DFG、PDG等圖的整體信息,用來表示一個程序的語義特征。所以GNN使用非常廣泛,可以用于代碼分類、缺陷預測、代碼推薦或者代碼摘要生成等任務。但是GNN不適用于圖中結點不斷變化的動態(tài)圖,而且還有很多缺陷需要進一步研究,例如圖的不動點計算問題、邊信息傳播問題,都需要新的方法進一步來解決。
隨著神經網絡、語言模型的不斷發(fā)展,深度學習在軟件系統(tǒng)中的應用越來越受到廣泛關注,基于深度學習的代碼分析研究已經成為軟件工程領域的新的研究熱點[53]。不過,從前面的分析,可以看出深度學習在代碼分析領域的應用才剛剛起步,未來還需要在以下幾方面進一步努力。以下總結了三個可能的研究方向:
(1)現有語言模型的改進
自從驗證了自然語言的模型對代碼分析具有很好的效果,各種編程語言模型紛紛出現,并廣泛應用到代碼生成、代碼克隆檢測、Bug檢測等各個領域。這些方法把自然語言深度學習模型直接應用到源碼中,學習代碼中詞、Token、語法樹中各類結點的向量表示。同時,Allamanis等人指出語言模型依賴代碼的文本自然特征,但是程序代碼和自然語言只有小部分詞匯是重復的,即使相同的詞,在自然語言和程序代碼中的含義也不相同,而且程序員可以隨意給變量命名,造成代碼的詞匯是開放性的,因此,不能直接把語言模型應用到代碼上[23],必須挖掘程序代碼的其他特征。
(2)多模態(tài)深度學習框架
從前面的分析可以看出,借助自然語言模型對代碼詞匯、Token、AST、CFG/DFG等進行向量表征,雖然該方向取得了一定研究進展,但目前仍存在以下問題:首先,現有方法能夠解決簡單相似以及結構相似問題,但對功能相似的研究還比較少,即使有些用同構子圖的方法來研究,但鑒于代價太高,不能廣泛應用。代碼不僅具有文本自然性,同時具有語法結構信息,而且代碼是可執(zhí)行的,具有動態(tài)語義特征,和詞匯、語法和語義都有相關性,因此結合深度學習技術,采用多模態(tài)代碼表征方式,構建新的學習框架,從詞匯、語法、執(zhí)行路徑等多個方面學習代碼的語義信息,直接學習隱藏在源代碼中更加復雜的語義和語法特征,將會成為程序分析領域的一個重點研究方向。
(3)通用的深度學習框架
面向特定應用領域的深度學習表征模型獲得了較好的性能,純粹的、通用的代碼表征是研究者致力解決的新問題。類似于自然語言的Word2Vec,Alon等人提出了Code2Vec方法,利用神經網絡訓練代碼的語義向量[19],緊接著Kang等人把Code2Vec方法應用到了注釋生成、作者識別、代碼克隆三種不同類型的任務,評價了Code2Vec對各項任務的通用性,實驗結果表明非特定任務的訓練,其效果并不比一般方法好[54]。
目前常用的表征粒度中,標識符可以表達領域語義和開發(fā)者的理論基礎,在特征定位和軟件模塊化有作用。抽象語法樹可以捕獲程序的語法結構和模式。CFG和DFG可以表達程序的部分語義信息,這些不同的表征,其抽象程度不同,適合不同的具體任務,Tufano等人提出集成學習方法,把不同粒度的表征向量通過加權平均組合為代碼的抽象表征[48]。Feng等人基于Transformer的神經網絡構建了CodeBERT模型,在大規(guī)模數據集上預訓練,致力學習源碼的通用表示方法,支持下游自然語言代碼搜索、代碼文檔生成等應用[55]。但總體上來說,通用的代碼表征框架和模型還不夠成熟,未來還有待進一步更廣泛的研究。
隨著開源代碼的日益增多,將深度學習技術融入代碼表征,開展更深層次的代碼表征的研究,已經成為軟件工程領域解決各類問題的新方法。通過從海量數據中學習代碼的語法、語義特征,用于解決各類下游任務。與傳統(tǒng)的基于機器學習的方法相比,基于深度學習的表征模型能夠自動學習代碼的結構和抽象語義的隱藏特征,更有效地提高代碼表征的準確性。本文主要介紹和分析了基于深度學習的代碼表征的研究現狀和進展,并根據現有工作的局限性討論了今后可能的發(fā)展方向和趨勢。