├── .gitignore ├── LICENSE ├── README.md ├── TestBytecodeCallHierarchy.jar ├── pom.xml └── src ├── main └── java │ └── com │ └── github │ └── goblinbr │ └── bchscanner │ ├── JavaScanner.java │ ├── call │ ├── CallHierarchy.java │ └── JavaMethod.java │ └── visitors │ ├── CallerClassVisitor.java │ └── SuperClassVisitor.java └── test └── java └── com └── github └── goblinbr └── bchscanner └── JavaScannerTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .settings 3 | .classpath 4 | .project -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Rodrigo de Bona Sartor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bytecode Call Hierarchy Scanner 2 | A java library to scan jar files and find calls to a method 3 | **This project uses [ASM](http://asm.ow2.org/) to ready the bytecode** 4 | 5 | ### Maven 6 | 7 | com.github.goblinbr 8 | bytecode-callhierarchy-scanner 9 | 1.0.1 10 | 11 | 12 | ### Example 13 | ```java 14 | import java.io.IOException; 15 | import java.util.Set; 16 | 17 | import com.github.goblinbr.bchscanner.JavaScanner; 18 | import com.github.goblinbr.bchscanner.call.CallHierarchy; 19 | import com.github.goblinbr.bchscanner.call.JavaMethod; 20 | 21 | public class App { 22 | public static void main(String[] args) { 23 | App app = new App(); 24 | System.out.println("-findDirectCallersOfIntegerGetInt-"); 25 | app.findDirectCallersOfIntegerGetInt(); 26 | System.out.println("-findDirectCallersOfShortAnyMethod-"); 27 | app.findDirectCallersOfShortAnyMethod(); 28 | System.out.println("-findAllClassesThatExtendsOrImplementsCloseable-"); 29 | app.findAllClassesThatExtendsOrImplementsCloseable(); 30 | System.out.println("-findCallHierarchyOfIntegerDecode-"); 31 | app.findCallHierarchyOfIntegerDecode(); 32 | } 33 | 34 | /** 35 | * Find all callers of method getInt() of the class java.lang.Integer 36 | */ 37 | private void findDirectCallersOfIntegerGetInt() { 38 | try ( JavaScanner javaScanner = new JavaScanner("C:/Program Files (x86)/java/jre7/lib/rt.jar") ){ 39 | Set callers = javaScanner.findCallersOfMethod("java/lang/Integer", "int intValue()"); 40 | for( JavaMethod caller : callers ){ 41 | System.out.println( "Class: " + caller.getPackageName() + "." + caller.getSimpleClassName() 42 | + " Method: " + caller.getMethodName() 43 | + " Method description: " + caller.getMethodDesc() ); 44 | } 45 | 46 | System.out.println( "Found " + callers.size() + " calls to Integer.intValue()" ); 47 | } 48 | catch (IOException e) { 49 | e.printStackTrace(); 50 | } 51 | } 52 | 53 | /** 54 | * Find all callers of any method of the class java.lang.Short 55 | */ 56 | private void findDirectCallersOfShortAnyMethod() { 57 | try ( JavaScanner javaScanner = new JavaScanner("C:/Program Files (x86)/java/jre7/lib/rt.jar") ){ 58 | Set callers = javaScanner.findCallersOfAnyMethod("java/lang/Short"); 59 | System.out.println( "Found " + callers.size() + " calls to any method of Short" ); 60 | } 61 | catch (IOException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | 66 | /** 67 | * Find all classes/interfaces that implements or extends java.io.Closable 68 | */ 69 | private void findAllClassesThatExtendsOrImplementsCloseable() { 70 | try ( JavaScanner javaScanner = new JavaScanner("C:/Program Files (x86)/java/jre7/lib/rt.jar") ){ 71 | Set classNames = javaScanner.findAllClassesThatExtendsOrImplements("java/io/Closeable"); 72 | for( String className : classNames ){ 73 | System.out.println( "Name: " + className ); 74 | } 75 | 76 | System.out.println( "Found " + classNames.size() + " classes that implements or extends Closeable" ); 77 | } 78 | catch (IOException e) { 79 | e.printStackTrace(); 80 | } 81 | } 82 | 83 | /** 84 | * Find call hierarchy of method decode(String) of the class java.lang.Integer 85 | */ 86 | private void findCallHierarchyOfIntegerDecode() { 87 | try ( JavaScanner javaScanner = new JavaScanner("C:/Program Files (x86)/java/jre7/lib/rt.jar") ){ 88 | CallHierarchy callHierarchy = javaScanner.findCallHierarchy("java/lang/Integer","Integer decode(String)"); 89 | printCallHierarchy( callHierarchy, 0 ); 90 | } 91 | catch (IOException e) { 92 | e.printStackTrace(); 93 | } 94 | } 95 | 96 | private void printCallHierarchy(CallHierarchy callHierarchy, int level) { 97 | JavaMethod javaMethod = callHierarchy.getMethod(); 98 | String levelPrefix = ""; 99 | for( int i = 0; i < level; i++ ){ 100 | levelPrefix += "|"; 101 | } 102 | levelPrefix += "-"; 103 | System.out.println( levelPrefix + " Class: " + javaMethod.getPackageName() + "." + javaMethod.getSimpleClassName() 104 | + " Method: " + javaMethod.getMethodName() 105 | + " Method description: " + javaMethod.getMethodDesc() ); 106 | 107 | for( CallHierarchy caller : callHierarchy.getCallers() ){ 108 | printCallHierarchy( caller, level + 1 ); 109 | } 110 | } 111 | } 112 | ``` 113 | -------------------------------------------------------------------------------- /TestBytecodeCallHierarchy.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goblinbr/BytecodeCallHierarchyScanner/92f2a406b6a21cf9865276915a8993503f8f39a9/TestBytecodeCallHierarchy.jar -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.github.goblinbr 5 | bytecode-callhierarchy-scanner 6 | 1.0.1 7 | jar 8 | 9 | Bytecode Call Hierarchy Scanner 10 | A java library to scan jar files and find calls to a method. 11 | https://github.com/goblinbr/BytecodeCallHierarchyScanner 12 | 13 | 14 | 15 | MIT License 16 | http://www.opensource.org/licenses/mit-license.php 17 | 18 | 19 | 20 | 21 | 22 | Rodrigo de Bona Sartor 23 | rodrigo.goblin@gmail.com 24 | 25 | 26 | 27 | 28 | 29 | 30 | scm:git:git@github.com:goblinbr/BytecodeCallHierarchyScanner.git 31 | scm:git:git@github.com:goblinbr/BytecodeCallHierarchyScanner.git 32 | https://github.com/goblinbr/BytecodeCallHierarchyScanner.git 33 | 34 | 35 | 36 | UTF-8 37 | 38 | 39 | 40 | 41 | ossrh 42 | https://oss.sonatype.org/content/repositories/snapshots 43 | 44 | 45 | ossrh 46 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-source-plugin 55 | 2.4 56 | 57 | 58 | attach-sources 59 | 60 | jar-no-fork 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-javadoc-plugin 68 | 2.10.3 69 | 70 | 71 | attach-javadocs 72 | 73 | jar 74 | 75 | 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-gpg-plugin 81 | 1.5 82 | 83 | 84 | sign-artifacts 85 | verify 86 | 87 | sign 88 | 89 | 90 | 91 | 92 | 93 | org.sonatype.plugins 94 | nexus-staging-maven-plugin 95 | 1.6.7 96 | true 97 | 98 | ossrh 99 | https://oss.sonatype.org/ 100 | true 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | org.ow2.asm 109 | asm 110 | 5.1 111 | 112 | 113 | 114 | org.ow2.asm 115 | asm-commons 116 | 5.1 117 | 118 | 119 | 120 | junit 121 | junit 122 | 4.12 123 | test 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/main/java/com/github/goblinbr/bchscanner/JavaScanner.java: -------------------------------------------------------------------------------- 1 | package com.github.goblinbr.bchscanner; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.Closeable; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.util.Enumeration; 8 | import java.util.Set; 9 | import java.util.jar.JarEntry; 10 | import java.util.jar.JarFile; 11 | 12 | import org.objectweb.asm.ClassReader; 13 | import org.objectweb.asm.commons.Method; 14 | 15 | import com.github.goblinbr.bchscanner.call.CallHierarchy; 16 | import com.github.goblinbr.bchscanner.call.JavaMethod; 17 | import com.github.goblinbr.bchscanner.visitors.CallerClassVisitor; 18 | import com.github.goblinbr.bchscanner.visitors.SuperClassVisitor; 19 | 20 | /** 21 | * A class that scans jar files and finds calls to a method 22 | * 23 | * @author Rodrigo de Bona Sartor 24 | */ 25 | public class JavaScanner implements Closeable { 26 | 27 | private JarFile jarFile; 28 | 29 | /** 30 | * Create a new JavaScanner with the JarFile containing at jarPath 31 | * @param jarPath OS path to the jar file that contains the class 32 | * @throws IOException if an I/O error has occurred 33 | * @throws SecurityException if access to the file is denied 34 | * by the SecurityManager 35 | */ 36 | public JavaScanner(String jarPath) throws IOException { 37 | this.jarFile = new JarFile(jarPath); 38 | } 39 | 40 | /** 41 | * Find callers of any method of the class className or any subclass 42 | * @param className the full class name. ex: "java/lang/Integer" 43 | * @return a Set with the callers or a empty Set if no one was found 44 | * @throws IOException if an I/O error has occurred 45 | */ 46 | public Set findCallersOfAnyMethod( String className ) throws IOException { 47 | return findCallersOfMethod( className, null ); 48 | } 49 | 50 | /** 51 | * Find callers of methodSignature of the class className or any subclass 52 | * @param className the full class name. ex: "java/lang/Integer" 53 | * @param methodSignature method signature. ex: "java.lang.Integer methodName(java.lang.Integer)" 54 | * @return a Set with the callers or a empty Set if no one was found 55 | * @throws IOException if an I/O error has occurred 56 | */ 57 | public Set findCallersOfMethod( String className, String methodSignature ) throws IOException { 58 | String methodName = ""; 59 | String methodDesc = ""; 60 | if( methodSignature != null ){ 61 | Method method = Method.getMethod(methodSignature); 62 | methodName = method.getName(); 63 | methodDesc = method.getDescriptor(); 64 | } 65 | 66 | return findCallersOfMethod( className, methodName, methodDesc ); 67 | } 68 | 69 | private Set findCallersOfMethod(String className, String methodName, String methodDesc) throws IOException { 70 | Set classNames = findAllClassesThatExtendsOrImplements(className); 71 | 72 | CallerClassVisitor callerClassVisitor = new CallerClassVisitor( classNames, methodName, methodDesc ); 73 | Enumeration entries = this.jarFile.entries(); 74 | while (entries.hasMoreElements()) { 75 | JarEntry entry = entries.nextElement(); 76 | 77 | if (entry.getName().endsWith(".class")) { 78 | InputStream stream = new BufferedInputStream(this.jarFile.getInputStream(entry), 1024); 79 | ClassReader reader = new ClassReader(stream); 80 | 81 | reader.accept(callerClassVisitor, 0); 82 | 83 | stream.close(); 84 | } 85 | } 86 | return callerClassVisitor.getCallers(); 87 | } 88 | 89 | /** 90 | * Find call hierarchy of methodSignature of the class className or any subclass 91 | * @param className the full class name. ex: "java/lang/Integer" 92 | * @param methodSignature method signature. ex: "java.lang.Integer methodName(java.lang.Integer)" 93 | * @return a CallHierarchy starting with the parameter method 94 | * @throws IOException if an I/O error has occurred 95 | */ 96 | public CallHierarchy findCallHierarchy( String className, String methodSignature ) throws IOException { 97 | String methodName = ""; 98 | String methodDesc = ""; 99 | if( methodSignature != null ){ 100 | Method method = Method.getMethod(methodSignature); 101 | methodName = method.getName(); 102 | methodDesc = method.getDescriptor(); 103 | } 104 | 105 | CallHierarchy callHierarchy = new CallHierarchy(null, className, methodName, methodDesc); 106 | 107 | findCallHierarchy( callHierarchy ); 108 | 109 | return callHierarchy; 110 | } 111 | 112 | private void findCallHierarchy(CallHierarchy callHierarchy) throws IOException { 113 | JavaMethod method = callHierarchy.getMethod(); 114 | Set callers = findCallersOfMethod( method.getClassName(), method.getMethodName(), method.getMethodDesc() ); 115 | for( JavaMethod caller : callers ){ 116 | if( !callHierarchy.containsCall( caller ) ){ 117 | 118 | CallHierarchy callHierarchyCaller = new CallHierarchy(callHierarchy, caller.getClassName(), caller.getMethodName(), caller.getMethodDesc() ); 119 | callHierarchy.addCaller( callHierarchyCaller ); 120 | } 121 | } 122 | 123 | for( CallHierarchy caller : callHierarchy.getCallers() ){ 124 | findCallHierarchy(caller); 125 | } 126 | } 127 | 128 | /** 129 | * Find call hierarchy of any method of the class className or any subclass 130 | * @param className the full class name. ex: "java/lang/Integer" 131 | * @return a CallHierarchy starting with the parameter class 132 | * @throws IOException if an I/O error has occurred 133 | */ 134 | public CallHierarchy findCallHierarchyOfAnyMethod(String className) throws IOException { 135 | return findCallHierarchy(className, null); 136 | } 137 | 138 | /** 139 | * Find all classes that extends or implements class/interface className 140 | * @param className the full class name. ex: "java/lang/Integer" 141 | * @return a Set with all classes names, className will be at the Set too, 142 | * it will always return a Set with at least className inside 143 | * @throws IOException if an I/O error has occurred 144 | */ 145 | public Set findAllClassesThatExtendsOrImplements( String className ) throws IOException{ 146 | SuperClassVisitor superClassVisitor = new SuperClassVisitor( className ); 147 | int qtyClass; 148 | do{ 149 | qtyClass = superClassVisitor.getClassNames().size(); 150 | 151 | Enumeration entries = this.jarFile.entries(); 152 | while (entries.hasMoreElements()) { 153 | JarEntry entry = entries.nextElement(); 154 | 155 | if (entry.getName().endsWith(".class")) { 156 | InputStream stream = new BufferedInputStream(this.jarFile.getInputStream(entry), 1024); 157 | ClassReader reader = new ClassReader(stream); 158 | 159 | reader.accept(superClassVisitor, 0); 160 | 161 | stream.close(); 162 | } 163 | } 164 | } while( qtyClass != superClassVisitor.getClassNames().size() ); 165 | 166 | return superClassVisitor.getClassNames(); 167 | } 168 | 169 | /** 170 | * Closes the jar file 171 | * @throws IOException if an I/O error has occurred 172 | */ 173 | public void close() throws IOException { 174 | if( this.jarFile != null ){ 175 | this.jarFile.close(); 176 | } 177 | } 178 | 179 | 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/com/github/goblinbr/bchscanner/call/CallHierarchy.java: -------------------------------------------------------------------------------- 1 | package com.github.goblinbr.bchscanner.call; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class CallHierarchy implements Comparable { 7 | 8 | private JavaMethod method; 9 | 10 | private CallHierarchy callee; 11 | private List callers; 12 | 13 | public CallHierarchy(CallHierarchy callee, String className, String methodName, String methodDesc) { 14 | this.callee = callee; 15 | this.method = new JavaMethod(className, methodName, methodDesc); 16 | this.callers = new ArrayList(); 17 | } 18 | 19 | public void addCaller(CallHierarchy caller) { 20 | this.callers.add(caller); 21 | } 22 | 23 | public CallHierarchy getCallee() { 24 | return callee; 25 | } 26 | 27 | public List getCallers() { 28 | return callers; 29 | } 30 | 31 | public JavaMethod getMethod() { 32 | return method; 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | return this.method.hashCode(); 38 | } 39 | 40 | @Override 41 | public boolean equals(Object obj) { 42 | return obj != null && obj.getClass() == CallHierarchy.class && obj.hashCode() == this.hashCode(); 43 | } 44 | 45 | public int compareTo(CallHierarchy o) { 46 | return this.method.compareTo(o.method); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | String spaces = ""; 52 | CallHierarchy c = this.callee; 53 | while( c != null ){ 54 | spaces += " "; 55 | c = c.getCallee(); 56 | } 57 | return spaces + this.method.toString(); 58 | } 59 | 60 | public boolean containsCall(JavaMethod method) { 61 | boolean contains = this.method.equals(method); 62 | if( !contains && callee != null ){ 63 | contains = callee.containsCall(method); 64 | } 65 | return contains; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/github/goblinbr/bchscanner/call/JavaMethod.java: -------------------------------------------------------------------------------- 1 | package com.github.goblinbr.bchscanner.call; 2 | 3 | public class JavaMethod implements Comparable { 4 | 5 | private String className; 6 | private String methodName; 7 | private String methodDesc; 8 | 9 | public JavaMethod(String className, String methodName, String methodDesc) { 10 | this.className = className; 11 | this.methodName = methodName; 12 | this.methodDesc = methodDesc; 13 | } 14 | 15 | public String getClassName() { 16 | return className; 17 | } 18 | 19 | public String getMethodDesc() { 20 | return methodDesc; 21 | } 22 | 23 | public String getMethodName() { 24 | return methodName; 25 | } 26 | 27 | public String getSimpleClassName() { 28 | String simpleName = this.className; 29 | int index = this.className.lastIndexOf("/"); 30 | if( index >= 0 ){ 31 | simpleName = this.className.substring( index + 1 ); 32 | } 33 | return simpleName; 34 | } 35 | 36 | public String getPackageName() { 37 | String packageName = ""; 38 | int index = this.className.lastIndexOf("/"); 39 | if( index >= 0 ){ 40 | packageName = this.className.substring( 0, index ).replace("/", "."); 41 | } 42 | return packageName; 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return (className + methodName + methodDesc).hashCode(); 48 | } 49 | 50 | @Override 51 | public boolean equals(Object obj) { 52 | return obj != null && obj.getClass() == JavaMethod.class && obj.hashCode() == this.hashCode(); 53 | } 54 | 55 | public int compareTo(JavaMethod o) { 56 | int comp = this.className.compareTo(o.className); 57 | if( comp == 0 ){ 58 | comp = this.methodName.compareTo(o.methodName); 59 | if( comp == 0 ){ 60 | comp = this.methodDesc.compareTo(o.methodDesc); 61 | } 62 | } 63 | return comp; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return this.className + ( ( this.methodName.equals("") ) ? "" : "." + this.methodName + this.methodDesc ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/github/goblinbr/bchscanner/visitors/CallerClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.github.goblinbr.bchscanner.visitors; 2 | 3 | import java.util.Collection; 4 | import java.util.Set; 5 | import java.util.TreeSet; 6 | 7 | import org.objectweb.asm.ClassVisitor; 8 | import org.objectweb.asm.MethodVisitor; 9 | import org.objectweb.asm.Opcodes; 10 | 11 | import com.github.goblinbr.bchscanner.call.JavaMethod; 12 | 13 | public class CallerClassVisitor extends ClassVisitor { 14 | 15 | private final ThisMethodVisitor methodVisitor; 16 | 17 | public CallerClassVisitor( Collection classesAndSuperClasses, String methodName, String methodDesc ) { 18 | super(Opcodes.ASM5); 19 | this.methodVisitor = new ThisMethodVisitor(classesAndSuperClasses, methodName, methodDesc); 20 | } 21 | 22 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 23 | this.methodVisitor.setActualClassName(name); 24 | } 25 | 26 | @Override 27 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 28 | this.methodVisitor.setActualMethodName(name); 29 | this.methodVisitor.setActualMethodDesc(desc); 30 | 31 | return this.methodVisitor; 32 | } 33 | 34 | public Set getCallers() { 35 | return this.methodVisitor.getCallers(); 36 | } 37 | 38 | class ThisMethodVisitor extends MethodVisitor { 39 | 40 | private Set callers; 41 | private Collection classesAndSuperClasses; 42 | private String methodName; 43 | private String methodDesc; 44 | 45 | private String actualClassName; 46 | private String actualMethodName; 47 | private String actualMethodDesc; 48 | 49 | public ThisMethodVisitor( Collection classesAndSuperClasses, String methodName, String methodDesc ) { 50 | super(Opcodes.ASM5); 51 | this.callers = new TreeSet(); 52 | this.classesAndSuperClasses = classesAndSuperClasses; 53 | this.methodName = methodName; 54 | this.methodDesc = methodDesc; 55 | } 56 | 57 | public void setActualClassName(String actualClassName) { 58 | this.actualClassName = actualClassName; 59 | } 60 | 61 | public void setActualMethodName(String actualMethodName) { 62 | this.actualMethodName = actualMethodName; 63 | } 64 | 65 | public void setActualMethodDesc(String actualMethodDesc) { 66 | this.actualMethodDesc = actualMethodDesc; 67 | } 68 | 69 | public Set getCallers() { 70 | return callers; 71 | } 72 | 73 | @Override 74 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 75 | if ( this.classesAndSuperClasses.contains(owner) && (this.methodName == null || this.methodName.equals("") || name.equals(this.methodName) && desc.equals(this.methodDesc))) { 76 | JavaMethod caller = new JavaMethod(this.actualClassName, this.actualMethodName, this.actualMethodDesc); 77 | this.callers.add(caller); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/github/goblinbr/bchscanner/visitors/SuperClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.github.goblinbr.bchscanner.visitors; 2 | 3 | import java.util.Set; 4 | import java.util.TreeSet; 5 | 6 | import org.objectweb.asm.ClassVisitor; 7 | import org.objectweb.asm.Opcodes; 8 | 9 | public class SuperClassVisitor extends ClassVisitor { 10 | 11 | private Set classNames; 12 | 13 | public SuperClassVisitor( String classNameToSearch ) { 14 | super(Opcodes.ASM5); 15 | this.classNames = new TreeSet(); 16 | this.classNames.add(classNameToSearch); 17 | } 18 | 19 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 20 | if( superName != null && this.classNames.contains(superName) ){ 21 | this.classNames.add(name); 22 | } 23 | else { 24 | for( String interfaceName : interfaces ){ 25 | if( this.classNames.contains(interfaceName) ){ 26 | this.classNames.add(name); 27 | break; 28 | } 29 | } 30 | } 31 | } 32 | 33 | public Set getClassNames() { 34 | return classNames; 35 | } 36 | } -------------------------------------------------------------------------------- /src/test/java/com/github/goblinbr/bchscanner/JavaScannerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.goblinbr.bchscanner; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | import org.junit.AfterClass; 8 | import org.junit.Assert; 9 | import org.junit.BeforeClass; 10 | import org.junit.Test; 11 | 12 | import com.github.goblinbr.bchscanner.JavaScanner; 13 | import com.github.goblinbr.bchscanner.call.CallHierarchy; 14 | import com.github.goblinbr.bchscanner.call.JavaMethod; 15 | 16 | public class JavaScannerTest { 17 | 18 | private static JavaScanner javaScanner; 19 | 20 | @BeforeClass 21 | public static void before() { 22 | try { 23 | javaScanner = new JavaScanner("TestBytecodeCallHierarchy.jar"); 24 | } catch (IOException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | 29 | @AfterClass 30 | public static void after() { 31 | try { 32 | javaScanner.close(); 33 | } catch (IOException e) { 34 | e.printStackTrace(); 35 | } 36 | } 37 | 38 | @Test 39 | public void findAllClassesThatExtendsOrImplementsAClassShouldReturnTwo(){ 40 | try { 41 | Assert.assertEquals(2,javaScanner.findAllClassesThatExtendsOrImplements("com/github/goblinbr/testbytecode/AClassImplementsDInterface").size()); 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | Assert.fail("Should not throw an exception"); 45 | } 46 | } 47 | 48 | @Test 49 | public void findAllClassesThatExtendsOrImplementsDInterfaceShouldReturnFour(){ 50 | try { 51 | Assert.assertEquals(4,javaScanner.findAllClassesThatExtendsOrImplements("com/github/goblinbr/testbytecode/DInterface").size()); 52 | } catch (IOException e) { 53 | e.printStackTrace(); 54 | Assert.fail("Should not throw an exception"); 55 | } 56 | } 57 | 58 | @Test 59 | public void findAllClassesThatExtendsOrImplementsBClassShouldReturnOne(){ 60 | try { 61 | Assert.assertEquals(1,javaScanner.findAllClassesThatExtendsOrImplements("com/github/goblinbr/testbytecode/BClassExtendsAClass").size()); 62 | } catch (IOException e) { 63 | e.printStackTrace(); 64 | Assert.fail("Should not throw an exception"); 65 | } 66 | } 67 | 68 | @Test 69 | public void findAllClassesThatExtendsOrImplementsInvalidClassShouldReturnOne(){ 70 | try { 71 | Assert.assertEquals(1,javaScanner.findAllClassesThatExtendsOrImplements("com/github/goblinbr/testbytecode/InvalidClass").size()); 72 | } catch (IOException e) { 73 | e.printStackTrace(); 74 | Assert.fail("Should not throw an exception"); 75 | } 76 | } 77 | 78 | @Test 79 | public void findCallersOfDInterfaceGetAShouldReturnFour(){ 80 | try { 81 | Set callers = javaScanner.findCallersOfMethod("com/github/goblinbr/testbytecode/DInterface", "int getA()"); 82 | Assert.assertEquals(4,callers.size()); 83 | } catch (IOException e) { 84 | e.printStackTrace(); 85 | Assert.fail("Should not throw an exception"); 86 | } 87 | } 88 | 89 | @Test 90 | public void findCallersOfGClassUseEClassShouldReturnOne(){ 91 | try { 92 | Set callers = javaScanner.findCallersOfMethod("com/github/goblinbr/testbytecode/GClassUsesAClassAndEClass", "Integer useEClass(Integer)"); 93 | Assert.assertEquals(1,callers.size()); 94 | } catch (IOException e) { 95 | e.printStackTrace(); 96 | Assert.fail("Should not throw an exception"); 97 | } 98 | } 99 | 100 | @Test 101 | public void findCallersOfInvalidClassShouldReturnZero(){ 102 | try { 103 | Set callers = javaScanner.findCallersOfMethod("com/github/goblinbr/testbytecode/InvalidClass", "Integer useEClass(Integer)"); 104 | Assert.assertEquals(0,callers.size()); 105 | } catch (IOException e) { 106 | e.printStackTrace(); 107 | Assert.fail("Should not throw an exception"); 108 | } 109 | } 110 | 111 | @Test 112 | public void findCallersOfInvalidMethodShouldReturnZero(){ 113 | try { 114 | Set callers = javaScanner.findCallersOfMethod("com/github/goblinbr/testbytecode/GClassUsesAClassAndEClass", "Integer invalidMethod(Integer)"); 115 | Assert.assertEquals(0,callers.size()); 116 | } catch (IOException e) { 117 | e.printStackTrace(); 118 | Assert.fail("Should not throw an exception"); 119 | } 120 | } 121 | 122 | @Test 123 | public void findCallersOfClassGClassAnyMethodShouldReturnOne(){ 124 | try { 125 | Set callers = javaScanner.findCallersOfAnyMethod("com/github/goblinbr/testbytecode/GClassUsesAClassAndEClass"); 126 | Assert.assertEquals(1,callers.size()); 127 | } catch (IOException e) { 128 | e.printStackTrace(); 129 | Assert.fail("Should not throw an exception"); 130 | } 131 | } 132 | 133 | @Test 134 | public void findCallersOfClassDInterfaceAnyMethodShouldReturnFive(){ 135 | try { 136 | Set callers = javaScanner.findCallersOfAnyMethod("com/github/goblinbr/testbytecode/DInterface"); 137 | Assert.assertEquals(5,callers.size()); 138 | } catch (IOException e) { 139 | e.printStackTrace(); 140 | Assert.fail("Should not throw an exception"); 141 | } 142 | } 143 | 144 | @Test 145 | public void findCallHierarchyOfClassDInterfaceShouldReturnFive(){ 146 | try { 147 | CallHierarchy callHierarchy = javaScanner.findCallHierarchyOfAnyMethod("com/github/goblinbr/testbytecode/DInterface"); 148 | List callers = callHierarchy.getCallers(); 149 | Assert.assertEquals(5,callers.size()); 150 | } catch (IOException e) { 151 | e.printStackTrace(); 152 | Assert.fail("Should not throw an exception"); 153 | } 154 | } 155 | 156 | @Test 157 | public void findCallersOfJClassCreateIClassShouldReturnOne(){ 158 | try { 159 | Set callers = javaScanner.findCallersOfMethod("com/github/goblinbr/testbytecode/JClassUsesIClass", "com.github.goblinbr.testbytecode.IClass createIClass(com.github.goblinbr.testbytecode.IClass,com.github.goblinbr.testbytecode.IClass)"); 160 | Assert.assertEquals(1,callers.size()); 161 | } catch (IOException e) { 162 | e.printStackTrace(); 163 | Assert.fail("Should not throw an exception"); 164 | } 165 | } 166 | 167 | } 168 | --------------------------------------------------------------------------------