1
2
3
4
5
6
7
8
9
10
11
12
13
14
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,
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,
177 new Type[] {
178 Type.INT
179 }, Constants.ACC_PUBLIC),
180 new MethodObject("java.lang.StringBuffer", "append", Type.STRINGBUFFER,
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 }