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

sootup.java.bytecode.interceptors.Aggregator 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.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import sootup.core.graph.MutableStmtGraph;
import sootup.core.jimple.basic.Local;
import sootup.core.jimple.basic.Value;
import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr;
import sootup.core.jimple.common.ref.JArrayRef;
import sootup.core.jimple.common.ref.JFieldRef;
import sootup.core.jimple.common.stmt.AbstractDefinitionStmt;
import sootup.core.jimple.common.stmt.JAssignStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.jimple.visitor.ReplaceUseStmtVisitor;
import sootup.core.model.Body;
import sootup.core.transform.BodyInterceptor;
import sootup.core.views.View;

/*
 * The Jimple Local Aggregator removes some unnecessary copies by combining local variables.
 * Essentially, it finds definitions which have only a single use and, if it is safe to do so,
 * removes the original definition after replacing the use with the definition's right-hand side.
 * At this stage in JimpleBody construction, local aggregation serves largely to remove the copies to and
 * from stack variables which simulate load and store instructions in the original bytecode.
 * */
public class Aggregator implements BodyInterceptor {

  // if this is true, only aggregate variables starting with "$" which are the ones which are *not*
  // referring to a field of a class
  protected boolean dontAggregateFieldLocals;

  public Aggregator() {
    this(false);
  }

  public Aggregator(boolean dontAggregateFieldLocals) {
    this.dontAggregateFieldLocals = dontAggregateFieldLocals;
  }

  /**
   * Traverse the statements in the given body, looking for aggregation possibilities; that is,
   * given a def d and a use u, d has no other uses, u has no other defs, collapse d and u.
   */
  @Override
  public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) {
    MutableStmtGraph graph = builder.getStmtGraph();
    List stmts = builder.getStmts();
    Map> usesMap = Body.collectUses(stmts);

    for (Stmt stmt : stmts) {
      if (!(stmt instanceof JAssignStmt)) {
        continue;
      }
      final JAssignStmt assignStmt = (JAssignStmt) stmt;
      Value lhs = assignStmt.getLeftOp();
      if (!(lhs instanceof Local)) {
        continue;
      }
      Local lhsLocal = (Local) lhs;
      if (dontAggregateFieldLocals && !lhsLocal.getName().startsWith("$")) {
        continue;
      }
      for (Value val : assignStmt.getUses()) {
        if (!(val instanceof Local)) {
          continue;
        }
        final Collection usesOfVal = usesMap.get(val);
        if (usesOfVal.size() > 1) {
          // there are other uses, so it can't be aggregated
          continue;
        }
        List defs = ((Local) val).getDefs(stmts);
        if (defs.size() != 1) {
          continue;
        }
        Stmt relevantDef = defs.get(0);
        if (!graph.containsNode(relevantDef) || !graph.containsNode(stmt)) {
          continue;
        }
        List path = graph.getExtendedBasicBlockPathBetween(relevantDef, stmt);
        if (path == null) {
          continue;
        }
        boolean cantAggr = false;
        boolean propagatingInvokeExpr = false;
        boolean propagatingFieldRef = false;
        boolean propagatingArrayRef = false;
        List fieldRefList = new ArrayList<>();

        Set localsUsed = new HashSet<>();
        for (Stmt pathStmt : path) {
          for (Value use : pathStmt.getUses()) {
            if (use instanceof Local) {
              localsUsed.add(use);
            } else if (use instanceof AbstractInstanceInvokeExpr) {
              propagatingInvokeExpr = true;
            } else if (use instanceof JArrayRef) {
              propagatingArrayRef = true;
            } else if (use instanceof JFieldRef) {
              propagatingFieldRef = true;
              fieldRefList.add((JFieldRef) use);
            }
          }
        }

        for (Stmt pathStmt : path) {
          if (pathStmt != stmt && pathStmt != relevantDef) {
            for (Value stmtDef : pathStmt.getDefs()) {
              if (localsUsed.contains(stmtDef)) {
                cantAggr = true;
                break;
              }
              if (propagatingInvokeExpr || propagatingFieldRef || propagatingArrayRef) {
                if (stmtDef instanceof JFieldRef) {
                  if (propagatingInvokeExpr) {
                    cantAggr = true;
                    break;
                  } else if (propagatingFieldRef) {
                    // Can't aggregate a field access if passing a definition of a field
                    // with the same name, because they might be aliased
                    for (JFieldRef fieldRef : fieldRefList) {
                      if (fieldRef.equals((JFieldRef) stmtDef)) {
                        cantAggr = true;
                        break;
                      }
                    }
                  }
                } else if (stmtDef instanceof JArrayRef) {
                  if (propagatingInvokeExpr || propagatingArrayRef) {
                    // Cannot aggregate an invoke expr past an array write and cannot
                    // aggregate an array read past a write
                    cantAggr = true;
                    break;
                  }
                }
              }
            }
          }
          // Check for intervening side effects due to method calls
          if (propagatingInvokeExpr || propagatingFieldRef || propagatingArrayRef) {
            for (final Value value : stmt.getUses()) {
              if (pathStmt == stmt && value == lhs) {
                break;
              }
              if (value instanceof AbstractInstanceInvokeExpr
                  || (propagatingInvokeExpr
                      && (value instanceof JFieldRef || value instanceof JArrayRef))) {
                cantAggr = true;
                break;
              }
            }
          }
        }

        if (cantAggr) {
          continue;
        }

        // can only aggregate JAssignStmts
        if (!(relevantDef instanceof JAssignStmt)) {
          continue;
        }

        Value aggregatee = ((AbstractDefinitionStmt) relevantDef).getRightOp();
        Stmt newStmt;

        final ReplaceUseStmtVisitor replaceVisitor = new ReplaceUseStmtVisitor(val, aggregatee);
        // FIXME: this try-catch is an awful hack for "ValueBox.canContainValue" -> try to determine
        // a replaceability earlier!
        try {
          replaceVisitor.caseAssignStmt(assignStmt);
          newStmt = replaceVisitor.getResult();
        } catch (ClassCastException iae) {
          newStmt = null;
        }

        if (newStmt != null) {
          graph.replaceNode(stmt, newStmt);
          if (graph.getStartingStmt() == relevantDef) {
            Stmt newStartingStmt = builder.getStmtGraph().successors(relevantDef).get(0);
            graph.setStartingStmt(newStartingStmt);
          }
          graph.removeNode(relevantDef);
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy