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
 106      *         list 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 the index of the instruction that must be returned.
 129      * @return the instruction whose index is given.
 130      * @throws IndexOutOfBoundsException if (index {@literal <} 0 || index {@literal >=} size()).
 131      */
 132     public AbstractInsnNode get(final int index) {
 133         if (index < 0 || index >= size) {
 134             throw new IndexOutOfBoundsException();
 135         }
 136         if (cache == null) {
 137             cache = toArray();
 138         }
 139         return cache[index];
 140     }
 141 
 142     /**
 143      * Returns <tt>true</tt> if the given instruction belongs to this list.
 144      * This method always scans the instructions of this list until it finds the
 145      * given instruction or reaches the end of the list.
 146      *
 147      * @param insn an instruction.
 148      * @return <tt>true</tt> if the given instruction belongs to this list.
 149      */
 150     public boolean contains(final AbstractInsnNode insn) {
 151         AbstractInsnNode i = first;
 152         while (i != null && i != insn) {
 153             i = i.next;
 154         }
 155         return i != null;
 156     }
 157 
 158     /**
 159      * Returns the index of the given instruction in this list. This method
 160      * builds a cache of the instruction indexes to avoid scanning the whole
 161      * list each time it is called. Once the cache is built, this method run in
 162      * constant time. The cache is invalidated by all the methods that modify
 163      * the list.
 164      *
 165      * @param insn an instruction <i>of this list</i>.
 166      * @return the index of the given instruction in this list. <i>The result of
 167      *         this method is undefined if the given instruction does not belong
 168      *         to this list</i>. Use {@link #contains contains} to test if an
 169      *         instruction belongs to an instruction list or not.
 170      */
 171     public int indexOf(final AbstractInsnNode insn) {
 172         if (cache == null) {
 173             cache = toArray();
 174         }
 175         return insn.index;
 176     }
 177 
 178     /**
 179      * Makes the given visitor visit all of the instructions in this list.
 180      *
 181      * @param mv the method visitor that must visit the instructions.
 182      */
 183     public void accept(final MethodVisitor mv) {
 184         AbstractInsnNode insn = first;
 185         while (insn != null) {
 186             insn.accept(mv);
 187             insn = insn.next;
 188         }
 189     }
 190 
 191     /**
 192      * Returns an iterator over the instructions in this list.
 193      *
 194      * @return an iterator over the instructions in this list.
 195      */
 196     public ListIterator<AbstractInsnNode> iterator() {
 197         return iterator(0);
 198     }
 199 
 200     /**
 201      * Returns an iterator over the instructions in this list.
 202      *
 203      * @return an iterator over the instructions in this list.
 204      */
 205     @SuppressWarnings("unchecked")
 206     public ListIterator<AbstractInsnNode> iterator(int index) {
 207         return new InsnListIterator(index);
 208     }
 209 
 210     /**
 211      * Returns an array containing all of the instructions in this list.
 212      *
 213      * @return an array containing all of the instructions in this list.
 214      */
 215     public AbstractInsnNode[] toArray() {
 216         int i = 0;
 217         AbstractInsnNode elem = first;
 218         AbstractInsnNode[] insns = new AbstractInsnNode[size];
 219         while (elem != null) {
 220             insns[i] = elem;
 221             elem.index = i++;
 222             elem = elem.next;
 223         }
 224         return insns;
 225     }
 226 
 227     /**
 228      * Replaces an instruction of this list with another instruction.
 229      *
 230      * @param location an instruction <i>of this list</i>.
 231      * @param insn another instruction, <i>which must not belong to any
 232      *        {@link InsnList}</i>.
 233      */
 234     public void set(final AbstractInsnNode location, final AbstractInsnNode insn) {
 235         AbstractInsnNode next = location.next;
 236         insn.next = next;
 237         if (next != null) {
 238             next.prev = insn;
 239         } else {
 240             last = insn;
 241         }
 242         AbstractInsnNode prev = location.prev;
 243         insn.prev = prev;
 244         if (prev != null) {
 245             prev.next = insn;
 246         } else {
 247             first = insn;
 248         }
 249         if (cache != null) {
 250             int index = location.index;
 251             cache[index] = insn;
 252             insn.index = index;
 253         } else {
 254             insn.index = 0; // insn now belongs to an InsnList
 255         }
 256         location.index = -1; // i no longer belongs to an InsnList
 257         location.prev = null;
 258         location.next = null;
 259     }
 260 
 261     /**
 262      * Adds the given instruction to the end of this list.
 263      *
 264      * @param insn an instruction, <i>which must not belong to any
 265      *        {@link InsnList}</i>.
 266      */
 267     public void add(final AbstractInsnNode insn) {
 268         ++size;
 269         if (last == null) {
 270             first = insn;
 271             last = insn;
 272         } else {
 273             last.next = insn;
 274             insn.prev = last;
 275         }
 276         last = insn;
 277         cache = null;
 278         insn.index = 0; // insn now belongs to an InsnList
 279     }
 280 
 281     /**
 282      * Adds the given instructions to the end of this list.
 283      *
 284      * @param insns an instruction list, which is cleared during the process.
 285      *        This list must be different from 'this'.
 286      */
 287     public void add(final InsnList insns) {
 288         if (insns.size == 0) {
 289             return;
 290         }
 291         size += insns.size;
 292         if (last == null) {
 293             first = insns.first;
 294             last = insns.last;
 295         } else {
 296             AbstractInsnNode elem = insns.first;
 297             last.next = elem;
 298             elem.prev = last;
 299             last = insns.last;
 300         }
 301         cache = null;
 302         insns.removeAll(false);
 303     }
 304 
 305     /**
 306      * Inserts the given instruction at the begining of this list.
 307      *
 308      * @param insn an instruction, <i>which must not belong to any
 309      *        {@link InsnList}</i>.
 310      */
 311     public void insert(final AbstractInsnNode insn) {
 312         ++size;
 313         if (first == null) {
 314             first = insn;
 315             last = insn;
 316         } else {
 317             first.prev = insn;
 318             insn.next = first;
 319         }
 320         first = insn;
 321         cache = null;
 322         insn.index = 0; // insn now belongs to an InsnList
 323     }
 324 
 325     /**
 326      * Inserts the given instructions at the begining of this list.
 327      *
 328      * @param insns an instruction list, which is cleared during the process.
 329      *        This list must be different from 'this'.
 330      */
 331     public void insert(final InsnList insns) {
 332         if (insns.size == 0) {
 333             return;
 334         }
 335         size += insns.size;
 336         if (first == null) {
 337             first = insns.first;
 338             last = insns.last;
 339         } else {
 340             AbstractInsnNode elem = insns.last;
 341             first.prev = elem;
 342             elem.next = first;
 343             first = insns.first;
 344         }
 345         cache = null;
 346         insns.removeAll(false);
 347     }
 348 
 349     /**
 350      * Inserts the given instruction after the specified instruction.
 351      *
 352      * @param location an instruction <i>of this list</i> after which insn must be
 353      *        inserted.
 354      * @param insn the instruction to be inserted, <i>which must not belong to
 355      *        any {@link InsnList}</i>.
 356      */
 357     public void insert(final AbstractInsnNode location, final AbstractInsnNode insn) {
 358         ++size;
 359         AbstractInsnNode next = location.next;
 360         if (next == null) {
 361             last = insn;
 362         } else {
 363             next.prev = insn;
 364         }
 365         location.next = insn;
 366         insn.next = next;
 367         insn.prev = location;
 368         cache = null;
 369         insn.index = 0; // insn now belongs to an InsnList
 370     }
 371 
 372     /**
 373      * Inserts the given instructions after the specified instruction.
 374      *
 375      * @param location an instruction <i>of this list</i> after which the
 376      *        instructions must be inserted.
 377      * @param insns the instruction list to be inserted, which is cleared during
 378      *        the process. This list must be different from 'this'.
 379      */
 380     public void insert(final AbstractInsnNode location, final InsnList insns) {
 381         if (insns.size == 0) {
 382             return;
 383         }
 384         size += insns.size;
 385         AbstractInsnNode ifirst = insns.first;
 386         AbstractInsnNode ilast = insns.last;
 387         AbstractInsnNode next = location.next;
 388         if (next == null) {
 389             last = ilast;
 390         } else {
 391             next.prev = ilast;
 392         }
 393         location.next = ifirst;
 394         ilast.next = next;
 395         ifirst.prev = location;
 396         cache = null;
 397         insns.removeAll(false);
 398     }
 399 
 400     /**
 401      * Inserts the given instruction before the specified instruction.
 402      *
 403      * @param location an instruction <i>of this list</i> before which insn must be
 404      *        inserted.
 405      * @param insn the instruction to be inserted, <i>which must not belong to
 406      *        any {@link InsnList}</i>.
 407      */
 408     public void insertBefore(final AbstractInsnNode location, final AbstractInsnNode insn) {
 409         ++size;
 410         AbstractInsnNode prev = location.prev;
 411         if (prev == null) {
 412             first = insn;
 413         } else {
 414             prev.next = insn;
 415         }
 416         location.prev = insn;
 417         insn.next = location;
 418         insn.prev = prev;
 419         cache = null;
 420         insn.index = 0; // insn now belongs to an InsnList
 421     }
 422 
 423     /**
 424      * Inserts the given instructions before the specified instruction.
 425      *
 426      * @param location  an instruction <i>of this list</i> before which the instructions
 427      *        must be inserted.
 428      * @param insns the instruction list to be inserted, which is cleared during
 429      *        the process. This list must be different from 'this'.
 430      */
 431     public void insertBefore(final AbstractInsnNode location, final InsnList insns) {
 432         if (insns.size == 0) {
 433             return;
 434         }
 435         size += insns.size;
 436         AbstractInsnNode ifirst = insns.first;
 437         AbstractInsnNode ilast = insns.last;
 438         AbstractInsnNode prev = location .prev;
 439         if (prev == null) {
 440             first = ifirst;
 441         } else {
 442             prev.next = ifirst;
 443         }
 444         location .prev = ilast;
 445         ilast.next = location ;
 446         ifirst.prev = prev;
 447         cache = null;
 448         insns.removeAll(false);
 449     }
 450 
 451 
 452 
 453     /**
 454      * Removes the given instruction from this list.
 455      *
 456      * @param insn the instruction <i>of this list</i> that must be removed.
 457      */
 458     public void remove(final AbstractInsnNode insn) {
 459         --size;
 460         AbstractInsnNode next = insn.next;
 461         AbstractInsnNode prev = insn.prev;
 462         if (next == null) {
 463             if (prev == null) {
 464                 first = null;
 465                 last = null;
 466             } else {
 467                 prev.next = null;
 468                 last = prev;
 469             }
 470         } else {
 471             if (prev == null) {
 472                 first = next;
 473                 next.prev = null;
 474             } else {
 475                 prev.next = next;
 476                 next.prev = prev;
 477             }
 478         }
 479         cache = null;
 480         insn.index = -1; // insn no longer belongs to an InsnList
 481         insn.prev = null;
 482         insn.next = null;
 483     }
 484 
 485     /**
 486      * Removes all of the instructions of this list.
 487      *
 488      * @param mark if the instructions must be marked as no longer belonging to
 489      *        any {@link InsnList}.
 490      */
 491     void removeAll(final boolean mark) {
 492         if (mark) {
 493             AbstractInsnNode insn = first;
 494             while (insn != null) {
 495                 AbstractInsnNode next = insn.next;
 496                 insn.index = -1; // insn no longer belongs to an InsnList
 497                 insn.prev = null;
 498                 insn.next = null;
 499                 insn = next;
 500             }
 501         }
 502         size = 0;
 503         first = null;
 504         last = null;
 505         cache = null;
 506     }
 507 
 508     /**
 509      * Removes all of the instructions of this list.
 510      */
 511     public void clear() {
 512         removeAll(false);
 513     }
 514 
 515     /**
 516      * Reset all labels in the instruction list. This method should be called
 517      * before reusing same instructions list between several
 518      * <code>ClassWriter</code>s.
 519      */
 520     public void resetLabels() {
 521         AbstractInsnNode insn = first;
 522         while (insn != null) {
 523             if (insn instanceof LabelNode) {
 524                 ((LabelNode) insn).resetLabel();
 525             }
 526             insn = insn.next;
 527         }
 528     }
 529 
 530     // this class is not generified because it will create bridges
 531     private final class InsnListIterator implements ListIterator/*<AbstractInsnNode>*/ {
 532 
 533         AbstractInsnNode next;
 534 
 535         AbstractInsnNode prev;
 536 
 537         InsnListIterator(int index) {
 538             if(index==size()) {
 539                 next = null;
 540                 prev = getLast();
 541             } else {
 542                 next = get(index);
 543                 prev = next.prev;
 544             }
 545         }
 546 
 547         public boolean hasNext() {
 548             return next != null;
 549         }
 550 
 551         public Object next() {
 552             if (next == null) {
 553                 throw new NoSuchElementException();
 554             }
 555             AbstractInsnNode result = next;
 556             prev = result;
 557             next = result.next;
 558             return result;
 559         }
 560 
 561         public void remove() {
 562             InsnList.this.remove(prev);
 563             prev = prev.prev;
 564         }
 565 
 566         public boolean hasPrevious() {
 567             return prev != null;
 568         }
 569 
 570         public Object previous() {
 571             AbstractInsnNode result = prev;
 572             next = result;
 573             prev = result.prev;
 574             return result;
 575         }
 576 
 577         public int nextIndex() {
 578             if (next == null) {
 579                 return size();
 580             }
 581             if (cache == null) {
 582                 cache = toArray();
 583             }
 584             return next.index;
 585         }
 586 
 587         public int previousIndex() {
 588             if (prev == null) {
 589                 return -1;
 590             }
 591             if (cache == null) {
 592                 cache = toArray();
 593             }
 594             return prev.index;
 595         }
 596 
 597         public void add(Object o) {
 598             InsnList.this.insertBefore(next, (AbstractInsnNode) o);
 599             prev = (AbstractInsnNode) o;
 600         }
 601 
 602         public void set(Object o) {
 603             InsnList.this.set(next.prev, (AbstractInsnNode) o);
 604             prev = (AbstractInsnNode) o;
 605         }
 606     }
 607 }