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