パターンマッチングのための
データフローグラフ抽出器の実装
発表者: 新山 祐介 (東工大)
荒堀 喜貴 (東工大)
権藤 克彦 (東工大)
スライド: euske.github.io

1. あらまし

プログラム理解のためのグラフ表現 "FGyama" を提案。

FGyama の特徴

int max(int[] a) {
    int y = -1;
    for (int x : a) {
        if (y < x) { y = x; }
    }
    return y;
}

従来のデータフローグラフ

欠けているもの:

int max(int[] a) {
    int y = -1;
    for (int x : a) {
        if (y < x) { y = x; }
    }
    return y;
}

2. 研究の目標

プログラム中の変数に与えられる高水準な (ドメイン依存の) 意味を自動抽出したい。

a = p * 1.08;  // 税込み価格
...
b = 99;        // 残り体力
...
c = x + w/2;   // 中心座標

具体的な方法

  1. プログラムをなんらかの中間表現に変換する。
  2. 既存のコードから「辞書」を獲得しておく。
  3. 中間表現のマッチングを用いて意味を付与する。
if (c == 1) {     b = b - 10     a1 = a1 + } else if (c ==     ... GitHub

適切な中間表現とは?

  1. ロジックの細部まで表現したい。
  2. プログラム解析をグラフ解析問題として扱いたい。
  3. あわよくば、他の目的にも利用できるもの。
    • クローン検出, taint解析, etc.

なぜグラフなのか?

現在までの進捗

  1. Java → FGyama に変換。 (Eclipse JDT使用)
  2. データベースに格納。(〜 1000 projects, 74M loc)
  3. 今のところ intraprocedural。(拡張予定)

今後の予定:

  1. 変数名・コメント等から対応関係を獲得。
  2. 実際のコードに適用し、評価。

3. FGyama の構造

基本的な関数

int f(a, b, c) {
    return (-a) +
           (a + b) * c;
}

副作用がある場合

a = a + b;
x = b * c;
y = c;

条件分岐

if (x) {
    y = 1;
} else {
    y = 2;
}

繰り返し

do {
    S;  // x を変更
} while (p);

脱出・例外

do {
    S;  // a を変更。
    if (t) { break; }
} while (...);

現在の欠点: 間接参照

a[0] = 4;
a[1] = 5;
b[2] = a[0] + a[1];
ARR = 4;
ARR = 5;
ARR = ARR + ARR;

4. FGyama データベースの構築

部分グラフの検索

  1. パスに分解し、trie を検索。
  2. 各パスを含んだグラフ番号の積集合を求める。
  3. 実際のグラフを取得し絞り込む。

実験内容

対象GitHub 上位★1000 repo
ファイル480,627個 (3.7GB, 74M loc)
抽出グラフ4.3M 関数, 42M ノード
データベース11GB (FGyama) + 10GB (trie)

5. 取得したグラフの応用

a. イテレータの発見

見つかったイテレータの例:

TextUtils.java:
 47:    int start = 0;
 48:    do {
 49:        int next = query.indexOf('&', start);
 50:        int end = (next == -1) ? query.length() : next;
            ...
 65:        start = end + 1;
 66:    } while (start < query.length());
ACache.java:
728:    String saveTimeStr = strs[0];
729:    while (saveTimeStr.startsWith("0")) {
730:        saveTimeStr = saveTimeStr
731:                .substring(1, saveTimeStr.length());
732:    }

b. クローン検出

  1. ある関数と同型のグラフをもつ関数を検索。
    同型 : 共通ノードが 5個以上。
  2. 見つかった 100 コード対を人手により評価。
Aトークンレベルで一致33
Bトークレベルでほぼ一致13
C同一ロジックの実装と思われる19
Dクローンではない35
実際の出力: clones100.html

得られた非クローンの例 (1):

同一ロジックを実装している例:

public static <T> List<T> reverse_foreach(List<T> list) {
    List<T> reversed = new ArrayList<>();
    for (int i = list.size() - 1; i >= 0; i--) {
        reversed.add(list.get(i));
    }
    return reversed;
}
public static <V> List<V> invertList(List<V> sourceList) {
    List<V> invertList = new ArrayList<V>(sourceList.size());
    for (int i = sourceList.size() - 1; i >= 0; i--) {
        invertList.add(sourceList.get(i));
    }
    return invertList;
}

得られた非クローンの例 (2):

同一ロジックだが意味が異なる例:

boolean isSet(int id) {
  int bit = 1 << id;
  return (set & bit) != 0;
}
public boolean isPointerDown(int pointerId) {
    return (mPointersDown & 1 << pointerId) != 0;
}

6. おわりに

プログラム理解のためのグラフ表現 "FGyama" を提案した。