12 Baker l Baxter l C Baker 1995
既存のコードクローン検出手法 (1/2) Bakerの手法 l 行単位でソースコードを比較してコードクローンを検 出する Baxterらの手法 l C言語のソースファイルを入力,構文解析して,解析 木(の部分木)を比較する [Baker 1995] B. S. Baker: “On finding Duplication and Near-Duplication in Large Software System, ” Proc. Second IEEE Working Conf. Reverse Eng. , pp. 8695, Tronto, Canada (Jul. , 1995). [Baxter 1998] I. D. Baxter, A. Yahin, L. Moura, M. Sant’Anna, and L. Bier: “Clone Detection Using Abstract Syntax Trees, ” Proc. of ICSM ’ 98, pp. 368 -377, Bethesda, Maryland (Nov. , 1998). 2002/11/15 6
既存のコードクローン検出手法 (2/2) l Merloらの手法 l 手続き(メソッドや関数)の特徴メトリクスを比較して, 計測値が似ていればコードクローンであると判定する [Balazinska 1999] M. Balazinska, E. Merlo, M. Dagenais, B. Lague, and K. A. Kontogiannis, "Measuring Clone Based Reengineering Opportunities", Proc. 6 th IEEE Int'l Symposium on Software Metrics (METRICS '99), pp. 292 -303, Boca Raton, Florida, Nov. 1999. 2002/11/15 7
例題ソースコード 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 2002/11/15 static void foo() throws RESyntax. Exception { String a[] = new String [] { "123, 400", "abc", "orange 100" }; org. apache. regexp. RE pat = new org. apache. regexp. RE("[0 -9, ]+"); int sum = 0; for (int i = 0; i < a. length; ++i) if (pat. match(a[i])) sum += Sample. parse. Number(pat. get. Paren(0)); System. out. println("sum = " + sum); } static void goo(String [] a) throws RESyntax. Exception { RE exp = new RE("[0 -9, ]+"); int sum = 0; for (int i = 0; i < a. length; ++i) if (exp. match(a[i])) sum += parse. Number(exp. get. Paren(0)); System. out. println("sum = " + sum); } 10
ステップ(5):コードクローンの位置を出力する 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 2002/11/15 static void foo() throws RESyntax. Exception { String a[] = new String [] { "123, 400", "abc", "orange 100" }; org. apache. regexp. RE pat = new org. apache. regexp. RE("[0 -9, ]+"); int sum = 0; for (int i = 0; i < a. length; ++i) if (pat. match(a[i])) sum += Sample. parse. Number(pat. get. Paren(0)); System. out. println("sum = " + sum); } static void goo(String [] a) throws RESyntax. Exception { RE exp = new RE("[0 -9, ]+"); int sum = 0; for (int i = 0; i < a. length; ++i) if (exp. match(a[i])) sum += parse. Number(exp. get. Paren(0)); System. out. println("sum = " + sum); } 16
変形ルールを用いない場合 4 -6行目と 13 -15行目, 8 -10行目と 17 -19行目 2002/11/15 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. static void foo() throws RESyntax. Exception { String a[] = new String [] { "123, 400", "abc", "orange 100" }; org. apache. regexp. RE pat = new org. apache. regexp. RE("[0 -9, ]+"); int sum = 0; for (int i = 0; i < a. length; ++i) { if (pat. match(a[i])) sum += Sample. parse. Number(pat. get. Paren(0)); } System. out. println("sum = " + sum); } static void goo(String [] a) throws RESyntax. Exception { RE exp = new RE("[0 -9, ]+"); int sum = 0; for (int i = 0; i < a. length; ++i) { if (exp. match(a[i])) sum += parse. Number(exp. get. Paren(0)); } System. out. println("sum = " + sum); } 17
Java向けの変形ルール RJ 1:パッケージ名除去 ( Package. Name ‘. ’ )+ Class. Name → Class. Name RJ 2: Callee挿入 NDot. Or. New NClass. Name ‘(‘ → NDot. Or. New Callee. Identifier ‘. ’ NClass. Name ‘(‘ RJ 3: テーブル初期化除去 '=' '{' Initalization. List, '}' → '=' '{' Unique. Identifier '}‘ ']' '{' Initalization. List, '}' → ']' '{' Unique. Identifier '}’ RJ 4: モジュールの分離 トップレベルの定義や宣言の終わりにUnique. Identifier を挿入する RJ 5: 可視性キーワードの除去 protected, public,synchronizedなどのキーワードを取り除く RJ 6: コンパウンド・ブロックの処理 if (…), else, while (…) の直後が単文なら{}を補う 2002/11/15 18
コードクローンが密集している部分 (A) l src/javax/swing/plaf/multi/*. java(29個) l l クラス名を除いてまったく同じクラスの定義 コード生成ツールによって生成された 31| */ 32| public class Multi. Button. UI extends Button. UI { 33| 160| 161| 162| 163| 164| 165| 2002/11/15 public static Component. UI create. UI(JComponent a) { Component. UI mui = new Multi. Button. UI(); return Multi. Look. And. Feel. create. UIs(mui, ((Multi. Button. UI) mui). uis, a); } 21
適用例#2 Free. BSD, Linux, Net. BSDの比較 l 3つのOSの比較 l l l Free. BSD 4. 0 (C 220万行) Linux 2. 4. 0 (C 240万行) Net. BSD 1. 5 (C 260万行) l Free. BSDとNet. BSDは同じソースコードから, Linuxは異なるソースコード l 実行には 108分を要した 2002/11/15 23
Free. BSD, Linux, Net. BSDのコードクローン散布図 2002/11/15 24
大学の 演習(2) P 1 P 2 P 3 P 4 P 5 2002/11/15 27
Gappedクローンの例 543| else { 544| t = NULL_STRING; 545| spaces. push_back(t); 546| errors. push_back(pos); 547| append_recover_to_last_space(); 548| state = MS_INIT; 549| } 550| 551| bool b. First. Trial = true; 552| bool b. Got. Avail. Token = false; 553| while (! b. Got. Avail. Token && pos < text. size()) { 554| if (get_word(&t, Special. Prev. Char)) { l 340| else { 341| t = ""; 342| //pool_thru(&pool, &t); 343| spaces. push_back(t); 344| errors. push_back(pos); 345| append_recover_to_last_space(); 346| } 347| 348| bool b. First. Trial = true; 349| bool b. Got. Avail. Token = false; 350| while (! b. Got. Avail. Token && pos < text. size()) { 351| if (get_word(&t)) { Geminiで検出されたGappedクローン l 2002/11/15 赤い文字の部分がギャップ 32
メソッドの抽出 Void method. A(int i){ method. Z(); System. out. println(“name: ” + name); System. out. println(“amount: ” + i); } Void method. B(int i){ method. Y(); System. out. println(“name: ” + name); System. out. println(“amount: ” + i); } 2002/11/15 void method. A(int i){ method. Z(); method. C(i); } void method. B(int i){ method. Y(); method. C(i); } Void method. C(int i){ System. out. println(“name: ” + name); System. out. println(“amount: ” + i); } 34
CCFinderで検出されるコードクローン(例1) righttokennumber = c. get. End. Number() - c. get. Start. Number() + 1; return clone; } } string get. Left. Clone() const { char temp[STRLENGTH]; snprintf(temp, STRLENGTH, "%st%d, %d, %dt", left. ID. c_str(), leftstartline, leftstartcolumn, leftstartnumber, leftendline, leftendcolumn, leftendnumber); string get. Right. Clone() const { char temp[STRLENGTH]; snprintf(temp, STRLENGTH, "%st%d, %d, %dt", right. ID. c_str(), rightstartline, rightstartcolumn, rightstartnumber, rightendline, rightendcolumn, rightendnumber); string clone(temp); return clone; } } string get. Right. Clone() const { char temp[STRLENGTH]; snprintf(temp, STRLENGTH, int get. Left. Token. Number() const { return lefttokennumber; } の部分を検出してほしい 2002/11/15 36
CCFinderで検出されるコードクローン(例2) if(func->parameter!=NULL) return error("Error: Parameter Exist"); cur. Vertex->kind=ST_call; lp=makeexptree(NULL, SIDENTIFIER, idtemp, SPROC EDURE); cur. Vertex->tree=lp; cur. Vertex->refer=search_tree_postorder(lp); cur. Vertex->refer=uniq_RDlist(cur. Vertex->refer); cut. St(); } break; case 66: #line 314 "sub. Pascal. Parse. y" {struct FUNCTION *func; struct RD *para; 2002/11/15 lp=makeexptree(&lp, NULL, SRPAREN, NULL, SNULL); rp=makeexptree(NULL, &lp, SIDENTIFIER, idtemp, SPROCEDURE); cur. Vertex->kind=ST_call; cur. Vertex->tree=rp; cur. Vertex->refer=search_tree_postorder(rp); cur. Vertex->refer=uniq_RDlist(cur. Vertex->refer); cut. St(); } break; case 67: #line 355 "sub. Pascal. Parse. y" {yyval. exptree = yyvsp[0]. exptree; } break; の部分を検出 CCFinderは しかし,このコードクローンはリファクタリングの 対象にはならない 37
適用実験その 1(ANTLR)(3/5) n 選択範囲のコードクローン public final void m. OPEN_ELEMENT_OPTION(boolean _create. Token) throws Recognition. Exception, Char. Stream. Exception, Token. Stream. Exception { int _ttype; Token _token=null; int _begin=text. length(); ttype = OPEN_ELEMENT_OPTION; int _save. Index; match('<'); if ( _create. Token && _token==null && _ttype!=Token. SKIP ) { _token = make. Token(_ttype); _token. set. Text(new String(text. get. Buffer(), _begin, text. length()-_begin)); } _return. Token = _token; } 2002/11/15 41
適用実験その 1(ANTLR)(5/5) n クラス図による比較 現状 ANTLRLexer m. OPEN_ELEMENT_OPTION(boolean _create. Token) m. CLOSE_ELEMENT_OPTION(boolean _create. Token) m. COMMA(boolean _create. Token) m. QUESTION(boolean _create. Token) m. TREE_BEGIN(boolean _create. Token) m. LPAREN(boolean _create. Token) m. RPAREN(boolean _create. Token) m. COLON(boolean _create. Token) m. STAR(boolean _create. Token) m. PLUS(boolean _create. Token) m. ASSIGN(boolean _create. Token) m. IMPLIES(boolean _create. Token) m. SEMI(boolean _create. Token) m. CARET(boolean _create. Token) m. BANG(boolean _create. Token) m. OR(boolean _create. Token) m. WILDCARD(boolean _create. Token) m. RANGE(boolean _create. Token) m. NOT_OP(boolean _create. Token) m. RCURLY(boolean _create. Token) : Other methods (22個) : 2002/11/15 43
適用実験その 1(ANTLR)(5/5) n クラス図による比較 リファクタリングした場合 ANTLRLexer m. TOKEN(boolean _create. Token, int i, char c) : Other methods(22個) : 2002/11/15 44
l END 2002/11/15 46
- Slides: 47