1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 package com.sun.org.apache.bcel.internal.generic; 6 7 /* ==================================================================== 8 * The Apache Software License, Version 1.1 9 * 10 * Copyright (c) 2001 The Apache Software Foundation. All rights 11 * reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in 22 * the documentation and/or other materials provided with the 23 * distribution. 24 * 25 * 3. The end-user documentation included with the redistribution, 26 * if any, must include the following acknowledgment: 27 * "This product includes software developed by the 28 * Apache Software Foundation (http://www.apache.org/)." 29 * Alternately, this acknowledgment may appear in the software itself, 30 * if and wherever such third-party acknowledgments normally appear. 31 * 32 * 4. The names "Apache" and "Apache Software Foundation" and 33 * "Apache BCEL" must not be used to endorse or promote products 34 * derived from this software without prior written permission. For 35 * written permission, please contact apache@apache.org. 36 * 37 * 5. Products derived from this software may not be called "Apache", 38 * "Apache BCEL", nor may "Apache" appear in their name, without 39 * prior written permission of the Apache Software Foundation. 40 * 41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 * SUCH DAMAGE. 53 * ==================================================================== 54 * 55 * This software consists of voluntary contributions made by many 56 * individuals on behalf of the Apache Software Foundation. For more 57 * information on the Apache Software Foundation, please see 58 * <http://www.apache.org/>. 59 */ 60 61 import com.sun.org.apache.bcel.internal.Constants; 62 import com.sun.org.apache.bcel.internal.classfile.Constant; 63 import com.sun.org.apache.bcel.internal.util.ByteSequence; 64 import java.io.*; 65 import java.util.Iterator; 66 import java.util.HashMap; 67 import java.util.ArrayList; 68 69 /** 70 * This class is a container for a list of <a 71 * href="Instruction.html">Instruction</a> objects. Instructions can 72 * be appended, inserted, moved, deleted, etc.. Instructions are being 73 * wrapped into <a 74 * href="InstructionHandle.html">InstructionHandles</a> objects that 75 * are returned upon append/insert operations. They give the user 76 * (read only) access to the list structure, such that it can be traversed and 77 * manipulated in a controlled way. 78 * 79 * A list is finally dumped to a byte code array with <a 80 * href="#getByteCode()">getByteCode</a>. 81 * 82 * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> 83 * @see Instruction 84 * @see InstructionHandle 85 * @see BranchHandle 86 */ 87 public class InstructionList implements Serializable { 88 private InstructionHandle start = null, end = null; 89 private int length = 0; // number of elements in list 90 private int[] byte_positions; // byte code offsets corresponding to instructions 91 92 /** 93 * Create (empty) instruction list. 94 */ 95 public InstructionList() {} 96 97 /** 98 * Create instruction list containing one instruction. 99 * @param i initial instruction 100 */ 101 public InstructionList(Instruction i) { 102 append(i); 103 } 104 105 /** 106 * Create instruction list containing one instruction. 107 * @param i initial instruction 108 */ 109 public InstructionList(BranchInstruction i) { 110 append(i); 111 } 112 113 /** 114 * Initialize list with (nonnull) compound instruction. Consumes argument 115 * list, i.e., it becomes empty. 116 * 117 * @param c compound instruction (list) 118 */ 119 public InstructionList(CompoundInstruction c) { 120 append(c.getInstructionList()); 121 } 122 123 /** 124 * Test for empty list. 125 */ 126 public boolean isEmpty() { return start == null; } // && end == null 127 128 /** 129 * Find the target instruction (handle) that corresponds to the given target 130 * position (byte code offset). 131 * 132 * @param ihs array of instruction handles, i.e. il.getInstructionHandles() 133 * @param pos array of positions corresponding to ihs, i.e. il.getInstructionPositions() 134 * @param count length of arrays 135 * @param target target position to search for 136 * @return target position's instruction handle if available 137 */ 138 public static InstructionHandle findHandle(InstructionHandle[] ihs, 139 int[] pos, int count, 140 int target) { 141 int l=0, r = count - 1; 142 143 /* Do a binary search since the pos array is orderd. 144 */ 145 do { 146 int i = (l + r) / 2; 147 int j = pos[i]; 148 149 if(j == target) // target found 150 return ihs[i]; 151 else if(target < j) // else constrain search area 152 r = i - 1; 153 else // target > j 154 l = i + 1; 155 } while(l <= r); 156 157 return null; 158 } 159 160 /** 161 * Get instruction handle for instruction at byte code position pos. 162 * This only works properly, if the list is freshly initialized from a byte array or 163 * setPositions() has been called before this method. 164 * 165 * @param pos byte code position to search for 166 * @return target position's instruction handle if available 167 */ 168 public InstructionHandle findHandle(int pos) { 169 InstructionHandle[] ihs = getInstructionHandles(); 170 return findHandle(ihs, byte_positions, length, pos); 171 } 172 173 /** 174 * Initialize instruction list from byte array. 175 * 176 * @param code byte array containing the instructions 177 */ 178 public InstructionList(byte[] code) { 179 ByteSequence bytes = new ByteSequence(code); 180 InstructionHandle[] ihs = new InstructionHandle[code.length]; 181 int[] pos = new int[code.length]; // Can't be more than that 182 int count = 0; // Contains actual length 183 184 /* Pass 1: Create an object for each byte code and append them 185 * to the list. 186 */ 187 try { 188 while(bytes.available() > 0) { 189 // Remember byte offset and associate it with the instruction 190 int off = bytes.getIndex(); 191 pos[count] = off; 192 193 /* Read one instruction from the byte stream, the byte position is set 194 * accordingly. 195 */ 196 Instruction i = Instruction.readInstruction(bytes); 197 InstructionHandle ih; 198 if(i instanceof BranchInstruction) // Use proper append() method 199 ih = append((BranchInstruction)i); 200 else 201 ih = append(i); 202 203 ih.setPosition(off); 204 ihs[count] = ih; 205 206 count++; 207 } 208 } catch(IOException e) { throw new ClassGenException(e.toString()); } 209 210 byte_positions = new int[count]; // Trim to proper size 211 System.arraycopy(pos, 0, byte_positions, 0, count); 212 213 /* Pass 2: Look for BranchInstruction and update their targets, i.e., 214 * convert offsets to instruction handles. 215 */ 216 for(int i=0; i < count; i++) { 217 if(ihs[i] instanceof BranchHandle) { 218 BranchInstruction bi = (BranchInstruction)ihs[i].instruction; 219 int target = bi.position + bi.getIndex(); /* Byte code position: 220 * relative -> absolute. */ 221 // Search for target position 222 InstructionHandle ih = findHandle(ihs, pos, count, target); 223 224 if(ih == null) // Search failed 225 throw new ClassGenException("Couldn't find target for branch: " + bi); 226 227 bi.setTarget(ih); // Update target 228 229 // If it is a Select instruction, update all branch targets 230 if(bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH 231 Select s = (Select)bi; 232 int[] indices = s.getIndices(); 233 234 for(int j=0; j < indices.length; j++) { 235 target = bi.position + indices[j]; 236 ih = findHandle(ihs, pos, count, target); 237 238 if(ih == null) // Search failed 239 throw new ClassGenException("Couldn't find target for switch: " + bi); 240 241 s.setTarget(j, ih); // Update target 242 } 243 } 244 } 245 } 246 } 247 248 /** 249 * Append another list after instruction (handle) ih contained in this list. 250 * Consumes argument list, i.e., it becomes empty. 251 * 252 * @param ih where to append the instruction list 253 * @param il Instruction list to append to this one 254 * @return instruction handle pointing to the <B>first</B> appended instruction 255 */ 256 public InstructionHandle append(InstructionHandle ih, InstructionList il) { 257 if(il == null) 258 throw new ClassGenException("Appending null InstructionList"); 259 260 if(il.isEmpty()) // Nothing to do 261 return ih; 262 263 InstructionHandle next = ih.next, ret = il.start; 264 265 ih.next = il.start; 266 il.start.prev = ih; 267 268 il.end.next = next; 269 270 if(next != null) // i == end ? 271 next.prev = il.end; 272 else 273 end = il.end; // Update end ... 274 275 length += il.length; // Update length 276 277 il.clear(); 278 279 return ret; 280 } 281 282 /** 283 * Append another list after instruction i contained in this list. 284 * Consumes argument list, i.e., it becomes empty. 285 * 286 * @param i where to append the instruction list 287 * @param il Instruction list to append to this one 288 * @return instruction handle pointing to the <B>first</B> appended instruction 289 */ 290 public InstructionHandle append(Instruction i, InstructionList il) { 291 InstructionHandle ih; 292 293 if((ih = findInstruction2(i)) == null) // Also applies for empty list 294 throw new ClassGenException("Instruction " + i + 295 " is not contained in this list."); 296 297 return append(ih, il); 298 } 299 300 /** 301 * Append another list to this one. 302 * Consumes argument list, i.e., it becomes empty. 303 * 304 * @param il list to append to end of this list 305 * @return instruction handle of the <B>first</B> appended instruction 306 */ 307 public InstructionHandle append(InstructionList il) { 308 if(il == null) 309 throw new ClassGenException("Appending null InstructionList"); 310 311 if(il.isEmpty()) // Nothing to do 312 return null; 313 314 if(isEmpty()) { 315 start = il.start; 316 end = il.end; 317 length = il.length; 318 319 il.clear(); 320 321 return start; 322 } else 323 return append(end, il); // was end.instruction 324 } 325 326 /** 327 * Append an instruction to the end of this list. 328 * 329 * @param ih instruction to append 330 */ 331 private void append(InstructionHandle ih) { 332 if(isEmpty()) { 333 start = end = ih; 334 ih.next = ih.prev = null; 335 } 336 else { 337 end.next = ih; 338 ih.prev = end; 339 ih.next = null; 340 end = ih; 341 } 342 343 length++; // Update length 344 } 345 346 /** 347 * Append an instruction to the end of this list. 348 * 349 * @param i instruction to append 350 * @return instruction handle of the appended instruction 351 */ 352 public InstructionHandle append(Instruction i) { 353 InstructionHandle ih = InstructionHandle.getInstructionHandle(i); 354 append(ih); 355 356 return ih; 357 } 358 359 /** 360 * Append a branch instruction to the end of this list. 361 * 362 * @param i branch instruction to append 363 * @return branch instruction handle of the appended instruction 364 */ 365 public BranchHandle append(BranchInstruction i) { 366 BranchHandle ih = BranchHandle.getBranchHandle(i); 367 append(ih); 368 369 return ih; 370 } 371 372 /** 373 * Append a single instruction j after another instruction i, which 374 * must be in this list of course! 375 * 376 * @param i Instruction in list 377 * @param j Instruction to append after i in list 378 * @return instruction handle of the first appended instruction 379 */ 380 public InstructionHandle append(Instruction i, Instruction j) { 381 return append(i, new InstructionList(j)); 382 } 383 384 /** 385 * Append a compound instruction, after instruction i. 386 * 387 * @param i Instruction in list 388 * @param c The composite instruction (containing an InstructionList) 389 * @return instruction handle of the first appended instruction 390 */ 391 public InstructionHandle append(Instruction i, CompoundInstruction c) { 392 return append(i, c.getInstructionList()); 393 } 394 395 /** 396 * Append a compound instruction. 397 * 398 * @param c The composite instruction (containing an InstructionList) 399 * @return instruction handle of the first appended instruction 400 */ 401 public InstructionHandle append(CompoundInstruction c) { 402 return append(c.getInstructionList()); 403 } 404 405 /** 406 * Append a compound instruction. 407 * 408 * @param ih where to append the instruction list 409 * @param c The composite instruction (containing an InstructionList) 410 * @return instruction handle of the first appended instruction 411 */ 412 public InstructionHandle append(InstructionHandle ih, CompoundInstruction c) { 413 return append(ih, c.getInstructionList()); 414 } 415 416 /** 417 * Append an instruction after instruction (handle) ih contained in this list. 418 * 419 * @param ih where to append the instruction list 420 * @param i Instruction to append 421 * @return instruction handle pointing to the <B>first</B> appended instruction 422 */ 423 public InstructionHandle append(InstructionHandle ih, Instruction i) { 424 return append(ih, new InstructionList(i)); 425 } 426 427 /** 428 * Append an instruction after instruction (handle) ih contained in this list. 429 * 430 * @param ih where to append the instruction list 431 * @param i Instruction to append 432 * @return instruction handle pointing to the <B>first</B> appended instruction 433 */ 434 public BranchHandle append(InstructionHandle ih, BranchInstruction i) { 435 BranchHandle bh = BranchHandle.getBranchHandle(i); 436 InstructionList il = new InstructionList(); 437 il.append(bh); 438 439 append(ih, il); 440 441 return bh; 442 } 443 444 /** 445 * Insert another list before Instruction handle ih contained in this list. 446 * Consumes argument list, i.e., it becomes empty. 447 * 448 * @param i where to append the instruction list 449 * @param il Instruction list to insert 450 * @return instruction handle of the first inserted instruction 451 */ 452 public InstructionHandle insert(InstructionHandle ih, InstructionList il) { 453 if(il == null) 454 throw new ClassGenException("Inserting null InstructionList"); 455 456 if(il.isEmpty()) // Nothing to do 457 return ih; 458 459 InstructionHandle prev = ih.prev, ret = il.start; 460 461 ih.prev = il.end; 462 il.end.next = ih; 463 464 il.start.prev = prev; 465 466 if(prev != null) // ih == start ? 467 prev.next = il.start; 468 else 469 start = il.start; // Update start ... 470 471 length += il.length; // Update length 472 473 il.clear(); 474 475 return ret; 476 } 477 478 /** 479 * Insert another list. 480 * 481 * @param il list to insert before start of this list 482 * @return instruction handle of the first inserted instruction 483 */ 484 public InstructionHandle insert(InstructionList il) { 485 if(isEmpty()) { 486 append(il); // Code is identical for this case 487 return start; 488 } 489 else 490 return insert(start, il); 491 } 492 493 /** 494 * Insert an instruction at start of this list. 495 * 496 * @param ih instruction to insert 497 */ 498 private void insert(InstructionHandle ih) { 499 if(isEmpty()) { 500 start = end = ih; 501 ih.next = ih.prev = null; 502 } else { 503 start.prev = ih; 504 ih.next = start; 505 ih.prev = null; 506 start = ih; 507 } 508 509 length++; 510 } 511 512 /** 513 * Insert another list before Instruction i contained in this list. 514 * Consumes argument list, i.e., it becomes empty. 515 * 516 * @param i where to append the instruction list 517 * @param il Instruction list to insert 518 * @return instruction handle pointing to the first inserted instruction, 519 * i.e., il.getStart() 520 */ 521 public InstructionHandle insert(Instruction i, InstructionList il) { 522 InstructionHandle ih; 523 524 if((ih = findInstruction1(i)) == null) 525 throw new ClassGenException("Instruction " + i + 526 " is not contained in this list."); 527 528 return insert(ih, il); 529 } 530 531 /** 532 * Insert an instruction at start of this list. 533 * 534 * @param i instruction to insert 535 * @return instruction handle of the inserted instruction 536 */ 537 public InstructionHandle insert(Instruction i) { 538 InstructionHandle ih = InstructionHandle.getInstructionHandle(i); 539 insert(ih); 540 541 return ih; 542 } 543 544 /** 545 * Insert a branch instruction at start of this list. 546 * 547 * @param i branch instruction to insert 548 * @return branch instruction handle of the appended instruction 549 */ 550 public BranchHandle insert(BranchInstruction i) { 551 BranchHandle ih = BranchHandle.getBranchHandle(i); 552 insert(ih); 553 return ih; 554 } 555 556 /** 557 * Insert a single instruction j before another instruction i, which 558 * must be in this list of course! 559 * 560 * @param i Instruction in list 561 * @param j Instruction to insert before i in list 562 * @return instruction handle of the first inserted instruction 563 */ 564 public InstructionHandle insert(Instruction i, Instruction j) { 565 return insert(i, new InstructionList(j)); 566 } 567 568 /** 569 * Insert a compound instruction before instruction i. 570 * 571 * @param i Instruction in list 572 * @param c The composite instruction (containing an InstructionList) 573 * @return instruction handle of the first inserted instruction 574 */ 575 public InstructionHandle insert(Instruction i, CompoundInstruction c) { 576 return insert(i, c.getInstructionList()); 577 } 578 579 /** 580 * Insert a compound instruction. 581 * 582 * @param c The composite instruction (containing an InstructionList) 583 * @return instruction handle of the first inserted instruction 584 */ 585 public InstructionHandle insert(CompoundInstruction c) { 586 return insert(c.getInstructionList()); 587 } 588 589 /** 590 * Insert an instruction before instruction (handle) ih contained in this list. 591 * 592 * @param ih where to insert to the instruction list 593 * @param i Instruction to insert 594 * @return instruction handle of the first inserted instruction 595 */ 596 public InstructionHandle insert(InstructionHandle ih, Instruction i) { 597 return insert(ih, new InstructionList(i)); 598 } 599 600 /** 601 * Insert a compound instruction. 602 * 603 * @param ih where to insert the instruction list 604 * @param c The composite instruction (containing an InstructionList) 605 * @return instruction handle of the first inserted instruction 606 */ 607 public InstructionHandle insert(InstructionHandle ih, CompoundInstruction c) { 608 return insert(ih, c.getInstructionList()); 609 } 610 611 /** 612 * Insert an instruction before instruction (handle) ih contained in this list. 613 * 614 * @param ih where to insert to the instruction list 615 * @param i Instruction to insert 616 * @return instruction handle of the first inserted instruction 617 */ 618 public BranchHandle insert(InstructionHandle ih, BranchInstruction i) { 619 BranchHandle bh = BranchHandle.getBranchHandle(i); 620 InstructionList il = new InstructionList(); 621 il.append(bh); 622 623 insert(ih, il); 624 625 return bh; 626 } 627 628 /** 629 * Take all instructions (handles) from "start" to "end" and append them after the 630 * new location "target". Of course, "end" must be after "start" and target must 631 * not be located withing this range. If you want to move something to the start of 632 * the list use null as value for target.<br> 633 * Any instruction targeters pointing to handles within the block, keep their targets. 634 * 635 * @param start of moved block 636 * @param end of moved block 637 * @param target of moved block 638 */ 639 public void move(InstructionHandle start, InstructionHandle end, InstructionHandle target) { 640 // Step 1: Check constraints 641 642 if((start == null) || (end == null)) 643 throw new ClassGenException("Invalid null handle: From " + start + " to " + end); 644 645 if((target == start) || (target == end)) 646 throw new ClassGenException("Invalid range: From " + start + " to " + end + 647 " contains target " + target); 648 649 for(InstructionHandle ih = start; ih != end.next; ih = ih.next) { 650 if(ih == null) // At end of list, end not found yet 651 throw new ClassGenException("Invalid range: From " + start + " to " + end); 652 else if(ih == target) // target may be null 653 throw new ClassGenException("Invalid range: From " + start + " to " + end + 654 " contains target " + target); 655 } 656 657 // Step 2: Temporarily remove the given instructions from the list 658 659 InstructionHandle prev = start.prev, next = end.next; 660 661 if(prev != null) 662 prev.next = next; 663 else // start == this.start! 664 this.start = next; 665 666 if(next != null) 667 next.prev = prev; 668 else // end == this.end! 669 this.end = prev; 670 671 start.prev = end.next = null; 672 673 // Step 3: append after target 674 675 if(target == null) { // append to start of list 676 end.next = this.start; 677 this.start = start; 678 } else { 679 next = target.next; 680 681 target.next = start; 682 start.prev = target; 683 end.next = next; 684 685 if(next != null) 686 next.prev = end; 687 } 688 } 689 690 /** 691 * Move a single instruction (handle) to a new location. 692 * 693 * @param ih moved instruction 694 * @param target new location of moved instruction 695 */ 696 public void move(InstructionHandle ih, InstructionHandle target) { 697 move(ih, ih, target); 698 } 699 700 /** 701 * Remove from instruction `prev' to instruction `next' both contained 702 * in this list. Throws TargetLostException when one of the removed instruction handles 703 * is still being targeted. 704 * 705 * @param prev where to start deleting (predecessor, exclusive) 706 * @param next where to end deleting (successor, exclusive) 707 */ 708 private void remove(InstructionHandle prev, InstructionHandle next) 709 throws TargetLostException 710 { 711 InstructionHandle first, last; // First and last deleted instruction 712 713 if((prev == null) && (next == null)) { // singleton list 714 first = last = start; 715 start = end = null; 716 } else { 717 if(prev == null) { // At start of list 718 first = start; 719 start = next; 720 } else { 721 first = prev.next; 722 prev.next = next; 723 } 724 725 if(next == null) { // At end of list 726 last = end; 727 end = prev; 728 } else { 729 last = next.prev; 730 next.prev = prev; 731 } 732 } 733 734 first.prev = null; // Completely separated from rest of list 735 last.next = null; 736 737 ArrayList target_vec = new ArrayList(); 738 739 for(InstructionHandle ih=first; ih != null; ih = ih.next) 740 ih.getInstruction().dispose(); // e.g. BranchInstructions release their targets 741 742 StringBuffer buf = new StringBuffer("{ "); 743 for(InstructionHandle ih=first; ih != null; ih = next) { 744 next = ih.next; 745 length--; 746 747 if(ih.hasTargeters()) { // Still got targeters? 748 target_vec.add(ih); 749 buf.append(ih.toString(true) + " "); 750 ih.next = ih.prev = null; 751 } else 752 ih.dispose(); 753 } 754 755 buf.append("}"); 756 757 if(!target_vec.isEmpty()) { 758 InstructionHandle[] targeted = new InstructionHandle[target_vec.size()]; 759 target_vec.toArray(targeted); 760 throw new TargetLostException(targeted, buf.toString()); 761 } 762 } 763 764 /** 765 * Remove instruction from this list. The corresponding Instruction 766 * handles must not be reused! 767 * 768 * @param ih instruction (handle) to remove 769 */ 770 public void delete(InstructionHandle ih) throws TargetLostException { 771 remove(ih.prev, ih.next); 772 } 773 774 /** 775 * Remove instruction from this list. The corresponding Instruction 776 * handles must not be reused! 777 * 778 * @param i instruction to remove 779 */ 780 public void delete(Instruction i) throws TargetLostException { 781 InstructionHandle ih; 782 783 if((ih = findInstruction1(i)) == null) 784 throw new ClassGenException("Instruction " + i + 785 " is not contained in this list."); 786 delete(ih); 787 } 788 789 /** 790 * Remove instructions from instruction `from' to instruction `to' contained 791 * in this list. The user must ensure that `from' is an instruction before 792 * `to', or risk havoc. The corresponding Instruction handles must not be reused! 793 * 794 * @param from where to start deleting (inclusive) 795 * @param to where to end deleting (inclusive) 796 */ 797 public void delete(InstructionHandle from, InstructionHandle to) 798 throws TargetLostException 799 { 800 remove(from.prev, to.next); 801 } 802 803 /** 804 * Remove instructions from instruction `from' to instruction `to' contained 805 * in this list. The user must ensure that `from' is an instruction before 806 * `to', or risk havoc. The corresponding Instruction handles must not be reused! 807 * 808 * @param from where to start deleting (inclusive) 809 * @param to where to end deleting (inclusive) 810 */ 811 public void delete(Instruction from, Instruction to) throws TargetLostException { 812 InstructionHandle from_ih, to_ih; 813 814 if((from_ih = findInstruction1(from)) == null) 815 throw new ClassGenException("Instruction " + from + 816 " is not contained in this list."); 817 818 if((to_ih = findInstruction2(to)) == null) 819 throw new ClassGenException("Instruction " + to + 820 " is not contained in this list."); 821 delete(from_ih, to_ih); 822 } 823 824 /** 825 * Search for given Instruction reference, start at beginning of list. 826 * 827 * @param i instruction to search for 828 * @return instruction found on success, null otherwise 829 */ 830 private InstructionHandle findInstruction1(Instruction i) { 831 for(InstructionHandle ih=start; ih != null; ih = ih.next) 832 if(ih.instruction == i) 833 return ih; 834 835 return null; 836 } 837 838 /** 839 * Search for given Instruction reference, start at end of list 840 * 841 * @param i instruction to search for 842 * @return instruction found on success, null otherwise 843 */ 844 private InstructionHandle findInstruction2(Instruction i) { 845 for(InstructionHandle ih=end; ih != null; ih = ih.prev) 846 if(ih.instruction == i) 847 return ih; 848 849 return null; 850 } 851 852 public boolean contains(InstructionHandle i) { 853 if(i == null) 854 return false; 855 856 for(InstructionHandle ih=start; ih != null; ih = ih.next) 857 if(ih == i) 858 return true; 859 860 return false; 861 } 862 863 public boolean contains(Instruction i) { 864 return findInstruction1(i) != null; 865 } 866 867 public void setPositions() { 868 setPositions(false); 869 } 870 871 /** 872 * Give all instructions their position number (offset in byte stream), i.e., 873 * make the list ready to be dumped. 874 * 875 * @param check Perform sanity checks, e.g. if all targeted instructions really belong 876 * to this list 877 */ 878 public void setPositions(boolean check) { 879 int max_additional_bytes = 0, additional_bytes = 0; 880 int index = 0, count = 0; 881 int[] pos = new int[length]; 882 883 /* Pass 0: Sanity checks 884 */ 885 if(check) { 886 for(InstructionHandle ih=start; ih != null; ih = ih.next) { 887 Instruction i = ih.instruction; 888 889 if(i instanceof BranchInstruction) { // target instruction within list? 890 Instruction inst = ((BranchInstruction)i).getTarget().instruction; 891 if(!contains(inst)) 892 throw new ClassGenException("Branch target of " + 893 Constants.OPCODE_NAMES[i.opcode] + ":" + 894 inst + " not in instruction list"); 895 896 if(i instanceof Select) { 897 InstructionHandle[] targets = ((Select)i).getTargets(); 898 899 for(int j=0; j < targets.length; j++) { 900 inst = targets[j].instruction; 901 if(!contains(inst)) 902 throw new ClassGenException("Branch target of " + 903 Constants.OPCODE_NAMES[i.opcode] + ":" + 904 inst + " not in instruction list"); 905 } 906 } 907 908 if(!(ih instanceof BranchHandle)) 909 throw new ClassGenException("Branch instruction " + 910 Constants.OPCODE_NAMES[i.opcode] + ":" + 911 inst + " not contained in BranchHandle."); 912 913 } 914 } 915 } 916 917 /* Pass 1: Set position numbers and sum up the maximum number of bytes an 918 * instruction may be shifted. 919 */ 920 for(InstructionHandle ih=start; ih != null; ih = ih.next) { 921 Instruction i = ih.instruction; 922 923 ih.setPosition(index); 924 pos[count++] = index; 925 926 /* Get an estimate about how many additional bytes may be added, because 927 * BranchInstructions may have variable length depending on the target 928 * offset (short vs. int) or alignment issues (TABLESWITCH and 929 * LOOKUPSWITCH). 930 */ 931 switch(i.getOpcode()) { 932 case Constants.JSR: case Constants.GOTO: 933 max_additional_bytes += 2; 934 break; 935 936 case Constants.TABLESWITCH: case Constants.LOOKUPSWITCH: 937 max_additional_bytes += 3; 938 break; 939 } 940 941 index += i.getLength(); 942 } 943 944 /* Pass 2: Expand the variable-length (Branch)Instructions depending on 945 * the target offset (short or int) and ensure that branch targets are 946 * within this list. 947 */ 948 for(InstructionHandle ih=start; ih != null; ih = ih.next) 949 additional_bytes += ih.updatePosition(additional_bytes, max_additional_bytes); 950 951 /* Pass 3: Update position numbers (which may have changed due to the 952 * preceding expansions), like pass 1. 953 */ 954 index=count=0; 955 for(InstructionHandle ih=start; ih != null; ih = ih.next) { 956 Instruction i = ih.instruction; 957 958 ih.setPosition(index); 959 pos[count++] = index; 960 index += i.getLength(); 961 } 962 963 byte_positions = new int[count]; // Trim to proper size 964 System.arraycopy(pos, 0, byte_positions, 0, count); 965 } 966 967 /** 968 * When everything is finished, use this method to convert the instruction 969 * list into an array of bytes. 970 * 971 * @return the byte code ready to be dumped 972 */ 973 public byte[] getByteCode() { 974 // Update position indices of instructions 975 setPositions(); 976 977 ByteArrayOutputStream b = new ByteArrayOutputStream(); 978 DataOutputStream out = new DataOutputStream(b); 979 980 try { 981 for(InstructionHandle ih=start; ih != null; ih = ih.next) { 982 Instruction i = ih.instruction; 983 i.dump(out); // Traverse list 984 } 985 } catch(IOException e) { 986 System.err.println(e); 987 return null; 988 } 989 990 return b.toByteArray(); 991 } 992 993 /** 994 * @return an array of instructions without target information for branch instructions. 995 */ 996 public Instruction[] getInstructions() { 997 ByteSequence bytes = new ByteSequence(getByteCode()); 998 ArrayList instructions = new ArrayList(); 999 1000 try { 1001 while(bytes.available() > 0) { 1002 instructions.add(Instruction.readInstruction(bytes)); 1003 } 1004 } catch(IOException e) { throw new ClassGenException(e.toString()); } 1005 1006 Instruction[] result = new Instruction[instructions.size()]; 1007 instructions.toArray(result); 1008 return result; 1009 } 1010 1011 public String toString() { 1012 return toString(true); 1013 } 1014 1015 /** 1016 * @param verbose toggle output format 1017 * @return String containing all instructions in this list. 1018 */ 1019 public String toString(boolean verbose) { 1020 StringBuffer buf = new StringBuffer(); 1021 1022 for(InstructionHandle ih=start; ih != null; ih = ih.next) { 1023 buf.append(ih.toString(verbose) + "\n"); 1024 } 1025 1026 return buf.toString(); 1027 } 1028 1029 /** 1030 * @return Enumeration that lists all instructions (handles) 1031 */ 1032 public Iterator iterator() { 1033 return new Iterator() { 1034 private InstructionHandle ih = start; 1035 1036 public Object next() { 1037 InstructionHandle i = ih; 1038 ih = ih.next; 1039 return i; 1040 } 1041 1042 public void remove() { 1043 throw new UnsupportedOperationException(); 1044 } 1045 1046 public boolean hasNext() { return ih != null; } 1047 }; 1048 } 1049 1050 /** 1051 * @return array containing all instructions (handles) 1052 */ 1053 public InstructionHandle[] getInstructionHandles() { 1054 InstructionHandle[] ihs = new InstructionHandle[length]; 1055 InstructionHandle ih = start; 1056 1057 for(int i=0; i < length; i++) { 1058 ihs[i] = ih; 1059 ih = ih.next; 1060 } 1061 1062 return ihs; 1063 } 1064 1065 /** 1066 * Get positions (offsets) of all instructions in the list. This relies on that 1067 * the list has been freshly created from an byte code array, or that setPositions() 1068 * has been called. Otherwise this may be inaccurate. 1069 * 1070 * @return array containing all instruction's offset in byte code 1071 */ 1072 public int[] getInstructionPositions() { return byte_positions; } 1073 1074 /** 1075 * @return complete, i.e., deep copy of this list 1076 */ 1077 public InstructionList copy() { 1078 HashMap map = new HashMap(); 1079 InstructionList il = new InstructionList(); 1080 1081 /* Pass 1: Make copies of all instructions, append them to the new list 1082 * and associate old instruction references with the new ones, i.e., 1083 * a 1:1 mapping. 1084 */ 1085 for(InstructionHandle ih=start; ih != null; ih = ih.next) { 1086 Instruction i = ih.instruction; 1087 Instruction c = i.copy(); // Use clone for shallow copy 1088 1089 if(c instanceof BranchInstruction) 1090 map.put(ih, il.append((BranchInstruction)c)); 1091 else 1092 map.put(ih, il.append(c)); 1093 } 1094 1095 /* Pass 2: Update branch targets. 1096 */ 1097 InstructionHandle ih=start; 1098 InstructionHandle ch=il.start; 1099 1100 while(ih != null) { 1101 Instruction i = ih.instruction; 1102 Instruction c = ch.instruction; 1103 1104 if(i instanceof BranchInstruction) { 1105 BranchInstruction bi = (BranchInstruction)i; 1106 BranchInstruction bc = (BranchInstruction)c; 1107 InstructionHandle itarget = bi.getTarget(); // old target 1108 1109 // New target is in hash map 1110 bc.setTarget((InstructionHandle)map.get(itarget)); 1111 1112 if(bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH 1113 InstructionHandle[] itargets = ((Select)bi).getTargets(); 1114 InstructionHandle[] ctargets = ((Select)bc).getTargets(); 1115 1116 for(int j=0; j < itargets.length; j++) { // Update all targets 1117 ctargets[j] = (InstructionHandle)map.get(itargets[j]); 1118 } 1119 } 1120 } 1121 1122 ih = ih.next; 1123 ch = ch.next; 1124 } 1125 1126 return il; 1127 } 1128 1129 /** Replace all references to the old constant pool with references to the new 1130 * constant pool 1131 */ 1132 public void replaceConstantPool(ConstantPoolGen old_cp, ConstantPoolGen new_cp) { 1133 for(InstructionHandle ih=start; ih != null; ih = ih.next) { 1134 Instruction i = ih.instruction; 1135 1136 if(i instanceof CPInstruction) { 1137 CPInstruction ci = (CPInstruction)i; 1138 Constant c = old_cp.getConstant(ci.getIndex()); 1139 ci.setIndex(new_cp.addConstant(c, old_cp)); 1140 } 1141 } 1142 } 1143 1144 private void clear() { 1145 start = end = null; 1146 length = 0; 1147 } 1148 1149 /** 1150 * Delete contents of list. Provides besser memory utilization, 1151 * because the system then may reuse the instruction handles. This 1152 * method is typically called right after 1153 * <href="MethodGen.html#getMethod()">MethodGen.getMethod()</a>. 1154 */ 1155 public void dispose() { 1156 // Traverse in reverse order, because ih.next is overwritten 1157 for(InstructionHandle ih=end; ih != null; ih = ih.prev) 1158 /* Causes BranchInstructions to release target and targeters, because it 1159 * calls dispose() on the contained instruction. 1160 */ 1161 ih.dispose(); 1162 1163 clear(); 1164 } 1165 1166 /** 1167 * @return start of list 1168 */ 1169 public InstructionHandle getStart() { return start; } 1170 1171 /** 1172 * @return end of list 1173 */ 1174 public InstructionHandle getEnd() { return end; } 1175 1176 /** 1177 * @return length of list (Number of instructions, not bytes) 1178 */ 1179 public int getLength() { return length; } 1180 1181 /** 1182 * @return length of list (Number of instructions, not bytes) 1183 */ 1184 public int size() { return length; } 1185 1186 /** 1187 * Redirect all references from old_target to new_target, i.e., update targets 1188 * of branch instructions. 1189 * 1190 * @param old_target the old target instruction handle 1191 * @param new_target the new target instruction handle 1192 */ 1193 public void redirectBranches(InstructionHandle old_target, 1194 InstructionHandle new_target) { 1195 for(InstructionHandle ih = start; ih != null; ih = ih.next) { 1196 Instruction i = ih.getInstruction(); 1197 1198 if(i instanceof BranchInstruction) { 1199 BranchInstruction b = (BranchInstruction)i; 1200 InstructionHandle target = b.getTarget(); 1201 1202 if(target == old_target) 1203 b.setTarget(new_target); 1204 1205 if(b instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH 1206 InstructionHandle[] targets = ((Select)b).getTargets(); 1207 1208 for(int j=0; j < targets.length; j++) // Update targets 1209 if(targets[j] == old_target) 1210 ((Select)b).setTarget(j, new_target); 1211 } 1212 } 1213 } 1214 } 1215 1216 /** 1217 * Redirect all references of local variables from old_target to new_target. 1218 * 1219 * @param lg array of local variables 1220 * @param old_target the old target instruction handle 1221 * @param new_target the new target instruction handle 1222 * @see MethodGen 1223 */ 1224 public void redirectLocalVariables(LocalVariableGen[] lg, 1225 InstructionHandle old_target, 1226 InstructionHandle new_target) { 1227 for(int i=0; i < lg.length; i++) { 1228 InstructionHandle start = lg[i].getStart(); 1229 InstructionHandle end = lg[i].getEnd(); 1230 1231 if(start == old_target) 1232 lg[i].setStart(new_target); 1233 1234 if(end == old_target) 1235 lg[i].setEnd(new_target); 1236 } 1237 } 1238 1239 /** 1240 * Redirect all references of exception handlers from old_target to new_target. 1241 * 1242 * @param exceptions array of exception handlers 1243 * @param old_target the old target instruction handle 1244 * @param new_target the new target instruction handle 1245 * @see MethodGen 1246 */ 1247 public void redirectExceptionHandlers(CodeExceptionGen[] exceptions, 1248 InstructionHandle old_target, 1249 InstructionHandle new_target) { 1250 for(int i=0; i < exceptions.length; i++) { 1251 if(exceptions[i].getStartPC() == old_target) 1252 exceptions[i].setStartPC(new_target); 1253 1254 if(exceptions[i].getEndPC() == old_target) 1255 exceptions[i].setEndPC(new_target); 1256 1257 if(exceptions[i].getHandlerPC() == old_target) 1258 exceptions[i].setHandlerPC(new_target); 1259 } 1260 } 1261 1262 private ArrayList observers; 1263 1264 /** Add observer for this object. 1265 */ 1266 public void addObserver(InstructionListObserver o) { 1267 if(observers == null) 1268 observers = new ArrayList(); 1269 1270 observers.add(o); 1271 } 1272 1273 /** Remove observer for this object. 1274 */ 1275 public void removeObserver(InstructionListObserver o) { 1276 if(observers != null) 1277 observers.remove(o); 1278 } 1279 1280 /** Call notify() method on all observers. This method is not called 1281 * automatically whenever the state has changed, but has to be 1282 * called by the user after he has finished editing the object. 1283 */ 1284 public void update() { 1285 if(observers != null) 1286 for(Iterator e = observers.iterator(); e.hasNext(); ) 1287 ((InstructionListObserver)e.next()).notify(this); 1288 } 1289 }