1 21 package proguard.classfile.attribute.visitor; 22 23 import proguard.classfile.*; 24 import proguard.classfile.attribute.*; 25 import proguard.classfile.instruction.*; 26 import proguard.classfile.instruction.visitor.InstructionVisitor; 27 import proguard.classfile.util.SimplifiedVisitor; 28 29 35 public class StackSizeComputer 36 extends SimplifiedVisitor 37 implements AttributeVisitor, 38 InstructionVisitor, 39 ExceptionInfoVisitor 40 { 41 private static final boolean DEBUG = false; 43 46 47 48 private boolean[] evaluated = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; 49 private int[] stackSizes = new int[ClassConstants.TYPICAL_CODE_LENGTH]; 50 51 private boolean exitInstructionBlock; 52 53 private int stackSize; 54 private int maxStackSize; 55 56 57 61 public boolean isReachable(int instructionOffset) 62 { 63 return evaluated[instructionOffset]; 64 } 65 66 67 71 public int getStackSize(int instructionOffset) 72 { 73 if (!evaluated[instructionOffset]) 74 { 75 throw new IllegalArgumentException ("Unknown stack size at unreachable instruction offset ["+instructionOffset+"]"); 76 } 77 78 return stackSizes[instructionOffset]; 79 } 80 81 82 85 public int getMaxStackSize() 86 { 87 return maxStackSize; 88 } 89 90 91 93 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 94 { 95 99 try 102 { 103 visitCodeAttribute0(clazz, method, codeAttribute); 105 } 106 catch (RuntimeException ex) 107 { 108 System.err.println("Unexpected error while computing stack sizes:"); 109 System.err.println(" Class = ["+clazz.getName()+"]"); 110 System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 111 System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); 112 113 throw ex; 114 } 115 } 116 117 118 public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) 119 { 120 if (DEBUG) 121 { 122 System.out.println("StackSizeComputer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); 123 } 124 125 int codeLength = codeAttribute.u4codeLength; 127 if (evaluated.length < codeLength) 128 { 129 evaluated = new boolean[codeLength]; 130 stackSizes = new int[codeLength]; 131 } 132 else 133 { 134 for (int index = 0; index < codeLength; index++) 135 { 136 evaluated[index] = false; 137 } 138 } 139 140 stackSize = 0; 142 maxStackSize = 0; 143 144 evaluateInstructionBlock(clazz, method, codeAttribute, 0); 146 147 codeAttribute.exceptionsAccept(clazz, method, this); 149 } 150 151 152 154 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 155 { 156 byte opcode = simpleInstruction.opcode; 157 158 exitInstructionBlock = 160 opcode == InstructionConstants.OP_IRETURN || 161 opcode == InstructionConstants.OP_LRETURN || 162 opcode == InstructionConstants.OP_FRETURN || 163 opcode == InstructionConstants.OP_DRETURN || 164 opcode == InstructionConstants.OP_ARETURN || 165 opcode == InstructionConstants.OP_RETURN || 166 opcode == InstructionConstants.OP_ATHROW; 167 } 168 169 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 170 { 171 exitInstructionBlock = false; 173 } 174 175 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 176 { 177 byte opcode = variableInstruction.opcode; 178 179 exitInstructionBlock = 181 opcode == InstructionConstants.OP_RET; 182 } 183 184 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 185 { 186 byte opcode = branchInstruction.opcode; 187 188 evaluateInstructionBlock(clazz, 190 method, 191 codeAttribute, 192 offset + 193 branchInstruction.branchOffset); 194 195 if (opcode == InstructionConstants.OP_JSR || 197 opcode == InstructionConstants.OP_JSR_W) 198 { 199 stackSize -= 1; 202 203 evaluateInstructionBlock(clazz, 204 method, 205 codeAttribute, 206 offset + branchInstruction.length(offset)); 207 } 208 209 exitInstructionBlock = 211 opcode == InstructionConstants.OP_GOTO || 212 opcode == InstructionConstants.OP_GOTO_W || 213 opcode == InstructionConstants.OP_JSR || 214 opcode == InstructionConstants.OP_JSR_W; 215 } 216 217 218 public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) 219 { 220 222 int[] jumpOffsets = switchInstruction.jumpOffsets; 224 225 for (int index = 0; index < jumpOffsets.length; index++) 226 { 227 evaluateInstructionBlock(clazz, 229 method, 230 codeAttribute, 231 offset + jumpOffsets[index]); 232 } 233 234 evaluateInstructionBlock(clazz, 236 method, 237 codeAttribute, 238 offset + switchInstruction.defaultOffset); 239 240 exitInstructionBlock = true; 242 } 243 244 245 247 public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) 248 { 249 if (DEBUG) 250 { 251 System.out.println("Exception:"); 252 } 253 254 stackSize = 1; 256 257 evaluateInstructionBlock(clazz, 260 method, 261 codeAttribute, 262 exceptionInfo.u2handlerPC); 263 } 264 265 266 268 274 private void evaluateInstructionBlock(Clazz clazz, 275 Method method, 276 CodeAttribute codeAttribute, 277 int instructionOffset) 278 { 279 if (DEBUG) 280 { 281 if (evaluated[instructionOffset]) 282 { 283 System.out.println("-- (instruction block at "+instructionOffset+" already evaluated)"); 284 } 285 else 286 { 287 System.out.println("-- instruction block:"); 288 } 289 } 290 291 int initialStackSize = stackSize; 293 294 if (maxStackSize < stackSize) 296 { 297 maxStackSize = stackSize; 298 } 299 300 while (!evaluated[instructionOffset]) 302 { 303 evaluated[instructionOffset] = true; 305 306 Instruction instruction = InstructionFactory.create(codeAttribute.code, 307 instructionOffset); 308 309 if (DEBUG) 310 { 311 int stackPushCount = instruction.stackPushCount(clazz); 312 int stackPopCount = instruction.stackPopCount(clazz); 313 System.out.println("["+instructionOffset+"]: "+ 314 stackSize+" - "+ 315 stackPopCount+" + "+ 316 stackPushCount+" = "+ 317 (stackSize+stackPushCount-stackPopCount)+": "+ 318 instruction.toString(instructionOffset)); 319 } 320 321 stackSizes[instructionOffset] = 323 stackSize += instruction.stackPushCount(clazz) - 324 instruction.stackPopCount(clazz); 325 326 if (stackSize < 0) 327 { 328 throw new IllegalArgumentException ("Stack size becomes negative after instruction "+ 329 instruction.toString(instructionOffset)+" in ["+ 330 clazz.getName()+"."+ 331 method.getName(clazz)+ 332 method.getDescriptor(clazz)+"]"); 333 } 334 335 if (maxStackSize < stackSize) 337 { 338 maxStackSize = stackSize; 339 } 340 341 int nextInstructionOffset = instructionOffset + 343 instruction.length(instructionOffset); 344 345 instruction.accept(clazz, method, codeAttribute, instructionOffset, this); 347 348 if (exitInstructionBlock) 350 { 351 break; 352 } 353 354 instructionOffset = nextInstructionOffset; 356 357 if (DEBUG) 358 { 359 if (evaluated[instructionOffset]) 360 { 361 System.out.println("-- (instruction at "+instructionOffset+" already evaluated)"); 362 } 363 } 364 } 365 366 this.stackSize = initialStackSize; 368 } 369 } 370 | Popular Tags |