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.dex.util.ExceptionWithContext; 20 import com.android.dx.rop.type.Type; 21 import com.android.dx.rop.type.TypeBearer; 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 non-null;} array specifying whether stack contents have entries 38 * in the local variable table 39 */ 40 private final boolean[] local; 41 /** 42 * {@code >= 0;} stack pointer (points one past the end) / current stack 43 * size 44 */ 45 private int stackPtr; 46 47 /** 48 * Constructs an instance. 49 * 50 * @param maxStack {@code >= 0;} the maximum size of the stack for this 51 * instance 52 */ ExecutionStack(int maxStack)53 public ExecutionStack(int maxStack) { 54 super(maxStack != 0); 55 stack = new TypeBearer[maxStack]; 56 local = new boolean[maxStack]; 57 stackPtr = 0; 58 } 59 60 /** 61 * Makes and returns a mutable copy of this instance. 62 * 63 * @return {@code non-null;} the copy 64 */ copy()65 public ExecutionStack copy() { 66 ExecutionStack result = new ExecutionStack(stack.length); 67 68 System.arraycopy(stack, 0, result.stack, 0, stack.length); 69 System.arraycopy(local, 0, result.local, 0, local.length); 70 result.stackPtr = stackPtr; 71 72 return result; 73 } 74 75 /** 76 * Annotates (adds context to) the given exception with information 77 * about this instance. 78 * 79 * @param ex {@code non-null;} the exception to annotate 80 */ annotate(ExceptionWithContext ex)81 public void annotate(ExceptionWithContext ex) { 82 int limit = stackPtr - 1; 83 84 for (int i = 0; i <= limit; i++) { 85 String idx = (i == limit) ? "top0" : Hex.u2(limit - i); 86 87 ex.addContext("stack[" + idx + "]: " + 88 stackElementString(stack[i])); 89 } 90 } 91 92 /** 93 * Replaces all the occurrences of the given uninitialized type in 94 * this stack with its initialized equivalent. 95 * 96 * @param type {@code non-null;} type to replace 97 */ makeInitialized(Type type)98 public void makeInitialized(Type type) { 99 if (stackPtr == 0) { 100 // We have to check for this before checking for immutability. 101 return; 102 } 103 104 throwIfImmutable(); 105 106 Type initializedType = type.getInitializedType(); 107 108 for (int i = 0; i < stackPtr; i++) { 109 if (stack[i] == type) { 110 stack[i] = initializedType; 111 } 112 } 113 } 114 115 /** 116 * Gets the maximum stack size for this instance. 117 * 118 * @return {@code >= 0;} the max stack size 119 */ getMaxStack()120 public int getMaxStack() { 121 return stack.length; 122 } 123 124 /** 125 * Gets the current stack size. 126 * 127 * @return {@code >= 0, < getMaxStack();} the current stack size 128 */ size()129 public int size() { 130 return stackPtr; 131 } 132 133 /** 134 * Clears the stack. (That is, this method pops everything off.) 135 */ clear()136 public void clear() { 137 throwIfImmutable(); 138 139 for (int i = 0; i < stackPtr; i++) { 140 stack[i] = null; 141 local[i] = false; 142 } 143 144 stackPtr = 0; 145 } 146 147 /** 148 * Pushes a value of the given type onto the stack. 149 * 150 * @param type {@code non-null;} type of the value 151 * @throws SimException thrown if there is insufficient room on the 152 * stack for the value 153 */ push(TypeBearer type)154 public void push(TypeBearer type) { 155 throwIfImmutable(); 156 157 int category; 158 159 try { 160 type = type.getFrameType(); 161 category = type.getType().getCategory(); 162 } catch (NullPointerException ex) { 163 // Elucidate the exception. 164 throw new NullPointerException("type == null"); 165 } 166 167 if ((stackPtr + category) > stack.length) { 168 throwSimException("overflow"); 169 return; 170 } 171 172 if (category == 2) { 173 stack[stackPtr] = null; 174 stackPtr++; 175 } 176 177 stack[stackPtr] = type; 178 stackPtr++; 179 } 180 181 /** 182 * Flags the next value pushed onto the stack as having local info. 183 */ setLocal()184 public void setLocal() { 185 throwIfImmutable(); 186 187 local[stackPtr] = true; 188 } 189 190 /** 191 * Peeks at the {@code n}th element down from the top of the stack. 192 * {@code n == 0} means to peek at the top of the stack. Note that 193 * this will return {@code null} if the indicated element is the 194 * deeper half of a category-2 value. 195 * 196 * @param n {@code >= 0;} which element to peek at 197 * @return {@code null-ok;} the type of value stored at that element 198 * @throws SimException thrown if {@code n >= size()} 199 */ peek(int n)200 public TypeBearer peek(int n) { 201 if (n < 0) { 202 throw new IllegalArgumentException("n < 0"); 203 } 204 205 if (n >= stackPtr) { 206 return throwSimException("underflow"); 207 } 208 209 return stack[stackPtr - n - 1]; 210 } 211 212 /** 213 * Peeks at the {@code n}th element down from the top of the 214 * stack, returning whether or not it has local info. 215 * 216 * @param n {@code >= 0;} which element to peek at 217 * @return {@code true} if the value has local info, {@code false} otherwise 218 * @throws SimException thrown if {@code n >= size()} 219 */ peekLocal(int n)220 public boolean peekLocal(int n) { 221 if (n < 0) { 222 throw new IllegalArgumentException("n < 0"); 223 } 224 225 if (n >= stackPtr) { 226 throw new SimException("stack: underflow"); 227 } 228 229 return local[stackPtr - n - 1]; 230 } 231 232 /** 233 * Peeks at the {@code n}th element down from the top of the 234 * stack, returning the type per se, as opposed to the 235 * <i>type-bearer</i>. This method is just a convenient shorthand 236 * for {@code peek(n).getType()}. 237 * 238 * @see #peek 239 */ peekType(int n)240 public Type peekType(int n) { 241 return peek(n).getType(); 242 } 243 244 /** 245 * Pops the top element off of the stack. 246 * 247 * @return {@code non-null;} the type formerly on the top of the stack 248 * @throws SimException thrown if the stack is empty 249 */ pop()250 public TypeBearer pop() { 251 throwIfImmutable(); 252 253 TypeBearer result = peek(0); 254 255 stack[stackPtr - 1] = null; 256 local[stackPtr - 1] = false; 257 stackPtr -= result.getType().getCategory(); 258 259 return result; 260 } 261 262 /** 263 * Changes an element already on a stack. This method is useful in limited 264 * contexts, particularly when merging two instances. As such, it places 265 * the following restriction on its behavior: You may only replace 266 * values with other values of the same category. 267 * 268 * @param n {@code >= 0;} which element to change, where {@code 0} is 269 * the top element of the stack 270 * @param type {@code non-null;} type of the new value 271 * @throws SimException thrown if {@code n >= size()} or 272 * the action is otherwise prohibited 273 */ change(int n, TypeBearer type)274 public void change(int n, TypeBearer type) { 275 throwIfImmutable(); 276 277 try { 278 type = type.getFrameType(); 279 } catch (NullPointerException ex) { 280 // Elucidate the exception. 281 throw new NullPointerException("type == null"); 282 } 283 284 int idx = stackPtr - n - 1; 285 TypeBearer orig = stack[idx]; 286 287 if ((orig == null) || 288 (orig.getType().getCategory() != type.getType().getCategory())) { 289 throwSimException("incompatible substitution: " + 290 stackElementString(orig) + " -> " + 291 stackElementString(type)); 292 } 293 294 stack[idx] = type; 295 } 296 297 /** 298 * Merges this stack with another stack. A new instance is returned if 299 * this merge results in a change. If no change results, this instance is 300 * returned. See {@link Merger#mergeStack(ExecutionStack,ExecutionStack) 301 * Merger.mergeStack()} 302 * 303 * @param other {@code non-null;} a stack to merge with 304 * @return {@code non-null;} the result of the merge 305 */ merge(ExecutionStack other)306 public ExecutionStack merge(ExecutionStack other) { 307 try { 308 return Merger.mergeStack(this, other); 309 } catch (SimException ex) { 310 ex.addContext("underlay stack:"); 311 this.annotate(ex); 312 ex.addContext("overlay stack:"); 313 other.annotate(ex); 314 throw ex; 315 } 316 } 317 318 /** 319 * Gets the string form for a stack element. This is the same as 320 * {@code toString()} except that {@code null} is converted 321 * to {@code "<invalid>"}. 322 * 323 * @param type {@code null-ok;} the stack element 324 * @return {@code non-null;} the string form 325 */ stackElementString(TypeBearer type)326 private static String stackElementString(TypeBearer type) { 327 if (type == null) { 328 return "<invalid>"; 329 } 330 331 return type.toString(); 332 } 333 334 /** 335 * Throws a properly-formatted exception. 336 * 337 * @param msg {@code non-null;} useful message 338 * @return never (keeps compiler happy) 339 */ throwSimException(String msg)340 private static TypeBearer throwSimException(String msg) { 341 throw new SimException("stack: " + msg); 342 } 343 } 344