├── .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 |
--------------------------------------------------------------------------------