001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License. 
016 *
017 */
018package org.apache.bcel.generic;
019
020import org.apache.bcel.Constants;
021
022/** 
023 * Instances of this class may be used, e.g., to generate typed
024 * versions of instructions. Its main purpose is to be used as the
025 * byte code generating backend of a compiler. You can subclass it to
026 * add your own create methods.
027 *
028 * @version $Id: InstructionFactory.html 898356 2014-02-18 05:44:40Z ggregory $
029 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
030 * @see Constants
031 */
032public class InstructionFactory implements InstructionConstants, java.io.Serializable {
033
034        private static final long serialVersionUID = -1210011499635580258L;
035
036    private static final String[] short_names = {
037            "C", "F", "D", "B", "S", "I", "L"
038    };
039
040    protected ClassGen cg;
041    protected ConstantPoolGen cp;
042
043
044    public InstructionFactory(ClassGen cg, ConstantPoolGen cp) {
045        this.cg = cg;
046        this.cp = cp;
047    }
048
049
050    /** Initialize with ClassGen object
051     */
052    public InstructionFactory(ClassGen cg) {
053        this(cg, cg.getConstantPool());
054    }
055
056
057    /** Initialize just with ConstantPoolGen object
058     */
059    public InstructionFactory(ConstantPoolGen cp) {
060        this(null, cp);
061    }
062
063
064    /** Create an invoke instruction.
065     *
066     * @param class_name name of the called class
067     * @param name name of the called method
068     * @param ret_type return type of method
069     * @param arg_types argument types of method
070     * @param kind how to invoke, i.e., INVOKEINTERFACE, INVOKESTATIC, INVOKEVIRTUAL,
071     * or INVOKESPECIAL
072     * @see Constants
073     */
074    public InvokeInstruction createInvoke( String class_name, String name, Type ret_type,
075            Type[] arg_types, short kind ) {
076        int index;
077        int nargs = 0;
078        String signature = Type.getMethodSignature(ret_type, arg_types);
079        for (Type arg_type : arg_types) {
080            nargs += arg_type.getSize();
081        }
082        if (kind == Constants.INVOKEINTERFACE) {
083            index = cp.addInterfaceMethodref(class_name, name, signature);
084        } else {
085            index = cp.addMethodref(class_name, name, signature);
086        }
087        switch (kind) {
088            case Constants.INVOKESPECIAL:
089                return new INVOKESPECIAL(index);
090            case Constants.INVOKEVIRTUAL:
091                return new INVOKEVIRTUAL(index);
092            case Constants.INVOKESTATIC:
093                return new INVOKESTATIC(index);
094            case Constants.INVOKEINTERFACE:
095                return new INVOKEINTERFACE(index, nargs + 1);
096            default:
097                throw new RuntimeException("Oops: Unknown invoke kind:" + kind);
098        }
099    }
100
101
102    /** Create a call to the most popular System.out.println() method.
103     *
104     * @param s the string to print
105     */
106    public InstructionList createPrintln( String s ) {
107        InstructionList il = new InstructionList();
108        int out = cp.addFieldref("java.lang.System", "out", "Ljava/io/PrintStream;");
109        int println = cp.addMethodref("java.io.PrintStream", "println", "(Ljava/lang/String;)V");
110        il.append(new GETSTATIC(out));
111        il.append(new PUSH(cp, s));
112        il.append(new INVOKEVIRTUAL(println));
113        return il;
114    }
115
116
117    /** Uses PUSH to push a constant value onto the stack.
118     * @param value must be of type Number, Boolean, Character or String
119     */
120    public Instruction createConstant( Object value ) {
121        PUSH push;
122        if (value instanceof Number) {
123            push = new PUSH(cp, (Number) value);
124        } else if (value instanceof String) {
125            push = new PUSH(cp, (String) value);
126        } else if (value instanceof Boolean) {
127            push = new PUSH(cp, (Boolean) value);
128        } else if (value instanceof Character) {
129            push = new PUSH(cp, (Character) value);
130        } else {
131            throw new ClassGenException("Illegal type: " + value.getClass());
132        }
133        return push.getInstruction();
134    }
135
136    private static class MethodObject {
137
138        Type[] arg_types;
139        Type result_type;
140        String class_name;
141        String name;
142
143
144        MethodObject(String c, String n, Type r, Type[] a) {
145            class_name = c;
146            name = n;
147            result_type = r;
148            arg_types = a;
149        }
150    }
151
152
153    private InvokeInstruction createInvoke( MethodObject m, short kind ) {
154        return createInvoke(m.class_name, m.name, m.result_type, m.arg_types, kind);
155    }
156
157    private static final MethodObject[] append_mos = {
158            new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER, new Type[] {
159                Type.STRING
160            }),
161            new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER, new Type[] {
162                Type.OBJECT
163            }),
164            null,
165            null, // indices 2, 3
166            new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER, new Type[] {
167                Type.BOOLEAN
168            }),
169            new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER, new Type[] {
170                Type.CHAR
171            }),
172            new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER, new Type[] {
173                Type.FLOAT
174            }),
175            new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER, new Type[] {
176                Type.DOUBLE
177            }),
178            new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER, new Type[] {
179                Type.INT
180            }),
181            new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER, // No append(byte)
182                    new Type[] {
183                        Type.INT
184                    }),
185            new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER, // No append(short)
186                    new Type[] {
187                        Type.INT
188                    }),
189            new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER, new Type[] {
190                Type.LONG
191            })
192    };
193
194
195    private static final boolean isString( Type type ) {
196        return ((type instanceof ObjectType) && ((ObjectType) type).getClassName().equals(
197                "java.lang.String"));
198    }
199
200
201    public Instruction createAppend( Type type ) {
202        byte t = type.getType();
203        if (isString(type)) {
204            return createInvoke(append_mos[0], Constants.INVOKEVIRTUAL);
205        }
206        switch (t) {
207            case Constants.T_BOOLEAN:
208            case Constants.T_CHAR:
209            case Constants.T_FLOAT:
210            case Constants.T_DOUBLE:
211            case Constants.T_BYTE:
212            case Constants.T_SHORT:
213            case Constants.T_INT:
214            case Constants.T_LONG:
215                return createInvoke(append_mos[t], Constants.INVOKEVIRTUAL);
216            case Constants.T_ARRAY:
217            case Constants.T_OBJECT:
218                return createInvoke(append_mos[1], Constants.INVOKEVIRTUAL);
219            default:
220                throw new RuntimeException("Oops: No append for this type? " + type);
221        }
222    }
223
224
225    /** Create a field instruction.
226     *
227     * @param class_name name of the accessed class
228     * @param name name of the referenced field
229     * @param type  type of field
230     * @param kind how to access, i.e., GETFIELD, PUTFIELD, GETSTATIC, PUTSTATIC
231     * @see Constants
232     */
233    public FieldInstruction createFieldAccess( String class_name, String name, Type type, short kind ) {
234        int index;
235        String signature = type.getSignature();
236        index = cp.addFieldref(class_name, name, signature);
237        switch (kind) {
238            case Constants.GETFIELD:
239                return new GETFIELD(index);
240            case Constants.PUTFIELD:
241                return new PUTFIELD(index);
242            case Constants.GETSTATIC:
243                return new GETSTATIC(index);
244            case Constants.PUTSTATIC:
245                return new PUTSTATIC(index);
246            default:
247                throw new RuntimeException("Oops: Unknown getfield kind:" + kind);
248        }
249    }
250
251
252    /** Create reference to `this'
253     */
254    public static Instruction createThis() {
255        return new ALOAD(0);
256    }
257
258
259    /** Create typed return
260     */
261    public static ReturnInstruction createReturn( Type type ) {
262        switch (type.getType()) {
263            case Constants.T_ARRAY:
264            case Constants.T_OBJECT:
265                return ARETURN;
266            case Constants.T_INT:
267            case Constants.T_SHORT:
268            case Constants.T_BOOLEAN:
269            case Constants.T_CHAR:
270            case Constants.T_BYTE:
271                return IRETURN;
272            case Constants.T_FLOAT:
273                return FRETURN;
274            case Constants.T_DOUBLE:
275                return DRETURN;
276            case Constants.T_LONG:
277                return LRETURN;
278            case Constants.T_VOID:
279                return RETURN;
280            default:
281                throw new RuntimeException("Invalid type: " + type);
282        }
283    }
284
285
286    private static final ArithmeticInstruction createBinaryIntOp( char first, String op ) {
287        switch (first) {
288            case '-':
289                return ISUB;
290            case '+':
291                return IADD;
292            case '%':
293                return IREM;
294            case '*':
295                return IMUL;
296            case '/':
297                return IDIV;
298            case '&':
299                return IAND;
300            case '|':
301                return IOR;
302            case '^':
303                return IXOR;
304            case '<':
305                return ISHL;
306            case '>':
307                return op.equals(">>>")
308                        ? (ArithmeticInstruction) IUSHR
309                        : (ArithmeticInstruction) ISHR;
310            default:
311                throw new RuntimeException("Invalid operand " + op);
312        }
313    }
314
315
316    private static final ArithmeticInstruction createBinaryLongOp( char first, String op ) {
317        switch (first) {
318            case '-':
319                return LSUB;
320            case '+':
321                return LADD;
322            case '%':
323                return LREM;
324            case '*':
325                return LMUL;
326            case '/':
327                return LDIV;
328            case '&':
329                return LAND;
330            case '|':
331                return LOR;
332            case '^':
333                return LXOR;
334            case '<':
335                return LSHL;
336            case '>':
337                return op.equals(">>>")
338                        ? (ArithmeticInstruction) LUSHR
339                        : (ArithmeticInstruction) LSHR;
340            default:
341                throw new RuntimeException("Invalid operand " + op);
342        }
343    }
344
345
346    private static final ArithmeticInstruction createBinaryFloatOp( char op ) {
347        switch (op) {
348            case '-':
349                return FSUB;
350            case '+':
351                return FADD;
352            case '*':
353                return FMUL;
354            case '/':
355                return FDIV;
356            case '%':
357                return FREM;
358            default:
359                throw new RuntimeException("Invalid operand " + op);
360        }
361    }
362
363
364    private static final ArithmeticInstruction createBinaryDoubleOp( char op ) {
365        switch (op) {
366            case '-':
367                return DSUB;
368            case '+':
369                return DADD;
370            case '*':
371                return DMUL;
372            case '/':
373                return DDIV;
374            case '%':
375                return DREM;
376            default:
377                throw new RuntimeException("Invalid operand " + op);
378        }
379    }
380
381
382    /**
383     * Create binary operation for simple basic types, such as int and float.
384     *
385     * @param op operation, such as "+", "*", "<<", etc.
386     */
387    public static ArithmeticInstruction createBinaryOperation( String op, Type type ) {
388        char first = op.toCharArray()[0];
389        switch (type.getType()) {
390            case Constants.T_BYTE:
391            case Constants.T_SHORT:
392            case Constants.T_INT:
393            case Constants.T_CHAR:
394                return createBinaryIntOp(first, op);
395            case Constants.T_LONG:
396                return createBinaryLongOp(first, op);
397            case Constants.T_FLOAT:
398                return createBinaryFloatOp(first);
399            case Constants.T_DOUBLE:
400                return createBinaryDoubleOp(first);
401            default:
402                throw new RuntimeException("Invalid type " + type);
403        }
404    }
405
406
407    /**
408     * @param size size of operand, either 1 (int, e.g.) or 2 (double)
409     */
410    public static StackInstruction createPop( int size ) {
411        return (size == 2) ? (StackInstruction) POP2 : (StackInstruction) POP;
412    }
413
414
415    /**
416     * @param size size of operand, either 1 (int, e.g.) or 2 (double)
417     */
418    public static StackInstruction createDup( int size ) {
419        return (size == 2) ? (StackInstruction) DUP2 : (StackInstruction) DUP;
420    }
421
422
423    /**
424     * @param size size of operand, either 1 (int, e.g.) or 2 (double)
425     */
426    public static StackInstruction createDup_2( int size ) {
427        return (size == 2) ? (StackInstruction) DUP2_X2 : (StackInstruction) DUP_X2;
428    }
429
430
431    /**
432     * @param size size of operand, either 1 (int, e.g.) or 2 (double)
433     */
434    public static StackInstruction createDup_1( int size ) {
435        return (size == 2) ? (StackInstruction) DUP2_X1 : (StackInstruction) DUP_X1;
436    }
437
438
439    /**
440     * @param index index of local variable
441     */
442    public static LocalVariableInstruction createStore( Type type, int index ) {
443        switch (type.getType()) {
444            case Constants.T_BOOLEAN:
445            case Constants.T_CHAR:
446            case Constants.T_BYTE:
447            case Constants.T_SHORT:
448            case Constants.T_INT:
449                return new ISTORE(index);
450            case Constants.T_FLOAT:
451                return new FSTORE(index);
452            case Constants.T_DOUBLE:
453                return new DSTORE(index);
454            case Constants.T_LONG:
455                return new LSTORE(index);
456            case Constants.T_ARRAY:
457            case Constants.T_OBJECT:
458                return new ASTORE(index);
459            default:
460                throw new RuntimeException("Invalid type " + type);
461        }
462    }
463
464
465    /**
466     * @param index index of local variable
467     */
468    public static LocalVariableInstruction createLoad( Type type, int index ) {
469        switch (type.getType()) {
470            case Constants.T_BOOLEAN:
471            case Constants.T_CHAR:
472            case Constants.T_BYTE:
473            case Constants.T_SHORT:
474            case Constants.T_INT:
475                return new ILOAD(index);
476            case Constants.T_FLOAT:
477                return new FLOAD(index);
478            case Constants.T_DOUBLE:
479                return new DLOAD(index);
480            case Constants.T_LONG:
481                return new LLOAD(index);
482            case Constants.T_ARRAY:
483            case Constants.T_OBJECT:
484                return new ALOAD(index);
485            default:
486                throw new RuntimeException("Invalid type " + type);
487        }
488    }
489
490
491    /**
492     * @param type type of elements of array, i.e., array.getElementType()
493     */
494    public static ArrayInstruction createArrayLoad( Type type ) {
495        switch (type.getType()) {
496            case Constants.T_BOOLEAN:
497            case Constants.T_BYTE:
498                return BALOAD;
499            case Constants.T_CHAR:
500                return CALOAD;
501            case Constants.T_SHORT:
502                return SALOAD;
503            case Constants.T_INT:
504                return IALOAD;
505            case Constants.T_FLOAT:
506                return FALOAD;
507            case Constants.T_DOUBLE:
508                return DALOAD;
509            case Constants.T_LONG:
510                return LALOAD;
511            case Constants.T_ARRAY:
512            case Constants.T_OBJECT:
513                return AALOAD;
514            default:
515                throw new RuntimeException("Invalid type " + type);
516        }
517    }
518
519
520    /**
521     * @param type type of elements of array, i.e., array.getElementType()
522     */
523    public static ArrayInstruction createArrayStore( Type type ) {
524        switch (type.getType()) {
525            case Constants.T_BOOLEAN:
526            case Constants.T_BYTE:
527                return BASTORE;
528            case Constants.T_CHAR:
529                return CASTORE;
530            case Constants.T_SHORT:
531                return SASTORE;
532            case Constants.T_INT:
533                return IASTORE;
534            case Constants.T_FLOAT:
535                return FASTORE;
536            case Constants.T_DOUBLE:
537                return DASTORE;
538            case Constants.T_LONG:
539                return LASTORE;
540            case Constants.T_ARRAY:
541            case Constants.T_OBJECT:
542                return AASTORE;
543            default:
544                throw new RuntimeException("Invalid type " + type);
545        }
546    }
547
548
549    /** Create conversion operation for two stack operands, this may be an I2C, instruction, e.g.,
550     * if the operands are basic types and CHECKCAST if they are reference types.
551     */
552    public Instruction createCast( Type src_type, Type dest_type ) {
553        if ((src_type instanceof BasicType) && (dest_type instanceof BasicType)) {
554            byte dest = dest_type.getType();
555            byte src = src_type.getType();
556            if (dest == Constants.T_LONG
557                    && (src == Constants.T_CHAR || src == Constants.T_BYTE || src == Constants.T_SHORT)) {
558                src = Constants.T_INT;
559            }
560            String name = "org.apache.bcel.generic." + short_names[src - Constants.T_CHAR] + "2"
561                    + short_names[dest - Constants.T_CHAR];
562            Instruction i = null;
563            try {
564                i = (Instruction) java.lang.Class.forName(name).newInstance();
565            } catch (Exception e) {
566                throw new RuntimeException("Could not find instruction: " + name, e);
567            }
568            return i;
569        } else if ((src_type instanceof ReferenceType) && (dest_type instanceof ReferenceType)) {
570            if (dest_type instanceof ArrayType) {
571                return new CHECKCAST(cp.addArrayClass((ArrayType) dest_type));
572            } else {
573                return new CHECKCAST(cp.addClass(((ObjectType) dest_type).getClassName()));
574            }
575        } else {
576            throw new RuntimeException("Can not cast " + src_type + " to " + dest_type);
577        }
578    }
579
580
581    public GETFIELD createGetField( String class_name, String name, Type t ) {
582        return new GETFIELD(cp.addFieldref(class_name, name, t.getSignature()));
583    }
584
585
586    public GETSTATIC createGetStatic( String class_name, String name, Type t ) {
587        return new GETSTATIC(cp.addFieldref(class_name, name, t.getSignature()));
588    }
589
590
591    public PUTFIELD createPutField( String class_name, String name, Type t ) {
592        return new PUTFIELD(cp.addFieldref(class_name, name, t.getSignature()));
593    }
594
595
596    public PUTSTATIC createPutStatic( String class_name, String name, Type t ) {
597        return new PUTSTATIC(cp.addFieldref(class_name, name, t.getSignature()));
598    }
599
600
601    public CHECKCAST createCheckCast( ReferenceType t ) {
602        if (t instanceof ArrayType) {
603            return new CHECKCAST(cp.addArrayClass((ArrayType) t));
604        } else {
605            return new CHECKCAST(cp.addClass((ObjectType) t));
606        }
607    }
608
609
610    public INSTANCEOF createInstanceOf( ReferenceType t ) {
611        if (t instanceof ArrayType) {
612            return new INSTANCEOF(cp.addArrayClass((ArrayType) t));
613        } else {
614            return new INSTANCEOF(cp.addClass((ObjectType) t));
615        }
616    }
617
618
619    public NEW createNew( ObjectType t ) {
620        return new NEW(cp.addClass(t));
621    }
622
623
624    public NEW createNew( String s ) {
625        return createNew(ObjectType.getInstance(s));
626    }
627
628
629    /** Create new array of given size and type.
630     * @return an instruction that creates the corresponding array at runtime, i.e. is an AllocationInstruction
631     */
632    public Instruction createNewArray( Type t, short dim ) {
633        if (dim == 1) {
634            if (t instanceof ObjectType) {
635                return new ANEWARRAY(cp.addClass((ObjectType) t));
636            } else if (t instanceof ArrayType) {
637                return new ANEWARRAY(cp.addArrayClass((ArrayType) t));
638            } else {
639                return new NEWARRAY(((BasicType) t).getType());
640            }
641        } else {
642            ArrayType at;
643            if (t instanceof ArrayType) {
644                at = (ArrayType) t;
645            } else {
646                at = new ArrayType(t, dim);
647            }
648            return new MULTIANEWARRAY(cp.addArrayClass(at), dim);
649        }
650    }
651
652
653    /** Create "null" value for reference types, 0 for basic types like int
654     */
655    public static Instruction createNull( Type type ) {
656        switch (type.getType()) {
657            case Constants.T_ARRAY:
658            case Constants.T_OBJECT:
659                return ACONST_NULL;
660            case Constants.T_INT:
661            case Constants.T_SHORT:
662            case Constants.T_BOOLEAN:
663            case Constants.T_CHAR:
664            case Constants.T_BYTE:
665                return ICONST_0;
666            case Constants.T_FLOAT:
667                return FCONST_0;
668            case Constants.T_DOUBLE:
669                return DCONST_0;
670            case Constants.T_LONG:
671                return LCONST_0;
672            case Constants.T_VOID:
673                return NOP;
674            default:
675                throw new RuntimeException("Invalid type: " + type);
676        }
677    }
678
679
680    /** Create branch instruction by given opcode, except LOOKUPSWITCH and TABLESWITCH.
681     * For those you should use the SWITCH compound instruction.
682     */
683    public static BranchInstruction createBranchInstruction( short opcode, InstructionHandle target ) {
684        switch (opcode) {
685            case Constants.IFEQ:
686                return new IFEQ(target);
687            case Constants.IFNE:
688                return new IFNE(target);
689            case Constants.IFLT:
690                return new IFLT(target);
691            case Constants.IFGE:
692                return new IFGE(target);
693            case Constants.IFGT:
694                return new IFGT(target);
695            case Constants.IFLE:
696                return new IFLE(target);
697            case Constants.IF_ICMPEQ:
698                return new IF_ICMPEQ(target);
699            case Constants.IF_ICMPNE:
700                return new IF_ICMPNE(target);
701            case Constants.IF_ICMPLT:
702                return new IF_ICMPLT(target);
703            case Constants.IF_ICMPGE:
704                return new IF_ICMPGE(target);
705            case Constants.IF_ICMPGT:
706                return new IF_ICMPGT(target);
707            case Constants.IF_ICMPLE:
708                return new IF_ICMPLE(target);
709            case Constants.IF_ACMPEQ:
710                return new IF_ACMPEQ(target);
711            case Constants.IF_ACMPNE:
712                return new IF_ACMPNE(target);
713            case Constants.GOTO:
714                return new GOTO(target);
715            case Constants.JSR:
716                return new JSR(target);
717            case Constants.IFNULL:
718                return new IFNULL(target);
719            case Constants.IFNONNULL:
720                return new IFNONNULL(target);
721            case Constants.GOTO_W:
722                return new GOTO_W(target);
723            case Constants.JSR_W:
724                return new JSR_W(target);
725            default:
726                throw new RuntimeException("Invalid opcode: " + opcode);
727        }
728    }
729
730
731    public void setClassGen( ClassGen c ) {
732        cg = c;
733    }
734
735
736    public ClassGen getClassGen() {
737        return cg;
738    }
739
740
741    public void setConstantPool( ConstantPoolGen c ) {
742        cp = c;
743    }
744
745
746    public ConstantPoolGen getConstantPool() {
747        return cp;
748    }
749}