1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   3  *
   4  * This code is free software; you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 2 only, as
   6  * published by the Free Software Foundation.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 /*
  26  * This file is available under and governed by the GNU General Public
  27  * License version 2 only, as published by the Free Software Foundation.
  28  * However, the following notice accompanied the original version of this
  29  * file:
  30  *
  31  * ASM: a very small and fast Java bytecode manipulation framework
  32  * Copyright (c) 2000-2011 INRIA, France Telecom
  33  * All rights reserved.
  34  *
  35  * Redistribution and use in source and binary forms, with or without
  36  * modification, are permitted provided that the following conditions
  37  * are met:
  38  * 1. Redistributions of source code must retain the above copyright
  39  *    notice, this list of conditions and the following disclaimer.
  40  * 2. Redistributions in binary form must reproduce the above copyright
  41  *    notice, this list of conditions and the following disclaimer in the
  42  *    documentation and/or other materials provided with the distribution.
  43  * 3. Neither the name of the copyright holders nor the names of its
  44  *    contributors may be used to endorse or promote products derived from
  45  *    this software without specific prior written permission.
  46  *
  47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  57  * THE POSSIBILITY OF SUCH DAMAGE.
  58  */
  59 package jdk.internal.org.objectweb.asm.tree;
  60 
  61 import java.util.ListIterator;
  62 import java.util.NoSuchElementException;
  63 
  64 import jdk.internal.org.objectweb.asm.MethodVisitor;
  65 
  66 /**
  67  * A doubly linked list of {@link AbstractInsnNode} objects. <i>This
  68  * implementation is not thread safe</i>.
  69  */
  70 public class InsnList {
  71 
  72     /**
  73      * The number of instructions in this list.
  74      */
  75     private int size;
  76 
  77     /**
  78      * The first instruction in this list. May be <tt>null</tt>.
  79      */
  80     private AbstractInsnNode first;
  81 
  82     /**
  83      * The last instruction in this list. May be <tt>null</tt>.
  84      */
  85     private AbstractInsnNode last;
  86 
  87     /**
  88      * A cache of the instructions of this list. This cache is used to improve
  89      * the performance of the {@link #get} method.
  90      */
  91     AbstractInsnNode[] cache;
  92 
  93     /**
  94      * Returns the number of instructions in this list.
  95      *
  96      * @return the number of instructions in this list.
  97      */
  98     public int size() {
  99         return size;
 100     }
 101 
 102     /**
 103      * Returns the first instruction in this list.
 104      *
 105      * @return the first instruction in this list, or <tt>null</tt> if the list
 106      *         is empty.
 107      */
 108     public AbstractInsnNode getFirst() {
 109         return first;
 110     }
 111 
 112     /**
 113      * Returns the last instruction in this list.
 114      *
 115      * @return the last instruction in this list, or <tt>null</tt> if the list
 116      *         is empty.
 117      */
 118     public AbstractInsnNode getLast() {
 119         return last;
 120     }
 121 
 122     /**
 123      * Returns the instruction whose index is given. This method builds a cache
 124      * of the instructions in this list to avoid scanning the whole list each
 125      * time it is called. Once the cache is built, this method run in constant
 126      * time. This cache is invalidated by all the methods that modify the list.
 127      *
 128      * @param index
 129      *            the index of the instruction that must be returned.
 130      * @return the instruction whose index is given.
 131      * @throws IndexOutOfBoundsException
 132      *             if (index &lt; 0 || index &gt;= size()).
 133      */
 134     public AbstractInsnNode get(final int index) {
 135         if (index < 0 || index >= size) {
 136             throw new IndexOutOfBoundsException();
 137         }
 138         if (cache == null) {
 139             cache = toArray();
 140         }
 141         return cache[index];
 142     }
 143 
 144     /**
 145      * Returns <tt>true</tt> if the given instruction belongs to this list. This
 146      * method always scans the instructions of this list until it finds the
 147      * given instruction or reaches the end of the list.
 148      *
 149      * @param insn
 150      *            an instruction.
 151      * @return <tt>true</tt> if the given instruction belongs to this list.
 152      */
 153     public boolean contains(final AbstractInsnNode insn) {
 154         AbstractInsnNode i = first;
 155         while (i != null && i != insn) {
 156             i = i.next;
 157         }
 158         return i != null;
 159     }
 160 
 161     /**
 162      * Returns the index of the given instruction in this list. This method
 163      * builds a cache of the instruction indexes to avoid scanning the whole
 164      * list each time it is called. Once the cache is built, this method run in
 165      * constant time. The cache is invalidated by all the methods that modify
 166      * the list.
 167      *
 168      * @param insn
 169      *            an instruction <i>of this list</i>.
 170      * @return the index of the given instruction in this list. <i>The result of
 171      *         this method is undefined if the given instruction does not belong
 172      *         to this list</i>. Use {@link #contains contains} to test if an
 173      *         instruction belongs to an instruction list or not.
 174      */
 175     public int indexOf(final AbstractInsnNode insn) {
 176         if (cache == null) {
 177             cache = toArray();
 178         }
 179         return insn.index;
 180     }
 181 
 182     /**
 183      * Makes the given visitor visit all of the instructions in this list.
 184      *
 185      * @param mv
 186      *            the method visitor that must visit the instructions.
 187      */
 188     public void accept(final MethodVisitor mv) {
 189         AbstractInsnNode insn = first;
 190         while (insn != null) {
 191             insn.accept(mv);
 192             insn = insn.next;
 193         }
 194     }
 195 
 196     /**
 197      * Returns an iterator over the instructions in this list.
 198      *
 199      * @return an iterator over the instructions in this list.
 200      */
 201     public ListIterator<AbstractInsnNode> iterator() {
 202         return iterator(0);
 203     }
 204 
 205     /**
 206      * Returns an iterator over the instructions in this list.
 207      *
 208      * @param index
 209      *            index of instruction for the iterator to start at
 210      *
 211      * @return an iterator over the instructions in this list.
 212      */
 213     @SuppressWarnings("unchecked")
 214     public ListIterator<AbstractInsnNode> iterator(int index) {
 215         return new InsnListIterator(index);
 216     }
 217 
 218     /**
 219      * Returns an array containing all of the instructions in this list.
 220      *
 221      * @return an array containing all of the instructions in this list.
 222      */
 223     public AbstractInsnNode[] toArray() {
 224         int i = 0;
 225         AbstractInsnNode elem = first;
 226         AbstractInsnNode[] insns = new AbstractInsnNode[size];
 227         while (elem != null) {
 228             insns[i] = elem;
 229             elem.index = i++;
 230             elem = elem.next;
 231         }
 232         return insns;
 233     }
 234 
 235     /**
 236      * Replaces an instruction of this list with another instruction.
 237      *
 238      * @param location
 239      *            an instruction <i>of this list</i>.
 240      * @param insn
 241      *            another instruction, <i>which must not belong to any
 242      *            {@link InsnList}</i>.
 243      */
 244     public void set(final AbstractInsnNode location, final AbstractInsnNode insn) {
 245         AbstractInsnNode next = location.next;
 246         insn.next = next;
 247         if (next != null) {
 248             next.prev = insn;
 249         } else {
 250             last = insn;
 251         }
 252         AbstractInsnNode prev = location.prev;
 253         insn.prev = prev;
 254         if (prev != null) {
 255             prev.next = insn;
 256         } else {
 257             first = insn;
 258         }
 259         if (cache != null) {
 260             int index = location.index;
 261             cache[index] = insn;
 262             insn.index = index;
 263         } else {
 264             insn.index = 0; // insn now belongs to an InsnList
 265         }
 266         location.index = -1; // i no longer belongs to an InsnList
 267         location.prev = null;
 268         location.next = null;
 269     }
 270 
 271     /**
 272      * Adds the given instruction to the end of this list.
 273      *
 274      * @param insn
 275      *            an instruction, <i>which must not belong to any
 276      *            {@link InsnList}</i>.
 277      */
 278     public void add(final AbstractInsnNode insn) {
 279         ++size;
 280         if (last == null) {
 281             first = insn;
 282             last = insn;
 283         } else {
 284             last.next = insn;
 285             insn.prev = last;
 286         }
 287         last = insn;
 288         cache = null;
 289         insn.index = 0; // insn now belongs to an InsnList
 290     }
 291 
 292     /**
 293      * Adds the given instructions to the end of this list.
 294      *
 295      * @param insns
 296      *            an instruction list, which is cleared during the process. This
 297      *            list must be different from 'this'.
 298      */
 299     public void add(final InsnList insns) {
 300         if (insns.size == 0) {
 301             return;
 302         }
 303         size += insns.size;
 304         if (last == null) {
 305             first = insns.first;
 306             last = insns.last;
 307         } else {
 308             AbstractInsnNode elem = insns.first;
 309             last.next = elem;
 310             elem.prev = last;
 311             last = insns.last;
 312         }
 313         cache = null;
 314         insns.removeAll(false);
 315     }
 316 
 317     /**
 318      * Inserts the given instruction at the begining of this list.
 319      *
 320      * @param insn
 321      *            an instruction, <i>which must not belong to any
 322      *            {@link InsnList}</i>.
 323      */
 324     public void insert(final AbstractInsnNode insn) {
 325         ++size;
 326         if (first == null) {
 327             first = insn;
 328             last = insn;
 329         } else {
 330             first.prev = insn;
 331             insn.next = first;
 332         }
 333         first = insn;
 334         cache = null;
 335         insn.index = 0; // insn now belongs to an InsnList
 336     }
 337 
 338     /**
 339      * Inserts the given instructions at the begining of this list.
 340      *
 341      * @param insns
 342      *            an instruction list, which is cleared during the process. This
 343      *            list must be different from 'this'.
 344      */
 345     public void insert(final InsnList insns) {
 346         if (insns.size == 0) {
 347             return;
 348         }
 349         size += insns.size;
 350         if (first == null) {
 351             first = insns.first;
 352             last = insns.last;
 353         } else {
 354             AbstractInsnNode elem = insns.last;
 355             first.prev = elem;
 356             elem.next = first;
 357             first = insns.first;
 358         }
 359         cache = null;
 360         insns.removeAll(false);
 361     }
 362 
 363     /**
 364      * Inserts the given instruction after the specified instruction.
 365      *
 366      * @param location
 367      *            an instruction <i>of this list</i> after which insn must be
 368      *            inserted.
 369      * @param insn
 370      *            the instruction to be inserted, <i>which must not belong to
 371      *            any {@link InsnList}</i>.
 372      */
 373     public void insert(final AbstractInsnNode location,
 374             final AbstractInsnNode insn) {
 375         ++size;
 376         AbstractInsnNode next = location.next;
 377         if (next == null) {
 378             last = insn;
 379         } else {
 380             next.prev = insn;
 381         }
 382         location.next = insn;
 383         insn.next = next;
 384         insn.prev = location;
 385         cache = null;
 386         insn.index = 0; // insn now belongs to an InsnList
 387     }
 388 
 389     /**
 390      * Inserts the given instructions after the specified instruction.
 391      *
 392      * @param location
 393      *            an instruction <i>of this list</i> after which the
 394      *            instructions must be inserted.
 395      * @param insns
 396      *            the instruction list to be inserted, which is cleared during
 397      *            the process. This list must be different from 'this'.
 398      */
 399     public void insert(final AbstractInsnNode location, final InsnList insns) {
 400         if (insns.size == 0) {
 401             return;
 402         }
 403         size += insns.size;
 404         AbstractInsnNode ifirst = insns.first;
 405         AbstractInsnNode ilast = insns.last;
 406         AbstractInsnNode next = location.next;
 407         if (next == null) {
 408             last = ilast;
 409         } else {
 410             next.prev = ilast;
 411         }
 412         location.next = ifirst;
 413         ilast.next = next;
 414         ifirst.prev = location;
 415         cache = null;
 416         insns.removeAll(false);
 417     }
 418 
 419     /**
 420      * Inserts the given instruction before the specified instruction.
 421      *
 422      * @param location
 423      *            an instruction <i>of this list</i> before which insn must be
 424      *            inserted.
 425      * @param insn
 426      *            the instruction to be inserted, <i>which must not belong to
 427      *            any {@link InsnList}</i>.
 428      */
 429     public void insertBefore(final AbstractInsnNode location,
 430             final AbstractInsnNode insn) {
 431         ++size;
 432         AbstractInsnNode prev = location.prev;
 433         if (prev == null) {
 434             first = insn;
 435         } else {
 436             prev.next = insn;
 437         }
 438         location.prev = insn;
 439         insn.next = location;
 440         insn.prev = prev;
 441         cache = null;
 442         insn.index = 0; // insn now belongs to an InsnList
 443     }
 444 
 445     /**
 446      * Inserts the given instructions before the specified instruction.
 447      *
 448      * @param location
 449      *            an instruction <i>of this list</i> before which the
 450      *            instructions must be inserted.
 451      * @param insns
 452      *            the instruction list to be inserted, which is cleared during
 453      *            the process. This list must be different from 'this'.
 454      */
 455     public void insertBefore(final AbstractInsnNode location,
 456             final InsnList insns) {
 457         if (insns.size == 0) {
 458             return;
 459         }
 460         size += insns.size;
 461         AbstractInsnNode ifirst = insns.first;
 462         AbstractInsnNode ilast = insns.last;
 463         AbstractInsnNode prev = location.prev;
 464         if (prev == null) {
 465             first = ifirst;
 466         } else {
 467             prev.next = ifirst;
 468         }
 469         location.prev = ilast;
 470         ilast.next = location;
 471         ifirst.prev = prev;
 472         cache = null;
 473         insns.removeAll(false);
 474     }
 475 
 476     /**
 477      * Removes the given instruction from this list.
 478      *
 479      * @param insn
 480      *            the instruction <i>of this list</i> that must be removed.
 481      */
 482     public void remove(final AbstractInsnNode insn) {
 483         --size;
 484         AbstractInsnNode next = insn.next;
 485         AbstractInsnNode prev = insn.prev;
 486         if (next == null) {
 487             if (prev == null) {
 488                 first = null;
 489                 last = null;
 490             } else {
 491                 prev.next = null;
 492                 last = prev;
 493             }
 494         } else {
 495             if (prev == null) {
 496                 first = next;
 497                 next.prev = null;
 498             } else {
 499                 prev.next = next;
 500                 next.prev = prev;
 501             }
 502         }
 503         cache = null;
 504         insn.index = -1; // insn no longer belongs to an InsnList
 505         insn.prev = null;
 506         insn.next = null;
 507     }
 508 
 509     /**
 510      * Removes all of the instructions of this list.
 511      *
 512      * @param mark
 513      *            if the instructions must be marked as no longer belonging to
 514      *            any {@link InsnList}.
 515      */
 516     void removeAll(final boolean mark) {
 517         if (mark) {
 518             AbstractInsnNode insn = first;
 519             while (insn != null) {
 520                 AbstractInsnNode next = insn.next;
 521                 insn.index = -1; // insn no longer belongs to an InsnList
 522                 insn.prev = null;
 523                 insn.next = null;
 524                 insn = next;
 525             }
 526         }
 527         size = 0;
 528         first = null;
 529         last = null;
 530         cache = null;
 531     }
 532 
 533     /**
 534      * Removes all of the instructions of this list.
 535      */
 536     public void clear() {
 537         removeAll(false);
 538     }
 539 
 540     /**
 541      * Reset all labels in the instruction list. This method should be called
 542      * before reusing same instructions list between several
 543      * <code>ClassWriter</code>s.
 544      */
 545     public void resetLabels() {
 546         AbstractInsnNode insn = first;
 547         while (insn != null) {
 548             if (insn instanceof LabelNode) {
 549                 ((LabelNode) insn).resetLabel();
 550             }
 551             insn = insn.next;
 552         }
 553     }
 554 
 555     // this class is not generified because it will create bridges
 556     @SuppressWarnings("rawtypes")
 557     private final class InsnListIterator implements ListIterator {
 558 
 559         AbstractInsnNode next;
 560 
 561         AbstractInsnNode prev;
 562 
 563         AbstractInsnNode remove;
 564 
 565         InsnListIterator(int index) {
 566             if (index == size()) {
 567                 next = null;
 568                 prev = getLast();
 569             } else {
 570                 next = get(index);
 571                 prev = next.prev;
 572             }
 573         }
 574 
 575         public boolean hasNext() {
 576             return next != null;
 577         }
 578 
 579         public Object next() {
 580             if (next == null) {
 581                 throw new NoSuchElementException();
 582             }
 583             AbstractInsnNode result = next;
 584             prev = result;
 585             next = result.next;
 586             remove = result;
 587             return result;
 588         }
 589 
 590         public void remove() {
 591             if (remove != null) {
 592                 if (remove == next) {
 593                     next = next.next;
 594                 } else {
 595                     prev = prev.prev;
 596                 }
 597                 InsnList.this.remove(remove);
 598                 remove = null;
 599             } else {
 600                 throw new IllegalStateException();
 601             }
 602         }
 603 
 604         public boolean hasPrevious() {
 605             return prev != null;
 606         }
 607 
 608         public Object previous() {
 609             AbstractInsnNode result = prev;
 610             next = result;
 611             prev = result.prev;
 612             remove = result;
 613             return result;
 614         }
 615 
 616         public int nextIndex() {
 617             if (next == null) {
 618                 return size();
 619             }
 620             if (cache == null) {
 621                 cache = toArray();
 622             }
 623             return next.index;
 624         }
 625 
 626         public int previousIndex() {
 627             if (prev == null) {
 628                 return -1;
 629             }
 630             if (cache == null) {
 631                 cache = toArray();
 632             }
 633             return prev.index;
 634         }
 635 
 636         public void add(Object o) {
 637             InsnList.this.insertBefore(next, (AbstractInsnNode) o);
 638             prev = (AbstractInsnNode) o;
 639             remove = null;
 640         }
 641 
 642         public void set(Object o) {
 643             InsnList.this.set(next.prev, (AbstractInsnNode) o);
 644             prev = (AbstractInsnNode) o;
 645         }
 646     }
 647 }