-
-
Notifications
You must be signed in to change notification settings - Fork 111
Open
Labels
call graphThis issues is related to the call graph constructionThis issues is related to the call graph constructionimprovementnew feature, improve in readability, structure or performancenew feature, improve in readability, structure or performance
Description
What happened?
Hi,
I tried to build the call graph of this project:
Operation.java
/**
* A simple enum that represents arithmetic operations.
* Each constant overrides {@link #apply(int,int)}.
* The enum also provides a static helper {@code execute} that
* demonstrates invoking the enum methods from client code.
*/
public enum Operation {
/** Addition */
ADD {
@Override
public int apply(int a, int b) {
return a + b;
}
},
/** Subtraction */
SUBTRACT {
@Override
public int apply(int a, int b) {
return a - b;
}
},
/** Multiplication */
MULTIPLY {
@Override
public int apply(int a, int b) {
return a * b;
}
},
/** Division – integer division, guards against divide‑by‑zero */
DIVIDE {
@Override
public int apply(int a, int b) {
if (b == 0) {
throw new ArithmeticException("Division by zero");
}
return a / b;
}
};
/** Abstract method each constant must implement */
public abstract int apply(int a, int b);
/**
* Utility that parses a textual operator and forwards the call
* to the corresponding enum constant.
*
* @param opSymbol one of "+", "-", "*", "/"
* @param left left operand
* @param right right operand
* @return result of the operation
*/
public static int execute(String opSymbol, int left, int right) {
switch (opSymbol) {
case "+":
return ADD.apply(left, right);
case "-":
return SUBTRACT.apply(left, right);
case "*":
return MULTIPLY.apply(left, right);
case "/":
return DIVIDE.apply(left, right);
default:
throw new IllegalArgumentException("Unsupported operator: " + opSymbol);
}
}
}Main.java
/**
* Simple driver that exercises the {@link Operation} enum.
* Run it with: java com.example.Main
*/
public class Main {
public static void main(String[] args) {
// Hard‑coded sample data
int a = 12;
int b = 4;
// Direct enum method calls
System.out.println("Direct enum calls:");
System.out.printf("%d + %d = %d%n", a, b, Operation.ADD.apply(a, b));
System.out.printf("%d - %d = %d%n", a, b, Operation.SUBTRACT.apply(a, b));
System.out.printf("%d * %d = %d%n", a, b, Operation.MULTIPLY.apply(a, b));
System.out.printf("%d / %d = %d%n", a, b, Operation.DIVIDE.apply(a, b));
// Calls via the static helper
System.out.println("\nCalls via Operation.execute(...):");
System.out.printf("%d + %d = %d%n", a, b, Operation.execute("+", a, b));
System.out.printf("%d - %d = %d%n", a, b, Operation.execute("-", a, b));
System.out.printf("%d * %d = %d%n", a, b, Operation.execute("*", a, b));
System.out.printf("%d / %d = %d%n", a, b, Operation.execute("/", a, b));
// Demonstrate exception handling (optional)
try {
Operation.execute("/", a, 0);
} catch (ArithmeticException ex) {
System.out.println("\nCaught expected exception: " + ex.getMessage());
}
}
}I built it this way:
package com.example;
import sootup.callgraph.CallGraph;
import sootup.callgraph.CallGraphAlgorithm;
import sootup.callgraph.ClassHierarchyAnalysisAlgorithm;
import sootup.core.inputlocation.AnalysisInputLocation;
import sootup.core.signatures.MethodSignature;
import sootup.core.types.VoidType;
import sootup.java.bytecode.frontend.inputlocation.DefaultRuntimeAnalysisInputLocation;
import sootup.java.bytecode.frontend.inputlocation.JavaClassPathAnalysisInputLocation;
import sootup.java.core.types.JavaClassType;
import sootup.java.core.views.JavaView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CallGraphDemo {
public static void main(String[] args) {
String cpString = "/Users/cyril/Library/Preferences/pharo/GitRepositories/jecisc/Famix-CallGraphs/resources/sources/example5/out/production/example5/";
List<AnalysisInputLocation> inputLocations = new ArrayList<>();
inputLocations.add(new JavaClassPathAnalysisInputLocation(cpString));
inputLocations.add(new DefaultRuntimeAnalysisInputLocation());
JavaView view = new JavaView(inputLocations);
JavaClassType classTypeA = view.getIdentifierFactory().getClassType("Main");
System.out.println(classTypeA);
MethodSignature entryMethodSignature =
view.getIdentifierFactory()
.getMethodSignature(
classTypeA,
"main",
"void",
Collections.singletonList("java.lang.String[]")
);
System.out.println(entryMethodSignature);
CallGraphAlgorithm cha = new ClassHierarchyAnalysisAlgorithm(view);
CallGraph cg = cha.initialize(Collections.singletonList(entryMethodSignature));
System.out.println(cg);
//cg.callsFrom(entryMethodSignature).forEach(System.out::println);
}
}In the result I got this:
<Main: void main(java.lang.String[])>:
to <Operation: void <clinit>()>
to <Operation: void <clinit>()>
to <Operation: void <clinit>()>
to <Operation: void <clinit>()>
to <Operation: void <clinit>()>
to <Operation: void <clinit>()>
to <Operation: void <clinit>()>
to <Operation: void <clinit>()>
to <Operation: void <clinit>()>
to <Operation: int execute(java.lang.String,int,int)>
to <Operation: int execute(java.lang.String,int,int)>
to <Operation: int execute(java.lang.String,int,int)>
to <Operation: int execute(java.lang.String,int,int)>
to <Operation: int execute(java.lang.String,int,int)>
to <Operation$1: int apply(int,int)>
to <Operation$1: int apply(int,int)>
to <Operation$1: int apply(int,int)>
to <Operation$1: int apply(int,int)>
to <Operation$2: int apply(int,int)>
to <Operation$2: int apply(int,int)>
to <Operation$2: int apply(int,int)>
to <Operation$2: int apply(int,int)>
to <Operation$3: int apply(int,int)>
to <Operation$3: int apply(int,int)>
to <Operation$3: int apply(int,int)>
to <Operation$3: int apply(int,int)>
to <Operation$4: int apply(int,int)>
to <Operation$4: int apply(int,int)>
to <Operation$4: int apply(int,int)>
to <Operation$4: int apply(int,int)>
to <java.io.PrintStream: java.io.PrintStream printf(java.lang.String,java.lang.Object[])>
to <java.io.PrintStream: java.io.PrintStream printf(java.lang.String,java.lang.Object[])>
to <java.io.PrintStream: java.io.PrintStream printf(java.lang.String,java.lang.Object[])>
to <java.io.PrintStream: java.io.PrintStream printf(java.lang.String,java.lang.Object[])>
to <java.io.PrintStream: java.io.PrintStream printf(java.lang.String,java.lang.Object[])>
to <java.io.PrintStream: java.io.PrintStream printf(java.lang.String,java.lang.Object[])>
to <java.io.PrintStream: java.io.PrintStream printf(java.lang.String,java.lang.Object[])>
to <java.io.PrintStream: java.io.PrintStream printf(java.lang.String,java.lang.Object[])>
to <java.io.PrintStream: void println(java.lang.String)>
to <java.io.PrintStream: void println(java.lang.String)>
to <java.io.PrintStream: void println(java.lang.String)>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: void <clinit>()>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.Integer: java.lang.Integer valueOf(int)>
to <java.lang.System: void <clinit>()>
to <java.lang.System: void <clinit>()>
to <java.lang.System: void <clinit>()>
to <java.lang.System: void <clinit>()>
to <java.lang.System: void <clinit>()>
to <java.lang.System: void <clinit>()>
to <java.lang.System: void <clinit>()>
to <java.lang.System: void <clinit>()>
to <java.lang.System: void <clinit>()>
to <java.lang.System: void <clinit>()>
to <java.lang.System: void <clinit>()>
to <java.lang.Throwable: java.lang.String getMessage()>
First I am surprised to find so many static initialization. I would have expected only one time the line to <Operation: void <clinit>()>.
But my biggest surpprize is to find 16 times a call to apply.
to <Operation$1: int apply(int,int)>
to <Operation$1: int apply(int,int)>
to <Operation$1: int apply(int,int)>
to <Operation$1: int apply(int,int)>
to <Operation$2: int apply(int,int)>
to <Operation$2: int apply(int,int)>
to <Operation$2: int apply(int,int)>
to <Operation$2: int apply(int,int)>
to <Operation$3: int apply(int,int)>
to <Operation$3: int apply(int,int)>
to <Operation$3: int apply(int,int)>
to <Operation$3: int apply(int,int)>
to <Operation$4: int apply(int,int)>
to <Operation$4: int apply(int,int)>
to <Operation$4: int apply(int,int)>
to <Operation$4: int apply(int,int)>
I have 4 calls to apply. Is it because with the CHA we consider that one call could lead to any of the possible enum values?
I'm tagging this as a bug, but it might be right and me wrong. I'd just like to understand the logic :)
Version used: <sootup.version>develop-19a4ac63ff-1</sootup.version>
Version
Latest develop branch
Relevant log output
Metadata
Metadata
Assignees
Labels
call graphThis issues is related to the call graph constructionThis issues is related to the call graph constructionimprovementnew feature, improve in readability, structure or performancenew feature, improve in readability, structure or performance