KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > asm > commons > GeneratorAdapter


1 /***
2  * ASM: a very small and fast Java bytecode manipulation framework
3  * Copyright (c) 2000-2005 INRIA, France Telecom
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the copyright holders nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGE.
29  */

30 package com.tc.asm.commons;
31
32 import java.util.ArrayList JavaDoc;
33 import java.util.Arrays JavaDoc;
34 import java.util.List JavaDoc;
35
36 import com.tc.asm.ClassVisitor;
37 import com.tc.asm.Label;
38 import com.tc.asm.MethodVisitor;
39 import com.tc.asm.Opcodes;
40 import com.tc.asm.Type;
41
42 /**
43  * A {@link org.objectweb.asm.MethodAdapter} with convenient methods to generate
44  * code. For example, using this adapter, the class below
45  *
46  * <pre>
47  * public class Example {
48  * public static void main(String[] args) {
49  * System.out.println(&quot;Hello world!&quot;);
50  * }
51  * }
52  * </pre>
53  *
54  * can be generated as follows:
55  *
56  * <pre>
57  * ClassWriter cw = new ClassWriter(true);
58  * cw.visit(V1_1, ACC_PUBLIC, &quot;Example&quot;, null, &quot;java/lang/Object&quot;, null);
59  *
60  * Method m = Method.getMethod(&quot;void &lt;init&gt; ()&quot;);
61  * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
62  * mg.loadThis();
63  * mg.invokeConstructor(Type.getType(Object.class), m);
64  * mg.returnValue();
65  * mg.endMethod();
66  *
67  * m = Method.getMethod(&quot;void main (String[])&quot;);
68  * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
69  * mg.getStatic(Type.getType(System.class), &quot;out&quot;, Type.getType(PrintStream.class));
70  * mg.push(&quot;Hello world!&quot;);
71  * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod(&quot;void println (String)&quot;));
72  * mg.returnValue();
73  * mg.endMethod();
74  *
75  * cw.visitEnd();
76  * </pre>
77  *
78  * @author Juozas Baliuka
79  * @author Chris Nokleberg
80  * @author Eric Bruneton
81  */

82 public class GeneratorAdapter extends LocalVariablesSorter {
83
84     private final static Type BYTE_TYPE = Type.getType("Ljava/lang/Byte;");
85
86     private final static Type BOOLEAN_TYPE = Type.getType("Ljava/lang/Boolean;");
87
88     private final static Type SHORT_TYPE = Type.getType("Ljava/lang/Short;");
89
90     private final static Type CHARACTER_TYPE = Type.getType("Ljava/lang/Character;");
91
92     private final static Type INTEGER_TYPE = Type.getType("Ljava/lang/Integer;");
93
94     private final static Type FLOAT_TYPE = Type.getType("Ljava/lang/Float;");
95
96     private final static Type LONG_TYPE = Type.getType("Ljava/lang/Long;");
97
98     private final static Type DOUBLE_TYPE = Type.getType("Ljava/lang/Double;");
99
100     private final static Type NUMBER_TYPE = Type.getType("Ljava/lang/Number;");
101
102     private final static Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;");
103
104     private final static Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()");
105
106     private final static Method CHAR_VALUE = Method.getMethod("char charValue()");
107
108     private final static Method INT_VALUE = Method.getMethod("int intValue()");
109
110     private final static Method FLOAT_VALUE = Method.getMethod("float floatValue()");
111
112     private final static Method LONG_VALUE = Method.getMethod("long longValue()");
113
114     private final static Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
115
116     /**
117      * Constant for the {@link #math math} method.
118      */

119     public final static int ADD = Opcodes.IADD;
120
121     /**
122      * Constant for the {@link #math math} method.
123      */

124     public final static int SUB = Opcodes.ISUB;
125
126     /**
127      * Constant for the {@link #math math} method.
128      */

129     public final static int MUL = Opcodes.IMUL;
130
131     /**
132      * Constant for the {@link #math math} method.
133      */

134     public final static int DIV = Opcodes.IDIV;
135
136     /**
137      * Constant for the {@link #math math} method.
138      */

139     public final static int REM = Opcodes.IREM;
140
141     /**
142      * Constant for the {@link #math math} method.
143      */

144     public final static int NEG = Opcodes.INEG;
145
146     /**
147      * Constant for the {@link #math math} method.
148      */

149     public final static int SHL = Opcodes.ISHL;
150
151     /**
152      * Constant for the {@link #math math} method.
153      */

154     public final static int SHR = Opcodes.ISHR;
155
156     /**
157      * Constant for the {@link #math math} method.
158      */

159     public final static int USHR = Opcodes.IUSHR;
160
161     /**
162      * Constant for the {@link #math math} method.
163      */

164     public final static int AND = Opcodes.IAND;
165
166     /**
167      * Constant for the {@link #math math} method.
168      */

169     public final static int OR = Opcodes.IOR;
170
171     /**
172      * Constant for the {@link #math math} method.
173      */

174     public final static int XOR = Opcodes.IXOR;
175
176     /**
177      * Constant for the {@link #ifCmp ifCmp} method.
178      */

179     public final static int EQ = Opcodes.IFEQ;
180
181     /**
182      * Constant for the {@link #ifCmp ifCmp} method.
183      */

184     public final static int NE = Opcodes.IFNE;
185
186     /**
187      * Constant for the {@link #ifCmp ifCmp} method.
188      */

189     public final static int LT = Opcodes.IFLT;
190
191     /**
192      * Constant for the {@link #ifCmp ifCmp} method.
193      */

194     public final static int GE = Opcodes.IFGE;
195
196     /**
197      * Constant for the {@link #ifCmp ifCmp} method.
198      */

199     public final static int GT = Opcodes.IFGT;
200
201     /**
202      * Constant for the {@link #ifCmp ifCmp} method.
203      */

204     public final static int LE = Opcodes.IFLE;
205
206     /**
207      * Access flags of the method visited by this adapter.
208      */

209     private final int access;
210
211     /**
212      * Return type of the method visited by this adapter.
213      */

214     private final Type returnType;
215
216     /**
217      * Argument types of the method visited by this adapter.
218      */

219     private final Type[] argumentTypes;
220
221     /**
222      * Types of the local variables of the method visited by this adapter.
223      */

224     private final List JavaDoc localTypes;
225
226     /**
227      * Creates a new {@link GeneratorAdapter}.
228      *
229      * @param mv the method visitor to which this adapter delegates calls.
230      * @param access the method's access flags (see {@link Opcodes}).
231      * @param name the method's name.
232      * @param desc the method's descriptor (see {@link Type Type}).
233      */

234     public GeneratorAdapter(
235         MethodVisitor mv,
236         int access,
237         String JavaDoc name,
238         String JavaDoc desc)
239     {
240         super(access, desc, mv);
241         this.access = access;
242         this.returnType = Type.getReturnType(desc);
243         this.argumentTypes = Type.getArgumentTypes(desc);
244         this.localTypes = new ArrayList JavaDoc();
245     }
246
247     /**
248      * Creates a new {@link GeneratorAdapter}.
249      *
250      * @param access access flags of the adapted method.
251      * @param method the adapted method.
252      * @param mv the method visitor to which this adapter delegates calls.
253      */

254     public GeneratorAdapter(
255         final int access,
256         final Method method,
257         final MethodVisitor mv)
258     {
259         super(access, method.getDescriptor(), mv);
260         this.access = access;
261         this.returnType = method.getReturnType();
262         this.argumentTypes = method.getArgumentTypes();
263         this.localTypes = new ArrayList JavaDoc();
264     }
265
266     /**
267      * Creates a new {@link GeneratorAdapter}.
268      *
269      * @param access access flags of the adapted method.
270      * @param method the adapted method.
271      * @param signature the signature of the adapted method (may be
272      * <tt>null</tt>).
273      * @param exceptions the exceptions thrown by the adapted method (may be
274      * <tt>null</tt>).
275      * @param cv the class visitor to which this adapter delegates calls.
276      */

277     public GeneratorAdapter(
278         final int access,
279         final Method method,
280         final String JavaDoc signature,
281         final Type[] exceptions,
282         final ClassVisitor cv)
283     {
284         this(access, method, cv.visitMethod(access,
285                 method.getName(),
286                 method.getDescriptor(),
287                 signature,
288                 getInternalNames(exceptions)));
289     }
290
291     /**
292      * Returns the internal names of the given types.
293      *
294      * @param types a set of types.
295      * @return the internal names of the given types.
296      */

297     private static String JavaDoc[] getInternalNames(final Type[] types) {
298         if (types == null) {
299             return null;
300         }
301         String JavaDoc[] names = new String JavaDoc[types.length];
302         for (int i = 0; i < names.length; ++i) {
303             names[i] = types[i].getInternalName();
304         }
305         return names;
306     }
307
308     // ------------------------------------------------------------------------
309
// Instructions to push constants on the stack
310
// ------------------------------------------------------------------------
311

312     /**
313      * Generates the instruction to push the given value on the stack.
314      *
315      * @param value the value to be pushed on the stack.
316      */

317     public void push(final boolean value) {
318         push(value ? 1 : 0);
319     }
320
321     /**
322      * Generates the instruction to push the given value on the stack.
323      *
324      * @param value the value to be pushed on the stack.
325      */

326     public void push(final int value) {
327         if (value >= -1 && value <= 5) {
328             mv.visitInsn(Opcodes.ICONST_0 + value);
329         } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
330             mv.visitIntInsn(Opcodes.BIPUSH, value);
331         } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
332             mv.visitIntInsn(Opcodes.SIPUSH, value);
333         } else {
334             mv.visitLdcInsn(new Integer JavaDoc(value));
335         }
336     }
337
338     /**
339      * Generates the instruction to push the given value on the stack.
340      *
341      * @param value the value to be pushed on the stack.
342      */

343     public void push(final long value) {
344         if (value == 0L || value == 1L) {
345             mv.visitInsn(Opcodes.LCONST_0 + (int) value);
346         } else {
347             mv.visitLdcInsn(new Long JavaDoc(value));
348         }
349     }
350
351     /**
352      * Generates the instruction to push the given value on the stack.
353      *
354      * @param value the value to be pushed on the stack.
355      */

356     public void push(final float value) {
357         int bits = Float.floatToIntBits(value);
358         if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2
359
mv.visitInsn(Opcodes.FCONST_0 + (int) value);
360         } else {
361             mv.visitLdcInsn(new Float JavaDoc(value));
362         }
363     }
364
365     /**
366      * Generates the instruction to push the given value on the stack.
367      *
368      * @param value the value to be pushed on the stack.
369      */

370     public void push(final double value) {
371         long bits = Double.doubleToLongBits(value);
372         if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d
373
mv.visitInsn(Opcodes.DCONST_0 + (int) value);
374         } else {
375             mv.visitLdcInsn(new Double JavaDoc(value));
376         }
377     }
378
379     /**
380      * Generates the instruction to push the given value on the stack.
381      *
382      * @param value the value to be pushed on the stack. May be <tt>null</tt>.
383      */

384     public void push(final String JavaDoc value) {
385         if (value == null) {
386             mv.visitInsn(Opcodes.ACONST_NULL);
387         } else {
388             mv.visitLdcInsn(value);
389         }
390     }
391
392     /**
393      * Generates the instruction to push the given value on the stack.
394      *
395      * @param value the value to be pushed on the stack.
396      */

397     public void push(final Type value) {
398         if (value == null) {
399             mv.visitInsn(Opcodes.ACONST_NULL);
400         } else {
401             mv.visitLdcInsn(value);
402         }
403     }
404
405     // ------------------------------------------------------------------------
406
// Instructions to load and store method arguments
407
// ------------------------------------------------------------------------
408

409     /**
410      * Returns the index of the given method argument in the frame's local
411      * variables array.
412      *
413      * @param arg the index of a method argument.
414      * @return the index of the given method argument in the frame's local
415      * variables array.
416      */

417     private int getArgIndex(final int arg) {
418         int index = ((access & Opcodes.ACC_STATIC) == 0 ? 1 : 0);
419         for (int i = 0; i < arg; i++) {
420             index += argumentTypes[i].getSize();
421         }
422         return index;
423     }
424
425     /**
426      * Generates the instruction to push a local variable on the stack.
427      *
428      * @param type the type of the local variable to be loaded.
429      * @param index an index in the frame's local variables array.
430      */

431     private void loadInsn(final Type type, final int index) {
432         mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index);
433     }
434
435     /**
436      * Generates the instruction to store the top stack value in a local
437      * variable.
438      *
439      * @param type the type of the local variable to be stored.
440      * @param index an index in the frame's local variables array.
441      */

442     private void storeInsn(final Type type, final int index) {
443         mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index);
444     }
445
446     /**
447      * Generates the instruction to load 'this' on the stack.
448      */

449     public void loadThis() {
450         if ((access & Opcodes.ACC_STATIC) != 0) {
451             throw new IllegalStateException JavaDoc("no 'this' pointer within static method");
452         }
453         mv.visitVarInsn(Opcodes.ALOAD, 0);
454     }
455
456     /**
457      * Generates the instruction to load the given method argument on the stack.
458      *
459      * @param arg the index of a method argument.
460      */

461     public void loadArg(final int arg) {
462         loadInsn(argumentTypes[arg], getArgIndex(arg));
463     }
464
465     /**
466      * Generates the instructions to load the given method arguments on the
467      * stack.
468      *
469      * @param arg the index of the first method argument to be loaded.
470      * @param count the number of method arguments to be loaded.
471      */

472     public void loadArgs(final int arg, final int count) {
473         int index = getArgIndex(arg);
474         for (int i = 0; i < count; ++i) {
475             Type t = argumentTypes[arg + i];
476             loadInsn(t, index);
477             index += t.getSize();
478         }
479     }
480
481     /**
482      * Generates the instructions to load all the method arguments on the stack.
483      */

484     public void loadArgs() {
485         loadArgs(0, argumentTypes.length);
486     }
487
488     /**
489      * Generates the instructions to load all the method arguments on the stack,
490      * as a single object array.
491      */

492     public void loadArgArray() {
493         push(argumentTypes.length);
494         newArray(OBJECT_TYPE);
495         for (int i = 0; i < argumentTypes.length; i++) {
496             dup();
497             push(i);
498             loadArg(i);
499             box(argumentTypes[i]);
500             arrayStore(OBJECT_TYPE);
501         }
502     }
503
504     /**
505      * Generates the instruction to store the top stack value in the given
506      * method argument.
507      *
508      * @param arg the index of a method argument.
509      */

510     public void storeArg(final int arg) {
511         storeInsn(argumentTypes[arg], getArgIndex(arg));
512     }
513
514     // ------------------------------------------------------------------------
515
// Instructions to load and store local variables
516
// ------------------------------------------------------------------------
517

518     /**
519      * Creates a new local variable of the given type.
520      *
521      * @param type the type of the local variable to be created.
522      * @return the identifier of the newly created local variable.
523      */

524     public int newLocal(final Type type) {
525         int local = super.newLocal(type.getSize());
526         setLocalType(local, type);
527         return local;
528     }
529
530     /**
531      * Returns the type of the given local variable.
532      *
533      * @param local a local variable identifier, as returned by {@link #newLocal
534      * newLocal}.
535      * @return the type of the given local variable.
536      */

537     public Type getLocalType(final int local) {
538         return (Type) localTypes.get(local - firstLocal);
539     }
540
541     /**
542      * Sets the current type of the given local variable.
543      *
544      * @param local a local variable identifier, as returned by {@link #newLocal
545      * newLocal}.
546      * @param type the type of the value being stored in the local variable
547      */

548     private void setLocalType(final int local, final Type type) {
549         int index = local - firstLocal;
550         while (localTypes.size() < index + 1)
551             localTypes.add(null);
552         localTypes.set(index, type);
553     }
554
555     /**
556      * Generates the instruction to load the given local variable on the stack.
557      *
558      * @param local a local variable identifier, as returned by {@link #newLocal
559      * newLocal}.
560      */

561     public void loadLocal(final int local) {
562         loadInsn(getLocalType(local), local);
563     }
564
565     /**
566      * Generates the instruction to load the given local variable on the stack.
567      *
568      * @param local a local variable identifier, as returned by {@link #newLocal
569      * newLocal}.
570      * @param type the type of this local variable.
571      */

572     public void loadLocal(final int local, final Type type) {
573         setLocalType(local, type);
574         loadInsn(type, local);
575     }
576
577     /**
578      * Generates the instruction to store the top stack value in the given local
579      * variable.
580      *
581      * @param local a local variable identifier, as returned by {@link #newLocal
582      * newLocal}.
583      */

584     public void storeLocal(final int local) {
585         storeInsn(getLocalType(local), local);
586     }
587
588     /**
589      * Generates the instruction to store the top stack value in the given local
590      * variable.
591      *
592      * @param local a local variable identifier, as returned by {@link #newLocal
593      * newLocal}.
594      * @param type the type of this local variable.
595      */

596     public void storeLocal(final int local, final Type type) {
597         setLocalType(local, type);
598         storeInsn(type, local);
599     }
600
601     /**
602      * Generates the instruction to load an element from an array.
603      *
604      * @param type the type of the array element to be loaded.
605      */

606     public void arrayLoad(final Type type) {
607         mv.visitInsn(type.getOpcode(Opcodes.IALOAD));
608     }
609
610     /**
611      * Generates the instruction to store an element in an array.
612      *
613      * @param type the type of the array element to be stored.
614      */

615     public void arrayStore(final Type type) {
616         mv.visitInsn(type.getOpcode(Opcodes.IASTORE));
617     }
618
619     // ------------------------------------------------------------------------
620
// Instructions to manage the stack
621
// ------------------------------------------------------------------------
622

623     /**
624      * Generates a POP instruction.
625      */

626     public void pop() {
627         mv.visitInsn(Opcodes.POP);
628     }
629
630     /**
631      * Generates a POP2 instruction.
632      */

633     public void pop2() {
634         mv.visitInsn(Opcodes.POP2);
635     }
636
637     /**
638      * Generates a DUP instruction.
639      */

640     public void dup() {
641         mv.visitInsn(Opcodes.DUP);
642     }
643
644     /**
645      * Generates a DUP2 instruction.
646      */

647     public void dup2() {
648         mv.visitInsn(Opcodes.DUP2);
649     }
650
651     /**
652      * Generates a DUP_X1 instruction.
653      */

654     public void dupX1() {
655         mv.visitInsn(Opcodes.DUP_X1);
656     }
657
658     /**
659      * Generates a DUP_X2 instruction.
660      */

661     public void dupX2() {
662         mv.visitInsn(Opcodes.DUP_X2);
663     }
664
665     /**
666      * Generates a DUP2_X1 instruction.
667      */

668     public void dup2X1() {
669         mv.visitInsn(Opcodes.DUP2_X1);
670     }
671
672     /**
673      * Generates a DUP2_X2 instruction.
674      */

675     public void dup2X2() {
676         mv.visitInsn(Opcodes.DUP2_X2);
677     }
678
679     /**
680      * Generates a SWAP instruction.
681      */

682     public void swap() {
683         mv.visitInsn(Opcodes.SWAP);
684     }
685
686     /**
687      * Generates the instructions to swap the top two stack values.
688      *
689      * @param prev type of the top - 1 stack value.
690      * @param type type of the top stack value.
691      */

692     public void swap(final Type prev, final Type type) {
693         if (type.getSize() == 1) {
694             if (prev.getSize() == 1) {
695                 swap(); // same as dupX1(), pop();
696
} else {
697                 dupX2();
698                 pop();
699             }
700         } else {
701             if (prev.getSize() == 1) {
702                 dup2X1();
703                 pop2();
704             } else {
705                 dup2X2();
706                 pop2();
707             }
708         }
709     }
710
711     // ------------------------------------------------------------------------
712
// Instructions to do mathematical and logical operations
713
// ------------------------------------------------------------------------
714

715     /**
716      * Generates the instruction to do the specified mathematical or logical
717      * operation.
718      *
719      * @param op a mathematical or logical operation. Must be one of ADD, SUB,
720      * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR.
721      * @param type the type of the operand(s) for this operation.
722      */

723     public void math(final int op, final Type type) {
724         mv.visitInsn(type.getOpcode(op));
725     }
726
727     /**
728      * Generates the instructions to compute the bitwise negation of the top
729      * stack value.
730      */

731     public void not() {
732         mv.visitInsn(Opcodes.ICONST_1);
733         mv.visitInsn(Opcodes.IXOR);
734     }
735
736     /**
737      * Generates the instruction to increment the given local variable.
738      *
739      * @param local the local variable to be incremented.
740      * @param amount the amount by which the local variable must be incremented.
741      */

742     public void iinc(final int local, final int amount) {
743         mv.visitIincInsn(local, amount);
744     }
745
746     /**
747      * Generates the instructions to cast a numerical value from one type to
748      * another.
749      *
750      * @param from the type of the top stack value
751      * @param to the type into which this value must be cast.
752      */

753     public void cast(final Type from, final Type to) {
754         if (from != to) {
755             if (from == Type.DOUBLE_TYPE) {
756                 if (to == Type.FLOAT_TYPE) {
757                     mv.visitInsn(Opcodes.D2F);
758                 } else if (to == Type.LONG_TYPE) {
759                     mv.visitInsn(Opcodes.D2L);
760                 } else {
761                     mv.visitInsn(Opcodes.D2I);
762                     cast(Type.INT_TYPE, to);
763                 }
764             } else if (from == Type.FLOAT_TYPE) {
765                 if (to == Type.DOUBLE_TYPE) {
766                     mv.visitInsn(Opcodes.F2D);
767                 } else if (to == Type.LONG_TYPE) {
768                     mv.visitInsn(Opcodes.F2L);
769                 } else {
770                     mv.visitInsn(Opcodes.F2I);
771                     cast(Type.INT_TYPE, to);
772                 }
773             } else if (from == Type.LONG_TYPE) {
774                 if (to == Type.DOUBLE_TYPE) {
775                     mv.visitInsn(Opcodes.L2D);
776                 } else if (to == Type.FLOAT_TYPE) {
777                     mv.visitInsn(Opcodes.L2F);
778                 } else {
779                     mv.visitInsn(Opcodes.L2I);
780                     cast(Type.INT_TYPE, to);
781                 }
782             } else {
783                 if (to == Type.BYTE_TYPE) {
784                     mv.visitInsn(Opcodes.I2B);
785                 } else if (to == Type.CHAR_TYPE) {
786                     mv.visitInsn(Opcodes.I2C);
787                 } else if (to == Type.DOUBLE_TYPE) {
788                     mv.visitInsn(Opcodes.I2D);
789                 } else if (to == Type.FLOAT_TYPE) {
790                     mv.visitInsn(Opcodes.I2F);
791                 } else if (to == Type.LONG_TYPE) {
792                     mv.visitInsn(Opcodes.I2L);
793                 } else if (to == Type.SHORT_TYPE) {
794                     mv.visitInsn(Opcodes.I2S);
795                 }
796             }
797         }
798     }
799
800     // ------------------------------------------------------------------------
801
// Instructions to do boxing and unboxing operations
802
// ------------------------------------------------------------------------
803

804     /**
805      * Generates the instructions to box the top stack value. This value is
806      * replaced by its boxed equivalent on top of the stack.
807      *
808      * @param type the type of the top stack value.
809      */

810     public void box(final Type type) {
811         if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
812             return;
813         }
814         if (type == Type.VOID_TYPE) {
815             push((String JavaDoc) null);
816         } else {
817             Type boxed = type;
818             switch (type.getSort()) {
819                 case Type.BYTE:
820                     boxed = BYTE_TYPE;
821                     break;
822                 case Type.BOOLEAN:
823                     boxed = BOOLEAN_TYPE;
824                     break;
825                 case Type.SHORT:
826                     boxed = SHORT_TYPE;
827                     break;
828                 case Type.CHAR:
829                     boxed = CHARACTER_TYPE;
830                     break;
831                 case Type.INT:
832                     boxed = INTEGER_TYPE;
833                     break;
834                 case Type.FLOAT:
835                     boxed = FLOAT_TYPE;
836                     break;
837                 case Type.LONG:
838                     boxed = LONG_TYPE;
839                     break;
840                 case Type.DOUBLE:
841                     boxed = DOUBLE_TYPE;
842                     break;
843             }
844             newInstance(boxed);
845             if (type.getSize() == 2) {
846                 // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
847
dupX2();
848                 dupX2();
849                 pop();
850             } else {
851                 // p -> po -> opo -> oop -> o
852
dupX1();
853                 swap();
854             }
855             invokeConstructor(boxed, new Method("<init>",
856                     Type.VOID_TYPE,
857                     new Type[] { type }));
858         }
859     }
860
861     /**
862      * Generates the instructions to unbox the top stack value. This value is
863      * replaced by its unboxed equivalent on top of the stack.
864      *
865      * @param type the type of the top stack value.
866      */

867     public void unbox(final Type type) {
868         Type t = NUMBER_TYPE;
869         Method sig = null;
870         switch (type.getSort()) {
871             case Type.VOID:
872                 return;
873             case Type.CHAR:
874                 t = CHARACTER_TYPE;
875                 sig = CHAR_VALUE;
876                 break;
877             case Type.BOOLEAN:
878                 t = BOOLEAN_TYPE;
879                 sig = BOOLEAN_VALUE;
880                 break;
881             case Type.DOUBLE:
882                 sig = DOUBLE_VALUE;
883                 break;
884             case Type.FLOAT:
885                 sig = FLOAT_VALUE;
886                 break;
887             case Type.LONG:
888                 sig = LONG_VALUE;
889                 break;
890             case Type.INT:
891             case Type.SHORT:
892             case Type.BYTE:
893                 sig = INT_VALUE;
894         }
895         if (sig == null) {
896             checkCast(type);
897         } else {
898             checkCast(t);
899             invokeVirtual(t, sig);
900         }
901     }
902
903     // ------------------------------------------------------------------------
904
// Instructions to jump to other instructions
905
// ------------------------------------------------------------------------
906

907     /**
908      * Creates a new {@link Label}.
909      *
910      * @return a new {@link Label}.
911      */

912     public Label newLabel() {
913         return new Label();
914     }
915
916     /**
917      * Marks the current code position with the given label.
918      *
919      * @param label a label.
920      */

921     public void mark(final Label label) {
922         mv.visitLabel(label);
923     }
924
925     /**
926      * Marks the current code position with a new label.
927      *
928      * @return the label that was created to mark the current code position.
929      */

930     public Label mark() {
931         Label label = new Label();
932         mv.visitLabel(label);
933         return label;
934     }
935
936     /**
937      * Generates the instructions to jump to a label based on the comparison of
938      * the top two stack values.
939      *
940      * @param type the type of the top two stack values.
941      * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
942      * LE.
943      * @param label where to jump if the comparison result is <tt>true</tt>.
944      */

945     public void ifCmp(final Type type, final int mode, final Label label) {
946         int intOp = -1;
947         int jumpMode = mode;
948         switch (mode) {
949             case GE:
950                 jumpMode = LT;
951                 break;
952             case LE:
953                 jumpMode = GT;
954                 break;
955         }
956         switch (type.getSort()) {
957             case Type.LONG:
958                 mv.visitInsn(Opcodes.LCMP);
959                 break;
960             case Type.DOUBLE:
961                 mv.visitInsn(Opcodes.DCMPG);
962                 break;
963             case Type.FLOAT:
964                 mv.visitInsn(Opcodes.FCMPG);
965                 break;
966             case Type.ARRAY:
967             case Type.OBJECT:
968                 switch (mode) {
969                     case EQ:
970                         mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
971                         return;
972                     case NE:
973                         mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
974                         return;
975                 }
976                 throw new IllegalArgumentException JavaDoc("Bad comparison for type "
977                         + type);
978             default:
979                 switch (mode) {
980                     case EQ:
981                         intOp = Opcodes.IF_ICMPEQ;
982                         break;
983                     case NE:
984                         intOp = Opcodes.IF_ICMPNE;
985                         break;
986                     case GE:
987                         intOp = Opcodes.IF_ICMPGE;
988                         break;
989                     case LT:
990                         intOp = Opcodes.IF_ICMPLT;
991                         break;
992                     case LE:
993                         intOp = Opcodes.IF_ICMPLE;
994                         break;
995                     case GT:
996                         intOp = Opcodes.IF_ICMPGT;
997                         break;
998                 }
999                 mv.visitJumpInsn(intOp, label);
1000                return;
1001        }
1002        mv.visitJumpInsn(jumpMode, label);
1003    }
1004
1005    /**
1006     * Generates the instructions to jump to a label based on the comparison of
1007     * the top two integer stack values.
1008     *
1009     * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
1010     * LE.
1011     * @param label where to jump if the comparison result is <tt>true</tt>.
1012     */

1013    public void ifICmp(final int mode, final Label label) {
1014        ifCmp(Type.INT_TYPE, mode, label);
1015    }
1016
1017    /**
1018     * Generates the instructions to jump to a label based on the comparison of
1019     * the top integer stack value with zero.
1020     *
1021     * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
1022     * LE.
1023     * @param label where to jump if the comparison result is <tt>true</tt>.
1024     */

1025    public void ifZCmp(final int mode, final Label label) {
1026        mv.visitJumpInsn(mode, label);
1027    }
1028
1029    /**
1030     * Generates the instruction to jump to the given label if the top stack
1031     * value is null.
1032     *
1033     * @param label where to jump if the condition is <tt>true</tt>.
1034     */

1035    public void ifNull(final Label label) {
1036        mv.visitJumpInsn(Opcodes.IFNULL, label);
1037    }
1038
1039    /**
1040     * Generates the instruction to jump to the given label if the top stack
1041     * value is not null.
1042     *
1043     * @param label where to jump if the condition is <tt>true</tt>.
1044     */

1045    public void ifNonNull(final Label label) {
1046        mv.visitJumpInsn(Opcodes.IFNONNULL, label);
1047    }
1048
1049    /**
1050     * Generates the instruction to jump to the given label.
1051     *
1052     * @param label where to jump if the condition is <tt>true</tt>.
1053     */

1054    public void goTo(final Label label) {
1055        mv.visitJumpInsn(Opcodes.GOTO, label);
1056    }
1057
1058    /**
1059     * Generates a RET instruction.
1060     *
1061     * @param local a local variable identifier, as returned by {@link #newLocal
1062     * newLocal}.
1063     */

1064    public void ret(final int local) {
1065        mv.visitVarInsn(Opcodes.RET, local);
1066    }
1067
1068    /**
1069     * Generates the instructions for a switch statement.
1070     *
1071     * @param keys the switch case keys.
1072     * @param generator a generator to generate the code for the switch cases.
1073     */

1074    public void tableSwitch(
1075        final int[] keys,
1076        final TableSwitchGenerator generator)
1077    {
1078        float density;
1079        if (keys.length == 0) {
1080            density = 0;
1081        } else {
1082            density = (float) keys.length
1083                    / (keys[keys.length - 1] - keys[0] + 1);
1084        }
1085        tableSwitch(keys, generator, density >= 0.5f);
1086    }
1087
1088    /**
1089     * Generates the instructions for a switch statement.
1090     *
1091     * @param keys the switch case keys.
1092     * @param generator a generator to generate the code for the switch cases.
1093     * @param useTable <tt>true</tt> to use a TABLESWITCH instruction, or
1094     * <tt>false</tt> to use a LOOKUPSWITCH instruction.
1095     */

1096    public void tableSwitch(
1097        final int[] keys,
1098        final TableSwitchGenerator generator,
1099        final boolean useTable)
1100    {
1101        for (int i = 1; i < keys.length; ++i) {
1102            if (keys[i] < keys[i - 1]) {
1103                throw new IllegalArgumentException JavaDoc("keys must be sorted ascending");
1104            }
1105        }
1106        Label def = newLabel();
1107        Label end = newLabel();
1108        if (keys.length > 0) {
1109            int len = keys.length;
1110            int min = keys[0];
1111            int max = keys[len - 1];
1112            int range = max - min + 1;
1113            if (useTable) {
1114                Label[] labels = new Label[range];
1115                Arrays.fill(labels, def);
1116                for (int i = 0; i < len; ++i) {
1117                    labels[keys[i] - min] = newLabel();
1118                }
1119                mv.visitTableSwitchInsn(min, max, def, labels);
1120                for (int i = 0; i < range; ++i) {
1121                    Label label = labels[i];
1122                    if (label != def) {
1123                        mark(label);
1124                        generator.generateCase(i + min, end);
1125                    }
1126                }
1127            } else {
1128                Label[] labels = new Label[len];
1129                for (int i = 0; i < len; ++i) {
1130                    labels[i] = newLabel();
1131                }
1132                mv.visitLookupSwitchInsn(def, keys, labels);
1133                for (int i = 0; i < len; ++i) {
1134                    mark(labels[i]);
1135                    generator.generateCase(keys[i], end);
1136                }
1137            }
1138        }
1139        mark(def);
1140        generator.generateDefault();
1141        mark(end);
1142    }
1143
1144    /**
1145     * Generates the instruction to return the top stack value to the caller.
1146     */

1147    public void returnValue() {
1148        mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
1149    }
1150
1151    // ------------------------------------------------------------------------
1152
// Instructions to load and store fields
1153
// ------------------------------------------------------------------------
1154

1155    /**
1156     * Generates a get field or set field instruction.
1157     *
1158     * @param opcode the instruction's opcode.
1159     * @param ownerType the class in which the field is defined.
1160     * @param name the name of the field.
1161     * @param fieldType the type of the field.
1162     */

1163    private void fieldInsn(
1164        final int opcode,
1165        final Type ownerType,
1166        final String JavaDoc name,
1167        final Type fieldType)
1168    {
1169        mv.visitFieldInsn(opcode,
1170                ownerType.getInternalName(),
1171                name,
1172                fieldType.getDescriptor());
1173    }
1174
1175    /**
1176     * Generates the instruction to push the value of a static field on the
1177     * stack.
1178     *
1179     * @param owner the class in which the field is defined.
1180     * @param name the name of the field.
1181     * @param type the type of the field.
1182     */

1183    public void getStatic(final Type owner, final String JavaDoc name, final Type type)
1184    {
1185        fieldInsn(Opcodes.GETSTATIC, owner, name, type);
1186    }
1187
1188    /**
1189     * Generates the instruction to store the top stack value in a static field.
1190     *
1191     * @param owner the class in which the field is defined.
1192     * @param name the name of the field.
1193     * @param type the type of the field.
1194     */

1195    public void putStatic(final Type owner, final String JavaDoc name, final Type type)
1196    {
1197        fieldInsn(Opcodes.PUTSTATIC, owner, name, type);
1198    }
1199
1200    /**
1201     * Generates the instruction to push the value of a non static field on the
1202     * stack.
1203     *
1204     * @param owner the class in which the field is defined.
1205     * @param name the name of the field.
1206     * @param type the type of the field.
1207     */

1208    public void getField(final Type owner, final String JavaDoc name, final Type type) {
1209        fieldInsn(Opcodes.GETFIELD, owner, name, type);
1210    }
1211
1212    /**
1213     * Generates the instruction to store the top stack value in a non static
1214     * field.
1215     *
1216     * @param owner the class in which the field is defined.
1217     * @param name the name of the field.
1218     * @param type the type of the field.
1219     */

1220    public void putField(final Type owner, final String JavaDoc name, final Type type) {
1221        fieldInsn(Opcodes.PUTFIELD, owner, name, type);
1222    }
1223
1224    // ------------------------------------------------------------------------
1225
// Instructions to invoke methods
1226
// ------------------------------------------------------------------------
1227

1228    /**
1229     * Generates an invoke method instruction.
1230     *
1231     * @param opcode the instruction's opcode.
1232     * @param type the class in which the method is defined.
1233     * @param method the method to be invoked.
1234     */

1235    private void invokeInsn(
1236        final int opcode,
1237        final Type type,
1238        final Method method)
1239    {
1240        String JavaDoc owner = type.getSort() == Type.ARRAY
1241                ? type.getDescriptor()
1242                : type.getInternalName();
1243        mv.visitMethodInsn(opcode,
1244                owner,
1245                method.getName(),
1246                method.getDescriptor());
1247    }
1248
1249    /**
1250     * Generates the instruction to invoke a normal method.
1251     *
1252     * @param owner the class in which the method is defined.
1253     * @param method the method to be invoked.
1254     */

1255    public void invokeVirtual(final Type owner, final Method method) {
1256        invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
1257    }
1258
1259    /**
1260     * Generates the instruction to invoke a constructor.
1261     *
1262     * @param type the class in which the constructor is defined.
1263     * @param method the constructor to be invoked.
1264     */

1265    public void invokeConstructor(final Type type, final Method method) {
1266        invokeInsn(Opcodes.INVOKESPECIAL, type, method);
1267    }
1268
1269    /**
1270     * Generates the instruction to invoke a static method.
1271     *
1272     * @param owner the class in which the method is defined.
1273     * @param method the method to be invoked.
1274     */

1275    public void invokeStatic(final Type owner, final Method method) {
1276        invokeInsn(Opcodes.INVOKESTATIC, owner, method);
1277    }
1278
1279    /**
1280     * Generates the instruction to invoke an interface method.
1281     *
1282     * @param owner the class in which the method is defined.
1283     * @param method the method to be invoked.
1284     */

1285    public void invokeInterface(final Type owner, final Method method) {
1286        invokeInsn(Opcodes.INVOKEINTERFACE, owner, method);
1287    }
1288
1289    // ------------------------------------------------------------------------
1290
// Instructions to create objects and arrays
1291
// ------------------------------------------------------------------------
1292

1293    /**
1294     * Generates a type dependent instruction.
1295     *
1296     * @param opcode the instruction's opcode.
1297     * @param type the instruction's operand.
1298     */

1299    private void typeInsn(final int opcode, final Type type) {
1300        String JavaDoc desc;
1301        if (type.getSort() == Type.ARRAY) {
1302            desc = type.getDescriptor();
1303        } else {
1304            desc = type.getInternalName();
1305        }
1306        mv.visitTypeInsn(opcode, desc);
1307    }
1308
1309    /**
1310     * Generates the instruction to create a new object.
1311     *
1312     * @param type the class of the object to be created.
1313     */

1314    public void newInstance(final Type type) {
1315        typeInsn(Opcodes.NEW, type);
1316    }
1317
1318    /**
1319     * Generates the instruction to create a new array.
1320     *
1321     * @param type the type of the array elements.
1322     */

1323    public void newArray(final Type type) {
1324        int typ;
1325        switch (type.getSort()) {
1326            case Type.BOOLEAN:
1327                typ = Opcodes.T_BOOLEAN;
1328                break;
1329            case Type.CHAR:
1330                typ = Opcodes.T_CHAR;
1331                break;
1332            case Type.BYTE:
1333                typ = Opcodes.T_BYTE;
1334                break;
1335            case Type.SHORT:
1336                typ = Opcodes.T_SHORT;
1337                break;
1338            case Type.INT:
1339                typ = Opcodes.T_INT;
1340                break;
1341            case Type.FLOAT:
1342                typ = Opcodes.T_FLOAT;
1343                break;
1344            case Type.LONG:
1345                typ = Opcodes.T_LONG;
1346                break;
1347            case Type.DOUBLE:
1348                typ = Opcodes.T_DOUBLE;
1349                break;
1350            default:
1351                typeInsn(Opcodes.ANEWARRAY, type);
1352                return;
1353        }
1354        mv.visitIntInsn(Opcodes.NEWARRAY, typ);
1355    }
1356
1357    // ------------------------------------------------------------------------
1358
// Miscelaneous instructions
1359
// ------------------------------------------------------------------------
1360

1361    /**
1362     * Generates the instruction to compute the length of an array.
1363     */

1364    public void arrayLength() {
1365        mv.visitInsn(Opcodes.ARRAYLENGTH);
1366    }
1367
1368    /**
1369     * Generates the instruction to throw an exception.
1370     */

1371    public void throwException() {
1372        mv.visitInsn(Opcodes.ATHROW);
1373    }
1374
1375    /**
1376     * Generates the instructions to create and throw an exception. The
1377     * exception class must have a constructor with a single String argument.
1378     *
1379     * @param type the class of the exception to be thrown.
1380     * @param msg the detailed message of the exception.
1381     */

1382    public void throwException(final Type type, final String JavaDoc msg) {
1383        newInstance(type);
1384        dup();
1385        push(msg);
1386        invokeConstructor(type, Method.getMethod("void <init> (String)"));
1387        throwException();
1388    }
1389
1390    /**
1391     * Generates the instruction to check that the top stack value is of the
1392     * given type.
1393     *
1394     * @param type a class or interface type.
1395     */

1396    public void checkCast(final Type type) {
1397        if (!type.equals(OBJECT_TYPE)) {
1398            typeInsn(Opcodes.CHECKCAST, type);
1399        }
1400    }
1401
1402    /**
1403     * Generates the instruction to test if the top stack value is of the given
1404     * type.
1405     *
1406     * @param type a class or interface type.
1407     */

1408    public void instanceOf(final Type type) {
1409        typeInsn(Opcodes.INSTANCEOF, type);
1410    }
1411
1412    /**
1413     * Generates the instruction to get the monitor of the top stack value.
1414     */

1415    public void monitorEnter() {
1416        mv.visitInsn(Opcodes.MONITORENTER);
1417    }
1418
1419    /**
1420     * Generates the instruction to release the monitor of the top stack value.
1421     */

1422    public void monitorExit() {
1423        mv.visitInsn(Opcodes.MONITOREXIT);
1424    }
1425
1426    // ------------------------------------------------------------------------
1427
// Non instructions
1428
// ------------------------------------------------------------------------
1429

1430    /**
1431     * Marks the end of the visited method.
1432     */

1433    public void endMethod() {
1434        if ((access & Opcodes.ACC_ABSTRACT) == 0) {
1435            mv.visitMaxs(0, 0);
1436        }
1437    }
1438
1439    /**
1440     * Marks the start of an exception handler.
1441     *
1442     * @param start beginning of the exception handler's scope (inclusive).
1443     * @param end end of the exception handler's scope (exclusive).
1444     * @param exception internal name of the type of exceptions handled by the
1445     * handler.
1446     */

1447    public void catchException(
1448        final Label start,
1449        final Label end,
1450        final Type exception)
1451    {
1452        mv.visitTryCatchBlock(start, end, mark(), exception.getInternalName());
1453    }
1454}
1455
Popular Tags