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

com.google.test.metric.method.Stack2Turing Maven / Gradle / Ivy

The newest version!
/*
 * 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.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.google.test.metric.JavaType;
import com.google.test.metric.Variable;
import com.google.test.metric.collection.KeyedMultiStack;
import com.google.test.metric.collection.PopClosure;
import com.google.test.metric.collection.KeyedMultiStack.ValueCompactor;
import com.google.test.metric.method.op.stack.JSR;
import com.google.test.metric.method.op.stack.StackOperation;
import com.google.test.metric.method.op.turing.Operation;

public class Stack2Turing {

  public static final Object EMPTY = new Object();

  public static class VariableCompactor extends ValueCompactor {
    @Override
    public List> compact(List> pushValues) {
      if (pushValues.size() < 2) {
        return pushValues;
      }
      ArrayList> compacted = new ArrayList>();
      Set equivalent = new HashSet();
      Iterator> iter;
      for (iter = pushValues.iterator(); iter.hasNext();) {
        List values = iter.next();
        Object key = computeKey(values);
        if (equivalent.add(key)) {
          compacted.add(values);
        }
      }
      return compacted;
    }

    private Object computeKey(List variables) {
      int size = variables.size();
      if (size == 0) {
        return EMPTY;
      } else if (size == 1) {
        return computeKey(variables.get(0));
      } else {
        ArrayList keys = new ArrayList();
        for (Variable variable : variables) {
          keys.add(computeKey(variable));
        }
        return keys;
      }
    }

    private Object computeKey(Variable variable) {
      Object key;
      if (variable instanceof Constant) {
        Constant constant = (Constant) variable;
        key = constant.getType();
      } else {
        key = variable;
      }
      return key;
    }
  }

  private final Block rootBlock;
  private final List operations = new ArrayList();
  private final ValueCompactor pathCompactor = new VariableCompactor();
  public KeyedMultiStack stack = new KeyedMultiStack(
      pathCompactor);

  public Stack2Turing(Block block) {
    this.rootBlock = block;
  }

  public List translate() {
    stack.init(rootBlock);
    translate(rootBlock, new ArrayList());
    return operations;
  }

  private Block translate(Block block, List parentProcessed) {
    List blocks = new ArrayList();
    List processed = parentProcessed;
    blocks.add(block);
    while (!blocks.isEmpty()) {
      block = blocks.remove(0);
      processed.add(block);
      for (StackOperation operation : block.getOperations()) {
        translateStackOperation(block, operation);
        if (operation instanceof JSR) {
          JSR jsr = (JSR) operation;
          Block jsrBlock = jsr.getBlock();
          stack.split(block, asList(jsrBlock));
          Block terminalBlock = translate(jsrBlock, processed);
          stack.join(asList(terminalBlock), block);
          processed.add(jsrBlock);
        }
      }
      List nextBlocks = new ArrayList(block.getNextBlocks());
      nextBlocks.removeAll(processed); // Don't visit already visited
      // blocks
      if (nextBlocks.size() > 0) {
        stack.split(block, nextBlocks);
      }
      blocks.addAll(nextBlocks);
      blocks.removeAll(processed);
    }
    // It appears that when exceptions are involved a method might have
    // paths where stacks are not emptied. So we can't assert this.
    // Verdict is still out.
    // stack.assertEmpty();
    return block;
  }

  private void translateStackOperation(Block block,
      final StackOperation operation) {
    stack.apply(block, new PopClosure() {
      @Override
      public List pop(Block key, List input) {
        List variables = operation.apply(input);
        // For performance reasons the line is commented out.
        // assertValid(variables);
        Operation turingOp = operation.toOperation(input);
        if (turingOp != null) {
          operations.add(turingOp);
        }
        return variables;
      }

      @Override
      public int getSize() {
        return operation.getOperatorCount();
      }
    });
  }

  protected void assertValid(List variables) {
    Iterator iter = variables.iterator();
    while (iter.hasNext()) {
      final Variable variable = iter.next();
      if (JavaType.isDoubleSlot(variable.getType())) {
        Variable varNext = iter.hasNext() ? iter.next() : null;
        if (variable != varNext) {
          throw new IllegalStateException("Variable list '" + variables
              + "' contanins variable '" + variable
              + "' which is a double but the next "
              + "variable in the list is not a duplicate.");
        }
      }
    }
  }

}