OperandStack.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.verifier.structurals;

  18. import java.util.ArrayList;

  19. import org.apache.bcel.generic.ObjectType;
  20. import org.apache.bcel.generic.ReferenceType;
  21. import org.apache.bcel.generic.Type;
  22. import org.apache.bcel.verifier.exc.AssertionViolatedException;
  23. import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;

  24. /**
  25.  * This class implements a stack used for symbolic JVM stack simulation. [It's used as an operand stack substitute.]
  26.  * Elements of this stack are {@link Type} objects.
  27.  */
  28. public class OperandStack implements Cloneable {

  29.     /** We hold the stack information here. */
  30.     private ArrayList<Type> stack = new ArrayList<>();

  31.     /** The maximum number of stack slots this OperandStack instance may hold. */
  32.     private final int maxStack;

  33.     /**
  34.      * Creates an empty stack with a maximum of maxStack slots.
  35.      */
  36.     public OperandStack(final int maxStack) {
  37.         this.maxStack = maxStack;
  38.     }

  39.     /**
  40.      * Creates an otherwise empty stack with a maximum of maxStack slots and the ObjectType 'obj' at the top.
  41.      */
  42.     public OperandStack(final int maxStack, final ObjectType obj) {
  43.         this.maxStack = maxStack;
  44.         push(obj);
  45.     }

  46.     /**
  47.      * Clears the stack.
  48.      */
  49.     public void clear() {
  50.         stack = new ArrayList<>();
  51.     }

  52.     /**
  53.      * Returns a deep copy of this object; that means, the clone operates on a new stack. However, the Type objects on the
  54.      * stack are shared.
  55.      */
  56.     @Override
  57.     public Object clone() {
  58.         final OperandStack newstack = new OperandStack(this.maxStack);
  59.         @SuppressWarnings("unchecked") // OK because this.stack is the same type
  60.         final ArrayList<Type> clone = (ArrayList<Type>) this.stack.clone();
  61.         newstack.stack = clone;
  62.         return newstack;
  63.     }

  64.     /**
  65.      * Returns true if and only if this OperandStack equals another, meaning equal lengths and equal objects on the stacks.
  66.      */
  67.     @Override
  68.     public boolean equals(final Object o) {
  69.         if (!(o instanceof OperandStack)) {
  70.             return false;
  71.         }
  72.         final OperandStack s = (OperandStack) o;
  73.         return this.stack.equals(s.stack);
  74.     }

  75.     /**
  76.      * Returns a (typed!) clone of this.
  77.      *
  78.      * @see #clone()
  79.      */
  80.     public OperandStack getClone() {
  81.         return (OperandStack) clone();
  82.     }

  83.     /**
  84.      * @return a hash code value for the object.
  85.      */
  86.     @Override
  87.     public int hashCode() {
  88.         return stack.hashCode();
  89.     }

  90.     /**
  91.      * Replaces all occurrences of u in this OperandStack instance with an "initialized" ObjectType.
  92.      */
  93.     public void initializeObject(final UninitializedObjectType u) {
  94.         for (int i = 0; i < stack.size(); i++) {
  95.             if (stack.get(i) == u) {
  96.                 stack.set(i, u.getInitialized());
  97.             }
  98.         }
  99.     }

  100.     /**
  101.      * Returns true IFF this OperandStack is empty.
  102.      */
  103.     public boolean isEmpty() {
  104.         return stack.isEmpty();
  105.     }

  106.     /**
  107.      * Returns the number of stack slots this stack can hold.
  108.      */
  109.     public int maxStack() {
  110.         return this.maxStack;
  111.     }

  112.     /**
  113.      * Merges another stack state into this instance's stack state. See the Java Virtual Machine Specification, Second
  114.      * Edition, page 146: 4.9.2 for details.
  115.      */
  116.     public void merge(final OperandStack s) {
  117.         try {
  118.             if (slotsUsed() != s.slotsUsed() || size() != s.size()) {
  119.                 throw new StructuralCodeConstraintException("Cannot merge stacks of different size:\nOperandStack A:\n" + this + "\nOperandStack B:\n" + s);
  120.             }

  121.             for (int i = 0; i < size(); i++) {
  122.                 // If the object _was_ initialized and we're supposed to merge
  123.                 // in some uninitialized object, we reject the code (see vmspec2, 4.9.4, last paragraph).
  124.                 if (!(stack.get(i) instanceof UninitializedObjectType) && s.stack.get(i) instanceof UninitializedObjectType) {
  125.                     throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object on the stack detected.");
  126.                 }
  127.                 // Even harder, we're not initialized but are supposed to broaden
  128.                 // the known object type
  129.                 if (!stack.get(i).equals(s.stack.get(i)) && stack.get(i) instanceof UninitializedObjectType
  130.                     && !(s.stack.get(i) instanceof UninitializedObjectType)) {
  131.                     throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object on the stack detected.");
  132.                 }
  133.                 // on the other hand...
  134.                 if (stack.get(i) instanceof UninitializedObjectType && !(s.stack.get(i) instanceof UninitializedObjectType)) { // that has been initialized by
  135.                                                                                                                                // now
  136.                     stack.set(i, ((UninitializedObjectType) stack.get(i)).getInitialized()); // note that.
  137.                 }
  138.                 if (!stack.get(i).equals(s.stack.get(i))) {
  139.                     if (!(stack.get(i) instanceof ReferenceType) || !(s.stack.get(i) instanceof ReferenceType)) {
  140.                         throw new StructuralCodeConstraintException("Cannot merge stacks of different types:\nStack A:\n" + this + "\nStack B:\n" + s);
  141.                     }
  142.                     stack.set(i, ((ReferenceType) stack.get(i)).getFirstCommonSuperclass((ReferenceType) s.stack.get(i)));
  143.                 }
  144.             }
  145.         } catch (final ClassNotFoundException e) {
  146.             // FIXME: maybe not the best way to handle this
  147.             throw new AssertionViolatedException("Missing class: " + e, e);
  148.         }
  149.     }

  150.     /**
  151.      * Returns the element on top of the stack. The element is not popped off the stack!
  152.      */
  153.     public Type peek() {
  154.         return peek(0);
  155.     }

  156.     /**
  157.      * Returns the element that's i elements below the top element; that means, iff i==0 the top element is returned. The
  158.      * element is not popped off the stack!
  159.      */
  160.     public Type peek(final int i) {
  161.         return stack.get(size() - i - 1);
  162.     }

  163.     /**
  164.      * Returns the element on top of the stack. The element is popped off the stack.
  165.      */
  166.     public Type pop() {
  167.         return stack.remove(size() - 1);
  168.     }

  169.     /**
  170.      * Pops i elements off the stack. Always returns null.
  171.      *
  172.      * @return Always returns null.
  173.      */
  174.     public Type pop(final int count) {
  175.         for (int j = 0; j < count; j++) {
  176.             pop();
  177.         }
  178.         return null;
  179.     }

  180.     /**
  181.      * Pushes a Type object onto the stack.
  182.      */
  183.     public void push(final Type type) {
  184.         if (type == null) {
  185.             throw new AssertionViolatedException("Cannot push NULL onto OperandStack.");
  186.         }
  187.         if (type == Type.BOOLEAN || type == Type.CHAR || type == Type.BYTE || type == Type.SHORT) {
  188.             throw new AssertionViolatedException("The OperandStack does not know about '" + type + "'; use Type.INT instead.");
  189.         }
  190.         if (slotsUsed() >= maxStack) {
  191.             throw new AssertionViolatedException("OperandStack too small, should have thrown proper Exception elsewhere. Stack: " + this);
  192.         }
  193.         stack.add(type);
  194.     }

  195.     /**
  196.      * Returns the size of this OperandStack; that means, how many Type objects there are.
  197.      */
  198.     public int size() {
  199.         return stack.size();
  200.     }

  201.     /**
  202.      * Returns the number of stack slots used.
  203.      *
  204.      * @see #maxStack()
  205.      */
  206.     public int slotsUsed() {
  207.         /*
  208.          * XXX change this to a better implementation using a variable that keeps track of the actual slotsUsed()-value
  209.          * monitoring all push()es and pop()s.
  210.          */
  211.         int slots = 0;
  212.         for (int i = 0; i < stack.size(); i++) {
  213.             slots += peek(i).getSize();
  214.         }
  215.         return slots;
  216.     }

  217.     /**
  218.      * Returns a String representation of this OperandStack instance.
  219.      */
  220.     @Override
  221.     public String toString() {
  222.         final StringBuilder sb = new StringBuilder();
  223.         sb.append("Slots used: ");
  224.         sb.append(slotsUsed());
  225.         sb.append(" MaxStack: ");
  226.         sb.append(maxStack);
  227.         sb.append(".\n");
  228.         for (int i = 0; i < size(); i++) {
  229.             sb.append(peek(i));
  230.             sb.append(" (Size: ");
  231.             sb.append(String.valueOf(peek(i).getSize()));
  232.             sb.append(")\n");
  233.         }
  234.         return sb.toString();
  235.     }

  236. }