All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.jimple.validation.NewValidator Maven / Gradle / Ivy

package soot.jimple.validation;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 1997 - 1999 Raja Vallee-Rai
 * %%
 * 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.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import soot.Body;
import soot.Local;
import soot.RefType;
import soot.Unit;
import soot.Value;
import soot.ValueBox;
import soot.jimple.AssignStmt;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.NewExpr;
import soot.jimple.SpecialInvokeExpr;
import soot.jimple.Stmt;
import soot.toolkits.graph.BriefUnitGraph;
import soot.toolkits.graph.UnitGraph;
import soot.validation.BodyValidator;
import soot.validation.ValidationException;

/**
 * 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 enum NewValidator implements BodyValidator {
  INSTANCE;

  private static final String errorMsg
      = "There is a path from '%s' to the usage '%s' where  does not get called in between.";

  public static boolean MUST_CALL_CONSTRUCTOR_BEFORE_RETURN = false;

  public static NewValidator v() {
    return INSTANCE;
  }

  /**
   * Checks whether after each new-instruction a constructor call follows.
   */
  @Override
  public void validate(Body body, List exceptions) {
    UnitGraph g = new BriefUnitGraph(body);
    for (Unit u : body.getUnits()) {
      if (u instanceof AssignStmt) {
        AssignStmt assign = (AssignStmt) u;

        // First seek for a JNewExpr.
        if (assign.getRightOp() instanceof NewExpr) {
          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 method %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 method have a call to the <init> method 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 method * @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, " method 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 method // There was no call on our way... exception.add(new ValidationException(newStmt, String.format(errorMsg, newStmt, curStmt))); return false; } workList.addAll(successors); } return true; } @Override public boolean isBasicValidator() { return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy