comparison src/com/go/trove/classfile/CodeBuilder.java @ 0:3dc0c5604566

Initial checkin of blitz 2.0 fcs - no installer yet.
author Dan Creswell <dan.creswell@gmail.com>
date Sat, 21 Mar 2009 11:00:06 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:3dc0c5604566
1 /* ====================================================================
2 * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group
3 * ====================================================================
4 * The Tea Software License, Version 1.1
5 *
6 * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The end-user documentation included with the redistribution,
21 * if any, must include the following acknowledgment:
22 * "This product includes software developed by the
23 * Walt Disney Internet Group (http://opensource.go.com/)."
24 * Alternately, this acknowledgment may appear in the software itself,
25 * if and wherever such third-party acknowledgments normally appear.
26 *
27 * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
28 * not be used to endorse or promote products derived from this
29 * software without prior written permission. For written
30 * permission, please contact opensource@dig.com.
31 *
32 * 5. Products derived from this software may not be called "Tea",
33 * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
34 * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
35 * written permission of the Walt Disney Internet Group.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
41 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
42 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
43 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
44 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
45 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48 * ====================================================================
49 *
50 * For more information about Tea, please see http://opensource.go.com/.
51 */
52
53 package com.go.trove.classfile;
54
55 import java.lang.reflect.*;
56
57 /******************************************************************************
58 * This class is used as an aid in generating code for a method.
59 * It controls the max stack, local variable allocation, labels and bytecode.
60 *
61 * @author Brian S O'Neill
62 * @version
63 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
64 */
65 public class CodeBuilder implements CodeBuffer, CodeAssembler {
66 private CodeAttr mCodeAttr;
67 private ClassFile mClassFile;
68 private ConstantPool mCp;
69
70 private InstructionList mInstructions = new InstructionList();
71
72 private LocalVariable mThisReference;
73 private LocalVariable[] mParameters;
74
75 private boolean mSaveLineNumberInfo;
76 private boolean mSaveLocalVariableInfo;
77
78 /**
79 * Construct a CodeBuilder for the CodeAttr of the given MethodInfo. The
80 * CodeBuffer for the CodeAttr is automatically set to this CodeBuilder.
81 */
82 public CodeBuilder(MethodInfo info) {
83 this(info, true, false);
84 }
85
86 /**
87 * Construct a CodeBuilder for the CodeAttr of the given MethodInfo. The
88 * CodeBuffer for the CodeAttr is automatically set to this CodeBuilder.
89 *
90 * @param saveLineNumberInfo When set false, all calls to mapLineNumber
91 * are ignored. By default, this value is true.
92 *
93 * @param saveLocalVariableInfo When set true, all local variable
94 * usage information is saved in the ClassFile. By default, this value
95 * is false.
96 *
97 * @see #mapLineNumber
98 */
99 public CodeBuilder(MethodInfo info, boolean saveLineNumberInfo,
100 boolean saveLocalVariableInfo) {
101 mCodeAttr = info.getCodeAttr();
102 mClassFile = info.getClassFile();
103 mCp = mClassFile.getConstantPool();
104
105 mCodeAttr.setCodeBuffer(this);
106
107 mSaveLineNumberInfo = saveLineNumberInfo;
108 mSaveLocalVariableInfo = saveLocalVariableInfo;
109
110 // Create LocalVariable references for "this" reference and other
111 // passed in parameters.
112
113 LocalVariable localVar;
114 int varNum = 0;
115
116 if (!info.getAccessFlags().isStatic()) {
117 localVar = mInstructions.createLocalParameter
118 ("this", mClassFile.getType(), varNum++);
119 mThisReference = localVar;
120
121 if (saveLocalVariableInfo) {
122 mCodeAttr.localVariableUse(localVar);
123 }
124 }
125
126 TypeDescriptor[] paramTypes =
127 info.getMethodDescriptor().getParameterTypes();
128 int paramSize = paramTypes.length;
129
130 mParameters = new LocalVariable[paramSize];
131
132 for (int i = 0; i<paramTypes.length; i++) {
133 localVar = mInstructions.createLocalParameter
134 (null, paramTypes[i], varNum);
135 varNum += (localVar.isDoubleWord() ? 2 : 1);
136 mParameters[i] = localVar;
137
138 if (saveLocalVariableInfo) {
139 mCodeAttr.localVariableUse(localVar);
140 }
141 }
142 }
143
144 public int getMaxStackDepth() {
145 return mInstructions.getMaxStackDepth();
146 }
147
148 public int getMaxLocals() {
149 return mInstructions.getMaxLocals();
150 }
151
152 public byte[] getByteCodes() {
153 return mInstructions.getByteCodes();
154 }
155
156 public ExceptionHandler[] getExceptionHandlers() {
157 return mInstructions.getExceptionHandlers();
158 }
159
160 private void addCode(int stackAdjust, byte opcode) {
161 mInstructions.new CodeInstruction(stackAdjust, new byte[] {opcode});
162 }
163
164 private void addCode(int stackAdjust, byte opcode, byte operand) {
165 mInstructions.new CodeInstruction
166 (stackAdjust, new byte[] {opcode, operand});
167 }
168
169 private void addCode(int stackAdjust, byte opcode, short operand) {
170 mInstructions.new CodeInstruction
171 (stackAdjust,
172 new byte[] {opcode, (byte)(operand >> 8), (byte)operand});
173 }
174
175 private void addCode(int stackAdjust, byte opcode, int operand) {
176 byte[] bytes = new byte[5];
177
178 bytes[0] = opcode;
179 bytes[1] = (byte)(operand >> 24);
180 bytes[2] = (byte)(operand >> 16);
181 bytes[3] = (byte)(operand >> 8);
182 bytes[4] = (byte)operand;
183
184 mInstructions.new CodeInstruction(stackAdjust, bytes);
185 }
186
187 private void addCode(int stackAdjust, byte opcode, ConstantInfo info) {
188 // The zeros get filled in later, when the ConstantInfo index
189 // is resolved.
190 mInstructions.new ConstantOperandInstruction
191 (stackAdjust,
192 new byte[] {opcode, (byte)0, (byte)0}, info);
193 }
194
195 /**
196 * Returns LocalVariable references for all the parameters passed into
197 * the method being assembled, not including any "this" reference.
198 * Returns a zero-length array if there are no passed in parameters.
199 *
200 * <p>The names of the LocalVariables returned by this method are initially
201 * set to null. It is encouraged that a name be provided.
202 */
203 public LocalVariable[] getParameters() {
204 return (LocalVariable[])mParameters.clone();
205 }
206
207 /**
208 * Creates a LocalVariable reference from a name and type. Although name
209 * is optional, it is encouraged that a name be provided. Names do not
210 * need to be unique.
211 *
212 * @param name Optional name for the LocalVariable.
213 * @param type The type of data that the requested LocalVariable can
214 * store.
215 */
216 public LocalVariable createLocalVariable(String name,
217 TypeDescriptor type) {
218 LocalVariable localVar = mInstructions.createLocalVariable(name, type);
219
220 if (mSaveLocalVariableInfo) {
221 mCodeAttr.localVariableUse(localVar);
222 }
223
224 return localVar;
225 }
226
227 /**
228 * Creates a label, whose location must be set. To create a label and
229 * locate it here, the following example demonstrates how the call to
230 * setLocation can be chained:
231 *
232 * <pre>
233 * CodeBuilder builder;
234 * ...
235 * Label label = builder.createLabel().setLocation();
236 * </pre>
237 *
238 * @see Label#setLocation
239 */
240 public Label createLabel() {
241 return mInstructions.new LabelInstruction();
242 }
243
244 /**
245 * Sets up an exception handler located here, the location of the next
246 * code to be generated.
247 *
248 * @param startLocation Location or label at the start of the section of
249 * code to be wrapped by an exception handler.
250 * @param endLocation Location or label directly after the end of the
251 * section of code.
252 * @param catchClassName The name of the type of exception to be caught;
253 * if null, then catch every object.
254 */
255 public void exceptionHandler(Location startLocation,
256 Location endLocation,
257 String catchClassName) {
258 Location catchLocation = createLabel().setLocation();
259
260 ConstantClassInfo catchClass;
261 if (catchClassName == null) {
262 catchClass = null;
263 }
264 else {
265 catchClass = ConstantClassInfo.make(mCp, catchClassName);
266 }
267
268 ExceptionHandler handler =
269 new ExceptionHandler(startLocation, endLocation,
270 catchLocation, catchClass);
271
272 mInstructions.addExceptionHandler(handler);
273 }
274
275 /**
276 * Map the location of the next code to be generated to a line number
277 * in source code. This enables line numbers in a stack trace from the
278 * generated code.
279 */
280 public void mapLineNumber(int lineNumber) {
281 if (mSaveLineNumberInfo) {
282 mCodeAttr.mapLineNumber(createLabel().setLocation(), lineNumber);
283 }
284 }
285
286 // load-constant-to-stack style instructions
287
288 /**
289 * Generates code that loads a constant string value onto the stack.
290 * If value is null, the generated code loads a null onto the stack.
291 * Strings that exceed 65535 UTF encoded bytes in length are loaded by
292 * creating a StringBuffer, appending substrings, and then converting to a
293 * String.
294 */
295 public void loadConstant(String value) {
296 if (value == null) {
297 addCode(1, Opcode.ACONST_NULL);
298 return;
299 }
300
301 int strlen = value.length();
302
303 if (strlen <= (65535 / 3)) {
304 // Guaranteed to fit in a Java UTF encoded string.
305 ConstantInfo info = ConstantStringInfo.make(mCp, value);
306 mInstructions.new LoadConstantInstruction(1, info);
307 return;
308 }
309
310 // Compute actual UTF length.
311
312 int utflen = 0;
313
314 for (int i=0; i<strlen; i++) {
315 int c = value.charAt(i);
316 if ((c >= 0x0001) && (c <= 0x007F)) {
317 utflen++;
318 }
319 else if (c > 0x07FF) {
320 utflen += 3;
321 }
322 else {
323 utflen += 2;
324 }
325 }
326
327 if (utflen <= 65535) {
328 ConstantInfo info = ConstantStringInfo.make(mCp, value);
329 mInstructions.new LoadConstantInstruction(1, info);
330 return;
331 }
332
333 // Break string up into chunks and construct in a StringBuffer.
334
335 TypeDescriptor stringBufferDesc =
336 new TypeDescriptor(StringBuffer.class);
337
338 TypeDescriptor intDesc = new TypeDescriptor(int.class);
339 TypeDescriptor stringDesc = new TypeDescriptor(String.class);
340 TypeDescriptor[] stringParam = new TypeDescriptor[] {stringDesc};
341
342 newObject(stringBufferDesc);
343 dup();
344 loadConstant(strlen);
345 invokeConstructor("java.lang.StringBuffer",
346 new TypeDescriptor[] {intDesc});
347
348 int beginIndex;
349 int endIndex = 0;
350
351 while (endIndex < strlen) {
352 beginIndex = endIndex;
353
354 // Make each chunk as large as possible.
355 utflen = 0;
356 for (; endIndex < strlen; endIndex++) {
357 int c = value.charAt(endIndex);
358 int size;
359 if ((c >= 0x0001) && (c <= 0x007F)) {
360 size = 1;
361 }
362 else if (c > 0x07FF) {
363 size = 3;
364 }
365 else {
366 size = 2;
367 }
368
369 if ((utflen + size) > 65535) {
370 break;
371 }
372 else {
373 utflen += size;
374 }
375 }
376
377 String substr = value.substring(beginIndex, endIndex);
378
379 ConstantInfo info = ConstantStringInfo.make(mCp, substr);
380 mInstructions.new LoadConstantInstruction(1, info);
381
382 invokeVirtual("java.lang.StringBuffer", "append",
383 stringBufferDesc, stringParam);
384 }
385
386 invokeVirtual("java.lang.StringBuffer", "toString",
387 stringDesc, null);
388 }
389
390 /**
391 * Generates code that loads a constant boolean value onto the stack.
392 */
393 public void loadConstant(boolean value) {
394 loadConstant(value?1:0);
395 }
396
397 /**
398 * Generates code that loads a constant int, char, short or byte value
399 * onto the stack.
400 */
401 public void loadConstant(int value) {
402 if (-1 <= value && value <= 5) {
403 byte op;
404
405 switch(value) {
406 case -1:
407 op = Opcode.ICONST_M1;
408 break;
409 case 0:
410 op = Opcode.ICONST_0;
411 break;
412 case 1:
413 op = Opcode.ICONST_1;
414 break;
415 case 2:
416 op = Opcode.ICONST_2;
417 break;
418 case 3:
419 op = Opcode.ICONST_3;
420 break;
421 case 4:
422 op = Opcode.ICONST_4;
423 break;
424 case 5:
425 op = Opcode.ICONST_5;
426 break;
427 default:
428 op = Opcode.NOP;
429 }
430
431 addCode(1, op);
432 }
433 else if (-128 <= value && value <= 127) {
434 addCode(1, Opcode.BIPUSH, (byte)value);
435 }
436 else if (-32768 <= value && value <= 32767) {
437 addCode(1, Opcode.SIPUSH, (short)value);
438 }
439 else {
440 ConstantInfo info = ConstantIntegerInfo.make(mCp, value);
441 mInstructions.new LoadConstantInstruction(1, info);
442 }
443 }
444
445 /**
446 * Generates code that loads a constant long value onto the stack.
447 */
448 public void loadConstant(long value) {
449 if (value == 0) {
450 addCode(2, Opcode.LCONST_0);
451 }
452 else if (value == 1) {
453 addCode(2, Opcode.LCONST_1);
454 }
455 else {
456 ConstantInfo info = ConstantLongInfo.make(mCp, value);
457 mInstructions.new LoadConstantInstruction(2, info, true);
458 }
459 }
460
461 /**
462 * Generates code that loads a constant float value onto the stack.
463 */
464 public void loadConstant(float value) {
465 if (value == 0) {
466 addCode(1, Opcode.FCONST_0);
467 }
468 else if (value == 1) {
469 addCode(1, Opcode.FCONST_1);
470 }
471 else if (value == 2) {
472 addCode(1, Opcode.FCONST_2);
473 }
474 else {
475 ConstantInfo info = ConstantFloatInfo.make(mCp, value);
476 mInstructions.new LoadConstantInstruction(1, info);
477 }
478 }
479
480 /**
481 * Generates code that loads a constant double value onto the stack.
482 */
483 public void loadConstant(double value) {
484 if (value == 0) {
485 addCode(2, Opcode.DCONST_0);
486 }
487 else if (value == 1) {
488 addCode(2, Opcode.DCONST_1);
489 }
490 else {
491 ConstantInfo info = ConstantDoubleInfo.make(mCp, value);
492 mInstructions.new LoadConstantInstruction(2, info, true);
493 }
494 }
495
496 // load-local-to-stack style instructions
497
498 /**
499 * Generates code that loads a local variable onto the stack. Parameters
500 * passed to a method and the "this" reference are all considered local
501 * variables, as well as any that were created.
502 *
503 * @param local The local variable reference
504 * @see #getParameters
505 * @see #createLocalVariable
506 */
507 public void loadLocal(LocalVariable local) {
508 if (local == null) {
509 throw new NullPointerException("No local variable specified");
510 }
511
512 TypeDescriptor type = local.getType();
513 Class clazz = type.getClassArg();
514
515 int stackAdjust = 1;
516
517 if (clazz != null && type.getDimensions() == 0 &&
518 (clazz == long.class || clazz == double.class)) {
519
520 stackAdjust++;
521 }
522
523 mInstructions.new LoadLocalInstruction(stackAdjust, local);
524 }
525
526 /**
527 * Loads a reference to "this" onto the stack. Static methods have no
528 * "this" reference, and an exception is thrown when attempting to
529 * generate "this" in a static method.
530 */
531 public void loadThis() {
532 if (mThisReference != null) {
533 loadLocal(mThisReference);
534 }
535 else {
536 throw new RuntimeException
537 ("Attempt to load \"this\" reference in a static method");
538 }
539 }
540
541 // store-from-stack-to-local style instructions
542
543 /**
544 * Generates code that pops a value off of the stack into a local variable.
545 * Parameters passed to a method and the "this" reference are all
546 * considered local variables, as well as any that were created.
547 *
548 * @param local The local variable reference
549 * @see #getParameters
550 * @see #createLocalVariable
551 */
552 public void storeLocal(LocalVariable local) {
553 if (local == null) {
554 throw new NullPointerException("No local variable specified");
555 }
556
557 TypeDescriptor type = local.getType();
558 Class clazz = type.getClassArg();
559
560 int stackAdjust = -1;
561
562 if (clazz != null && type.getDimensions() == 0 &&
563 (clazz == long.class || clazz == double.class)) {
564
565 stackAdjust--;
566 }
567
568 mInstructions.new StoreLocalInstruction(stackAdjust, local);
569 }
570
571 // load-to-stack-from-array style instructions
572
573 /**
574 * Generates code that loads a value from an array. An array
575 * reference followed by an index must be on the stack. The array
576 * reference and index are replaced by the value retrieved from the array
577 * after the generated instruction has executed.
578 *
579 * <p>The type doesn't need to be an exact match for objects. Object.class
580 * works fine for all objects. For primitive types, use the class that
581 * matches that type. For an int the type is int.class.
582 *
583 * @param type The type of data stored in the array.
584 */
585 public void loadFromArray(Class type) {
586 byte op;
587 int stackAdjust;
588
589 if (type == int.class) {
590 stackAdjust = -1;
591 op = Opcode.IALOAD;
592 }
593 else if (type == boolean.class || type == byte.class) {
594 stackAdjust = -1;
595 op = Opcode.BALOAD;
596 }
597 else if (type == short.class) {
598 stackAdjust = -1;
599 op = Opcode.SALOAD;
600 }
601 else if (type == char.class) {
602 stackAdjust = -1;
603 op = Opcode.CALOAD;
604 }
605 else if (type == long.class) {
606 stackAdjust = 0;
607 op = Opcode.LALOAD;
608 }
609 else if (type == float.class) {
610 stackAdjust = -1;
611 op = Opcode.FALOAD;
612 }
613 else if (type == double.class) {
614 stackAdjust = 0;
615 op = Opcode.DALOAD;
616 }
617 else {
618 stackAdjust = -1;
619 op = Opcode.AALOAD;
620 }
621
622 addCode(stackAdjust, op);
623 }
624
625 // store-to-array-from-stack style instructions
626
627 /**
628 * Generates code that stores a value to an array. An array
629 * reference followed by an index, followed by a value (or two if a long
630 * or double) must be on the stack. All items on the stack are gone
631 * after the generated instruction has executed.
632 *
633 * <p>The type doesn't need to be an exact match for objects. Object.class
634 * works fine for all objects. For primitive types, use the class that
635 * matches that type. For an int the type is int.class.
636 *
637 * @param type The type of data stored in the array.
638 */
639 public void storeToArray(Class type) {
640 byte op;
641 int stackAdjust;
642
643 if (type == int.class) {
644 stackAdjust = -3;
645 op = Opcode.IASTORE;
646 }
647 else if (type == boolean.class || type == byte.class) {
648 stackAdjust = -3;
649 op = Opcode.BASTORE;
650 }
651 else if (type == short.class) {
652 stackAdjust = -3;
653 op = Opcode.SASTORE;
654 }
655 else if (type == char.class) {
656 stackAdjust = -3;
657 op = Opcode.CASTORE;
658 }
659 else if (type == long.class) {
660 stackAdjust = -4;
661 op = Opcode.LASTORE;
662 }
663 else if (type == float.class) {
664 stackAdjust = -3;
665 op = Opcode.FASTORE;
666 }
667 else if (type == double.class) {
668 stackAdjust = -4;
669 op = Opcode.DASTORE;
670 }
671 else {
672 stackAdjust = -3;
673 op = Opcode.AASTORE;
674 }
675
676 addCode(stackAdjust, op);
677 }
678
679 // load-field-to-stack style instructions
680
681 /**
682 * Generates code that loads a value from a field from this class.
683 * An object reference must be on the stack. After the generated code
684 * has executed, the object reference is replaced by the value retrieved
685 * from the field.
686 */
687 public void loadField(String fieldName,
688 TypeDescriptor type) {
689 getfield(0, Opcode.GETFIELD, constantField(fieldName, type), type);
690 }
691
692 /**
693 * Generates code that loads a value from a field from any class.
694 * An object reference must be on the stack. After the generated code
695 * has executed, the object reference is replaced by the value retrieved
696 * from the field.
697 */
698 public void loadField(String className,
699 String fieldName,
700 TypeDescriptor type) {
701
702 getfield(0, Opcode.GETFIELD,
703 mCp.addConstantField(className, fieldName, type),
704 type);
705 }
706
707 /**
708 * Generates code that loads a value from a static field from this class.
709 * After the generated code has executed, the value retrieved is placed
710 * on the stack.
711 */
712 public void loadStaticField(String fieldName,
713 TypeDescriptor type) {
714
715 getfield(1, Opcode.GETSTATIC, constantField(fieldName, type), type);
716 }
717
718 /**
719 * Generates code that loads a value from a static field from any class.
720 * After the generated code has executed, the value retrieved is placed
721 * on the stack.
722 */
723 public void loadStaticField(String className,
724 String fieldName,
725 TypeDescriptor type) {
726
727 getfield(1, Opcode.GETSTATIC,
728 mCp.addConstantField(className, fieldName, type),
729 type);
730 }
731
732 private void getfield(int stackAdjust, byte opcode, ConstantInfo info,
733 TypeDescriptor type) {
734
735 Class clazz = type.getClassArg();
736 if (clazz != null && type.getDimensions() == 0 &&
737 (clazz == long.class || clazz == double.class)) {
738 stackAdjust++;
739 }
740
741 addCode(stackAdjust, opcode, info);
742 }
743
744 private ConstantFieldInfo constantField(String fieldName,
745 TypeDescriptor type) {
746 return mCp.addConstantField
747 (mClassFile.getClassName(), fieldName, type);
748 }
749
750 // store-to-field-from-stack style instructions
751
752 /**
753 * Generates code that stores a value into a field from this class.
754 * An object reference and value must be on the stack. After the generated
755 * code has executed, the object reference and value are gone from
756 * the stack.
757 */
758 public void storeField(String fieldName,
759 TypeDescriptor type) {
760
761 putfield(-1, Opcode.PUTFIELD, constantField(fieldName, type), type);
762 }
763
764 /**
765 * Generates code that stores a value into a field from any class.
766 * An object reference and value must be on the stack. After the generated
767 * code has executed, the object reference and value are gone from
768 * the stack.
769 */
770 public void storeField(String className,
771 String fieldName,
772 TypeDescriptor type) {
773
774 putfield(-1, Opcode.PUTFIELD,
775 mCp.addConstantField(className, fieldName, type),
776 type);
777 }
778
779 /**
780 * Generates code that stores a value into a field from this class.
781 * A value must be on the stack. After the generated
782 * code has executed, the value is gone from the stack.
783 */
784 public void storeStaticField(String fieldName,
785 TypeDescriptor type) {
786
787 putfield(0, Opcode.PUTSTATIC, constantField(fieldName, type), type);
788 }
789
790 /**
791 * Generates code that stores a value into a field from any class.
792 * A value must be on the stack. After the generated
793 * code has executed, the value is gone from the stack.
794 */
795 public void storeStaticField(String className,
796 String fieldName,
797 TypeDescriptor type) {
798
799 putfield(0, Opcode.PUTSTATIC,
800 mCp.addConstantField(className, fieldName, type),
801 type);
802 }
803
804 private void putfield(int stackAdjust, byte opcode, ConstantInfo info,
805 TypeDescriptor type) {
806
807 Class clazz = type.getClassArg();
808 if (clazz != null && type.getDimensions() == 0 &&
809 (clazz == long.class || clazz == double.class)) {
810
811 stackAdjust -= 2;
812 }
813 else {
814 stackAdjust--;
815 }
816
817 addCode(stackAdjust, opcode, info);
818 }
819
820 // return style instructions
821
822 /**
823 * Generates code that returns void.
824 */
825 public void returnVoid() {
826 addCode(0, Opcode.RETURN);
827 }
828
829 /**
830 * Generates code that returns an object or primitive type. The value to
831 * return must be on the stack.
832 *
833 * <p>The type doesn't need to be an exact match for objects. Object.class
834 * works fine for all objects. For primitive types, use the class that
835 * matches that type. For an int the type is int.class.
836 */
837 public void returnValue(Class type) {
838 int stackAdjust = -1;
839 byte op;
840
841 if (type == int.class ||
842 type == boolean.class ||
843 type == byte.class ||
844 type == short.class ||
845 type == char.class) {
846
847 op = Opcode.IRETURN;
848 }
849 else if (type == long.class) {
850 stackAdjust--;
851 op = Opcode.LRETURN;
852 }
853 else if (type == float.class) {
854 op = Opcode.FRETURN;
855 }
856 else if (type == double.class) {
857 stackAdjust--;
858 op = Opcode.DRETURN;
859 }
860 else if (type == void.class) {
861 stackAdjust++;
862 op = Opcode.RETURN;
863 }
864 else {
865 op = Opcode.ARETURN;
866 }
867
868 addCode(stackAdjust, op);
869 }
870
871 // numerical conversion style instructions
872
873 /**
874 * Generates code that converts the value of a primitive type already
875 * on the stack.
876 */
877 public void convert(Class fromType, Class toType) {
878 int stackAdjust = 0;
879 byte op;
880
881 if (fromType == int.class ||
882 fromType == boolean.class ||
883 fromType == byte.class ||
884 fromType == short.class ||
885 fromType == char.class) {
886
887 if (toType == byte.class) {
888 op = Opcode.I2B;
889 }
890 else if (toType == short.class) {
891 op = Opcode.I2S;
892 }
893 else if (toType == char.class) {
894 op = Opcode.I2C;
895 }
896 else if (toType == float.class) {
897 op = Opcode.I2F;
898 }
899 else if (toType == long.class) {
900 stackAdjust = 1;
901 op = Opcode.I2L;
902 }
903 else if (toType == double.class) {
904 stackAdjust = 1;
905 op = Opcode.I2D;
906 }
907 else if (toType == int.class) {
908 return;
909 }
910 else {
911 throw new RuntimeException("Invalid conversion: int to " +
912 toType);
913 }
914
915 addCode(stackAdjust, op);
916 return;
917 }
918 else if (fromType == long.class) {
919 if (toType == int.class) {
920 stackAdjust = -1;
921 op = Opcode.L2I;
922 }
923 else if (toType == float.class) {
924 stackAdjust = -1;
925 op = Opcode.L2F;
926 }
927 else if (toType == double.class) {
928 op = Opcode.L2D;
929 }
930 else if (toType == byte.class ||
931 toType == char.class ||
932 toType == short.class) {
933
934 convert(fromType, int.class);
935 convert(int.class, toType);
936 return;
937 }
938 else if (toType == long.class) {
939 return;
940 }
941 else {
942 throw new RuntimeException("Invalid conversion: long to " +
943 toType);
944 }
945
946 addCode(stackAdjust, op);
947 return;
948 }
949 else if (fromType == float.class) {
950 if (toType == int.class) {
951 op = Opcode.F2I;
952 }
953 else if (toType == long.class) {
954 stackAdjust = 1;
955 op = Opcode.F2L;
956 }
957 else if (toType == double.class) {
958 stackAdjust = 1;
959 op = Opcode.F2D;
960 }
961 else if (toType == byte.class ||
962 toType == char.class ||
963 toType == short.class) {
964
965 convert(fromType, int.class);
966 convert(int.class, toType);
967 return;
968 }
969 else if (toType == float.class) {
970 return;
971 }
972 else {
973 throw new RuntimeException("Invalid conversion: float to " +
974 toType);
975 }
976
977 addCode(stackAdjust, op);
978 return;
979 }
980 else if (fromType == double.class) {
981 if (toType == int.class) {
982 stackAdjust = -1;
983 op = Opcode.D2I;
984 }
985 else if (toType == float.class) {
986 stackAdjust = -1;
987 op = Opcode.D2F;
988 }
989 else if (toType == long.class) {
990 op = Opcode.D2L;
991 }
992 else if (toType == byte.class ||
993 toType == char.class ||
994 toType == short.class) {
995
996 convert(fromType, int.class);
997 convert(int.class, toType);
998 return;
999 }
1000 else if (toType == double.class) {
1001 return;
1002 }
1003 else {
1004 throw new RuntimeException("Invalid conversion: double to " +
1005 toType);
1006 }
1007
1008 addCode(stackAdjust, op);
1009 return;
1010 }
1011 else {
1012 throw new RuntimeException("Invalid conversion: " + fromType +
1013 " to " + toType);
1014 }
1015 }
1016
1017 // invocation style instructions
1018
1019 /**
1020 * Generates code to invoke a method in any class. If the method is
1021 * non-static, the object reference and the method's argument(s) must be
1022 * on the stack. If the method is static and has any arguments, just
1023 * the method's arguments must be on the stack.
1024 */
1025 public void invoke(Method method) {
1026 TypeDescriptor ret = new TypeDescriptor(method.getReturnType());
1027
1028 Class[] paramClasses = method.getParameterTypes();
1029 TypeDescriptor[] params = new TypeDescriptor[paramClasses.length];
1030 for (int i=0; i<params.length; i++) {
1031 params[i] = new TypeDescriptor(paramClasses[i]);
1032 }
1033
1034 Class clazz = method.getDeclaringClass();
1035
1036 if (Modifier.isStatic(method.getModifiers())) {
1037 invokeStatic(clazz.getName(),
1038 method.getName(),
1039 ret,
1040 params);
1041 }
1042 else if (clazz.isInterface()) {
1043 invokeInterface(clazz.getName(),
1044 method.getName(),
1045 ret,
1046 params);
1047 }
1048 else {
1049 invokeVirtual(clazz.getName(),
1050 method.getName(),
1051 ret,
1052 params);
1053 }
1054 }
1055
1056 /**
1057 * Generates code to invoke a class constructor in any class. The object
1058 * reference and the constructor's argument(s) must be on the stack.
1059 */
1060 public void invoke(Constructor constructor) {
1061 Class[] paramClasses = constructor.getParameterTypes();
1062 TypeDescriptor[] params = new TypeDescriptor[paramClasses.length];
1063 for (int i=0; i<params.length; i++) {
1064 params[i] = new TypeDescriptor(paramClasses[i]);
1065 }
1066
1067 invokeConstructor(constructor.getDeclaringClass().toString(), params);
1068 }
1069
1070 /**
1071 * Generates code to invoke a virtual method in this class. The object
1072 * reference and the method's argument(s) must be on the stack.
1073 *
1074 * @param ret May be null if method returns void.
1075 * @param params May be null if method takes no parameters.
1076 */
1077 public void invokeVirtual(String methodName,
1078 TypeDescriptor ret,
1079 TypeDescriptor[] params) {
1080
1081 ConstantInfo info = mCp.addConstantMethod
1082 (mClassFile.getClassName(), methodName, ret, params);
1083
1084 int stackAdjust = returnSize(ret) - 1;
1085 if (params != null) {
1086 stackAdjust -= argSize(params);
1087 }
1088
1089 addCode(stackAdjust, Opcode.INVOKEVIRTUAL, info);
1090 }
1091
1092 /**
1093 * Generates code to invoke a virtual method in any class. The object
1094 * reference and the method's argument(s) must be on the stack.
1095 *
1096 * @param ret May be null if method returns void.
1097 * @param params May be null if method takes no parameters.
1098 */
1099 public void invokeVirtual(String className,
1100 String methodName,
1101 TypeDescriptor ret,
1102 TypeDescriptor[] params) {
1103 ConstantInfo info =
1104 mCp.addConstantMethod(className, methodName, ret, params);
1105
1106 int stackAdjust = returnSize(ret) - 1;
1107 if (params != null) {
1108 stackAdjust -= argSize(params);
1109 }
1110
1111 addCode(stackAdjust, Opcode.INVOKEVIRTUAL, info);
1112 }
1113
1114 /**
1115 * Generates code to invoke a static method in this class. The method's
1116 * argument(s) must be on the stack.
1117 *
1118 * @param ret May be null if method returns void.
1119 * @param params May be null if method takes no parameters.
1120 */
1121 public void invokeStatic(String methodName,
1122 TypeDescriptor ret,
1123 TypeDescriptor[] params) {
1124 ConstantInfo info = mCp.addConstantMethod
1125 (mClassFile.getClassName(), methodName, ret, params);
1126
1127 int stackAdjust = returnSize(ret) - 0;
1128 if (params != null) {
1129 stackAdjust -= argSize(params);
1130 }
1131
1132 addCode(stackAdjust, Opcode.INVOKESTATIC, info);
1133 }
1134
1135 /**
1136 * Generates code to invoke a static method in any class. The method's
1137 * argument(s) must be on the stack.
1138 *
1139 * @param ret May be null if method returns void.
1140 * @param params May be null if method takes no parameters.
1141 */
1142 public void invokeStatic(String className,
1143 String methodName,
1144 TypeDescriptor ret,
1145 TypeDescriptor[] params) {
1146 ConstantInfo info =
1147 mCp.addConstantMethod(className, methodName, ret, params);
1148
1149 int stackAdjust = returnSize(ret) - 0;
1150 if (params != null) {
1151 stackAdjust -= argSize(params);
1152 }
1153
1154 addCode(stackAdjust, Opcode.INVOKESTATIC, info);
1155 }
1156
1157 /**
1158 * Generates code to invoke an interface method in any class. The object
1159 * reference and the method's argument(s) must be on the stack.
1160 *
1161 * @param ret May be null if method returns void.
1162 * @param params May be null if method takes no parameters.
1163 */
1164 public void invokeInterface(String className,
1165 String methodName,
1166 TypeDescriptor ret,
1167 TypeDescriptor[] params) {
1168
1169 ConstantInfo info =
1170 mCp.addConstantInterfaceMethod(className, methodName, ret, params);
1171
1172 int paramCount = 1;
1173 if (params != null) {
1174 paramCount += argSize(params);
1175 }
1176
1177 int stackAdjust = returnSize(ret) - paramCount;
1178
1179 byte[] bytes = new byte[5];
1180
1181 bytes[0] = Opcode.INVOKEINTERFACE;
1182 bytes[1] = (byte)0;
1183 bytes[2] = (byte)0;
1184 bytes[3] = (byte)paramCount;
1185 bytes[4] = (byte)0;
1186
1187 mInstructions.new ConstantOperandInstruction(stackAdjust, bytes, info);
1188 }
1189
1190 /**
1191 * Generates code to invoke a private method in this class.
1192 * The object reference and the method's argument(s) must be on the stack.
1193 *
1194 * @param ret May be null if method returns void.
1195 * @param params May be null if method takes no parameters.
1196 */
1197 public void invokePrivate(String methodName,
1198 TypeDescriptor ret,
1199 TypeDescriptor[] params) {
1200 ConstantInfo info = mCp.addConstantMethod
1201 (mClassFile.getClassName(), methodName, ret, params);
1202
1203 int stackAdjust = returnSize(ret) - 1;
1204 if (params != null) {
1205 stackAdjust -= argSize(params);
1206 }
1207
1208 addCode(stackAdjust, Opcode.INVOKESPECIAL, info);
1209 }
1210
1211 /**
1212 * Generates code to invoke a method in the super class.
1213 * The object reference and the method's argument(s) must be on the stack.
1214 *
1215 * @param ret May be null if method returns void.
1216 * @param params May be null if method takes no parameters.
1217 */
1218 public void invokeSuper(String superClassName,
1219 String methodName,
1220 TypeDescriptor ret,
1221 TypeDescriptor[] params) {
1222 ConstantInfo info =
1223 mCp.addConstantMethod(superClassName, methodName, ret, params);
1224
1225 int stackAdjust = returnSize(ret) - 1;
1226 if (params != null) {
1227 stackAdjust -= argSize(params);
1228 }
1229
1230 addCode(stackAdjust, Opcode.INVOKESPECIAL, info);
1231 }
1232
1233 /**
1234 * Generates code to invoke a method in the super class.
1235 * The object reference and the method's argument(s) must be on the stack.
1236 */
1237 public void invokeSuper(Method method) {
1238 TypeDescriptor ret = new TypeDescriptor(method.getReturnType());
1239
1240 Class[] paramClasses = method.getParameterTypes();
1241 TypeDescriptor[] params = new TypeDescriptor[paramClasses.length];
1242 for (int i=0; i<params.length; i++) {
1243 params[i] = new TypeDescriptor(paramClasses[i]);
1244 }
1245
1246 invokeSuper(method.getDeclaringClass().getName(),
1247 method.getName(),
1248 ret,
1249 params);
1250 }
1251
1252 /**
1253 * Generates code to invoke a class constructor in this class. The object
1254 * reference and the constructor's argument(s) must be on the stack.
1255 *
1256 * @param params May be null if constructor takes no parameters.
1257 */
1258 public void invokeConstructor(TypeDescriptor[] params) {
1259 ConstantInfo info =
1260 mCp.addConstantConstructor(mClassFile.getClassName(), params);
1261
1262 int stackAdjust = -1;
1263 if (params != null) {
1264 stackAdjust -= argSize(params);
1265 }
1266
1267 addCode(stackAdjust, Opcode.INVOKESPECIAL, info);
1268 }
1269
1270 /**
1271 * Generates code to invoke a class constructor in any class. The object
1272 * reference and the constructor's argument(s) must be on the stack.
1273 *
1274 * @param params May be null if constructor takes no parameters.
1275 */
1276 public void invokeConstructor(String className, TypeDescriptor[] params) {
1277 ConstantInfo info = mCp.addConstantConstructor(className, params);
1278
1279 int stackAdjust = -1;
1280 if (params != null) {
1281 stackAdjust -= argSize(params);
1282 }
1283
1284 addCode(stackAdjust, Opcode.INVOKESPECIAL, info);
1285 }
1286
1287 /**
1288 * Generates code to invoke a super class constructor. The object
1289 * reference and the constructor's argument(s) must be on the stack.
1290 *
1291 * @param params May be null if constructor takes no parameters.
1292 */
1293 public void invokeSuperConstructor(TypeDescriptor[] params) {
1294 invokeConstructor(mClassFile.getSuperClassName(), params);
1295 }
1296
1297 /**
1298 * Generates code to invoke a super class constructor. The object
1299 * reference and the constructor's argument(s) must be on the stack.
1300 */
1301 public void invokeSuper(Constructor constructor) {
1302 Class[] paramClasses = constructor.getParameterTypes();
1303 TypeDescriptor[] params = new TypeDescriptor[paramClasses.length];
1304 for (int i=0; i<params.length; i++) {
1305 params[i] = new TypeDescriptor(paramClasses[i]);
1306 }
1307
1308 invokeSuperConstructor(params);
1309 }
1310
1311 private int returnSize(TypeDescriptor ret) {
1312 if (ret == null) return 0;
1313
1314 String className = ret.getClassName();
1315
1316 if (className.equals(void.class.getName())) {
1317 return 0;
1318 }
1319 else if (className.equals(long.class.getName()) ||
1320 className.equals(double.class.getName())) {
1321 return 2;
1322 }
1323 else {
1324 return 1;
1325 }
1326 }
1327
1328 private int argSize(TypeDescriptor[] params) {
1329 int size = 0;
1330 if (params != null) {
1331 for (int i=0; i<params.length; i++) {
1332 String className = params[i].getClassName();
1333 if (params[i].getDimensions() == 0 &&
1334 (className.equals(long.class.getName()) ||
1335 className.equals(double.class.getName()))) {
1336 size += 2;
1337 }
1338 else {
1339 size++;
1340 }
1341 }
1342 }
1343
1344 return size;
1345 }
1346
1347 // creation style instructions
1348
1349 /**
1350 * Generates code to create a new object. Unless the new object is an
1351 * array, it is invalid until a constructor method is invoked on it.
1352 * When creating arrays, the size for each dimension must be on the
1353 * operand stack.
1354 *
1355 * @see #invokeConstructor
1356 */
1357 public void newObject(TypeDescriptor type) {
1358 int dim = type.getSpecifiedDimensions();
1359
1360 if (dim == 0) {
1361 ConstantInfo info = mCp.addConstantClass(type);
1362 addCode(1, Opcode.NEW, info);
1363 return;
1364 }
1365
1366 TypeDescriptor componentType = type.getComponentType();
1367
1368 if (dim == 1) {
1369 if (componentType.getDimensions() == 0) {
1370 Class clazz = componentType.getClassArg();
1371
1372 if (clazz != null && clazz.isPrimitive()) {
1373 byte atype = (byte)0;
1374
1375 if (clazz == int.class) {
1376 atype = (byte)10;
1377 }
1378 else if (clazz == byte.class) {
1379 atype = (byte)8;
1380 }
1381 else if (clazz == boolean.class) {
1382 atype = (byte)4;
1383 }
1384 else if (clazz == char.class) {
1385 atype = (byte)5;
1386 }
1387 else if (clazz == float.class) {
1388 atype = (byte)6;
1389 }
1390 else if (clazz == double.class) {
1391 atype = (byte)7;
1392 }
1393 else if (clazz == short.class) {
1394 atype = (byte)9;
1395 }
1396 else if (clazz == long.class) {
1397 atype = (byte)11;
1398 }
1399
1400 addCode(0, Opcode.NEWARRAY, atype);
1401 return;
1402 }
1403 }
1404
1405 ConstantInfo info = mCp.addConstantClass(componentType);
1406 addCode(0, Opcode.ANEWARRAY, info);
1407 return;
1408 }
1409
1410 // multidimensional
1411 int stackAdjust = -(dim - 1);
1412
1413 ConstantInfo info = mCp.addConstantClass(componentType);
1414
1415 byte[] bytes = new byte[4];
1416
1417 bytes[0] = Opcode.MULTIANEWARRAY;
1418 bytes[1] = (byte)0;
1419 bytes[2] = (byte)0;
1420 bytes[3] = (byte)dim;
1421
1422 mInstructions.new ConstantOperandInstruction(stackAdjust, bytes, info);
1423 }
1424
1425 // stack operation style instructions
1426
1427 /**
1428 * Generates code for the dup instruction.
1429 */
1430 public void dup() {
1431 addCode(1, Opcode.DUP);
1432 }
1433
1434 /**
1435 * Generates code for the dup_x1 instruction.
1436 */
1437 public void dupX1() {
1438 addCode(1, Opcode.DUP_X1);
1439 }
1440
1441 /**
1442 * Generates code for the dup_x2 instruction.
1443 */
1444 public void dupX2() {
1445 addCode(1, Opcode.DUP_X2);
1446 }
1447
1448 /**
1449 * Generates code for the dup2 instruction.
1450 */
1451 public void dup2() {
1452 addCode(2, Opcode.DUP2);
1453 }
1454
1455 /**
1456 * Generates code for the dup2_x1 instruction.
1457 */
1458 public void dup2X1() {
1459 addCode(2, Opcode.DUP2_X1);
1460 }
1461
1462 /**
1463 * Generates code for the dup2_x2 instruction.
1464 */
1465 public void dup2X2() {
1466 addCode(2, Opcode.DUP2_X2);
1467 }
1468
1469 /**
1470 * Generates code for the pop instruction.
1471 */
1472 public void pop() {
1473 addCode(-1, Opcode.POP);
1474 }
1475
1476 /**
1477 * Generates code for the pop2 instruction.
1478 */
1479 public void pop2() {
1480 addCode(-2, Opcode.POP2);
1481 }
1482
1483 /**
1484 * Generates code for the swap instruction.
1485 */
1486 public void swap() {
1487 addCode(0, Opcode.SWAP);
1488 }
1489
1490 /**
1491 * Generates code for a swap2 instruction.
1492 */
1493 public void swap2() {
1494 dup2X2();
1495 pop2();
1496 }
1497
1498 // flow control instructions
1499
1500 private void branch(int stackAdjust, Location location, byte opcode) {
1501 mInstructions.new BranchInstruction(stackAdjust, opcode, location);
1502 }
1503
1504 /**
1505 * Generates code that performs an unconditional branch to the specified
1506 * location or label.
1507 *
1508 * @param location The location or label to branch to
1509 */
1510 public void branch(Location location) {
1511 branch(0, location, Opcode.GOTO);
1512 }
1513
1514 /**
1515 * Generates code that performs a conditional branch based on the
1516 * value of an object on the stack. A branch is performed based on whether
1517 * the object reference on the stack is null or not.
1518 *
1519 * @param location The location or label to branch to
1520 * @param choice If true, do branch when null, else branch when not null
1521 */
1522 public void ifNullBranch(Location location, boolean choice) {
1523 branch(-1, location, choice ? Opcode.IFNULL : Opcode.IFNONNULL);
1524 }
1525
1526
1527 /**
1528 * Generates code that performs a conditional branch based on the value of
1529 * two object references on the stack. A branch is performed based on
1530 * whether the two objects are equal.
1531 *
1532 * @param location The location or label to branch to
1533 * @param choice If true, branch when equal, else branch when not equal
1534 */
1535 public void ifEqualBranch(Location location, boolean choice) {
1536 branch(-2, location, choice ? Opcode.IF_ACMPEQ : Opcode.IF_ACMPNE);
1537 }
1538
1539 /**
1540 * Generates code the performs a conditional branch based on a comparison
1541 * between an int value on the stack and zero. The int value on the
1542 * stack is on the left side of the comparison expression.
1543 *
1544 * @param location The location or label to branch to
1545 * @param choice One of "==", "!=", "<", ">=", ">" or "<="
1546 * @exception IllegalArgumentException When the choice is not valid
1547 */
1548 public void ifZeroComparisonBranch(Location location, String choice)
1549 throws IllegalArgumentException {
1550
1551 choice = choice.intern();
1552
1553 byte opcode;
1554 if (choice == "==") {
1555 opcode = Opcode.IFEQ;
1556 }
1557 else if (choice == "!=") {
1558 opcode = Opcode.IFNE;
1559 }
1560 else if (choice == "<") {
1561 opcode = Opcode.IFLT;
1562 }
1563 else if (choice == ">=") {
1564 opcode = Opcode.IFGE;
1565 }
1566 else if (choice == ">") {
1567 opcode = Opcode.IFGT;
1568 }
1569 else if (choice == "<=") {
1570 opcode = Opcode.IFLE;
1571 }
1572 else {
1573 throw new IllegalArgumentException
1574 ("Invalid comparision choice: " + choice);
1575 }
1576
1577 branch(-1, location, opcode);
1578 }
1579
1580 /**
1581 * Generates code the performs a conditional branch based on a comparison
1582 * between two int values on the stack. The first int value on the stack
1583 * is on the left side of the comparison expression.
1584 *
1585 * @param location The location or label to branch to
1586 * @param choice One of "==", "!=", "<", ">=", ">" or "<="
1587 * @exception IllegalArgumentException When the choice is not valid
1588 */
1589 public void ifComparisonBranch(Location location, String choice)
1590 throws IllegalArgumentException {
1591
1592 choice = choice.intern();
1593
1594 byte opcode;
1595 if (choice == "==") {
1596 opcode = Opcode.IF_ICMPEQ;
1597 }
1598 else if (choice == "!=") {
1599 opcode = Opcode.IF_ICMPNE;
1600 }
1601 else if (choice == "<") {
1602 opcode = Opcode.IF_ICMPLT;
1603 }
1604 else if (choice == ">=") {
1605 opcode = Opcode.IF_ICMPGE;
1606 }
1607 else if (choice == ">") {
1608 opcode = Opcode.IF_ICMPGT;
1609 }
1610 else if (choice == "<=") {
1611 opcode = Opcode.IF_ICMPLE;
1612 }
1613 else {
1614 throw new IllegalArgumentException
1615 ("Invalid comparision choice: " + choice);
1616 }
1617
1618 branch(-2, location, opcode);
1619 }
1620
1621 /**
1622 * Generates code for a switch statement. The generated code is either a
1623 * lookupswitch or tableswitch. The choice of which switch type to generate
1624 * is made based on the amount of bytes to be generated. A tableswitch
1625 * is usually smaller, unless the cases are sparse.
1626 *
1627 * <p>The key value to switch on must already be on the stack when this
1628 * instruction executes.
1629 *
1630 * @param cases The values to match on. The array length must be the same
1631 * as for locations.
1632 * @param locations The locations or labels to branch to for each case.
1633 * The array length must be the same as for cases.
1634 * @param defaultLocation The location or label to branch to if the key on
1635 * the stack was not matched.
1636 */
1637 public void switchBranch(int[] cases,
1638 Location[] locations, Location defaultLocation) {
1639
1640 mInstructions.new SwitchInstruction(cases, locations, defaultLocation);
1641 }
1642
1643 /**
1644 * Generates code that performs a subroutine branch to the specified
1645 * location. The instruction generated is either jsr or jsr_w. It is most
1646 * often used for implementing a finally block.
1647 *
1648 * @param location The location or label to branch to
1649 */
1650 public void jsr(Location location) {
1651 // Adjust the stack by one to make room for the return address.
1652 branch(1, location, Opcode.JSR);
1653 }
1654
1655 /**
1656 * Generates code that returns from a subroutine invoked by jsr.
1657 *
1658 * @param local The local variable reference that contains the return
1659 * address. The local variable must be of an object type.
1660 */
1661 public void ret(LocalVariable local) {
1662 if (local == null) {
1663 throw new NullPointerException("No local variable specified");
1664 }
1665
1666 mInstructions.new RetInstruction(local);
1667 }
1668
1669 // math instructions
1670
1671 /**
1672 * Generates code for either a unary or binary math operation on one
1673 * or two values pushed on the stack.
1674 *
1675 * <p>Pass in an opcode from the the Opcode class. The only valid math
1676 * opcodes are:
1677 *
1678 * <pre>
1679 * IADD, ISUB, IMUL, IDIV, IREM, INEG, IAND, IOR, IXOR, ISHL, ISHR, IUSHR
1680 * LADD, LSUB, LMUL, LDIV, LREM, LNEG, LAND, LOR, LXOR, LSHL, LSHR, LUSHR
1681 * FADD, FSUB, FMUL, FDIV, FREM, FNEG
1682 * DADD, DSUB, DMUL, DDIV, DREM, DNEG
1683 *
1684 * LCMP
1685 * FCMPG, FCMPL
1686 * DCMPG, DCMPL
1687 * </pre>
1688 *
1689 * A not operation (~) is performed by doing a loadConstant with either
1690 * -1 or -1L followed by math(Opcode.IXOR) or math(Opcode.LXOR).
1691 *
1692 * @param opcode An opcode from the Opcode class.
1693 * @exception IllegalArgumentException When the opcode selected is not
1694 * a math operation.
1695 * @see Opcode
1696 */
1697 public void math(byte opcode) {
1698 int stackAdjust;
1699
1700 switch(opcode) {
1701 case Opcode.INEG:
1702 case Opcode.LNEG:
1703 case Opcode.FNEG:
1704 case Opcode.DNEG:
1705 stackAdjust = 0;
1706 break;
1707 case Opcode.IADD:
1708 case Opcode.ISUB:
1709 case Opcode.IMUL:
1710 case Opcode.IDIV:
1711 case Opcode.IREM:
1712 case Opcode.IAND:
1713 case Opcode.IOR:
1714 case Opcode.IXOR:
1715 case Opcode.ISHL:
1716 case Opcode.ISHR:
1717 case Opcode.IUSHR:
1718 case Opcode.FADD:
1719 case Opcode.FSUB:
1720 case Opcode.FMUL:
1721 case Opcode.FDIV:
1722 case Opcode.FREM:
1723 case Opcode.FCMPG:
1724 case Opcode.FCMPL:
1725 stackAdjust = -1;
1726 break;
1727 case Opcode.LADD:
1728 case Opcode.LSUB:
1729 case Opcode.LMUL:
1730 case Opcode.LDIV:
1731 case Opcode.LREM:
1732 case Opcode.LAND:
1733 case Opcode.LOR:
1734 case Opcode.LXOR:
1735 case Opcode.LSHL:
1736 case Opcode.LSHR:
1737 case Opcode.LUSHR:
1738 case Opcode.DADD:
1739 case Opcode.DSUB:
1740 case Opcode.DMUL:
1741 case Opcode.DDIV:
1742 case Opcode.DREM:
1743 stackAdjust = -2;
1744 break;
1745 case Opcode.LCMP:
1746 case Opcode.DCMPG:
1747 case Opcode.DCMPL:
1748 stackAdjust = -3;
1749 break;
1750 default:
1751 throw new IllegalArgumentException
1752 ("Not a math opcode: " + Opcode.getMnemonic(opcode));
1753 }
1754
1755 addCode(stackAdjust, opcode);
1756 }
1757
1758 // miscellaneous instructions
1759
1760 /**
1761 * Generates code for an arraylength instruction. The object to get the
1762 * length from must already be on the stack.
1763 */
1764 public void arrayLength() {
1765 addCode(0, Opcode.ARRAYLENGTH);
1766 }
1767
1768 /**
1769 * Generates code that throws an exception. The object to throw must
1770 * already be on the stack.
1771 */
1772 public void throwObject() {
1773 addCode(-1, Opcode.ATHROW);
1774 }
1775
1776 /**
1777 * Generates code that performs an object cast operation. The object
1778 * to check must already be on the stack.
1779 */
1780 public void checkCast(TypeDescriptor type) {
1781 ConstantInfo info = mCp.addConstantClass(type);
1782 addCode(0, Opcode.CHECKCAST, info);
1783 }
1784
1785 /**
1786 * Generates code that performs an instanceof operation. The object to
1787 * check must already be on the stack.
1788 */
1789 public void instanceOf(TypeDescriptor type) {
1790 ConstantInfo info = mCp.addConstantClass(type);
1791 addCode(0, Opcode.INSTANCEOF, info);
1792 }
1793
1794 /**
1795 * Generates code that increments a local integer variable by a signed
1796 * constant amount.
1797 */
1798 public void integerIncrement(LocalVariable local, int amount) {
1799 if (local == null) {
1800 throw new NullPointerException("No local variable specified");
1801 }
1802
1803 if (-32768 <= amount && amount <= 32767) {
1804 mInstructions.new ShortIncrementInstruction(local, (short)amount);
1805 }
1806 else {
1807 // Amount can't possibly fit in a 16-bit value, so use regular
1808 // instructions instead.
1809
1810 loadLocal(local);
1811 loadConstant(amount);
1812 math(Opcode.IADD);
1813 storeLocal(local);
1814 }
1815 }
1816
1817 /**
1818 * Generates code to enter the monitor on an object loaded on the stack.
1819 */
1820 public void monitorEnter() {
1821 addCode(-1, Opcode.MONITORENTER);
1822 }
1823
1824 /**
1825 * Generates code to exit the monitor on an object loaded on the stack.
1826 */
1827 public void monitorExit() {
1828 addCode(-1, Opcode.MONITOREXIT);
1829 }
1830
1831 /**
1832 * Generates an instruction that does nothing. (No-OPeration)
1833 */
1834 public void nop() {
1835 addCode(0, Opcode.NOP);
1836 }
1837
1838 /**
1839 * Generates a breakpoint instruction for use in a debugging environment.
1840 */
1841 public void breakpoint() {
1842 addCode(0, Opcode.BREAKPOINT);
1843 }
1844 }