diff --git a/sootup.core/src/main/java/sootup/core/validation/MethodDeclarationValidator.java b/sootup.core/src/main/java/sootup/core/validation/MethodDeclarationValidator.java index 6c22513b396..7216150008f 100644 --- a/sootup.core/src/main/java/sootup/core/validation/MethodDeclarationValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/MethodDeclarationValidator.java @@ -4,7 +4,7 @@ * #%L * Soot - a J*va Optimization Framework * %% - * Copyright (C) 1997-2020 Raja Vallée-Rai, Linghui Luo and others + * Copyright (C) 1997-2020 Raja Vallée-Rai, Linghui Luo, Akshita Dubey and others * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as @@ -24,24 +24,74 @@ import java.util.List; import sootup.core.model.SootClass; +import sootup.core.model.SootMethod; +import sootup.core.types.NullType; +import sootup.core.types.Type; +import sootup.core.types.VoidType; /** - * Validates classes to make sure that all method signatures are valid + * Validates classes to make sure that all method signatures are valid and does not contain + * impossible method modifier combinations * - * @author Steven Arzt + * @author Akshita Dubey */ public class MethodDeclarationValidator implements ClassValidator { @Override public void validate(SootClass sc, List exceptions) { - // TODO: check code from old soot in the comment - - /* - * if (sc.isConcrete()) { for (SootMethod sm : sc.getMethods()) { for (Type tp : sm.getParameterTypes()) { if (tp == - * null) { exceptions.add(new ValidationException(sm, "Null parameter types are invalid")); } if (tp instanceof VoidType) - * { exceptions.add(new ValidationException(sm, "Void parameter types are invalid")); } if (!tp.isAllowedInFinalCode()) { - * exceptions.add(new ValidationException(sm, "Parameter type not allowed in final code")); } } } } - */ + + for (SootMethod sm : sc.getMethods()) { + if (sc.isConcrete()) { + List parameterTypes = sm.getParameterTypes(); + for (Type tp : parameterTypes) { + if (tp instanceof NullType) { + exceptions.add(new ValidationException(sm, "Null parameter types are invalid")); + } + if (tp instanceof VoidType) { + exceptions.add(new ValidationException(sm, "Void parameter types are invalid")); + } + } + } + if (sm.isAbstract()) { + if (sm.isFinal()) { + exceptions.add(new ValidationException(sm, "Method cannot be Abstract and Final")); + } + if (sm.isNative()) { + exceptions.add(new ValidationException(sm, "Method cannot be Abstract and Native")); + } + if (sm.isPrivate()) { + exceptions.add(new ValidationException(sm, "Method cannot be Abstract and Private")); + } + if (sm.isStatic()) { + exceptions.add(new ValidationException(sm, "Method cannot be Abstract and Static")); + } + if (sm.isSynchronized()) { + exceptions.add(new ValidationException(sm, "Method cannot be Abstract and Synchronized")); + } + } + + if (sc.isInterface()) { + if (sm.isProtected()) { + exceptions.add( + new ValidationException(sm, "Method cannot be an interface and protected")); + } + if (sm.isSynchronized()) { + exceptions.add( + new ValidationException(sm, "Method cannot be an interface and synchronized")); + } + if (sm.isFinal()) { + exceptions.add(new ValidationException(sm, "Method cannot be an interface and final")); + } + if (sm.isNative()) { + exceptions.add(new ValidationException(sm, "Method cannot be an interface and native")); + } + } + + if ((sm.isPrivate() || sm.isProtected()) && (sm.isPublic()) || sm.isProtected()) { + exceptions.add( + new ValidationException(sm, "Method can only be either Public, Protected or Private")); + } + } } @Override diff --git a/sootup.tests/src/test/java/sootup/tests/validator/MethodDeclarationValidatorTest.java b/sootup.tests/src/test/java/sootup/tests/validator/MethodDeclarationValidatorTest.java new file mode 100644 index 00000000000..9cbf5bbf41d --- /dev/null +++ b/sootup.tests/src/test/java/sootup/tests/validator/MethodDeclarationValidatorTest.java @@ -0,0 +1,150 @@ +package sootup.tests.validator; + +import static org.junit.Assert.assertEquals; + +import categories.Java8Test; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import sootup.core.frontend.OverridingBodySource; +import sootup.core.inputlocation.EagerInputLocation; +import sootup.core.jimple.Jimple; +import sootup.core.jimple.basic.LocalGenerator; +import sootup.core.jimple.basic.NoPositionInformation; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.common.stmt.JIdentityStmt; +import sootup.core.jimple.common.stmt.JReturnVoidStmt; +import sootup.core.model.Body; +import sootup.core.model.ClassModifier; +import sootup.core.model.MethodModifier; +import sootup.core.model.SourceType; +import sootup.core.signatures.MethodSignature; +import sootup.core.types.ClassType; +import sootup.core.validation.MethodDeclarationValidator; +import sootup.core.validation.ValidationException; +import sootup.java.core.*; +import sootup.java.core.views.JavaView; + +@Category(Java8Test.class) +public class MethodDeclarationValidatorTest { + JavaView view; + MethodDeclarationValidator methodDeclarationValidator; + + @Before + public void setUp() { + view = new JavaView(Collections.singletonList(new EagerInputLocation())); + methodDeclarationValidator = new MethodDeclarationValidator(); + } + + public JavaSootClass testClassCreatorWithModifiers( + EnumSet classModifierEnumSet, + EnumSet methodModifierEnumSet, + List methodParameters) { + + JavaView view = new JavaView(Collections.singletonList(new EagerInputLocation())); + ClassType type = view.getIdentifierFactory().getClassType("java.lang.String"); + + LocalGenerator generator = new LocalGenerator(new HashSet<>()); + MethodSignature methodSignature = + view.getIdentifierFactory() + .getMethodSignature("dummyMain", "main", "void", methodParameters); + Body.BodyBuilder bodyBuilder = Body.builder(); + + final JIdentityStmt firstStmt = + Jimple.newIdentityStmt( + generator.generateLocal(type), + Jimple.newParameterRef(type, 0), + StmtPositionInfo.getNoStmtPositionInfo()); + final JReturnVoidStmt returnVoidStmt = + new JReturnVoidStmt(StmtPositionInfo.getNoStmtPositionInfo()); + + Body body = + bodyBuilder.setMethodSignature(methodSignature).setLocals(generator.getLocals()).build(); + assertEquals(1, body.getLocalCount()); + + JavaSootMethod dummyMainMethod = + new JavaSootMethod( + new OverridingBodySource(methodSignature, body), + methodSignature, + methodModifierEnumSet, + Collections.emptyList(), + Collections.emptyList(), + NoPositionInformation.getInstance()); + + JavaSootClass mainClass = + new JavaSootClass( + new OverridingJavaClassSource( + new EagerInputLocation(), + null, + view.getIdentifierFactory().getClassType("dummyMain"), + null, + Collections.emptySet(), + null, + Collections.emptySet(), + Collections.singleton(dummyMainMethod), + NoPositionInformation.getInstance(), + classModifierEnumSet, + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList()), + SourceType.Application); + assertEquals(mainClass.getMethods().size(), 1); + + return mainClass; + } + + @Test + public void testClassModifiersValidator_success() { + List validationExceptions_success = new ArrayList<>(); + + JavaSootClass javaSootClass = + testClassCreatorWithModifiers( + EnumSet.of(ClassModifier.PUBLIC, ClassModifier.INTERFACE), + EnumSet.of(MethodModifier.ABSTRACT, MethodModifier.PUBLIC), + Collections.emptyList()); + + methodDeclarationValidator.validate(javaSootClass, validationExceptions_success); + + assertEquals(0, validationExceptions_success.size()); + } + + @Test + public void testClassModifiersValidator_fail1() { + List validationExceptions_fail1 = new ArrayList<>(); + + JavaSootClass javaSootClass = + testClassCreatorWithModifiers( + EnumSet.of(ClassModifier.PUBLIC, ClassModifier.INTERFACE), + EnumSet.of( + MethodModifier.ABSTRACT, + MethodModifier.PRIVATE, + MethodModifier.FINAL, + MethodModifier.SYNCHRONIZED, + MethodModifier.NATIVE, + MethodModifier.STATIC, + MethodModifier.PROTECTED), + Collections.emptyList()); + + methodDeclarationValidator.validate(javaSootClass, validationExceptions_fail1); + + assertEquals(10, validationExceptions_fail1.size()); + } + + @Test + public void testClassModifiersValidator_fail2() { + List validationExceptions_fail2 = new ArrayList<>(); + + JavaSootClass javaSootClass = + testClassCreatorWithModifiers( + EnumSet.of(ClassModifier.PUBLIC), + EnumSet.of(MethodModifier.PUBLIC), + Stream.of("void", "null").collect(Collectors.toList())); + + methodDeclarationValidator.validate(javaSootClass, validationExceptions_fail2); + + assertEquals(2, validationExceptions_fail2.size()); + } +}