Skip to content

Commit

Permalink
Add possible generic types of java methods (#1548)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
arthurscchan authored Jun 10, 2024
1 parent 4eaf05d commit 886fdbb
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 3 deletions.
5 changes: 5 additions & 0 deletions frontends/java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>3.26.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import ossf.fuzz.introspector.soot.utils.CalculationUtils;
import ossf.fuzz.introspector.soot.utils.CalltreeUtils;
import ossf.fuzz.introspector.soot.utils.EdgeUtils;
import ossf.fuzz.introspector.soot.utils.GenericUtils;
import ossf.fuzz.introspector.soot.utils.SinkDiscoveryUtils;
import ossf.fuzz.introspector.soot.yaml.Callsite;
import ossf.fuzz.introspector.soot.yaml.FunctionConfig;
Expand Down Expand Up @@ -116,7 +117,9 @@ public SootSceneTransformer(
.filter(f -> f.endsWith(".java"))
.collect(Collectors.toList());
for (String source : sourceList) {
projectClassList.add(source.substring(source.lastIndexOf("/") + 1).replace(".java", ""));
String sourceName = source.substring(source.lastIndexOf("/") + 1).replace(".java", "");
GenericUtils.addSourcePath(sourceName, Paths.get(source).toAbsolutePath());
projectClassList.add(sourceName);
}
} catch (IOException e) {
// Fail to retrieve project class list, ignore the list.
Expand Down Expand Up @@ -411,6 +414,9 @@ private void processMethods(
element.setCountInformation(
blockGraph.size(), iCount, CalculationUtils.calculateCyclomaticComplexity(blockGraph));

// Update generic information for method return type and method arguments
GenericUtils.updateGenericTypes(m, element);

this.methodList.addFunctionElement(element);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// Copyright 2024 Fuzz Introspector Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///////////////////////////////////////////////////////////////////////////

package ossf.fuzz.introspector.soot.utils;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import ossf.fuzz.introspector.soot.yaml.FunctionElement;
import soot.SootMethod;

public class GenericUtils {
private static Map<String, Path> sourcePaths = new HashMap<String, Path>();

/**
* This method adds mapping to the sourcePaths hash map to allow the
* discovering the generic of certain methods return type nad argument
* types through source code analysis.
* @param sourceName the name of the public class of the source file
* @param sourcePath the absolute path of the source file
*/
public static void addSourcePath(String sourceName, Path sourcePath) {
sourcePaths.put(sourceName, sourcePath);
}

/**
* This method analyse the return type and the argument types of a method.
* If source code of the target method is found. It parses the source code
* and retrieves the return type and the argument types and determine if
* generic has been used. If generic has been used, the generic types is
* then added to additional fields in the FunctionElement instance to
* indicate its generic types as additional information.
*
* @param m the target SootMethod object to be processed
* @param element the target FunctionElement object to be processed
*/
public static void updateGenericTypes(SootMethod m, FunctionElement element) {
String className = m.getDeclaringClass().getName();
className = className.substring(className.lastIndexOf(".") + 1);
if (sourcePaths.containsKey(className)) {
try {
// Try read and parse the source file related to the class of the target method.
InputStream is = new FileInputStream(sourcePaths.get(className).toFile());
Optional<CompilationUnit> optional = new JavaParser().parse(is).getResult();
if (optional.isPresent()) {
for(MethodDeclaration md : optional.get().findAll(MethodDeclaration.class)) {
if (md.getName().asString().equals(m.getName())) {
if (handleArgumentGeneric(md, element)) {
// There could be overloading method with the same name in the same class
// handleArgumentGeneric will return true if the the MethodDeclaration
// points to the correct method and handles it successfully.
handleReturnTypeGeneric(md, element);
}
}
}
}
} catch (IOException e) {
// Assume the source file is not found nor parsable.
return;
}
}
}

private static void handleReturnTypeGeneric(MethodDeclaration md, FunctionElement element) {
String returnType = element.getReturnType();
String realReturnType = md.getType().asString();

if (!returnType.equals(realReturnType)) {
if (containsGeneric(realReturnType)) {
if (returnType.contains(".")) {
returnType = returnType.substring(0, returnType.lastIndexOf("."));
element.setReturnType(returnType + "." + realReturnType);
} else {
element.setReturnType(realReturnType);
}
}
}
}

private static boolean handleArgumentGeneric(MethodDeclaration md, FunctionElement element) {
List<String> argTypes = element.getArgTypes();
List<Parameter> mdArgTypes = md.getParameters();

if (argTypes.size() > 0 && argTypes.size() == mdArgTypes.size()) {
List<String> newArgTypes = new ArrayList<String>();
Boolean isGeneric = false;
for (Integer i = 0; i < argTypes.size(); i++) {
String mdArgType = mdArgTypes.get(i).getType().asString();
String argType = argTypes.get(i);

// Check equality of the two argument types to ensure
// it is the correct MethodDeclaration of the target method
if (!isSameArgType(argType, mdArgType)) {
return false;
}

// Add the arguments with or without generic to the function elements
if (containsGeneric(mdArgType)) {
if (argType.contains(".")) {
argType = argType.substring(0, argType.lastIndexOf("."));
newArgTypes.add(argType + "." + mdArgType);
} else {
newArgTypes.add(mdArgType);
}
isGeneric = true;
} else {
newArgTypes.add(argTypes.get(i));
}
}

// Save method name with generic
if (isGeneric) {
String functionName = element.getFunctionName().split("\\(")[0];
String argString = String.join(",", newArgTypes);
element.setFunctionName(String.format("%s(%s)", functionName, argString));
element.setArgTypes(newArgTypes);
}

return true;
}
return false;
}

private static boolean containsGeneric(String type) {
if (type.contains("<") && type.contains(">")) {
return true;
} else {
return false;
}
}

private static boolean isSameArgType(String argType, String mdArgType) {
String simpleArgType = null;
String simpleMdArgType = null;

// Simplify argType
if (argType.contains(".")) {
simpleArgType = argType.substring(argType.lastIndexOf(".") + 1);
} else {
simpleArgType = argType;
}

// Simplify mdArgType
if (containsGeneric(mdArgType)) {
simpleMdArgType = mdArgType.split("<")[0];
} else {
simpleMdArgType = mdArgType;
}

return simpleArgType.equals(simpleMdArgType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,21 @@ public FunctionElement() {
this.branchProfiles = new ArrayList<BranchProfile>();
this.callsites = new ArrayList<Callsite>();

this.functionName = "";
this.functionSourceFile = "";
this.linkageType = "";
this.returnType = "";

this.functionLinenumber = -1;
this.functionDepth = 0;
this.functionUses = 0;
this.edgeCount = 0;
this.argCount = 0;
this.BBCount = 0;
this.iCount = 0;
this.edgeCount = 0;
this.CyclomaticComplexity = 0;
this.functionUses = 0;

this.javaMethodInfo = new JavaMethodInfo();
}

public String getFunctionName() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class JavaMethodInfo {
private List<String> exceptions;
private List<String> interfaces;
private List<ClassField> classFields;
private List<String> argumentGenericTypes;
private String returnValueGenericType;
private String superClass;
private Integer methodStatus;
private Boolean isConcrete;
Expand All @@ -36,6 +38,8 @@ public JavaMethodInfo() {
this.exceptions = new ArrayList<String>();
this.interfaces = new ArrayList<String>();
this.classFields = new ArrayList<ClassField>();
this.argumentGenericTypes = new ArrayList<String>();
this.returnValueGenericType = "";
this.superClass = "";
this.isConcrete = true;
this.isJavaLibraryMethod = false;
Expand Down Expand Up @@ -90,6 +94,22 @@ public void setClassFields(List<ClassField> classFields) {
this.classFields = classFields;
}

public List<String> getArgumentGenericTypes() {
return this.argumentGenericTypes;
}

public void setArgumentGenericTypes(List<String> argumentGenericTypes) {
this.argumentGenericTypes = argumentGenericTypes;
}

public String getReturnValueGenericType() {
return this.returnValueGenericType;
}

public void setReturnValueGenericType(String returnValueGenericType) {
this.returnValueGenericType = returnValueGenericType;
}

public Boolean isConcrete() {
return this.isConcrete;
}
Expand Down

0 comments on commit 886fdbb

Please sign in to comment.