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

com.strobel.decompiler.ast.LoopsAndConditions Maven / Gradle / Ivy

There is a newer version: 2.5.0.Final
Show newest version
/*
 * LoopsAndConditions.java
 *
 * Copyright (c) 2013 Mike Strobel
 *
 * This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain;
 * and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa.
 *
 * This source code is subject to terms and conditions of the Apache License, Version 2.0.
 * A copy of the license can be found in the License.html file at the root of this distribution.
 * By using this source code in any fashion, you are agreeing to be bound by the terms of the
 * Apache License, Version 2.0.
 *
 * You must not remove this notice, or any other, from this software.
 */

package com.strobel.decompiler.ast;

import com.strobel.annotations.NotNull;
import com.strobel.assembler.flowanalysis.ControlFlowEdge;
import com.strobel.assembler.flowanalysis.ControlFlowGraph;
import com.strobel.assembler.flowanalysis.ControlFlowNode;
import com.strobel.assembler.flowanalysis.ControlFlowNodeType;
import com.strobel.assembler.flowanalysis.JumpType;
import com.strobel.assembler.metadata.SwitchInfo;
import com.strobel.core.ArrayUtilities;
import com.strobel.core.Pair;
import com.strobel.core.Predicate;
import com.strobel.core.StrongBox;
import com.strobel.decompiler.DecompilerContext;

import java.util.*;

import static com.strobel.core.CollectionUtilities.*;
import static com.strobel.decompiler.ast.AstOptimizer.*;
import static com.strobel.decompiler.ast.PatternMatching.*;

final class LoopsAndConditions {
    private final Map labelsToNodes = new IdentityHashMap<>();
    @SuppressWarnings({ "FieldCanBeLocal", "UnusedDeclaration" })
    private final DecompilerContext context;

    private int _nextLabelIndex;

    LoopsAndConditions(final DecompilerContext context) {
        this.context = context;
    }

    public final void findConditions(final Block block) {
        final List body = block.getBody();

        if (body.isEmpty() || block.getEntryGoto() == null) {
            return;
        }

        final ControlFlowGraph graph = buildGraph(body, (Label) block.getEntryGoto().getOperand());

        graph.computeDominance();
        graph.computeDominanceFrontier();

        final Set cfNodes = new LinkedHashSet<>();
        final List graphNodes = graph.getNodes();

        for (int i = 3; i < graphNodes.size(); i++) {
            cfNodes.add(graphNodes.get(i));
        }

        final List newBody = findConditions(cfNodes, graph.getEntryPoint());

        block.getBody().clear();
        block.getBody().addAll(newBody);
    }

    public final void findLoops(final Block block) {
        final List body = block.getBody();

        if (body.isEmpty() || block.getEntryGoto() == null) {
            return;
        }

        final ControlFlowGraph graph = buildGraph(body, (Label) block.getEntryGoto().getOperand());

        graph.computeDominance();
        graph.computeDominanceFrontier();

        final Set cfNodes = new LinkedHashSet<>();
        final List graphNodes = graph.getNodes();

        for (int i = 3; i < graphNodes.size(); i++) {
            cfNodes.add(graphNodes.get(i));
        }

        final List newBody = findLoops(cfNodes, graph.getEntryPoint(), false);

        block.getBody().clear();
        block.getBody().addAll(newBody);
    }

    private ControlFlowGraph buildGraph(final List nodes, final Label entryLabel) {
        int index = 0;

        final List cfNodes = new ArrayList<>();

        final ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint);
        final ControlFlowNode regularExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.RegularExit);
        final ControlFlowNode exceptionalExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.ExceptionalExit);

        cfNodes.add(entryPoint);
        cfNodes.add(regularExit);
        cfNodes.add(exceptionalExit);

        //
        // Create graph nodes.
        //

        labelsToNodes.clear();

        final Map astNodesToControlFlowNodes = new IdentityHashMap<>();

        for (final Node node : nodes) {
            final ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal);

            cfNodes.add(cfNode);
            astNodesToControlFlowNodes.put(node, cfNode);
            cfNode.setUserData(node);

            //
            // Find all contained labels.
            //
            for (final Label label : node.getSelfAndChildrenRecursive(Label.class)) {
                labelsToNodes.put(label, cfNode);
            }
        }

        final ControlFlowNode entryNode = labelsToNodes.get(entryLabel);
        final ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal);

        entryPoint.getOutgoing().add(entryEdge);
        entryNode.getIncoming().add(entryEdge);

        //
        // Create edges.
        //

        for (final Node node : nodes) {
            final ControlFlowNode source = astNodesToControlFlowNodes.get(node);

            //
            // Find all branches.
            //

            for (final Expression e : node.getSelfAndChildrenRecursive(Expression.class)) {
                if (!e.isBranch()) {
                    continue;
                }

                for (final Label target : e.getBranchTargets()) {
                    final ControlFlowNode destination = labelsToNodes.get(target);

                    if (destination != null &&
                        (destination != source || canBeSelfContainedLoop((BasicBlock) node, e, target))) {

                        final ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal);

                        if (!source.getOutgoing().contains(edge)) {
                            source.getOutgoing().add(edge);
                        }

                        if (!destination.getIncoming().contains(edge)) {
                            destination.getIncoming().add(edge);
                        }
                    }
                }
            }
        }

        return new ControlFlowGraph(cfNodes.toArray(new ControlFlowNode[cfNodes.size()]));
    }

    private boolean canBeSelfContainedLoop(final BasicBlock node, final Expression branch, final Label target) {
        final List nodeBody = node.getBody();

        if (target == null || nodeBody.isEmpty()) {
            return false;
        }

        if (target == nodeBody.get(0)) {
            return true;
        }

        final Node secondNode = getOrDefault(nodeBody, 1);

        if (secondNode instanceof TryCatchBlock) {
            final Node next = getOrDefault(nodeBody, 2);

            if (next != branch) {
                return false;
            }

            final TryCatchBlock tryCatch = (TryCatchBlock) secondNode;
            final Block tryBlock = tryCatch.getTryBlock();

            final Predicate labelMatch = new Predicate() {
                @Override
                public boolean test(final Expression e) {
                    return e != tryBlock.getEntryGoto() && e.getBranchTargets().contains(target);
                }
            };

/*
            if (tryBlock != null) {
                final Node firstInTryBody = firstOrDefault(tryBlock.getBody());

                if (!(firstInTryBody instanceof BasicBlock &&
                      target == firstOrDefault(((BasicBlock) firstInTryBody).getBody()))) {

                    return false;
                }

                final boolean branchInTry = any(tryBlock.getSelfAndChildrenRecursive(Expression.class), labelMatch);

                if (branchInTry) {
                    return false;
                }
            }
*/

            for (final CatchBlock catchBlock : tryCatch.getCatchBlocks()) {
                if (any(catchBlock.getSelfAndChildrenRecursive(Expression.class), labelMatch)) {
                    return true;
                }
            }

            if (tryCatch.getFinallyBlock() != null &&
                any(tryCatch.getFinallyBlock().getSelfAndChildrenRecursive(Expression.class), labelMatch)) {

                return true;
            }

            return true;
        }

        return false;
    }

    @SuppressWarnings("ConstantConditions")
    private List findLoops(final Set scopeNodes, final ControlFlowNode entryPoint, final boolean excludeEntryPoint) {
        final List result = new ArrayList<>();
        final StrongBox switchLabels = new StrongBox<>();
        final Set scope = new LinkedHashSet<>(scopeNodes);
        final ArrayDeque agenda = new ArrayDeque<>();

        agenda.addLast(entryPoint);

        while (!agenda.isEmpty()) {
            final ControlFlowNode node = agenda.pollFirst();

            //
            // If the node is a loop header...
            //
            if (scope.contains(node) &&
                node.getDominanceFrontier().contains(node) &&
                (node != entryPoint || !excludeEntryPoint)) {

                final Set loopContents = findLoopContents(scope, node);

                //
                // If the first or last expression is a loop condition...
                //
                final BasicBlock basicBlock = (BasicBlock) node.getUserData();
                final StrongBox condition = new StrongBox<>();
                final StrongBox




© 2015 - 2024 Weber Informatics LLC | Privacy Policy