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