001/*** 002 * ASM: a very small and fast Java bytecode manipulation framework 003 * Copyright (c) 2000-2011 INRIA, France Telecom 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or without 007 * modification, are permitted provided that the following conditions 008 * are met: 009 * 1. Redistributions of source code must retain the above copyright 010 * notice, this list of conditions and the following disclaimer. 011 * 2. Redistributions in binary form must reproduce the above copyright 012 * notice, this list of conditions and the following disclaimer in the 013 * documentation and/or other materials provided with the distribution. 014 * 3. Neither the name of the copyright holders nor the names of its 015 * contributors may be used to endorse or promote products derived from 016 * this software without specific prior written permission. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 021 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 022 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 028 * THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package io.ebean.enhance.asm.tree; 031 032import io.ebean.enhance.asm.MethodVisitor; 033 034import java.util.ListIterator; 035import java.util.NoSuchElementException; 036 037/** 038 * A doubly linked list of {@link AbstractInsnNode} objects. <i>This 039 * implementation is not thread safe</i>. 040 */ 041public class InsnList { 042 043 /** 044 * The number of instructions in this list. 045 */ 046 private int size; 047 048 /** 049 * The first instruction in this list. May be <tt>null</tt>. 050 */ 051 private AbstractInsnNode first; 052 053 /** 054 * The last instruction in this list. May be <tt>null</tt>. 055 */ 056 private AbstractInsnNode last; 057 058 /** 059 * A cache of the instructions of this list. This cache is used to improve 060 * the performance of the {@link #get} method. 061 */ 062 AbstractInsnNode[] cache; 063 064 /** 065 * Returns the number of instructions in this list. 066 * 067 * @return the number of instructions in this list. 068 */ 069 public int size() { 070 return size; 071 } 072 073 /** 074 * Returns the first instruction in this list. 075 * 076 * @return the first instruction in this list, or <tt>null</tt> if the list 077 * is empty. 078 */ 079 public AbstractInsnNode getFirst() { 080 return first; 081 } 082 083 /** 084 * Returns the last instruction in this list. 085 * 086 * @return the last instruction in this list, or <tt>null</tt> if the list 087 * is empty. 088 */ 089 public AbstractInsnNode getLast() { 090 return last; 091 } 092 093 /** 094 * Returns the instruction whose index is given. This method builds a cache 095 * of the instructions in this list to avoid scanning the whole list each 096 * time it is called. Once the cache is built, this method run in constant 097 * time. This cache is invalidated by all the methods that modify the list. 098 * 099 * @param index 100 * the index of the instruction that must be returned. 101 * @return the instruction whose index is given. 102 * @throws IndexOutOfBoundsException 103 * if (index < 0 || index >= size()). 104 */ 105 public AbstractInsnNode get(final int index) { 106 if (index < 0 || index >= size) { 107 throw new IndexOutOfBoundsException(); 108 } 109 if (cache == null) { 110 cache = toArray(); 111 } 112 return cache[index]; 113 } 114 115 /** 116 * Returns <tt>true</tt> if the given instruction belongs to this list. This 117 * method always scans the instructions of this list until it finds the 118 * given instruction or reaches the end of the list. 119 * 120 * @param insn 121 * an instruction. 122 * @return <tt>true</tt> if the given instruction belongs to this list. 123 */ 124 public boolean contains(final AbstractInsnNode insn) { 125 AbstractInsnNode i = first; 126 while (i != null && i != insn) { 127 i = i.next; 128 } 129 return i != null; 130 } 131 132 /** 133 * Returns the index of the given instruction in this list. This method 134 * builds a cache of the instruction indexes to avoid scanning the whole 135 * list each time it is called. Once the cache is built, this method run in 136 * constant time. The cache is invalidated by all the methods that modify 137 * the list. 138 * 139 * @param insn 140 * an instruction <i>of this list</i>. 141 * @return the index of the given instruction in this list. <i>The result of 142 * this method is undefined if the given instruction does not belong 143 * to this list</i>. Use {@link #contains contains} to test if an 144 * instruction belongs to an instruction list or not. 145 */ 146 public int indexOf(final AbstractInsnNode insn) { 147 if (cache == null) { 148 cache = toArray(); 149 } 150 return insn.index; 151 } 152 153 /** 154 * Makes the given visitor visit all of the instructions in this list. 155 * 156 * @param mv 157 * the method visitor that must visit the instructions. 158 */ 159 public void accept(final MethodVisitor mv) { 160 AbstractInsnNode insn = first; 161 while (insn != null) { 162 insn.accept(mv); 163 insn = insn.next; 164 } 165 } 166 167 /** 168 * Returns an iterator over the instructions in this list. 169 * 170 * @return an iterator over the instructions in this list. 171 */ 172 public ListIterator<AbstractInsnNode> iterator() { 173 return iterator(0); 174 } 175 176 /** 177 * Returns an iterator over the instructions in this list. 178 * 179 * @param index 180 * index of instruction for the iterator to start at 181 * 182 * @return an iterator over the instructions in this list. 183 */ 184 @SuppressWarnings("unchecked") 185 public ListIterator<AbstractInsnNode> iterator(int index) { 186 return new InsnListIterator(index); 187 } 188 189 /** 190 * Returns an array containing all of the instructions in this list. 191 * 192 * @return an array containing all of the instructions in this list. 193 */ 194 public AbstractInsnNode[] toArray() { 195 int i = 0; 196 AbstractInsnNode elem = first; 197 AbstractInsnNode[] insns = new AbstractInsnNode[size]; 198 while (elem != null) { 199 insns[i] = elem; 200 elem.index = i++; 201 elem = elem.next; 202 } 203 return insns; 204 } 205 206 /** 207 * Replaces an instruction of this list with another instruction. 208 * 209 * @param location 210 * an instruction <i>of this list</i>. 211 * @param insn 212 * another instruction, <i>which must not belong to any 213 * {@link InsnList}</i>. 214 */ 215 public void set(final AbstractInsnNode location, final AbstractInsnNode insn) { 216 AbstractInsnNode next = location.next; 217 insn.next = next; 218 if (next != null) { 219 next.prev = insn; 220 } else { 221 last = insn; 222 } 223 AbstractInsnNode prev = location.prev; 224 insn.prev = prev; 225 if (prev != null) { 226 prev.next = insn; 227 } else { 228 first = insn; 229 } 230 if (cache != null) { 231 int index = location.index; 232 cache[index] = insn; 233 insn.index = index; 234 } else { 235 insn.index = 0; // insn now belongs to an InsnList 236 } 237 location.index = -1; // i no longer belongs to an InsnList 238 location.prev = null; 239 location.next = null; 240 } 241 242 /** 243 * Adds the given instruction to the end of this list. 244 * 245 * @param insn 246 * an instruction, <i>which must not belong to any 247 * {@link InsnList}</i>. 248 */ 249 public void add(final AbstractInsnNode insn) { 250 ++size; 251 if (last == null) { 252 first = insn; 253 last = insn; 254 } else { 255 last.next = insn; 256 insn.prev = last; 257 } 258 last = insn; 259 cache = null; 260 insn.index = 0; // insn now belongs to an InsnList 261 } 262 263 /** 264 * Adds the given instructions to the end of this list. 265 * 266 * @param insns 267 * an instruction list, which is cleared during the process. This 268 * list must be different from 'this'. 269 */ 270 public void add(final InsnList insns) { 271 if (insns.size == 0) { 272 return; 273 } 274 size += insns.size; 275 if (last == null) { 276 first = insns.first; 277 last = insns.last; 278 } else { 279 AbstractInsnNode elem = insns.first; 280 last.next = elem; 281 elem.prev = last; 282 last = insns.last; 283 } 284 cache = null; 285 insns.removeAll(false); 286 } 287 288 /** 289 * Inserts the given instruction at the begining of this list. 290 * 291 * @param insn 292 * an instruction, <i>which must not belong to any 293 * {@link InsnList}</i>. 294 */ 295 public void insert(final AbstractInsnNode insn) { 296 ++size; 297 if (first == null) { 298 first = insn; 299 last = insn; 300 } else { 301 first.prev = insn; 302 insn.next = first; 303 } 304 first = insn; 305 cache = null; 306 insn.index = 0; // insn now belongs to an InsnList 307 } 308 309 /** 310 * Inserts the given instructions at the begining of this list. 311 * 312 * @param insns 313 * an instruction list, which is cleared during the process. This 314 * list must be different from 'this'. 315 */ 316 public void insert(final InsnList insns) { 317 if (insns.size == 0) { 318 return; 319 } 320 size += insns.size; 321 if (first == null) { 322 first = insns.first; 323 last = insns.last; 324 } else { 325 AbstractInsnNode elem = insns.last; 326 first.prev = elem; 327 elem.next = first; 328 first = insns.first; 329 } 330 cache = null; 331 insns.removeAll(false); 332 } 333 334 /** 335 * Inserts the given instruction after the specified instruction. 336 * 337 * @param location 338 * an instruction <i>of this list</i> after which insn must be 339 * inserted. 340 * @param insn 341 * the instruction to be inserted, <i>which must not belong to 342 * any {@link InsnList}</i>. 343 */ 344 public void insert(final AbstractInsnNode location, 345 final AbstractInsnNode insn) { 346 ++size; 347 AbstractInsnNode next = location.next; 348 if (next == null) { 349 last = insn; 350 } else { 351 next.prev = insn; 352 } 353 location.next = insn; 354 insn.next = next; 355 insn.prev = location; 356 cache = null; 357 insn.index = 0; // insn now belongs to an InsnList 358 } 359 360 /** 361 * Inserts the given instructions after the specified instruction. 362 * 363 * @param location 364 * an instruction <i>of this list</i> after which the 365 * instructions must be inserted. 366 * @param insns 367 * the instruction list to be inserted, which is cleared during 368 * the process. This list must be different from 'this'. 369 */ 370 public void insert(final AbstractInsnNode location, final InsnList insns) { 371 if (insns.size == 0) { 372 return; 373 } 374 size += insns.size; 375 AbstractInsnNode ifirst = insns.first; 376 AbstractInsnNode ilast = insns.last; 377 AbstractInsnNode next = location.next; 378 if (next == null) { 379 last = ilast; 380 } else { 381 next.prev = ilast; 382 } 383 location.next = ifirst; 384 ilast.next = next; 385 ifirst.prev = location; 386 cache = null; 387 insns.removeAll(false); 388 } 389 390 /** 391 * Inserts the given instruction before the specified instruction. 392 * 393 * @param location 394 * an instruction <i>of this list</i> before which insn must be 395 * inserted. 396 * @param insn 397 * the instruction to be inserted, <i>which must not belong to 398 * any {@link InsnList}</i>. 399 */ 400 public void insertBefore(final AbstractInsnNode location, 401 final AbstractInsnNode insn) { 402 ++size; 403 AbstractInsnNode prev = location.prev; 404 if (prev == null) { 405 first = insn; 406 } else { 407 prev.next = insn; 408 } 409 location.prev = insn; 410 insn.next = location; 411 insn.prev = prev; 412 cache = null; 413 insn.index = 0; // insn now belongs to an InsnList 414 } 415 416 /** 417 * Inserts the given instructions before the specified instruction. 418 * 419 * @param location 420 * an instruction <i>of this list</i> before which the 421 * instructions must be inserted. 422 * @param insns 423 * the instruction list to be inserted, which is cleared during 424 * the process. This list must be different from 'this'. 425 */ 426 public void insertBefore(final AbstractInsnNode location, 427 final InsnList insns) { 428 if (insns.size == 0) { 429 return; 430 } 431 size += insns.size; 432 AbstractInsnNode ifirst = insns.first; 433 AbstractInsnNode ilast = insns.last; 434 AbstractInsnNode prev = location.prev; 435 if (prev == null) { 436 first = ifirst; 437 } else { 438 prev.next = ifirst; 439 } 440 location.prev = ilast; 441 ilast.next = location; 442 ifirst.prev = prev; 443 cache = null; 444 insns.removeAll(false); 445 } 446 447 /** 448 * Removes the given instruction from this list. 449 * 450 * @param insn 451 * the instruction <i>of this list</i> that must be removed. 452 */ 453 public void remove(final AbstractInsnNode insn) { 454 --size; 455 AbstractInsnNode next = insn.next; 456 AbstractInsnNode prev = insn.prev; 457 if (next == null) { 458 if (prev == null) { 459 first = null; 460 last = null; 461 } else { 462 prev.next = null; 463 last = prev; 464 } 465 } else { 466 if (prev == null) { 467 first = next; 468 next.prev = null; 469 } else { 470 prev.next = next; 471 next.prev = prev; 472 } 473 } 474 cache = null; 475 insn.index = -1; // insn no longer belongs to an InsnList 476 insn.prev = null; 477 insn.next = null; 478 } 479 480 /** 481 * Removes all of the instructions of this list. 482 * 483 * @param mark 484 * if the instructions must be marked as no longer belonging to 485 * any {@link InsnList}. 486 */ 487 void removeAll(final boolean mark) { 488 if (mark) { 489 AbstractInsnNode insn = first; 490 while (insn != null) { 491 AbstractInsnNode next = insn.next; 492 insn.index = -1; // insn no longer belongs to an InsnList 493 insn.prev = null; 494 insn.next = null; 495 insn = next; 496 } 497 } 498 size = 0; 499 first = null; 500 last = null; 501 cache = null; 502 } 503 504 /** 505 * Removes all of the instructions of this list. 506 */ 507 public void clear() { 508 removeAll(false); 509 } 510 511 /** 512 * Reset all labels in the instruction list. This method should be called 513 * before reusing same instructions list between several 514 * <code>ClassWriter</code>s. 515 */ 516 public void resetLabels() { 517 AbstractInsnNode insn = first; 518 while (insn != null) { 519 if (insn instanceof LabelNode) { 520 ((LabelNode) insn).resetLabel(); 521 } 522 insn = insn.next; 523 } 524 } 525 526 // this class is not generified because it will create bridges 527 @SuppressWarnings("rawtypes") 528 private final class InsnListIterator implements ListIterator { 529 530 AbstractInsnNode next; 531 532 AbstractInsnNode prev; 533 534 AbstractInsnNode remove; 535 536 InsnListIterator(int index) { 537 if (index == size()) { 538 next = null; 539 prev = getLast(); 540 } else { 541 next = get(index); 542 prev = next.prev; 543 } 544 } 545 546 public boolean hasNext() { 547 return next != null; 548 } 549 550 public Object next() { 551 if (next == null) { 552 throw new NoSuchElementException(); 553 } 554 AbstractInsnNode result = next; 555 prev = result; 556 next = result.next; 557 remove = result; 558 return result; 559 } 560 561 public void remove() { 562 if (remove != null) { 563 if (remove == next) { 564 next = next.next; 565 } else { 566 prev = prev.prev; 567 } 568 InsnList.this.remove(remove); 569 remove = null; 570 } else { 571 throw new IllegalStateException(); 572 } 573 } 574 575 public boolean hasPrevious() { 576 return prev != null; 577 } 578 579 public Object previous() { 580 AbstractInsnNode result = prev; 581 next = result; 582 prev = result.prev; 583 remove = result; 584 return result; 585 } 586 587 public int nextIndex() { 588 if (next == null) { 589 return size(); 590 } 591 if (cache == null) { 592 cache = toArray(); 593 } 594 return next.index; 595 } 596 597 public int previousIndex() { 598 if (prev == null) { 599 return -1; 600 } 601 if (cache == null) { 602 cache = toArray(); 603 } 604 return prev.index; 605 } 606 607 public void add(Object o) { 608 if (next != null) { 609 InsnList.this.insertBefore(next, (AbstractInsnNode) o); 610 } else if (prev != null) { 611 InsnList.this.insert(prev, (AbstractInsnNode) o); 612 } else { 613 InsnList.this.add((AbstractInsnNode) o); 614 } 615 prev = (AbstractInsnNode) o; 616 remove = null; 617 } 618 619 public void set(Object o) { 620 if (remove != null) { 621 InsnList.this.set(remove, (AbstractInsnNode) o); 622 if (remove == prev) { 623 prev = (AbstractInsnNode) o; 624 } else { 625 next = (AbstractInsnNode) o; 626 } 627 } else { 628 throw new IllegalStateException(); 629 } 630 } 631 } 632}