Skip to content

Instantly share code, notes, and snippets.

@chimerast
Created March 24, 2011 03:53
Show Gist options
  • Save chimerast/884540 to your computer and use it in GitHub Desktop.
Save chimerast/884540 to your computer and use it in GitHub Desktop.
scala-library.jarをJavassistで走査して並列化されているメソッドを探します
package org.karatachi.javassist;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.ConstPool;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
public class StaticCallGraphScanner {
/**
* Static Call Graphのノード
*/
private static class CallGraphNode {
public final boolean isPublic;
public final String caller;
public final String callee;
public CallGraphNode(boolean isPublic, String caller, String callee) {
this.isPublic = isPublic;
this.caller = replaceOperator(caller);
this.callee = replaceOperator(callee);
}
}
public static void main(String[] args) throws Exception {
List<CallGraphNode> nodelist = new ArrayList<CallGraphNode>();
scanJarFile(nodelist, new JarFile(new File("scala-library.jar")));
Set<String> paralleled = new TreeSet<String>();
// executeAndWaitResultが呼ばれていれば並列化されている
paralleled.add("scala.collection.parallel.Tasks#executeAndWaitResult");
// executeAndWaitResultを呼び出しているメソッドを探し出す
while (true) {
int begin = paralleled.size();
for (CallGraphNode node : nodelist) {
if (paralleled.contains(node.callee)) {
paralleled.add(node.caller);
}
}
int end = paralleled.size();
if (begin == end) {
break;
}
}
// publicなメソッドをリスト化しているだけ
Set<String> isPublic = new TreeSet<String>();
for (CallGraphNode node : nodelist) {
if (node.isPublic) {
isPublic.add(node.caller);
}
}
for (String name : paralleled) {
// インナークラスと非publicメソッドは除く
if (!name.contains("$") && isPublic.contains(name)) {
System.out.println(name);
}
}
}
/**
* Jarファイルを走査して呼び出しリストを構築
*/
private static void scanJarFile(List<CallGraphNode> nodelist, JarFile jar)
throws IOException, BadBytecode {
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
// scala.collection.parallelパッケージの中のみ走査
if (entry.getName().startsWith("scala/collection/parallel/")
&& entry.getName().endsWith(".class")) {
scanEntry(nodelist, jar, entry);
}
}
}
/**
* クラスファイルを走査して呼び出しリストを構築
*/
@SuppressWarnings("unchecked")
private static void scanEntry(List<CallGraphNode> nodelist, JarFile jar,
JarEntry entry) throws IOException, BadBytecode {
DataInputStream in =
new DataInputStream(new BufferedInputStream(
jar.getInputStream(entry)));
try {
ClassFile cf = new ClassFile(in);
ConstPool cp = cf.getConstPool();
for (MethodInfo minfo : (List<MethodInfo>) cf.getMethods()) {
boolean isPublic =
(cf.getAccessFlags() & AccessFlag.PUBLIC) != 0
&& (minfo.getAccessFlags() & AccessFlag.PUBLIC) != 0;
String caller = cf.getName() + "#" + minfo.getName();
// trait対応
caller = caller.replace("$class", "");
CodeAttribute ca = minfo.getCodeAttribute();
if (ca == null) {
continue; // メソッドの実装がない
}
// メソッドのbodyを走査してinvokeinterfaceとinvokevirtualを見つける
CodeIterator ci = ca.iterator();
while (ci.hasNext()) {
int index = ci.next();
int op = ci.byteAt(index);
String callee;
switch (op) {
case Opcode.INVOKEINTERFACE: {
int mi = ci.u16bitAt(index + 1);
callee =
cp.getInterfaceMethodrefClassName(mi) + "#"
+ cp.getInterfaceMethodrefName(mi);
break;
}
case Opcode.INVOKESPECIAL: {
int mi = ci.u16bitAt(index + 1);
callee =
cp.getMethodrefClassName(mi) + "#"
+ cp.getMethodrefName(mi);
break;
}
case Opcode.INVOKEVIRTUAL: {
int mi = ci.u16bitAt(index + 1);
callee =
cp.getMethodrefClassName(mi) + "#"
+ cp.getMethodrefName(mi);
break;
}
default:
continue;
}
nodelist.add(new CallGraphNode(isPublic, caller, callee));
}
}
} finally {
in.close();
}
}
/**
* メソッド名で置き換えられている文字を元に戻す
*/
private static String replaceOperator(String name) {
name = name.replace("$eq", "=");
name = name.replace("$greater", ">");
name = name.replace("$less", "<");
name = name.replace("$plus", "+");
name = name.replace("$minus", "-");
name = name.replace("$times", "*");
name = name.replace("$div", "/");
name = name.replace("$bang", "!");
name = name.replace("$at", "@");
name = name.replace("$hash", "#");
name = name.replace("$percent", "%");
name = name.replace("$up", "^");
name = name.replace("$amp", "&");
name = name.replace("$tilde", "~");
name = name.replace("$qmark", "?");
name = name.replace("$up", "^");
name = name.replace("$bar", "|");
name = name.replace("$bslash", "\\");
name = name.replace("$colon", ":");
// $updatedが変換されてしまうので元に戻す
name = name.replace("^date", "$update");
return name;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment