Skip to content

Commit 886fdbb

Browse files
authored
Add possible generic types of java methods (#1548)
* Fix structure of yaml Signed-off-by: Arthur Chan <arthur.chan@adalogics.com> * Add javaparser dependency Signed-off-by: Arthur Chan <arthur.chan@adalogics.com> * Add logic to determine generic values for types Signed-off-by: Arthur Chan <arthur.chan@adalogics.com> * Fix function name field Signed-off-by: Arthur Chan <arthur.chan@adalogics.com> * Fix logic Signed-off-by: Arthur Chan <arthur.chan@adalogics.com> --------- Signed-off-by: Arthur Chan <arthur.chan@adalogics.com>
1 parent 4eaf05d commit 886fdbb

File tree

5 files changed

+218
-3
lines changed

5 files changed

+218
-3
lines changed

frontends/java/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
<artifactId>commons-lang3</artifactId>
4343
<version>3.12.0</version>
4444
</dependency>
45+
<dependency>
46+
<groupId>com.github.javaparser</groupId>
47+
<artifactId>javaparser-core</artifactId>
48+
<version>3.26.0</version>
49+
</dependency>
4550
<dependency>
4651
<groupId>org.junit.jupiter</groupId>
4752
<artifactId>junit-jupiter-engine</artifactId>

frontends/java/src/main/java/ossf/fuzz/introspector/soot/SootSceneTransformer.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import ossf.fuzz.introspector.soot.utils.CalculationUtils;
3737
import ossf.fuzz.introspector.soot.utils.CalltreeUtils;
3838
import ossf.fuzz.introspector.soot.utils.EdgeUtils;
39+
import ossf.fuzz.introspector.soot.utils.GenericUtils;
3940
import ossf.fuzz.introspector.soot.utils.SinkDiscoveryUtils;
4041
import ossf.fuzz.introspector.soot.yaml.Callsite;
4142
import ossf.fuzz.introspector.soot.yaml.FunctionConfig;
@@ -116,7 +117,9 @@ public SootSceneTransformer(
116117
.filter(f -> f.endsWith(".java"))
117118
.collect(Collectors.toList());
118119
for (String source : sourceList) {
119-
projectClassList.add(source.substring(source.lastIndexOf("/") + 1).replace(".java", ""));
120+
String sourceName = source.substring(source.lastIndexOf("/") + 1).replace(".java", "");
121+
GenericUtils.addSourcePath(sourceName, Paths.get(source).toAbsolutePath());
122+
projectClassList.add(sourceName);
120123
}
121124
} catch (IOException e) {
122125
// Fail to retrieve project class list, ignore the list.
@@ -411,6 +414,9 @@ private void processMethods(
411414
element.setCountInformation(
412415
blockGraph.size(), iCount, CalculationUtils.calculateCyclomaticComplexity(blockGraph));
413416

417+
// Update generic information for method return type and method arguments
418+
GenericUtils.updateGenericTypes(m, element);
419+
414420
this.methodList.addFunctionElement(element);
415421
}
416422
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright 2024 Fuzz Introspector Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
///////////////////////////////////////////////////////////////////////////
15+
16+
package ossf.fuzz.introspector.soot.utils;
17+
18+
import com.github.javaparser.JavaParser;
19+
import com.github.javaparser.ast.CompilationUnit;
20+
import com.github.javaparser.ast.body.MethodDeclaration;
21+
import com.github.javaparser.ast.body.Parameter;
22+
import java.io.File;
23+
import java.io.FileInputStream;
24+
import java.io.InputStream;
25+
import java.io.IOException;
26+
import java.nio.file.Path;
27+
import java.util.ArrayList;
28+
import java.util.HashMap;
29+
import java.util.List;
30+
import java.util.Map;
31+
import java.util.Optional;
32+
import ossf.fuzz.introspector.soot.yaml.FunctionElement;
33+
import soot.SootMethod;
34+
35+
public class GenericUtils {
36+
private static Map<String, Path> sourcePaths = new HashMap<String, Path>();
37+
38+
/**
39+
* This method adds mapping to the sourcePaths hash map to allow the
40+
* discovering the generic of certain methods return type nad argument
41+
* types through source code analysis.
42+
* @param sourceName the name of the public class of the source file
43+
* @param sourcePath the absolute path of the source file
44+
*/
45+
public static void addSourcePath(String sourceName, Path sourcePath) {
46+
sourcePaths.put(sourceName, sourcePath);
47+
}
48+
49+
/**
50+
* This method analyse the return type and the argument types of a method.
51+
* If source code of the target method is found. It parses the source code
52+
* and retrieves the return type and the argument types and determine if
53+
* generic has been used. If generic has been used, the generic types is
54+
* then added to additional fields in the FunctionElement instance to
55+
* indicate its generic types as additional information.
56+
*
57+
* @param m the target SootMethod object to be processed
58+
* @param element the target FunctionElement object to be processed
59+
*/
60+
public static void updateGenericTypes(SootMethod m, FunctionElement element) {
61+
String className = m.getDeclaringClass().getName();
62+
className = className.substring(className.lastIndexOf(".") + 1);
63+
if (sourcePaths.containsKey(className)) {
64+
try {
65+
// Try read and parse the source file related to the class of the target method.
66+
InputStream is = new FileInputStream(sourcePaths.get(className).toFile());
67+
Optional<CompilationUnit> optional = new JavaParser().parse(is).getResult();
68+
if (optional.isPresent()) {
69+
for(MethodDeclaration md : optional.get().findAll(MethodDeclaration.class)) {
70+
if (md.getName().asString().equals(m.getName())) {
71+
if (handleArgumentGeneric(md, element)) {
72+
// There could be overloading method with the same name in the same class
73+
// handleArgumentGeneric will return true if the the MethodDeclaration
74+
// points to the correct method and handles it successfully.
75+
handleReturnTypeGeneric(md, element);
76+
}
77+
}
78+
}
79+
}
80+
} catch (IOException e) {
81+
// Assume the source file is not found nor parsable.
82+
return;
83+
}
84+
}
85+
}
86+
87+
private static void handleReturnTypeGeneric(MethodDeclaration md, FunctionElement element) {
88+
String returnType = element.getReturnType();
89+
String realReturnType = md.getType().asString();
90+
91+
if (!returnType.equals(realReturnType)) {
92+
if (containsGeneric(realReturnType)) {
93+
if (returnType.contains(".")) {
94+
returnType = returnType.substring(0, returnType.lastIndexOf("."));
95+
element.setReturnType(returnType + "." + realReturnType);
96+
} else {
97+
element.setReturnType(realReturnType);
98+
}
99+
}
100+
}
101+
}
102+
103+
private static boolean handleArgumentGeneric(MethodDeclaration md, FunctionElement element) {
104+
List<String> argTypes = element.getArgTypes();
105+
List<Parameter> mdArgTypes = md.getParameters();
106+
107+
if (argTypes.size() > 0 && argTypes.size() == mdArgTypes.size()) {
108+
List<String> newArgTypes = new ArrayList<String>();
109+
Boolean isGeneric = false;
110+
for (Integer i = 0; i < argTypes.size(); i++) {
111+
String mdArgType = mdArgTypes.get(i).getType().asString();
112+
String argType = argTypes.get(i);
113+
114+
// Check equality of the two argument types to ensure
115+
// it is the correct MethodDeclaration of the target method
116+
if (!isSameArgType(argType, mdArgType)) {
117+
return false;
118+
}
119+
120+
// Add the arguments with or without generic to the function elements
121+
if (containsGeneric(mdArgType)) {
122+
if (argType.contains(".")) {
123+
argType = argType.substring(0, argType.lastIndexOf("."));
124+
newArgTypes.add(argType + "." + mdArgType);
125+
} else {
126+
newArgTypes.add(mdArgType);
127+
}
128+
isGeneric = true;
129+
} else {
130+
newArgTypes.add(argTypes.get(i));
131+
}
132+
}
133+
134+
// Save method name with generic
135+
if (isGeneric) {
136+
String functionName = element.getFunctionName().split("\\(")[0];
137+
String argString = String.join(",", newArgTypes);
138+
element.setFunctionName(String.format("%s(%s)", functionName, argString));
139+
element.setArgTypes(newArgTypes);
140+
}
141+
142+
return true;
143+
}
144+
return false;
145+
}
146+
147+
private static boolean containsGeneric(String type) {
148+
if (type.contains("<") && type.contains(">")) {
149+
return true;
150+
} else {
151+
return false;
152+
}
153+
}
154+
155+
private static boolean isSameArgType(String argType, String mdArgType) {
156+
String simpleArgType = null;
157+
String simpleMdArgType = null;
158+
159+
// Simplify argType
160+
if (argType.contains(".")) {
161+
simpleArgType = argType.substring(argType.lastIndexOf(".") + 1);
162+
} else {
163+
simpleArgType = argType;
164+
}
165+
166+
// Simplify mdArgType
167+
if (containsGeneric(mdArgType)) {
168+
simpleMdArgType = mdArgType.split("<")[0];
169+
} else {
170+
simpleMdArgType = mdArgType;
171+
}
172+
173+
return simpleArgType.equals(simpleMdArgType);
174+
}
175+
}

frontends/java/src/main/java/ossf/fuzz/introspector/soot/yaml/FunctionElement.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,21 @@ public FunctionElement() {
5353
this.branchProfiles = new ArrayList<BranchProfile>();
5454
this.callsites = new ArrayList<Callsite>();
5555

56+
this.functionName = "";
57+
this.functionSourceFile = "";
58+
this.linkageType = "";
59+
this.returnType = "";
60+
61+
this.functionLinenumber = -1;
5662
this.functionDepth = 0;
57-
this.functionUses = 0;
58-
this.edgeCount = 0;
63+
this.argCount = 0;
5964
this.BBCount = 0;
6065
this.iCount = 0;
66+
this.edgeCount = 0;
6167
this.CyclomaticComplexity = 0;
68+
this.functionUses = 0;
69+
70+
this.javaMethodInfo = new JavaMethodInfo();
6271
}
6372

6473
public String getFunctionName() {

frontends/java/src/main/java/ossf/fuzz/introspector/soot/yaml/JavaMethodInfo.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public class JavaMethodInfo {
2222
private List<String> exceptions;
2323
private List<String> interfaces;
2424
private List<ClassField> classFields;
25+
private List<String> argumentGenericTypes;
26+
private String returnValueGenericType;
2527
private String superClass;
2628
private Integer methodStatus;
2729
private Boolean isConcrete;
@@ -36,6 +38,8 @@ public JavaMethodInfo() {
3638
this.exceptions = new ArrayList<String>();
3739
this.interfaces = new ArrayList<String>();
3840
this.classFields = new ArrayList<ClassField>();
41+
this.argumentGenericTypes = new ArrayList<String>();
42+
this.returnValueGenericType = "";
3943
this.superClass = "";
4044
this.isConcrete = true;
4145
this.isJavaLibraryMethod = false;
@@ -90,6 +94,22 @@ public void setClassFields(List<ClassField> classFields) {
9094
this.classFields = classFields;
9195
}
9296

97+
public List<String> getArgumentGenericTypes() {
98+
return this.argumentGenericTypes;
99+
}
100+
101+
public void setArgumentGenericTypes(List<String> argumentGenericTypes) {
102+
this.argumentGenericTypes = argumentGenericTypes;
103+
}
104+
105+
public String getReturnValueGenericType() {
106+
return this.returnValueGenericType;
107+
}
108+
109+
public void setReturnValueGenericType(String returnValueGenericType) {
110+
this.returnValueGenericType = returnValueGenericType;
111+
}
112+
93113
public Boolean isConcrete() {
94114
return this.isConcrete;
95115
}

0 commit comments

Comments
 (0)