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