Javaのクラス内が依存しているフィールド、メソッドの一覧を表示する
世の中には javassist というとても便利なライブラリがある。通常は、バイトコードの書き換えに利用することが多いが、バイトコードを調べることで依存関係やメタ情報を取得することもできる。
単に依存しているクラスを取得するだけであれば、CtClass#getRefClasses() を使って簡単に取得できるが、フィールドやメソッドとなるとそうもいかない。次のようにバイトコードをひとつひとつ解析する必要がある。
ClassPool cp = ClassPool.getDefault(); CtClass cc = cp.get(クラス名); for (CtBehavior cb : cc.getDeclaredBehaviors()) { MethodInfo info = cb.getMethodInfo2(); ConstPool pool = info.getConstPool(); CodeAttribute code = info.getCodeAttribute(); if (code == null) return; CodeIterator i = code.iterator(); while (i.hasNext()) { int pos = i.next(); int opecode = i.byteAt(pos); switch (opecode) { case Opcode.NEW: case Opcode.ANEWARRAY: case Opcode.MULTIANEWARRAY: { int index = i.u16bitAt(pos + 1); System.out.print(cc.getName()); System.out.print('\t'); System.out.print(cb.getLongName()); System.out.print('\t'); System.out.print("new"); System.out.print('\t'); String className = pool.getClassInfo(index); if (opecode == Opcode.ANEWARRAY || opecode == Opcode.MULTIANEWARRAY) { className = "new " + className + "[]"; } System.out.print(className); System.out.println(); break; } case Opcode.NEWARRAY: { System.out.print(cc.getName()); System.out.print('\t'); System.out.print(cb.getLongName()); System.out.print('\t'); System.out.print("new"); System.out.print('\t'); String className = null; switch (i.byteAt(pos + 1)) { case Opcode.T_BOOLEAN: className = "new boolean[]"; break; case Opcode.T_CHAR: className = "new char[]"; break; case Opcode.T_BYTE: className = "new byte[]"; break; case Opcode.T_SHORT: className = "new short[]"; break; case Opcode.T_INT: className = "new int[]"; break; case Opcode.T_LONG: className = "new long[]"; break; case Opcode.T_FLOAT: className = "new float[]"; break; case Opcode.T_DOUBLE: className = "new double[]"; break; } System.out.print(className); System.out.println(); break; } case Opcode.CHECKCAST: { int index = i.u16bitAt(pos + 1); System.out.print(cc.getName()); System.out.print('\t'); System.out.print(cb.getLongName()); System.out.print('\t'); System.out.print("cast"); System.out.print('\t'); String className = "(" + pool.getClassInfo(index) + ")"; System.out.print(className); System.out.println(); break; } case Opcode.GETSTATIC: case Opcode.PUTSTATIC: case Opcode.GETFIELD: case Opcode.PUTFIELD: { int index = i.u16bitAt(pos + 1); System.out.print(cc.getName()); System.out.print('\t'); System.out.print(cb.getLongName()); System.out.print('\t'); System.out.print("field"); String className = pool.getFieldrefClassName(index); String fieldName = pool.getFieldrefName(index); String desc = pool.getFieldrefType(index); String typeName = Descriptor.toClassName(desc); System.out.print('\t'); System.out.print(typeName); System.out.print(' '); System.out.print(className); System.out.print('.'); System.out.print(fieldName); System.out.println(); break; } case Opcode.INVOKEVIRTUAL: case Opcode.INVOKESPECIAL: case Opcode.INVOKESTATIC: case Opcode.INVOKEINTERFACE: { int index = i.u16bitAt(pos + 1); System.out.print(cc.getName()); System.out.print('\t'); System.out.print(cb.getLongName()); System.out.print('\t'); System.out.print("method"); String className; if (opecode == Opcode.INVOKEINTERFACE) { className = pool.getInterfaceMethodrefClassName(index); } else { className = pool.getMethodrefClassName(index); } String methodName; if (opecode == Opcode.INVOKEINTERFACE) { methodName = pool.getInterfaceMethodrefName(index); } else { methodName = pool.getMethodrefName(index); } String desc; if (opecode == Opcode.INVOKEINTERFACE) { desc = pool.getInterfaceMethodrefType(index); } else { desc = pool.getMethodrefType(index); } String ret = null; List<String> params = new ArrayList<String>(); if (desc.charAt(0) == '(') { int start = 1; boolean inClass = false; for (int j = start; j < desc.length(); j++) { char c = desc.charAt(j); if (inClass) { if (c == ';') { params.add(Descriptor.toClassName(desc.substring(start, j + 1))); start = j + 1; inClass = false; } } else if (c == ')') { ret = Descriptor.toClassName(desc.substring(j + 1)); break; } else if (c == 'L') { inClass = true; } else if (c != '[') { params.add(Descriptor.toClassName(desc.substring(start, j + 1))); start = j + 1; } } } else { ret = Descriptor.toClassName(desc); } System.out.print('\t'); System.out.print(ret); System.out.print(' '); System.out.print(className); System.out.print('.'); System.out.print(methodName); System.out.print('('); for (int j = 0; j < params.size(); j++) { if (j > 0) System.out.print(','); System.out.print(params.get(j)); } System.out.println(')'); break; } } } }