001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.bcel.generic;
020
021import org.apache.bcel.Const;
022
023/**
024 * Instances of this class may be used, for example, to generate typed versions of instructions. Its main purpose is to be used
025 * as the byte code generating backend of a compiler. You can subclass it to add your own create methods.
026 * <p>
027 * Note: The static createXXX methods return singleton instances from the {@link InstructionConst} class.
028 * </p>
029 *
030 * @see Const
031 * @see InstructionConst
032 */
033public class InstructionFactory implements InstructionConstants {
034
035    private static final class MethodObject {
036
037        final Type[] argTypes;
038        final Type resultType;
039        final String className;
040        final String name;
041
042        MethodObject(final String c, final String n, final Type r, final Type[] a) {
043            this.className = c;
044            this.name = n;
045            this.resultType = r;
046            this.argTypes = a;
047        }
048    }
049
050    private static final String APPEND = "append";
051
052    private static final String FQCN_STRING_BUFFER = "java.lang.StringBuffer";
053
054    /**
055     * These must agree with the order of Constants.T_CHAR through T_LONG.
056     */
057    private static final String[] SHORT_NAMES = {"C", "F", "D", "B", "S", "I", "L"};
058
059    private static final MethodObject[] APPEND_METHOD_OBJECTS = {
060            new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.STRING }),
061            new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.OBJECT }), null, null, // indices 2, 3
062            new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.BOOLEAN }),
063            new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.CHAR }),
064            new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.FLOAT }),
065            new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.DOUBLE }),
066            new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.INT }),
067            new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.INT }), // No append(byte)
068            new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.INT }), // No append(short)
069            new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[] { Type.LONG })};
070
071    /**
072     * Creates an array load instruction for the given type.
073     *
074     * @param type type of elements of array, that is, array.getElementType().
075     * @return the array load instruction.
076     */
077    public static ArrayInstruction createArrayLoad(final Type type) {
078        switch (type.getType()) {
079        case Const.T_BOOLEAN:
080        case Const.T_BYTE:
081            return InstructionConst.BALOAD;
082        case Const.T_CHAR:
083            return InstructionConst.CALOAD;
084        case Const.T_SHORT:
085            return InstructionConst.SALOAD;
086        case Const.T_INT:
087            return InstructionConst.IALOAD;
088        case Const.T_FLOAT:
089            return InstructionConst.FALOAD;
090        case Const.T_DOUBLE:
091            return InstructionConst.DALOAD;
092        case Const.T_LONG:
093            return InstructionConst.LALOAD;
094        case Const.T_ARRAY:
095        case Const.T_OBJECT:
096            return InstructionConst.AALOAD;
097        default:
098            throw new IllegalArgumentException("Invalid type " + type);
099        }
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