1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 package org.apache.bcel.verifier.structurals; 19 20 import java.util.ArrayList; 21 import java.util.HashMap; 22 import java.util.HashSet; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.Set; 26 27 import org.apache.bcel.generic.ASTORE; 28 import org.apache.bcel.generic.ATHROW; 29 import org.apache.bcel.generic.BranchInstruction; 30 import org.apache.bcel.generic.CodeExceptionGen; 31 import org.apache.bcel.generic.GotoInstruction; 32 import org.apache.bcel.generic.IndexedInstruction; 33 import org.apache.bcel.generic.Instruction; 34 import org.apache.bcel.generic.InstructionHandle; 35 import org.apache.bcel.generic.JsrInstruction; 36 import org.apache.bcel.generic.LocalVariableInstruction; 37 import org.apache.bcel.generic.MethodGen; 38 import org.apache.bcel.generic.RET; 39 import org.apache.bcel.generic.ReturnInstruction; 40 import org.apache.bcel.generic.Select; 41 import org.apache.bcel.verifier.exc.AssertionViolatedException; 42 import org.apache.bcel.verifier.exc.StructuralCodeConstraintException; 43 44 /** 45 * Instances of this class contain information about the subroutines 46 * found in a code array of a method. 47 * This implementation considers the top-level (the instructions 48 * reachable without a JSR or JSR_W starting off from the first 49 * instruction in a code array of a method) being a special subroutine; 50 * see getTopLevel() for that. 51 * Please note that the definition of subroutines in the Java Virtual 52 * Machine Specification, Second Edition is somewhat incomplete. 53 * Therefore, JustIce uses an own, more rigid notion. 54 * Basically, a subroutine is a piece of code that starts at the target 55 * of a JSR of JSR_W instruction and ends at a corresponding RET 56 * instruction. Note also that the control flow of a subroutine 57 * may be complex and non-linear; and that subroutines may be nested. 58 * JustIce also mandates subroutines not to be protected by exception 59 * handling code (for the sake of control flow predictability). 60 * To understand JustIce's notion of subroutines, please read 61 * 62 * TODO: refer to the paper. 63 * 64 * @version $Id$ 65 * @see #getTopLevel() 66 */ 67 public class Subroutines{ 68 /** 69 * This inner class implements the Subroutine interface. 70 */ 71 private class SubroutineImpl implements Subroutine{ 72 /** 73 * UNSET, a symbol for an uninitialized localVariable 74 * field. This is used for the "top-level" Subroutine; 75 * i.e. no subroutine. 76 */ 77 private static final int UNSET = -1; 78 79 /** 80 * The Local Variable slot where the first 81 * instruction of this subroutine (an ASTORE) stores 82 * the JsrInstruction's ReturnAddress in and 83 * the RET of this subroutine operates on. 84 */ 85 private int localVariable = UNSET; 86 87 /** The instructions that belong to this subroutine. */ 88 private final Set<InstructionHandle> instructions = new HashSet<>(); // Elements: InstructionHandle 89 90 /* 91 * Refer to the Subroutine interface for documentation. 92 */ 93 @Override contains(final InstructionHandle inst)94 public boolean contains(final InstructionHandle inst) { 95 return instructions.contains(inst); 96 } 97 98 /** 99 * The JSR or JSR_W instructions that define this 100 * subroutine by targeting it. 101 */ 102 private final Set<InstructionHandle> theJSRs = new HashSet<>(); 103 104 /** 105 * The RET instruction that leaves this subroutine. 106 */ 107 private InstructionHandle theRET; 108 109 /** 110 * Returns a String representation of this object, merely 111 * for debugging purposes. 112 * (Internal) Warning: Verbosity on a problematic subroutine may cause 113 * stack overflow errors due to recursive subSubs() calls. 114 * Don't use this, then. 115 */ 116 @Override toString()117 public String toString() { 118 final StringBuilder ret = new StringBuilder(); 119 ret.append("Subroutine: Local variable is '").append(localVariable); 120 ret.append("', JSRs are '").append(theJSRs); 121 ret.append("', RET is '").append(theRET); 122 ret.append("', Instructions: '").append(instructions).append("'."); 123 124 ret.append(" Accessed local variable slots: '"); 125 int[] alv = getAccessedLocalsIndices(); 126 for (final int element : alv) { 127 ret.append(element);ret.append(" "); 128 } 129 ret.append("'."); 130 131 ret.append(" Recursively (via subsub...routines) accessed local variable slots: '"); 132 alv = getRecursivelyAccessedLocalsIndices(); 133 for (final int element : alv) { 134 ret.append(element);ret.append(" "); 135 } 136 ret.append("'."); 137 138 return ret.toString(); 139 } 140 141 /** 142 * Sets the leaving RET instruction. Must be invoked after all instructions are added. 143 * Must not be invoked for top-level 'subroutine'. 144 */ setLeavingRET()145 void setLeavingRET() { 146 if (localVariable == UNSET) { 147 throw new AssertionViolatedException( 148 "setLeavingRET() called for top-level 'subroutine' or forgot to set local variable first."); 149 } 150 InstructionHandle ret = null; 151 for (final InstructionHandle actual : instructions) { 152 if (actual.getInstruction() instanceof RET) { 153 if (ret != null) { 154 throw new StructuralCodeConstraintException( 155 "Subroutine with more then one RET detected: '"+ret+"' and '"+actual+"'."); 156 } 157 ret = actual; 158 } 159 } 160 if (ret == null) { 161 throw new StructuralCodeConstraintException("Subroutine without a RET detected."); 162 } 163 if (((RET) ret.getInstruction()).getIndex() != localVariable) { 164 throw new StructuralCodeConstraintException( 165 "Subroutine uses '"+ret+"' which does not match the correct local variable '"+localVariable+"'."); 166 } 167 theRET = ret; 168 } 169 170 /* 171 * Refer to the Subroutine interface for documentation. 172 */ 173 @Override getEnteringJsrInstructions()174 public InstructionHandle[] getEnteringJsrInstructions() { 175 if (this == getTopLevel()) { 176 throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine."); 177 } 178 final InstructionHandle[] jsrs = new InstructionHandle[theJSRs.size()]; 179 return theJSRs.toArray(jsrs); 180 } 181 182 /** 183 * Adds a new JSR or JSR_W that has this subroutine as its target. 184 */ addEnteringJsrInstruction(final InstructionHandle jsrInst)185 public void addEnteringJsrInstruction(final InstructionHandle jsrInst) { 186 if ( (jsrInst == null) || (! (jsrInst.getInstruction() instanceof JsrInstruction))) { 187 throw new AssertionViolatedException("Expecting JsrInstruction InstructionHandle."); 188 } 189 if (localVariable == UNSET) { 190 throw new AssertionViolatedException("Set the localVariable first!"); 191 } 192 // Something is wrong when an ASTORE is targeted that does not operate on the same local variable than the rest of the 193 // JsrInstruction-targets and the RET. 194 // (We don't know out leader here so we cannot check if we're really targeted!) 195 if (localVariable != ((ASTORE) (((JsrInstruction) jsrInst.getInstruction()).getTarget().getInstruction())).getIndex()) { 196 throw new AssertionViolatedException("Setting a wrong JsrInstruction."); 197 } 198 theJSRs.add(jsrInst); 199 } 200 201 /* 202 * Refer to the Subroutine interface for documentation. 203 */ 204 @Override getLeavingRET()205 public InstructionHandle getLeavingRET() { 206 if (this == getTopLevel()) { 207 throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine."); 208 } 209 return theRET; 210 } 211 212 /* 213 * Refer to the Subroutine interface for documentation. 214 */ 215 @Override getInstructions()216 public InstructionHandle[] getInstructions() { 217 final InstructionHandle[] ret = new InstructionHandle[instructions.size()]; 218 return instructions.toArray(ret); 219 } 220 221 /* 222 * Adds an instruction to this subroutine. 223 * All instructions must have been added before invoking setLeavingRET(). 224 * @see #setLeavingRET 225 */ addInstruction(final InstructionHandle ih)226 void addInstruction(final InstructionHandle ih) { 227 if (theRET != null) { 228 throw new AssertionViolatedException("All instructions must have been added before invoking setLeavingRET()."); 229 } 230 instructions.add(ih); 231 } 232 233 /* Satisfies Subroutine.getRecursivelyAccessedLocalsIndices(). */ 234 @Override getRecursivelyAccessedLocalsIndices()235 public int[] getRecursivelyAccessedLocalsIndices() { 236 final Set<Integer> s = new HashSet<>(); 237 final int[] lvs = getAccessedLocalsIndices(); 238 for (final int lv : lvs) { 239 s.add(Integer.valueOf(lv)); 240 } 241 _getRecursivelyAccessedLocalsIndicesHelper(s, this.subSubs()); 242 final int[] ret = new int[s.size()]; 243 int j=-1; 244 for (final Integer index : s) { 245 j++; 246 ret[j] = index.intValue(); 247 } 248 return ret; 249 } 250 251 /** 252 * A recursive helper method for getRecursivelyAccessedLocalsIndices(). 253 * @see #getRecursivelyAccessedLocalsIndices() 254 */ _getRecursivelyAccessedLocalsIndicesHelper(final Set<Integer> s, final Subroutine[] subs)255 private void _getRecursivelyAccessedLocalsIndicesHelper(final Set<Integer> s, final Subroutine[] subs) { 256 for (final Subroutine sub : subs) { 257 final int[] lvs = sub.getAccessedLocalsIndices(); 258 for (final int lv : lvs) { 259 s.add(Integer.valueOf(lv)); 260 } 261 if(sub.subSubs().length != 0) { 262 _getRecursivelyAccessedLocalsIndicesHelper(s, sub.subSubs()); 263 } 264 } 265 } 266 267 /* 268 * Satisfies Subroutine.getAccessedLocalIndices(). 269 */ 270 @Override getAccessedLocalsIndices()271 public int[] getAccessedLocalsIndices() { 272 //TODO: Implement caching. 273 final Set<Integer> acc = new HashSet<>(); 274 if (theRET == null && this != getTopLevel()) { 275 throw new AssertionViolatedException( 276 "This subroutine object must be built up completely before calculating accessed locals."); 277 } 278 { 279 for (final InstructionHandle ih : instructions) { 280 // RET is not a LocalVariableInstruction in the current version of BCEL. 281 if (ih.getInstruction() instanceof LocalVariableInstruction || ih.getInstruction() instanceof RET) { 282 final int idx = ((IndexedInstruction) (ih.getInstruction())).getIndex(); 283 acc.add(Integer.valueOf(idx)); 284 // LONG? DOUBLE?. 285 try{ 286 // LocalVariableInstruction instances are typed without the need to look into 287 // the constant pool. 288 if (ih.getInstruction() instanceof LocalVariableInstruction) { 289 final int s = ((LocalVariableInstruction) ih.getInstruction()).getType(null).getSize(); 290 if (s==2) { 291 acc.add(Integer.valueOf(idx+1)); 292 } 293 } 294 } 295 catch(final RuntimeException re) { 296 throw new AssertionViolatedException("Oops. BCEL did not like NULL as a ConstantPoolGen object.", re); 297 } 298 } 299 } 300 } 301 302 { 303 final int[] ret = new int[acc.size()]; 304 int j=-1; 305 for (final Integer accessedLocal : acc) { 306 j++; 307 ret[j] = accessedLocal.intValue(); 308 } 309 return ret; 310 } 311 } 312 313 /* 314 * Satisfies Subroutine.subSubs(). 315 */ 316 @Override subSubs()317 public Subroutine[] subSubs() { 318 final Set<Subroutine> h = new HashSet<>(); 319 320 for (final InstructionHandle ih : instructions) { 321 final Instruction inst = ih.getInstruction(); 322 if (inst instanceof JsrInstruction) { 323 final InstructionHandle targ = ((JsrInstruction) inst).getTarget(); 324 h.add(getSubroutine(targ)); 325 } 326 } 327 final Subroutine[] ret = new Subroutine[h.size()]; 328 return h.toArray(ret); 329 } 330 331 /* 332 * Sets the local variable slot the ASTORE that is targeted 333 * by the JsrInstructions of this subroutine operates on. 334 * This subroutine's RET operates on that same local variable 335 * slot, of course. 336 */ setLocalVariable(final int i)337 void setLocalVariable(final int i) { 338 if (localVariable != UNSET) { 339 throw new AssertionViolatedException("localVariable set twice."); 340 } 341 localVariable = i; 342 } 343 344 /** 345 * The default constructor. 346 */ SubroutineImpl()347 public SubroutineImpl() { 348 } 349 350 }// end Inner Class SubrouteImpl 351 352 //Node coloring constants 353 private enum ColourConstants{ 354 WHITE, 355 GRAY, 356 BLACK 357 } 358 359 /** 360 * The map containing the subroutines found. 361 * Key: InstructionHandle of the leader of the subroutine. 362 * Elements: SubroutineImpl objects. 363 */ 364 private final Map<InstructionHandle, Subroutine> subroutines = new HashMap<>(); 365 366 /** 367 * This is referring to a special subroutine, namely the 368 * top level. This is not really a subroutine but we use 369 * it to distinguish between top level instructions and 370 * unreachable instructions. 371 */ 372 // CHECKSTYLE:OFF 373 public final Subroutine TOPLEVEL; // TODO can this be made private? 374 // CHECKSTYLE:ON 375 376 /** 377 * Constructor. 378 * @param mg A MethodGen object representing method to 379 * create the Subroutine objects of. 380 * Assumes that JustIce strict checks are needed. 381 */ Subroutines(final MethodGen mg)382 public Subroutines(final MethodGen mg) { 383 this(mg, true); 384 } 385 386 /** 387 * Constructor. 388 * @param mg A MethodGen object representing method to 389 * create the Subroutine objects of. 390 * @param enableJustIceCheck whether to enable additional JustIce checks 391 * @since 6.0 392 */ Subroutines(final MethodGen mg, final boolean enableJustIceCheck)393 public Subroutines(final MethodGen mg, final boolean enableJustIceCheck) { 394 final InstructionHandle[] all = mg.getInstructionList().getInstructionHandles(); 395 final CodeExceptionGen[] handlers = mg.getExceptionHandlers(); 396 397 // Define our "Toplevel" fake subroutine. 398 TOPLEVEL = new SubroutineImpl(); 399 400 // Calculate "real" subroutines. 401 final Set<InstructionHandle> sub_leaders = new HashSet<>(); // Elements: InstructionHandle 402 for (final InstructionHandle element : all) { 403 final Instruction inst = element.getInstruction(); 404 if (inst instanceof JsrInstruction) { 405 sub_leaders.add(((JsrInstruction) inst).getTarget()); 406 } 407 } 408 409 // Build up the database. 410 for (final InstructionHandle astore : sub_leaders) { 411 final SubroutineImpl sr = new SubroutineImpl(); 412 sr.setLocalVariable( ((ASTORE) (astore.getInstruction())).getIndex() ); 413 subroutines.put(astore, sr); 414 } 415 416 // Fake it a bit. We want a virtual "TopLevel" subroutine. 417 subroutines.put(all[0], TOPLEVEL); 418 sub_leaders.add(all[0]); 419 420 // Tell the subroutines about their JsrInstructions. 421 // Note that there cannot be a JSR targeting the top-level 422 // since "Jsr 0" is disallowed in Pass 3a. 423 // Instructions shared by a subroutine and the toplevel are 424 // disallowed and checked below, after the BFS. 425 for (final InstructionHandle element : all) { 426 final Instruction inst = element.getInstruction(); 427 if (inst instanceof JsrInstruction) { 428 final InstructionHandle leader = ((JsrInstruction) inst).getTarget(); 429 ((SubroutineImpl) getSubroutine(leader)).addEnteringJsrInstruction(element); 430 } 431 } 432 433 // Now do a BFS from every subroutine leader to find all the 434 // instructions that belong to a subroutine. 435 // we don't want to assign an instruction to two or more Subroutine objects. 436 final Set<InstructionHandle> instructions_assigned = new HashSet<>(); 437 438 //Graph colouring. Key: InstructionHandle, Value: ColourConstants enum . 439 final Map<InstructionHandle, ColourConstants> colors = new HashMap<>(); 440 441 final List<InstructionHandle> Q = new ArrayList<>(); 442 for (final InstructionHandle actual : sub_leaders) { 443 // Do some BFS with "actual" as the root of the graph. 444 // Init colors 445 for (final InstructionHandle element : all) { 446 colors.put(element, ColourConstants.WHITE); 447 } 448 colors.put(actual, ColourConstants.GRAY); 449 // Init Queue 450 451 Q.clear(); 452 Q.add(actual); // add(Obj) adds to the end, remove(0) removes from the start. 453 454 /* 455 * BFS ALGORITHM MODIFICATION: 456 * Start out with multiple "root" nodes, as exception handlers are starting points of top-level code, too. 457 * [why top-level? 458 * TODO: Refer to the special JustIce notion of subroutines.] 459 */ 460 if (actual == all[0]) { 461 for (final CodeExceptionGen handler : handlers) { 462 colors.put(handler.getHandlerPC(), ColourConstants.GRAY); 463 Q.add(handler.getHandlerPC()); 464 } 465 } 466 /* CONTINUE NORMAL BFS ALGORITHM */ 467 468 // Loop until Queue is empty 469 while (Q.size() != 0) { 470 final InstructionHandle u = Q.remove(0); 471 final InstructionHandle[] successors = getSuccessors(u); 472 for (final InstructionHandle successor : successors) { 473 if (colors.get(successor) == ColourConstants.WHITE) { 474 colors.put(successor, ColourConstants.GRAY); 475 Q.add(successor); 476 } 477 } 478 colors.put(u, ColourConstants.BLACK); 479 } 480 // BFS ended above. 481 for (final InstructionHandle element : all) { 482 if (colors.get(element) == ColourConstants.BLACK) { 483 ((SubroutineImpl) (actual==all[0]?getTopLevel():getSubroutine(actual))).addInstruction(element); 484 if (instructions_assigned.contains(element)) { 485 throw new StructuralCodeConstraintException("Instruction '"+element+ 486 "' is part of more than one subroutine (or of the top level and a subroutine)."); 487 } 488 instructions_assigned.add(element); 489 } 490 } 491 if (actual != all[0]) {// If we don't deal with the top-level 'subroutine' 492 ((SubroutineImpl) getSubroutine(actual)).setLeavingRET(); 493 } 494 } 495 496 if (enableJustIceCheck) { 497 // Now make sure no instruction of a Subroutine is protected by exception handling code 498 // as is mandated by JustIces notion of subroutines. 499 for (final CodeExceptionGen handler : handlers) { 500 InstructionHandle _protected = handler.getStartPC(); 501 while (_protected != handler.getEndPC().getNext()) { 502 // Note the inclusive/inclusive notation of "generic API" exception handlers! 503 for (final Subroutine sub : subroutines.values()) { 504 if (sub != subroutines.get(all[0])) { // We don't want to forbid top-level exception handlers. 505 if (sub.contains(_protected)) { 506 throw new StructuralCodeConstraintException("Subroutine instruction '"+_protected+ 507 "' is protected by an exception handler, '"+handler+ 508 "'. This is forbidden by the JustIce verifier due to its clear definition of subroutines."); 509 } 510 } 511 } 512 _protected = _protected.getNext(); 513 } 514 } 515 } 516 517 // Now make sure no subroutine is calling a subroutine 518 // that uses the same local variable for the RET as themselves 519 // (recursively). 520 // This includes that subroutines may not call themselves 521 // recursively, even not through intermediate calls to other 522 // subroutines. 523 noRecursiveCalls(getTopLevel(), new HashSet<Integer>()); 524 525 } 526 527 /** 528 * This (recursive) utility method makes sure that 529 * no subroutine is calling a subroutine 530 * that uses the same local variable for the RET as themselves 531 * (recursively). 532 * This includes that subroutines may not call themselves 533 * recursively, even not through intermediate calls to other 534 * subroutines. 535 * 536 * @throws StructuralCodeConstraintException if the above constraint is not satisfied. 537 */ noRecursiveCalls(final Subroutine sub, final Set<Integer> set)538 private void noRecursiveCalls(final Subroutine sub, final Set<Integer> set) { 539 final Subroutine[] subs = sub.subSubs(); 540 541 for (final Subroutine sub2 : subs) { 542 final int index = ((RET) (sub2.getLeavingRET().getInstruction())).getIndex(); 543 544 if (!set.add(Integer.valueOf(index))) { 545 // Don't use toString() here because of possibly infinite recursive subSubs() calls then. 546 final SubroutineImpl si = (SubroutineImpl) sub2; 547 throw new StructuralCodeConstraintException("Subroutine with local variable '"+si.localVariable+"', JSRs '"+ 548 si.theJSRs+"', RET '"+si.theRET+ 549 "' is called by a subroutine which uses the same local variable index as itself; maybe even a recursive call?"+ 550 " JustIce's clean definition of a subroutine forbids both."); 551 } 552 553 noRecursiveCalls(sub2, set); 554 555 set.remove(Integer.valueOf(index)); 556 } 557 } 558 559 /** 560 * Returns the Subroutine object associated with the given 561 * leader (that is, the first instruction of the subroutine). 562 * You must not use this to get the top-level instructions 563 * modeled as a Subroutine object. 564 * 565 * @see #getTopLevel() 566 */ getSubroutine(final InstructionHandle leader)567 public Subroutine getSubroutine(final InstructionHandle leader) { 568 final Subroutine ret = subroutines.get(leader); 569 570 if (ret == null) { 571 throw new AssertionViolatedException( 572 "Subroutine requested for an InstructionHandle that is not a leader of a subroutine."); 573 } 574 575 if (ret == TOPLEVEL) { 576 throw new AssertionViolatedException("TOPLEVEL special subroutine requested; use getTopLevel()."); 577 } 578 579 return ret; 580 } 581 582 /** 583 * Returns the subroutine object associated with the 584 * given instruction. This is a costly operation, you 585 * should consider using getSubroutine(InstructionHandle). 586 * Returns 'null' if the given InstructionHandle lies 587 * in so-called 'dead code', i.e. code that can never 588 * be executed. 589 * 590 * @see #getSubroutine(InstructionHandle) 591 * @see #getTopLevel() 592 */ subroutineOf(final InstructionHandle any)593 public Subroutine subroutineOf(final InstructionHandle any) { 594 for (final Subroutine s : subroutines.values()) { 595 if (s.contains(any)) { 596 return s; 597 } 598 } 599 System.err.println("DEBUG: Please verify '"+any.toString(true)+"' lies in dead code."); 600 return null; 601 //throw new AssertionViolatedException("No subroutine for InstructionHandle found (DEAD CODE?)."); 602 } 603 604 /** 605 * For easy handling, the piece of code that is <B>not</B> a 606 * subroutine, the top-level, is also modeled as a Subroutine 607 * object. 608 * It is a special Subroutine object where <B>you must not invoke 609 * getEnteringJsrInstructions() or getLeavingRET()</B>. 610 * 611 * @see Subroutine#getEnteringJsrInstructions() 612 * @see Subroutine#getLeavingRET() 613 */ getTopLevel()614 public Subroutine getTopLevel() { 615 return TOPLEVEL; 616 } 617 /** 618 * A utility method that calculates the successors of a given InstructionHandle 619 * <B>in the same subroutine</B>. That means, a RET does not have any successors 620 * as defined here. A JsrInstruction has its physical successor as its successor 621 * (opposed to its target) as defined here. 622 */ getSuccessors(final InstructionHandle instruction)623 private static InstructionHandle[] getSuccessors(final InstructionHandle instruction) { 624 final InstructionHandle[] empty = new InstructionHandle[0]; 625 final InstructionHandle[] single = new InstructionHandle[1]; 626 627 final Instruction inst = instruction.getInstruction(); 628 629 if (inst instanceof RET) { 630 return empty; 631 } 632 633 // Terminates method normally. 634 if (inst instanceof ReturnInstruction) { 635 return empty; 636 } 637 638 // Terminates method abnormally, because JustIce mandates 639 // subroutines not to be protected by exception handlers. 640 if (inst instanceof ATHROW) { 641 return empty; 642 } 643 644 // See method comment. 645 if (inst instanceof JsrInstruction) { 646 single[0] = instruction.getNext(); 647 return single; 648 } 649 650 if (inst instanceof GotoInstruction) { 651 single[0] = ((GotoInstruction) inst).getTarget(); 652 return single; 653 } 654 655 if (inst instanceof BranchInstruction) { 656 if (inst instanceof Select) { 657 // BCEL's getTargets() returns only the non-default targets, 658 // thanks to Eli Tilevich for reporting. 659 final InstructionHandle[] matchTargets = ((Select) inst).getTargets(); 660 final InstructionHandle[] ret = new InstructionHandle[matchTargets.length+1]; 661 ret[0] = ((Select) inst).getTarget(); 662 System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length); 663 return ret; 664 } 665 final InstructionHandle[] pair = new InstructionHandle[2]; 666 pair[0] = instruction.getNext(); 667 pair[1] = ((BranchInstruction) inst).getTarget(); 668 return pair; 669 } 670 671 // default case: Fall through. 672 single[0] = instruction.getNext(); 673 return single; 674 } 675 676 /** 677 * Returns a String representation of this object; merely for debugging puposes. 678 */ 679 @Override toString()680 public String toString() { 681 return "---\n"+subroutines+"\n---\n"; 682 } 683 } 684