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 */ 18 package org.apache.bcel.verifier.structurals; 19 20 21 import java.util.ArrayList; 22 23 import org.apache.bcel.generic.ObjectType; 24 import org.apache.bcel.generic.ReferenceType; 25 import org.apache.bcel.generic.Type; 26 import org.apache.bcel.verifier.exc.AssertionViolatedException; 27 import org.apache.bcel.verifier.exc.StructuralCodeConstraintException; 28 29 /** 30 * This class implements a stack used for symbolic JVM stack simulation. 31 * [It's used an an operand stack substitute.] 32 * Elements of this stack are {@link Type} objects. 33 * 34 * @version $Id$ 35 */ 36 public class OperandStack implements Cloneable { 37 38 /** We hold the stack information here. */ 39 private ArrayList<Type> stack = new ArrayList<>(); 40 41 /** The maximum number of stack slots this OperandStack instance may hold. */ 42 private final int maxStack; 43 44 /** 45 * Creates an empty stack with a maximum of maxStack slots. 46 */ OperandStack(final int maxStack)47 public OperandStack(final int maxStack) { 48 this.maxStack = maxStack; 49 } 50 51 /** 52 * Creates an otherwise empty stack with a maximum of maxStack slots and 53 * the ObjectType 'obj' at the top. 54 */ OperandStack(final int maxStack, final ObjectType obj)55 public OperandStack(final int maxStack, final ObjectType obj) { 56 this.maxStack = maxStack; 57 this.push(obj); 58 } 59 /** 60 * Returns a deep copy of this object; that means, the clone operates 61 * on a new stack. However, the Type objects on the stack are 62 * shared. 63 */ 64 @Override clone()65 public Object clone() { 66 final OperandStack newstack = new OperandStack(this.maxStack); 67 @SuppressWarnings("unchecked") // OK because this.stack is the same type 68 final ArrayList<Type> clone = (ArrayList<Type>) this.stack.clone(); 69 newstack.stack = clone; 70 return newstack; 71 } 72 73 /** 74 * Clears the stack. 75 */ clear()76 public void clear() { 77 stack = new ArrayList<>(); 78 } 79 80 /** @return a hash code value for the object. 81 */ 82 @Override hashCode()83 public int hashCode() { return stack.hashCode(); } 84 85 /** 86 * Returns true if and only if this OperandStack 87 * equals another, meaning equal lengths and equal 88 * objects on the stacks. 89 */ 90 @Override equals(final Object o)91 public boolean equals(final Object o) { 92 if (!(o instanceof OperandStack)) { 93 return false; 94 } 95 final OperandStack s = (OperandStack) o; 96 return this.stack.equals(s.stack); 97 } 98 99 /** 100 * Returns a (typed!) clone of this. 101 * 102 * @see #clone() 103 */ getClone()104 public OperandStack getClone() { 105 return (OperandStack) this.clone(); 106 } 107 108 /** 109 * Returns true IFF this OperandStack is empty. 110 */ isEmpty()111 public boolean isEmpty() { 112 return stack.isEmpty(); 113 } 114 115 /** 116 * Returns the number of stack slots this stack can hold. 117 */ maxStack()118 public int maxStack() { 119 return this.maxStack; 120 } 121 122 /** 123 * Returns the element on top of the stack. The element is not popped off the stack! 124 */ peek()125 public Type peek() { 126 return peek(0); 127 } 128 129 /** 130 * Returns the element that's i elements below the top element; that means, 131 * iff i==0 the top element is returned. The element is not popped off the stack! 132 */ peek(final int i)133 public Type peek(final int i) { 134 return stack.get(size()-i-1); 135 } 136 137 /** 138 * Returns the element on top of the stack. The element is popped off the stack. 139 */ pop()140 public Type pop() { 141 final Type e = stack.remove(size()-1); 142 return e; 143 } 144 145 /** 146 * Pops i elements off the stack. ALWAYS RETURNS "null"!!! 147 */ pop(final int i)148 public Type pop(final int i) { 149 for (int j=0; j<i; j++) { 150 pop(); 151 } 152 return null; 153 } 154 155 /** 156 * Pushes a Type object onto the stack. 157 */ push(final Type type)158 public void push(final Type type) { 159 if (type == null) { 160 throw new AssertionViolatedException("Cannot push NULL onto OperandStack."); 161 } 162 if (type == Type.BOOLEAN || type == Type.CHAR || type == Type.BYTE || type == Type.SHORT) { 163 throw new AssertionViolatedException("The OperandStack does not know about '"+type+"'; use Type.INT instead."); 164 } 165 if (slotsUsed() >= maxStack) { 166 throw new AssertionViolatedException( 167 "OperandStack too small, should have thrown proper Exception elsewhere. Stack: "+this); 168 } 169 stack.add(type); 170 } 171 172 /** 173 * Returns the size of this OperandStack; that means, how many Type objects there are. 174 */ size()175 public int size() { 176 return stack.size(); 177 } 178 179 /** 180 * Returns the number of stack slots used. 181 * @see #maxStack() 182 */ slotsUsed()183 public int slotsUsed() { 184 /* XXX change this to a better implementation using a variable 185 that keeps track of the actual slotsUsed()-value monitoring 186 all push()es and pop()s. 187 */ 188 int slots = 0; 189 for (int i=0; i<stack.size(); i++) { 190 slots += peek(i).getSize(); 191 } 192 return slots; 193 } 194 195 /** 196 * Returns a String representation of this OperandStack instance. 197 */ 198 @Override toString()199 public String toString() { 200 final StringBuilder sb = new StringBuilder(); 201 sb.append("Slots used: "); 202 sb.append(slotsUsed()); 203 sb.append(" MaxStack: "); 204 sb.append(maxStack); 205 sb.append(".\n"); 206 for (int i=0; i<size(); i++) { 207 sb.append(peek(i)); 208 sb.append(" (Size: "); 209 sb.append(String.valueOf(peek(i).getSize())); 210 sb.append(")\n"); 211 } 212 return sb.toString(); 213 } 214 215 /** 216 * Merges another stack state into this instance's stack state. 217 * See the Java Virtual Machine Specification, Second Edition, page 146: 4.9.2 218 * for details. 219 */ merge(final OperandStack s)220 public void merge(final OperandStack s) { 221 try { 222 if ( (slotsUsed() != s.slotsUsed()) || (size() != s.size()) ) { 223 throw new StructuralCodeConstraintException( 224 "Cannot merge stacks of different size:\nOperandStack A:\n"+this+"\nOperandStack B:\n"+s); 225 } 226 227 for (int i=0; i<size(); i++) { 228 // If the object _was_ initialized and we're supposed to merge 229 // in some uninitialized object, we reject the code (see vmspec2, 4.9.4, last paragraph). 230 if ( (! (stack.get(i) instanceof UninitializedObjectType)) && (s.stack.get(i) instanceof UninitializedObjectType) ) { 231 throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object on the stack detected."); 232 } 233 // Even harder, we're not initialized but are supposed to broaden 234 // the known object type 235 if ( (!(stack.get(i).equals(s.stack.get(i)))) && 236 (stack.get(i) instanceof UninitializedObjectType) && (!(s.stack.get(i) instanceof UninitializedObjectType))) { 237 throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object on the stack detected."); 238 } 239 // on the other hand... 240 if (stack.get(i) instanceof UninitializedObjectType) { //if we have an uninitialized object here 241 if (! (s.stack.get(i) instanceof UninitializedObjectType)) { //that has been initialized by now 242 stack.set(i, ((UninitializedObjectType) (stack.get(i))).getInitialized() ); //note that. 243 } 244 } 245 if (! stack.get(i).equals(s.stack.get(i))) { 246 if ( (stack.get(i) instanceof ReferenceType) && 247 (s.stack.get(i) instanceof ReferenceType) ) { 248 stack.set(i, ((ReferenceType) stack.get(i)).getFirstCommonSuperclass((ReferenceType) (s.stack.get(i)))); 249 } 250 else{ 251 throw new StructuralCodeConstraintException( 252 "Cannot merge stacks of different types:\nStack A:\n"+this+"\nStack B:\n"+s); 253 } 254 } 255 } 256 } catch (final ClassNotFoundException e) { 257 // FIXME: maybe not the best way to handle this 258 throw new AssertionViolatedException("Missing class: " + e, e); 259 } 260 } 261 262 /** 263 * Replaces all occurences of u in this OperandStack instance 264 * with an "initialized" ObjectType. 265 */ initializeObject(final UninitializedObjectType u)266 public void initializeObject(final UninitializedObjectType u) { 267 for (int i=0; i<stack.size(); i++) { 268 if (stack.get(i) == u) { 269 stack.set(i, u.getInitialized()); 270 } 271 } 272 } 273 274 } 275