Download presentation
Presentation is loading. Please wait.
1
コードクローンの集約によるリファクタリング支援システムの提案と実装
肥後 芳樹 井上研究室 博士前期課程2年 井上研究室の肥後です. それでは「コードクローンの集約によるリファクタリング支援システムの提案と実装」というタイトルで発表させていただきます. 2018/12/5 平成15年度修士学位論文発表会
2
研究の背景 コードクローン ソースコード中に類似したコード片があるとき、 それらをコードクローンという クローンペア
研究の背景 コードクローン ソースコード中に類似したコード片があるとき、 それらをコードクローンという コードクローンはソフトウェア保守を困難にする あるコードクローンに対して機能変更を行う場合は,そのコードクローン全てに対しても変更の是非を考慮する必要がある クローンクラス クローンペア まず,研究の背景であるコードクローンについて説明します. コードクローンとはソースコード中に存在する,互いに類似したコード片を指します. この図を見ていただきたいのですが,この2つの図はそれぞれソースファイルを示しています. そして,この3つの灰色の部分が互いに類似した部分,つまりコードクローンであるとします. このような場合,私たちは,コードクローンの対のことをクローンペアといい,互いに類似しているコードクローン全てをまとめてクローンクラスと呼んでいます. そして,一般的に,コードクローンはソフトウェアの保守を困難にするといわれています. それは,例えばソースコードのある部分に機能変更やバグ修正を行う場合にもしその部分がコードクローンになっていたら,それと対応するすべてのコードクローンに対しても,同様の変更の是非を考慮しなければならないからです. 2018/12/5 平成15年度修士学位論文発表会
3
クローンに対する保守支援 ソースコード中のクローンの量,分布状態などの把握 クローンの集約(リファクタリング)
すべてのクローンに対して漏れなく修正を行う 品質・複雑度の調査・予測 クローンの集約(リファクタリング) リファクタリングとは,ソフトウェアの外部的振る舞いを保ったままで、内部の構造を改善していく作業 現在までに数十個のリファクタリングパターンが提案されている 重複したコード(クローン)は最も優先してリファクタリングすべき,といわれている そこでコードクローンに対する保守支援が必要となってきます. 保守支援の形態としては,まず,ソースコード中のクローンの量・分布状態を把握することがあげられます. これにより,全てのクローンに対して漏れなく修正を行ったり,クローンを用いた品質や複雑度の調査・予測が可能です. また,もう1つの形態として,ソフトウェアからクローンを取り除くということがあります. 現在までに数十個のリファクタリングパターンが提案されています. また,重複したコード,いわゆるコードクローンは最も優先してリファクタリングすべき,といわれています. 2018/12/5 平成15年度修士学位論文発表会
4
目的に応じたクローン検出 ソースコード中のクローンの量,分布状態などの把握 ソースコード上のクローンを漏れなく検出することが望ましい
クローンの集約(リファクタリング) ソースコード上のクローンを漏れなく検出することが望ましい コードクローン情報をどのように用いるか,によって検出すべきクローンが異なります. ソースコード中のクローンの量,分布状態の把握を目的とした場合は,ソースコード中のクローンを漏れなく検出することが望ましいといえます. 一方,クローンの集約を目的とした場合は,集約が可能で,かつ,集約の効果が大きいクローンを検出することが望ましいといえます. 集約が可能で,かつ,集約の効果が大きいクローンを検出することが望ましい 2018/12/5 平成15年度修士学位論文発表会
5
集約を目的としたクローン検出 プログラム依存グラフ(PDG)上での類似部分の抽出[1]
非常に精度が高い PDG構築の計算量が多い(O(n2) : nはソースコード中の文や式の数) メトリクスを用いて関数・メソッド単位の類似部分を抽出[2] 検出対象が関数・メソッドのみ クローン検出は21種類のメトリクスを用いる これまでに,集約を目的としたクローン検出手法はいくつか提案されています. 例えば,プログラム依存グラフ上での類似部分をクローンとして検出する手法が提案されています. この手法はプログラム中のデータフロー,コントロールフローを考慮しているため,非常に精度が高いのが特徴です. しかし,プログラム依存グラフの構築には,非常に高い計算コストを必要とし,実際のソフトウェア開発・保守現場では適用が困難であります. また,メトリクスを用いた検出手法も提案されています. この手法では,各関数・メソッドに対して21種類のメトリクスを計測し,その値の一致,近似に応じて8種類の類似度を表現しています. この手法では,特にクローン検出に高い計算コストは必要としませんが,検出対象を関数・メソッドに絞り込んでいるため,検出されるクローンが限られています. [1] R. Komondoor and S. Horwitz, “Using slicing to identify duplication in source code”, In Proc. of the 8th International Symposium on Static Analysis, Paris, France, July 16-18, 2001. [2] Magdalena Balazinska, Ettore Merlo, Michel Dagenais, Bruno Lague, and Lostas Kontogiannis, “Advanced Clone-Analysis to Support Object-Oriented System Refactoring”, WCRE 2000, pp
6
研究内容 本研究では,リファクタリングを目的としたクローン検出・集約手法を提案する クローン検出 集約方法の提示 高いスケーラビリティ
細粒度のクローンを検出 集約方法の提示 メトリクスを用いたクローンの特徴づけ そこで,本研究ではこれらの問題を解決するようなコードクローン検出手法を提案します. そして,検出したクローンに対してはメトリクスを用いて定量的に特徴づけを行い,集約方法の提示を行います. 2018/12/5 平成15年度修士学位論文発表会
7
クローン検出(概要) 例:Java言語では, 宣言単位 メソッド単位 文単位 目標 class宣言,interface宣言
メソッド本体,コンストラクタ,スタティックイニシャライザ 文単位 do文,for文,if文,switch文,synchronized文,try文,while文 目標 それほど大きな計算コストを必要としない(高いスケーラビリティ) 細粒度のクローンも検出 言語における構造的なまとまりを持ったクローンを検出 では,クローン検出から説明したいと思います. クローン検出における目標は,それほど大きな計算コストを必要としないこと,と細粒度のクローンを検出することの2つです. 本研究ではこれらを満たすものとして,言語における構造的なまとまりを持ったクローンの検出を試みます. 例えば,Java言語では,構造的なまとまりはこのようになっています. 宣言単位では,class宣言やinterface宣言,メソッド単位ではメソッド本体,コンストラクタ,スタティックイニシャライザがあげられます. 文単位では,これら7つのものがあげられます. 2018/12/5 平成15年度修士学位論文発表会
8
クローン検出(手順) 既存のコードクローン検出ツールCCFinderを利用 CCFinderは,
ソースコードをトークン単位で直接比較することによりクローンを検出 名前空間の正規化 ユーザ定義名の置き換え テーブル初期化部分を取り除く モジュールの区切りを認識する 数百万行規模のシステムでも実用時間で解析可能 クローン検出の過程では,既存のコードクローン検出ツールCCFinderを用います. ここで簡単にCCFinderの特徴を説明します. CCFinderはソースコードをトークン単位で直接比較することによってクローンを検出します. 検出を行う際に,これらの工夫を行うことによってより実用的なクローンを検出するように実装されています. また,CCFinderのクローン検出は非常に高速であり,数百万行規模のシステムであっても実用的な時間で解析可能であります. 2018/12/5 平成15年度修士学位論文発表会
9
クローン検出(手順) CCFinderはトークンの列としてコードクローンを検出するため,検出されたコードクローンは必ずしも集約には適していない CCFinderの検出したクローン中に存在する最大の構造的なまとまりを抽出する 大規模なソフトウェアからでも実用的な時間で構造的なまとまりを持ったクローンを検出可能 このようにCCFinderはトークンの列としてコードクローンを検出します. そのため,検出されたコードクローンは必ずしも,集約には適した単位とはなっていません. そこで,CCFinderの検出したクローン中に存在する最大の構造的なまとまりを抽出します. このようなフィルタリング処理を行うことによって,大規模なソフトウェアからでも実用的な時間で構造的なまとまりを持つコードクローンを検出可能となります. 2018/12/5 平成15年度修士学位論文発表会
10
コード片1 CCFinderが検出するクローン コード片2 提案手法が検出するクローン 次に提案手法での抽出の例を示します.
609: reset(); 610: grammar = g; 611: // Lookup make-switch threshold in the grammar generic options 612: if (grammar.hasOption("codeGenMakeSwitchThreshold")) { 613: try { 614: makeSwitchThreshold = grammar.getIntegerOption("codeGenMakeSwitchThreshold"); 615: //System.out.println("setting codeGenMakeSwitchThreshold to " + makeSwitchThreshold); 616: } catch (NumberFormatException e) { 617: tool.error( 618: "option 'codeGenMakeSwitchThreshold' must be an integer", 619: grammar.getClassName(), 620: grammar.getOption("codeGenMakeSwitchThreshold").getLine() 621: ); 622: } 623: } 624: 625: // Lookup bitset-test threshold in the grammar generic options 626: if (grammar.hasOption("codeGenBitsetTestThreshold")) { 627: try { 628: bitsetTestThreshold = grammar.getIntegerOption("codeGenBitsetTestThreshold"); 提案手法が検出するクローン CCFinderが検出するクローン コード片2 623: } 624: 625: // Lookup bitset-test threshold in the grammar generic options 626: if (grammar.hasOption("codeGenBitsetTestThreshold")) { 627: try { 628: bitsetTestThreshold = grammar.getIntegerOption("codeGenBitsetTestThreshold"); 629: //System.out.println("setting codeGenBitsetTestThreshold to " + bitsetTestThreshold); 630: } catch (NumberFormatException e) { 631: tool.error( 632: "option 'codeGenBitsetTestThreshold' must be an integer", 633: grammar.getClassName(), 634: grammar.getOption("codeGenBitsetTestThreshold").getLine() 635: ); 636: } 637: } 638: 639: // Lookup debug code-gen in the grammar generic options 640: if (grammar.hasOption("codeGenDebug")) { 641: Token t = grammar.getOption("codeGenDebug"); 642: if (t.getText().equals("true")) { 次に提案手法での抽出の例を示します. このようなソースコードが与えられた場合,CCFinderはこのハイライトがかかった部分をクローンとして検出します. 提案手法ではこのような場合,このクローン内に存在する最大の構造的なまとまりであるif文の部分を抽出します.
11
コード片3 コード片4 CCFinderが検出するクローンペア もう1つ例を示します.
1527: if ( inputState.guessing==0 ) { 1528: t=a.getText(); 1529: } 1530: { 1531: _loop84: 1532: do { 1533: if ((LA(1)==COMMA)) { 1534: match(COMMA); 1535: id(); 1536: if ( inputState.guessing==0 ) { 1537: t+=","+b.getText(); 1538: } 1539: } 1007: if ( inputState.guessing==0 ) { 1008: buf.append(a.getText()); 1009: } 1010: { 1011: _loop144: 1012: do { 1013: if ((LA(1)==WILDCARD)) { 1014: match(WILDCARD); 1015: a=id(); 1016: if ( inputState.guessing==0 ) { 1017: buf.append('.'); buf.append(a.getText()); 1018: } 1019: } コード片3 コード片4 CCFinderが検出するクローンペア もう1つ例を示します. このようなソースコードが与えられた場合CCFinderはこの部分をクローンをして検出します. しかし,このクローン内には特に構造的なまとまりは存在しません. 提案手法ではこの様な場合を識別し,何も抽出しません.
12
集約方法の提示 概要 検出したクローンの集約方法としては以下のものが考えられる
メソッドの抽出: クローンになっている部分をメソッドとして抽出 メソッドの引き上げ: クローンになっている部分を親クラスへ引き上げる 検出したクローンがどちらに適しているか,メトリクスを用いて判定する 次に集約方法の提示について説明したいと思います. クローンを集約しようと考えた場合以下の方法が考えられます. これらについては次に説明します. そして,検出したクローンがどちらに適しているのか,またはどちらにも適していないのかをメトリクスを用いて判定します. 2018/12/5 平成15年度修士学位論文発表会
13
集約方法の提示 メソッドの抽出 ある部分を新たなメソッドとして抽出するためには,周囲との結合度が低いことが望ましい(抽出部分の外側で定義された変数を用いていないことが望ましい) リファクタリング前 リファクタリング後 void methodA(int i){ methodZ(); System.out.println(“name:” + name); System.out.println(“amount:” + i); } void methodB(int i){ methodY(); void methodA(int i){ methodZ(); methodC(i); } void methodB(int i){ methodY(); void methodC(int i){ System.out.println(“name:” + name); System.out.println(“amount:” + i); methodC(i); 抽出したメソッドの呼び出し メソッドの抽出 では,メソッドの抽出から説明します. メソッドの抽出とは,既存のコード片の一部を新たなメソッドとして再定義することを意味します. この例では,このように2箇所に存在しているsystem.out.println文が抽出されています. このリファクタリングを行う場合,抽出部分において,その外部で定義された変数を用いている場合は,新たに定義したメソッドの引数として渡す必要があるため,できるだけそのような変数を用いていないことが望ましいといえます. 2018/12/5 平成15年度修士学位論文発表会
14
集約方法の提示 メソッドの引き上げ コードクローンを含むクラスが同じクラスを継承していなければいけない リファクタリング前
リファクタリング後 コードクローンを含むクラスが同じクラスを継承していなければいけない Employee Employee メソッドの引き上げ getName() 次にメソッドの引き上げについて説明します. これは,子クラスに存在するメソッドを親クラスに引き上げることを意味しています. この例では,employeeクラスを継承しているsalesmanクラスとengineerクラスに存在しているgetName()というメソッドがemployeeクラスに引き上げられています. 当然ですが,このリファクタリングを行う場合はメソッドが定義されているクラスが同じクラスを継承していなければいけません. Salesman Engineer Salesman Engineer getName() getName() 2018/12/5 平成15年度修士学位論文発表会
15
集約方法の提示 コードクローンメトリクス(1/2)
以下の2つのことがらについて調べる クローンの外側で定義された変数を内側でいくつ使用(代入,参照)しているか クローンが含まれるクラスがクラス階層においてどのような関係にあるか 解析結果はメトリクスとして数値化する つまり,「メソッドの抽出」と「メソッドの引き上げ」を容易に適用できるクローンを検出するために以下の2つのことがらについて調べます. 1つ目が,クローンの外側で定義された変数を内側でいくつ用いているか,で,2つ目がクローンが含まれるクラスがクラス階層においてどのような関係にあるかです. そしてこれらの解析結果はメトリクスとして数値化します. 2018/12/5 平成15年度修士学位論文発表会
16
集約方法の提示 コードクローンメトリクス(2/2)
クローンクラス内の全てのコード片が,ある1つのクラス内に存在する場合 DCH : 0 クローンクラス内の全てのコード片があるクラスとその子クラス内に存在する場合 DCH : 1 クローンクラス内のコード片が含まれるクラスが共通の親を持たない場合 DCH : -1 クローンの外側で定義されていて,内側で用いられている変数 RVK :クローンの外側で定義されていて,内側で用いられている変数の数 RVN : RVKの変数に使用回数の重みをつけたもの クローンのクラス階層内における位置関係 DCH : そのクローンクラスがクラス階層においてどの程度分散しているか クローンの外側で定義された変数a,bを,クローンの内側でそれぞれ2,3回使用している RVK : 1 + 1 = 2 RVN : 2 + 3 = 5 では定義したメトリクスについて説明したいと思います. まず,外部定義の変数に関してはRVKとRVNという二つのメトリクスを定義しました. RVKは外側で定義されていて,内側で用いられている変数の数を表すメトリクスです. また,RVNはRVKに使用回数の重みをつけたものです. 例えば,クローンの内側で,外部で定義された変数a,bをそれぞれ2回,3回使用しているような場合は,RVKの値は2,RVNの値は5になります. 次にクローンのクラス階層内における位置関係についてはDCHというメトリクスを定義しました. このメトリクスはそのクローンクラスのクラス階層内における分散度を現すメトリクスです. 例えば,クローンクラス内の全てのコード片が,ある1つのクラス内に存在する場合はDCHの値は0,あるクラスとその子クラス内に存在する場合は1,同様にあるクラスとその第二世代の子までの場合は2と,分散度が大きいほど,このメトリクス値も大きくなります. また,クローンクラス内のコード片が含まれるクラスが共通の親クラスを持たない場合は例外的に-1としています. 2018/12/5 平成15年度修士学位論文発表会
17
リファクタリング支援ツール Cancer(概要)
対象言語: Java クローン検出部には既存のコードクローン検出ツールCCFinderを利用 ツールの記述言語: Java 構文,意味解析を行うコンポーネントはオープンソースの構文解析器生成ツールJavaCCを用いて構築 動作環境:JDK1.4以上のVMが実行可能な環境 サイズ 解析部: 約15000行 GUI部: 約8000行 ユーザはGUIを操作することで,コードクローンの絞り込みを行うことができる そして,これらの提案手法を実装したリファクタリング支援ツールを試作しました. 現在のところ,対象言語はJavaのみとなっています. さきほど説明しましたように,クローン検出部にはCCFinderを用いています. ユーザはGUIを操作することで検出したコードクローンの絞込みを行うことができます. 2018/12/5 平成15年度修士学位論文発表会
18
リファクタリング支援ツール Cancer(解析の流れ)
解析部 GUI部 クローン情報 コードクローン検出 ユーザインターフェース 構造的なクローン抽出 メトリクス値の計算 ユーザ ソースコード メトリクス情報のついた構造的なクローン情報 構造的なまとまりの抽出 クラス階層情報の抽出 変数定義情報の抽出 構造情報 これが試作したツールの解析の流れです.このツールでは対象ソースコードを読み込み,内部でCCFinderを実行しコードクローン検出を行います. また,構文解析,簡単な意味解析を行い,構造的なまとまりやメトリクス計算に必要な情報を取得します. そして,CCFinderの検出したコードクローン情報と突き合せ,メトリクス情報のついた構造的なクローン情報を得ます. ユーザはこのツールのGUI部を操作することで,興味のあるメトリクスに基づいてコードクローンを絞り込むことができます. 2018/12/5 平成15年度修士学位論文発表会
19
メトリクスグラフ クローンクラスリスト クローンユニット選択チェックボックス これは,ツールのスナップショットです.
この部分はメトリクスグラフと呼ばれるものです,ユーザはこのグラフにおいて各メトリクスの上限・下限を設定することで任意のクローンクラスを選択可能です. また,このパネルでは,ユーザは興味のあるクローンの単位を選択することが可能です. 例えば,メソッドの単位のクローンのみを選択といったことができます. また,このクローンクラスリストには,メトリクスグラフにおいて選択されたクローンクラスの一覧が表示されます. このクローンクラスリストでは,各メトリクス値に基づいてクローンクラスを昇順,降順にソート可能です. クローンユニット選択チェックボックス
20
コードフラグメントリスト ソースコードビュー RVK変数リスト
クローンクラスリストにおいてクローンを選択すると,このようなウィンドウが立ち上がります. このウィンドウの上部には,このクローンクラスに含まれるコード片の一覧が表示されます. 各コード片について,そのコード片が含まれるファイルへのパス,ファイル内での位置,コード片のトークン数が表示されます. また,左下の部分はソースコードビューです. コードフラグメントリストにおいて選択されたコード片周辺のソースコードが表示されます. クローンになっている部分はハイライトがかかって表示されます. また,右下のリストには,クローンの外部で定義されていて,クローンの内部で使用している変数の一覧が表示されます. 各変数について,種類,変数名,使用回数が表示されます. ソースコードビュー RVK変数リスト
21
適用実験 対象プログラム Ant(627ファイル,約180,000行) ANTLR(188ファイル,約42,000行)
学生プログラム1(126ファイル,約16,500行) 学生プログラム2(70ファイル,約7,200行) それでは,本ツールを用いて行った適用実験について説明したいと思います. 適用実験はこれら4つのプログラムに対して行いました. 2018/12/5 平成15年度修士学位論文発表会
22
適用実験 Ant(概要) makeに代表されるビルドツール 入力 構造的なクローン検出には約30秒 ソースファイル数: 627個
ソースファイル数: 627個 総行数: 約18万行 構造的なクローン検出には約30秒 151個のクローンクラスを検出 実験環境 FreeBSD 4.9 CPU Xeon2.8G×2 メモリ 4G 本日は時間の関係上,Antに対しての適用実験のみについて述べたいと思います. Antとはmakeに代表されるビルドツールの1つであり,Javaで記述されております. ソースコードは627個,総行数は約18万行です. このクローン検出には約30秒かかりました. 2018/12/5 平成15年度修士学位論文発表会
23
適用実験 Ant(メソッドの抽出) 「メソッドの抽出」を適用するためにクローンを絞り込んだ条件は次の3つ 151個のうちの32個が該当した
文単位のクローンである 全てのコード片が1つのクラス内に存在する クローンの外部で定義されたローカル変数を高々1つしか使用していない 151個のうちの32個が該当した 「メソッドの抽出」を適用するために,検出したクローンを以下の条件で絞り込みました. まず,文単位のクローンである,2つ目がすべてのコード片が1つのクラス内に存在する,最後の条件が,クローンの外部で定義されたローカル変数を高々1つしか使用していないです. これらの条件で,151個のクローンを絞り込んだところ,32個のクローンが該当しました. 2018/12/5 平成15年度修士学位論文発表会
24
適用実験 Ant(メソッドの抽出) 32個の内訳は以下の通り 分類 数 抽出のみ 3個 外部定義の変数を引数として抽出 18個
if (iSaveMenuItem == null) { try { iSaveMenuItem = new MenuItem(); iSaveMenuItem.setLabel("Save BuildInfo To Repository"); } catch (Throwable iExc) { handleException(iExc); } } 変数への代入 // javacopts if (javacopts != null && !javacopts.equals("")) { genicTask.createArg().setValue("-javacopts"); genicTask.createArg().setLine(javacopts); } ローカル変数 if (!isChecked()) { // make sure we don't have a circular reference here Stack stk = new Stack(); stk.push(this); dieOnCircularReference(stk, getProject()); } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } 32個の内訳は以下の通り 分類 数 抽出のみ 3個 外部定義の変数を引数として抽出 18個 引数とした変数を返り値として返す 7個 その他 4個 32個の内訳は以下の通りです. 単純にクローンになっている部分を抽出するのみで,リファクタリングできたものは3個でした. また,外部定義の変数を,新しく定義するメソッドの引数として抽出したのは,18個でした. さらに,引数とした変数を返り値として返す必要のあったものは7個でした. また,集約にその他の工夫が必要であるものは,4つでした. 2018/12/5 平成15年度修士学位論文発表会
25
まとめと今後の課題 まとめ 今後の課題 大規模ソフトウェアに対しても実用時間で適用可能なリファクタリング支援手法を提案しツールを試作した
実際のソフトウェアに対して適用実験を行い,絞り込んだうちの多くのクローンについて集約を行った 今後の課題 集約が困難であったクローンを特徴付ける 他の手法との比較 では,まとめと今後の課題について述べます. 本研究では,大規模ソフトウェアに対しても実用時間で適用可能なリファクタリング支援手法を提案し,ツールを試作しました. そして,実際に使われているソフトウェアに対して適用実験を行い,絞り込みを行ったクローンに対して集約を行いました. 今後は,適用実験で集約が困難であったクローンを特徴付け,他のクローンとの区別ができるようにする. また,他の手法との比較が考えられます. 2018/12/5 平成15年度修士学位論文発表会
26
デモンストレーション 学生プログラム2(70ファイル,約7,200行)に対して行う ツールの解析部にソースコードを入力
ツールのGUI部に得られたクローン情報を入力 2018/12/5 平成15年度修士学位論文発表会
27
研究計画(1/2) より多くのリファクタリングパターンに適用できるようにする
ある粒度のクローンによって、それよりも大きい粒度の類似度が表現できる 例 メソッド単位のクローンを用いてクラス単位の類似度を測る 親クラスの抽出などでリファクタリングできる 複数のパターンを組み合わせて用いる 新しいパターンの提案 Class A methodA() methodB() methodC() Class B methodD() New Class では,後期課程進学後の研究計画について簡単に述べたいと思います. まず,行おうと考えていることは,本研究の提案手法を,より多くのリファクタリングパターンに適用できるようにすることです. 例えば,ある粒度のクローンによって,それよりも大きい粒度の類似度が表現できると考えています. 例えば,メソッド単位のクローンを用いて,クラス単位の類似度を測り,親クラスの抽出などを行えると考えています. この図に示すように... また,複数のパターンを組み合わせて,適用したり,新しいパターンの提案などもできればと考えています. 2018/12/5 平成15年度修士学位論文発表会
28
研究計画(2/2) ソフトウェアの品質・複雑度などの視点からそのクローンの集約を行うべきかを評価 ソフトウェアの品質・複雑度の評価
集約の前後での品質・複雑度を定量的に求める その前後の値を比較することなどによりその集約の効果を評価する ソフトウェアの品質・複雑度の評価 そのソフトウェアは品質改善の必要があるか もしあるのなら,どの部分をリファクタリングするのか 次に,ソフトウェアの品質・複雑度などの視点からそのクローンの集約を行うべきかを評価したいと考えています. 集約の前後での品質・複雑度を定量的に求め,その値を比較することなどにより,その集約の効果を評価できるのではないかと考えています. さらに,クローンの集約に限ったリファクタリングではなく,品質・複雑度の評価を行い,そのソフトウェアにはリファクタリングを行うべきか,行うべきならどの部分に対して行うのかなどの研究を行おうと考えています. 2018/12/5 平成15年度修士学位論文発表会
29
2018/12/5 平成15年度修士学位論文発表会
30
コードクローン検出ツール CCFinder(処理概要)
Source files 字句解析 変換処理 トークン列 検出処理 変換後トークン列 クローン情報 出力整形処理 クローンペア位置情報 1. static void foo() throws RESyntaxException { 2. String a[] = new String [] { "123,400", "abc", "orange 100" }; 3. org.apache.regexp.RE pat = new org.apache.regexp.RE("[0-9,]+"); 4. int sum = 0; 5. for (int i = 0; i < a.length; ++i) if (pat.match(a[i])) sum += Sample.parseNumber(pat.getParen(0)); 8. System.out.println("sum = " + sum); 9. } 10. static void goo(String [] a) throws RESyntaxException { RE exp = new RE("[0-9,]+"); int sum = 0; for (int i = 0; i < a.length; ++i) if (exp.match(a[i])) sum += parseNumber(exp.getParen(0)); System.out.println("sum = " + sum); 17. } 字句解析 変換処理 トークン列 検出処理 変換後トークン列 クローン情報 出力整形処理 字句解析 変換処理 トークン列 検出処理 変換後トークン列 クローン情報 出力整形処理 字句解析 変換処理 トークン列 検出処理 変換後トークン列 クローン情報 出力整形処理 1. static void foo() throws RESyntaxException { 2. String a[] = new String [] { "123,400", "abc", "orange 100" }; 3. org.apache.regexp.RE pat = new org.apache.regexp.RE("[0-9,]+"); 4. int sum = 0; 5. for (int i = 0; i < a.length; ++i) if (pat.match(a[i])) sum += Sample.parseNumber(pat.getParen(0)); 8. System.out.println("sum = " + sum); 9. } 10. static void goo(String [] a) throws RESyntaxException { RE exp = new RE("[0-9,]+"); int sum = 0; for (int i = 0; i < a.length; ++i) if (exp.match(a[i])) sum += parseNumber(exp.getParen(0)); System.out.println("sum = " + sum); 17. } 次にCCFinderのコードクローン検出手順を簡単に説明します. 今回はこのソースコードを例にとって説明します. まず,CCFinderは与えられたソースコードを字句解析し,トークン単位に分割します. 次に,変換処理を行います. 変換処理ではテーブル初期化部分の正規化,ユーザ定義の変数名,メソッド名の正規化などが行われます. そして,この変換後のトークン列に対してサフィックスツリーアルゴリズムを用いて一致部分の検出を行います. この例では,この赤の下線と青の下線の部分が一致部分として検出されました. そして,最後に検出した部分を実際のソースコードにマッピングさせます. CCFinderはこのような処理を行うことでソースコード中の類似部分を検出しています. 2018/12/5 平成15年度修士学位論文発表会
31
適用実験 Ant(「メソッドの抽出」) if (errorProperty != null) { errorBaos = new ByteArrayOutputStream(); managingTask.log("Error redirected to property: " + errorProperty, Project.MSG_VERBOSE); if (error == null) { errorStream = errorBaos; } else { errorStream = new TeeOutputStream(errorStream, errorBaos); } } else { errorBaos = null; } 2018/12/5 平成15年度修士学位論文発表会
32
適用実験 Ant(「メソッドの抽出」) if (append != null) { if (!append.isAbsolute()) { append = new File(getProject().getBaseDir(), append.getPath()); } appendReader = new BufferedReader(new FileReader(append)); } 2018/12/5 平成15年度修士学位論文発表会
33
適用実験 Ant(「メソッドの抽出」) if (className != null) { try { // load the specified Cache, save the reference and configure it cache = (Cache) Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); } } 2018/12/5 平成15年度修士学位論文発表会
34
適用実験 Ant(メソッドの引き上げ) 「メソッドの引き上げ」を適用するためにクローンを絞り込んだ条件は次の2つ
メソッド単位のクローンである クローンの存在するクラスが共通の親クラスを持つ 151個のうちの20個が該当した 「メソッドの抽出」を適用するために,検出したクローンを以下の条件で絞り込みました. まず,文単位のクローンである,2つ目がすべてのコード片が1つのクラス内に存在する,最後の条件が,クローンの外部で定義されたローカル変数を高々1つしか使用していないです. これらの条件で,151個のクローンを絞り込んだところ,32個のクローンが該当しました. 2018/12/5 平成15年度修士学位論文発表会
35
適用実験 Ant(メソッドの引き上げ) 20個の内訳は以下の通り 分類 数 引き上げのみ 0個 外部定義の変数を引数として引き上げ 10個
引数にした変数を返り値として返す 2個 その他 8個 2018/12/5 平成15年度修士学位論文発表会
36
適用実験 Ant(メソッドの引き上げ) private void getCommentFileCommand(Commandline cmd) { if (getCommentFile() != null) { /* Had to make two separate commands here because if a space is inserted between the flag and the value, it is treated as a Windows filename with a space and it is enclosed in double quotes ("). This breaks clearcase. */ cmd.createArgument().setValue(FLAG_COMMENTFILE); cmd.createArgument().setValue(getCommentFile()); } } 自クラスへのメソッド呼び出し 自クラスへのフィールド変数 2018/12/5 平成15年度修士学位論文発表会
37
適用実験 Ant(メソッドの引き上げ) public void verifySettings() { if (targetdir == null) { setError("The targetdir attribute is required."); } if (mapperElement == null) { map = new IdentityMapper(); } else { map = mapperElement.getImplementation(); } if (map == null) { setError("Could not set <mapper> element."); } } 自クラスへのフィールドへの代入 2018/12/5 平成15年度修士学位論文発表会
38
適用実験 Ant(メソッドの引き上げ) 自クラスのメソッド呼び出し 2018/12/5 平成15年度修士学位論文発表会
public void execute() throws BuildException { Commandline commandLine = new Commandline(); Project aProj = getProject(); int result = 0; // Default the viewpath to basedir if it is not specified if (getViewPath() == null) { setViewPath(aProj.getBaseDir().getPath()); } // build the command line from what we got. the format is // cleartool checkin [options...] [viewpath ...] // as specified in the CLEARTOOL.EXE help commandLine.setExecutable(getClearToolCommand()); commandLine.createArgument().setValue(COMMAND_CHECKIN); checkOptions(commandLine); result = run(commandLine); if (Execute.isFailure(result)) { String msg = "Failed executing: " + commandLine.toString(); throw new BuildException(msg, getLocation()); } } 自クラスのメソッド呼び出し 2018/12/5 平成15年度修士学位論文発表会
39
リファクタリング支援ツール Cancer(計算コスト)
O(nt) n: ソースコードのトークン数 t: 検出した最も長いクローンのトークン数 解析部 クローン情報 構造的なクローン抽出 メトリクス値の計算 コードクローン検出 ソースコード O(cs log c) c: ファイル1つあたりに含まれるコードクローンの数 s: ソースファイル数 構造的なまとまりの抽出 クラス階層情報の抽出 変数定義情報の抽出 メトリクス情報のついた構造的なクローン情報 構造情報 O(n) n: ソースコードのトークン数 2018/12/5 平成15年度修士学位論文発表会
40
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. → A common path means a clone Suffix tree is a tree that satisfies three conditions. Condition 1 is that a leaf node represents the starting position of sub-string. Condition 2 is that a path from root node to a leaf node represents a sub-string. Condition 3 is that first characters of labels of all the edges from one node are different from each other. Therefore, a common path means a clone pair. For example, This figure is a suffix-tree for this string “xxyxyz “. Next this path from root node to this leaf node represents the string “xyxyz “, and this sub-string starting from number 2 is also “xyxyz “. So, this leaf has the number 2. The path from root node to this leaf node represents the string “xyz “, and this sub-string starting from number 4 is also “xyz “. The common path between them is “xy “. Then this sub-string “xy “ is detected as a code clone. Also in this scatter plot, that “xy “ is shown here.
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.