そこに仁義はあるのか(仮)

略してそこ仁!

JJUG CCC 2014 Fallの懇親会でLTしてきました! #jjug_ccc

参加レポは別で書くよん

BCELについて

Apache Commons BCEL™ -

BCELはClassファイルを解析するためのライブラリです!
findBugsでも使われていて、『このclassでこういうmethodを使用しているときにはbugを検知する』っていう解析ができる!

前に仕事で、findBugsの解析クラスを作成したことがあって、そのときにBCELを知りました!
今回のLTでは、クラスファイル名とメソッド名を調べるだけだったけど、
アクセス修飾子(publicなのかprivateなのか)のような、MethodやClassの属性を調べることもできる

ただし、開発自体は2006年でとまってる…(^p^)

f:id:syobochim:20141122160642p:plain

LT終わった後に、@さんにASM - Home Pageを教えてもらった!
こちらは、今も開発されているので、Java8環境での解析をする場合は、ASMの方がイイ!!らしい
ASMの話はまた別でする!*1

BCEL

準備

mavenリポジトリがある!
Maven Repository: org.apache.bcel » bcel » 5.2

ので、準備も簡単!

<dependency>
    <groupId>org.apache.bcel</groupId>
    <artifactId>bcel</artifactId>
    <version>5.2</version>
</dependency>

コードの解析

Classの取得

Classを取得したいときはこう書けばOK!

JavaClass javaClass = new ClassParser(<<解析したいClassファイルのファイルパス>>).parse();
Methodの取得

ClassファイルのMethodを取得して、Listとしてもつときはこう

List<Method> methods = Arrays.asList(javaClass.getMethods());

methodを一つ取得して、アクセス修飾子を調べたい場合はこう書けばいい

methods.get(1).isPrivate()
Codeの解析

↓こうかけば、javapしたときに表示される情報が一行ずつ詰まってるStreamができる*2

methods.stream()
 .map(Method::getCode)
 .map(code -> Utility.codeToString(code.getCode(), javaClass.getConstantPool(), 0, 1, false))
 .flatMap(s -> Arrays.stream(s.split("\n")))

↓この情報が入ってるよ!

173:  invokevirtual	java.lang.StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

ちなみに

を確認することができるよ!

ちなみに、LTしたときのソースはこんな感じでした!

import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.Utility;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

public class SyobochimJyoshiryokuDetector {
    static final String PATH = "/Users/syobochim/dev/gist/build/classes/main/jjugccc2014/";

    public static void main(String[] args) {
        File dir = new File(PATH);
        List<File> files = Arrays.asList(dir.listFiles());

        files.stream()
                .filter(File::isFile)
                .forEach(file -> {
                    try {
                        JavaClass javaClass = new ClassParser(file.getPath()).parse();
                        System.out.print(javaClass.getClassName() + " : ");
                        if (hasJyoshiryoku(javaClass)) {
                            System.out.println("女子力あります");
                        } else {
                            System.out.println("女子力ないです");
                        }
                        System.out.println();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
    }

    private static boolean hasJyoshiryoku(final JavaClass javaClass) {
        List<Method> methods = Arrays.asList(javaClass.getMethods());

        return methods.stream()
                .map(Method::getCode)
                .map(code -> Utility.codeToString(code.getCode(), javaClass.getConstantPool(), 0, 1, false))
                .flatMap(s -> Arrays.stream(s.split("\n")))
                .anyMatch(s -> s.matches(".*Syobochim\\.jyoshiryoku.*"));

    }
}

*1:本当はこの記事で話したかったんだけど、公式サイトがずっとつながらなかった(^q^)

*2:javapしたときの情報はJavaソースそのままの一行とは違うから注意!