最近在學習自定義規則的時候瞭解了一下Clang和一些編譯知識,就寫了一下總結。
Clang和LLVM
不論是OC還是Swift,都是Clang作為編譯器前端,LLVM(Low Level Virtual Machine)作為編譯器後端。
編譯器前端
編譯器前端會對程式碼進行預處理,進行詞法分析(Lexical analysis),語法分析(Parsing),生成中間碼(生成AST)然後根據CodeGen生成LLVM IR。 編譯器後端會把前端輸出的LLVM IR進行優化,然後生成機器碼,最終生成Mach-O。 OCLint就是依賴於AST進行規制檢查的。舉個栗子:
31 int main(int i) {
32 if (i<0) {
33 return -i;
34 }
35 return i;
36 }
複製程式碼
在終端執行clang -Xclang -ast-dump -fsyntax-only $PATH
會log出一大堆東西,我們擷取了最後一段
`-FunctionDecl 0x7f916b5407f8 <PATH.m:31:1, line:36:1> line:31:5 main 'int (int)'
|-ParmVarDecl 0x7f916b540770 <col:10, col:14> col:14 used i 'int'
`-CompoundStmt 0x7f916b540a50 <col:17, line:36:1>
|-IfStmt 0x7f916b5409c8 <line:32:5, line:34:5>
| |-<<<NULL>>>
| |-BinaryOperator 0x7f916b540908 <line:32:9, col:11> 'int' '<'
| | |-ImplicitCastExpr 0x7f916b5408f0 <col:9> 'int' <LValueToRValue>
| | | `-DeclRefExpr 0x7f916b5408a8 <col:9> 'int' lvalue ParmVar 0x7f916b540770 'i' 'int'
| | `-IntegerLiteral 0x7f916b5408d0 <col:11> 'int' 0
| |-CompoundStmt 0x7f916b5409a8 <col:14, line:34:5>
| | `-ReturnStmt 0x7f916b540990 <line:33:9, col:17>
| | `-UnaryOperator 0x7f916b540970 <col:16, col:17> 'int' prefix '-'
| | `-ImplicitCastExpr 0x7f916b540958 <col:17> 'int' <LValueToRValue>
| | `-DeclRefExpr 0x7f916b540930 <col:17> 'int' lvalue ParmVar 0x7f916b540770 'i' 'int'
| `-<<<NULL>>>
`-ReturnStmt 0x7f916b540a38 <line:35:5, col:12>
`-ImplicitCastExpr 0x7f916b540a20 <col:12> 'int' <LValueToRValue>
`-DeclRefExpr 0x7f916b5409f8 <col:12> 'int' lvalue ParmVar 0x7f916b540770 'i' 'int'
複製程式碼
以上這一段就是Clang對於main函式解析成的AST。裡面有兩種型別節點。一種是Stmt(Statement),另外一種是Decl(Declaration)。Stmt很明顯,就是表示式的節點,具有操作性,比如CompoundStmt對應的{},ifstmt對應if,以上所有的Operator、Expr、Literal結尾的都是stmt的子類。Decl就對應的var,method,function。
我們可以通過某個節點往下遍歷所有節點。比如FucntionDecl
往下遍歷可以得到ParamVarDecl
和CompoundStmt
,然後通過CompoundStmt
可以得到IfStmt
和ReturnStmt
.
通過AST我們可以做什麼?
1.我們可以通過這些節點做程式碼掃描。 2.我們可以接管這個過程,然後加入自定義程式碼。因為AST是屬於編譯器前端功能,還沒有最後翻譯成彙編,所以就有改動的餘地。具體可以參考陽神的DynamicCocoa,它就是接管了AST,然後將AST利用自己寫的Clang外掛轉換成JS檔案。