├── .gitignore ├── README.md ├── src └── main │ └── java │ └── com │ └── zhhiyp │ └── incubator │ └── asm │ ├── core │ ├── HierachyFinder.java │ ├── CallGraph.java │ ├── CallMethodNode.java │ ├── SourceMethodParser.java │ └── MethodLinker.java │ └── facade │ └── DataSourceFacade.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /*.iml 3 | *.class 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MethodParser 2 | asm解析class文件获得方法调用链路 3 | 储存到CallGraph 4 | 5 | 这是基础demo, 作为工程分析的脚手架. 6 | -------------------------------------------------------------------------------- /src/main/java/com/zhhiyp/incubator/asm/core/HierachyFinder.java: -------------------------------------------------------------------------------- 1 | package com.zhhiyp.incubator.asm.core; 2 | 3 | /** 4 | * @author zhiyp 5 | * @date 2018/10/10 0010 23:05 6 | */ 7 | public class HierachyFinder { 8 | //TODO 通过方法名打印 到上层所有链路 9 | } 10 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | MethodParser 8 | com.zhhiyp.incubator.asm 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.apache.maven.plugins 15 | maven-compiler-plugin 16 | 3.5 17 | 18 | 1.8 19 | 1.8 20 | UTF-8 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/com/zhhiyp/incubator/asm/facade/DataSourceFacade.java: -------------------------------------------------------------------------------- 1 | package com.zhhiyp.incubator.asm.facade; 2 | 3 | import com.zhhiyp.incubator.asm.core.CallGraph; 4 | import com.zhhiyp.incubator.asm.core.MethodLinker; 5 | import com.zhhiyp.incubator.asm.core.SourceMethodParser; 6 | 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.io.FileNotFoundException; 10 | 11 | /** 12 | * @author zhiyp 13 | * @date 2018/10/9 0009 21:51 14 | */ 15 | public class DataSourceFacade { 16 | 17 | public static void main(String[] args) { 18 | String projectRoot = "D:\\workspace\\github-workspace\\leetcode_drift\\out"; 19 | buildSource(projectRoot); 20 | //最终建立被调用的关系 21 | CallGraph callGraph = MethodLinker.buildBeInvokedRelation(CallGraph.getInstance()); 22 | } 23 | 24 | //传入一个工程路径 25 | //搜索下面的class 26 | //读成流,发给解析类 27 | public static void buildSource(String projectRoot) { 28 | File root = new File(projectRoot); 29 | if (!root.exists()) { 30 | return; 31 | } 32 | 33 | if (root.isDirectory()) { 34 | String path = root.getAbsolutePath(); 35 | if (!path.endsWith(".git") || !path.endsWith(".idea")) { 36 | //递归 37 | for (String child : root.list()) { 38 | buildSource(path + File.separator + child); 39 | } 40 | } 41 | } else { 42 | if (root.getAbsolutePath().endsWith(".class")) { 43 | try { 44 | FileInputStream in = new FileInputStream(root); 45 | SourceMethodParser.parseSourceClass(in); 46 | } catch (FileNotFoundException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/zhhiyp/incubator/asm/core/CallGraph.java: -------------------------------------------------------------------------------- 1 | package com.zhhiyp.incubator.asm.core; 2 | 3 | import jdk.internal.org.objectweb.asm.tree.ClassNode; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author zhiyp 10 | * @date 2018/10/10 0010 19:59 11 | * 储存方法调用关系 12 | */ 13 | public class CallGraph { 14 | /** 15 | * className 16 | * methodSignature = methodName + # + desc 17 | */ 18 | private Map> invokeMap = new HashMap<>(); 19 | private Map classMap = new HashMap<>(); 20 | private Map> virtualInvokeMap = new HashMap<>(); 21 | 22 | private static CallGraph callGraph = new CallGraph(); 23 | 24 | private CallGraph(){} 25 | 26 | public static CallGraph getInstance(){ 27 | return callGraph; 28 | } 29 | 30 | public Map> getInvokeMap() { 31 | return invokeMap; 32 | } 33 | 34 | public void putClass(String className){ 35 | invokeMap.put(className,new HashMap<>()); 36 | } 37 | 38 | public void putMethod(String className,String methodSig,CallMethodNode methodNode){ 39 | if(!invokeMap.containsKey(className)){ 40 | putClass(className); 41 | } 42 | invokeMap.get(className).put(methodSig,methodNode); 43 | } 44 | 45 | public void putVirtualMethod(String className,String methodSig,CallMethodNode methodNode){ 46 | if(!virtualInvokeMap.containsKey(className)){ 47 | virtualInvokeMap.put(className,new HashMap<>()); 48 | } 49 | virtualInvokeMap.get(className).put(methodSig,methodNode); 50 | } 51 | 52 | public void putClasSNode(String className,ClassNode classNode){ 53 | classMap.put(className,classNode); 54 | } 55 | 56 | public ClassNode getClassNode(String className){ 57 | return classMap.get(className); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/zhhiyp/incubator/asm/core/CallMethodNode.java: -------------------------------------------------------------------------------- 1 | package com.zhhiyp.incubator.asm.core; 2 | 3 | import jdk.internal.org.objectweb.asm.tree.ClassNode; 4 | import jdk.internal.org.objectweb.asm.tree.MethodNode; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | /** 10 | * @author zhiyp 11 | * @date 2018/10/10 0010 20:01 12 | */ 13 | public class CallMethodNode { 14 | 15 | //methodNode之间直接维持父子关系 16 | private final Set parentMethods = new HashSet<>(); 17 | private final Set childMethods = new HashSet<>(); 18 | //根据方法内操作指令记录调用的方法 19 | private final Set childSet = new HashSet<>(); 20 | 21 | private String methodSig; 22 | private String className; 23 | 24 | private MethodNode methodNode; 25 | //考虑到接口,抽象类需要实现类去调用该方法,需要父子关系统计 26 | private ClassNode classNode; 27 | 28 | public CallMethodNode(String methodSig, String className) { 29 | this.methodSig = methodSig; 30 | this.className = className; 31 | } 32 | 33 | public Set getParentMethods() { 34 | return parentMethods; 35 | } 36 | 37 | public Set getChildMethods() { 38 | return childMethods; 39 | } 40 | 41 | public Set getChildSet() { 42 | return childSet; 43 | } 44 | 45 | public String getMethodSig() { 46 | return methodSig; 47 | } 48 | 49 | public void setMethodSig(String methodSig) { 50 | this.methodSig = methodSig; 51 | } 52 | 53 | public String getClassName() { 54 | return className; 55 | } 56 | 57 | public void setClassName(String className) { 58 | this.className = className; 59 | } 60 | 61 | public MethodNode getMethodNode() { 62 | return methodNode; 63 | } 64 | 65 | public void setMethodNode(MethodNode methodNode) { 66 | this.methodNode = methodNode; 67 | } 68 | 69 | public ClassNode getClassNode() { 70 | return classNode; 71 | } 72 | 73 | public void setClassNode(ClassNode classNode) { 74 | this.classNode = classNode; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/zhhiyp/incubator/asm/core/SourceMethodParser.java: -------------------------------------------------------------------------------- 1 | package com.zhhiyp.incubator.asm.core; 2 | 3 | import jdk.internal.org.objectweb.asm.ClassReader; 4 | import jdk.internal.org.objectweb.asm.tree.*; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.List; 9 | import java.util.ListIterator; 10 | 11 | /** 12 | * @author zhiyp 13 | * @date 2018/10/10 0010 7:54 14 | * 解析class文件 获得一个方法及其之中的真实调用 15 | */ 16 | public class SourceMethodParser { 17 | 18 | private static CallGraph callGraph = CallGraph.getInstance(); 19 | 20 | public static void parseSourceClass(InputStream in) { 21 | ClassNode cn = new ClassNode(); 22 | try { 23 | ClassReader cr = new ClassReader(in); 24 | cr.accept(cn, 0); 25 | } catch (IOException e) { 26 | e.printStackTrace(); 27 | } 28 | 29 | //记录该类 30 | callGraph.putClass(cn.name); 31 | callGraph.putClasSNode(cn.name,cn); 32 | 33 | if (cn.methods != null && cn.methods.size() > 0) { 34 | for (MethodNode mn : cn.methods) { 35 | 36 | //不记录类加载方法 37 | if ("".equals(mn.name)) { 38 | continue; 39 | } 40 | 41 | 42 | //内部类需要解析 43 | //如果是类加载器先加载全部jar和class,就只需要以内部类的name去loadClass,只是方式不同.取决于如何读取class 44 | //如果是直接每个class文件遍历读,内部类则已经读取了. 45 | //parseInnerClass(cn); 46 | 47 | //记录该类的method 48 | CallMethodNode cMethodNode = new CallMethodNode(mn.name + "#" + mn.desc, cn.name); 49 | callGraph.putMethod(cn.name, mn.name + "#" + mn.desc, cMethodNode); 50 | cMethodNode.setMethodNode(mn); 51 | cMethodNode.setClassNode(cn); 52 | 53 | //抽象方法就不记录更多 54 | if (mn.instructions.size() > 0) { 55 | ListIterator iterator = mn.instructions.iterator(); 56 | while (iterator.hasNext()) { 57 | AbstractInsnNode insnNode = iterator.next(); 58 | if (insnNode instanceof MethodInsnNode) { 59 | //储存该方法内 方法调用指令节点 60 | String methodOwner = ((MethodInsnNode) insnNode).owner; 61 | String methodName = ((MethodInsnNode) insnNode).name; 62 | String methodDesc = ((MethodInsnNode) insnNode).desc; 63 | cMethodNode.getChildSet().add(methodOwner + "#" + methodName + "#" + methodDesc); 64 | } 65 | } 66 | } 67 | } 68 | } 69 | 70 | } 71 | 72 | private static void parseInnerClass(ClassNode cn) { 73 | List innerClasses = cn.innerClasses; 74 | for (InnerClassNode innerClass : innerClasses) { 75 | if(innerClass.name.equals(cn.innerClasses)){ 76 | //添加新方法 parse 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/zhhiyp/incubator/asm/core/MethodLinker.java: -------------------------------------------------------------------------------- 1 | package com.zhhiyp.incubator.asm.core; 2 | 3 | import jdk.internal.org.objectweb.asm.tree.ClassNode; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * @author zhiyp 9 | * @date 2018/10/10 0010 21:30 10 | */ 11 | public class MethodLinker { 12 | 13 | public static CallGraph buildBeInvokedRelation(CallGraph callGraph) { 14 | Map> invokeMap = callGraph.getInvokeMap(); 15 | 16 | invokeMap.forEach((className, classMethodMap) -> { 17 | //基于类 18 | classMethodMap.forEach((methodSig, methodParentNode) -> { 19 | //基于每个方法node 20 | methodParentNode.getChildSet().forEach((classMethodSig) -> { 21 | // 包名类名#方法名#方法描述 22 | String ownerName = classMethodSig.substring(0, classMethodSig.indexOf('#')); 23 | String invokeMethodSig = classMethodSig.substring(classMethodSig.indexOf('#') + 1); 24 | //明确是 调用了本工程的类 25 | if (invokeMap.containsKey(ownerName)) { 26 | CallMethodNode beInvokedNode = invokeMap.get(ownerName).get(invokeMethodSig); 27 | if (beInvokedNode == null) { 28 | //排除枚举的ordinal方法 29 | if ("ordinal#()I".equals(invokeMethodSig)) { 30 | return; 31 | } else { 32 | //search in superClass 33 | ClassNode classNode = callGraph.getClassNode(ownerName); 34 | if(classNode.superName != null && !"java/lang/Object".equals(classNode.superName)){ 35 | beInvokedNode = invokeMap.get(classNode.superName).get(invokeMethodSig); 36 | if(beInvokedNode == null){ 37 | //can't happen unless superClass hasn't been recorded 38 | //virtual supperClass 39 | virtualAndLinkNode(callGraph,methodParentNode,classNode.superName,invokeMethodSig); 40 | return; 41 | }else{ 42 | link(methodParentNode, beInvokedNode); 43 | return; 44 | } 45 | } 46 | 47 | } 48 | } 49 | link(methodParentNode, beInvokedNode); 50 | }else { 51 | //不是本工程类 52 | //虚拟方法节点,例如数据流想知道工程哪里调用过List集合的add,所以虚拟该节点,但其childMethods是空.只有parent 53 | virtualAndLinkNode(callGraph, methodParentNode, ownerName, invokeMethodSig); 54 | } 55 | }); 56 | }); 57 | }); 58 | return callGraph; 59 | } 60 | 61 | private static void virtualAndLinkNode(CallGraph callGraph, CallMethodNode methodParentNode, String ownerName, String invokeMethodSig) { 62 | CallMethodNode virtualMethod = new CallMethodNode(invokeMethodSig,ownerName); 63 | callGraph.putVirtualMethod(ownerName,invokeMethodSig,virtualMethod); 64 | link(methodParentNode, virtualMethod); 65 | } 66 | 67 | private static void link(CallMethodNode methodParentNode, CallMethodNode beInvokedNode) { 68 | //父添加到子的parentMethods 69 | beInvokedNode.getParentMethods().add(methodParentNode); 70 | //子添加到父的childMethods 71 | methodParentNode.getChildMethods().add(beInvokedNode); 72 | } 73 | } 74 | --------------------------------------------------------------------------------