Package jadx.core.dex.visitors.regions

Source Code of jadx.core.dex.visitors.regions.ProcessTryCatchRegions

package jadx.core.dex.visitors.regions;

import jadx.core.dex.attributes.AType;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.regions.AbstractRegion;
import jadx.core.dex.regions.Region;
import jadx.core.dex.regions.TryCatchRegion;
import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.RegionUtils;
import jadx.core.utils.exceptions.JadxRuntimeException;

import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Extract blocks to separate try/catch region
*/
public class ProcessTryCatchRegions extends AbstractRegionVisitor {

  private static final Logger LOG = LoggerFactory.getLogger(ProcessTryCatchRegions.class);

  public static void process(MethodNode mth) {
    if (mth.isNoCode() || mth.isNoExceptionHandlers()) {
      return;
    }

    final Map<BlockNode, TryCatchBlock> tryBlocksMap = new HashMap<BlockNode, TryCatchBlock>(2);
    searchTryCatchDominators(mth, tryBlocksMap);

    int k = 0;
    while (!tryBlocksMap.isEmpty()) {
      DepthRegionTraversal.traverseAll(mth, new AbstractRegionVisitor() {
        @Override
        public void leaveRegion(MethodNode mth, IRegion region) {
          checkAndWrap(mth, tryBlocksMap, region);
        }
      });
      if (k++ > 100) {
        throw new JadxRuntimeException("Try/catch wrap count limit reached in " + mth);
      }
    }
  }

  private static void searchTryCatchDominators(MethodNode mth, Map<BlockNode, TryCatchBlock> tryBlocksMap) {
    final Set<TryCatchBlock> tryBlocks = new HashSet<TryCatchBlock>();
    // collect all try/catch blocks
    for (BlockNode block : mth.getBasicBlocks()) {
      CatchAttr c = block.get(AType.CATCH_BLOCK);
      if (c != null) {
        tryBlocks.add(c.getTryBlock());
      }
    }

    // for each try block search nearest dominator block
    for (TryCatchBlock tb : tryBlocks) {
      BitSet bs = null;
      // build bitset with dominators of blocks covered with this try/catch block
      for (BlockNode block : mth.getBasicBlocks()) {
        CatchAttr c = block.get(AType.CATCH_BLOCK);
        if (c != null && c.getTryBlock() == tb) {
          if (bs == null) {
            bs = (BitSet) block.getDoms().clone();
          } else {
            bs.and(block.getDoms());
          }
        }
      }
      if (bs == null) {
        LOG.debug(" Can't build try/catch dominators bitset, tb: {}, mth: {} ", tb, mth);
        continue;
      }

      // intersect to get dominator of dominators
      List<BlockNode> domBlocks = BlockUtils.bitSetToBlocks(mth, bs);
      for (BlockNode block : domBlocks) {
        bs.andNot(block.getDoms());
      }
      domBlocks = BlockUtils.bitSetToBlocks(mth, bs);
      if (domBlocks.size() != 1) {
        throw new JadxRuntimeException(
            "Exception block dominator not found, method:" + mth + ". bs: " + bs);
      }

      BlockNode domBlock = domBlocks.get(0);

      TryCatchBlock prevTB = tryBlocksMap.put(domBlock, tb);
      if (prevTB != null) {
        LOG.info("!!! TODO: merge try blocks in {}", mth);
      }
    }
  }

  private static void checkAndWrap(MethodNode mth, Map<BlockNode, TryCatchBlock> tryBlocksMap, IRegion region) {
    // search dominator blocks in this region (don't need to go deeper)
    for (Map.Entry<BlockNode, TryCatchBlock> entry : tryBlocksMap.entrySet()) {
      BlockNode dominator = entry.getKey();
      if (region.getSubBlocks().contains(dominator)) {
        TryCatchBlock tb = tryBlocksMap.get(dominator);
        if (!wrapBlocks(region, tb, dominator)) {
          ErrorsCounter.methodError(mth, "Can't wrap try/catch for " + region);
        }
        tryBlocksMap.remove(dominator);
        return;
      }
    }
  }

  /**
   * Extract all block dominated by 'dominator' to separate region and mark as try/catch block
   */
  private static boolean wrapBlocks(IRegion replaceRegion, TryCatchBlock tb, BlockNode dominator) {
    IRegion region = replaceRegion;
    if (region instanceof LoopRegion) {
      LoopRegion loop = (LoopRegion) region;
      region = loop.getBody();
    }

    Region tryRegion = new Region(region);
    List<IContainer> subBlocks = region.getSubBlocks();
    for (IContainer cont : subBlocks) {
      if (RegionUtils.isDominatedBy(dominator, cont)) {
        if (isHandlerPath(tb, cont)) {
          break;
        }
        tryRegion.getSubBlocks().add(cont);
      }
    }
    if (tryRegion.getSubBlocks().isEmpty()) {
      return false;
    }

    TryCatchRegion tryCatchRegion = new TryCatchRegion(region, tryRegion);
    tryRegion.setParent(tryCatchRegion);
    tryCatchRegion.setTryCatchBlock(tb.getCatchAttr().getTryBlock());

    // replace first node by region
    IContainer firstNode = tryRegion.getSubBlocks().get(0);
    if (!region.replaceSubBlock(firstNode, tryCatchRegion)) {
      return false;
    }
    subBlocks.removeAll(tryRegion.getSubBlocks());

    // fix parents for tryRegion sub blocks
    for (IContainer cont : tryRegion.getSubBlocks()) {
      if (cont instanceof AbstractRegion) {
        AbstractRegion aReg = (AbstractRegion) cont;
        aReg.setParent(tryRegion);
      }
    }
    return true;
  }

  private static boolean isHandlerPath(TryCatchBlock tb, IContainer cont) {
    for (ExceptionHandler h : tb.getHandlers()) {
      if (RegionUtils.hasPathThruBlock(h.getHandlerBlock(), cont)) {
        return true;
      }
    }
    return false;
  }
}
TOP

Related Classes of jadx.core.dex.visitors.regions.ProcessTryCatchRegions

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.