1 // ASM: a very small and fast Java bytecode manipulation framework 2 // Copyright (c) 2000-2011 INRIA, France Telecom 3 // All rights reserved. 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions 7 // are met: 8 // 1. Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // 2. Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // 3. Neither the name of the copyright holders nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 // THE POSSIBILITY OF SUCH DAMAGE. 28 package org.objectweb.asm.tree.analysis; 29 30 import java.util.ArrayList; 31 import java.util.HashMap; 32 import java.util.List; 33 import java.util.Map; 34 import org.objectweb.asm.Opcodes; 35 import org.objectweb.asm.Type; 36 import org.objectweb.asm.tree.AbstractInsnNode; 37 import org.objectweb.asm.tree.IincInsnNode; 38 import org.objectweb.asm.tree.InsnList; 39 import org.objectweb.asm.tree.JumpInsnNode; 40 import org.objectweb.asm.tree.LabelNode; 41 import org.objectweb.asm.tree.LookupSwitchInsnNode; 42 import org.objectweb.asm.tree.MethodNode; 43 import org.objectweb.asm.tree.TableSwitchInsnNode; 44 import org.objectweb.asm.tree.TryCatchBlockNode; 45 import org.objectweb.asm.tree.VarInsnNode; 46 47 /** 48 * A semantic bytecode analyzer. <i>This class does not fully check that JSR and RET instructions 49 * are valid.</i> 50 * 51 * @param <V> type of the {@link Value} used for the analysis. 52 * @author Eric Bruneton 53 */ 54 public class Analyzer<V extends Value> implements Opcodes { 55 56 /** The interpreter to use to symbolically interpret the bytecode instructions. */ 57 private final Interpreter<V> interpreter; 58 59 /** The instructions of the currently analyzed method. */ 60 private InsnList insnList; 61 62 /** The size of {@link #insnList}. */ 63 private int insnListSize; 64 65 /** The exception handlers of the currently analyzed method (one list per instruction index). */ 66 private List<TryCatchBlockNode>[] handlers; 67 68 /** The execution stack frames of the currently analyzed method (one per instruction index). */ 69 private Frame<V>[] frames; 70 71 /** The subroutines of the currently analyzed method (one per instruction index). */ 72 private Subroutine[] subroutines; 73 74 /** The instructions that remain to process (one boolean per instruction index). */ 75 private boolean[] inInstructionsToProcess; 76 77 /** The indices of the instructions that remain to process in the currently analyzed method. */ 78 private int[] instructionsToProcess; 79 80 /** The number of instructions that remain to process in the currently analyzed method. */ 81 private int numInstructionsToProcess; 82 83 /** 84 * Constructs a new {@link Analyzer}. 85 * 86 * @param interpreter the interpreter to use to symbolically interpret the bytecode instructions. 87 */ Analyzer(final Interpreter<V> interpreter)88 public Analyzer(final Interpreter<V> interpreter) { 89 this.interpreter = interpreter; 90 } 91 92 /** 93 * Analyzes the given method. 94 * 95 * @param owner the internal name of the class to which 'method' belongs (see {@link 96 * Type#getInternalName()}). 97 * @param method the method to be analyzed. The maxStack and maxLocals fields must have correct 98 * values. 99 * @return the symbolic state of the execution stack frame at each bytecode instruction of the 100 * method. The size of the returned array is equal to the number of instructions (and labels) 101 * of the method. A given frame is {@literal null} if and only if the corresponding 102 * instruction cannot be reached (dead code). 103 * @throws AnalyzerException if a problem occurs during the analysis. 104 */ 105 @SuppressWarnings("unchecked") analyze(final String owner, final MethodNode method)106 public Frame<V>[] analyze(final String owner, final MethodNode method) throws AnalyzerException { 107 if ((method.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) { 108 frames = (Frame<V>[]) new Frame<?>[0]; 109 return frames; 110 } 111 insnList = method.instructions; 112 insnListSize = insnList.size(); 113 handlers = (List<TryCatchBlockNode>[]) new List<?>[insnListSize]; 114 frames = (Frame<V>[]) new Frame<?>[insnListSize]; 115 subroutines = new Subroutine[insnListSize]; 116 inInstructionsToProcess = new boolean[insnListSize]; 117 instructionsToProcess = new int[insnListSize]; 118 numInstructionsToProcess = 0; 119 120 // For each exception handler, and each instruction within its range, record in 'handlers' the 121 // fact that execution can flow from this instruction to the exception handler. 122 for (int i = 0; i < method.tryCatchBlocks.size(); ++i) { 123 TryCatchBlockNode tryCatchBlock = method.tryCatchBlocks.get(i); 124 int startIndex = insnList.indexOf(tryCatchBlock.start); 125 int endIndex = insnList.indexOf(tryCatchBlock.end); 126 for (int j = startIndex; j < endIndex; ++j) { 127 List<TryCatchBlockNode> insnHandlers = handlers[j]; 128 if (insnHandlers == null) { 129 insnHandlers = new ArrayList<>(); 130 handlers[j] = insnHandlers; 131 } 132 insnHandlers.add(tryCatchBlock); 133 } 134 } 135 136 // Finds the method's subroutines. 137 findSubroutines(method.maxLocals); 138 139 // Initializes the data structures for the control flow analysis. 140 Frame<V> currentFrame = computeInitialFrame(owner, method); 141 merge(0, currentFrame, null); 142 init(owner, method); 143 144 // Control flow analysis. 145 while (numInstructionsToProcess > 0) { 146 // Get and remove one instruction from the list of instructions to process. 147 int insnIndex = instructionsToProcess[--numInstructionsToProcess]; 148 Frame<V> oldFrame = frames[insnIndex]; 149 Subroutine subroutine = subroutines[insnIndex]; 150 inInstructionsToProcess[insnIndex] = false; 151 152 // Simulate the execution of this instruction. 153 AbstractInsnNode insnNode = null; 154 try { 155 insnNode = method.instructions.get(insnIndex); 156 int insnOpcode = insnNode.getOpcode(); 157 int insnType = insnNode.getType(); 158 159 if (insnType == AbstractInsnNode.LABEL 160 || insnType == AbstractInsnNode.LINE 161 || insnType == AbstractInsnNode.FRAME) { 162 merge(insnIndex + 1, oldFrame, subroutine); 163 newControlFlowEdge(insnIndex, insnIndex + 1); 164 } else { 165 currentFrame.init(oldFrame).execute(insnNode, interpreter); 166 subroutine = subroutine == null ? null : new Subroutine(subroutine); 167 168 if (insnNode instanceof JumpInsnNode) { 169 JumpInsnNode jumpInsn = (JumpInsnNode) insnNode; 170 if (insnOpcode != GOTO && insnOpcode != JSR) { 171 currentFrame.initJumpTarget(insnOpcode, /* target = */ null); 172 merge(insnIndex + 1, currentFrame, subroutine); 173 newControlFlowEdge(insnIndex, insnIndex + 1); 174 } 175 int jumpInsnIndex = insnList.indexOf(jumpInsn.label); 176 currentFrame.initJumpTarget(insnOpcode, jumpInsn.label); 177 if (insnOpcode == JSR) { 178 merge( 179 jumpInsnIndex, 180 currentFrame, 181 new Subroutine(jumpInsn.label, method.maxLocals, jumpInsn)); 182 } else { 183 merge(jumpInsnIndex, currentFrame, subroutine); 184 } 185 newControlFlowEdge(insnIndex, jumpInsnIndex); 186 } else if (insnNode instanceof LookupSwitchInsnNode) { 187 LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode; 188 int targetInsnIndex = insnList.indexOf(lookupSwitchInsn.dflt); 189 currentFrame.initJumpTarget(insnOpcode, lookupSwitchInsn.dflt); 190 merge(targetInsnIndex, currentFrame, subroutine); 191 newControlFlowEdge(insnIndex, targetInsnIndex); 192 for (int i = 0; i < lookupSwitchInsn.labels.size(); ++i) { 193 LabelNode label = lookupSwitchInsn.labels.get(i); 194 targetInsnIndex = insnList.indexOf(label); 195 currentFrame.initJumpTarget(insnOpcode, label); 196 merge(targetInsnIndex, currentFrame, subroutine); 197 newControlFlowEdge(insnIndex, targetInsnIndex); 198 } 199 } else if (insnNode instanceof TableSwitchInsnNode) { 200 TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnNode; 201 int targetInsnIndex = insnList.indexOf(tableSwitchInsn.dflt); 202 currentFrame.initJumpTarget(insnOpcode, tableSwitchInsn.dflt); 203 merge(targetInsnIndex, currentFrame, subroutine); 204 newControlFlowEdge(insnIndex, targetInsnIndex); 205 for (int i = 0; i < tableSwitchInsn.labels.size(); ++i) { 206 LabelNode label = tableSwitchInsn.labels.get(i); 207 currentFrame.initJumpTarget(insnOpcode, label); 208 targetInsnIndex = insnList.indexOf(label); 209 merge(targetInsnIndex, currentFrame, subroutine); 210 newControlFlowEdge(insnIndex, targetInsnIndex); 211 } 212 } else if (insnOpcode == RET) { 213 if (subroutine == null) { 214 throw new AnalyzerException(insnNode, "RET instruction outside of a subroutine"); 215 } 216 for (int i = 0; i < subroutine.callers.size(); ++i) { 217 JumpInsnNode caller = subroutine.callers.get(i); 218 int jsrInsnIndex = insnList.indexOf(caller); 219 if (frames[jsrInsnIndex] != null) { 220 merge( 221 jsrInsnIndex + 1, 222 frames[jsrInsnIndex], 223 currentFrame, 224 subroutines[jsrInsnIndex], 225 subroutine.localsUsed); 226 newControlFlowEdge(insnIndex, jsrInsnIndex + 1); 227 } 228 } 229 } else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) { 230 if (subroutine != null) { 231 if (insnNode instanceof VarInsnNode) { 232 int varIndex = ((VarInsnNode) insnNode).var; 233 subroutine.localsUsed[varIndex] = true; 234 if (insnOpcode == LLOAD 235 || insnOpcode == DLOAD 236 || insnOpcode == LSTORE 237 || insnOpcode == DSTORE) { 238 subroutine.localsUsed[varIndex + 1] = true; 239 } 240 } else if (insnNode instanceof IincInsnNode) { 241 int varIndex = ((IincInsnNode) insnNode).var; 242 subroutine.localsUsed[varIndex] = true; 243 } 244 } 245 merge(insnIndex + 1, currentFrame, subroutine); 246 newControlFlowEdge(insnIndex, insnIndex + 1); 247 } 248 } 249 250 List<TryCatchBlockNode> insnHandlers = handlers[insnIndex]; 251 if (insnHandlers != null) { 252 for (TryCatchBlockNode tryCatchBlock : insnHandlers) { 253 Type catchType; 254 if (tryCatchBlock.type == null) { 255 catchType = Type.getObjectType("java/lang/Throwable"); 256 } else { 257 catchType = Type.getObjectType(tryCatchBlock.type); 258 } 259 if (newControlFlowExceptionEdge(insnIndex, tryCatchBlock)) { 260 Frame<V> handler = newFrame(oldFrame); 261 handler.clearStack(); 262 handler.push(interpreter.newExceptionValue(tryCatchBlock, handler, catchType)); 263 merge(insnList.indexOf(tryCatchBlock.handler), handler, subroutine); 264 } 265 } 266 } 267 } catch (AnalyzerException e) { 268 throw new AnalyzerException( 269 e.node, "Error at instruction " + insnIndex + ": " + e.getMessage(), e); 270 } catch (RuntimeException e) { 271 // DontCheck(IllegalCatch): can't be fixed, for backward compatibility. 272 throw new AnalyzerException( 273 insnNode, "Error at instruction " + insnIndex + ": " + e.getMessage(), e); 274 } 275 } 276 277 return frames; 278 } 279 280 /** 281 * Analyzes the given method and computes and sets its maximum stack size and maximum number of 282 * local variables. 283 * 284 * @param owner the internal name of the class to which 'method' belongs (see {@link 285 * Type#getInternalName()}). 286 * @param method the method to be analyzed. 287 * @return the symbolic state of the execution stack frame at each bytecode instruction of the 288 * method. The size of the returned array is equal to the number of instructions (and labels) 289 * of the method. A given frame is {@literal null} if and only if the corresponding 290 * instruction cannot be reached (dead code). 291 * @throws AnalyzerException if a problem occurs during the analysis. 292 */ analyzeAndComputeMaxs(final String owner, final MethodNode method)293 public Frame<V>[] analyzeAndComputeMaxs(final String owner, final MethodNode method) 294 throws AnalyzerException { 295 method.maxLocals = computeMaxLocals(method); 296 method.maxStack = -1; 297 analyze(owner, method); 298 method.maxStack = computeMaxStack(frames); 299 return frames; 300 } 301 302 /** 303 * Computes and returns the maximum number of local variables used in the given method. 304 * 305 * @param method a method. 306 * @return the maximum number of local variables used in the given method. 307 */ computeMaxLocals(final MethodNode method)308 private static int computeMaxLocals(final MethodNode method) { 309 int maxLocals = Type.getArgumentsAndReturnSizes(method.desc) >> 2; 310 if ((method.access & Opcodes.ACC_STATIC) != 0) { 311 maxLocals -= 1; 312 } 313 for (AbstractInsnNode insnNode : method.instructions) { 314 if (insnNode instanceof VarInsnNode) { 315 int local = ((VarInsnNode) insnNode).var; 316 int size = 317 (insnNode.getOpcode() == Opcodes.LLOAD 318 || insnNode.getOpcode() == Opcodes.DLOAD 319 || insnNode.getOpcode() == Opcodes.LSTORE 320 || insnNode.getOpcode() == Opcodes.DSTORE) 321 ? 2 322 : 1; 323 maxLocals = Math.max(maxLocals, local + size); 324 } else if (insnNode instanceof IincInsnNode) { 325 int local = ((IincInsnNode) insnNode).var; 326 maxLocals = Math.max(maxLocals, local + 1); 327 } 328 } 329 return maxLocals; 330 } 331 332 /** 333 * Computes and returns the maximum stack size of a method, given its stack map frames. 334 * 335 * @param frames the stack map frames of a method. 336 * @return the maximum stack size of the given method. 337 */ computeMaxStack(final Frame<?>[] frames)338 private static int computeMaxStack(final Frame<?>[] frames) { 339 int maxStack = 0; 340 for (Frame<?> frame : frames) { 341 if (frame != null) { 342 int stackSize = 0; 343 for (int i = 0; i < frame.getStackSize(); ++i) { 344 stackSize += frame.getStack(i).getSize(); 345 } 346 maxStack = Math.max(maxStack, stackSize); 347 } 348 } 349 return maxStack; 350 } 351 352 /** 353 * Finds the subroutines of the currently analyzed method and stores them in {@link #subroutines}. 354 * 355 * @param maxLocals the maximum number of local variables of the currently analyzed method (long 356 * and double values count for two variables). 357 * @throws AnalyzerException if the control flow graph can fall off the end of the code. 358 */ findSubroutines(final int maxLocals)359 private void findSubroutines(final int maxLocals) throws AnalyzerException { 360 // For each instruction, compute the subroutine to which it belongs. 361 // Follow the main 'subroutine', and collect the jsr instructions to nested subroutines. 362 Subroutine main = new Subroutine(null, maxLocals, null); 363 List<AbstractInsnNode> jsrInsns = new ArrayList<>(); 364 findSubroutine(0, main, jsrInsns); 365 // Follow the nested subroutines, and collect their own nested subroutines, until all 366 // subroutines are found. 367 Map<LabelNode, Subroutine> jsrSubroutines = new HashMap<>(); 368 while (!jsrInsns.isEmpty()) { 369 JumpInsnNode jsrInsn = (JumpInsnNode) jsrInsns.remove(0); 370 Subroutine subroutine = jsrSubroutines.get(jsrInsn.label); 371 if (subroutine == null) { 372 subroutine = new Subroutine(jsrInsn.label, maxLocals, jsrInsn); 373 jsrSubroutines.put(jsrInsn.label, subroutine); 374 findSubroutine(insnList.indexOf(jsrInsn.label), subroutine, jsrInsns); 375 } else { 376 subroutine.callers.add(jsrInsn); 377 } 378 } 379 // Clear the main 'subroutine', which is not a real subroutine (and was used only as an 380 // intermediate step above to find the real ones). 381 for (int i = 0; i < insnListSize; ++i) { 382 if (subroutines[i] != null && subroutines[i].start == null) { 383 subroutines[i] = null; 384 } 385 } 386 } 387 388 /** 389 * Follows the control flow graph of the currently analyzed method, starting at the given 390 * instruction index, and stores a copy of the given subroutine in {@link #subroutines} for each 391 * encountered instruction. Jumps to nested subroutines are <i>not</i> followed: instead, the 392 * corresponding instructions are put in the given list. 393 * 394 * @param insnIndex an instruction index. 395 * @param subroutine a subroutine. 396 * @param jsrInsns where the jsr instructions for nested subroutines must be put. 397 * @throws AnalyzerException if the control flow graph can fall off the end of the code. 398 */ findSubroutine( final int insnIndex, final Subroutine subroutine, final List<AbstractInsnNode> jsrInsns)399 private void findSubroutine( 400 final int insnIndex, final Subroutine subroutine, final List<AbstractInsnNode> jsrInsns) 401 throws AnalyzerException { 402 ArrayList<Integer> instructionIndicesToProcess = new ArrayList<>(); 403 instructionIndicesToProcess.add(insnIndex); 404 while (!instructionIndicesToProcess.isEmpty()) { 405 int currentInsnIndex = 406 instructionIndicesToProcess.remove(instructionIndicesToProcess.size() - 1); 407 if (currentInsnIndex < 0 || currentInsnIndex >= insnListSize) { 408 throw new AnalyzerException(null, "Execution can fall off the end of the code"); 409 } 410 if (subroutines[currentInsnIndex] != null) { 411 continue; 412 } 413 subroutines[currentInsnIndex] = new Subroutine(subroutine); 414 AbstractInsnNode currentInsn = insnList.get(currentInsnIndex); 415 416 // Push the normal successors of currentInsn onto instructionIndicesToProcess. 417 if (currentInsn instanceof JumpInsnNode) { 418 if (currentInsn.getOpcode() == JSR) { 419 // Do not follow a jsr, it leads to another subroutine! 420 jsrInsns.add(currentInsn); 421 } else { 422 JumpInsnNode jumpInsn = (JumpInsnNode) currentInsn; 423 instructionIndicesToProcess.add(insnList.indexOf(jumpInsn.label)); 424 } 425 } else if (currentInsn instanceof TableSwitchInsnNode) { 426 TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) currentInsn; 427 findSubroutine(insnList.indexOf(tableSwitchInsn.dflt), subroutine, jsrInsns); 428 for (int i = tableSwitchInsn.labels.size() - 1; i >= 0; --i) { 429 LabelNode labelNode = tableSwitchInsn.labels.get(i); 430 instructionIndicesToProcess.add(insnList.indexOf(labelNode)); 431 } 432 } else if (currentInsn instanceof LookupSwitchInsnNode) { 433 LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) currentInsn; 434 findSubroutine(insnList.indexOf(lookupSwitchInsn.dflt), subroutine, jsrInsns); 435 for (int i = lookupSwitchInsn.labels.size() - 1; i >= 0; --i) { 436 LabelNode labelNode = lookupSwitchInsn.labels.get(i); 437 instructionIndicesToProcess.add(insnList.indexOf(labelNode)); 438 } 439 } 440 441 // Push the exception handler successors of currentInsn onto instructionIndicesToProcess. 442 List<TryCatchBlockNode> insnHandlers = handlers[currentInsnIndex]; 443 if (insnHandlers != null) { 444 for (TryCatchBlockNode tryCatchBlock : insnHandlers) { 445 instructionIndicesToProcess.add(insnList.indexOf(tryCatchBlock.handler)); 446 } 447 } 448 449 // Push the next instruction, if the control flow can go from currentInsn to the next. 450 switch (currentInsn.getOpcode()) { 451 case GOTO: 452 case RET: 453 case TABLESWITCH: 454 case LOOKUPSWITCH: 455 case IRETURN: 456 case LRETURN: 457 case FRETURN: 458 case DRETURN: 459 case ARETURN: 460 case RETURN: 461 case ATHROW: 462 break; 463 default: 464 instructionIndicesToProcess.add(currentInsnIndex + 1); 465 break; 466 } 467 } 468 } 469 470 /** 471 * Computes the initial execution stack frame of the given method. 472 * 473 * @param owner the internal name of the class to which 'method' belongs (see {@link 474 * Type#getInternalName()}). 475 * @param method the method to be analyzed. 476 * @return the initial execution stack frame of the 'method'. 477 */ computeInitialFrame(final String owner, final MethodNode method)478 private Frame<V> computeInitialFrame(final String owner, final MethodNode method) { 479 Frame<V> frame = newFrame(method.maxLocals, method.maxStack); 480 int currentLocal = 0; 481 boolean isInstanceMethod = (method.access & ACC_STATIC) == 0; 482 if (isInstanceMethod) { 483 Type ownerType = Type.getObjectType(owner); 484 frame.setLocal( 485 currentLocal, interpreter.newParameterValue(isInstanceMethod, currentLocal, ownerType)); 486 currentLocal++; 487 } 488 Type[] argumentTypes = Type.getArgumentTypes(method.desc); 489 for (Type argumentType : argumentTypes) { 490 frame.setLocal( 491 currentLocal, 492 interpreter.newParameterValue(isInstanceMethod, currentLocal, argumentType)); 493 currentLocal++; 494 if (argumentType.getSize() == 2) { 495 frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal)); 496 currentLocal++; 497 } 498 } 499 while (currentLocal < method.maxLocals) { 500 frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal)); 501 currentLocal++; 502 } 503 frame.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc))); 504 return frame; 505 } 506 507 /** 508 * Returns the symbolic execution stack frame for each instruction of the last analyzed method. 509 * 510 * @return the symbolic state of the execution stack frame at each bytecode instruction of the 511 * method. The size of the returned array is equal to the number of instructions (and labels) 512 * of the method. A given frame is {@literal null} if the corresponding instruction cannot be 513 * reached, or if an error occurred during the analysis of the method. 514 */ getFrames()515 public Frame<V>[] getFrames() { 516 return frames; 517 } 518 519 /** 520 * Returns the exception handlers for the given instruction. 521 * 522 * @param insnIndex the index of an instruction of the last analyzed method. 523 * @return a list of {@link TryCatchBlockNode} objects. 524 */ getHandlers(final int insnIndex)525 public List<TryCatchBlockNode> getHandlers(final int insnIndex) { 526 return handlers[insnIndex]; 527 } 528 529 /** 530 * Initializes this analyzer. This method is called just before the execution of control flow 531 * analysis loop in {@link #analyze}. The default implementation of this method does nothing. 532 * 533 * @param owner the internal name of the class to which the method belongs (see {@link 534 * Type#getInternalName()}). 535 * @param method the method to be analyzed. 536 * @throws AnalyzerException if a problem occurs. 537 */ init(final String owner, final MethodNode method)538 protected void init(final String owner, final MethodNode method) throws AnalyzerException { 539 // Nothing to do. 540 } 541 542 /** 543 * Constructs a new frame with the given size. 544 * 545 * @param numLocals the maximum number of local variables of the frame. 546 * @param numStack the maximum stack size of the frame. 547 * @return the created frame. 548 */ newFrame(final int numLocals, final int numStack)549 protected Frame<V> newFrame(final int numLocals, final int numStack) { 550 return new Frame<>(numLocals, numStack); 551 } 552 553 /** 554 * Constructs a copy of the given frame. 555 * 556 * @param frame a frame. 557 * @return the created frame. 558 */ newFrame(final Frame<? extends V> frame)559 protected Frame<V> newFrame(final Frame<? extends V> frame) { 560 return new Frame<>(frame); 561 } 562 563 /** 564 * Creates a control flow graph edge. The default implementation of this method does nothing. It 565 * can be overridden in order to construct the control flow graph of a method (this method is 566 * called by the {@link #analyze} method during its visit of the method's code). 567 * 568 * @param insnIndex an instruction index. 569 * @param successorIndex index of a successor instruction. 570 */ newControlFlowEdge(final int insnIndex, final int successorIndex)571 protected void newControlFlowEdge(final int insnIndex, final int successorIndex) { 572 // Nothing to do. 573 } 574 575 /** 576 * Creates a control flow graph edge corresponding to an exception handler. The default 577 * implementation of this method does nothing. It can be overridden in order to construct the 578 * control flow graph of a method (this method is called by the {@link #analyze} method during its 579 * visit of the method's code). 580 * 581 * @param insnIndex an instruction index. 582 * @param successorIndex index of a successor instruction. 583 * @return true if this edge must be considered in the data flow analysis performed by this 584 * analyzer, or false otherwise. The default implementation of this method always returns 585 * true. 586 */ newControlFlowExceptionEdge(final int insnIndex, final int successorIndex)587 protected boolean newControlFlowExceptionEdge(final int insnIndex, final int successorIndex) { 588 return true; 589 } 590 591 /** 592 * Creates a control flow graph edge corresponding to an exception handler. The default 593 * implementation of this method delegates to {@link #newControlFlowExceptionEdge(int, int)}. It 594 * can be overridden in order to construct the control flow graph of a method (this method is 595 * called by the {@link #analyze} method during its visit of the method's code). 596 * 597 * @param insnIndex an instruction index. 598 * @param tryCatchBlock TryCatchBlockNode corresponding to this edge. 599 * @return true if this edge must be considered in the data flow analysis performed by this 600 * analyzer, or false otherwise. The default implementation of this method delegates to {@link 601 * #newControlFlowExceptionEdge(int, int)}. 602 */ newControlFlowExceptionEdge( final int insnIndex, final TryCatchBlockNode tryCatchBlock)603 protected boolean newControlFlowExceptionEdge( 604 final int insnIndex, final TryCatchBlockNode tryCatchBlock) { 605 return newControlFlowExceptionEdge(insnIndex, insnList.indexOf(tryCatchBlock.handler)); 606 } 607 608 // ----------------------------------------------------------------------------------------------- 609 610 /** 611 * Merges the given frame and subroutine into the frame and subroutines at the given instruction 612 * index. If the frame or the subroutine at the given instruction index changes as a result of 613 * this merge, the instruction index is added to the list of instructions to process (if it is not 614 * already the case). 615 * 616 * @param insnIndex an instruction index. 617 * @param frame a frame. This frame is left unchanged by this method. 618 * @param subroutine a subroutine. This subroutine is left unchanged by this method. 619 * @throws AnalyzerException if the frames have incompatible sizes. 620 */ merge(final int insnIndex, final Frame<V> frame, final Subroutine subroutine)621 private void merge(final int insnIndex, final Frame<V> frame, final Subroutine subroutine) 622 throws AnalyzerException { 623 boolean changed; 624 Frame<V> oldFrame = frames[insnIndex]; 625 if (oldFrame == null) { 626 frames[insnIndex] = newFrame(frame); 627 changed = true; 628 } else { 629 changed = oldFrame.merge(frame, interpreter); 630 } 631 Subroutine oldSubroutine = subroutines[insnIndex]; 632 if (oldSubroutine == null) { 633 if (subroutine != null) { 634 subroutines[insnIndex] = new Subroutine(subroutine); 635 changed = true; 636 } 637 } else { 638 if (subroutine != null) { 639 changed |= oldSubroutine.merge(subroutine); 640 } 641 } 642 if (changed && !inInstructionsToProcess[insnIndex]) { 643 inInstructionsToProcess[insnIndex] = true; 644 instructionsToProcess[numInstructionsToProcess++] = insnIndex; 645 } 646 } 647 648 /** 649 * Merges the given frame and subroutine into the frame and subroutines at the given instruction 650 * index (case of a RET instruction). If the frame or the subroutine at the given instruction 651 * index changes as a result of this merge, the instruction index is added to the list of 652 * instructions to process (if it is not already the case). 653 * 654 * @param insnIndex the index of an instruction immediately following a jsr instruction. 655 * @param frameBeforeJsr the execution stack frame before the jsr instruction. This frame is 656 * merged into 'frameAfterRet'. 657 * @param frameAfterRet the execution stack frame after a ret instruction of the subroutine. This 658 * frame is merged into the frame at 'insnIndex' (after it has itself been merge with 659 * 'frameBeforeJsr'). 660 * @param subroutineBeforeJsr if the jsr is itself part of a subroutine (case of nested 661 * subroutine), the subroutine it belongs to. 662 * @param localsUsed the local variables read or written in the subroutine. 663 * @throws AnalyzerException if the frames have incompatible sizes. 664 */ merge( final int insnIndex, final Frame<V> frameBeforeJsr, final Frame<V> frameAfterRet, final Subroutine subroutineBeforeJsr, final boolean[] localsUsed)665 private void merge( 666 final int insnIndex, 667 final Frame<V> frameBeforeJsr, 668 final Frame<V> frameAfterRet, 669 final Subroutine subroutineBeforeJsr, 670 final boolean[] localsUsed) 671 throws AnalyzerException { 672 frameAfterRet.merge(frameBeforeJsr, localsUsed); 673 674 boolean changed; 675 Frame<V> oldFrame = frames[insnIndex]; 676 if (oldFrame == null) { 677 frames[insnIndex] = newFrame(frameAfterRet); 678 changed = true; 679 } else { 680 changed = oldFrame.merge(frameAfterRet, interpreter); 681 } 682 Subroutine oldSubroutine = subroutines[insnIndex]; 683 if (oldSubroutine != null && subroutineBeforeJsr != null) { 684 changed |= oldSubroutine.merge(subroutineBeforeJsr); 685 } 686 if (changed && !inInstructionsToProcess[insnIndex]) { 687 inInstructionsToProcess[insnIndex] = true; 688 instructionsToProcess[numInstructionsToProcess++] = insnIndex; 689 } 690 } 691 } 692