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

sootup.java.bytecode.interceptors.DeadAssignmentEliminator Maven / Gradle / Ivy

There is a newer version: 1.3.0
Show newest version
package sootup.java.bytecode.interceptors;
/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 1997-2020 Raja Vallée-Rai, Christian Brüggemann
 * %%
 * 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.*;
import javax.annotation.Nonnull;
import sootup.core.graph.MutableStmtGraph;
import sootup.core.jimple.Jimple;
import sootup.core.jimple.basic.LValue;
import sootup.core.jimple.basic.Local;
import sootup.core.jimple.basic.Value;
import sootup.core.jimple.common.constant.IntConstant;
import sootup.core.jimple.common.constant.NullConstant;
import sootup.core.jimple.common.expr.*;
import sootup.core.jimple.common.ref.JArrayRef;
import sootup.core.jimple.common.ref.JFieldRef;
import sootup.core.jimple.common.ref.JInstanceFieldRef;
import sootup.core.jimple.common.stmt.JAssignStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.Body;
import sootup.core.model.MethodModifier;
import sootup.core.transform.BodyInterceptor;
import sootup.core.types.*;
import sootup.core.views.View;

/**
 * This interceptor eliminates assignment statements to locals whose values are not subsequently
 * used, unless evaluating the right-hand side of the assignment may cause side-effects. Complexity
 * is linear with respect to the statements.
 *
 * @author Marcus Nachtigall
 */
public class DeadAssignmentEliminator implements BodyInterceptor {
  boolean eliminateOnlyStackLocals;

  public DeadAssignmentEliminator() {
    this(false);
  }

  public DeadAssignmentEliminator(boolean eliminateOnlyStackLocals) {
    this.eliminateOnlyStackLocals = eliminateOnlyStackLocals;
  }

  @Override
  public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) {
    MutableStmtGraph stmtGraph = builder.getStmtGraph();
    List stmts = builder.getStmts();
    Deque deque = new ArrayDeque<>(stmts.size());

    // Make a first pass through the statements, noting the statements we must absolutely keep
    boolean isStatic = MethodModifier.isStatic(builder.getModifiers());
    boolean allEssential = true;
    boolean containsInvoke = false;
    Local thisLocal = null;

    for (Iterator iterator = stmtGraph.getNodes().iterator(); iterator.hasNext(); ) {
      Stmt stmt = iterator.next();
      boolean isEssential = true;

      if (stmt instanceof JAssignStmt) {
        JAssignStmt assignStmt = (JAssignStmt) stmt;
        Value lhs = assignStmt.getLeftOp();
        Value rhs = assignStmt.getRightOp();

        // Stmt is of the form a = a which is useless
        if (lhs == rhs && lhs instanceof Local) {
          iterator.remove();
          continue;
        }

        if (lhs instanceof Local
            && (!eliminateOnlyStackLocals
                || ((Local) lhs).getName().startsWith("$")
                || lhs.getType() instanceof NullType)) {
          isEssential = false;

          if (!containsInvoke) {
            // performance optimization: to not repeat containsInvokeExpr()
            containsInvoke = assignStmt.containsInvokeExpr();
          }

          if (rhs instanceof JCastExpr) {
            // CastExpr: can trigger ClassCastException, but null-casts never fail
            JCastExpr castExpr = (JCastExpr) rhs;
            Type type = castExpr.getType();
            Value value = castExpr.getOp();
            isEssential = !(value instanceof NullConstant) && type instanceof ReferenceType;
          } else if (rhs instanceof AbstractInvokeExpr
              || rhs instanceof JArrayRef
              || rhs instanceof JNewExpr
              || rhs instanceof JNewArrayExpr
              || rhs instanceof JNewMultiArrayExpr) {
            // InvokeExpr: can have side effects (like throwing a null pointer exception)
            // JArrayRef: can have side effects (like throwing a null pointer exception)
            // JNewExpr: can trigger class initialization
            // JNewArrayExpr: can throw exception
            // JNewMultiArrayExpr: can throw exception
            isEssential = true;
          } else if (rhs instanceof JFieldRef) {
            // can trigger class initialization
            isEssential = true;

            if (rhs instanceof JInstanceFieldRef) {
              JInstanceFieldRef instanceFieldRef = (JInstanceFieldRef) rhs;
              if (!isStatic && thisLocal == null) {
                thisLocal = Body.getThisLocal(stmtGraph);
              }

              // Any JInstanceFieldRef may have side effects, unless the base is reading from 'this'
              // in a non-static method
              isEssential = (isStatic || thisLocal != instanceFieldRef.getBase());
            }
          } else if (rhs instanceof JDivExpr || rhs instanceof JRemExpr) {
            AbstractBinopExpr expr = (AbstractBinopExpr) rhs;
            Type type1 = expr.getOp1().getType();
            Type type2 = expr.getOp2().getType();

            // Can trigger a division by zero
            boolean type2Int =
                type2 instanceof PrimitiveType && type2.equals(PrimitiveType.getInt());
            isEssential =
                type2Int
                    || type1 instanceof PrimitiveType
                        && (type1.equals(PrimitiveType.getInt())
                            || type1.equals(PrimitiveType.getLong()))
                    || type2 instanceof PrimitiveType && type2.equals(PrimitiveType.getLong())
                    || type1 instanceof UnknownType
                    || type2 instanceof UnknownType;

            if (isEssential && type2Int) {
              Value value = expr.getOp2();
              if (value instanceof IntConstant) {
                IntConstant intConstant = (IntConstant) value;
                isEssential = (intConstant.getValue() == 0);
              } else {
                // [ms] oh the irony..
                isEssential = true; // could be 0, we don't know
              }
            }
          }
        }
      }

      if (isEssential) {
        deque.addFirst(stmt);
      }

      allEssential &= isEssential;
    }

    if (!containsInvoke && allEssential) {
      return;
    }

    // Add all the statements which are used to compute values for the essential statements,
    // recursively
    Map> allDefs = Body.collectDefs(stmtGraph.getNodes());

    Set essentialStmts = new HashSet<>(stmts.size());
    while (!deque.isEmpty()) {
      Stmt stmt = deque.removeFirst();
      if (essentialStmts.add(stmt)) {
        for (Value value : stmt.getUses()) {
          if (value instanceof Local) {
            Local local = (Local) value;
            Collection defs = allDefs.get(local);
            if (defs != null) {
              deque.addAll(defs);
            }
          }
        }
      }
    }

    // Remove the dead statements from the stmtGraph
    for (Stmt stmt : stmts) {
      if (!essentialStmts.contains(stmt)) {
        stmtGraph.removeNode(stmt);
      }
    }

    if (!containsInvoke) {
      return;
    }

    Map> essentialUses = Body.collectUses(essentialStmts);
    // Eliminate dead assignments from invokes such as x = f(), where x is no longer used
    List postProcess = new ArrayList<>();
    for (Stmt stmt : stmts) {
      if (stmt instanceof JAssignStmt) {
        JAssignStmt assignStmt = (JAssignStmt) stmt;
        if (assignStmt.containsInvokeExpr()) {
          // find at least one use of Value which is in an essential stmt
          boolean deadAssignment = true;

          List values = assignStmt.getUsesAndDefs();
          for (Value value : values) {
            if (!(value instanceof LValue)) {
              continue;
            }
            final Collection stmtsWithValuesUse = essentialUses.get(value);
            if (stmtsWithValuesUse != null) {
              deadAssignment = false;
              break;
            }
          }
          if (deadAssignment) {
            postProcess.add(assignStmt);
          }
        }
      }
    }

    // change JAssignStmt+InvokeExpr where the lhs is not used/essential to an JInvokeStmt
    for (JAssignStmt assignStmt : postProcess) {
      // Transform it into a simple invoke
      Stmt newInvoke =
          Jimple.newInvokeStmt(assignStmt.getInvokeExpr(), assignStmt.getPositionInfo());
      stmtGraph.replaceNode(assignStmt, newInvoke);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy