View Javadoc

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