Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InvokeArgumentValidator changes #865

Draft
wants to merge 13 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,72 @@
* #L%
*/

import java.util.List;
import sootup.core.IdentifierFactory;
import sootup.core.jimple.basic.Immediate;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.Body;
import sootup.core.signatures.MethodSignature;
import sootup.core.typehierarchy.TypeHierarchy;
import sootup.core.types.ClassType;
import sootup.core.types.PrimitiveType;
import sootup.core.types.Type;
import sootup.core.views.View;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* A basic validator that checks whether the length of the invoke statement's argument list matches
* the length of the target methods's parameter type list.
*
* @author Steven Arzt
*/
public class InvokeArgumentValidator implements BodyValidator {
@Override
public List<ValidationException> validate(Body body, View view) {
List<ValidationException> validationException = new ArrayList<>();

for (Stmt stmt : body.getStmts()) {
if (stmt.containsInvokeExpr()) {
AbstractInvokeExpr invExpr =
stmt.getInvokeExpr();
MethodSignature callee = invExpr.getMethodSignature();
List<Immediate> args = invExpr.getArgs();
List<Type> parameterTypes = callee.getParameterTypes();
if (invExpr.getArgCount() != parameterTypes.size()) {
validationException.add(new ValidationException(stmt, "Invalid number of arguments"));
} else {
// check argument type
TypeHierarchy typeHierarchy = view.getTypeHierarchy();
IdentifierFactory identifierFactory = view.getIdentifierFactory();
Iterator<Immediate> iterArgs = args.iterator();
Iterator<Type> iterParameters = parameterTypes.iterator();
while (iterArgs.hasNext() && iterParameters.hasNext()) {
ClassType argType = getClassType(identifierFactory, iterArgs.next().getType());
ClassType paramType = getClassType(identifierFactory, iterParameters.next());
if (argType != paramType && (!typeHierarchy.contains(paramType)
Momo-Not-Emo marked this conversation as resolved.
Show resolved Hide resolved
|| !typeHierarchy.subtypesOf(paramType).contains(argType))) {
validationException.add(new ValidationException(stmt,
String.format("Invalid argument type. Required %s but provided %s.", paramType, argType)));
}
}
}
}
Momo-Not-Emo marked this conversation as resolved.
Show resolved Hide resolved
}
return validationException;
}

@Override
public List<ValidationException> validate(Body body, View view) {
// TODO: check copied code from old soot
/*
* for (Unit u : body.getUnits()) { Stmt s = (Stmt) u; if (s.containsInvokeExpr()) { InvokeExpr iinvExpr =
* s.getInvokeExpr(); SootMethod callee = iinvExpr.getMethod(); if (callee != null && iinvExpr.getArgCount() !=
* callee.getParameterCount()) { exceptions.add(new ValidationException(s, "Invalid number of arguments")); } } }
*/
return null;
}
private ClassType getClassType(IdentifierFactory identifierFactory, Type type) {
if (type instanceof PrimitiveType) {
type = identifierFactory.getBoxedType((PrimitiveType) type);
}
return identifierFactory.getClassType(type.toString());
}

@Override
public boolean isBasicValidator() {
return true;
}
@Override
public boolean isBasicValidator() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import sootup.core.jimple.basic.Local;
import sootup.core.jimple.basic.Value;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.SootClass;
import sootup.core.model.SootMethod;

Expand Down Expand Up @@ -58,4 +59,8 @@ public ValidationException(SootMethod method, String s, String s1) {
// TODO: auto generated stub

}

public ValidationException(Stmt stmt, String s) {
// TODO: auto generated stub
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,13 @@ public Optional<JavaSootClassSource> getClassSource(@Nonnull ClassType type, @No
try {
FileSystem fs = fileSystemCache.get(path);
final Path archiveRoot = fs.getPath("/");
JavaClassType javaClassType = null;
if (type instanceof JavaClassType)
Momo-Not-Emo marked this conversation as resolved.
Show resolved Hide resolved
javaClassType = (JavaClassType) type;
else
javaClassType = new JavaClassType(type.getClassName(), type.getPackageName());
return getClassSourceInternal(
(JavaClassType) type, archiveRoot, new AsmJavaClassProvider(view));
javaClassType, archiveRoot, new AsmJavaClassProvider(view));
Momo-Not-Emo marked this conversation as resolved.
Show resolved Hide resolved
} catch (ExecutionException e) {
throw new RuntimeException("Failed to retrieve file system from cache for " + path, e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ public Collection<PrimitiveType> getAllPrimitiveTypes() {
@Nonnull
public JavaClassType getBoxedType(@Nonnull PrimitiveType primitiveType) {
String name = primitiveType.getName();
if (primitiveType instanceof PrimitiveType.IntType)
name = "integer";
Momo-Not-Emo marked this conversation as resolved.
Show resolved Hide resolved
StringBuilder boxedname = new StringBuilder(name);
boxedname.setCharAt(0, Character.toUpperCase(boxedname.charAt(0)));
return getClassType(boxedname.toString(), "java.lang");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package sootup.tests.validator;

import org.junit.Before;
import org.junit.Test;
import sootup.core.model.SootClass;
import sootup.core.model.SourceType;
import sootup.core.signatures.PackageName;
import sootup.core.types.ClassType;
import sootup.core.validation.InvokeArgumentValidator;
import sootup.core.validation.ValidationException;
import sootup.java.bytecode.inputlocation.DefaultRTJarAnalysisInputLocation;
import sootup.jimple.parser.JimpleAnalysisInputLocation;
import sootup.jimple.parser.JimpleView;

import java.nio.file.Paths;
import java.util.*;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

public class InvokeArgumentValidatorTest {
InvokeArgumentValidator invokeArgumentValidator;
JimpleView jimpleView;
Collection<SootClass> classes;

@Before
public void Setup() {

invokeArgumentValidator = new InvokeArgumentValidator();

ClassType classTypeFieldRefValidator =
new ClassType() {
@Override
public boolean isBuiltInClass() {
return false;
}

@Override
public String getFullyQualifiedName() {
return "jimple.InvokeArgumentValidator";
}

@Override
public String getClassName() {
return "InvokeArgumentValidator";
}

@Override
public PackageName getPackageName() {
return new PackageName("jimple");
}
};

String classPath = "src/test/resources/validator/jimple";
JimpleAnalysisInputLocation jimpleInputLocation =
new JimpleAnalysisInputLocation(Paths.get(classPath), SourceType.Application);

// rt.jar is required since the validator uses typeHierarchy
DefaultRTJarAnalysisInputLocation defaultRTJarAnalysisInputLocation = new DefaultRTJarAnalysisInputLocation();
jimpleView = new JimpleView(Arrays.asList(jimpleInputLocation, defaultRTJarAnalysisInputLocation));

classes = new HashSet<>(); // Set to track the classes to check
for (SootClass aClass : jimpleView.getClasses()) {
if (!aClass.isLibraryClass()) {
Momo-Not-Emo marked this conversation as resolved.
Show resolved Hide resolved
classes.add(aClass);
}
}

// Speed up the class search process by limiting the search scope within application classes
final Optional<SootClass> classSource1 = classes.stream().filter(c -> c.getType()
.equals(classTypeFieldRefValidator)).findFirst();
assertFalse(classSource1.isPresent());
}

@Test
public void invokeArgumentValidator_success() {
List<ValidationException> validationExceptions_success;

validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod(
jimpleView.getIdentifierFactory()
.parseMethodSignature("<InvokeArgumentValidator: void invokeArgumentValidator_success()>") )
.get().getBody(), jimpleView);

assertEquals(0, validationExceptions_success.size());
}

@Test
public void testArgumentNumber_fail() {
List<ValidationException> validationExceptions_success;

validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod(
jimpleView.getIdentifierFactory()
.parseMethodSignature("<InvokeArgumentValidator: void testArgumentNumber_fail()>") )
.get().getBody(), jimpleView);
assertEquals(1, validationExceptions_success.size());
}

@Test
public void testArgumentType_fail() {
List<ValidationException> validationExceptions_success;

validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod(
jimpleView.getIdentifierFactory()
.parseMethodSignature("<InvokeArgumentValidator: void testArgumentType_fail()>") )
.get().getBody(), jimpleView);
assertEquals(2, validationExceptions_success.size());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
public class InvokeArgumentValidator extends java.lang.Object
{
public void <init>()
{
InvokeArgumentValidator l0;

l0 := @this: InvokeArgumentValidator;
specialinvoke l0.<java.lang.Object: void <init>()>();

return;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a testcase for array types. Primitive type array, Classtype array and both cases also as multi-dimensional array

public void join(java.lang.Number,java.lang.String)
{
InvokeArgumentValidator l0;
java.lang.Number l1;
java.lang.String l2;


l0 := @this: InvokeArgumentValidator;
l1 := @parameter0: java.lang.Number;
l2 := @parameter1: java.lang.String;

return;
}

public void invokeArgumentValidator_success()
{
InvokeArgumentValidator l0;

l0 := @this: InvokeArgumentValidator;
virtualinvoke l0.<InvokeArgumentValidator: void join(java.lang.Number,java.lang.String)>(1, "hello");

return;
}

public void testArgumentNumber_fail()
{
InvokeArgumentValidator l0;

l0 := @this: InvokeArgumentValidator;
virtualinvoke l0.<InvokeArgumentValidator: void join(java.lang.Number,java.lang.String)>(1);

return;
}

public void testArgumentType_fail()
{
InvokeArgumentValidator l0;

l0 := @this: InvokeArgumentValidator;
virtualinvoke l0.<InvokeArgumentValidator: void join(java.lang.Number,java.lang.String)>("hello", 1);

return;
}
}