類似コード片を用いた リファクタリングの試み 肥後 芳樹*, 神谷年洋**,楠本真二*,井上克郎* *大阪大学 大学院情報科学研究科 コンピュータサイエンス専攻 ** 科学技術振興事業団 若手個人研究推進事業 2003/7/17 ソフトウェア工学研究発表会 発表内容  コードクローン  コードクローン分析ツールCCFinder&Gemini  現場からの要望(リファクタリング)  提案するコードクローンフィルタリング手法  適用実験  まとめ 2003/7/17 ソフトウェア工学研究発表会.

Download Report

Transcript 類似コード片を用いた リファクタリングの試み 肥後 芳樹*, 神谷年洋**,楠本真二*,井上克郎* *大阪大学 大学院情報科学研究科 コンピュータサイエンス専攻 ** 科学技術振興事業団 若手個人研究推進事業 2003/7/17 ソフトウェア工学研究発表会 発表内容  コードクローン  コードクローン分析ツールCCFinder&Gemini  現場からの要望(リファクタリング)  提案するコードクローンフィルタリング手法  適用実験  まとめ 2003/7/17 ソフトウェア工学研究発表会.

類似コード片を用いた
リファクタリングの試み
肥後 芳樹*, 神谷年洋**,楠本真二*,井上克郎*
*大阪大学 大学院情報科学研究科 コンピュータサイエンス専攻
** 科学技術振興事業団 若手個人研究推進事業
2003/7/17
ソフトウェア工学研究発表会
1
発表内容
 コードクローン
 コードクローン分析ツールCCFinder&Gemini
 現場からの要望(リファクタリング)
 提案するコードクローンフィルタリング手法
 適用実験
 まとめ
2003/7/17
ソフトウェア工学研究発表会
2
研究の背景 コードクローン
 ソースコード中に類似したコード片があるとき、
それらをコードクローンという
クローンペア
クローンクラス
 コードクローンはソフトウェア保守を困難にする

あるコードクローンに対して機能変更を行う場合は、そ
のコードクローン全てに対しても変更の是非を考慮す
る必要がある
2003/7/17
ソフトウェア工学研究発表会
3
コードクローン分析ツール
CCFinder&Gemini
コードクローン検出ツールCCFinderを開発
視覚的にコードクローンの位置を知ることの
できるツールGeminiを開発
さまざまな実システムに適用し、有用性を
確認してきた
2003/7/17
ソフトウェア工学研究発表会
4
CCFinderの概要
ソースコードをトークン単位で直接比較するこ
とによりコードクローンを検出
 名前空間の正規化
 ユーザ定義名の置き換え
 テーブル初期化部分を取り除く
 モジュールの区切りを認識する
数百万行規模のシステムでも実用時間で解
析可能
2003/7/17
ソフトウェア工学研究発表会
5
CCFinderの処理概要
Source files
static
throws
{{{ String
1. static
static
foo()
throws
RESyntaxException
{
{{ $$ $$
(( (( )) ))throws
$void
void
foofoo()
throws$$RESyntaxException
RESyntaxException
String aaa {
static
RESyntaxException
String
1.
void
throws
RESyntaxException
static void
$ $$foo
throws
]{{] {${$ "123,400"
,
[[ [][] String
$String
$$ =
new
$[] {
]]] === a[]
new
$[]
2. [[String
a[]
=
new
String
{ "123,400",
"123,400",
"abc", "orange
"orange 100"
100" };
};
2.
new
"abc",
[String
$String
"abc"
,
"orange
100"
}
;
org
.
apache
.
regexp
;
}
3. org.apache.regexp.RE
org.apache.regexp.RE
pat == new
new org.apache.regexp.RE("[0-9,]+");
org.apache.regexp.RE("[0-9,]+");
} ;
3.
pat
$$ === new
$$ pat
RE
pat
RE
new
4. .int
int
sum
0; org . apache . regexp
4.
sum
==new
0;
. RE
)) ;;; int
$$ ==== $0$00
$$ sum
$$
$$ ((( "[0-9,]+"
RE
int
sum
sum
5. for
for
(int"[0-9,]+"
i == 0;
0; ii) << a.length;
a.length;
++i)
5.
(int
i
++i)
;; for
$$ i$$i === 0$0$ ;;; i$$i <<<<
int
for (( int
6. a$ ifif. (pat.match(a[i]))
(pat.match(a[i]))
6.
;++
$$ ii)) ))if
$$ ;; ++
length
; ++
++
pat
pat
$ . length
if ifif(( ((($$ pat
7. .. match
sum
+=
Sample.parseNumber(pat.getParen(0));
7.
sum
+=
Sample.parseNumber(pat.getParen(0));
$$ (( (( $$ aa [[ [[ $$ ii ]] ]] )) )) )) ))) $$sum
sum
sum
. match
8.
System.out.println("sum
" ++ sum);
sum);
8. +=
System.out.println("sum
"getParen
+=
Sample
((( 000
(( $$ .. $$ (( ((pat
$$ .. $.$. parseNumber
parseNumber
pat$$ .=
. getParen
pat
.=
getParen
+=
9. }} )) )) ;; System
(( $$ ((( "sum
.. .$$. println
$$ .. .$$. out
System
out
println
"sum==="""
9.
println
"sum
static
String
$$ $$goo
}}} static
))) ;;; goo(String
$$ void
sum
static void
void
goo
String
10. static
static
void
goo(String
[]((a)
a)((( $$throws
throws
RESyntaxException {{
+++ sum
goo
String
10.
[]
RESyntaxException
static
{{ $$ $$ =
$$RE("[0-9,]+");
throws
RESyntaxException
RE exp
exp ===
[[[ exp
]]] )))=
RESyntaxException
RE
exp
11. a$a$RE
RE
exp
=throws
newRESyntaxException
throws
= {{{ RE
11.
new
RE("[0-9,]+");
new
RE
=
$$ )) ;;)) $$;; $$int
$$ (( "[0-9,]+"
int sum
sum
new
=sum$$=== 000
12. new
int
sum"[0-9,]+"
0;
12.
int
sum
== 0;
$$ i$$i === 0$0$ ;;; i$$i <<<<
for
int
for
((( int
for (int
13. ;;;for
for
0; ii << a.length;
a.length; ++i)
++i)
13.
(int
ii == 0;
$
(
$$ ii)) ))if
$$ ;; ++
if
(
exp
length
; ++
++
if
(
exp
a$a$ ... length
;++
(
exp
if ( $
14. . match
(exp.match(a[i]))
14.
ifif (exp.match(a[i]))
]
$
[
$
(
$
(
a
[
i
]
(
a
[
i
]
sum
sum
. $ ( $ [ $ ] )) )) )) ))) $$sum
15. +=
sum
+=
parseNumber(exp.getParen(0));
15.
sum
+=
parseNumber(exp.getParen(0));
(( .. $$ getParen
$$ (( ( $$exp.. ((. $$exp
+=
getParen
()) 0)) )(( )00 )) ))
parseNumber
exp
getParen
$$$ ... parseNumber
+= parseNumber
16. ;;;System.out.println("sum
System.out.println("sum
==="""""+++++sum
sum);
16.
sum);
$$ =
(( $$ ((++ "sum
.. .$$. println
$$ .. .$$. out
=
System
out
println
"sum
sum
System
"sum
sum
17. }}))) ;;; }}}
17.
2003/7/17
ソフトウェア工学研究発表会
字句解析
字句解析
トークン列
トークン列
変換処理
変換処理
変換後トークン列
変換後トークン列
検出処理
検出処理
クローン情報
クローン情報
出力整形処理
出力整形処理
クローンペア位置情報
6
Geminiの概要(1/2)
 GUIベースのコードクローン分析環境
 CCFinderをコードクローン検出エンジンとして使用
 各種インターフェース
 Scatter plot
 マウスドラッグによるコードクローン選択
 ソート機能,ズーム機能...
 Metric graph
 メトリクス値の範囲指定によるコードクローン選択
 Source code view
 クローンペアのソースコードを対で表示
 実装言語
2003/7/17
 Java
ソフトウェア工学研究発表会
7
Geminiの概要(2/2)
Gemini
User Interfaces
a b c a b c a d e c
a b c a b c a d e c
Source files
Clone pair manager
Clone
selection
information
CCFinder
Scatter plot view
Source code
manager
Code clone
detector
Code clone
database
a, b, c, ... : tokens
: matched position
2003/7/17
Clone
selection
information
User
Source code view
Metrics
manager
Metric graph views
ソフトウェア工学研究発表会
8
CCFinder&Geminiの適用事例
 フリーソフトウェア




JDK libraries (Java, 570 KLOC)
Linux, FreeBSD (C, 1.6 + 1.3 MLOC)
FreeBSD, OpenBSD,NetBSD(C)
Qt(C++,240KLOC)
 商用

NTT Data Corp., Hitachi Ltd., Hitachi GP, NEC soft Ltd.,
ASTEC Inc., SRA Inc., NASDA,Daiwa Computer, その他
 大学の演習
 プログラム著作権関係の裁判証拠
2003/7/17
ソフトウェア工学研究発表会
9
検出したコードクローンへの対策
CCFinder/Geminiで発見したコードクローン
をリファクタリングの対象にすることはできな
いだろうか?

CCFinderが検出するコードクローンはトークンの並びであ
り,必ずしも意味的な固まり(クラス、メソッド、etc…)には
なっていない
2003/7/17
ソフトウェア工学研究発表会
10
目的
 CCFinderの出力結果からリファクタリング
(「メソッドの抽出」、「メソッドの引き上げ」な
ど*)に適しているコードクローンの抽出手
法を提案する
 オープンソースのソフトウェアに適用実験を
行い提案手法の有効性を確認する
*M. Fowler: Refactoring: Improving the Design of Existing Code, Addison-Wesley, 1999.
2003/7/17
ソフトウェア工学研究発表会
11
メソッドの抽出
Void methodA(int i){
methodZ();
System.out.println(“name:” + name);
System.out.println(“amount:” + i);
}
Void methodB(int i){
methodY();
System.out.println(“name:” + name);
System.out.println(“amount:” + i);
}
2003/7/17
void methodA(int i){
methodZ();
methodC(i);
}
void methodB(int i){
methodY();
methodC(i);
}
Void methodC(int i){
System.out.println(“name:” + name);
System.out.println(“amount:” + i);
}
ソフトウェア工学研究発表会
12
メソッドの引き上げ
クラスA
クラスA
クラスB
メソッドA
2003/7/17
メソッドA
クラスC
メソッドA
クラスB
ソフトウェア工学研究発表会
クラスC
13
提案手法の概要

CCFinderの検出したコー
ドクローンから、意味的に
まとまりのある部分のみ
を抽出する
Source files
CCFinder
クローン情報

抽出したクローン情報を
CCFinderと同じフォー
マットで出力することで
Geminiで表示できるよう
にする
フィルター
クローン情報
意味的にまとまりの
あるクローン情報
Gemini
2003/7/17
ソフトウェア工学研究発表会
14
意味的なまとまり(for Java)




宣言:class { … }、interface { …}
メソッド:メソッド、コンストラクタ、staticイニ
シャライザ
文:if文、for文、while文、do文、switch文、
try文、synchronized文
ブロック:`{`と`}`で囲まれた範囲
2003/7/17
ソフトウェア工学研究発表会
15
ツールの概略
 CCShaper(Code Clone Shaper)





CCFinderの検出したコードクローンから意味
的なまとまりのある部分のみを抽出する
解析対象:Java
ツールの記述言語:Java
サイズ:約12,000行
構文解析部の構築にはJavaCCを用いた
2003/7/17
ソフトウェア工学研究発表会
16
ツールが行う処理



構文解析部: コードクローンの
存在するソースコードを構文解
析する
Output of CCFinder
クローン抽出部: 構文解析結
果とCCFinderの出力から意味
的にまとまりのあるコードクロー
ンを検出する
クローン管理部: クローン抽出
部の発見したコードクローンを
ソート、マージして適切な順番
でファイルに書き出す
2003/7/17
ソフトウェア工学研究発表会
Source files
構文解析部
クローン抽出部
クローン管理部
出力ファイル
17
・・・・・
methodA(){
・・・・・
if( ・・・ ){
・・・・・
}
・・・・・
}
・・・・・
2003/7/17
・・・・・
methodB(){
・・・・・
if( ・・・ ){
・・・・・
}
・・・・・
}
・・・・・
ソフトウェア工学研究発表会
18
提案手法において抽出されるクローン(例1)
}
righttokennumber = c.getEndNumber() - c.getStartNumber() + 1;
string getLeftClone() const
{
char temp[STRLENGTH];
snprintf(temp,STRLENGTH,
"%s\t%d,%d,%d\t%d,%d,%d\t",leftID.c_str(),
leftstartline,leftstartcolumn,leftstartnumber,
leftendline,leftendcolumn,leftendnumber);
}
string clone(temp);
return clone;
string getRightClone() const
{
char temp[STRLENGTH];
snprintf(temp,STRLENGTH,
"%s\t%d,%d,%d\t%d,%d,%d\t",rightID.c_str(),
rightstartline,rightstartcolumn,rightstartnumber,
rightendline,rightendcolumn,rightendnumber);
}
string clone(temp);
return clone;
int getLeftTokenNumber() const
{
return lefttokennumber;
}
2003/7/17
ソフトウェア工学研究発表会
19
提案手法において抽出されるクローン(例1)
}
righttokennumber = c.getEndNumber() - c.getStartNumber() + 1;
string getLeftClone() const
{
char temp[STRLENGTH];
snprintf(temp,STRLENGTH,
"%s\t%d,%d,%d\t%d,%d,%d\t",leftID.c_str(),
leftstartline,leftstartcolumn,leftstartnumber,
leftendline,leftendcolumn,leftendnumber);
}
string clone(temp);
return clone;
string getRightClone() const
{
char temp[STRLENGTH];
snprintf(temp,STRLENGTH,
}
return clone;
string getRightClone() const
{
char temp[STRLENGTH];
snprintf(temp,STRLENGTH,
"%s\t%d,%d,%d\t%d,%d,%d\t",rightID.c_str(),
rightstartline,rightstartcolumn,rightstartnumber,
rightendline,rightendcolumn,rightendnumber);
}
string clone(temp);
return clone;
int getLeftTokenNumber() const
{
return lefttokennumber;
}
の部分のみを抽出
2003/7/17
ソフトウェア工学研究発表会
20
提案手法において抽出されるクローン(例2)
if(func->parameter!=NULL)
return error("Error:Parameter Exist");
curVertex->kind=ST_call;
lp=makeexptree(NULL,NULL,SIDENTIFIER,idtemp,SPR
OCEDURE);
curVertex->tree=lp;
curVertex->refer=search_tree_postorder(lp);
curVertex->refer=uniq_RDlist(curVertex->refer);
cutSt();
}
break;
case 66:
#line 314 "subPascalParse.y"
{struct FUNCTION *func;
struct RD
2003/7/17
*para;
lp=makeexptree(&lp,NULL,SRPAREN,NULL,SNULL);
rp=makeexptree(NULL,&lp,SIDENTIFIER,
idtemp,SPROCEDURE);
curVertex->kind=ST_call;
curVertex->tree=rp;
curVertex->refer=search_tree_postorder(rp);
curVertex->refer=uniq_RDlist(curVertex->refer);
cutSt();
}
break;
case 67:
#line 355 "subPascalParse.y"
{yyval.exptree = yyvsp[0].exptree;}
break;
の部分を検出
CCFinderは
しかし、このコードクローンは意味的なまとま
りはない(クローンの抽出は行われない)
ソフトウェア工学研究発表会
21
ANTLR,Ant
のソースコード
適用実験の概要
 オープンソースのJavaソフト
CCFinder
ウェア(ANTLR,Ant)のソース
コードに適用
CCShaper



Gemini
意味的にまとまりの
あるコードクローン
2003/7/17
Pentium4 1.5GHz
memory SDRAM512MB
50トークン以上のコードクローン
を対象
コードクローン
ソフトウェア工学研究発表会
22
適用実験その1(Ant)(1/5)
概要
 ソースファイルは689個
 総行数は約164,000行
 数値による結果比較
CCShaper無し
クローンペア数
クローンクラス数
12,870
1,161
CCShaper有り
159
87
CCShaperの解析時間:約5秒
2003/7/17
ソフトウェア工学研究発表会
23
適用実験その1(Ant)(2/5)
スキャタープロット図による比較
CCShaper無し
CCShaper有り
a
2003/7/17
ソフトウェア工学研究発表会
24
適用実験その1(Ant)(3/5)
検出したコードクローンの例
図中のaの部分のコード
public void getAutoresponse(Commandline cmd) {
if (m_AutoResponse == null) {
cmd.createArgument().setValue(FLAG_AUTORESPONSE_DEF);
} else if (m_AutoResponse.equalsIgnoreCase("Y")) {
cmd.createArgument().setValue(FLAG_AUTORESPONSE_YES);
} else if (m_AutoResponse.equalsIgnoreCase("N")) {
cmd.createArgument().setValue(FLAG_AUTORESPONSE_NO);
} else {
cmd.createArgument().setValue(FLAG_AUTORESPONSE_DEF);
} // end of else
}
2003/7/17
ソフトウェア工学研究発表会
25
適用実験その1(Ant)(4/5)
リファクタリング例
発見されたコードクローンの検証



全く同一(識別子名も同じ)のメソッド単位のコードクローン
このコードクローンは7つのクラスにみつかった
この7つのクラスは同一のクラスを継承していた
このメソッドを親クラスに移すことで1つの
メソッドにまとめることができる
2003/7/17
ソフトウェア工学研究発表会
26
適用実験その1(Ant)(5/5)
クラス図:リファクタリング前
 クラス図による比較
現状
MSVSS
MSVSSADD
MSVSSCHECKIN
MSVSSCHECKOUT
getAutoresponse
(Commandline cmd)
getAutoresponse
(Commandline cmd)
getAutoresponse
(Commandline cmd)
MSVSSCP
MSVSSCREATE
MSVSSGET
MSVSSLABEL
getAutoresponse
getAutoresponse
getAutoresponse
getAutoresponse
(Commandline cmd) (Commandline cmd) (Commandline cmd) (Commandline cmd)
2003/7/17
ソフトウェア工学研究発表会
27
適用実験その1(Ant)(5/5)
クラス図:リファクタリング後
 クラス図による比較
リファクタリングした場合
MSVSS
getAutoresponse
(Commandline cmd)
MSVSSADD
MSVSSCP
2003/7/17
MSVSSCHECKIN
MSVSSCREATE
MSVSSCHECKOUT
MSVSSGET
ソフトウェア工学研究発表会
MSVSSLABEL
28
適用実験その2(ANTLR)(1/5)
概要
 ソースファイルは239個
 総行数は約44,000行
 数値による結果比較
CCShaper無し
クローンペア数
クローンクラス数
388,574
1,072
CCShaperあり
984
148
CCShaperの解析時間:約2分
2003/7/17
ソフトウェア工学研究発表会
29
適用実験その2(ANTLR)(2/5)
スキャタープロット図による比較
CCShaper無し
CCShaper有り
b
2003/7/17
ソフトウェア工学研究発表会
30
適用実験その2(ANTLR)(3/5)
検出したコードクローンの例
 図中のbの部分のコード
public final void mOPEN_ELEMENT_OPTION(boolean _createToken)
throws RecognitionException, CharStreamException, TokenStreamException {
int _ttype;
Token _token=null;
int _begin=text.length();
ttype = OPEN_ELEMENT_OPTION;
int _saveIndex;
}
match('<');
if ( _createToken && _token==null && _ttype!=Token.SKIP ) {
_token = makeToken(_ttype);
_token.setText(new String(text.getBuffer(), _begin, text.length()-_begin));
}
_returnToken = _token;
2003/7/17
ソフトウェア工学研究発表会
31
適用実験その2(ANTLR)(4/5)
リファクタリング例
 発見されたコードクローンの検証



異なっていたのは
の部分のみ
このコードクローンが20箇所に現れていた
すべてのコードクローンは同じクラスのメソッドであった
引数を2つ増やすことで1つのメソッドにま
とめることができる
2003/7/17
ソフトウェア工学研究発表会
32
適用実験その2(ANTLR)(5/5)
回帰テスト


実際に検出したコードク
ローンに対してリファクタ
リングを行った
回帰テストを行った

入力(文法定義ファイル)
ANTLR
(変更後)
ANTLR
付属のサンプル84個全て
(変更なし)
に対して、変更前の出力
結果と比較を行い、全く同
一の結果を得た
比較
出力(構文解析器の
ソースファイル)
2003/7/17
ソフトウェア工学研究発表会
出力(構文解析器の
ソースファイル)
33
まとめ
 リファクタリングに適したコードクローンを抽出す
る手法を提案した
 ツール(CCShaper)を試作した
 Javaのソースコード(Ant、ANTLR)に適用するこ
とで有用性を確認した
2003/7/17
ソフトウェア工学研究発表会
34
今後の課題
コンテキストの考慮


参照している変数
クローンとなっているコード間の位置関係
商用システムのソースコードへの適用
他言語への拡張
2003/7/17
ソフトウェア工学研究発表会
35
2003/7/17
ソフトウェア工学研究発表会
36
Suffix-tree
 Suffix tree is a tree that satisfies the following
conditions.



A leaf node represents the starting
position of sub-string.
A path from root node to a leaf node
represents a sub-string.
First characters of labels
of all the edges from one node
are different from each other.
xyxyz% 1
xyz% 2
y
x
xyz%
 → A common path means
a clone
y
1 2 3 4 5 6 7
x x y x y z %
z%
1 x *
2 x * *
3 y
*
%
4 x * *
*
5 y
*
*
6 z
*
2003/7/177 %
ソフトウェア工学研究発表会
*
z%
4
3
z%
5
6
7
1 2 3 4 5 6 7
x x y x y z%
37
CCFinderの検出したコードクローンをリ
ファクタリングに使用する上での問題点


CCFinderが検出するコードクローンはトー
クンの並びであり,必ずしも意味的な固まり
(クラス、メソッド、etc…)にはなっていない
リファクタリングの対象にならないコードク
ローンも発見してしまっている
2003/7/17
ソフトウェア工学研究発表会
38