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 }
--- EOF ---