View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.bcel.generic;
20  
21  import org.apache.bcel.Const;
22  
23  /**
24   * Instances of this class may be used, for example, to generate typed versions of instructions. Its main purpose is to be used
25   * as the byte code generating backend of a compiler. You can subclass it to add your own create methods.
26   * <p>
27   * Note: The static createXXX methods return singleton instances from the {@link InstructionConst} class.
28   * </p>
29   *
30   * @see Const
31   * @see InstructionConst
32   */
33  public class InstructionFactory implements InstructionConstants {
34  
35      private static final class MethodObject {
36  
37          final Type[] argTypes;
38          final Type resultType;
39          final String className;
40          final String name;
41  
42          MethodObject(final String c, final String n, final Type r, final Type[] a) {
43              this.className = c;
44              this.name = n;
45              this.resultType = r;
46              this.argTypes = a;
47          }
48      }
49  
50      private static final String APPEND = "append";
51  
52      private static final String FQCN_STRING_BUFFER = "java.lang.StringBuffer";
53  
54      /**
55       * These must agree with the order of Constants.T_CHAR through T_LONG.
56       */
57      private static final String[] SHORT_NAMES = {"C", "F", "D", "B", "S", "I", "L"};
58  
59      private static final MethodObject[] APPEND_METHOD_OBJECTS = {
60              new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.STRING }),
61              new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.OBJECT }), null, null, // indices 2, 3
62              new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.BOOLEAN }),
63              new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.CHAR }),
64              new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.FLOAT }),
65              new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.DOUBLE }),
66              new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.INT }),
67              new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.INT }), // No append(byte)
68              new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.INT }), // No append(short)
69              new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.LONG })};
70  
71      /**
72       * Creates an array load instruction for the given type.
73       *
74       * @param type type of elements of array, that is, array.getElementType().
75       * @return the array load instruction.
76       */
77      public static ArrayInstruction createArrayLoad(final Type type) {
78          switch (type.getType()) {
79          case Const.T_BOOLEAN:
80          case Const.T_BYTE:
81              return InstructionConst.BALOAD;
82          case Const.T_CHAR:
83              return InstructionConst.CALOAD;
84          case Const.T_SHORT:
85              return InstructionConst.SALOAD;
86          case Const.T_INT:
87              return InstructionConst.IALOAD;
88          case Const.T_FLOAT:
89              return InstructionConst.FALOAD;
90          case Const.T_DOUBLE:
91              return InstructionConst.DALOAD;
92          case Const.T_LONG:
93              return InstructionConst.LALOAD;
94          case Const.T_ARRAY:
95          case Const.T_OBJECT:
96              return InstructionConst.AALOAD;
97          default:
98              throw new IllegalArgumentException("Invalid type " + type);
99          }
100     }
101 
102     /**
103      * Creates an array store instruction for the given type.
104      *
105      * @param type type of elements of array, that is, array.getElementType().
106      * @return the array store instruction.
107      */
108     public static ArrayInstruction createArrayStore(final Type type) {
109         switch (type.getType()) {
110         case Const.T_BOOLEAN:
111         case Const.T_BYTE:
112             return InstructionConst.BASTORE;
113         case Const.T_CHAR:
114             return InstructionConst.CASTORE;
115         case Const.T_SHORT:
116             return InstructionConst.SASTORE;
117         case Const.T_INT:
118             return InstructionConst.IASTORE;
119         case Const.T_FLOAT:
120             return InstructionConst.FASTORE;
121         case Const.T_DOUBLE:
122             return InstructionConst.DASTORE;
123         case Const.T_LONG:
124             return InstructionConst.LASTORE;
125         case Const.T_ARRAY:
126         case Const.T_OBJECT:
127             return InstructionConst.AASTORE;
128         default:
129             throw new IllegalArgumentException("Invalid type " + type);
130         }
131     }
132 
133     private static ArithmeticInstruction createBinaryDoubleOp(final char op) {
134         switch (op) {
135         case '-':
136             return InstructionConst.DSUB;
137         case '+':
138             return InstructionConst.DADD;
139         case '*':
140             return InstructionConst.DMUL;
141         case '/':
142             return InstructionConst.DDIV;
143         case '%':
144             return InstructionConst.DREM;
145         default:
146             throw new IllegalArgumentException("Invalid operand " + op);
147         }
148     }
149 
150     private static ArithmeticInstruction createBinaryFloatOp(final char op) {
151         switch (op) {
152         case '-':
153             return InstructionConst.FSUB;
154         case '+':
155             return InstructionConst.FADD;
156         case '*':
157             return InstructionConst.FMUL;
158         case '/':
159             return InstructionConst.FDIV;
160         case '%':
161             return InstructionConst.FREM;
162         default:
163             throw new IllegalArgumentException("Invalid operand " + op);
164         }
165     }
166 
167     private static ArithmeticInstruction createBinaryIntOp(final char first, final String op) {
168         switch (first) {
169         case '-':
170             return InstructionConst.ISUB;
171         case '+':
172             return InstructionConst.IADD;
173         case '%':
174             return InstructionConst.IREM;
175         case '*':
176             return InstructionConst.IMUL;
177         case '/':
178             return InstructionConst.IDIV;
179         case '&':
180             return InstructionConst.IAND;
181         case '|':
182             return InstructionConst.IOR;
183         case '^':
184             return InstructionConst.IXOR;
185         case '<':
186             return InstructionConst.ISHL;
187         case '>':
188             return op.equals(">>>") ? InstructionConst.IUSHR : InstructionConst.ISHR;
189         default:
190             throw new IllegalArgumentException("Invalid operand " + op);
191         }
192     }
193 
194     /**
195      * Create an invokedynamic instruction.
196      *
197      * @param bootstrap_index index into the bootstrap_methods array.
198      * @param name name of the called method.
199      * @param ret_type return type of method.
200      * @param argTypes argument types of method.
201      * @see Const
202      */
203 
204     /*
205      * createInvokeDynamic only needed if instrumentation code wants to generate a new invokedynamic instruction. I don't
206      * think we need.
207      *
208      * public InvokeInstruction createInvokeDynamic( int bootstrap_index, String name, Type ret_type, Type[] argTypes) {
209      * int index; int nargs = 0; String signature = Type.getMethodSignature(ret_type, argTypes); for (int i = 0; i <
210      * argTypes.length; i++) { nargs += argTypes[i].getSize(); } // UNDONE - needs to be added to ConstantPoolGen //index
211      * = cp.addInvokeDynamic(bootstrap_index, name, signature); index = 0; return new INVOKEDYNAMIC(index); }
212      */
213 
214     private static ArithmeticInstruction createBinaryLongOp(final char first, final String op) {
215         switch (first) {
216         case '-':
217             return InstructionConst.LSUB;
218         case '+':
219             return InstructionConst.LADD;
220         case '%':
221             return InstructionConst.LREM;
222         case '*':
223             return InstructionConst.LMUL;
224         case '/':
225             return InstructionConst.LDIV;
226         case '&':
227             return InstructionConst.LAND;
228         case '|':
229             return InstructionConst.LOR;
230         case '^':
231             return InstructionConst.LXOR;
232         case '<':
233             return InstructionConst.LSHL;
234         case '>':
235             return op.equals(">>>") ? InstructionConst.LUSHR : InstructionConst.LSHR;
236         default:
237             throw new IllegalArgumentException("Invalid operand " + op);
238         }
239     }
240 
241     /**
242      * Create binary operation for simple basic types, such as int and float.
243      *
244      * @param op operation, such as "+", "*", "&lt;&lt;", etc.
245      * @param type the operand type.
246      * @return the binary arithmetic instruction.
247      */
248     public static ArithmeticInstruction createBinaryOperation(final String op, final Type type) {
249         final char first = op.charAt(0);
250         switch (type.getType()) {
251         case Const.T_BYTE:
252         case Const.T_SHORT:
253         case Const.T_INT:
254         case Const.T_CHAR:
255             return createBinaryIntOp(first, op);
256         case Const.T_LONG:
257             return createBinaryLongOp(first, op);
258         case Const.T_FLOAT:
259             return createBinaryFloatOp(first);
260         case Const.T_DOUBLE:
261             return createBinaryDoubleOp(first);
262         default:
263             throw new IllegalArgumentException("Invalid type " + type);
264         }
265     }
266 
267     /**
268      * Create branch instruction by given opcode, except LOOKUPSWITCH and TABLESWITCH. For those you should use the SWITCH
269      * compound instruction.
270      *
271      * @param opcode the branch opcode.
272      * @param target the target instruction handle.
273      * @return the branch instruction.
274      */
275     public static BranchInstruction createBranchInstruction(final short opcode, final InstructionHandle target) {
276         switch (opcode) {
277         case Const.IFEQ:
278             return new IFEQ(target);
279         case Const.IFNE:
280             return new IFNE(target);
281         case Const.IFLT:
282             return new IFLT(target);
283         case Const.IFGE:
284             return new IFGE(target);
285         case Const.IFGT:
286             return new IFGT(target);
287         case Const.IFLE:
288             return new IFLE(target);
289         case Const.IF_ICMPEQ:
290             return new IF_ICMPEQ(target);
291         case Const.IF_ICMPNE:
292             return new IF_ICMPNE(target);
293         case Const.IF_ICMPLT:
294             return new IF_ICMPLT(target);
295         case Const.IF_ICMPGE:
296             return new IF_ICMPGE(target);
297         case Const.IF_ICMPGT:
298             return new IF_ICMPGT(target);
299         case Const.IF_ICMPLE:
300             return new IF_ICMPLE(target);
301         case Const.IF_ACMPEQ:
302             return new IF_ACMPEQ(target);
303         case Const.IF_ACMPNE:
304             return new IF_ACMPNE(target);
305         case Const.GOTO:
306             return new GOTO(target);
307         case Const.JSR:
308             return new JSR(target);
309         case Const.IFNULL:
310             return new IFNULL(target);
311         case Const.IFNONNULL:
312             return new IFNONNULL(target);
313         case Const.GOTO_W:
314             return new GOTO_W(target);
315         case Const.JSR_W:
316             return new JSR_W(target);
317         default:
318             throw new IllegalArgumentException("Invalid opcode: " + opcode);
319         }
320     }
321 
322     /**
323      * Creates a DUP instruction.
324      *
325      * @param size size of operand, either 1 (int, for example) or 2 (double).
326      * @return the DUP instruction.
327      */
328     public static StackInstruction createDup(final int size) {
329         return size == 2 ? InstructionConst.DUP2 : InstructionConst.DUP;
330     }
331 
332     /**
333      * Creates a DUP_X1 instruction.
334      *
335      * @param size size of operand, either 1 (int, for example) or 2 (double).
336      * @return the DUP instruction.
337      */
338     public static StackInstruction createDup_1(final int size) {
339         return size == 2 ? InstructionConst.DUP2_X1 : InstructionConst.DUP_X1;
340     }
341 
342     /**
343      * Creates a DUP_X2 instruction.
344      *
345      * @param size size of operand, either 1 (int, for example) or 2 (double).
346      * @return the DUP instruction.
347      */
348     public static StackInstruction createDup_2(final int size) {
349         return size == 2 ? InstructionConst.DUP2_X2 : InstructionConst.DUP_X2;
350     }
351 
352     /**
353      * Creates a load instruction for the given type.
354      *
355      * @param type the variable type.
356      * @param index index of local variable.
357      * @return the load instruction.
358      */
359     public static LocalVariableInstruction createLoad(final Type type, final int index) {
360         switch (type.getType()) {
361         case Const.T_BOOLEAN:
362         case Const.T_CHAR:
363         case Const.T_BYTE:
364         case Const.T_SHORT:
365         case Const.T_INT:
366             return new ILOAD(index);
367         case Const.T_FLOAT:
368             return new FLOAD(index);
369         case Const.T_DOUBLE:
370             return new DLOAD(index);
371         case Const.T_LONG:
372             return new LLOAD(index);
373         case Const.T_ARRAY:
374         case Const.T_OBJECT:
375             return new ALOAD(index);
376         default:
377             throw new IllegalArgumentException("Invalid type " + type);
378         }
379     }
380 
381     /**
382      * Create "null" value for reference types, 0 for basic types like int.
383      *
384      * @param type the type.
385      * @return the null or zero instruction.
386      */
387     public static Instruction createNull(final Type type) {
388         switch (type.getType()) {
389         case Const.T_ARRAY:
390         case Const.T_OBJECT:
391             return InstructionConst.ACONST_NULL;
392         case Const.T_INT:
393         case Const.T_SHORT:
394         case Const.T_BOOLEAN:
395         case Const.T_CHAR:
396         case Const.T_BYTE:
397             return InstructionConst.ICONST_0;
398         case Const.T_FLOAT:
399             return InstructionConst.FCONST_0;
400         case Const.T_DOUBLE:
401             return InstructionConst.DCONST_0;
402         case Const.T_LONG:
403             return InstructionConst.LCONST_0;
404         case Const.T_VOID:
405             return InstructionConst.NOP;
406         default:
407             throw new IllegalArgumentException("Invalid type: " + type);
408         }
409     }
410 
411     /**
412      * Creates a pop instruction.
413      *
414      * @param size size of operand, either 1 (int, for example) or 2 (double).
415      * @return the pop instruction.
416      */
417     public static StackInstruction createPop(final int size) {
418         return size == 2 ? InstructionConst.POP2 : InstructionConst.POP;
419     }
420 
421     /**
422      * Create typed return.
423      *
424      * @param type the return type.
425      * @return the return instruction.
426      */
427     public static ReturnInstruction createReturn(final Type type) {
428         switch (type.getType()) {
429         case Const.T_ARRAY:
430         case Const.T_OBJECT:
431             return InstructionConst.ARETURN;
432         case Const.T_INT:
433         case Const.T_SHORT:
434         case Const.T_BOOLEAN:
435         case Const.T_CHAR:
436         case Const.T_BYTE:
437             return InstructionConst.IRETURN;
438         case Const.T_FLOAT:
439             return InstructionConst.FRETURN;
440         case Const.T_DOUBLE:
441             return InstructionConst.DRETURN;
442         case Const.T_LONG:
443             return InstructionConst.LRETURN;
444         case Const.T_VOID:
445             return InstructionConst.RETURN;
446         default:
447             throw new IllegalArgumentException("Invalid type: " + type);
448         }
449     }
450 
451     /**
452      * Creates a store instruction.
453      *
454      * @param type the variable type.
455      * @param index index of local variable.
456      * @return the store instruction.
457      */
458     public static LocalVariableInstruction createStore(final Type type, final int index) {
459         switch (type.getType()) {
460         case Const.T_BOOLEAN:
461         case Const.T_CHAR:
462         case Const.T_BYTE:
463         case Const.T_SHORT:
464         case Const.T_INT:
465             return new ISTORE(index);
466         case Const.T_FLOAT:
467             return new FSTORE(index);
468         case Const.T_DOUBLE:
469             return new DSTORE(index);
470         case Const.T_LONG:
471             return new LSTORE(index);
472         case Const.T_ARRAY:
473         case Const.T_OBJECT:
474             return new ASTORE(index);
475         default:
476             throw new IllegalArgumentException("Invalid type " + type);
477         }
478     }
479 
480     /**
481      * Create reference to 'this'.
482      *
483      * @return reference to 'this'.
484      */
485     public static Instruction createThis() {
486         return new ALOAD(0);
487     }
488 
489     private static boolean isString(final Type type) {
490         return type instanceof ObjectType && ((ObjectType) type).getClassName().equals("java.lang.String");
491     }
492 
493     /**
494      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
495      */
496     @Deprecated
497     protected ClassGen cg;
498 
499     /**
500      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
501      */
502     @Deprecated
503     protected ConstantPoolGen cp;
504 
505     /**
506      * Initialize with ClassGen object.
507      *
508      * @param cg the class generator.
509      */
510     public InstructionFactory(final ClassGen cg) {
511         this(cg, cg.getConstantPool());
512     }
513 
514     /**
515      * Constructs an InstructionFactory.
516      *
517      * @param cg the class generator.
518      * @param cp the constant pool generator.
519      */
520     public InstructionFactory(final ClassGen cg, final ConstantPoolGen cp) {
521         this.cg = cg;
522         this.cp = cp;
523     }
524 
525     /**
526      * Initialize just with ConstantPoolGen object.
527      *
528      * @param cp the constant pool generator.
529      */
530     public InstructionFactory(final ConstantPoolGen cp) {
531         this(null, cp);
532     }
533 
534     /**
535      * Creates an append instruction for the given type.
536      *
537      * @param type the type to append.
538      * @return the append instruction.
539      */
540     public Instruction createAppend(final Type type) {
541         final byte t = type.getType();
542         if (isString(type)) {
543             return createInvoke(APPEND_METHOD_OBJECTS[0], Const.INVOKEVIRTUAL);
544         }
545         switch (t) {
546         case Const.T_BOOLEAN:
547         case Const.T_CHAR:
548         case Const.T_FLOAT:
549         case Const.T_DOUBLE:
550         case Const.T_BYTE:
551         case Const.T_SHORT:
552         case Const.T_INT:
553         case Const.T_LONG:
554             return createInvoke(APPEND_METHOD_OBJECTS[t], Const.INVOKEVIRTUAL);
555         case Const.T_ARRAY:
556         case Const.T_OBJECT:
557             return createInvoke(APPEND_METHOD_OBJECTS[1], Const.INVOKEVIRTUAL);
558         default:
559             throw new IllegalArgumentException("No append for this type? " + type);
560         }
561     }
562 
563     /**
564      * Create conversion operation for two stack operands, this may be an I2C, instruction, for example, if the operands are basic
565      * types and CHECKCAST if they are reference types.
566      *
567      * @param srcType the source type.
568      * @param destType the destination type.
569      * @return the conversion instruction.
570      */
571     public Instruction createCast(final Type srcType, final Type destType) {
572         if (srcType instanceof BasicType && destType instanceof BasicType) {
573             final byte dest = destType.getType();
574             byte src = srcType.getType();
575             if (dest == Const.T_LONG && (src == Const.T_CHAR || src == Const.T_BYTE || src == Const.T_SHORT)) {
576                 src = Const.T_INT;
577             }
578             final String name = "org.apache.bcel.generic." + SHORT_NAMES[src - Const.T_CHAR] + "2" + SHORT_NAMES[dest - Const.T_CHAR];
579             Instruction i = null;
580             try {
581                 i = (Instruction) Class.forName(name).getConstructor().newInstance();
582             } catch (final Exception e) {
583                 throw new IllegalArgumentException("Could not find instruction: " + name, e);
584             }
585             return i;
586         }
587         if (!(srcType instanceof ReferenceType) || !(destType instanceof ReferenceType)) {
588             throw new IllegalArgumentException("Cannot cast " + srcType + " to " + destType);
589         }
590         if (destType instanceof ArrayType) {
591             return new CHECKCAST(cp.addArrayClass((ArrayType) destType));
592         }
593         return new CHECKCAST(cp.addClass(((ObjectType) destType).getClassName()));
594     }
595 
596     /**
597      * Creates a CHECKCAST instruction.
598      *
599      * @param t the reference type.
600      * @return the CHECKCAST instruction.
601      */
602     public CHECKCAST createCheckCast(final ReferenceType t) {
603         if (t instanceof ArrayType) {
604             return new CHECKCAST(cp.addArrayClass((ArrayType) t));
605         }
606         return new CHECKCAST(cp.addClass((ObjectType) t));
607     }
608 
609     /**
610      * Uses PUSH to push a constant value onto the stack.
611      *
612      * @param value must be of type Number, Boolean, Character or String.
613      * @return the instruction.
614      */
615     public Instruction createConstant(final Object value) {
616         final PUSH push;
617         if (value instanceof Number) {
618             push = new PUSH(cp, (Number) value);
619         } else if (value instanceof String) {
620             push = new PUSH(cp, (String) value);
621         } else if (value instanceof Boolean) {
622             push = new PUSH(cp, (Boolean) value);
623         } else if (value instanceof Character) {
624             push = new PUSH(cp, (Character) value);
625         } else {
626             throw new ClassGenException("Illegal type: " + value.getClass());
627         }
628         return push.getInstruction();
629     }
630 
631     /**
632      * Create a field instruction.
633      *
634      * @param className name of the accessed class.
635      * @param name name of the referenced field.
636      * @param type type of field.
637      * @param kind how to access, that is, GETFIELD, PUTFIELD, GETSTATIC, PUTSTATIC.
638      * @see Const
639      */
640     public FieldInstruction createFieldAccess(final String className, final String name, final Type type, final short kind) {
641         final String signature = type.getSignature();
642         final int index = cp.addFieldref(className, name, signature);
643         switch (kind) {
644         case Const.GETFIELD:
645             return new GETFIELD(index);
646         case Const.PUTFIELD:
647             return new PUTFIELD(index);
648         case Const.GETSTATIC:
649             return new GETSTATIC(index);
650         case Const.PUTSTATIC:
651             return new PUTSTATIC(index);
652         default:
653             throw new IllegalArgumentException("Unknown getfield kind:" + kind);
654         }
655     }
656 
657     /**
658      * Creates a GETFIELD instruction.
659      *
660      * @param className the class name.
661      * @param name the field name.
662      * @param t the field type.
663      * @return the GETFIELD instruction.
664      */
665     public GETFIELD createGetField(final String className, final String name, final Type t) {
666         return new GETFIELD(cp.addFieldref(className, name, t.getSignature()));
667     }
668 
669     /**
670      * Creates a GETSTATIC instruction.
671      *
672      * @param className the class name.
673      * @param name the field name.
674      * @param t the field type.
675      * @return the GETSTATIC instruction.
676      */
677     public GETSTATIC createGetStatic(final String className, final String name, final Type t) {
678         return new GETSTATIC(cp.addFieldref(className, name, t.getSignature()));
679     }
680 
681     /**
682      * Creates an INSTANCEOF instruction.
683      *
684      * @param t the reference type.
685      * @return the INSTANCEOF instruction.
686      */
687     public INSTANCEOF createInstanceOf(final ReferenceType t) {
688         if (t instanceof ArrayType) {
689             return new INSTANCEOF(cp.addArrayClass((ArrayType) t));
690         }
691         return new INSTANCEOF(cp.addClass((ObjectType) t));
692     }
693 
694     private InvokeInstruction createInvoke(final MethodObject m, final short kind) {
695         return createInvoke(m.className, m.name, m.resultType, m.argTypes, kind);
696     }
697 
698     /**
699      * Create an invoke instruction. (Except for invokedynamic.)
700      *
701      * @param className name of the called class.
702      * @param name name of the called method.
703      * @param retType return type of method.
704      * @param argTypes argument types of method.
705      * @param kind how to invoke, that is, INVOKEINTERFACE, INVOKESTATIC, INVOKEVIRTUAL, or INVOKESPECIAL.
706      * @return the invoke instruction.
707      * @see Const
708      */
709     public InvokeInstruction createInvoke(final String className, final String name, final Type retType, final Type[] argTypes, final short kind) {
710         return createInvoke(className, name, retType, argTypes, kind, kind == Const.INVOKEINTERFACE);
711     }
712 
713     /**
714      * Create an invoke instruction. (Except for invokedynamic.)
715      *
716      * @param className name of the called class.
717      * @param name name of the called method.
718      * @param retType return type of method.
719      * @param argTypes argument types of method.
720      * @param kind how to invoke: INVOKEINTERFACE, INVOKESTATIC, INVOKEVIRTUAL, or INVOKESPECIAL.
721      * @param useInterface force use of InterfaceMethodref.
722      * @return A new InvokeInstruction.
723      * @since 6.5.0
724      */
725     public InvokeInstruction createInvoke(final String className, final String name, final Type retType, final Type[] argTypes, final short kind,
726         final boolean useInterface) {
727         if (kind != Const.INVOKESPECIAL && kind != Const.INVOKEVIRTUAL && kind != Const.INVOKESTATIC && kind != Const.INVOKEINTERFACE
728             && kind != Const.INVOKEDYNAMIC) {
729             throw new IllegalArgumentException("Unknown invoke kind: " + kind);
730         }
731         final int index;
732         int nargs = 0;
733         final String signature = Type.getMethodSignature(retType, argTypes);
734         if (argTypes != null) {
735             for (final Type argType : argTypes) {
736                 nargs += argType.getSize();
737             }
738         }
739         if (useInterface) {
740             index = cp.addInterfaceMethodref(className, name, signature);
741         } else {
742             index = cp.addMethodref(className, name, signature);
743         }
744         switch (kind) {
745         case Const.INVOKESPECIAL:
746             return new INVOKESPECIAL(index);
747         case Const.INVOKEVIRTUAL:
748             return new INVOKEVIRTUAL(index);
749         case Const.INVOKESTATIC:
750             return new INVOKESTATIC(index);
751         case Const.INVOKEINTERFACE:
752             return new INVOKEINTERFACE(index, nargs + 1);
753         case Const.INVOKEDYNAMIC:
754             return new INVOKEDYNAMIC(index);
755         default:
756             // Can't happen
757             throw new IllegalStateException("Unknown invoke kind: " + kind);
758         }
759     }
760 
761     /**
762      * Creates a NEW instruction.
763      *
764      * @param t the object type.
765      * @return the NEW instruction.
766      */
767     public NEW createNew(final ObjectType t) {
768         return new NEW(cp.addClass(t));
769     }
770 
771     /**
772      * Creates a NEW instruction.
773      *
774      * @param s the class name.
775      * @return the NEW instruction.
776      */
777     public NEW createNew(final String s) {
778         return createNew(ObjectType.getInstance(s));
779     }
780 
781     /**
782      * Create new array of given size and type.
783      *
784      * @param t the array element type.
785      * @param dim the array dimensions.
786      * @return an instruction that creates the corresponding array at runtime, for example is an AllocationInstruction.
787      */
788     public Instruction createNewArray(final Type t, final short dim) {
789         if (dim == 1) {
790             if (t instanceof ObjectType) {
791                 return new ANEWARRAY(cp.addClass((ObjectType) t));
792             }
793             if (t instanceof ArrayType) {
794                 return new ANEWARRAY(cp.addArrayClass((ArrayType) t));
795             }
796             return new NEWARRAY(t.getType());
797         }
798         final ArrayType at;
799         if (t instanceof ArrayType) {
800             at = (ArrayType) t;
801         } else {
802             at = new ArrayType(t, dim);
803         }
804         return new MULTIANEWARRAY(cp.addArrayClass(at), dim);
805     }
806 
807     /**
808      * Create a call to the most popular System.out.println() method.
809      *
810      * @param s the string to print.
811      * @return the instruction list.
812      */
813     public InstructionList createPrintln(final String s) {
814         final InstructionList il = new InstructionList();
815         il.append(createGetStatic("java.lang.System", "out", Type.getType("Ljava/io/PrintStream;")));
816         il.append(new PUSH(cp, s));
817         final MethodObject methodObject = new MethodObject("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.getType("Ljava/lang/String;") });
818         il.append(createInvoke(methodObject, Const.INVOKEVIRTUAL));
819         return il;
820     }
821 
822     /**
823      * Creates a PUTFIELD instruction.
824      *
825      * @param className the class name.
826      * @param name the field name.
827      * @param t the field type.
828      * @return the PUTFIELD instruction.
829      */
830     public PUTFIELD createPutField(final String className, final String name, final Type t) {
831         return new PUTFIELD(cp.addFieldref(className, name, t.getSignature()));
832     }
833 
834     /**
835      * Creates a PUTSTATIC instruction.
836      *
837      * @param className the class name.
838      * @param name the field name.
839      * @param t the field type.
840      * @return the PUTSTATIC instruction.
841      */
842     public PUTSTATIC createPutStatic(final String className, final String name, final Type t) {
843         return new PUTSTATIC(cp.addFieldref(className, name, t.getSignature()));
844     }
845 
846     /**
847      * Gets the class generator.
848      *
849      * @return the class generator.
850      */
851     public ClassGen getClassGen() {
852         return cg;
853     }
854 
855     /**
856      * Gets the constant pool generator.
857      *
858      * @return the constant pool generator.
859      */
860     public ConstantPoolGen getConstantPool() {
861         return cp;
862     }
863 
864     /**
865      * Sets the class generator.
866      *
867      * @param c the class generator.
868      */
869     public void setClassGen(final ClassGen c) {
870         cg = c;
871     }
872 
873     /**
874      * Sets the constant pool generator.
875      *
876      * @param c the constant pool generator.
877      */
878     public void setConstantPool(final ConstantPoolGen c) {
879         cp = c;
880     }
881 }
882