From 1fb79eb208e81a558bb0b361160191d3317fea86 Mon Sep 17 00:00:00 2001 From: akshitad11 Date: Mon, 30 Oct 2023 17:40:49 +0100 Subject: [PATCH] Refactored NewValidator.java to work with Sootup. Deleting CheckEscapingValidator.java and ReturnStatementsValidator.java. --- .../binary/NewKeywordValidator.class | Bin 0 -> 463 bytes .../source/NewKeywordValidator.java | 9 + .../java/sootup/core/jimple/basic/Local.java | 11 +- .../src/main/java/sootup/core/model/Body.java | 3 +- .../validation/CheckEscapingValidator.java | 49 ---- .../sootup/core/validation/NewValidator.java | 239 ++++++++++-------- .../validation/ReturnStatementsValidator.java | 63 ----- .../NewKeywordValidatorTest.java | 32 +++ 8 files changed, 190 insertions(+), 216 deletions(-) create mode 100644 shared-test-resources/miniTestSuite/ValidatorTests/binary/NewKeywordValidator.class create mode 100644 shared-test-resources/miniTestSuite/ValidatorTests/source/NewKeywordValidator.java delete mode 100644 sootup.core/src/main/java/sootup/core/validation/CheckEscapingValidator.java delete mode 100644 sootup.core/src/main/java/sootup/core/validation/ReturnStatementsValidator.java create mode 100644 sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ValidatorTests/NewKeywordValidatorTest.java diff --git a/shared-test-resources/miniTestSuite/ValidatorTests/binary/NewKeywordValidator.class b/shared-test-resources/miniTestSuite/ValidatorTests/binary/NewKeywordValidator.class new file mode 100644 index 0000000000000000000000000000000000000000..9531b60cb6c7d97534c810bbaf5686b5ef71fe56 GIT binary patch literal 463 zcmZuuT}uK%6g{JxtJ|iPY4%+&p$7R-Z$>XcAc+Nr5Iv1L$jE-gwr2jUq7n-F0sW}x ztOX+UFn8wOd(OS*-ue1|{{(Q1T?;0bQ-~vLV#VT3Cx^U+RTNCD35Z9Ynn2=2dpbN7 zh!;yO0prZ?DuGO0d#W*T+bX!0ZHE!VmD&@?7jNr*`5-Hf^m>(M7-+9ojfhs$9|RqB zt|M@vp@tV~H1vaROFFtMLq9l-Xf`a&+ejiMko^Z_V%^3DHcf2V*v5{);ldp(9P0$Op7IP8PV2u>g$oz4R7>_d955#ZAFzU~kJ9t6t4T&i^ RO%jk`Q~nt(C!`<4(hnq|S-SuL literal 0 HcmV?d00001 diff --git a/shared-test-resources/miniTestSuite/ValidatorTests/source/NewKeywordValidator.java b/shared-test-resources/miniTestSuite/ValidatorTests/source/NewKeywordValidator.java new file mode 100644 index 00000000000..d3057c84b9a --- /dev/null +++ b/shared-test-resources/miniTestSuite/ValidatorTests/source/NewKeywordValidator.java @@ -0,0 +1,9 @@ +public class NewKeywordValidator { + + int x = 5; + + public static void main(String[] args) { + NewKeywordValidator myObj = new NewKeywordValidator(); + System.out.println(myObj.x); + } +} diff --git a/sootup.core/src/main/java/sootup/core/jimple/basic/Local.java b/sootup.core/src/main/java/sootup/core/jimple/basic/Local.java index 07cf93cf5eb..88a98571301 100644 --- a/sootup.core/src/main/java/sootup/core/jimple/basic/Local.java +++ b/sootup.core/src/main/java/sootup/core/jimple/basic/Local.java @@ -33,6 +33,7 @@ import sootup.core.model.Body; import sootup.core.model.Position; import sootup.core.types.Type; +import sootup.core.types.VoidType; import sootup.core.util.Copyable; import sootup.core.util.printer.StmtPrinter; @@ -55,9 +56,15 @@ public Local(@Nonnull String name, @Nonnull Type type) { } /** Constructs a JimpleLocal of the given name and type. */ - public Local(@Nonnull String name, @Nonnull Type type, @Nonnull Position position) { + public Local(@Nonnull String name, @Nonnull Type type, @Nonnull Position position) { this.name = name; - this.type = type; + if(type instanceof VoidType) + { + throw new RuntimeException("Type should not be VoidType"); + } + else { + this.type = type; + } this.position = position; } diff --git a/sootup.core/src/main/java/sootup/core/model/Body.java b/sootup.core/src/main/java/sootup/core/model/Body.java index 706d8e7e8d8..dfc9dba4d6f 100644 --- a/sootup.core/src/main/java/sootup/core/model/Body.java +++ b/sootup.core/src/main/java/sootup/core/model/Body.java @@ -68,8 +68,7 @@ public class Body implements Copyable { new ValuesValidator(), new CheckInitValidator(), new CheckTypesValidator(), - new CheckVoidLocalesValidator(), - new CheckEscapingValidator()); + new CheckVoidLocalesValidator()); /** * Creates an body which is not associated to any method. diff --git a/sootup.core/src/main/java/sootup/core/validation/CheckEscapingValidator.java b/sootup.core/src/main/java/sootup/core/validation/CheckEscapingValidator.java deleted file mode 100644 index 1973d840c07..00000000000 --- a/sootup.core/src/main/java/sootup/core/validation/CheckEscapingValidator.java +++ /dev/null @@ -1,49 +0,0 @@ -package sootup.core.validation; - -/*- - * #%L - * Soot - a J*va Optimization Framework - * %% - * Copyright (C) 1997-2020 Raja Vallée-Rai, Linghui Luo - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 2.1 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.List; -import sootup.core.model.Body; - -public class CheckEscapingValidator implements BodyValidator { - - @Override - public void validate(Body body, List exception) { - - // TODO: check code from old soot below - - /* - * for (Unit u : body.getUnits()) { if (u instanceof Stmt) { Stmt stmt = (Stmt) u; if (stmt.containsInvokeExpr()) { - * InvokeExpr iexpr = stmt.getInvokeExpr(); SootMethodRef ref = iexpr.getMethodRef(); if (ref.name().contains("'") || - * ref.declaringClass().getName().contains("'")) { throw new ValidationException(stmt, - * "Escaped name in signature found"); } for (Type paramType : ref.parameterTypes()) { if - * (paramType.toString().contains("'")) { throw new ValidationException(stmt, "Escaped name in signature found"); } } } } - * } - */ - } - - @Override - public boolean isBasicValidator() { - return false; - } -} diff --git a/sootup.core/src/main/java/sootup/core/validation/NewValidator.java b/sootup.core/src/main/java/sootup/core/validation/NewValidator.java index 6d7f94904e6..dc9ce7a80e3 100644 --- a/sootup.core/src/main/java/sootup/core/validation/NewValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/NewValidator.java @@ -1,37 +1,19 @@ package sootup.core.validation; -/*- - * #%L - * Soot - a J*va Optimization Framework - * %% - * Copyright (C) 1997-2020 Raja Vallée-Rai, Christian Brüggemann, Markus Schmidt 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 - * published by the Free Software Foundation, either version 2.1 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.List; +import java.util.*; +import sootup.core.graph.StmtGraph; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.expr.JNewExpr; +import sootup.core.jimple.common.expr.JSpecialInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.JInvokeStmt; +import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; +import sootup.core.types.ReferenceType; +import sootup.core.types.UnknownType; -/** - * A relatively simple validator. It tries to check whether after each new-expression-statement - * there is a corresponding call to the <init> method before a use or the end of the method. - * - * @author Marc Miltenberger - * @author Steven Arzt - */ public class NewValidator implements BodyValidator { private static final String errorMsg = @@ -39,78 +21,135 @@ public class NewValidator implements BodyValidator { public static boolean MUST_CALL_CONSTRUCTOR_BEFORE_RETURN = false; - /** Checks whether after each new-instruction a constructor call follows. */ + // Checks whether after each new-instruction a constructor call follows. / @Override public void validate(Body body, List exceptions) { - // TODO: check copied code from old soot - /* - * UnitGraph g = new BriefUnitGraph(body); for (Unit u : body.getUnits()) { if (u instanceof JAssignStmt) { JAssignStmt - * assign = (JAssignStmt) u; - * - * // First seek for a JNewExpr. if (assign.getRightOp() instanceof JNewExpr) { if (!(assign.getLeftOp().getType() - * instanceof RefType)) { exceptions.add(new ValidationException(u, - * "A new-expression must be used on reference type locals", - * String.format("Body of methodRef %s contains a new-expression, which is assigned to a non-reference local", - * body.getMethod().getSignature()))); return; } - * - * // We search for a JSpecialInvokeExpr on the local. LinkedHashSet locals = new LinkedHashSet(); - * locals.add((Local) assign.getLeftOp()); - * - * checkForInitializerOnPath(g, assign, exceptions); } } } - * - * } - * - * - *

Checks whether all pathes from start to the end of the methodRef have a call to the <init> methodRef in - * between.

$r0 = new X;
...
specialinvoke $r0.()>; //validator checks whether this - * statement is missing

Regarding aliasingLocals:
The first local in the set is always the local - * on the LHS of the new-expression-assignment (called: original local; in the example $r0).

- * - * @param g the unit graph of the methodRef - * - * @param exception the list of all collected exceptions - * - * @return true if a call to a <init>-Method has been found on this way. - * - * private boolean checkForInitializerOnPath(UnitGraph g, AssignStmt newStmt, List exception) { - * List workList = new ArrayList(); Set doneSet = new HashSet(); workList.add(newStmt); - * - * Set aliasingLocals = new HashSet(); aliasingLocals.add((Local) newStmt.getLeftOp()); - * - * while (!workList.isEmpty()) { Stmt curStmt = (Stmt) workList.remove(0); if (!doneSet.add(curStmt)) { continue; } if - * (!newStmt.equals(curStmt)) { if (curStmt.containsInvokeExpr()) { InvokeExpr expr = curStmt.getInvokeExpr(); if - * (expr.getMethod().isConstructor()) { if (!(expr instanceof SpecialInvokeExpr)) { exception.add(new - * ValidationException(curStmt, " methodRef calls may only be used with specialinvoke.")); // At least we found an - * initializer, so we return true... return true; } if (!(curStmt instanceof InvokeStmt)) { exception.add(new - * ValidationException(curStmt, " methods may only be called with invoke statements.")); // At least we found an - * initializer, so we return true... return true; } - * - * SpecialInvokeExpr invoke = (SpecialInvokeExpr) expr; if (aliasingLocals.contains(invoke.getBase())) { // We are happy - * now, continue the loop and check other paths continue; } } } - * - * // We are still in the loop, so this was not the constructor call we // were looking for boolean creatingAlias = - * false; if (curStmt instanceof AssignStmt) { AssignStmt assignCheck = (AssignStmt) curStmt; if - * (aliasingLocals.contains(assignCheck.getRightOp())) { if (assignCheck.getLeftOp() instanceof Local) { // A new alias - * is created. aliasingLocals.add((Local) assignCheck.getLeftOp()); creatingAlias = true; } } Local originalLocal = - * aliasingLocals.iterator().next(); if (originalLocal.equals(assignCheck.getLeftOp())) { // In case of dead assignments: - * - * // Handles cases like // $r0 = new x; // $r0 = null; - * - * // But not cases like // $r0 = new x; // $r1 = $r0; // $r1 = null; // Because we check for the original local - * continue; } else { // Since the local on the left hand side gets overwritten // even if it was aliasing with our - * original local, // now it does not any more... aliasingLocals.remove(assignCheck.getLeftOp()); } } - * - * if (!creatingAlias) { for (ValueBox box : curStmt.getUseBoxes()) { Value used = box.getValue(); if - * (aliasingLocals.contains(used)) { // The current unit uses one of the aliasing locals, but // there was no initializer - * in between. // However, when creating such an alias, the use is okay. exception.add(new ValidationException(newStmt, - * String.format(errorMsg, newStmt, curStmt))); return false; } } } } // Enqueue the successors List successors = - * g.getSuccsOf(curStmt); if (successors.isEmpty() && MUST_CALL_CONSTRUCTOR_BEFORE_RETURN) { // This means that we are - * e.g. at the end of the methodRef // There was no call on our way... exception.add(new - * ValidationException(newStmt, String.format(errorMsg, newStmt, curStmt))); return false; } workList.addAll(successors); - * } return true; - * - * - */ + + StmtGraph g = body.getStmtGraph(); + for (Stmt u : body.getStmts()) { + if (u instanceof JAssignStmt) { + JAssignStmt assign = (JAssignStmt) u; + + // First seek for a JNewExpr. + if (assign.getRightOp() instanceof JNewExpr) { + if (!((assign.getLeftOp().getType() instanceof ReferenceType) + || assign.getLeftOp().getType() instanceof UnknownType)) { + exceptions.add( + new ValidationException( + assign.getLeftOp(), + String.format( + "Body of methodRef %s contains a new-expression, which is assigned to a non-reference local", + body.getMethodSignature()))); + return; + } + + // We search for a JSpecialInvokeExpr on the local. + LinkedHashSet locals = new LinkedHashSet(); + locals.add((Local) assign.getLeftOp()); + + checkForInitializerOnPath(g, assign, exceptions); + } + } + } + } + + private boolean checkForInitializerOnPath( + StmtGraph g, JAssignStmt newStmt, List exception) { + List workList = new ArrayList(); + Set doneSet = new HashSet(); + workList.add(newStmt); + + Set aliasingLocals = new HashSet(); + aliasingLocals.add((Local) newStmt.getLeftOp()); + + while (!workList.isEmpty()) { + Stmt curStmt = (Stmt) workList.remove(0); + if (!doneSet.add(curStmt)) { + continue; + } + if (!newStmt.equals(curStmt)) { + if (curStmt.containsInvokeExpr()) { + AbstractInvokeExpr expr = curStmt.getInvokeExpr(); + if (!(expr instanceof JSpecialInvokeExpr)) { + exception.add( + new ValidationException( + curStmt.getInvokeExpr(), + " methodRef calls may only be used with specialinvoke.")); // At least we + // found an + // initializer, + // so we return + // true...return true; + } + if (!(curStmt instanceof JInvokeStmt)) { + exception.add( + new ValidationException( + curStmt.getInvokeExpr(), + " methods may only be called with invoke statements.")); // At least we + // found an + // initializer, + // so we return + // true...return + // true; + } + + JSpecialInvokeExpr invoke = (JSpecialInvokeExpr) expr; + if (aliasingLocals.contains( + invoke.getBase())) { // We are happy now,continue the loop and check other paths + // continue; + } + } + + // We are still in the loop, so this was not the constructor call we // were looking for + boolean creatingAlias = false; + if (curStmt instanceof JAssignStmt) { + JAssignStmt assignCheck = (JAssignStmt) curStmt; + if (aliasingLocals.contains(assignCheck.getRightOp())) { + if (assignCheck.getLeftOp() + instanceof Local) { // A new aliasis created.aliasingLocals.add((Local) + // assignCheck.getLeftOp()); + creatingAlias = true; + } + } + Local originalLocal = aliasingLocals.iterator().next(); + if (originalLocal.equals(assignCheck.getLeftOp())) { // In case of dead assignments: + + // Handles cases like // $r0 = new x; // $r0 = null; + + // But not cases like // $r0 = new x; // $r1 = $r0; // $r1 = null; // Because we check + // for the original local + continue; + } else { // Since the local on the left hand side gets overwritten // even if it was + // aliasing with our original local, // now it does not any more... + // aliasingLocals.remove(assignCheck.getLeftOp()); } } + + if (!creatingAlias) { + for (Value box : curStmt.getUses()) { + if (aliasingLocals.contains( + box)) { // The current unit uses one of the aliasing locals, but // there was no + // initializer in between. // However, when creating such an alias, the + // use is okay. exception.add(new ValidationException(newStmt, + String.format(errorMsg, newStmt, curStmt); + return false; + } + } + } + } + // Enqueue the successors + List successors = g.successors(curStmt); + if (successors.isEmpty() + && MUST_CALL_CONSTRUCTOR_BEFORE_RETURN) { // This means that we are e.g.at the end of + // the methodRef // There was no call + // on our way... + exception.add( + new ValidationException( + newStmt.getLeftOp(), String.format(errorMsg, newStmt, curStmt))); + return false; + } + workList.addAll(successors); + } + } + } + return true; } @Override diff --git a/sootup.core/src/main/java/sootup/core/validation/ReturnStatementsValidator.java b/sootup.core/src/main/java/sootup/core/validation/ReturnStatementsValidator.java deleted file mode 100644 index e76f5922c58..00000000000 --- a/sootup.core/src/main/java/sootup/core/validation/ReturnStatementsValidator.java +++ /dev/null @@ -1,63 +0,0 @@ -package sootup.core.validation; - -/*- - * #%L - * Soot - a J*va Optimization Framework - * %% - * Copyright (C) 1997-2020 Raja Vallée-Rai, Markus Schmidt 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 - * published by the Free Software Foundation, either version 2.1 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.List; -import sootup.core.model.Body; - -public class ReturnStatementsValidator implements BodyValidator { - - /** - * Checks the following invariants on this Jimple body: - * - *
    - *
  1. this-references may only occur in instance methods - *
  2. this-references may only occur as the first statement in a method, if they occur at all - *
  3. param-references must precede all statements that are not themselves param-references or - * this-references, if they occur at all - *
- */ - @Override - public void validate(Body body, List exceptions) { - - // TODO: check copied code from old soot - /* - * // Checks that this Jimple body actually contains a return statement for (Unit u : body.getUnits()) { if ((u - * instanceof JReturnStmt) || (u instanceof JReturnVoidStmt) || (u instanceof JRetStmt) || (u instanceof JThrowStmt)) { - * return; } } - * - * // A methodRef can have an infinite loop // and no return statement: // // public class Infinite { // public static - * void main(String[] args) { // int i = 0; while (true) {i += 1;} } } // // Only check that the execution cannot fall - * off the code. Unit last = body.getUnits().getLast(); if (last instanceof JGotoStmt|| last instanceof JThrowStmt) { - * return; } - * - * exceptions.add(new ValidationException(body.getMethod(), "The methodRef does not contain a return statement", - * "Body of methodRef " + body.getMethod().getSignature() + " does not contain a return statement")); - */ - } - - @Override - public boolean isBasicValidator() { - return true; - } -} diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ValidatorTests/NewKeywordValidatorTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ValidatorTests/NewKeywordValidatorTest.java new file mode 100644 index 00000000000..4ab027d1dbd --- /dev/null +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ValidatorTests/NewKeywordValidatorTest.java @@ -0,0 +1,32 @@ +package sootup.java.bytecode.minimaltestsuite.java6.ValidatorTests; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.Test; +import sootup.core.validation.NewValidator; +import sootup.core.validation.ValidationException; +import sootup.java.bytecode.minimaltestsuite.MinimalBytecodeTestSuiteBase; +import sootup.java.core.JavaSootClass; +import sootup.java.core.JavaSootMethod; + +public class NewKeywordValidatorTest extends MinimalBytecodeTestSuiteBase { + + @Test + public void testNewValidator() { + JavaSootClass sootClass = loadClass(getDeclaredClassSignature()); + + List main = + sootClass.getMethods().stream() + .filter(javaSootMethod -> javaSootMethod.getName().contains("main")) + .collect(Collectors.toList()); + + List validationExceptions = new ArrayList<>(); + + new NewValidator().validate(main.get(0).getBody(), validationExceptions); + + assertEquals(0, validationExceptions.size()); + } +}