From 62de4ca9ecc788ebb6d08fb8bd8e83f029fb38dc Mon Sep 17 00:00:00 2001 From: nuri Date: Mon, 17 Jun 2024 00:13:18 +0200 Subject: [PATCH 1/6] feat: base spoon setup and method extraction --- pom.xml | 58 ++++++++++++ src/main/java/tum/dpid/App.java | 45 +++++++++- src/main/java/tum/dpid/cfg/CfgExtractor.java | 88 +++++++++++++++++++ .../processor/CallHierarchyProcessor.java | 43 +++++++++ 4 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 src/main/java/tum/dpid/cfg/CfgExtractor.java create mode 100644 src/main/java/tum/dpid/processor/CallHierarchyProcessor.java diff --git a/pom.xml b/pom.xml index 2c02afe..61cc124 100644 --- a/pom.xml +++ b/pom.xml @@ -24,8 +24,66 @@ 4.13.1 test + + + + fr.inria.gforge.spoon + spoon-core + + 10.4.1 + + + + + + org.soot-oss + sootup.core + 1.2.0 + + + org.soot-oss + sootup.java.core + 1.2.0 + + + org.soot-oss + sootup.java.sourcecode + 1.2.0 + + + org.soot-oss + sootup.java.bytecode + 1.2.0 + + + org.soot-oss + sootup.jimple.parser + 1.2.0 + + + org.soot-oss + sootup.callgraph + 1.2.0 + + + org.soot-oss + sootup.analysis + 1.2.0 + + + + + jitpack.io + https://jitpack.io + + + /maven.google.com + https://maven.google.com + + + diff --git a/src/main/java/tum/dpid/App.java b/src/main/java/tum/dpid/App.java index cf88980..1c7d904 100644 --- a/src/main/java/tum/dpid/App.java +++ b/src/main/java/tum/dpid/App.java @@ -1,5 +1,16 @@ package tum.dpid; +import spoon.Launcher; +import spoon.reflect.CtModel; +import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.CtType; +import tum.dpid.processor.CallHierarchyProcessor; + +import java.util.Map; +import java.util.Set; + + /** * Hello world! * @@ -8,6 +19,38 @@ public class App { public static void main( String[] args ) { - System.out.println( "Hello World!" ); + Launcher launcher =new Launcher(); + launcher.getEnvironment().setNoClasspath(true); + + //String testDirectory = "src/main/resources/tester"; //"../../../resources/tester"; + String directoryPath = "../../fromItestra/LoopAntiPattern"; + launcher.addInputResource(directoryPath); + launcher.buildModel(); + CtModel model = launcher.getModel(); + + +// /**/ + CallHierarchyProcessor processor = new CallHierarchyProcessor(); + for (CtType type : launcher.getFactory().Class().getAll()) { + if (type instanceof CtClass) { + for (CtMethod method : ((CtClass) type).getMethods()) { + processor.visitCtMethod(method); + } + } + } + + Map> callHierarchy = processor.getCallHierarchy(); + + callHierarchy.forEach((method, calls) -> { + System.out.println(method + " calls:"); + StringBuilder indent = new StringBuilder("---"); + for (String call : calls) { + System.out.println(indent + " " + call); + indent.append("---"); + } + }); + } + } + diff --git a/src/main/java/tum/dpid/cfg/CfgExtractor.java b/src/main/java/tum/dpid/cfg/CfgExtractor.java new file mode 100644 index 0000000..bacd985 --- /dev/null +++ b/src/main/java/tum/dpid/cfg/CfgExtractor.java @@ -0,0 +1,88 @@ +package tum.dpid.cfg; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Optional; + +import sootup.callgraph.CallGraph; +import sootup.callgraph.CallGraphAlgorithm; +import sootup.callgraph.ClassHierarchyAnalysisAlgorithm; +import sootup.core.inputlocation.AnalysisInputLocation; +import sootup.core.jimple.common.expr.JVirtualInvokeExpr; +import sootup.core.jimple.common.stmt.JInvokeStmt; +import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSignature; +import sootup.core.typehierarchy.ViewTypeHierarchy; +import sootup.core.types.ClassType; +import sootup.core.types.VoidType; +import sootup.core.util.DotExporter; +import sootup.core.views.View; +import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; +import sootup.java.bytecode.inputlocation.PathBasedAnalysisInputLocation; +import sootup.java.core.JavaIdentifierFactory; +import sootup.java.core.JavaSootMethod; +import sootup.java.core.language.JavaJimple; +import sootup.java.core.types.JavaClassType; +import sootup.java.core.views.JavaView; +import sootup.java.sourcecode.inputlocation.JavaSourcePathAnalysisInputLocation; + +/** + * Experimental class to extract control flow graph of project by using SootUp + */ + +public class CfgExtractor { + + public void CfgExtractorFunc(String inputPath){ + + AnalysisInputLocation inputLocation = + new JavaClassPathAnalysisInputLocation("target/classes"); + + JavaView view = new JavaView(inputLocation); + + System.out.println("All Class: " + view.getClasses()); + + JavaClassType classType = + view.getIdentifierFactory().getClassType("tum.dpid.App"); + + if (!view.getClass(classType).isPresent()) { + System.out.println("Class not found!"); + return; + } + + // Retrieve the specified class from the project. + SootClass sootClass = view.getClass(classType).get(); + + + MethodSignature methodSignature = view.getIdentifierFactory().getMethodSignature( + classType, "main", "void", Collections.singletonList("java.lang.String[]")); + + Optional opt = view.getMethod(methodSignature); + + // Create type hierarchy and CHA + final ViewTypeHierarchy typeHierarchy = new ViewTypeHierarchy(view); + + CallGraphAlgorithm cha = new ClassHierarchyAnalysisAlgorithm(view); + + // Create CG by initializing CHA with entry method(s) + CallGraph cg = cha.initialize(Collections.singletonList(methodSignature)); + + cg.callsFrom(methodSignature).forEach(System.out::println); + + var x = DotExporter.createUrlToWebeditor( opt.get().getBody().getStmtGraph()); + + System.out.println("Dot Graph is " + x); + + //ystem.out.println(cg); + } + + /* //CFG with sootup + String sourcePath = "../../fromItestra/LoopAntiPattern"; + String binaryPath = "../../fromItestra/LoopAntiPattern/build/classes/java/main/com/example/LoopAntiPattern" ; //LoopAntiPattern-0.0.1-SNAPSHOT.jar" + CfgExtractor cfgExtractor = new CfgExtractor(); + cfgExtractor.CfgExtractorFunc(binaryPath); + System.out.println("*************************************************************************************************************************************"); + + */ +} diff --git a/src/main/java/tum/dpid/processor/CallHierarchyProcessor.java b/src/main/java/tum/dpid/processor/CallHierarchyProcessor.java new file mode 100644 index 0000000..7e64000 --- /dev/null +++ b/src/main/java/tum/dpid/processor/CallHierarchyProcessor.java @@ -0,0 +1,43 @@ +package tum.dpid.processor; + +import spoon.reflect.CtModel; +import spoon.reflect.code.CtInvocation; +import spoon.reflect.declaration.CtExecutable; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.reference.CtExecutableReference; +import spoon.reflect.visitor.CtAbstractVisitor; +import spoon.reflect.visitor.filter.TypeFilter; + +import java.util.*; +import java.util.stream.Collectors; +import spoon.reflect.visitor.PrinterHelper; + + +public class CallHierarchyProcessor extends CtAbstractVisitor { + private Map> callHierarchy = new HashMap<>(); + + //ToDo add recursion + /*This prints one degree of children*/ + @Override + public void visitCtMethod(CtMethod method) { + String methodSignature = method.getDeclaringType().getQualifiedName() + "#" + method.getSignature(); + Set calledMethods = new HashSet<>(); + method.getElements(e -> e instanceof CtInvocation) + .forEach(e -> { + CtInvocation invocation = (CtInvocation) e; + CtExecutableReference executable = invocation.getExecutable(); + if (executable.getDeclaringType() != null) { + //System.out.println("CtExecutableReference is " + executable); + String calledMethodSignature = executable.getDeclaringType().getQualifiedName() + "#" + executable.getSignature(); + calledMethods.add(calledMethodSignature); + } + }); + callHierarchy.put(methodSignature, calledMethods); + + } + + public Map> getCallHierarchy() { + return callHierarchy; + } + +} From 41d12cedd10425b207db04c377b90c598618b407 Mon Sep 17 00:00:00 2001 From: nuri Date: Thu, 20 Jun 2024 15:08:39 +0200 Subject: [PATCH 2/6] chore: runner class --- src/main/java/tum/dpid/App.java | 56 ---------------------- src/main/java/tum/dpid/Runner.java | 76 ++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 56 deletions(-) delete mode 100644 src/main/java/tum/dpid/App.java create mode 100644 src/main/java/tum/dpid/Runner.java diff --git a/src/main/java/tum/dpid/App.java b/src/main/java/tum/dpid/App.java deleted file mode 100644 index 1c7d904..0000000 --- a/src/main/java/tum/dpid/App.java +++ /dev/null @@ -1,56 +0,0 @@ -package tum.dpid; - -import spoon.Launcher; -import spoon.reflect.CtModel; -import spoon.reflect.declaration.CtClass; -import spoon.reflect.declaration.CtMethod; -import spoon.reflect.declaration.CtType; -import tum.dpid.processor.CallHierarchyProcessor; - -import java.util.Map; -import java.util.Set; - - -/** - * Hello world! - * - */ -public class App -{ - public static void main( String[] args ) - { - Launcher launcher =new Launcher(); - launcher.getEnvironment().setNoClasspath(true); - - //String testDirectory = "src/main/resources/tester"; //"../../../resources/tester"; - String directoryPath = "../../fromItestra/LoopAntiPattern"; - launcher.addInputResource(directoryPath); - launcher.buildModel(); - CtModel model = launcher.getModel(); - - -// /**/ - CallHierarchyProcessor processor = new CallHierarchyProcessor(); - for (CtType type : launcher.getFactory().Class().getAll()) { - if (type instanceof CtClass) { - for (CtMethod method : ((CtClass) type).getMethods()) { - processor.visitCtMethod(method); - } - } - } - - Map> callHierarchy = processor.getCallHierarchy(); - - callHierarchy.forEach((method, calls) -> { - System.out.println(method + " calls:"); - StringBuilder indent = new StringBuilder("---"); - for (String call : calls) { - System.out.println(indent + " " + call); - indent.append("---"); - } - }); - - } - -} - diff --git a/src/main/java/tum/dpid/Runner.java b/src/main/java/tum/dpid/Runner.java new file mode 100644 index 0000000..f193a0a --- /dev/null +++ b/src/main/java/tum/dpid/Runner.java @@ -0,0 +1,76 @@ +package tum.dpid; + +import spoon.Launcher; +import spoon.reflect.CtModel; +import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.visitor.filter.TypeFilter; +import tum.dpid.processor.DatabaseMethodFinder; +import tum.dpid.processor.MethodCallTracer; + +import java.util.List; +import java.util.Set; + + +/** + * Hello world! + * + */ +public class Runner +{ + public static void main( String[] args ) + { + Launcher launcher =new Launcher(); + launcher.getEnvironment().setNoClasspath(true); + + //String testDirectory = "src/main/resources/tester"; //"../../../resources/tester"; + String directoryPath = "../../fromItestra/LoopAntiPattern"; + launcher.addInputResource(directoryPath); + launcher.buildModel(); + CtModel model = launcher.getModel(); + + List> allClasses = model.getElements(new TypeFilter<>(CtClass.class)); + List> allMethods = model.getElements(new TypeFilter<>(CtMethod.class)); + // Step 1: Identify Database Methods + + Set> databaseMethods = DatabaseMethodFinder.findDatabaseMethods(model, allClasses); + + // Step 2: Trace Method Calls + Set> callingMethods = MethodCallTracer.findMethodsCallingDatabaseMethods(model,allMethods, databaseMethods); + + // Print results + for (CtMethod method : callingMethods) { + System.out.println("Method calling database: " + method.getSignature()); + } + + Set callChains = MethodCallTracer.traceMethodCalls(databaseMethods, allMethods); + // Print results + for (String callChain : callChains) { + System.out.println("Call chain: " + callChain); + } +// +//// /*First Degree Caller*/ +// CallHierarchyProcessor processor = new CallHierarchyProcessor(); +// for (CtType type : launcher.getFactory().Class().getAll()) { +// if (type instanceof CtClass) { +// for (CtMethod method : ((CtClass) type).getMethods()) { +// processor.visitCtMethod(method); +// } +// } +// } +// +// Map> callHierarchy = processor.getCallHierarchy(); +// +// callHierarchy.forEach((method, calls) -> { +// System.out.println(method + " calls:"); +// StringBuilder indent = new StringBuilder("---"); +// for (String call : calls) { +// System.out.println(indent + " " + call); +// indent.append("---"); +// } +// }); + + } + +} + From 2e9b375338196e724c4b09622035a6d750fb62cf Mon Sep 17 00:00:00 2001 From: nuri Date: Thu, 20 Jun 2024 15:09:13 +0200 Subject: [PATCH 3/6] feat: db method find and method call chain tracer --- pom.xml | 1 - .../dpid/processor/DatabaseMethodFinder.java | 31 ++++ .../tum/dpid/processor/MethodCallTracer.java | 168 ++++++++++++++++++ 3 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 src/main/java/tum/dpid/processor/DatabaseMethodFinder.java create mode 100644 src/main/java/tum/dpid/processor/MethodCallTracer.java diff --git a/pom.xml b/pom.xml index 61cc124..c224ab0 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,6 @@ fr.inria.gforge.spoon spoon-core - 10.4.1 diff --git a/src/main/java/tum/dpid/processor/DatabaseMethodFinder.java b/src/main/java/tum/dpid/processor/DatabaseMethodFinder.java new file mode 100644 index 0000000..2870169 --- /dev/null +++ b/src/main/java/tum/dpid/processor/DatabaseMethodFinder.java @@ -0,0 +1,31 @@ +package tum.dpid.processor; + +import spoon.Launcher; +import spoon.reflect.CtModel; +import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.visitor.filter.TypeFilter; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class DatabaseMethodFinder { + + public static Set> findDatabaseMethods(CtModel model, List> allClasses) { + Set> databaseMethods = new HashSet<>(); + + for (CtClass ctClass : allClasses) { + if (isDatabaseClass(ctClass)) { + databaseMethods.addAll(ctClass.getMethods()); + } + } + return databaseMethods; + } + + private static boolean isDatabaseClass(CtClass ctClass) { + // Define the package name where database-related classes are located + String databasePackage = "com.example.LoopAntiPattern.data.repository"; + return ctClass.getPackage().getQualifiedName().startsWith(databasePackage); + } +} diff --git a/src/main/java/tum/dpid/processor/MethodCallTracer.java b/src/main/java/tum/dpid/processor/MethodCallTracer.java new file mode 100644 index 0000000..071f0bc --- /dev/null +++ b/src/main/java/tum/dpid/processor/MethodCallTracer.java @@ -0,0 +1,168 @@ +package tum.dpid.processor; + +import spoon.Launcher; +import spoon.reflect.CtModel; +import spoon.reflect.code.CtInvocation; +import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.reference.CtExecutableReference; +import spoon.reflect.visitor.filter.TypeFilter; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class MethodCallTracer { + + public static Set> findMethodsCallingDatabaseMethods(CtModel model, List> allMethods, Set> databaseMethods) { +// Launcher launcher = new Launcher(); +// launcher.addInputResource(projectPath); +// launcher.buildModel(); +// CtModel model = launcher.getModel(); + + Set> callingMethods = new HashSet<>(); + + for (CtMethod method : allMethods) { + for (CtExecutableReference calledMethodRef : method.getElements(new TypeFilter<>(CtExecutableReference.class))) { + for (CtMethod dbMethod : databaseMethods) { + if (calledMethodRef.getSimpleName().equals(dbMethod.getSimpleName()) && + calledMethodRef.getDeclaringType().equals(dbMethod.getDeclaringType().getReference())) { + callingMethods.add(method); + } + } + } + } + + return callingMethods; + } + + + public static Set traceMethodCalls(Set> databaseMethods, List> allMethods) { + + Set callChains = new HashSet<>(); + Set> visitedMethods = new HashSet<>(); + + for (CtMethod dbMethod : databaseMethods) { + traceMethod(dbMethod, allMethods, "", callChains, visitedMethods); + } + + return callChains; + } + + private static void traceMethod(CtMethod method, List> allMethods, String callChain, Set callChains, Set> visitedMethods) { + if (visitedMethods.contains(method)) { + return; + } + visitedMethods.add(method); + + callChain = method.getSimpleName() + " #" + method.getDeclaringType().getQualifiedName() + " -> " + callChain; + + for (CtMethod caller : findCallingMethods(method, allMethods)) { + traceMethod(caller,allMethods, callChain, callChains, visitedMethods); + } + callChains.add(callChain); + } + +// private static Set> findCallingMethods(CtMethod method) { +// Set> callingMethods = new HashSet<>(); +// CtClass parentClass = (CtClass) method.getDeclaringType(); +// +// for (CtMethod candidateMethod : parentClass.getMethods()) { +// for (CtInvocation invocation : candidateMethod.getElements(new TypeFilter<>(CtInvocation.class))) { +// CtExecutableReference executableRef = invocation.getExecutable(); +// //if (executableRef != null) { +// if (executableRef.getDeclaration()!=null && executableRef.getDeclaration().equals(method)) { +// callingMethods.add(candidateMethod); +// } +// +// } +// } +// +// return callingMethods; +// } + + private static Set> findCallingMethods(CtMethod method, List> allMethods) { + Set> callingMethods = new HashSet<>(); + + for (CtMethod candidateMethod : allMethods) { + for (CtInvocation invocation : candidateMethod.getElements(new TypeFilter<>(CtInvocation.class))) { + CtExecutableReference executableRef = invocation.getExecutable(); + if (executableRef.getDeclaration() != null && executableRef.getDeclaration().equals(method)) { + callingMethods.add(candidateMethod); + } + } + } + + return callingMethods; + } + +} + +/* +* import spoon.Launcher; +import spoon.reflect.CtModel; +import spoon.reflect.code.CtInvocation; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.reference.CtExecutableReference; +import spoon.reflect.visitor.filter.TypeFilter; + +import java.util.HashSet; +import java.util.Set; + +public class CallChainTracer { + + public static Set traceMethodCalls(String projectPath, Set> databaseMethods) { + Launcher launcher = new Launcher(); + launcher.addInputResource(projectPath); + launcher.buildModel(); + CtModel model = launcher.getModel(); + + Set callChains = new HashSet<>(); + Set> visitedMethods = new HashSet<>(); + + for (CtMethod dbMethod : databaseMethods) { + traceMethod(dbMethod, "", callChains, visitedMethods); + } + + return callChains; + } + + private static void traceMethod(CtMethod method, String callChain, Set callChains, Set> visitedMethods) { + if (visitedMethods.contains(method)) { + return; + } + visitedMethods.add(method); + + callChain = method.getSimpleName() + " -> " + callChain; + + for (CtMethod caller : findCallingMethods(method)) { + traceMethod(caller, callChain, callChains, visitedMethods); + } + + + } + + private static Set> findCallingMethods(CtMethod method) { + Set> callingMethods = new HashSet<>(); + CtClass parentClass = method.getDeclaringType(); + + for (CtMethod candidateMethod : parentClass.getMethods()) { + for (CtInvocation invocation : candidateMethod.getElements(new TypeFilter<>(CtInvocation.class))) { + CtExecutableReference executableRef = invocation.getExecutable(); + if (executableRef.getDeclaration().equals(method)) { + callingMethods.add(candidateMethod); + } + } + } + + return callingMethods; + } + + private static boolean isEntryPoint(CtMethod method) { + // Check for controller annotations or method naming conventions + return method.hasAnnotation("org.springframework.web.bind.annotation.PostMapping") || + method.hasAnnotation("org.springframework.web.bind.annotation.GetMapping") || + method.hasAnnotation("org.springframework.web.bind.annotation.RequestMapping"); + } +} +*/ From 2bffb048d0e641dccd348e1af96da63b07b191dc Mon Sep 17 00:00:00 2001 From: nuri Date: Thu, 11 Jul 2024 14:39:16 +0200 Subject: [PATCH 4/6] fix: db method getter - setter exclusion --- src/main/java/tum/dpid/Runner.java | 24 +++-- .../dpid/processor/DatabaseMethodFinder.java | 22 ++++- .../tum/dpid/processor/MethodCallTracer.java | 95 +------------------ 3 files changed, 37 insertions(+), 104 deletions(-) diff --git a/src/main/java/tum/dpid/Runner.java b/src/main/java/tum/dpid/Runner.java index f193a0a..82d4d01 100644 --- a/src/main/java/tum/dpid/Runner.java +++ b/src/main/java/tum/dpid/Runner.java @@ -29,27 +29,31 @@ public static void main( String[] args ) launcher.buildModel(); CtModel model = launcher.getModel(); + //All Java Classes in the project List> allClasses = model.getElements(new TypeFilter<>(CtClass.class)); + + //All Methods in the project List> allMethods = model.getElements(new TypeFilter<>(CtMethod.class)); - // Step 1: Identify Database Methods + // Database methods in project Set> databaseMethods = DatabaseMethodFinder.findDatabaseMethods(model, allClasses); - - // Step 2: Trace Method Calls - Set> callingMethods = MethodCallTracer.findMethodsCallingDatabaseMethods(model,allMethods, databaseMethods); - - // Print results - for (CtMethod method : callingMethods) { - System.out.println("Method calling database: " + method.getSignature()); + for (CtMethod method : databaseMethods) { + System.out.println("DATABASE Method is: " + method.getSignature() + "#" + method.getDeclaringType().getQualifiedName()); } +// Set> callingMethods = MethodCallTracer.findMethodsCallingDatabaseMethods(model,allMethods, databaseMethods); +// for (CtMethod method : callingMethods) { +// System.out.println("Method calling database: " + method.getSignature()); +// } + + //Method Call Tracer File Set callChains = MethodCallTracer.traceMethodCalls(databaseMethods, allMethods); // Print results for (String callChain : callChains) { System.out.println("Call chain: " + callChain); } -// -//// /*First Degree Caller*/ + + /*First Degree Caller*/ // CallHierarchyProcessor processor = new CallHierarchyProcessor(); // for (CtType type : launcher.getFactory().Class().getAll()) { // if (type instanceof CtClass) { diff --git a/src/main/java/tum/dpid/processor/DatabaseMethodFinder.java b/src/main/java/tum/dpid/processor/DatabaseMethodFinder.java index 2870169..a48df80 100644 --- a/src/main/java/tum/dpid/processor/DatabaseMethodFinder.java +++ b/src/main/java/tum/dpid/processor/DatabaseMethodFinder.java @@ -4,6 +4,7 @@ import spoon.reflect.CtModel; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.ModifierKind; import spoon.reflect.visitor.filter.TypeFilter; import java.util.HashSet; @@ -17,7 +18,11 @@ public static Set> findDatabaseMethods(CtModel model, List ctClass : allClasses) { if (isDatabaseClass(ctClass)) { - databaseMethods.addAll(ctClass.getMethods()); + for(CtMethod method : ctClass.getMethods()) { + if (!isGetterOrSetter(method)) { + databaseMethods.add(method); + } + } } } return databaseMethods; @@ -28,4 +33,19 @@ private static boolean isDatabaseClass(CtClass ctClass) { String databasePackage = "com.example.LoopAntiPattern.data.repository"; return ctClass.getPackage().getQualifiedName().startsWith(databasePackage); } + + private static boolean isGetterOrSetter(CtMethod method) { + String methodName = method.getSimpleName(); + boolean isStatic = method.getModifiers().contains(ModifierKind.STATIC); + + // Check for getter method + boolean isGetter = !isStatic && method.getParameters().isEmpty() && + (methodName.startsWith("get") || methodName.startsWith("is")); + + // Check for setter method + boolean isSetter = !isStatic && method.getParameters().size() == 1 && + methodName.startsWith("set"); + + return isGetter || isSetter; + } } diff --git a/src/main/java/tum/dpid/processor/MethodCallTracer.java b/src/main/java/tum/dpid/processor/MethodCallTracer.java index 071f0bc..b9b9e25 100644 --- a/src/main/java/tum/dpid/processor/MethodCallTracer.java +++ b/src/main/java/tum/dpid/processor/MethodCallTracer.java @@ -15,11 +15,6 @@ public class MethodCallTracer { public static Set> findMethodsCallingDatabaseMethods(CtModel model, List> allMethods, Set> databaseMethods) { -// Launcher launcher = new Launcher(); -// launcher.addInputResource(projectPath); -// launcher.buildModel(); -// CtModel model = launcher.getModel(); - Set> callingMethods = new HashSet<>(); for (CtMethod method : allMethods) { @@ -32,7 +27,6 @@ public static Set> findMethodsCallingDatabaseMethods(CtModel model, } } } - return callingMethods; } @@ -58,28 +52,12 @@ private static void traceMethod(CtMethod method, List> allMethods callChain = method.getSimpleName() + " #" + method.getDeclaringType().getQualifiedName() + " -> " + callChain; for (CtMethod caller : findCallingMethods(method, allMethods)) { - traceMethod(caller,allMethods, callChain, callChains, visitedMethods); + traceMethod(caller, allMethods, callChain, callChains, visitedMethods); } callChains.add(callChain); + } -// private static Set> findCallingMethods(CtMethod method) { -// Set> callingMethods = new HashSet<>(); -// CtClass parentClass = (CtClass) method.getDeclaringType(); -// -// for (CtMethod candidateMethod : parentClass.getMethods()) { -// for (CtInvocation invocation : candidateMethod.getElements(new TypeFilter<>(CtInvocation.class))) { -// CtExecutableReference executableRef = invocation.getExecutable(); -// //if (executableRef != null) { -// if (executableRef.getDeclaration()!=null && executableRef.getDeclaration().equals(method)) { -// callingMethods.add(candidateMethod); -// } -// -// } -// } -// -// return callingMethods; -// } private static Set> findCallingMethods(CtMethod method, List> allMethods) { Set> callingMethods = new HashSet<>(); @@ -97,72 +75,3 @@ private static Set> findCallingMethods(CtMethod method, List traceMethodCalls(String projectPath, Set> databaseMethods) { - Launcher launcher = new Launcher(); - launcher.addInputResource(projectPath); - launcher.buildModel(); - CtModel model = launcher.getModel(); - - Set callChains = new HashSet<>(); - Set> visitedMethods = new HashSet<>(); - - for (CtMethod dbMethod : databaseMethods) { - traceMethod(dbMethod, "", callChains, visitedMethods); - } - - return callChains; - } - - private static void traceMethod(CtMethod method, String callChain, Set callChains, Set> visitedMethods) { - if (visitedMethods.contains(method)) { - return; - } - visitedMethods.add(method); - - callChain = method.getSimpleName() + " -> " + callChain; - - for (CtMethod caller : findCallingMethods(method)) { - traceMethod(caller, callChain, callChains, visitedMethods); - } - - - } - - private static Set> findCallingMethods(CtMethod method) { - Set> callingMethods = new HashSet<>(); - CtClass parentClass = method.getDeclaringType(); - - for (CtMethod candidateMethod : parentClass.getMethods()) { - for (CtInvocation invocation : candidateMethod.getElements(new TypeFilter<>(CtInvocation.class))) { - CtExecutableReference executableRef = invocation.getExecutable(); - if (executableRef.getDeclaration().equals(method)) { - callingMethods.add(candidateMethod); - } - } - } - - return callingMethods; - } - - private static boolean isEntryPoint(CtMethod method) { - // Check for controller annotations or method naming conventions - return method.hasAnnotation("org.springframework.web.bind.annotation.PostMapping") || - method.hasAnnotation("org.springframework.web.bind.annotation.GetMapping") || - method.hasAnnotation("org.springframework.web.bind.annotation.RequestMapping"); - } -} -*/ From 7b5fc2e863eb754afef8c4f3ff0bb3c4184f46e8 Mon Sep 17 00:00:00 2001 From: nuri Date: Fri, 12 Jul 2024 13:51:08 +0200 Subject: [PATCH 5/6] chore: packaging --- .../DatabaseMethodFinder.java | 4 +--- .../v1}/CallHierarchyProcessor.java | 12 ++++------- .../v1}/MethodCallTracer.java | 21 ++++++++++++++++--- 3 files changed, 23 insertions(+), 14 deletions(-) rename src/main/java/tum/dpid/{processor => services}/DatabaseMethodFinder.java (94%) rename src/main/java/tum/dpid/{processor => services/v1}/CallHierarchyProcessor.java (84%) rename src/main/java/tum/dpid/{processor => services/v1}/MethodCallTracer.java (80%) diff --git a/src/main/java/tum/dpid/processor/DatabaseMethodFinder.java b/src/main/java/tum/dpid/services/DatabaseMethodFinder.java similarity index 94% rename from src/main/java/tum/dpid/processor/DatabaseMethodFinder.java rename to src/main/java/tum/dpid/services/DatabaseMethodFinder.java index a48df80..e88d105 100644 --- a/src/main/java/tum/dpid/processor/DatabaseMethodFinder.java +++ b/src/main/java/tum/dpid/services/DatabaseMethodFinder.java @@ -1,11 +1,9 @@ -package tum.dpid.processor; +package tum.dpid.services; -import spoon.Launcher; import spoon.reflect.CtModel; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.ModifierKind; -import spoon.reflect.visitor.filter.TypeFilter; import java.util.HashSet; import java.util.List; diff --git a/src/main/java/tum/dpid/processor/CallHierarchyProcessor.java b/src/main/java/tum/dpid/services/v1/CallHierarchyProcessor.java similarity index 84% rename from src/main/java/tum/dpid/processor/CallHierarchyProcessor.java rename to src/main/java/tum/dpid/services/v1/CallHierarchyProcessor.java index 7e64000..26f4771 100644 --- a/src/main/java/tum/dpid/processor/CallHierarchyProcessor.java +++ b/src/main/java/tum/dpid/services/v1/CallHierarchyProcessor.java @@ -1,22 +1,18 @@ -package tum.dpid.processor; +package tum.dpid.services.v1; -import spoon.reflect.CtModel; import spoon.reflect.code.CtInvocation; -import spoon.reflect.declaration.CtExecutable; import spoon.reflect.declaration.CtMethod; import spoon.reflect.reference.CtExecutableReference; import spoon.reflect.visitor.CtAbstractVisitor; -import spoon.reflect.visitor.filter.TypeFilter; import java.util.*; -import java.util.stream.Collectors; -import spoon.reflect.visitor.PrinterHelper; - +/* + Previous version of method call chain processor but it lacks of all. It only prints first degree of method call chain + */ public class CallHierarchyProcessor extends CtAbstractVisitor { private Map> callHierarchy = new HashMap<>(); - //ToDo add recursion /*This prints one degree of children*/ @Override public void visitCtMethod(CtMethod method) { diff --git a/src/main/java/tum/dpid/processor/MethodCallTracer.java b/src/main/java/tum/dpid/services/v1/MethodCallTracer.java similarity index 80% rename from src/main/java/tum/dpid/processor/MethodCallTracer.java rename to src/main/java/tum/dpid/services/v1/MethodCallTracer.java index b9b9e25..ca9c582 100644 --- a/src/main/java/tum/dpid/processor/MethodCallTracer.java +++ b/src/main/java/tum/dpid/services/v1/MethodCallTracer.java @@ -1,9 +1,7 @@ -package tum.dpid.processor; +package tum.dpid.services.v1; -import spoon.Launcher; import spoon.reflect.CtModel; import spoon.reflect.code.CtInvocation; -import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtMethod; import spoon.reflect.reference.CtExecutableReference; import spoon.reflect.visitor.filter.TypeFilter; @@ -12,6 +10,9 @@ import java.util.List; import java.util.Set; +/* + Previous version of method call chain processor. + */ public class MethodCallTracer { public static Set> findMethodsCallingDatabaseMethods(CtModel model, List> allMethods, Set> databaseMethods) { @@ -75,3 +76,17 @@ private static Set> findCallingMethods(CtMethod method, List> callingMethods = MethodCallTracer.findMethodsCallingDatabaseMethods(model,allMethods, databaseMethods); +// for (CtMethod method : callingMethods) { +// System.out.println("Method calling database: " + method.getSignature()); +// } + +// +// //Method Call Tracer File +// Set callChains = MethodCallTracer.traceMethodCalls(databaseMethods, allMethods); +// // Print results +// for (String callChain : callChains) { +// System.out.println("Call chain: " + callChain); +// } From 688b6e96ef81bddec5a439404d30b927b2f7e995 Mon Sep 17 00:00:00 2001 From: nuri Date: Fri, 12 Jul 2024 13:51:39 +0200 Subject: [PATCH 6/6] feat: method call chain processor implementation --- src/main/java/tum/dpid/Runner.java | 73 ++++++++++--------- .../processors/ClassHierarchyOrder.java | 39 ++++++++++ .../services/processors/MethodCallChain.java | 73 +++++++++++++++++++ .../dpid/services/processors/MethodOrder.java | 38 ++++++++++ 4 files changed, 187 insertions(+), 36 deletions(-) create mode 100644 src/main/java/tum/dpid/services/processors/ClassHierarchyOrder.java create mode 100644 src/main/java/tum/dpid/services/processors/MethodCallChain.java create mode 100644 src/main/java/tum/dpid/services/processors/MethodOrder.java diff --git a/src/main/java/tum/dpid/Runner.java b/src/main/java/tum/dpid/Runner.java index 82d4d01..a3bebca 100644 --- a/src/main/java/tum/dpid/Runner.java +++ b/src/main/java/tum/dpid/Runner.java @@ -4,23 +4,29 @@ import spoon.reflect.CtModel; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtMethod; +import spoon.reflect.reference.CtExecutableReference; +import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.filter.TypeFilter; -import tum.dpid.processor.DatabaseMethodFinder; -import tum.dpid.processor.MethodCallTracer; +import tum.dpid.services.DatabaseMethodFinder; +import tum.dpid.services.processors.ClassHierarchyOrder; +import tum.dpid.services.processors.MethodCallChain; +import tum.dpid.services.processors.MethodOrder; import java.util.List; +import java.util.Map; import java.util.Set; /** - * Hello world! + * Runner class of method call chain processor and analyzer by utilizing spoon * */ public class Runner { + public static void main( String[] args ) { - Launcher launcher =new Launcher(); + Launcher launcher = new Launcher(); launcher.getEnvironment().setNoClasspath(true); //String testDirectory = "src/main/resources/tester"; //"../../../resources/tester"; @@ -35,46 +41,41 @@ public static void main( String[] args ) //All Methods in the project List> allMethods = model.getElements(new TypeFilter<>(CtMethod.class)); - // Database methods in project + // Methods which makes request to database in project Set> databaseMethods = DatabaseMethodFinder.findDatabaseMethods(model, allClasses); for (CtMethod method : databaseMethods) { - System.out.println("DATABASE Method is: " + method.getSignature() + "#" + method.getDeclaringType().getQualifiedName()); + System.out.println("Database Method is: " + method.getSignature() + " (" + method.getDeclaringType().getQualifiedName() + ")"); } -// Set> callingMethods = MethodCallTracer.findMethodsCallingDatabaseMethods(model,allMethods, databaseMethods); -// for (CtMethod method : callingMethods) { -// System.out.println("Method calling database: " + method.getSignature()); -// } + //Initialize class hierarchy order processor and start processing it + ClassHierarchyOrder classHierarchyOrder = new ClassHierarchyOrder(); + launcher.addProcessor(classHierarchyOrder); + launcher.process(); - //Method Call Tracer File - Set callChains = MethodCallTracer.traceMethodCalls(databaseMethods, allMethods); - // Print results - for (String callChain : callChains) { - System.out.println("Call chain: " + callChain); - } + //Initialize method execution order processor (call chain of method) and start processing it + MethodOrder methodOrder = new MethodOrder(); + launcher.addProcessor(methodOrder); + launcher.process(); - /*First Degree Caller*/ -// CallHierarchyProcessor processor = new CallHierarchyProcessor(); -// for (CtType type : launcher.getFactory().Class().getAll()) { -// if (type instanceof CtClass) { -// for (CtMethod method : ((CtClass) type).getMethods()) { -// processor.visitCtMethod(method); -// } -// } -// } -// -// Map> callHierarchy = processor.getCallHierarchy(); -// -// callHierarchy.forEach((method, calls) -> { -// System.out.println(method + " calls:"); -// StringBuilder indent = new StringBuilder("---"); -// for (String call : calls) { -// System.out.println(indent + " " + call); -// indent.append("---"); -// } -// }); + Map, List>> callList = methodOrder.getCallList(); + Map, Set>> classHierarchy = classHierarchyOrder.getClassImplementors() ; + //Process each method in the project and print out their call chain + for (CtMethod ctMethod: allMethods) { + List methodCallHierarchies = MethodCallChain.processMethod(ctMethod, callList, classHierarchy); + if (methodCallHierarchies.isEmpty()) { + System.out.println("No method `" + ctMethod.getDeclaringType() + "` found. \n"); + } + if (methodCallHierarchies.size() > 1) { + System.out.println("Found " + methodCallHierarchies.size() + " matching methods...\n"); + } + for (MethodCallChain methodCallHierarchy : methodCallHierarchies) { + methodCallHierarchy.printCallChain(); + System.out.println(); + } + } } + } diff --git a/src/main/java/tum/dpid/services/processors/ClassHierarchyOrder.java b/src/main/java/tum/dpid/services/processors/ClassHierarchyOrder.java new file mode 100644 index 0000000..e4a3111 --- /dev/null +++ b/src/main/java/tum/dpid/services/processors/ClassHierarchyOrder.java @@ -0,0 +1,39 @@ +package tum.dpid.services.processors; + +import spoon.processing.AbstractProcessor; +import spoon.reflect.reference.CtTypeReference; +import spoon.support.reflect.declaration.CtClassImpl; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class ClassHierarchyOrder extends AbstractProcessor> { + + private final Map, Set>> classImplementors = new HashMap<>(); + + public void findInheritance(CtTypeReference classRef, CtTypeReference superClass) { + Set> subclasses = classImplementors.computeIfAbsent(superClass, k -> new HashSet<>()); + subclasses.add(classRef); + } + + @Override + public void process(CtClassImpl ctClass) { + if (ctClass.getReference().isAnonymous()) { + return; + } + if (ctClass.getSuperclass() != null) { + findInheritance(ctClass.getReference(), ctClass.getSuperclass()); + } + for (Object o : ctClass.getSuperInterfaces()) { + CtTypeReference superclass = (CtTypeReference) o; + findInheritance(ctClass.getReference(), superclass); + } + } + + public Map, Set>> getClassImplementors() { + return classImplementors; + } + +} diff --git a/src/main/java/tum/dpid/services/processors/MethodCallChain.java b/src/main/java/tum/dpid/services/processors/MethodCallChain.java new file mode 100644 index 0000000..660a8bf --- /dev/null +++ b/src/main/java/tum/dpid/services/processors/MethodCallChain.java @@ -0,0 +1,73 @@ +package tum.dpid.services.processors; + +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.reference.CtExecutableReference; +import spoon.reflect.reference.CtTypeReference; + +import java.util.*; + +public class MethodCallChain { + + private final CtExecutableReference executableReference; + private final Map , List >> callList; + private final Map , Set >> classHierarchy; + + private MethodCallChain(CtExecutableReference executableReference, + Map , List >> callList, + Map , Set >> classHierarchy) { + this.executableReference = executableReference; + this.callList = callList; + this.classHierarchy = classHierarchy; + } + + public static List processMethod(CtMethod methodName, + Map , List>> callList, + Map , Set >> classHierarchy) { + ArrayList result = new ArrayList<>(); + for (CtExecutableReference executableReference : findExecutablesForMethod(methodName, callList)) { + result.add(new MethodCallChain(executableReference, callList, classHierarchy)); + } + return result; + } + + static List > findExecutablesForMethod(CtMethod methodName, Map , List >> callList) { + ArrayList > result = new ArrayList<>(); + for (CtExecutableReference executableReference : callList.keySet()) { + if (executableReference.equals(methodName.getReference())){ + result.add(executableReference); + } + } + return result; + } + + public void printCallChain() { + System.out.println("Method call hierarchy of " + executableReference + ""); + printCallChain(executableReference, "\t", new HashSet >()); + } + + private void printCallChain(CtExecutableReference method, String indents, Set > alreadyVisited) { + if (alreadyVisited.contains(method)) { + return; + } + alreadyVisited.add(method); + List > callListForMethod = callList.get(method); + if (callListForMethod == null) { + return; + } + for (CtExecutableReference eachReference : callListForMethod) { + System.out.println(indents + eachReference.toString()); + + printCallChain(eachReference, indents.concat("\t"), alreadyVisited); + Set > subclasses = classHierarchy.get(eachReference.getDeclaringType()); + if (subclasses != null) { + for (CtTypeReference subclass : subclasses) { + CtExecutableReference ctExecutableReference = eachReference.getOverridingExecutable(subclass); + if (ctExecutableReference != null) { + System.out.println(indents + "* " + ctExecutableReference.toString()); + printCallChain( ctExecutableReference, indents.concat("\t"), alreadyVisited); + } + } + } + } + } +} diff --git a/src/main/java/tum/dpid/services/processors/MethodOrder.java b/src/main/java/tum/dpid/services/processors/MethodOrder.java new file mode 100644 index 0000000..c6042a8 --- /dev/null +++ b/src/main/java/tum/dpid/services/processors/MethodOrder.java @@ -0,0 +1,38 @@ +package tum.dpid.services.processors; + +import spoon.processing.AbstractProcessor; +import spoon.reflect.code.CtAbstractInvocation; +import spoon.reflect.declaration.CtElement; +import spoon.reflect.reference.CtExecutableReference; +import spoon.reflect.visitor.filter.AbstractFilter; +import spoon.support.reflect.declaration.CtMethodImpl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MethodOrder extends AbstractProcessor> { + private Map, List>> callList = new HashMap<>(); + + @Override + public void process(CtMethodImpl ctMethod) { + List elements = ctMethod.getElements(new AbstractFilter(CtElement.class) { + @Override + public boolean matches(CtElement ctElement) { + return ctElement instanceof CtAbstractInvocation; + } + }); + List> calls = new ArrayList<>(); + for (CtElement element : elements) { + CtAbstractInvocation invocation = (CtAbstractInvocation) element; + calls.add(invocation.getExecutable()); + + } + callList.put(ctMethod.getReference(), calls); + } + + public Map, List>> getCallList() { + return callList; + } +}