From b5a7b799af35704936b72319ef3b170d678cb94b Mon Sep 17 00:00:00 2001 From: momo <149540451+Momo-Not-Emo@users.noreply.github.com> Date: Wed, 21 Feb 2024 15:18:18 -0500 Subject: [PATCH 01/11] InvokeArgumentValidator changes --- .../validation/InvokeArgumentValidator.java | 41 ++++--- .../core/validation/ValidationException.java | 5 + .../InvokeArgumentValidatorTest.java | 114 ++++++++++++++++++ .../jimple/InvokeArgumentValidator.jimple | 52 ++++++++ 4 files changed, 197 insertions(+), 15 deletions(-) create mode 100644 sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java create mode 100644 sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple diff --git a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java index bd01dbc0f2b..5b84e7ed7b1 100644 --- a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java @@ -22,10 +22,15 @@ * #L% */ -import java.util.List; +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.views.View; +import java.util.ArrayList; +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. @@ -33,20 +38,26 @@ * @author Steven Arzt */ public class InvokeArgumentValidator implements BodyValidator { + @Override + public List validate(Body body, View view) { + List validationException = new ArrayList<>(); - @Override - public List 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; - } + for (Stmt stmt : body.getStmts()) { + if (stmt.containsInvokeExpr()) { + AbstractInvokeExpr invExpr = + stmt.getInvokeExpr(); + MethodSignature callee = invExpr.getMethodSignature(); + if (invExpr.getArgCount() != + callee.getParameterTypes().size()) { + validationException.add(new ValidationException(stmt, "Invalid number of arguments")); + } + } + } + return validationException; + } - @Override - public boolean isBasicValidator() { - return true; - } + @Override + public boolean isBasicValidator() { + return true; + } } diff --git a/sootup.core/src/main/java/sootup/core/validation/ValidationException.java b/sootup.core/src/main/java/sootup/core/validation/ValidationException.java index 70301cba3ad..2a68f10e138 100644 --- a/sootup.core/src/main/java/sootup/core/validation/ValidationException.java +++ b/sootup.core/src/main/java/sootup/core/validation/ValidationException.java @@ -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; @@ -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 + } } diff --git a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java new file mode 100644 index 00000000000..fcb2d4eb295 --- /dev/null +++ b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java @@ -0,0 +1,114 @@ +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.jimple.parser.JimpleAnalysisInputLocation; +import sootup.jimple.parser.JimpleView; + +import java.nio.file.Paths; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class InvokeArgumentValidatorTest { + InvokeArgumentValidator invokeArgumentValidator; + JimpleView jimpleView; + Collection 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); + + jimpleView = new JimpleView(jimpleInputLocation); + final Optional classSource1 = jimpleView.getClass(classTypeFieldRefValidator); + assertFalse(classSource1.isPresent()); + + classes = new HashSet<>(); // Set to track the classes to check + + for (SootClass aClass : jimpleView.getClasses()) { + if (!aClass.isLibraryClass()) { + classes.add(aClass); + } + } + } + + @Test + public void testInvokeArgumentValidatorSuccess() { + List validationExceptions_success; + + validationExceptions_success = + invokeArgumentValidator.validate( + classes.stream() + .filter(c -> c.getType().getClassName().equals("InvokeArgumentValidator")) + .findFirst() + .get() + .getMethods() + .stream() + .filter(m -> m.getName().equals("invokeArgumentValidator_success")) + .map(m -> m.getBody()) + .findFirst() + .get(), + jimpleView); + + assertEquals(0, validationExceptions_success.size()); + } + + @Test + public void testInvokeArgumentValidatorFailure() { + List validationExceptions_success; + + validationExceptions_success = + invokeArgumentValidator.validate( + classes.stream() + .filter(c -> c.getType().getClassName().equals("InvokeArgumentValidator")) + .findFirst() + .get() + .getMethods() + .stream() + .filter(m -> m.getName().equals("invokeArgumentValidator_fail")) + .map(m -> m.getBody()) + .findFirst() + .get(), + jimpleView); + assertEquals(1, validationExceptions_success.size()); + } + +} diff --git a/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple b/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple new file mode 100644 index 00000000000..45ca74cc7c3 --- /dev/null +++ b/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple @@ -0,0 +1,52 @@ +public class InvokeArgumentValidator extends java.lang.Object +{ + public void () + { + InvokeArgumentValidator l0; + + l0 := @this: InvokeArgumentValidator; + specialinvoke l0.()>(); + + return; + } + + public java.lang.String join(int,java.lang.String) + { + InvokeArgumentValidator l0; + int l1; + java.lang.String l2; + unknown $stack3; + + + l0 := @this: InvokeArgumentValidator; + l1 := @parameter0: int; + l2 := @parameter1: java.lang.String; + $stack3 = dynamicinvoke "makeConcatWithConstants" (l1, l2) ("\u0001\u0001"); + + return $stack3; + } + + public void invokeArgumentValidator_success() + { + InvokeArgumentValidator l0; + unknown l1; + + + l0 := @this: InvokeArgumentValidator; + l1 = virtualinvoke l0.(1, "hello world"); + + return; + } + + public void invokeArgumentValidator_fail() + { + InvokeArgumentValidator l0; + unknown l1; + + + l0 := @this: InvokeArgumentValidator; + l1 = virtualinvoke l0.(1); + + return; + } +} \ No newline at end of file From f067ac49c752ff75c7b755ac268409beea772acb Mon Sep 17 00:00:00 2001 From: momo <149540451+Momo-Not-Emo@users.noreply.github.com> Date: Thu, 22 Feb 2024 13:57:56 -0500 Subject: [PATCH 02/11] check argument types and fix some bugs 1. Refactor the test case code as per the review comments for simplicity. 2. Implement argument type checking by including `rt.jar` in the analysis input locations, as the validator relies on typeHierarchy. Add a corresponding test case for this scenario. 3. Rectify a bug in `JavaIdentifierFactory.getBoxedType(...)` related to the int type. Replace the erroneous `java.lang.Int` with the correct type, which is `java.lang.Integer`. 4. Address a bug in `ArchiveBasedAnalysisInputLocation.getClassSource(...)` where attempting to cast `ClassType` to `JavaClassType` is incorrect. Update the code to handle `ClassType` appropriately, as it cannot be directly cast to `JavaClassType`. --- .../validation/InvokeArgumentValidator.java | 35 ++++++++++- .../ArchiveBasedAnalysisInputLocation.java | 7 ++- .../java/core/JavaIdentifierFactory.java | 2 + .../InvokeArgumentValidatorTest.java | 60 +++++++++---------- .../jimple/InvokeArgumentValidator.jimple | 32 +++++----- 5 files changed, 86 insertions(+), 50 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java index 5b84e7ed7b1..88fc82517b6 100644 --- a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java @@ -22,13 +22,20 @@ * #L% */ +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; /** @@ -47,15 +54,39 @@ public List validate(Body body, View view) { AbstractInvokeExpr invExpr = stmt.getInvokeExpr(); MethodSignature callee = invExpr.getMethodSignature(); - if (invExpr.getArgCount() != - callee.getParameterTypes().size()) { + List args = invExpr.getArgs(); + List 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 iterArgs = args.iterator(); + Iterator 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) + || !typeHierarchy.subtypesOf(paramType).contains(argType))) { + validationException.add(new ValidationException(stmt, + String.format("Invalid argument type. Required %s but provided %s.", paramType, argType))); + System.out.println(String.format("Invalid argument type. Required %s but provided %s.", paramType, argType)); + } + } } } } return validationException; } + 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; diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java index b7e6e48f8f8..609bc76b934 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java @@ -99,8 +99,13 @@ public Optional getClassSource(@Nonnull ClassType type, @No try { FileSystem fs = fileSystemCache.get(path); final Path archiveRoot = fs.getPath("/"); + JavaClassType javaClassType = null; + if (type instanceof JavaClassType) + javaClassType = (JavaClassType) type; + else + javaClassType = new JavaClassType(type.getClassName(), type.getPackageName()); return getClassSourceInternal( - (JavaClassType) type, archiveRoot, new AsmJavaClassProvider(view)); + javaClassType, archiveRoot, new AsmJavaClassProvider(view)); } catch (ExecutionException e) { throw new RuntimeException("Failed to retrieve file system from cache for " + path, e); } diff --git a/sootup.java.core/src/main/java/sootup/java/core/JavaIdentifierFactory.java b/sootup.java.core/src/main/java/sootup/java/core/JavaIdentifierFactory.java index 52f8a08b2fe..9d969ec5f10 100644 --- a/sootup.java.core/src/main/java/sootup/java/core/JavaIdentifierFactory.java +++ b/sootup.java.core/src/main/java/sootup/java/core/JavaIdentifierFactory.java @@ -237,6 +237,8 @@ public Collection getAllPrimitiveTypes() { @Nonnull public JavaClassType getBoxedType(@Nonnull PrimitiveType primitiveType) { String name = primitiveType.getName(); + if (primitiveType instanceof PrimitiveType.IntType) + name = "integer"; StringBuilder boxedname = new StringBuilder(name); boxedname.setCharAt(0, Character.toUpperCase(boxedname.charAt(0))); return getClassType(boxedname.toString(), "java.lang"); diff --git a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java index fcb2d4eb295..cf16f323ba0 100644 --- a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java @@ -8,14 +8,12 @@ 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.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; +import java.util.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -57,7 +55,10 @@ public PackageName getPackageName() { JimpleAnalysisInputLocation jimpleInputLocation = new JimpleAnalysisInputLocation(Paths.get(classPath), SourceType.Application); - jimpleView = new JimpleView(jimpleInputLocation); + // rt.jar is required since the validator uses typeHierarchy + DefaultRTJarAnalysisInputLocation defaultRTJarAnalysisInputLocation = new DefaultRTJarAnalysisInputLocation(); + jimpleView = new JimpleView(Arrays.asList(jimpleInputLocation, defaultRTJarAnalysisInputLocation)); + final Optional classSource1 = jimpleView.getClass(classTypeFieldRefValidator); assertFalse(classSource1.isPresent()); @@ -71,44 +72,37 @@ public PackageName getPackageName() { } @Test - public void testInvokeArgumentValidatorSuccess() { + public void invokeArgumentValidator_success() { List validationExceptions_success; - validationExceptions_success = - invokeArgumentValidator.validate( - classes.stream() - .filter(c -> c.getType().getClassName().equals("InvokeArgumentValidator")) - .findFirst() - .get() - .getMethods() - .stream() - .filter(m -> m.getName().equals("invokeArgumentValidator_success")) - .map(m -> m.getBody()) - .findFirst() - .get(), - jimpleView); + validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod( + jimpleView.getIdentifierFactory() + .parseMethodSignature("") ) + .get().getBody(), jimpleView); assertEquals(0, validationExceptions_success.size()); } @Test - public void testInvokeArgumentValidatorFailure() { + public void testArgumentNumber_fail() { List validationExceptions_success; - validationExceptions_success = - invokeArgumentValidator.validate( - classes.stream() - .filter(c -> c.getType().getClassName().equals("InvokeArgumentValidator")) - .findFirst() - .get() - .getMethods() - .stream() - .filter(m -> m.getName().equals("invokeArgumentValidator_fail")) - .map(m -> m.getBody()) - .findFirst() - .get(), - jimpleView); + validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod( + jimpleView.getIdentifierFactory() + .parseMethodSignature("") ) + .get().getBody(), jimpleView); assertEquals(1, validationExceptions_success.size()); } + @Test + public void testArgumentType_fail() { + List validationExceptions_success; + + validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod( + jimpleView.getIdentifierFactory() + .parseMethodSignature("") ) + .get().getBody(), jimpleView); + assertEquals(2, validationExceptions_success.size()); + } + } diff --git a/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple b/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple index 45ca74cc7c3..6846ce5dfd8 100644 --- a/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple +++ b/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple @@ -10,42 +10,46 @@ public class InvokeArgumentValidator extends java.lang.Object return; } - public java.lang.String join(int,java.lang.String) - { + public void join(java.lang.Number,java.lang.String) + { InvokeArgumentValidator l0; - int l1; + java.lang.Number l1; java.lang.String l2; - unknown $stack3; l0 := @this: InvokeArgumentValidator; - l1 := @parameter0: int; + l1 := @parameter0: java.lang.Number; l2 := @parameter1: java.lang.String; - $stack3 = dynamicinvoke "makeConcatWithConstants" (l1, l2) ("\u0001\u0001"); - return $stack3; - } + return; + } public void invokeArgumentValidator_success() { InvokeArgumentValidator l0; - unknown l1; - l0 := @this: InvokeArgumentValidator; - l1 = virtualinvoke l0.(1, "hello world"); + virtualinvoke l0.(1, "hello"); return; } - public void invokeArgumentValidator_fail() + public void testArgumentNumber_fail() { InvokeArgumentValidator l0; - unknown l1; + l0 := @this: InvokeArgumentValidator; + virtualinvoke l0.(1); + + return; + } + + public void testArgumentType_fail() + { + InvokeArgumentValidator l0; l0 := @this: InvokeArgumentValidator; - l1 = virtualinvoke l0.(1); + virtualinvoke l0.("hello", 1); return; } From d7e9a9c65f28e8c5cfc04feb1e6c634421186e0b Mon Sep 17 00:00:00 2001 From: momo <149540451+Momo-Not-Emo@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:27:17 -0500 Subject: [PATCH 03/11] Optimize classTypeFieldRefValidator existence check Speed up the class search process by limiting the search scope within application classes, since classTypeFieldRefValidator does not exist in rt.jar --- .../sootup/core/validation/InvokeArgumentValidator.java | 3 +-- .../tests/validator/InvokeArgumentValidatorTest.java | 9 +++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java index 88fc82517b6..3418044049a 100644 --- a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java @@ -71,8 +71,7 @@ public List validate(Body body, View view) { || !typeHierarchy.subtypesOf(paramType).contains(argType))) { validationException.add(new ValidationException(stmt, String.format("Invalid argument type. Required %s but provided %s.", paramType, argType))); - System.out.println(String.format("Invalid argument type. Required %s but provided %s.", paramType, argType)); - } + } } } } diff --git a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java index cf16f323ba0..16d355e72ab 100644 --- a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java @@ -59,16 +59,17 @@ public PackageName getPackageName() { DefaultRTJarAnalysisInputLocation defaultRTJarAnalysisInputLocation = new DefaultRTJarAnalysisInputLocation(); jimpleView = new JimpleView(Arrays.asList(jimpleInputLocation, defaultRTJarAnalysisInputLocation)); - final Optional classSource1 = jimpleView.getClass(classTypeFieldRefValidator); - assertFalse(classSource1.isPresent()); - classes = new HashSet<>(); // Set to track the classes to check - for (SootClass aClass : jimpleView.getClasses()) { if (!aClass.isLibraryClass()) { classes.add(aClass); } } + + // Speed up the class search process by limiting the search scope within application classes + final Optional classSource1 = classes.stream().filter(c -> c.getType() + .equals(classTypeFieldRefValidator)).findFirst(); + assertFalse(classSource1.isPresent()); } @Test From c5f7e22789e6a98fd209df6304bebb3ac44219cf Mon Sep 17 00:00:00 2001 From: momo <149540451+Momo-Not-Emo@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:30:46 -0500 Subject: [PATCH 04/11] add a getBoxedName() to the primitive types --- .../java/sootup/core/types/PrimitiveType.java | 51 +++++++++++++++++++ .../validation/InvokeArgumentValidator.java | 8 +-- .../java/core/JavaIdentifierFactory.java | 7 +-- 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java b/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java index 73189e9b50a..88b34f961f2 100644 --- a/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java +++ b/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java @@ -50,6 +50,9 @@ public String getName() { return name; } + @Nonnull + public abstract String getBoxedName(); + @Override @Nonnull public String toString() { @@ -107,6 +110,12 @@ public static ByteType getInstance() { return INSTANCE; } + @Nonnull + @Override + public String getBoxedName() { + return "Byte"; + } + @Override public void accept(@Nonnull TypeVisitor v) { v.caseByteType(); @@ -124,6 +133,12 @@ public static ShortType getInstance() { return INSTANCE; } + @Nonnull + @Override + public String getBoxedName() { + return "Short"; + } + @Override public void accept(@Nonnull TypeVisitor v) { v.caseShortType(); @@ -145,6 +160,12 @@ public static IntType getInstance() { return INSTANCE; } + @Nonnull + @Override + public String getBoxedName() { + return "Integer"; + } + @Override public void accept(@Nonnull TypeVisitor v) { v.caseIntType(); @@ -162,6 +183,12 @@ public static DoubleType getInstance() { return INSTANCE; } + @Nonnull + @Override + public String getBoxedName() { + return "Double"; + } + @Override public void accept(@Nonnull TypeVisitor v) { v.caseDoubleType(); @@ -179,6 +206,12 @@ public static LongType getInstance() { return INSTANCE; } + @Nonnull + @Override + public String getBoxedName() { + return "Long"; + } + @Override public void accept(@Nonnull TypeVisitor v) { v.caseLongType(); @@ -196,6 +229,12 @@ public static FloatType getInstance() { return INSTANCE; } + @Nonnull + @Override + public String getBoxedName() { + return "Float"; + } + @Override public void accept(@Nonnull TypeVisitor v) { v.caseFloatType(); @@ -213,6 +252,12 @@ public static CharType getInstance() { return INSTANCE; } + @Nonnull + @Override + public String getBoxedName() { + return "Character"; + } + @Override public void accept(@Nonnull TypeVisitor v) { v.caseCharType(); @@ -230,6 +275,12 @@ public static BooleanType getInstance() { return INSTANCE; } + @Nonnull + @Override + public String getBoxedName() { + return "Boolean"; + } + @Override public void accept(@Nonnull TypeVisitor v) { v.caseBooleanType(); diff --git a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java index 3418044049a..ae5cb189628 100644 --- a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java @@ -80,10 +80,10 @@ public List validate(Body body, View view) { } private ClassType getClassType(IdentifierFactory identifierFactory, Type type) { - if (type instanceof PrimitiveType) { - type = identifierFactory.getBoxedType((PrimitiveType) type); - } - return identifierFactory.getClassType(type.toString()); + if (type instanceof PrimitiveType) + return identifierFactory.getBoxedType((PrimitiveType) type); + else + return identifierFactory.getClassType(type.toString()); } @Override diff --git a/sootup.java.core/src/main/java/sootup/java/core/JavaIdentifierFactory.java b/sootup.java.core/src/main/java/sootup/java/core/JavaIdentifierFactory.java index 9d969ec5f10..043d3fadab8 100644 --- a/sootup.java.core/src/main/java/sootup/java/core/JavaIdentifierFactory.java +++ b/sootup.java.core/src/main/java/sootup/java/core/JavaIdentifierFactory.java @@ -236,12 +236,7 @@ public Collection getAllPrimitiveTypes() { @Override @Nonnull public JavaClassType getBoxedType(@Nonnull PrimitiveType primitiveType) { - String name = primitiveType.getName(); - if (primitiveType instanceof PrimitiveType.IntType) - name = "integer"; - StringBuilder boxedname = new StringBuilder(name); - boxedname.setCharAt(0, Character.toUpperCase(boxedname.charAt(0))); - return getClassType(boxedname.toString(), "java.lang"); + return getClassType(primitiveType.getBoxedName(), "java.lang"); } @Override From b9b3a31929e9cb27e583563317685eb0aed7cf01 Mon Sep 17 00:00:00 2001 From: Momo-Not-Emo <149540451+Momo-Not-Emo@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:07:17 -0500 Subject: [PATCH 05/11] Check implicit conversion between primitive type --- .../java/sootup/core/types/PrimitiveType.java | 23 +++++++++++++++++++ .../validation/InvokeArgumentValidator.java | 18 +++++++++++---- .../ArchiveBasedAnalysisInputLocation.java | 2 ++ .../InvokeArgumentValidatorTest.java | 11 +++++++++ .../jimple/InvokeArgumentValidator.jimple | 21 +++++++++++++++++ 5 files changed, 70 insertions(+), 5 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java b/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java index 88b34f961f2..08a73d2619d 100644 --- a/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java +++ b/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java @@ -23,8 +23,14 @@ */ import javax.annotation.Nonnull; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import sootup.core.jimple.visitor.TypeVisitor; +import java.util.Map; +import java.util.Set; + /** Represents Java's primitive types. */ public abstract class PrimitiveType extends Type { @@ -59,6 +65,23 @@ public String toString() { return name; } + private static final Map, Set>> implicitConversionMap = + ImmutableMap., Set>>builder() + .put(ByteType.class, ImmutableSet.of(ShortType.class, IntType.class, LongType.class, FloatType.class, DoubleType.class)) + .put(ShortType.class, ImmutableSet.of(IntType.class, LongType.class, FloatType.class, DoubleType.class)) + .put(CharType.class, ImmutableSet.of(IntType.class, LongType.class, FloatType.class, DoubleType.class)) + .put(IntType.class, ImmutableSet.of(LongType.class, FloatType.class, DoubleType.class)) + .put(LongType.class, ImmutableSet.of(FloatType.class, DoubleType.class)) + .put(FloatType.class, ImmutableSet.of(DoubleType.class)) + .build(); + + public static boolean isImplicitlyConvertibleTo(PrimitiveType fromType, PrimitiveType toType) { + Class fromTypeClass = fromType.getClass(); + Class toTypeClass = toType.getClass(); + return implicitConversionMap.containsKey(fromTypeClass) && implicitConversionMap.get(fromTypeClass).contains(toTypeClass) + || IntType.class.isAssignableFrom(fromTypeClass) && implicitConversionMap.get(IntType.class).contains(toTypeClass); + } + @Nonnull public static ByteType getByte() { return ByteType.getInstance(); diff --git a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java index ae5cb189628..1d8aedb350a 100644 --- a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java @@ -65,12 +65,20 @@ public List validate(Body body, View view) { Iterator iterArgs = args.iterator(); Iterator 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) - || !typeHierarchy.subtypesOf(paramType).contains(argType))) { + // handle implicit conversion cases. e.g., `int` is used as an argument of a `double` parameter + Type argType = iterArgs.next().getType(); + Type paraType = iterParameters.next(); + if (argType instanceof PrimitiveType && paraType instanceof PrimitiveType + && PrimitiveType.isImplicitlyConvertibleTo((PrimitiveType) argType, (PrimitiveType) paraType)) { + continue; + } + // other cases + ClassType argClassType = getClassType(identifierFactory, argType); + ClassType paramClassType = getClassType(identifierFactory, paraType); + if (argClassType != paramClassType && (!typeHierarchy.contains(paramClassType) + || !typeHierarchy.subtypesOf(paramClassType).contains(argClassType))) { validationException.add(new ValidationException(stmt, - String.format("Invalid argument type. Required %s but provided %s.", paramType, argType))); + String.format("Invalid argument type. Required %s but provided %s.", paraType, argType))); } } } diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java index 609bc76b934..29b9d7c83e9 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java @@ -100,6 +100,8 @@ public Optional getClassSource(@Nonnull ClassType type, @No FileSystem fs = fileSystemCache.get(path); final Path archiveRoot = fs.getPath("/"); JavaClassType javaClassType = null; + + // This is a temporary workaround to prevent a casting exception when passing an anonymous ClassType object if (type instanceof JavaClassType) javaClassType = (JavaClassType) type; else diff --git a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java index 16d355e72ab..f319e2f43a0 100644 --- a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java @@ -106,4 +106,15 @@ public void testArgumentType_fail() { assertEquals(2, validationExceptions_success.size()); } + @Test + public void testPrimitiveTypeConversion_success() { + List validationExceptions_success; + + validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod( + jimpleView.getIdentifierFactory() + .parseMethodSignature("") ) + .get().getBody(), jimpleView); + assertEquals(0, validationExceptions_success.size()); + } + } diff --git a/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple b/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple index 6846ce5dfd8..b786bff4cc1 100644 --- a/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple +++ b/sootup.tests/src/test/resources/validator/jimple/InvokeArgumentValidator.jimple @@ -24,6 +24,27 @@ public class InvokeArgumentValidator extends java.lang.Object return; } + public void receivePrimitive(double) + { + InvokeArgumentValidator l0; + double l1; + + l0 := @this: InvokeArgumentValidator; + l1 := @parameter0: double; + + return; + } + + public void testPrimitiveTypeConversion_success() + { + InvokeArgumentValidator l0; + + l0 := @this: InvokeArgumentValidator; + virtualinvoke l0.(1); + + return; + } + public void invokeArgumentValidator_success() { InvokeArgumentValidator l0; From d0350151d6375bdd77bbb4bee024baf9b7eaded1 Mon Sep 17 00:00:00 2001 From: "M.Schmidt" Date: Thu, 29 Feb 2024 10:50:06 +0100 Subject: [PATCH 06/11] streamline Validator, code style --- .../java/sootup/core/types/PrimitiveType.java | 60 +++-- .../validation/InvokeArgumentValidator.java | 135 ++++++---- .../ArchiveBasedAnalysisInputLocation.java | 13 +- .../InvokeArgumentValidatorTest.java | 232 ++++++++++-------- 4 files changed, 260 insertions(+), 180 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java b/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java index 08a73d2619d..6f678ef5507 100644 --- a/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java +++ b/sootup.core/src/main/java/sootup/core/types/PrimitiveType.java @@ -22,14 +22,12 @@ * #L% */ -import javax.annotation.Nonnull; - import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import sootup.core.jimple.visitor.TypeVisitor; - import java.util.Map; import java.util.Set; +import javax.annotation.Nonnull; +import sootup.core.jimple.visitor.TypeVisitor; /** Represents Java's primitive types. */ public abstract class PrimitiveType extends Type { @@ -65,22 +63,44 @@ public String toString() { return name; } - private static final Map, Set>> implicitConversionMap = - ImmutableMap., Set>>builder() - .put(ByteType.class, ImmutableSet.of(ShortType.class, IntType.class, LongType.class, FloatType.class, DoubleType.class)) - .put(ShortType.class, ImmutableSet.of(IntType.class, LongType.class, FloatType.class, DoubleType.class)) - .put(CharType.class, ImmutableSet.of(IntType.class, LongType.class, FloatType.class, DoubleType.class)) - .put(IntType.class, ImmutableSet.of(LongType.class, FloatType.class, DoubleType.class)) - .put(LongType.class, ImmutableSet.of(FloatType.class, DoubleType.class)) - .put(FloatType.class, ImmutableSet.of(DoubleType.class)) - .build(); - - public static boolean isImplicitlyConvertibleTo(PrimitiveType fromType, PrimitiveType toType) { - Class fromTypeClass = fromType.getClass(); - Class toTypeClass = toType.getClass(); - return implicitConversionMap.containsKey(fromTypeClass) && implicitConversionMap.get(fromTypeClass).contains(toTypeClass) - || IntType.class.isAssignableFrom(fromTypeClass) && implicitConversionMap.get(IntType.class).contains(toTypeClass); - } + private static final Map, Set>> + implicitConversionMap = + ImmutableMap + ., Set>>builder() + .put( + ByteType.class, + ImmutableSet.of( + ShortType.class, + IntType.class, + LongType.class, + FloatType.class, + DoubleType.class)) + .put( + ShortType.class, + ImmutableSet.of(IntType.class, LongType.class, FloatType.class, DoubleType.class)) + .put( + CharType.class, + ImmutableSet.of(IntType.class, LongType.class, FloatType.class, DoubleType.class)) + .put( + IntType.class, ImmutableSet.of(LongType.class, FloatType.class, DoubleType.class)) + .put(LongType.class, ImmutableSet.of(FloatType.class, DoubleType.class)) + .put(FloatType.class, ImmutableSet.of(DoubleType.class)) + .build(); + + /** + * @param fromType e.g. the method argument + * @param toType e.g. the method parameter + * @return true if type conversion is possible + */ + public static boolean isImplicitlyConvertibleTo( + @Nonnull PrimitiveType fromType, @Nonnull PrimitiveType toType) { + Class fromTypeClass = fromType.getClass(); + Class toTypeClass = toType.getClass(); + return implicitConversionMap.containsKey(fromTypeClass) + && implicitConversionMap.get(fromTypeClass).contains(toTypeClass) + || IntType.class.isAssignableFrom(fromTypeClass) + && implicitConversionMap.get(IntType.class).contains(toTypeClass); + } @Nonnull public static ByteType getByte() { diff --git a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java index 1d8aedb350a..5071d160f23 100644 --- a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java @@ -22,6 +22,9 @@ * #L% */ +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import sootup.core.IdentifierFactory; import sootup.core.jimple.basic.Immediate; import sootup.core.jimple.common.expr.AbstractInvokeExpr; @@ -34,10 +37,6 @@ 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. @@ -45,57 +44,91 @@ * @author Steven Arzt */ public class InvokeArgumentValidator implements BodyValidator { - @Override - public List validate(Body body, View view) { - List validationException = new ArrayList<>(); + @Override + public List validate(Body body, View view) { + List validationException = new ArrayList<>(); + + TypeHierarchy typeHierarchy = view.getTypeHierarchy(); + IdentifierFactory identifierFactory = view.getIdentifierFactory(); + + for (Stmt stmt : body.getStmts()) { + if (!stmt.containsInvokeExpr()) { + continue; + } + + AbstractInvokeExpr invExpr = stmt.getInvokeExpr(); + MethodSignature callee = invExpr.getMethodSignature(); + List args = invExpr.getArgs(); + List parameterTypes = callee.getParameterTypes(); + + if (invExpr.getArgCount() != parameterTypes.size()) { + validationException.add( + new ValidationException( + stmt, + "Argument count '" + + invExpr.getArgCount() + + "' does not match the number of expected parameters '" + + parameterTypes.size() + + "'.")); + continue; + } - for (Stmt stmt : body.getStmts()) { - if (stmt.containsInvokeExpr()) { - AbstractInvokeExpr invExpr = - stmt.getInvokeExpr(); - MethodSignature callee = invExpr.getMethodSignature(); - List args = invExpr.getArgs(); - List 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 iterArgs = args.iterator(); - Iterator iterParameters = parameterTypes.iterator(); - while (iterArgs.hasNext() && iterParameters.hasNext()) { - // handle implicit conversion cases. e.g., `int` is used as an argument of a `double` parameter - Type argType = iterArgs.next().getType(); - Type paraType = iterParameters.next(); - if (argType instanceof PrimitiveType && paraType instanceof PrimitiveType - && PrimitiveType.isImplicitlyConvertibleTo((PrimitiveType) argType, (PrimitiveType) paraType)) { - continue; - } - // other cases - ClassType argClassType = getClassType(identifierFactory, argType); - ClassType paramClassType = getClassType(identifierFactory, paraType); - if (argClassType != paramClassType && (!typeHierarchy.contains(paramClassType) - || !typeHierarchy.subtypesOf(paramClassType).contains(argClassType))) { - validationException.add(new ValidationException(stmt, - String.format("Invalid argument type. Required %s but provided %s.", paraType, argType))); - } - } - } + // check argument type + ClassType argClassType; + Iterator iterParameters = parameterTypes.iterator(); + for (Immediate arg : args) { + Type argType = arg.getType(); + Type parameterType = iterParameters.next(); + + // handle implicit conversion cases. e.g., `int` is used as an argument of a `double` + // parameter + if (argType instanceof PrimitiveType) { + if (parameterType instanceof PrimitiveType) { + + if (argType == parameterType) { + continue; } + + if (!PrimitiveType.isImplicitlyConvertibleTo( + (PrimitiveType) argType, (PrimitiveType) parameterType)) { + validationException.add( + new ValidationException( + stmt, + String.format( + "Invalid argument type - type conversion is not applicable to '%s' and the provided '%s'.", + parameterType, argType))); + } + continue; + } + + // prepare autoboxing test + argClassType = identifierFactory.getBoxedType(((PrimitiveType) argType)); + + } else { + argClassType = (ClassType) argType; } - return validationException; - } - private ClassType getClassType(IdentifierFactory identifierFactory, Type type) { - if (type instanceof PrimitiveType) - return identifierFactory.getBoxedType((PrimitiveType) type); - else - return identifierFactory.getClassType(type.toString()); + // non-primitive type cases, primitive+autoboxing + ClassType parameterClassType = (ClassType) parameterType; + if (argClassType == parameterClassType) { + continue; + } + if (typeHierarchy.contains(parameterClassType) + && !typeHierarchy.isSubtype(parameterClassType, argClassType)) { + validationException.add( + new ValidationException( + stmt, + String.format( + "Invalid argument type. Required '%s' but provided was '%s'.", + parameterType, argType))); + } + } } + return validationException; + } - @Override - public boolean isBasicValidator() { - return true; - } + @Override + public boolean isBasicValidator() { + return true; + } } diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java index 29b9d7c83e9..422b4ba13bb 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/inputlocation/ArchiveBasedAnalysisInputLocation.java @@ -101,13 +101,12 @@ public Optional getClassSource(@Nonnull ClassType type, @No final Path archiveRoot = fs.getPath("/"); JavaClassType javaClassType = null; - // This is a temporary workaround to prevent a casting exception when passing an anonymous ClassType object - if (type instanceof JavaClassType) - javaClassType = (JavaClassType) type; - else - javaClassType = new JavaClassType(type.getClassName(), type.getPackageName()); - return getClassSourceInternal( - javaClassType, archiveRoot, new AsmJavaClassProvider(view)); + // FIXME: This is a temporary workaround to prevent a casting exception when passing an + // anonymous + // ClassType object e.g. from a JimpleAnalysisInputLocation + if (type instanceof JavaClassType) javaClassType = (JavaClassType) type; + else javaClassType = new JavaClassType(type.getClassName(), type.getPackageName()); + return getClassSourceInternal(javaClassType, archiveRoot, new AsmJavaClassProvider(view)); } catch (ExecutionException e) { throw new RuntimeException("Failed to retrieve file system from cache for " + path, e); } diff --git a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java index f319e2f43a0..7d35197f1ba 100644 --- a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java @@ -1,5 +1,10 @@ package sootup.tests.validator; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.nio.file.Paths; +import java.util.*; import org.junit.Before; import org.junit.Test; import sootup.core.model.SootClass; @@ -12,109 +17,132 @@ 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 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()) { - classes.add(aClass); - } - } - - // Speed up the class search process by limiting the search scope within application classes - final Optional classSource1 = classes.stream().filter(c -> c.getType() - .equals(classTypeFieldRefValidator)).findFirst(); - assertFalse(classSource1.isPresent()); - } - - @Test - public void invokeArgumentValidator_success() { - List validationExceptions_success; - - validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod( - jimpleView.getIdentifierFactory() - .parseMethodSignature("") ) - .get().getBody(), jimpleView); - - assertEquals(0, validationExceptions_success.size()); - } - - @Test - public void testArgumentNumber_fail() { - List validationExceptions_success; - - validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod( - jimpleView.getIdentifierFactory() - .parseMethodSignature("") ) - .get().getBody(), jimpleView); - assertEquals(1, validationExceptions_success.size()); - } - - @Test - public void testArgumentType_fail() { - List validationExceptions_success; - - validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod( - jimpleView.getIdentifierFactory() - .parseMethodSignature("") ) - .get().getBody(), jimpleView); - assertEquals(2, validationExceptions_success.size()); - } - - @Test - public void testPrimitiveTypeConversion_success() { - List validationExceptions_success; - - validationExceptions_success = invokeArgumentValidator.validate( jimpleView.getMethod( - jimpleView.getIdentifierFactory() - .parseMethodSignature("") ) - .get().getBody(), jimpleView); - assertEquals(0, validationExceptions_success.size()); + InvokeArgumentValidator invokeArgumentValidator; + JimpleView jimpleView; + Collection 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()) { + classes.add(aClass); + } } + // Speed up the class search process by limiting the search scope within application classes + final Optional classSource1 = + classes.stream().filter(c -> c.getType().equals(classTypeFieldRefValidator)).findFirst(); + assertFalse(classSource1.isPresent()); + } + + @Test + public void invokeArgumentValidator_success() { + List validationExceptions_success; + + validationExceptions_success = + invokeArgumentValidator.validate( + jimpleView + .getMethod( + jimpleView + .getIdentifierFactory() + .parseMethodSignature( + "")) + .get() + .getBody(), + jimpleView); + + assertEquals(0, validationExceptions_success.size()); + } + + @Test + public void testArgumentNumber_fail() { + List validationExceptions_success; + + validationExceptions_success = + invokeArgumentValidator.validate( + jimpleView + .getMethod( + jimpleView + .getIdentifierFactory() + .parseMethodSignature( + "")) + .get() + .getBody(), + jimpleView); + assertEquals(1, validationExceptions_success.size()); + } + + @Test + public void testArgumentType_fail() { + List validationExceptions_success; + + validationExceptions_success = + invokeArgumentValidator.validate( + jimpleView + .getMethod( + jimpleView + .getIdentifierFactory() + .parseMethodSignature( + "")) + .get() + .getBody(), + jimpleView); + assertEquals(2, validationExceptions_success.size()); + } + + @Test + public void testPrimitiveTypeConversion_success() { + List validationExceptions_success; + + validationExceptions_success = + invokeArgumentValidator.validate( + jimpleView + .getMethod( + jimpleView + .getIdentifierFactory() + .parseMethodSignature( + "")) + .get() + .getBody(), + jimpleView); + assertEquals(0, validationExceptions_success.size()); + } } From 37b73b3ff957b03d84ab8d07964992260160bea4 Mon Sep 17 00:00:00 2001 From: "M.Schmidt" Date: Thu, 29 Feb 2024 10:57:43 +0100 Subject: [PATCH 07/11] adapt to upstream changes in develop --- .../validator/InvokeArgumentValidatorTest.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java index 7d35197f1ba..280a222c0b5 100644 --- a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java @@ -1,12 +1,10 @@ package sootup.tests.validator; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - import java.nio.file.Paths; import java.util.*; -import org.junit.Before; -import org.junit.Test; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import sootup.core.model.SootClass; import sootup.core.model.SourceType; import sootup.core.signatures.PackageName; @@ -17,12 +15,15 @@ import sootup.jimple.parser.JimpleAnalysisInputLocation; import sootup.jimple.parser.JimpleView; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + public class InvokeArgumentValidatorTest { InvokeArgumentValidator invokeArgumentValidator; JimpleView jimpleView; Collection classes; - @Before + @BeforeEach public void Setup() { invokeArgumentValidator = new InvokeArgumentValidator(); From c692e3032c80f090bf4fdb1b8b37b1b1efb259d0 Mon Sep 17 00:00:00 2001 From: "M.Schmidt" Date: Thu, 29 Feb 2024 11:30:01 +0100 Subject: [PATCH 08/11] avoid classcastexceptions --- .../core/typehierarchy/TypeHierarchy.java | 6 ++--- .../validation/InvokeArgumentValidator.java | 25 +++++++++++++------ .../InvokeArgumentValidatorTest.java | 7 +++--- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/TypeHierarchy.java b/sootup.core/src/main/java/sootup/core/typehierarchy/TypeHierarchy.java index 096193ceb28..8d0a7d52b91 100644 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/TypeHierarchy.java +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/TypeHierarchy.java @@ -162,10 +162,10 @@ default boolean isSubtype(@Nonnull Type supertype, @Nonnull Type potentialSubtyp || supertypeName.equals("java.io.Serializable") || supertypeName.equals("java.lang.Cloneable"); } else { - throw new AssertionError("potentialSubtype has unexpected type"); + throw new IllegalStateException("potentialSubtype has unexpected type"); } } else { - throw new AssertionError("supertype has unexpected type"); + throw new IllegalStateException("supertype has unexpected type"); } } @@ -200,6 +200,6 @@ default List superClassesOf(@Nonnull ClassType classType) { Set directlyExtendedInterfacesOf(@Nonnull ClassType type); // checks if a Type is contained int the TypeHierarchy - should return the equivalent to - // View.getClass(...).isPresent() + // View.getClass(...).isPresent() - the opposite is not true! boolean contains(ClassType type); } diff --git a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java index 5071d160f23..ae21be88fc4 100644 --- a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java @@ -32,9 +32,7 @@ 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.types.*; import sootup.core.views.View; /** @@ -109,12 +107,25 @@ public List validate(Body body, View view) { } // non-primitive type cases, primitive+autoboxing - ClassType parameterClassType = (ClassType) parameterType; - if (argClassType == parameterClassType) { + if (argClassType == parameterType) { continue; } - if (typeHierarchy.contains(parameterClassType) - && !typeHierarchy.isSubtype(parameterClassType, argClassType)) { + + // check if the (base-) type is contained in the typehierarchy - else it throws exceptions + // TODO: incorporate into api after #874 is done + if (parameterType instanceof ClassType + && !typeHierarchy.contains((ClassType) parameterType)) { + continue; + } + + if (parameterType instanceof ArrayType) { + Type baseType = ((ArrayType) parameterType).getBaseType(); + if (baseType instanceof ClassType && !typeHierarchy.contains((ClassType) baseType)) { + continue; + } + } + + if (!typeHierarchy.isSubtype(parameterType, argClassType)) { validationException.add( new ValidationException( stmt, diff --git a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java index 280a222c0b5..45209c8bec7 100644 --- a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java @@ -1,8 +1,10 @@ package sootup.tests.validator; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + import java.nio.file.Paths; import java.util.*; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import sootup.core.model.SootClass; @@ -15,9 +17,6 @@ import sootup.jimple.parser.JimpleAnalysisInputLocation; import sootup.jimple.parser.JimpleView; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - public class InvokeArgumentValidatorTest { InvokeArgumentValidator invokeArgumentValidator; JimpleView jimpleView; From d63996bf026a4797aa590533cbdde7c0e425fe19 Mon Sep 17 00:00:00 2001 From: "M.Schmidt" Date: Thu, 29 Feb 2024 11:33:35 +0100 Subject: [PATCH 09/11] simplify testcases --- .../InvokeArgumentValidatorTest.java | 51 ++++--------------- 1 file changed, 9 insertions(+), 42 deletions(-) diff --git a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java index 45209c8bec7..6fef95c8a1a 100644 --- a/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/validator/InvokeArgumentValidatorTest.java @@ -5,12 +5,10 @@ import java.nio.file.Paths; import java.util.*; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.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; @@ -18,37 +16,11 @@ import sootup.jimple.parser.JimpleView; public class InvokeArgumentValidatorTest { - InvokeArgumentValidator invokeArgumentValidator; - JimpleView jimpleView; - Collection classes; - - @BeforeEach - 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"); - } - }; + InvokeArgumentValidator invokeArgumentValidator = new InvokeArgumentValidator(); + static JimpleView jimpleView; + + @BeforeAll + public static void setup() { String classPath = "src/test/resources/validator/jimple"; JimpleAnalysisInputLocation jimpleInputLocation = @@ -60,16 +32,11 @@ public PackageName getPackageName() { 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()) { - classes.add(aClass); - } - } - // Speed up the class search process by limiting the search scope within application classes final Optional classSource1 = - classes.stream().filter(c -> c.getType().equals(classTypeFieldRefValidator)).findFirst(); + jimpleView.getClasses().stream() + .filter(c -> c.getType().toString().equals("jimple.InvokeArgumentValidator")) + .findFirst(); assertFalse(classSource1.isPresent()); } From 15cd418548b1164cdd808ea6b34f4ed8e851e7d3 Mon Sep 17 00:00:00 2001 From: "M.Schmidt" Date: Thu, 29 Feb 2024 11:34:34 +0100 Subject: [PATCH 10/11] remove cce leftover --- .../java/sootup/core/validation/InvokeArgumentValidator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java index ae21be88fc4..bf2646e5506 100644 --- a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java @@ -28,6 +28,7 @@ import sootup.core.IdentifierFactory; import sootup.core.jimple.basic.Immediate; import sootup.core.jimple.common.expr.AbstractInvokeExpr; + import sootup.core.jimple.common.ref.Ref; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; import sootup.core.signatures.MethodSignature; @@ -72,7 +73,7 @@ public List validate(Body body, View view) { } // check argument type - ClassType argClassType; + ReferenceType argClassType; Iterator iterParameters = parameterTypes.iterator(); for (Immediate arg : args) { Type argType = arg.getType(); @@ -103,7 +104,7 @@ public List validate(Body body, View view) { argClassType = identifierFactory.getBoxedType(((PrimitiveType) argType)); } else { - argClassType = (ClassType) argType; + argClassType = (ReferenceType) argType; } // non-primitive type cases, primitive+autoboxing From a0fe341e9b1d28bcab29f13fca6a626e998477c7 Mon Sep 17 00:00:00 2001 From: "M.Schmidt" Date: Thu, 29 Feb 2024 11:52:30 +0100 Subject: [PATCH 11/11] style --- .../java/sootup/core/validation/InvokeArgumentValidator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java index bf2646e5506..04539a17ab9 100644 --- a/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/InvokeArgumentValidator.java @@ -28,7 +28,6 @@ import sootup.core.IdentifierFactory; import sootup.core.jimple.basic.Immediate; import sootup.core.jimple.common.expr.AbstractInvokeExpr; - import sootup.core.jimple.common.ref.Ref; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; import sootup.core.signatures.MethodSignature;