抽象構文木を利用した
API の後方互換性が破壊される変更の検出
入山 優1,a)

肥後 芳樹1,b)

楠本 真二1,c)

概要：ライブラリが更新されると，API も変更される．API の変更は様々で，後方互換性を壊す変更と後方互換性を維持する変更に分類できる．API の変更を検出し，その変更によって API の後方互換性が維持されているかを判別することはコードレビューやリリースノートの作成に役立つ．ライブラリから API の変更を手動で調べるには負担が大きいため，その負担の軽減を目的とした API の変更の自動検出に関する研究が行われている．API の変更を自動で検出し，後方互換性を壊す変更と後方互換性を維持する変更に分類するツールとして APIDiﬀ がある．APIDiﬀ は Java ライブラリの 2 つのバージョンを入力として受け取り，コードの類似度に基づいて API の変更を検出し，各変更を後方互換性を壊す変更と後方互換性を維持する変更に分類する．しかし，コードの類似度の閾値を適切に設定することは難しく，APIDiﬀ はリファクタリングとして分類すべき API の変更を誤って分類する場合がある．また検出した変更を変更の種類ごとに後方互換性を壊す変更なのかを判別しているため，ある API に対して複数の変更が行われるとそれらの変更によって API の後方互換性が維持されているかを正しく判別できない場合がある．そこで提案手法では，コードの類似度の閾値に依存せず抽象構文木を用いてリファクタリングを検出する
RefactoringMiner を利用し，API の変更を検出する．そして API ごとに変更をグループ化して後方互換性を壊す変更が含まれているかを調べることで，それらの変更によって API の後方互換性が維持されているかを判別する．8 個のオープンソースソフトウェアに対して実験を行った結果，既存手法と比べて APIの変更をより高い精度で検出できることを確認した．また 1 つの API に対して後方互換性を維持する変更と後方互換性を壊す変更が行われた場合でも，それらの変更によって API の後方互換性が維持されているかを判別できることを確認した．

1. はじめにその負担の軽減を目的とした API の変更の自動検出に関する研究が行われている．API の変更を自動で検出し分類す多くのソフトウェアにおいて生産性の向上のためライブるツールとして，APIDiﬀ [5] が提案されている．APIDiﬀ

ラリが活用されている [1, 2]．ライブラリは，アプリケー

は Java ライブラリの 2 つのバージョンを入力として受けション・プログラミング・インターフェース（以下 API）を取ると，そのバージョン間で適用された API の変更操作

介して機能を提供している．ライブラリが更新されると，のリストを出力する．APIDiﬀ は RefDiﬀ [6] と呼ばれるリAPI も変更される場合がある．API の変更は新機能の追加

ファクタリング検出ツールを利用している．RefDiﬀ はコーや不必要な機能の削除， 保守性の向上を目的としたリファドの類似度に基づいて 2 つのバージョン間で適用されたリクタリングなど様々である [3]．これらの変更は後方互換

ファクタリング操作のリストを出力する．APIDiﬀ はバー

性を壊す変更と後方互換性を維持する変更に分類が可能でジョンごとの API を検出し，変更前後の API の比較結果

ある [3]．API の変更を検出し，その変更によって API のと RefDiﬀ によって得られるリファクタリング操作のリス

後方互換性が維持されているかを判別することはコードレ

トをもとに，API の変更を検出して後方互換性を壊す変更

ビューやリリースノートの作成に役立つ [4]．

と後方互換性を維持する変更に分類する．

API の変更を手動で検出することは負担が大きいため，
1

a)
b)
c)

大阪大学大学院情報科学研究科
Graduate School of Information Science and Technology, Osaka University
m-iriyam@ist.osaka-u.ac.jp
higo@ist.osaka-u.ac.jp
kusumoto@ist.osaka-u.ac.jp

この APIDiﬀ を利用してさまざまな研究が行われている．例えばライブラリの安定性 [7]，API の後方互換性を壊す変更がクライアントに与える影響 [7]，開発者が APIの後方互換性を壊す変更を行なった理由 [8]，API の後方互換性を壊す変更の危険性に対する開発者の認識 [9] など

を明らかにするための研究が行われている．

るためのインターフェースである．本研究では Java におしかし，APIDiﬀ には課題点がある．RefDiﬀ においてける，アクセスレベルが public または protected なクラコードの類似度の閾値を適切に設定することは難しく，

ス，メソッド，フィールドを API とみなす．API を利用す

APIDiﬀ はメソッドのパラメータリストの変更やフィールる際に考慮すべき情報としてクラス名やメソッド名，シグド名の変更といったリファクタリングとして分類すべき

ネチャ，戻り値の型，修飾子，親クラス，例外などがあげ

API の変更を API の削除および API の追加として誤ってられる．本研究ではこれの情報を含めて API と見なす．メ

分類する場合がある．また検出した変更を変更の種類ごと

ソッドの本体の文がどのように実装されているか，どのよに後方互換性を壊す変更なのかを判別しているため，あるうなローカル変数を利用しているのかといった情報は API

API に対して複数の変更が行われるとそれらの変更によっ

利用時に考慮する必要がないため，これらの情報は API とて API の後方互換性が維持されているかを正しく判別でして含めない．

きない場合がある．その結果，開発者や API の利用者がAPI の変更に対して誤った認識を持つ可能性がある．

2.2 API の変更

そこで本研究は，コードの類似度の閾値に依存しない方本研究ではクラス名やシグネチャ，戻り値の型，修飾子

法を用いて API の変更をより高い精度で検出すること，そなど API を利用する際に考慮すべき情報の変更に加えてしてある API に対して複数の変更が行われた場合でもそ新しい API の追加や既存 API の削除を API の変更とみなれらの変更によって API の後方互換性が維持されている

す．バグ修正によるメソッド本体の文の変更やローカル変かどうかを正しく判別することを目的とする．RefDiﬀ で数の変数名の変更などは API 利用時に考慮する必要がな

はなく RefactoringMiner [10] を用いて，API の変更を検いため API の変更として扱わない．

出し，API ごとに変更をグループ化することでそれらの変更によって後方互換性が維持されているかどうかを判別す

2.3 API の後方互換性る手法を提案する．RefactoringMiner は Java ライブラリ

本研究ではライブラリを更新したときに呼び出し側のの 2 つのバージョンを入力として受け取ると，抽象構文木

コードの変更が一切不要である場合，新しいバージョンの（以下 AST）を生成して文単位でマッチングを行い，バー

API はその前のバージョンの API と後方互換性を持つとジョン間で適用されたリファクタリング操作のリストをし，コードの変更が必要な場合は後方互換性を持たないと出力する．コードの類似度を計算し閾値より大きいかどうする．public から protected へメソッドのアクセスレベ

かで一部が異なる文をマッチングさせるのではなく，3 段

ルが変更されたとき，そのメソッドが所属するクラスを継階で構成されるマッチングを行うことで，一部が異なる文承したクラスからメソッドを利用していた場合はコードのでもマッチングさせる．また AST の対応づけにおいて類変更が不要だが，そうでない場合はコードの変更が必要で似度を計算したり閾値と比較したりせずに，マッチングにある．このように呼び出し側の実装によってコードの変更

成功した文の数と失敗した文の数の比較を行う．そのためが必要かどうかが変わる場合，新しいバージョンの API

コードの類似度の閾値に依存せずにリファクタリングをはその前のバージョンの API と後方互換性を持たないと検出できる．提案手法では変更前後の API の比較結果とする．

RefactoringMiner によって得られるリファクタリング操作のリストをもとに，API の変更を検出する．そして API

2.4 RefDiﬀ

ごとに変更をグループ化して後方互換性を壊す変更が含ま

RefDiﬀ とはリファクタリングを検出するツールの 1 つれているかを調べることで，それらの変更によって後方互である [6]．Java ライブラリの 2 つのバージョンを入力と換性が維持されているかを判別する．8 個のオープンソー

して受け取ると，コードをトークンに分解し，出現頻度がスソフトウェア（以下 OSS）に対して実験を行った結果，低いトークンの重みが大きくなるように各トークンの重既存手法と比べて API の変更をより高い精度で検出できみを設定する．トークンごとに変更前の重みと変更後の重ることを確認した．また 1 つの API に対して後方互換性

みを比較し，2 つのうち大きい重みの合計に対する小さいを維持する変更と後方互換性を壊す変更が行われた場合で重みの合計の比を類似度として算出する．算出した類似度も，それらの変更によって API の後方互換性が維持されてが閾値より大きければバージョン間でクラス，メソッド，

いるかを判別できることを確認した．

フィールドを対応づける．対応づけの結果を元にリファク

2. 準備
2.1 API
API とはアプリケーション・プログラミング・インターフェースの略称で，外部ライブラリが持つ機能を使用す

タリング操作のリストを生成する．類似度の閾値は，10 個の OSS からランダムに選択された 10 個のコミットに対する実験結果をもとに決定されている．

2.5 APIDiﬀ における API の変更の検出および分類方法
APIDiﬀ は Java ライブラリの 2 つのバージョンを入力として受け取り，RefDiﬀ を用いてそのバージョン間で APIに対して行われた変更操作のリストを出力する [5]．APIの変更の検出と分類は次の 5 つのステップで構成される．

Step 1 クラス，メソッド，フィールドの抽出
Step 2 類似度の計算によるリファクタリングの検出
Step 3 クラス，メソッド，フィールドの抽出
Step 4 バージョン間で API をマッピング

変更後

変更前
Sub.java

Sub.java

public class Sub extends Super{
private int y;public int getX(){
return x;}．．．
}

public class Sub extends Super{
private int y;・・・}

Super.java

Super.java

public class Super{protected int x;
・・・}

public class Super{protected int x;
protected int getX(){
return x;}
・・・}

Step 5 API の変更の検出
Step 6 後方互換性を壊す変更かどうかを判別
Step 1 および Step 2 は RefDiﬀ によって行われる処理で⚫ クラスSubからメソッドgetX()を削除⚫ クラスSuperからメソッドgetX()を追加ある．Step 1 では APIDiﬀ の入力として与えられた Java(a) APIDiﬀ の出力

ライブラリの 2 つのバージョンを RefDiﬀ の入力として与え，バージョンごとにクラス，メソッド，フィールドを抽

変更後

変更前

出する．Step 2 ではコードの類似度を計算し，バージョン

Sub.java

間で行われたリファクタリング操作のリストを得る．この

public class Sub extends Super{
private int y;public int getX(){
return x;}．．．
}

リファクタリング操作のリストには API に対するリファク
タリングだけでなくプライベートメソッドのメソッド名の変更のように API 以外に対するリファクタリングも含まれSub.java
public class Sub extends Super{
private int y;・・・}ている．そのため API 以外に対するリファクタリング操作

Super.java

Super.java

をフィルタリングする．Step 3 では APIDiﬀ の入力としてpublic class Super{protected int x;
・・・}

public class Super{protected int x;
protected int getX(){
return x;}
・・・}

与えられた Java ライブラリの 2 つのバージョンからバー
ジョンごとにクラス，メソッド，フィールドを抽出する．

Step 1 のクラス，メソッド，フィールドの抽出は RefDiﬀ
内部で行われる処理であるのに対して，Step 3 の処理は

⚫ クラスSubからクラスSuperへメソッドgetX()をプルアップ⚫ メソッドgetX()のアクセスレベルをpublicからprotectedに変更

APIDiﬀ 内部で行われる処理である．Step 4 ではバージョ(b) 実際の API の変更操作

ン間での API の比較および Step 2 で得られたリファクタ

図 1

実際の API の変更操作と APIDiﬀ の出力が異なる例

リング操作のリストを元に API をマッピングする．まず 2つのバージョン間で，クラスについては完全限定名が一致するクラスを，メソッドについてはそのメソッドが所属す

3. 研究目的るクラスの完全限定名とメソッド名，パラメータの並び 3

APIDiﬀ は RefDiﬀ によって得られるリファクタリング操

つ全てが一致するメソッドを，フィールドについてはその作のリストをもとに API をリファクタリングされた API，

フィールドが所属するクラスの完全限定名とフィールド名削除された API，追加された API に分類している．しかが一致するフィールドを対応づける．対応づけることがでしコードの類似度の閾値を適切に設定することは難しいた

きなかった API をリファクタリング操作のリストをもとにめ，RefDiﬀ で検出および分類できないリファクタリング

リファクタリングされた API，削除された API，追加されが存在する．そのため RefDiﬀ が API に対するリファクタ

た API に分類する．Step 5 では，Step 4 で行われた API

リングを検出および分類できなかった場合は，そのリファのマッピングの結果と API の修飾子やアノテーションな

クタリングされた API を削除された API または追加されどを参照し，どの API が削除・追加・リファクタリングさた API として分類してしまう．また APIDiﬀ は変更を主れたのか，どの API が非推奨化されたのか、どの API の体として考え，検出した変更を後方互換性を壊す変更と後

可視性修飾子が変更されたのかといった情報を得る．Step

方互換性を維持する変更に分類している．API に対して複6 では検出した変更を変更の種類ごとに後方互換性を壊す数の変更が行われる場合, 一部の変更は後方互換性を壊す

変更と後方互換性を維持する変更に分類し，変更の種類や変更として分類されるにもかかわらず，他の変更は後方互

変更前後の API，後方互換性を壊す変更かどうかの判別結換性を維持する変更として分類されるため，それらの変更

果などの情報を参照可能な API の変更操作のリストを作によって API の後方互換性が維持されているかを正しく成する．

判別できない．
図 1 に実際の API の変更操作と APIDiﬀ の出力が異な

変更前のコード

public void
public class Sub extends Super{
resetNumMin(boolean b){
private int y;
if (b){
public int getX(){x = 0;
return x;}}y = 0;．．．
numMin = 0;
}
}

変更前の
AST

変更後

変更前
Sub.java

Sub.javaroot
public class Sub extends Super{
private int y;
代入文
If文
・・・
}
then文
条件式
y
=
0

代入文
numMin

=

x

=

条件式

then文

b

代入文

0

x

Super.java

public class Super{
private int x;
・・・}

public class Super{
private int x;
変更後のprotected int getX(){ AST
return x;
root
}
・・・
}
If文

代入文

If文
0

代入文

b

Super.java

変更後のコード

root

=

y

=

代入文
numMin0=00

文単位でマッチング

public void
resetMinNum(boolean b){
代入文
if (b){
x = 0;
then文
minNum=0
条件式
y = 0;⚫ クラスSubからクラスSuperへメソッドgetX()をプルアップ}⚫ メソッドgetX()のアクセスレベルをpublicからprotectedに変更
代入文
代入文
b
minNum = 0;
}
y
=
0
x
=
0

root
代入文

If文
条件式

then文

b

代入文
x

=

minNum

=

0

代入文
0

y

=

0

⚫ 各ノードおよび深さが一致する部分木⚫ 各ノードは一致するが深さが異なる部分木⚫ 一部ノードが異なる部分木

図 3
メソッドgetY()• 非推奨化（互換性を維持する変更）• アクセスレベルをpublicからprotectedに変更（互換性が失われる変更）

RefactoringMiner の概要+ @Deprecated- public int getY(){+ protected int getY(){
return y;}
図 2 複数の変更が同時に行われた API の例

そこで，本研究では類似度の閾値に依存しない方法でAPI の変更をより高い精度で検出する手法を提案する．そしてある API に対して複数の変更が行われた場合でもそれらの変更によって API の後方互換性が維持されているかを判別する手法を提案する．

4. 提案手法
る例を示す．変更前後で APIDiﬀ を用いて得られる変更
操作は，変更前のクラス Sub のメソッド getX() の削除，
変更後のクラス Super のメソッド getX() の追加となる(図 1(a))．しかし，実際に行われた API の変更操作はクラス Sub からクラス Super へメソッド getX() のプルアップおよび public から protected にアクセスレベルの変更である (図 1(b))．この違いは，RefDiﬀ の類似度の閾値が高く設定されていることが原因であり，APIDiﬀ はメソッ
ドのプルアップとして検出することができない．クラス

Sub を継承したクラスからメソッド getX() を利用していた API 利用者は，コードを変更せずに API を利用し続けることができるにもかかわらず，クラス Sub のメソッド

getX() が削除されたため同様の機能を持つ API を探す必要があると誤った認識を持ち，生産性が下がってしまうおそれがある．
また後方互換性を維持する変更と後方互換性を壊す変更が同時に行われた API の例を図 2 を示す．メソッド getY()
に対してメソッドの非推奨化と public から protectedにアクセスレベルの変更が行われている．APIDiﬀ は非推奨化を後方互換性を維持する変更として，public から

protected への変更を後方互換性を壊す変更として分類す
る．1 つの API に対して後方互換性を壊す変更と後方互換性を維持する変更が行われており，それらの変更によってAPI の後方互換性が維持されているかを判別するには粒度が細かい．

提案手法では，AST の文単位のマッチングによりリファ
クタリングを検出する RefactoringMiner を利用し，API の変更を検出する．これによってコードの類似度の閾値に依存せず，API の変更を分類することが可能である．そしてAPI を主体として考え，API ごとに変更をグループ化し，後方互換性を壊す変更が含まれているかを調べることで，それらの変更によって API の後方互換性が維持されているかを判別する．

4.1 RefactoringMiner
図 3 のように RefactoringMiner は Java ライブラリの 2つのバージョンを入力として受け取ると，クラス、メソッ
ド、フィールドを抽出する．メソッドの AST を生成してバージョン間で部分木を文単位でマッチングし，バージョン間で適用されたリファクタリング操作のリストを出力す
る．文単位のマッチングは 3 段階で行われる．1 段階目は各ノードおよび深さが一致する部分木をマッチングさせる．
葉ノードについては変数名や定数などの値を比較し，一致すれば同じノードとしてみなす．それ以外のノードについては if 文や代入文などラベルが一致すれば，同じノードとみなす．2 段階目は文の移動などに対応するために各ノー
ドは一致するが深さが異なる部分木をマッチングさせる．

3 段階目は変数名や型変更などに対応するために一部ノードが異なる部分木をマッチングさせる．このマッチングでStep 1
クラス，メソッド，フィールドの抽出

Step 2
ASTのマッチングによるリファクタリングの検出

Step 3
バージョン間でAPIをマッピング

Step 4マッピングされたAPIごとにAPIの変更を検出

入力
変更前

クラス1
- フィールドa- メソッドb
クラス2(クラス1を継承)- メソッドc

クラス1- フィールドa- メソッドb
クラス2(クラス1を継承)- メソッドc

メソッドcのプルアップ
クラス1からクラス2へ
・・・

変更後
クラス1
- フィールドa- メソッドcクラス2(クラス1を継承)- メソッドd

変更前後の
ライブラリ

リファクタリング操作のリスト

クラス1- フィールドa- メソッドcクラス2(クラス1を継承)- メソッドdフィールドa- 非推奨化
メソッドb
- 削除
メソッドc
- クラス2からクラス1へプルアップ- アクセスレベルをpublicからprotectedに変更
メソッドd
- 追加
マッピングされたAPIごとのAPIの変更操作のリスト

Step 5
APIの後方互換性が維持されているかを判別

マッピングされたAPI

抽出した
クラス，メソッド，フィールド⚫ リファクタリングされたAPI⚫ 変更前後でマッピングされたAPI⚫ マッピングできなかったAPI

出力フィールドaの非推奨化(互換性は維持される)
メソッドbの削除(互換性は破壊される)
メソッドcをクラス2からクラス1へプルアップ(互換性は破壊される)
メソッドcのアクセスレベルをpublicからprotectedに変更(互換性は破壊される)
メソッドdの追加(互換性は維持される)
・・・

APIの変更操作のリスト

図 4

提案手法の概要

は部分木の深さ考慮しない．マッチングに成功した文の数

既存手法と大きく異なる点は 3 つある．1 つ目は，Stepが失敗した文の数よりも多ければ AST を比較した 2 つの2 でコードの類似度の計算によってリファクタリングを検メソッドを変更前後のメソッドとして対応づける．マッチ

出するのではなく，AST のマッチングによってリファクタングに成功したメソッドの組み合わせが複数ある場合，一リングを検出する点である．これによりコードの類似度に致した文の数や完全一致した文の数などでソートし，もっ依存せずに API の変更を検出できる．2 つ目は，既存手法

とも値が高い組み合わせを変更前後の組み合わせとみなす．

の Step 3 で APIDiﬀ の内部でもクラス，メソッド，フィーそして変更前後のメソッドをその後に行うマッチング対象

ルドを抽出し，その結果を利用して API をバージョン間でのメソッドから除外することで，処理量が膨れ上がらない

マッピングしているのに対して，提案手法では実行時間のようにマッチングの回数を減らす．このマッチングの結果

短縮のため Step 1 で抽出したクラス，メソッド，フィールを元にリファクタリング操作のリストを生成する．3 段階

ドを再利用している点である．3 つ目は，既存手法においで構成されるマッチングを行うことで，一部が異なる文のて他の変更を考慮せずに変更の種類ごとに後方互換性を壊

マッチングでも文の類似度を計算したり閾値と比較したりす変更なのかどうかを判別していたのに対して，提案手法する必要がない．AST の対応づけではマッチングに成功

では Step 4 でマッピングされた API ごとに API の変更をした文の数とマッチングに失敗した文の数を利用し，コー

検出し，Step 5 でマッピングされた API ごとに後方互換

ドの類似度の計算や閾値との比較は行わない．そのため類性を壊す変更が含まれているかを調べることで API の後

似度の閾値に依存せずにリファクタリングを検出できる．

方互換性が維持されているかを判別している点である. こRefactoringMiner は RefDiﬀ が検出可能な変更の種類に加れにより API に対して複数の変更が行われた場合でも，そえて変数名の変更やメソッドの戻り値の型変更，フィール

れらの変更によって API の後方互換性が維持されている

ドの型変更など様々な種類が検出できる．

かを判別できる．
提案手法では既存手法と同様に Java ライブラリの 2 つ

4.2 API の変更の検出および分類方法のバージョンを入力として受け取り，そのバージョン間で提案手法において API の変更の検出と分類は次の 5 つ

API に対して行われた変更操作のリストを出力する．Stepのステップで構成される．提案手法の概要を図 4 に示す．

1 では提案手法の入力として与えられた Java ライブラリ

Step 1 クラス，メソッド，フィールドの抽出の 2 つのバージョンを RefactoringMiner の入力として与Step 2 AST のマッチングによるリファクタリングの検出

え，バージョンごとにクラス，メソッド，フィールドを抽Step 3 バージョン間で API をマッピング

出する．論文 [10] では RefactoringMiner のバージョンは

Step 4 マッピングされた API ごとに API の変更を検出

2.0 だが，その後，著者らによってサポートするリファク

Step 5 API の後方互換性が維持されているかを判別

タリングが拡張された 2.1 が公開されており，提案手法では 2.1 を利用した．Step 2 では AST を文単位でマッチングすることで，バージョン間で行われたリファクタリング
操作のリストを得る．このリファクタリング操作のリスト

5.1 評価項目提案手法を評価するために 4 つの評価項目を設定した．
項目 1：API に対する変更の検出数には API に対するリファクタリング操作だけでなく，ロー提案手法と既存手法からそれぞれ得られる検出結果を比カル変数の変数名の変更やプライベートメソッドのメソッ

較し，API に対する変更の検出数にどのような変化が見

ド名の変更など API 以外に対するリファクタリング操作られるのかを調べる．また変更の種類別に検出結果を比較も含まれている．そのため API 以外に対するリファクタ

し，どのような変化が見られるかを調べる．

リング操作をフィルタリングする．Step 1 および Step 2

項目 2：検出した変更の適合率

は RefactoringMiner によって行われる処理である．Step

提案手法によって API の変更を既存手法より高い精度

3 では既存手法と同様にバージョン間で API をマッピンで検出できるかどうかを検証する．提案手法によって改善グする．ただし変更前後の API の比較において，メソッが期待される変更はリファクタリングや修飾子の変更など

ドについてはそのメソッドが所属するクラスの完全限定名であるため，検出された変更が正しいのかを確認する．

とメソッド名，パラメータの並びに戻り値の型を加えた 4

項目 3：後方互換性が維持されるかどうかの判別精度

種類全てが一致するメソッドを，フィールドについてはそその変更によって後方互換性が維持されているかどうかのフィールドが所属するクラスの完全限定名とフィールドの判別結果が正しいのかを確認する．提案手法では 1 つの名にフィールドの型を加えた 3 種類全てが一致するフィー

API に対して後方互換性を壊す変更と後方互換性を維持す

ルドを対応づけるように変更した．これは既存手法のようる変更が行われた場合に，後方互換性を壊す変更と後方互に変更前後で対応づけたメソッドの戻り値の型やフィール

換性を維持する変更の両変更によって API の後方互換性

ドの型を比較することでメソッドの戻り値の型やフィールが壊されたと判別される．そのため既存手法で常に後方互

ドの型の変更を検出するのではなく，RefactoringMiner に換性を維持する変更として分類される変更のうち，その変よってメソッドの戻り値の変更やフィールドの型の変更

更によって API の後方互換性が壊されたと判別された変を検出し，そのリファクタリング操作をもとに API をリ

更が正しいのかを確認する．

ファクタリングされた API として分類するためである．こ項目 4：実行時間

れによって変更前後でクラスの完全限定名やメソッド名，提案手法の実行時間と既存手法の実行時間を比較し，提フィールド名が一致しない場合でも，メソッドの戻り値の案手法の処理速度を評価する．また実行時間が長くなる要型変更やフィールドの型変更を検出でききるようになっ因となる処理を確認する．

た．Step 4 では，Step 3 で行われた API のマッピングの結果と API の修飾子やアノテーションなどを参照し，APIの変更を検出する．このとき変更前後の API とその APIに対して行われた変更を対応付けることで，複数の変更を

5.2 既存手法の実装
既存手法の実装は．既存研究 [5] の著者らによって実装された [11] を利用した．

管理する．Step 5 ではマッピングされた API ごとに後方互換性が維持されているかどうかを判別する．まず他の変5.3 実験対象

更を考慮せずに各変更を変更の種類ごとに後方互換性を壊

master ブランチ上に存在する全てのコミットに対してす変更かどうか判別する．そして API ごとに，API と関

提案手法および既存手法が適用可能な OSS を選出するた連づけた変更操作の中に後方互換性を壊す変更が含まれてめに，RefactoringMiner を用いた縦断的研究 [12] の実験

いるかを確認する．後方互換性を壊す変更が含まれている対象である OSS の中から，コミット数が 20,000 以下でス場合，それらの変更によって API の後方互換性が壊されター数が多い OSS から順に 8 個の OSS を実験対象としたと判別し，後方互換性を壊す変更が含まれていない場合た．これらの OSS に対して，提案手法および既存手法がは，変更によって後方互換性が維持されていると判別する．

適用可能であることを確認した．実験対象の OSS を表 1

その後，変更の種類や変更前後の API，その変更によってAPI の後方互換性が維持されているかなどの情報を参照可
能な API の変更操作のリストを作成する．

5. 実験
本章では，提案手法を用いて行なった実験とその結果について述べる．

表 1 実験対象の OSS
プロジェクト名

LOC

コミット数

OkHttp72,696

4,839

対象の最終コミット日

Retroﬁt

26,9951,865

2021 年 2 月 24 日

MPAndroidChart

25,2322,068

2020 年 10 月 30 日

LeakCanary

26,2691,609

2021 年 4 月 8 日

Hystrix

50,5102,108

2018 年 11 月 20 日

iosched23,5502,7572020 年 6 月 26 日

Fresco97,1942,897

2021 年 4 月 29 日

Logger

1,441

144

2018 年 4 月 10 日

2021 年 4 月 22 日

に示す．コミット数に制限を設けた理由は，大規模な OSSした．

に対して提案手法，既存手法のどちらを適用しても実行時間が長くなり評価が難しいためである．種類別の検出数の5.5 項目 2：検出した変更の適合率の比較

比較と検出した変更の適合率の比較，後方互換性が維持

評価項目 2 の検出した変更の適合率について述べる．8

されるかどうかの判別精度の確認では 8 個の OSS のうち個の OSS の中から MPAndoridChart を選択し，提案手法

MPAndroidChart のみを対象とした．これは APIDiﬀ の適と既存手法からそれぞれ得られる検出結果を目視確認し適用事例でも利用されていた OSS である．

合率を比較した．提案手法のみで検出された API の変更と既存手法のみで検出された API の変更を目視確認した．提5.4 項目 1：API に対する変更の検出数の比較

案手法のみで検出された API の変更の数が多かったため，

評価項目 1 の API に対する変更の検出数について述べる．

許容誤差 5%，信頼度 95%となるように 311 個サンプリングを行った．また API の変更の種類ごとに可能な限り均等

5.4.1 全リポジトリに対しての検出数の比較
実験対象の OSS の master ブランチ*1 上に存在する全てになるようにサンプリングを行なった．既存手法のみで検のコミットに対して提案手法と既存手法を適用し，提案手出された API の変更は全て目視確認を行った．目視確認を法と既存手法からそれぞれ得られる検出結果を比較した結

行なった結果が表 3 の適合率の列である．Inline Method

果が表 2 である．既存手法では実験対象のリポジトリかと Move Method については既存手法の適合率が提案手法

ら 3,774（=2,582+1,192）個の API の変更しか検出できなの適合率より高くなったが，全体としては既存手法の適合かったのに対して，提案手法では 7,376（=2,582+4,794）

率が 50.0%であったのに対して提案手法の適合率が 90.0%

個の API の変更を検出できた．すべてのプロジェクトにと高い結果となった．また提案手法で検出した API の変更

おいて提案手法が検出した API の変更の数が，既存手法がの数と既存手法で検出した API の変更の数が等しい 4 種

検出した API の変更の数を上回った．また提案手法のみ類の変更についても適合率を求めた．両手法で検出されたが検出した API の変更の数は，提案手法または既存手法にAPI の変更を目視確認した結果が表 3 の適合率の列であ

よって検出された API の変更の総数の 30.0%〜70.6%を占る．4 種類の変更の適合率はすべて 100%となった．このめており，その平均は 54.3%であった．それに対して既存結果から両手法で検出された変更の適合率は高いと考えら手法のみが検出した API の変更の数は，提案手法またはれる．

既存手法によって検出された API の変更の総数の 2.8%〜

29.8%を占めており，その平均は 13.5%であった．

5.6 項目 3：後方互換性が維持されるかどうかの判別精度

5.4.2 種類別の検出数の比較

評価項目 3 の後方互換性が維持されるかどうかの判別精

8 個の OSS の中から MPAndoridChart を選択し，提案度について述べる．8 個の OSS の中から MPAndoridChart

手法と既存手法からそれぞれ得られる検出結果を API のを選択し，既存手法で後方互換性を維持する変更として常変更の種類別に比較した．その結果が表 3 である．提案手に分類される変更のうち，提案手法ではその変更によって法では既存手法で検出された 28（=24+4）種類の変更に

API の後方互換性が壊されたと判別された変更を目視確認

加えて，Change in Parameter List や Rename Field など

し，判別結果が正しいかを確認した．その結果を表 4 に示

新たに 9 種類の変更を検出した．また両手法で検出された

す．提案手法で API の後方互換性が壊されたと判別され28 種類の変更のうち Lost Visibility in Method 以外の 27

た変更は 61 個あり，そのうち 53 個が変更内容および判別種類に関しては提案手法は既存手法以上の数の変更を検出

結果ともに正しかった．

*1

LeakCanary 及び iosched についてはデフォルトのブランチがmain ブランチであるため，main ブランチに対して実験を行なった．
表 2
プロジェクト名

API に対する変更の検出数

両手法が検出した提案手法のみが検出した

既存手法のみが検出した

変更の数

変更の数

変更の数

OkHttp675（44.1%）

460（30.0%）

Retroﬁt243（36.5%）338（50.8%）

1,120（39.4%）

1,607（56.5%）

MPAndroidChart

合計

396（25.9%） 1,531
84（12.6%）665

116（ 4.1%） 2,843

LeakCanary

41（24.0%）79（46.2%）

51（29.8%）

Hystrix

292（24.4%）722（60.3%）183（15.3%） 1,197

iosched91（32.7%）

143（51.4%）

Fresco452（19.4%）

1,514（65.1%）

Logger合計

44（15.8%）

171278

359（15.4%） 2,325

29（26.6%）

77（70.6%）3（ 2.8%）2,582（32.3%）

4,794（54.3%）

1,192（13.5%） 8,841

109

5.7 項目 4：既存手法との実行時間の比較の抽出処理を，API の変更の検出はバージョン間での API

評価項目 4 の実行時間について述べる．実験対象の OSSのマッピングから API 変更操作のリスト作成までに要すの master ブランチ上に存在する全てのコミットに対してる処理を表している．図 5(a) は提案手法の実行時間およ提案手法と既存手法を適用し，実行時間を比較した．各処

び既存手法の実行時間がともに 5 分以内となった OSS の理の CPU 実行時間を計測するためにプロファイラを利用

結果で，それ以外の OSS の結果を図 5(b) に示している．

した．実験に用いた計算機の性能を表 5 に示す．実験結8 個の OSS のうち 5 個の OSS において提案手法の実行時

果を図 5 に示す．図 5 においてリファクタリングの検出

間は既存手法の実行時間より短くなった．残りの 3 個のは RefDiﬀ や RefactoringMiner によるクラス，メソッド，

OSS において，提案手法の実行時間が長くなった主な原因

フィールドの抽出からリファクタリングの検出までの処理

はリファクタリングの検出に要する時間が長くなったためを表している．またクラス，メソッド，フィールドの抽出である．

は APIDiﬀ 内部で行われるクラス，メソッド，フィールド

表 4 後方互換性が維持されるかどうかの判別結果

表 5

実験に用いた計算機の性能

種類

OS

macOS Big Sur

個数

判別結果が正しい変更

53

CPU

Intel Core i5（1.4GHz，4 コア）

変更内容が誤っている変更

2

GPUIntel Iris Plus Graphics 645

変更内容は正しいが，判別結果が誤っている変更

6

メモリ

16GB

表 3

API の変更の種類

検出数

種類別の検出数と適合率

両手法が検出した提案手法のみが既存手法のみがAPI の変更検出した API の変更検出した API の変更

サンプル

適合率（%）

サイズ

検出数

サンプル
サイズ

適合率（%）

検出数

サンプル

適合率（%）

サイズ

Change in Field Default Value

107

0

18

13

100

1

1

Change in Return Type Method

125

0

56

13

100

3

3

100

0

0

133

14

78.6

4

4

25.0
100

Extract Method

100

Inline Method

5

0

39

13

84.6

4

4

Lost Visibility in Method

19

0

32

13

38.5

44

44

0.0

Pull Up Method

115

0

107

14

100

20

20

100

Push Down Field

6

0

2

2

100

1

1

100

Push Down Method

28

0

29

13

100

2

2

100

Move Field

45

0

75

14

100

1

1

100

Move Method

60

0

46

13

15.4

12

12

66.7

Rename Method

147

0

67

14

100

22

22

68.1

Rename Type

27

0

2

2

100

2

2

100

Add Static Modiﬁer in Method

1

0

3

3

100

0

0

Change in Field Type

53

0

10

10

50.0

0

0

Change in Supertype

132

0

2

2

100

0

0

Deprecated Method

6

0

48

13

100

0

0

Deprecated Type

3

0

2

2

94.7

0

0

Gain Visibility in Field

43

0

35

13

73.7

0

0

Gain Visibility in Method

47

0

56

13

92.3

0

0

Gain Visibility in Type

2

0

4

4

100

0

0

Lost Visibility in Field

8

0

18

13

100

0

0

Move and Rename Type

3

0

2

2

100

0

0

Move Type

69

0

8

8

100

0

0

Pull Up Field

28

0

45

13

100

0

0

Change in Parameter List

0

0

626

14

100

0

0

Extract Field

0

0

3

3

100

0

0

Extract Subtype

0

0

2

2

100

0

0

Extract Supertype

0

0

36

13

100

0

0

Extract Type

0

0

25

13

100

0

0

Move and Rename Field

0

0

4

4

100

0

0

Move and Rename Method

0

0

32

13

100

0

0

Remove Static Modiﬁer in Method

0

0

2

2

100

0

0

Rename Field

0

0

58

13

84.6

0

0

Add Final Modiﬁer in Field

1

1

100

0

0

0

0

Add Supertype

28

28

100

0

0

0

0

Remove Final Modiﬁer in Field

5

5

100

0

0

0

0

Remove Supertype

7

7

100

0

0

0

0

1,607

311

116

116

平均

1,120

90.0

50.0

変更前

変更後

protected Entry cloneEntry() {Entry entry = new Entry(mVal, mXIndex);
return entry;}

public Entry copy() {return new Entry(mVal, mXIndex);}⚫ cloneEntry()からcopy()にリネーム

変更前

実行時間（秒）300

変更後

protected Entry cloneEntry() {Entry entry = new Entry(mVal, mXIndex);
return entry;
}

250

200
150

+

100

public Entry copy() {return new Entry(mVal, mXIndex);}

public interface OnChartGestureListener {⚫ メソッドcloneEntry()の削除
public void onChartLongPressed(MotionEvent me);⚫ メソッドcopy()の追加
void onChartLongPressed(MotionEvent me);}(a) 提案手法の出力

50
0
提 既
案 存
手 手
法 法
Retrofit

提 既
提 既
案 存
案 存
手 手
手 手
法 法
法 法
MPAndroidChart Leakcanary

提 既
案 存
手 手
法 法
Logger

変更前

変更後

protected Entry cloneEntry() {Entry entry = new Entry(mVal, mXIndex);
return entry;}

public Entry copy() {return new Entry(mVal, mXIndex);}⚫ cloneEntry()からcopy()にリネーム

APIの変更の検出
クラス，メソッド，フィールドの抽出(b) 既存手法の出力

リファクタリングの検出

変更前
変更後
図
7 既存手法のみが検出可能な変更の例(a) 実行時間が 5 分未満のプロジェクト

protected Entry cloneEntry() {Entry entry = new Entry(mVal, mXIndex);
return entry;}

public Entry copy() {return new Entry(mVal, mXIndex);}

実行時間（分）更前後で提案手法を用いて得られる変更操作は，変更前
250のメソッド cloneEntry()の削除および変更後のメソッド⚫ メソッドcopy()の追加

200

copy() となる（図 7(a)）．しかし実際に行われた API の

150

変更操作はメソッドのリネームであり，既存手法ではこの

100

変更操作を正しく検出することができる（図 7(b)）．この50

違いは，マッチングに成功した文の数とマッチングに失敗⚫ メソッドcloneEntry()の削除

0
提 既
案 存
手 手
法 法
Okhttp

提 既
案 存
手 手
法 法
Hystrix

提 既
案 存
手 手
法 法
iosched

提 既
案 存
手 手
法 法
Fresco

APIの変更の検出
クラス，メソッド，フィールドの抽出
リファクタリングの検出(b) 実行時間が 5 分以上のプロジェクト
図 5+

API の変更の検出に要する時間

public interface OnChartGestureListener {public void onChartLongPressed(MotionEvent me);void onChartLongPressed(MotionEvent me);}

図 6 Lost Visibility in Method の誤検出の例
変更前

変更後

protected Entry cloneEntry() {Entry entry = new Entry(mVal, mXIndex);
return entry;
}

6. 考察

public Entry copy() {return new Entry(mVal, mXIndex);}

表 3 において既存手法の適合率が 0%であった Lost Vis変更後

ibilityin Method
protected Entry
cloneEntry() と既存手法のみが検出可能な変更につい{public Entry copy() {Entry entry = new Entry(mVal, mXIndex);
return entry;

前後のメソッドとして対応づけることができなかったことが原因である．これを解決するためには return 文における変数 entry を代入文を用いて置換し，return 文のみをマッ
チングする．

6.3 変更内容は正しいが判別結果が誤っている変更
変更内容は正しいが判別結果が誤っている変更の例を図 8に示す．検出された変更操作は protected から public へ
アクセスレベルの変更およびパラメータリストの変更である．提案手法ではこれらの変更によって API の後方互換性が壊されたとして判別した．しかしメソッドのパラメー
タの型の変化に注目すると変更後の API は変更前の APIと後方互換性を持っていることがわかる．そのため 2 つの変更によってメソッド setData の後方互換性は維持されて⚫ cloneEntry()からcopy()にリネーム

変更前

した文の数が同じ値であるため，RefactoringMiner は変更

return new Entry(mVal, mXIndex);

いると判別すべきである．変更によって API の後方互換性が維持されているかを正しく判別できるようにするためて考察する．また表
4 において変更内容は正しいが，判別}には，継承関係を考慮して互換性を壊す変更なのかを判別

結果が誤っている変更について考察する．

する必要がある．

}⚫ メソッドcloneEntry()の削除⚫ メソッドcopy()の追加

6.1 Lost Visibility in Method

7. 妥当性の脅威

既存手法で検出した変更の例を図 6 に示す．APIDiﬀ は提案手法の分類の精度を評価するために目視確認を行メソッド onChartLongPressed(MotionEvent me) のアクなった．しかしこの結果は第一著者の主観に依存しており，

セスレベルを public から default への変更として検出す

リファクタリングでないにも関わらずリファクタリングとる．しかしこのメソッドはインタフェースのメソッドであ判断したり，リファクタリングであるにも関わらずリファ

りアクセス修飾子 public を削除してもアクセスレベルはクタリングでないと判断したりしている可能性がある．

変更されないので．誤検出としてみなすことができる．

検出数が少ない API の変更について十分な数を目視確認することができなかったため，適合率を適切に求められ6.2 既存手法のみが検出可能な変更
既存手法のみが検出可能な変更の例を図 7 に示す．変ていない可能性がある．
本実験ではアクセスレベルが public または protected++public abstract class Chart extends View implements AnimatorUpdateListener {public abstract class Chart<T extends ChartData<? extends DataSet<? extends Entry>>>
extends View implements AnimatorUpdateListener {+protected void setData(ChartData<? extends DataSet<? extends Entry>> data) {public void setData(T data) {
・・・
}
・・・
}

図 8

変更内容は正しいが判別結果が誤っている変更の例

なクラス，メソッド，フィールドを API とみなしている．
しかし実際には API として公開するためではなく内部処[5]理のためにアクセスレベルを public または protected に設定している場合がある．そのため開発者が意図しないクラスなどを除くと実験結果が変化する可能性がある．

[6]

8. おわりに本研究では，コードの類似度の閾値に依存せず AST を[7]用いてリファクタリングを検出する RefactoringMiner を利用することで API の変更を検出し，API に対して複数の変更が行われた場合でもそれらの変更によって API の後方互換性が維持されているかを判別する手法を提案した．

[8]

提案手法を用いて 8 個の OSS に対して実験を行ったところ，既存手法と比べて API の変更をより高い精度で分類できることを確認した．また 1 つの API に対して後方互換[9]性を維持する変更と後方互換性を壊す変更が行われた場合でもそれらの変更によって API の後方互換性が維持されているか判別できることを確認した．
今後の課題としては対象の OSS を増やし，検出数が少[10]ない API の変更について十分な数を目視確認することや継承関係を考慮して後方互換性を壊す変更かどうかを判別[11]することでそれらの変更によって後方互換性が維持されているかどうかの判別精度を向上させることが挙げられる．
謝辞 本研究の一部は，日本学術振興会科学研究費補助
金基盤研究（B）（課題番号：20H04166）の助成を得て行
われた．
参考文献
[1]

[2]

[3]

[4]

Moser, S. and Nierstrasz, O.: The eﬀect of objectoriented frameworks on developer productivity, Computer, Vol. 29, No. 9, pp. 45–51 (1996).
Michail, A.: Data mining library reuse patterns in userselected applications, the proceedings of IEEE International Conference on Automated Software Engineering,pp. 24–33 (1999).
Dig, D. and Johnson, R.: How do APIs evolve? A
story of refactoring, Software Maintenance and Evolution: Research and Practice, Vol. 18, No. 2, pp. 83–107(2006).
Moreno, L., Bavota, G., Penta, M. D., Oliveto, R., Marcus, A. and Canfora, G.: ARENA: An Approach for theAutomated Generation of Release Notes, IEEE Transactions on Software Engineering, Vol. 43, No. 2, pp.

[12]

106–127 (2017).
Brito, A., Xavier, L., Hora, A. and Valente, M. T.:
APIDiﬀ: Detecting API breaking changes, the proceedings of IEEE International Conference on Software
Analysis, Evolution and Reengineering, pp. 507–511(2018).
Silva, D. and Valente, M. T.: RefDiﬀ: Detecting
Refactorings in Version Histories, the proceedings ofIEEE/ACM International Conference on Mining Software Repositories, pp. 269–279 (2017).
Xavier, L., Brito, A., Hora, A. and Valente, M. T.: Historical and impact analysis of API breaking changes:
A large-scale study, the proceedings of IEEE International Conference on Software Analysis, Evolution andReengineering, pp. 138–147 (2017).
Brito, A., Xavier, L., Hora, A. and Valente, M. T.: Whyand how Java developers break APIs, the proceedings ofIEEE International Conference on Software Analysis,
Evolution and Reengineering, pp. 255–265 (2018).
Xavier, L., Hora, A. and Valente, M. T.: Why do webreak APIs? First answers from developers, the proceedings of IEEE International Conference on Software Analysis, Evolution and Reengineering, pp. 392–
396 (2017).
Tsantalis, N., Ketkar, A. and Dig, D.: RefactoringMiner
2.0, IEEE Transactions on Software Engineering, pp.
1–21 (2020).
Brito, A.: A tool to identify API breaking andnon-breaking changes between two versions of a Javalibrary. [Online]., Available: https://github.com/
aserg-ufmg/apidiff.
Cedrim, D., Garcia, A., Mongiovi, M., Gheyi, R., Sousa,
L., de Mello, R., Fonseca, B., Ribeiro, M. and Chávez,
A.: Understanding the Impact of Refactoring on Smells:
A Longitudinal Study of 23 Software Projects, the proceedings of Joint Meeting on Foundations of Software
Engineering, pp. 465–475 (2017).