1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dx.cf.code; 18 19 import com.android.dx.rop.type.Type; 20 import com.android.dx.rop.type.TypeBearer; 21 import com.android.dx.util.ExceptionWithContext; 22 import com.android.dx.util.Hex; 23 import com.android.dx.util.MutabilityControl; 24 25 /** 26 * Representation of a Java method execution stack. 27 * 28 * <p><b>Note:</b> For the most part, the documentation for this class 29 * ignores the distinction between {@link Type} and {@link 30 * TypeBearer}.</p> 31 */ 32 public final class ExecutionStack extends MutabilityControl { 33 /** {@code non-null;} array of stack contents */ 34 private final TypeBearer[] stack; 35 36 /** 37 * {@code >= 0;} stack pointer (points one past the end) / current stack 38 * size 39 */ 40 private int stackPtr; 41 42 /** 43 * Constructs an instance. 44 * 45 * @param maxStack {@code >= 0;} the maximum size of the stack for this 46 * instance 47 */ ExecutionStack(int maxStack)48 public ExecutionStack(int maxStack) { 49 super(maxStack != 0); 50 stack = new TypeBearer[maxStack]; 51 stackPtr = 0; 52 } 53 54 /** 55 * Makes and returns a mutable copy of this instance. 56 * 57 * @return {@code non-null;} the copy 58 */ copy()59 public ExecutionStack copy() { 60 ExecutionStack result = new ExecutionStack(stack.length); 61 62 System.arraycopy(stack, 0, result.stack, 0, stack.length); 63 result.stackPtr = stackPtr; 64 65 return result; 66 } 67 68 /** 69 * Annotates (adds context to) the given exception with information 70 * about this instance. 71 * 72 * @param ex {@code non-null;} the exception to annotate 73 */ annotate(ExceptionWithContext ex)74 public void annotate(ExceptionWithContext ex) { 75 int limit = stackPtr - 1; 76 77 for (int i = 0; i <= limit; i++) { 78 String idx = (i == limit) ? "top0" : Hex.u2(limit - i); 79 80 ex.addContext("stack[" + idx + "]: " + 81 stackElementString(stack[i])); 82 } 83 } 84 85 /** 86 * Replaces all the occurrences of the given uninitialized type in 87 * this stack with its initialized equivalent. 88 * 89 * @param type {@code non-null;} type to replace 90 */ makeInitialized(Type type)91 public void makeInitialized(Type type) { 92 if (stackPtr == 0) { 93 // We have to check for this before checking for immutability. 94 return; 95 } 96 97 throwIfImmutable(); 98 99 Type initializedType = type.getInitializedType(); 100 101 for (int i = 0; i < stackPtr; i++) { 102 if (stack[i] == type) { 103 stack[i] = initializedType; 104 } 105 } 106 } 107 108 /** 109 * Gets the maximum stack size for this instance. 110 * 111 * @return {@code >= 0;} the max stack size 112 */ getMaxStack()113 public int getMaxStack() { 114 return stack.length; 115 } 116 117 /** 118 * Gets the current stack size. 119 * 120 * @return {@code >= 0, < getMaxStack();} the current stack size 121 */ size()122 public int size() { 123 return stackPtr; 124 } 125 126 /** 127 * Clears the stack. (That is, this method pops everything off.) 128 */ clear()129 public void clear() { 130 throwIfImmutable(); 131 132 for (int i = 0; i < stackPtr; i++) { 133 stack[i] = null; 134 } 135 136 stackPtr = 0; 137 } 138 139 /** 140 * Pushes a value of the given type onto the stack. 141 * 142 * @param type {@code non-null;} type of the value 143 * @throws SimException thrown if there is insufficient room on the 144 * stack for the value 145 */ push(TypeBearer type)146 public void push(TypeBearer type) { 147 throwIfImmutable(); 148 149 int category; 150 151 try { 152 type = type.getFrameType(); 153 category = type.getType().getCategory(); 154 } catch (NullPointerException ex) { 155 // Elucidate the exception. 156 throw new NullPointerException("type == null"); 157 } 158 159 if ((stackPtr + category) > stack.length) { 160 throwSimException("overflow"); 161 return; 162 } 163 164 if (category == 2) { 165 stack[stackPtr] = null; 166 stackPtr++; 167 } 168 169 stack[stackPtr] = type; 170 stackPtr++; 171 } 172 173 /** 174 * Peeks at the {@code n}th element down from the top of the stack. 175 * {@code n == 0} means to peek at the top of the stack. Note that 176 * this will return {@code null} if the indicated element is the 177 * deeper half of a category-2 value. 178 * 179 * @param n {@code >= 0;} which element to peek at 180 * @return {@code null-ok;} the type of value stored at that element 181 * @throws SimException thrown if {@code n >= size()} 182 */ peek(int n)183 public TypeBearer peek(int n) { 184 if (n < 0) { 185 throw new IllegalArgumentException("n < 0"); 186 } 187 188 if (n >= stackPtr) { 189 return throwSimException("underflow"); 190 } 191 192 return stack[stackPtr - n - 1]; 193 } 194 195 /** 196 * Peeks at the {@code n}th element down from the top of the 197 * stack, returning the type per se, as opposed to the 198 * <i>type-bearer</i>. This method is just a convenient shorthand 199 * for {@code peek(n).getType()}. 200 * 201 * @see #peek 202 */ peekType(int n)203 public Type peekType(int n) { 204 return peek(n).getType(); 205 } 206 207 /** 208 * Pops the top element off of the stack. 209 * 210 * @return {@code non-null;} the type formerly on the top of the stack 211 * @throws SimException thrown if the stack is empty 212 */ pop()213 public TypeBearer pop() { 214 throwIfImmutable(); 215 216 TypeBearer result = peek(0); 217 218 stack[stackPtr - 1] = null; 219 stackPtr -= result.getType().getCategory(); 220 221 return result; 222 } 223 224 /** 225 * Changes an element already on a stack. This method is useful in limited 226 * contexts, particularly when merging two instances. As such, it places 227 * the following restriction on its behavior: You may only replace 228 * values with other values of the same category. 229 * 230 * @param n {@code >= 0;} which element to change, where {@code 0} is 231 * the top element of the stack 232 * @param type {@code non-null;} type of the new value 233 * @throws SimException thrown if {@code n >= size()} or 234 * the action is otherwise prohibited 235 */ change(int n, TypeBearer type)236 public void change(int n, TypeBearer type) { 237 throwIfImmutable(); 238 239 try { 240 type = type.getFrameType(); 241 } catch (NullPointerException ex) { 242 // Elucidate the exception. 243 throw new NullPointerException("type == null"); 244 } 245 246 int idx = stackPtr - n - 1; 247 TypeBearer orig = stack[idx]; 248 249 if ((orig == null) || 250 (orig.getType().getCategory() != type.getType().getCategory())) { 251 throwSimException("incompatible substitution: " + 252 stackElementString(orig) + " -> " + 253 stackElementString(type)); 254 } 255 256 stack[idx] = type; 257 } 258 259 /** 260 * Merges this stack with another stack. A new instance is returned if 261 * this merge results in a change. If no change results, this instance is 262 * returned. See {@link Merger#mergeStack(ExecutionStack,ExecutionStack) 263 * Merger.mergeStack()} 264 * 265 * @param other {@code non-null;} a stack to merge with 266 * @return {@code non-null;} the result of the merge 267 */ merge(ExecutionStack other)268 public ExecutionStack merge(ExecutionStack other) { 269 try { 270 return Merger.mergeStack(this, other); 271 } catch (SimException ex) { 272 ex.addContext("underlay stack:"); 273 this.annotate(ex); 274 ex.addContext("overlay stack:"); 275 other.annotate(ex); 276 throw ex; 277 } 278 } 279 280 /** 281 * Gets the string form for a stack element. This is the same as 282 * {@code toString()} except that {@code null} is converted 283 * to {@code "<invalid>"}. 284 * 285 * @param type {@code null-ok;} the stack element 286 * @return {@code non-null;} the string form 287 */ stackElementString(TypeBearer type)288 private static String stackElementString(TypeBearer type) { 289 if (type == null) { 290 return "<invalid>"; 291 } 292 293 return type.toString(); 294 } 295 296 /** 297 * Throws a properly-formatted exception. 298 * 299 * @param msg {@code non-null;} useful message 300 * @return never (keeps compiler happy) 301 */ throwSimException(String msg)302 private static TypeBearer throwSimException(String msg) { 303 throw new SimException("stack: " + msg); 304 } 305 } 306