Package com.google.test.metric.method

Source Code of com.google.test.metric.method.BlockDecomposer$Frame

/*
* Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.test.metric.method;

import static java.util.Arrays.asList;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.Label;

import com.google.test.metric.Type;
import com.google.test.metric.method.op.stack.JSR;
import com.google.test.metric.method.op.stack.Return;
import com.google.test.metric.method.op.stack.StackOperation;
import com.google.test.metric.method.op.stack.Throw;
import com.google.test.metric.method.op.turing.Operation;

/**
* @author misko@google.com <Misko Hevery>
*/
public class BlockDecomposer {

  /**
   * Method is broken down into one frame per bytecode in order to break it down
   * into blocks
   */
  private static class Frame {

    /**
     * Next frame (ie next bytecode)
     */
    private Frame next;
    /**
     * Operation in this frame
     */
    private final StackOperation operation;
    /**
     * Any label associated with this bytecode
     */
    private Label label;
    /**
     * Block which this bytecode ended up assigned to
     */
    public Block block;
    /**
     * True if this is first bytecode of a block (GOTO). If true then new block
     * will start here.
     */
    public boolean blockStartsHere;
    /**
     * If true then this will be last instruction in block and new block will
     * start on next frame.
     */
    public boolean blockEndsHere;
    /**
     * If not null current block will link with block that this field points to.
     */
    public List<Label> gotoLabels = new LinkedList<Label>();
    /**
     * This instruction ends execution of block (ie return, throw) If this is
     * true then this and subsequent blocks will not be linked
     */
    public boolean terminal;

    public Frame(Frame frame, StackOperation operation, Label label) {
      this.operation = operation;
      this.label = label;
      if (frame != null) {
        frame.addFrame(this);
      }
    }

    private void addFrame(Frame frame) {
      next = frame;
    }

    @Override
    public String toString() {
      ByteArrayOutputStream buf = new ByteArrayOutputStream();
      PrintStream out = new PrintStream(buf, true);
      out.printf("%-10s %-70s %-15s s=%-5b e=%-5b t=%-5b goto=%s", label, operation,
          block == null ? null : block.getId(), blockStartsHere, blockEndsHere,
          terminal, gotoLabels);
      return buf.toString();
    }

  }

  private final Map<Label, Frame> frames = new HashMap<Label, Frame>();
  private final Map<Label, Block> blocks = new HashMap<Label, Block>();
  private final List<Runnable> extraLinkSteps = new LinkedList<Runnable>();
  private Frame firstFrame;
  private Frame lastFrame;
  private Label lastLabel;
  private Block mainBlock;
  private int counter = 0;

  public void addOp(StackOperation operation) {
    lastFrame = new Frame(lastFrame, operation, lastLabel);
    if (operation instanceof Return || operation instanceof Throw) {
      lastFrame.terminal = true;
      lastFrame.blockEndsHere = true;
    }
    if (firstFrame == null) {
      firstFrame = lastFrame;
    }
    applyLastLabel();
  }

  private void applyLastLabel() {
    if (lastLabel != null) {
      frames.put(lastLabel, lastFrame);
      lastFrame.label = lastLabel;
      lastLabel = null;
    }
  }

  public void label(Label label) {
    if (lastLabel == null) {
      lastLabel = label;
    } else {
      throw new IllegalStateException("Multiple lables per line are not allowed.");
    }
  }

  public void unconditionalGoto(Label label) {
    lastFrame.blockEndsHere = true;
    lastFrame.gotoLabels.add(label);
    lastFrame.terminal = true;
    applyLastLabel();
  }

  public void conditionalGoto(Label label) {
    lastFrame.blockEndsHere = true;
    lastFrame.gotoLabels.add(label);
    applyLastLabel();
  }

  public void jumpSubroutine(Label label, int lineNumber) {
    Block subBlock = blocks.get(label);
    if (subBlock == null) {
      subBlock = new Block("sub_" + (counter++));
    }
    addOp(new JSR(lineNumber, subBlock));
    blocks.put(label, subBlock);
    applyLastLabel();
  }

  public void tryCatchBlock(Label start, final Label end, final Label handler,
      final String eType) {
    extraLinkSteps.add(new Runnable() {
      public void run() {
        Block endBlock = frames.get(end).block;
        Block handlerBlock = frames.get(handler).block;
        Type type = eType == null ? Type.fromClass(Throwable.class) : Type.fromJava(eType);
        handlerBlock.setExceptionHandler(-1, new Constant("?", type));
        endBlock.addNextBlock(handlerBlock);
      }
    });
  }

  public void tableSwitch(final Label dflt, final Label... labels) {
    lastFrame.terminal = true;
    lastFrame.blockEndsHere = true;
    lastFrame.gotoLabels.addAll(asList(labels));
    lastFrame.gotoLabels.add(dflt);
    applyLastLabel();
  }

  public void done() {
    if (firstFrame != null) {
      breakIntoBlocks();
      copyToBlocks();
      linkBlocks();
      for (Runnable extraLinkStep : extraLinkSteps) {
        extraLinkStep.run();
      }
      mainBlock = firstFrame.block;
    }
  }

  private void breakIntoBlocks() {
    Frame frame = firstFrame;
    while (frame != null) {
      for (Label label : frame.gotoLabels) {
        frames.get(label).blockStartsHere = true;
        frame.blockEndsHere = true;
      }
      if (blocks.containsKey(frame.label)) {
        frame.blockStartsHere = true;
      }
      frame = frame.next;
    }
  }

  private void copyToBlocks() {
    Frame frame = firstFrame;
    Block block = null;
    while (frame != null) {
      if (frame.blockStartsHere) {
        block = null;
      }
      Label frameLabel = frame.label;
      if (blocks.containsKey(frameLabel)) {
        block = blocks.get(frameLabel);
      }
      if (block == null) {
        block = new Block("block_" + (counter++));
      }
      frame.block = block;
      frame.block.addOp(frame.operation);
      if (frame.blockEndsHere) {
        block = null;
      }
      frame = frame.next;
    }
  }

  private void linkBlocks() {
    Frame previousFrame = firstFrame;
    Frame thisFrame = previousFrame.next;
    while (thisFrame != null) {
      Block previousBlock = previousFrame.block;
      Block thisBlock = thisFrame.block;
      if (previousBlock != thisBlock && !previousFrame.terminal) {
        previousBlock.addNextBlock(thisFrame.block);
      }
      for (Label label : previousFrame.gotoLabels) {
        previousBlock.addNextBlock(frames.get(label).block);
      }
      previousFrame = thisFrame;
      thisFrame = thisFrame.next;
    }
  }

  public List<Operation> getOperations() {
    if (mainBlock == null) {
      return Collections.emptyList();
    } else {
      return new Stack2Turing(mainBlock).translate();
    }
  }

  public Block getBlock(Label label) {
    return frames.get(label).block;
  }

  public Block getMainBlock() {
    return mainBlock;
  }

  @Override
  public String toString() {
    StringBuilder buf = new StringBuilder();
    Frame frame = firstFrame;
    while (frame != null) {
      buf.append(frame);
      buf.append("\n");
      frame = frame.next;
    }
    if (mainBlock != null) {
      frame = firstFrame;
      List<Block> processed = new LinkedList<Block>();
      while (frame!= null) {
        Block block = frame.block;
        if (!processed.contains(block)) {
          buf.append(block);
          processed.add(block);
        }
        frame = frame.next;
      }
    }
    return buf.toString();
  }

}
TOP

Related Classes of com.google.test.metric.method.BlockDecomposer$Frame

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.