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

mockit.coverage.paths.NodeBuilder Maven / Gradle / Ivy

Go to download

JMockit Coverage is a code coverage tool with several metrics (line, path, data) capable of generating HTML reports. It is designed with ease of use in mind, avoiding the need for complex configuration. Instead, smart (but overridable) defaults are employed, such as the selection of which classes to consider for coverage, and where to find sources files for report generation.

There is a newer version: 1.23
Show newest version
/*
 * Copyright (c) 2006-2015 Rogério Liesenfeld
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
 */
package mockit.coverage.paths;

import java.util.*;
import javax.annotation.*;

import mockit.coverage.paths.Node.*;
import mockit.external.asm.*;

public final class NodeBuilder
{
   public int firstLine;
   @Nonnull final List nodes = new ArrayList();

   @Nullable private Entry entryNode;
   @Nullable private SimpleFork currentSimpleFork;
   @Nullable private BasicBlock currentBasicBlock;
   @Nullable private Join currentJoin;
   @Nonnull private final Map> jumpTargetToForks = new LinkedHashMap>();
   @Nonnull private final Map> gotoTargetToSuccessors =
      new LinkedHashMap>();

   private int potentiallyTrivialJump;

   public void handleEntry(int line)
   {
      firstLine = line;
      entryNode = new Entry(line);
      addNewNode(entryNode);
   }

   private int addNewNode(@Nonnull Node newNode)
   {
      int newNodeIndex = nodes.size();

      if (newNodeIndex == 0 && !(newNode instanceof Entry)) {
         return -1;
      }

      nodes.add(newNode);

      if (newNodeIndex > 0) {
         Node precedingNode = nodes.get(newNodeIndex - 1);

         if (precedingNode.line == newNode.line) {
            newNode.setSegmentAccordingToPrecedingNode(precedingNode);
         }
      }

      return newNodeIndex;
   }

   public boolean hasNodes() { return !nodes.isEmpty(); }

   public int handleRegularInstruction(int line, int opcode)
   {
      if (currentSimpleFork == null && currentJoin == null) {
         potentiallyTrivialJump = 0;
         return -1;
      }

      assert currentBasicBlock == null;

      BasicBlock newNode = new BasicBlock(line);
      connectNodes(newNode, opcode);

      return addNewNode(newNode);
   }

   public int handleJump(@Nonnull Label targetBlock, int line, boolean conditional)
   {
      if (conditional) {
         SimpleFork newFork = new SimpleFork(line);
         assert currentSimpleFork == null;
         connectNodes(targetBlock, newFork);
         currentSimpleFork = newFork;
         potentiallyTrivialJump = 1;
         return addNewNode(newFork);
      }
      else if (currentBasicBlock == null && currentJoin == null) {
         Goto newGoto = new Goto(line);
         connectNodes(newGoto);
         setUpMappingFromGotoTargetToCurrentGotoSuccessor(targetBlock, newGoto);
         return addNewNode(newGoto);
      }
      else {
         setUpMappingFromGotoTargetToCurrentGotoSuccessor(targetBlock, null);
         return -1;
      }
   }

   public int handleJumpTarget(@Nonnull Label basicBlock, int line)
   {
      // Ignore for visitLabel calls preceding visitLineNumber:
      if (isNewLineTarget(basicBlock)) {
         return -1;
      }

      Join newNode = new Join(line);
      connectNodes(basicBlock, newNode);

      return addNewNode(newNode);
   }

   private boolean isNewLineTarget(@Nonnull Label basicBlock)
   {
      return !jumpTargetToForks.containsKey(basicBlock) && !gotoTargetToSuccessors.containsKey(basicBlock);
   }

   private void connectNodes(@Nonnull BasicBlock newBasicBlock, int opcode)
   {
      if (currentSimpleFork != null) {
         currentSimpleFork.nextConsecutiveNode = newBasicBlock;
         currentSimpleFork = null;

         if (potentiallyTrivialJump == 1) {
            potentiallyTrivialJump = opcode == Opcodes.ICONST_1 ? 2 : 0;
         }
      }
      else {
         assert currentJoin != null;

         if (potentiallyTrivialJump == 3) {
            if (opcode == Opcodes.ICONST_0) {
               currentJoin.fromTrivialFork = true;
            }

            potentiallyTrivialJump = 0;
         }

         currentJoin.nextNode = newBasicBlock;
         currentJoin = null;
      }

      currentBasicBlock = newBasicBlock;
   }

   private void connectNodes(@Nonnull Label targetBlock, @Nonnull Fork newFork)
   {
      assert entryNode != null;

      if (entryNode.nextNode == null) {
         entryNode.nextNode = newFork;
      }

      setUpMappingFromConditionalTargetToFork(targetBlock, newFork);
      connectNodes(newFork);
   }

   private void setUpMappingFromConditionalTargetToFork(@Nonnull Label targetBlock, @Nonnull Fork newFork)
   {
      List forksWithSameTarget = jumpTargetToForks.get(targetBlock);

      if (forksWithSameTarget == null) {
         forksWithSameTarget = new LinkedList();
         jumpTargetToForks.put(targetBlock, forksWithSameTarget);
      }

      forksWithSameTarget.add(newFork);
   }

   private void setUpMappingFromGotoTargetToCurrentGotoSuccessor(@Nonnull Label targetBlock, @Nullable Goto gotoNode)
   {
      List successors = gotoTargetToSuccessors.get(targetBlock);

      if (successors == null) {
         successors = new LinkedList();
         gotoTargetToSuccessors.put(targetBlock, successors);
      }

      // TODO: they both can be non-null here; what to do?
      if (currentBasicBlock != null) {
         assert currentJoin == null : "Ambiguous situation for " + targetBlock;
         successors.add(currentBasicBlock);
         currentBasicBlock = null;

         if (potentiallyTrivialJump == 2) {
            potentiallyTrivialJump = 3;
         }
      }
      else if (currentJoin != null) {
         successors.add(currentJoin);
         currentJoin = null;
      }
      else {
         successors.add(gotoNode);
      }
   }

   private void connectNodes(@Nonnull Label basicBlock, @Nonnull Join newJoin)
   {
      connectNodes(newJoin);
      connectSourceForksToTargetedJoin(basicBlock, newJoin);
      connectGotoSuccessorsToNewJoin(basicBlock, newJoin);
      currentJoin = newJoin;
   }

   public int handleExit(int exitLine)
   {
      Exit newNode = new Exit(exitLine);
      connectNodes(newNode);

      return addNewNode(newNode);
   }

   private void connectNodes(@Nonnull ConditionalSuccessor newNode)
   {
      if (currentSimpleFork != null) {
         currentSimpleFork.nextConsecutiveNode = newNode;
         currentSimpleFork = null;
         assert currentJoin == null;
         assert currentBasicBlock == null;
      }

      if (currentJoin != null) {
         currentJoin.nextNode = newNode;
         currentJoin = null;
         assert currentBasicBlock == null;
      }

      if (currentBasicBlock != null) {
         currentBasicBlock.nextConsecutiveNode = newNode;
         currentBasicBlock = null;
      }
   }

   private void connectSourceForksToTargetedJoin(@Nonnull Label targetBlock, @Nonnull Join newJoin)
   {
      List forks = jumpTargetToForks.get(targetBlock);

      if (forks != null) {
         for (Fork fork : forks) {
            fork.addNextNode(newJoin);
         }

         jumpTargetToForks.remove(targetBlock);
      }
   }

   private void connectGotoSuccessorsToNewJoin(@Nonnull Label targetBlock, @Nonnull Join newJoin)
   {
      List successors = gotoTargetToSuccessors.get(targetBlock);

      if (successors != null) {
         for (GotoSuccessor successorToGoto : successors) {
            successorToGoto.setNextNodeAfterGoto(newJoin);
         }

         gotoTargetToSuccessors.remove(targetBlock);
      }
   }

   public int handleForwardJumpsToNewTargets(@Nonnull Label defaultBlock, @Nonnull Label[] caseBlocks, int line)
   {
      Fork newJoin = new MultiFork(line);

      for (Label targetBlock : caseBlocks) {
         if (targetBlock != defaultBlock) {
            connectNodes(targetBlock, newJoin);
         }
      }

      connectNodes(defaultBlock, newJoin);

      return addNewNode(newJoin);
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy