Document

コードクローンの分布情報を
用いた特徴抽出手法の提案
井上研究室
服部 剛之
1
コードクローン



ソースコード中に存在する同一,もしくは類似したコード
片を同一システム中に持つコード片
(以降単にクローンと呼ぶ)
コピーアンドペースト等により生成される
ソフトウェア保守を困難にする

コードの変更が必要な場合


バグ修正,機能追加
保守作業の手間が増大
クローンセット
2
CCFinder,Gemini

クローンを対象とした保守支援ツール



検出ツール: CCFinder[1]
分析ツール: Gemini[2]
国内外の個人・組織に配布



研究機関での利用
企業での商用ソフトウェア開発プロセスへの導入
配布先からのフィードバックを得ている
[1] T. Kamiya, S. Kusumoto, and K. Inoue, “CCFinder: A multi-linguistic token-based code clone detection system
for large scale source code”, IEEE Transactions on Software Engineering, 28(7):654-670, 2002.
[2] Y. Ueda, T. Kamiya, S. Kusumoto and K. Inoue, “Gemini: Maintenance Support Environment Based on Code
Clone Analysis”, Proc. of the 8th IEEE International Symposium on Software Metrics, 67-76, 2002.
3
フィードバックから得られた情報

クローン情報の利用法

リファクタリング支援


設計情報との一貫性確認


互いにクローンになっているものを1つにまとめる
設計上説明できるクローンであるか
問題点


検出されたクローンの数が多すぎてどれをみるべきかわからない
調査の関心がないクローン(定型処理など)が含まれていて,どの
クローンから見ていけばいいかわからない
クローンの特徴を知る必要性
4
目的とアプローチ

目的: クローン分析の効率化



クローンを特徴に基づいて分類する
分類を使って,調査の関心がないクローン(定型処理な
ど)をフィルタする
アプローチ: メトリクスを用いてクローンを分類

以下のメトリクスを用いる



RAD: クローン間の遠さの尺度
NIF: クローンが存在するファイルの数
RNR: クローン内の重複した処理の少なさの度合い
5
RAD: クローン間の遠さの尺度

RAD: 与えられたクローンセットの各要素を含む
ファイルから共通の親ディレクトリまでの距離の
最大値
•RAD = 2
•例:RAD = 1
2
1
ファイルC
ファイルA
ファイルB
は,クローンを表す
ファイルA ファイルB
6
NIF: クローンが存在するファイルの数

NIF: 与えられたクローンセットの各要素を含む
ファイルの数
•例:NIF = 2
•NIF = 3
ファイルC
ファイルA
ファイルB
2
は,クローンを表す
ファイルA ファイルB
3
7
RNR: クローン内の重複した処理の
少なさの度合い
∑ Tokensrepeated(C)
RNR = 1 -
C∈S
∑ Tokensall(C)
C∈S
S: クローンセット
C: クローンセットの要素
Tokensall(C) : クローンC中の総トークン数
Tokensrepeated(C) : クローンC中の繰り返し部分のトークン数
8トークン
X A B A B A B Y
4トークン
直前の繰り返し
Tokensall = 8
Tokensrepeated = 4
8
クローンの分布に着目した分類方法

RAD,NIFは,クローンの分布の特徴を表すメト
リクス

値の高低により,クローンを4つに分類する
RAD
高
vertical
global
local
horizontal
低
低
高 NIF
9
カテゴリ: local

クローンがディレクトリ階層上近い少数のファイ
ルに存在する
RAD
高
vertical
global
local
horizontal
低
は,クローンを表す
低
高 NIF
10
カテゴリ: horizontal

クローンがディレクトリ階層上近い多数のファイ
ルに存在する
RAD
高
vertical
global
local
horizontal
低
は,クローンを表す
低
高 NIF
11
カテゴリ: vertical

クローンがディレクトリ階層上遠くの少数のファイ
ルに存在する
RAD
高
vertical
global
local
horizontal
低
低
は,クローンを表す
高 NIF
12
カテゴリ: global

クローンがプログラム広範囲の多数のファイル
に存在する
RAD
高
vertical
global
local
horizontal
低
低
は,クローンを表す
高 NIF
13
実験



目的: RAD,NIF,RNRに着目して分類した各カテゴリに含まれるクロー
ンの特徴を調べる
Java,C,C++で書かれたオープンソースソフトウェアについて分類を
行った
 CCFinderにより検出された30トークン以上のクローンセットを対象
 人の手によって確認
 Java:
Ant,Webmail,httpunit,Art of Illusion
 C
:
Small Device C Compiler,Sketch
 C++ :
CppUnit,SWIG
 総ファイル数: 2948個
 総クローンセット数(30トークン以上): 14874個
Ant


総ファイル数: 954個
総クローンセット数(30トークン以上): 1643個
14
クローンの特徴を表す指標

クローンが含まれる関数の名前を用いて指標を定義する



クローンがどのような関数に存在するかで分類
same: 同じ名前の関数内に存在するクローン
similar: 名前の似た関数内に存在するクローン




名前が似ている: 関数名の一部に共通した単語を持つ
例) addConfiguredInputMapper
addConfiguredOutputMapper
addConfiguredErrorMapper
different: 異なる関数内に存在するクローン
クローンセットが各指標に該当するか調べることで分類し
たカテゴリを評価する

複数の指標に該当することを許す
15
評価条件


カテゴリ分けでのRAD,NIFの高低の閾値はそれぞれの
最大値の半分とした
RNRについては次のように分割した



RNR低: 0 ≦ RNR ≦ 0.3
RNR中: 0.3 < RNR ≦ 0.7
RNR高: 0.7 < RNR ≦ 1
低
中
高
RNR
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1
16
RNRに着目した特徴

RNRの低いクローンは単純なロジックのクロー
ンであった(繰り返しの多いクローン)
public static String getMethodAccess(int access_flags) {
StringBuffer sb = new StringBuffer();
if (isPublic(access_flags)) {
sb.append("public ");
全体が1つのクローン
} else if (isPrivate(access_flags)) {
sb.append("private ");
} else if (isProtected(access_flags)) {
sb.append("protected ");
}
if (isFinal(access_flags)) {
sb.append("final ");
}
直前の繰り返し
if (isStatic(access_flags)) {
sb.append("static ");
}
if (isSynchronized(access_flags)) {
sb.append("synchronized ");
}
17
Antでのクローンセットの分布
RAD
高
vertical
33個
global
7個
local
530個
horizontal
11個
低
低
高 NIF
要素数が3以上のクローンセットについて調べた
(検出されたクローンセットの約35%)
18
カテゴリ: local の結果
RNR高: 305個
RNR中: 131個
RNR低: 94個
different
similar
same
0

20
40
60
80
100 %
same,similarに該当するクローンが多い


あるデータ構造の要素に対して処理を行う各メソッド内にクロー
ンが見られた
同じデータ構造を用いている
19
カテゴリ: localに属するクローンの例
public int getClassEntry(String className) {
int index = -1;
for (int i = 0; i < entries.size() && index == -1; ++i) {
Object element = entries.elementAt(i);
if (element instanceof ClassCPInfo) {
ClassCPInfo classinfo = (ClassCPInfo) element;
if (classinfo.getClassName().equals(className)) {
index = i;
}
}
}
return index;
同じクローンセットに含まれるメソッドの例として,
getConstantEntry
getMethodRefEntry
などが存在した
}
20
カテゴリ: horizontal の結果
RNR高: 9個
RNR中: 1個
RNR低: 1個
different
similar
same
0

20
40
60
80
100 %
sameに該当するクローンが多い


例えば他のプログラムを実行するメソッドにクローンが見られた
引数ごとに異なるメソッドとして実装されている
21
カテゴリ: horizontalに属するクローンの例
public void execute() throws BuildException {
Commandline commandLine = new Commandline();
Project aProj = getProject();
int result = 0;
if (getViewPath() == null) {
setViewPath(aProj.getBaseDir().getPath());
}
外部プログラムの各機能を
呼び出すメソッド中に
クローンが存在した
commandLine.setExecutable(getClearToolCommand());
commandLine.createArgument().setValue(COMMAND_CHECKIN);
checkOptions(commandLine);
if (!getFailOnErr()) {
getProject().log("Ignoring any errors that occur for: "
+ getViewPathBasename(),Project.MSG_VERBOSE);
}
result = run(commandLine);
if (Execute.isFailure(result) && getFailOnErr()) {
String msg = "Failed executing: " + commandLine.toString();
throw new BuildException(msg, getLocation());
}
}
22
カテゴリ: vertical の結果
RNR高: 4個
RNR中: 12個
RNR低: 17個
different
similar
same
0

20
40
60
80
100 %
differentに該当するクローンが多い

類似した構造のパッケージにクローンが見られた
23
カテゴリ: vertical に属するクローンの例
main
ant
taskdefs
while (classEnum.hasMoreElements()) {
String className = (String)
classEnum.nextElement();
log(" Class " + className + " affects:",
Project.MSG_DEBUG);
testcases Hashtable affectedClasses
= (Hashtable) affectedClassMap.get(className);
Enumeration affectedClassEnum =
affectedClasses.keys();
while (affectedClassEnum.hasMoreElements()) {
ant
String affectedClass = (String)
affectedClassEnum.nextElement();
ClassFileInfo info
taskdefs
= (ClassFileInfo)
affectedClasses.get(affectedClass);
log(" " + affectedClass + " in "
+ info.absoluteFile.getPath(),
Project.MSG_DEBUG);
}
24
カテゴリ: global の例
RNR高: 4個
RNR中: 0個
RNR低: 3個
different
similar
same
0

20
40
60
80
100 %
differentに該当するクローンがほとんどである

定型処理に該当するクローンが見られた
25
カテゴリ: global に属するクローンの例
} catch (IOException ioex) {
throw new BuildException("Error while
+ ioex.getMessage(), ioex);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ignore) {
// ignore
}
}
if (os != null) {
try {
os.close();
} catch (IOException ignore) {
// ignore
}
}
}
}
concatenating: "
クローンが存在しているメソッドは
ストリームを閉じる処理を2回続けて
行う内容であった
26
考察


異なるソフトウェアでも,同じカテゴリに分類されたクローンに同じ特
徴が見られた
 ソフトウェアの記述言語を問わない
 メトリクス値に基づいてクローンの特徴を抽出可能
各カテゴリの特徴を利用することでクローンをフィルタすることができ
る
 例1) globalに属するクローンは定型処理であるので,リファクタリング
対象にならない
 例2) verticalに属するクローンは設計情報との一貫性を確認すること
が有益
 他のパッケージからのアドホックなコピーの恐れ
 例3) RNRが低いクローンは単純なロジックの繰り返しであるので利
用しにくい


単純な代入文の連続など
リファクタリングの対象にならない
27
まとめと今後の課題

まとめ


コードクローン分析の効率化を支援するコードクロー
ンの特徴抽出手法の提案と評価
今後の課題



分類の詳細化
ツールとしての実装
実際の開発現場での適用実験と評価
28
29