1/* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15package javassist.bytecode.analysis; 16 17 18/** 19 * Represents the stack frame and local variable table at a particular point in time. 20 * 21 * @author Jason T. Greene 22 */ 23public class Frame { 24 private Type[] locals; 25 private Type[] stack; 26 private int top; 27 private boolean jsrMerged; 28 private boolean retMerged; 29 30 /** 31 * Create a new frame with the specified local variable table size, and max stack size 32 * 33 * @param locals the number of local variable table entries 34 * @param stack the maximum stack size 35 */ 36 public Frame(int locals, int stack) { 37 this.locals = new Type[locals]; 38 this.stack = new Type[stack]; 39 } 40 41 /** 42 * Returns the local varaible table entry at index. 43 * 44 * @param index the position in the table 45 * @return the type if one exists, or null if the position is empty 46 */ 47 public Type getLocal(int index) { 48 return locals[index]; 49 } 50 51 /** 52 * Sets the local variable table entry at index to a type. 53 * 54 * @param index the position in the table 55 * @param type the type to set at the position 56 */ 57 public void setLocal(int index, Type type) { 58 locals[index] = type; 59 } 60 61 62 /** 63 * Returns the type on the stack at the specified index. 64 * 65 * @param index the position on the stack 66 * @return the type of the stack position 67 */ 68 public Type getStack(int index) { 69 return stack[index]; 70 } 71 72 /** 73 * Sets the type of the stack position 74 * 75 * @param index the position on the stack 76 * @param type the type to set 77 */ 78 public void setStack(int index, Type type) { 79 stack[index] = type; 80 } 81 82 /** 83 * Empties the stack 84 */ 85 public void clearStack() { 86 top = 0; 87 } 88 89 /** 90 * Gets the index of the type sitting at the top of the stack. 91 * This is not to be confused with a length operation which 92 * would return the number of elements, not the position of 93 * the last element. 94 * 95 * @return the position of the element at the top of the stack 96 */ 97 public int getTopIndex() { 98 return top - 1; 99 } 100 101 /** 102 * Returns the number of local variable table entries, specified 103 * at construction. 104 * 105 * @return the number of local variable table entries 106 */ 107 public int localsLength() { 108 return locals.length; 109 } 110 111 /** 112 * Gets the top of the stack without altering it 113 * 114 * @return the top of the stack 115 */ 116 public Type peek() { 117 if (top < 1) 118 throw new IndexOutOfBoundsException("Stack is empty"); 119 120 return stack[top - 1]; 121 } 122 123 /** 124 * Alters the stack to contain one less element and return it. 125 * 126 * @return the element popped from the stack 127 */ 128 public Type pop() { 129 if (top < 1) 130 throw new IndexOutOfBoundsException("Stack is empty"); 131 return stack[--top]; 132 } 133 134 /** 135 * Alters the stack by placing the passed type on the top 136 * 137 * @param type the type to add to the top 138 */ 139 public void push(Type type) { 140 stack[top++] = type; 141 } 142 143 144 /** 145 * Makes a shallow copy of this frame, i.e. the type instances will 146 * remain the same. 147 * 148 * @return the shallow copy 149 */ 150 public Frame copy() { 151 Frame frame = new Frame(locals.length, stack.length); 152 System.arraycopy(locals, 0, frame.locals, 0, locals.length); 153 System.arraycopy(stack, 0, frame.stack, 0, stack.length); 154 frame.top = top; 155 return frame; 156 } 157 158 /** 159 * Makes a shallow copy of the stack portion of this frame. The local 160 * variable table size will be copied, but its contents will be empty. 161 * 162 * @return the shallow copy of the stack 163 */ 164 public Frame copyStack() { 165 Frame frame = new Frame(locals.length, stack.length); 166 System.arraycopy(stack, 0, frame.stack, 0, stack.length); 167 frame.top = top; 168 return frame; 169 } 170 171 /** 172 * Merges all types on the stack of this frame instance with that of the specified frame. 173 * The local variable table is left untouched. 174 * 175 * @param frame the frame to merge the stack from 176 * @return true if any changes where made 177 */ 178 public boolean mergeStack(Frame frame) { 179 boolean changed = false; 180 if (top != frame.top) 181 throw new RuntimeException("Operand stacks could not be merged, they are different sizes!"); 182 183 for (int i = 0; i < top; i++) { 184 if (stack[i] != null) { 185 Type prev = stack[i]; 186 Type merged = prev.merge(frame.stack[i]); 187 if (merged == Type.BOGUS) 188 throw new RuntimeException("Operand stacks could not be merged due to differing primitive types: pos = " + i); 189 190 stack[i] = merged; 191 // always replace the instance in case a multi-interface type changes to a normal Type 192 if ((! merged.equals(prev)) || merged.popChanged()) { 193 changed = true; 194 } 195 } 196 } 197 198 return changed; 199 } 200 201 /** 202 * Merges all types on the stack and local variable table of this frame with that of the specified 203 * type. 204 * 205 * @param frame the frame to merge with 206 * @return true if any changes to this frame where made by this merge 207 */ 208 public boolean merge(Frame frame) { 209 boolean changed = false; 210 211 // Local variable table 212 for (int i = 0; i < locals.length; i++) { 213 if (locals[i] != null) { 214 Type prev = locals[i]; 215 Type merged = prev.merge(frame.locals[i]); 216 // always replace the instance in case a multi-interface type changes to a normal Type 217 locals[i] = merged; 218 if (! merged.equals(prev) || merged.popChanged()) { 219 changed = true; 220 } 221 } else if (frame.locals[i] != null) { 222 locals[i] = frame.locals[i]; 223 changed = true; 224 } 225 } 226 227 changed |= mergeStack(frame); 228 return changed; 229 } 230 231 public String toString() { 232 StringBuffer buffer = new StringBuffer(); 233 234 buffer.append("locals = ["); 235 for (int i = 0; i < locals.length; i++) { 236 buffer.append(locals[i] == null ? "empty" : locals[i].toString()); 237 if (i < locals.length - 1) 238 buffer.append(", "); 239 } 240 buffer.append("] stack = ["); 241 for (int i = 0; i < top; i++) { 242 buffer.append(stack[i]); 243 if (i < top - 1) 244 buffer.append(", "); 245 } 246 buffer.append("]"); 247 248 return buffer.toString(); 249 } 250 251 /** 252 * Whether or not state from the source JSR instruction has been merged 253 * 254 * @return true if JSR state has been merged 255 */ 256 boolean isJsrMerged() { 257 return jsrMerged; 258 } 259 260 /** 261 * Sets whether of not the state from the source JSR instruction has been merged 262 * 263 * @param jsrMerged true if merged, otherwise false 264 */ 265 void setJsrMerged(boolean jsrMerged) { 266 this.jsrMerged = jsrMerged; 267 } 268 269 /** 270 * Whether or not state from the RET instruction, of the subroutine that was jumped 271 * to has been merged. 272 * 273 * @return true if RET state has been merged 274 */ 275 boolean isRetMerged() { 276 return retMerged; 277 } 278 279 /** 280 * Sets whether or not state from the RET instruction, of the subroutine that was jumped 281 * to has been merged. 282 * 283 * @param retMerged true if RET state has been merged 284 */ 285 void setRetMerged(boolean retMerged) { 286 this.retMerged = retMerged; 287 } 288} 289