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      * @return an iterator over the instructions in this list.
 209      */
 210     @SuppressWarnings("unchecked")
 211     public ListIterator<AbstractInsnNode> iterator(int index) {
 212         return new InsnListIterator(index);
 213     }
 214 
 215     /**
 216      * Returns an array containing all of the instructions in this list.
 217      *
 218      * @return an array containing all of the instructions in this list.
 219      */
 220     public AbstractInsnNode[] toArray() {
 221         int i = 0;
 222         AbstractInsnNode elem = first;
 223         AbstractInsnNode[] insns = new AbstractInsnNode[size];
 224         while (elem != null) {
 225             insns[i] = elem;
 226             elem.index = i++;
 227             elem = elem.next;
 228         }
 229         return insns;
 230     }
 231 
 232     /**
 233      * Replaces an instruction of this list with another instruction.
 234      *
 235      * @param location
 236      *            an instruction <i>of this list</i>.
 237      * @param insn
 238      *            another instruction, <i>which must not belong to any
 239      *            {@link InsnList}</i>.
 240      */
 241     public void set(final AbstractInsnNode location, final AbstractInsnNode insn) {
 242         AbstractInsnNode next = location.next;
 243         insn.next = next;
 244         if (next != null) {
 245             next.prev = insn;
 246         } else {
 247             last = insn;
 248         }
 249         AbstractInsnNode prev = location.prev;
 250         insn.prev = prev;
 251         if (prev != null) {
 252             prev.next = insn;
 253         } else {
 254             first = insn;
 255         }
 256         if (cache != null) {
 257             int index = location.index;
 258             cache[index] = insn;
 259             insn.index = index;
 260         } else {
 261             insn.index = 0; // insn now belongs to an InsnList
 262         }
 263         location.index = -1; // i no longer belongs to an InsnList
 264         location.prev = null;
 265         location.next = null;
 266     }
 267 
 268     /**
 269      * Adds the given instruction to the end of this list.
 270      *
 271      * @param insn
 272      *            an instruction, <i>which must not belong to any
 273      *            {@link InsnList}</i>.
 274      */
 275     public void add(final AbstractInsnNode insn) {
 276         ++size;
 277         if (last == null) {
 278             first = insn;
 279             last = insn;
 280         } else {
 281             last.next = insn;
 282             insn.prev = last;
 283         }
 284         last = insn;
 285         cache = null;
 286         insn.index = 0; // insn now belongs to an InsnList
 287     }
 288 
 289     /**
 290      * Adds the given instructions to the end of this list.
 291      *
 292      * @param insns
 293      *            an instruction list, which is cleared during the process. This
 294      *            list must be different from 'this'.
 295      */
 296     public void add(final InsnList insns) {
 297         if (insns.size == 0) {
 298             return;
 299         }
 300         size += insns.size;
 301         if (last == null) {
 302             first = insns.first;
 303             last = insns.last;
 304         } else {
 305             AbstractInsnNode elem = insns.first;
 306             last.next = elem;
 307             elem.prev = last;
 308             last = insns.last;
 309         }
 310         cache = null;
 311         insns.removeAll(false);
 312     }
 313 
 314     /**
 315      * Inserts the given instruction at the begining of this list.
 316      *
 317      * @param insn
 318      *            an instruction, <i>which must not belong to any
 319      *            {@link InsnList}</i>.
 320      */
 321     public void insert(final AbstractInsnNode insn) {
 322         ++size;
 323         if (first == null) {
 324             first = insn;
 325             last = insn;
 326         } else {
 327             first.prev = insn;
 328             insn.next = first;
 329         }
 330         first = insn;
 331         cache = null;
 332         insn.index = 0; // insn now belongs to an InsnList
 333     }
 334 
 335     /**
 336      * Inserts the given instructions at the begining of this list.
 337      *
 338      * @param insns
 339      *            an instruction list, which is cleared during the process. This
 340      *            list must be different from 'this'.
 341      */
 342     public void insert(final InsnList insns) {
 343         if (insns.size == 0) {
 344             return;
 345         }
 346         size += insns.size;
 347         if (first == null) {
 348             first = insns.first;
 349             last = insns.last;
 350         } else {
 351             AbstractInsnNode elem = insns.last;
 352             first.prev = elem;
 353             elem.next = first;
 354             first = insns.first;
 355         }
 356         cache = null;
 357         insns.removeAll(false);
 358     }
 359 
 360     /**
 361      * Inserts the given instruction after the specified instruction.
 362      *
 363      * @param location
 364      *            an instruction <i>of this list</i> after which insn must be
 365      *            inserted.
 366      * @param insn
 367      *            the instruction to be inserted, <i>which must not belong to
 368      *            any {@link InsnList}</i>.
 369      */
 370     public void insert(final AbstractInsnNode location,
 371             final AbstractInsnNode insn) {
 372         ++size;
 373         AbstractInsnNode next = location.next;
 374         if (next == null) {
 375             last = insn;
 376         } else {
 377             next.prev = insn;
 378         }
 379         location.next = insn;
 380         insn.next = next;
 381         insn.prev = location;
 382         cache = null;
 383         insn.index = 0; // insn now belongs to an InsnList
 384     }
 385 
 386     /**
 387      * Inserts the given instructions after the specified instruction.
 388      *
 389      * @param location
 390      *            an instruction <i>of this list</i> after which the
 391      *            instructions must be inserted.
 392      * @param insns
 393      *            the instruction list to be inserted, which is cleared during
 394      *            the process. This list must be different from 'this'.
 395      */
 396     public void insert(final AbstractInsnNode location, final InsnList insns) {
 397         if (insns.size == 0) {
 398             return;
 399         }
 400         size += insns.size;
 401         AbstractInsnNode ifirst = insns.first;
 402         AbstractInsnNode ilast = insns.last;
 403         AbstractInsnNode next = location.next;
 404         if (next == null) {
 405             last = ilast;
 406         } else {
 407             next.prev = ilast;
 408         }
 409         location.next = ifirst;
 410         ilast.next = next;
 411         ifirst.prev = location;
 412         cache = null;
 413         insns.removeAll(false);
 414     }
 415 
 416     /**
 417      * Inserts the given instruction before the specified instruction.
 418      *
 419      * @param location
 420      *            an instruction <i>of this list</i> before which insn must be
 421      *            inserted.
 422      * @param insn
 423      *            the instruction to be inserted, <i>which must not belong to
 424      *            any {@link InsnList}</i>.
 425      */
 426     public void insertBefore(final AbstractInsnNode location,
 427             final AbstractInsnNode insn) {
 428         ++size;
 429         AbstractInsnNode prev = location.prev;
 430         if (prev == null) {
 431             first = insn;
 432         } else {
 433             prev.next = insn;
 434         }
 435         location.prev = insn;
 436         insn.next = location;
 437         insn.prev = prev;
 438         cache = null;
 439         insn.index = 0; // insn now belongs to an InsnList
 440     }
 441 
 442     /**
 443      * Inserts the given instructions before the specified instruction.
 444      *
 445      * @param location
 446      *            an instruction <i>of this list</i> before which the
 447      *            instructions must be inserted.
 448      * @param insns
 449      *            the instruction list to be inserted, which is cleared during
 450      *            the process. This list must be different from 'this'.
 451      */
 452     public void insertBefore(final AbstractInsnNode location,
 453             final InsnList insns) {
 454         if (insns.size == 0) {
 455             return;
 456         }
 457         size += insns.size;
 458         AbstractInsnNode ifirst = insns.first;
 459         AbstractInsnNode ilast = insns.last;
 460         AbstractInsnNode prev = location.prev;
 461         if (prev == null) {
 462             first = ifirst;
 463         } else {
 464             prev.next = ifirst;
 465         }
 466         location.prev = ilast;
 467         ilast.next = location;
 468         ifirst.prev = prev;
 469         cache = null;
 470         insns.removeAll(false);
 471     }
 472 
 473     /**
 474      * Removes the given instruction from this list.
 475      *
 476      * @param insn
 477      *            the instruction <i>of this list</i> that must be removed.
 478      */
 479     public void remove(final AbstractInsnNode insn) {
 480         --size;
 481         AbstractInsnNode next = insn.next;
 482         AbstractInsnNode prev = insn.prev;
 483         if (next == null) {
 484             if (prev == null) {
 485                 first = null;
 486                 last = null;
 487             } else {
 488                 prev.next = null;
 489                 last = prev;
 490             }
 491         } else {
 492             if (prev == null) {
 493                 first = next;
 494                 next.prev = null;
 495             } else {
 496                 prev.next = next;
 497                 next.prev = prev;
 498             }
 499         }
 500         cache = null;
 501         insn.index = -1; // insn no longer belongs to an InsnList
 502         insn.prev = null;
 503         insn.next = null;
 504     }
 505 
 506     /**
 507      * Removes all of the instructions of this list.
 508      *
 509      * @param mark
 510      *            if the instructions must be marked as no longer belonging to
 511      *            any {@link InsnList}.
 512      */
 513     void removeAll(final boolean mark) {
 514         if (mark) {
 515             AbstractInsnNode insn = first;
 516             while (insn != null) {
 517                 AbstractInsnNode next = insn.next;
 518                 insn.index = -1; // insn no longer belongs to an InsnList
 519                 insn.prev = null;
 520                 insn.next = null;
 521                 insn = next;
 522             }
 523         }
 524         size = 0;
 525         first = null;
 526         last = null;
 527         cache = null;
 528     }
 529 
 530     /**
 531      * Removes all of the instructions of this list.
 532      */
 533     public void clear() {
 534         removeAll(false);
 535     }
 536 
 537     /**
 538      * Reset all labels in the instruction list. This method should be called
 539      * before reusing same instructions list between several
 540      * <code>ClassWriter</code>s.
 541      */
 542     public void resetLabels() {
 543         AbstractInsnNode insn = first;
 544         while (insn != null) {
 545             if (insn instanceof LabelNode) {
 546                 ((LabelNode) insn).resetLabel();
 547             }
 548             insn = insn.next;
 549         }
 550     }
 551 
 552     // this class is not generified because it will create bridges
 553     private final class InsnListIterator implements ListIterator {
 554 
 555         AbstractInsnNode next;
 556 
 557         AbstractInsnNode prev;
 558 
 559         InsnListIterator(int index) {
 560             if (index == size()) {
 561                 next = null;
 562                 prev = getLast();
 563             } else {
 564                 next = get(index);
 565                 prev = next.prev;
 566             }
 567         }
 568 
 569         public boolean hasNext() {
 570             return next != null;
 571         }
 572 
 573         public Object next() {
 574             if (next == null) {
 575                 throw new NoSuchElementException();
 576             }
 577             AbstractInsnNode result = next;
 578             prev = result;
 579             next = result.next;
 580             return result;
 581         }
 582 
 583         public void remove() {
 584             InsnList.this.remove(prev);
 585             prev = prev.prev;
 586         }
 587 
 588         public boolean hasPrevious() {
 589             return prev != null;
 590         }
 591 
 592         public Object previous() {
 593             AbstractInsnNode result = prev;
 594             next = result;
 595             prev = result.prev;
 596             return result;
 597         }
 598 
 599         public int nextIndex() {
 600             if (next == null) {
 601                 return size();
 602             }
 603             if (cache == null) {
 604                 cache = toArray();
 605             }
 606             return next.index;
 607         }
 608 
 609         public int previousIndex() {
 610             if (prev == null) {
 611                 return -1;
 612             }
 613             if (cache == null) {
 614                 cache = toArray();
 615             }
 616             return prev.index;
 617         }
 618 
 619         public void add(Object o) {
 620             InsnList.this.insertBefore(next, (AbstractInsnNode) o);
 621             prev = (AbstractInsnNode) o;
 622         }
 623 
 624         public void set(Object o) {
 625             InsnList.this.set(next.prev, (AbstractInsnNode) o);
 626             prev = (AbstractInsnNode) o;
 627         }
 628     }
 629 }