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