comparison src/com/go/trove/classfile/InstructionList.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.util.*;
56 import java.io.*;
57
58 /******************************************************************************
59 * The InstructionList class is used by the CodeBuilder to perform lower-level
60 * bookkeeping operations and flow analysis.
61 *
62 * @author Brian S O'Neill
63 * @version
64 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
65 * @see CodeBuilder
66 */
67 class InstructionList implements CodeBuffer {
68 private static final boolean DEBUG = false;
69
70 Instruction mFirst;
71 Instruction mLast;
72
73 boolean mResolved = false;
74
75 private List mExceptionHandlers = new ArrayList(4);
76 private List mLocalVariables = new ArrayList();
77
78 private int mMaxStack;
79 private int mMaxLocals;
80
81 private byte[] mByteCodes;
82 private int mBufferLength;
83
84 protected InstructionList() {
85 super();
86 }
87
88 /**
89 * Returns an immutable collection of all the instructions in this
90 * InstructionList.
91 */
92 public Collection getInstructions() {
93 return new AbstractCollection() {
94 public Iterator iterator() {
95 return new Iterator() {
96 private Instruction mNext = mFirst;
97
98 public boolean hasNext() {
99 return mNext != null;
100 }
101
102 public Object next() {
103 if (mNext == null) {
104 throw new NoSuchElementException();
105 }
106
107 Instruction current = mNext;
108 mNext = mNext.mNext;
109 return current;
110 }
111
112 public void remove() {
113 throw new UnsupportedOperationException();
114 }
115 };
116 }
117
118 public int size() {
119 int count = 0;
120 for (Instruction i = mFirst; i != null; i = i.mNext) {
121 count++;
122 }
123 return count;
124 }
125 };
126 }
127
128 public int getMaxStackDepth() {
129 resolve();
130 return mMaxStack;
131 }
132
133 public int getMaxLocals() {
134 resolve();
135 return mMaxLocals;
136 }
137
138 public byte[] getByteCodes() {
139 resolve();
140 return mByteCodes;
141 }
142
143 public ExceptionHandler[] getExceptionHandlers() {
144 resolve();
145
146 ExceptionHandler[] handlers =
147 new ExceptionHandler[mExceptionHandlers.size()];
148 return (ExceptionHandler[])mExceptionHandlers.toArray(handlers);
149 }
150
151 public void addExceptionHandler(ExceptionHandler handler) {
152 mExceptionHandlers.add(handler);
153 }
154
155 public LocalVariable createLocalVariable(String name,
156 TypeDescriptor type) {
157 LocalVariable var = new LocalVariableImpl(name, type, -1);
158 mLocalVariables.add(var);
159 return var;
160 }
161
162 public LocalVariable createLocalParameter(String name,
163 TypeDescriptor type,
164 int number) {
165 LocalVariableImpl var = new LocalVariableImpl(name, type, number);
166 mLocalVariables.add(var);
167 if (mFirst == null) {
168 // Make sure there is an initial instruction. Create a pseudo one.
169 LabelInstruction label = new LabelInstruction();
170 label.setLocation();
171 mFirst = label;
172 }
173
174 // Parameters are initialized first, so ensure flow analysis starts
175 // at the beginning.
176 var.addStoreInstruction(mFirst);
177
178 return var;
179 }
180
181 private void resolve() {
182 if (mResolved) {
183 return;
184 }
185
186 if (!DEBUG) {
187 resolve0();
188 }
189 else {
190 try {
191 resolve0();
192 }
193 finally {
194 System.out.println("-- Instructions --");
195
196 Iterator it = getInstructions().iterator();
197 while (it.hasNext()) {
198 System.out.println(it.next());
199 }
200 }
201 }
202 }
203
204 private void resolve0() {
205 mMaxStack = 0;
206 mMaxLocals = 0;
207
208 Instruction instr;
209
210 // Sweep through the instructions, marking them as not being
211 // visted by flow analysis and set fake locations.
212 int instrCount = 0;
213 for (instr = mFirst; instr != null; instr = instr.mNext) {
214 instr.mStackDepth = -1;
215 instr.mLocation = instrCount++;
216 }
217
218 // Assign variable numbers using the simplest technique.
219
220 int size = mLocalVariables.size();
221 List activeLocationBits = new ArrayList(size);
222 for (int i=0; i<size; i++) {
223 LocalVariableImpl var = (LocalVariableImpl)mLocalVariables.get(i);
224 if (var.getNumber() < 0) {
225 var.setNumber(mMaxLocals);
226 }
227
228 int max = var.getNumber() + (var.isDoubleWord() ? 2 : 1);
229 if (max > mMaxLocals) {
230 mMaxLocals = max;
231 }
232 }
233
234 /*
235 // Perform variable flow analysis for each local variable, in order to
236 // determine which register it should be assigned.
237
238 int size = mLocalVariables.size();
239 List activeLocationBits = new ArrayList(size);
240 for (int i=0; i<size; i++) {
241 LocalVariableImpl var = (LocalVariableImpl)mLocalVariables.get(i);
242 BitList activeLocations = new BitList(instrCount);
243 activeLocationBits.add(activeLocations);
244
245 // Start flow analysis at store variable instructions.
246 Iterator it = var.iterateStoreInstructions();
247 while (it.hasNext()) {
248 Instruction enter = (Instruction)it.next();
249 variableResolve(var, enter, activeLocations,
250 new BitList(instrCount), false);
251 }
252
253 // Continue flow analysis into all exception handlers that wrap
254 // the active locations.
255 boolean passAgain;
256 do {
257 passAgain = false;
258 it = mExceptionHandlers.iterator();
259 while (it.hasNext()) {
260 ExceptionHandler handler = (ExceptionHandler)it.next();
261 if (!isIntersecting(activeLocations, handler)) {
262 continue;
263 }
264 Instruction enter =
265 (Instruction)handler.getCatchLocation();
266 passAgain =
267 variableResolve(var, enter, activeLocations,
268 new BitList(instrCount), false);
269 passAgain = false;
270 }
271 } while (passAgain);
272
273 if (!var.isFixedNumber()) {
274 var.setNumber(-1);
275
276 // Assign variable number by checking first if it can be shared
277 // with another variable of the same size.
278
279 boolean[] conflicts = new boolean[mMaxLocals];
280
281 for (int j=0; j<i; j++) {
282 LocalVariable av = (LocalVariable)mLocalVariables.get(j);
283 if (av.getNumber() < 0 ||
284 av.isDoubleWord() != var.isDoubleWord()) {
285 continue;
286 }
287 BitList alocs = (BitList)activeLocationBits.get(j);
288 if (isIntersecting(alocs, activeLocations)) {
289 conflicts[av.getNumber()] = true;
290 if (av.getNumber() == var.getNumber()) {
291 var.setNumber(-1);
292 }
293 }
294 else if (conflicts[av.getNumber()]) {
295 var.setNumber(-1);
296 }
297 else {
298 var.setNumber(av.getNumber());
299 }
300 }
301
302 if (var.getNumber() < 0) {
303 var.setNumber(mMaxLocals);
304 }
305 }
306
307 int max = var.getNumber() + (var.isDoubleWord() ? 2 : 1);
308 if (max > mMaxLocals) {
309 mMaxLocals = max;
310 }
311
312 // TODO
313 //var.setLocations(activeLocations);
314 }
315
316 activeLocationBits = null;
317 */
318
319 // Perform stack flow analysis to determine the max stack size.
320
321 // Start the flow analysis at the first instruction.
322 Map subAdjustMap = new HashMap(11);
323 stackResolve(0, mFirst, subAdjustMap);
324
325 // Continue flow analysis into exception handler entry points.
326 Iterator it = mExceptionHandlers.iterator();
327 while (it.hasNext()) {
328 ExceptionHandler handler = (ExceptionHandler)it.next();
329 Instruction enter = (Instruction)handler.getCatchLocation();
330 stackResolve(1, enter, subAdjustMap);
331 }
332
333 // Okay, build up the byte code and set real instruction locations.
334 // Multiple passes may be required because instructions may adjust
335 // their size as locations are set. Changing size affects the
336 // locations of other instructions, so that is why additional passes
337 // are required.
338
339 boolean passAgain;
340 do {
341 passAgain = false;
342
343 mByteCodes = new byte[16];
344 mBufferLength = 0;
345
346 for (instr = mFirst; instr != null; instr = instr.mNext) {
347 if (!instr.isResolved()) {
348 passAgain = true;
349 }
350
351 if (instr instanceof Label) {
352 if (instr.mLocation != mBufferLength) {
353 if (instr.mLocation >= 0) {
354 // If the location of this label is not where it
355 // should be, (most likely because an instruction
356 // needed to expand in size) then do another pass.
357 passAgain = true;
358 }
359
360 instr.mLocation = mBufferLength;
361 }
362 }
363 else {
364 instr.mLocation = mBufferLength;
365
366 byte[] bytes = instr.getBytes();
367 if (bytes != null) {
368 if (passAgain) {
369 // If there is going to be another pass, don't
370 // bother collecting bytes into the array. Just
371 // expand the the length variable.
372 mBufferLength += bytes.length;
373 }
374 else {
375 addBytes(bytes);
376 }
377 }
378 }
379 }
380 } while (passAgain); // do {} while ();
381
382 if (mBufferLength != mByteCodes.length) {
383 byte[] newBytes = new byte[mBufferLength];
384 System.arraycopy(mByteCodes, 0, newBytes, 0, mBufferLength);
385 mByteCodes = newBytes;
386 }
387
388 // Set resolved at end because during resolution, this field gets
389 // set false again while changes are being made to the list
390 // of instructions.
391 mResolved = true;
392 }
393
394 private void addBytes(byte[] code) {
395 growBuffer(code.length);
396 System.arraycopy(code, 0, mByteCodes, mBufferLength, code.length);
397 mBufferLength += code.length;
398 }
399
400 private void growBuffer(int amount) {
401 if ((mBufferLength + amount) > mByteCodes.length) {
402 int newCapacity = mByteCodes.length * 2;
403 if ((mBufferLength + amount) > newCapacity) {
404 newCapacity = mBufferLength + amount;
405 }
406
407 byte[] newBuffer = new byte[newCapacity];
408 System.arraycopy(mByteCodes, 0, newBuffer, 0, mBufferLength);
409 mByteCodes = newBuffer;
410 }
411 }
412
413 /*
414 private boolean variableResolve(LocalVariableImpl var,
415 Instruction instr,
416 BitList activeLocations,
417 BitList possibleLocations,
418 boolean fork) {
419 while (instr != null) {
420 int instrLoc = instr.getLocation();
421
422 if (activeLocations.get(instrLoc)) {
423 activeLocations.or(possibleLocations);
424 if (possibleLocations.isAllClear()) {
425 return false;
426 }
427 else {
428 possibleLocations.clear();
429 return true;
430 }
431 }
432
433 if (possibleLocations.get(instrLoc)) {
434 return false;
435 }
436
437 possibleLocations.set(instrLoc);
438
439 if (instr instanceof LocalOperandInstruction &&
440 ((LocalOperandInstruction)instr).getLocalVariable() == var) {
441
442 if (instr instanceof StoreLocalInstruction) {
443 activeLocations.set(instrLoc);
444 if (fork) {
445 possibleLocations.clear(instrLoc);
446 }
447 else {
448 possibleLocations.clear();
449 }
450 }
451 else {
452 activeLocations.or(possibleLocations);
453 possibleLocations.clear();
454 }
455 }
456
457 // Determine the next instruction to flow down to.
458 Instruction next = null;
459
460 if (instr.isFlowThrough()) {
461 if ((next = instr.mNext) == null) {
462 throw new RuntimeException
463 ("Execution flows through end of method");
464 }
465 }
466
467 Location[] targets = instr.getBranchTargets();
468 if (targets != null) {
469 for (int i=0; i<targets.length; i++) {
470 LabelInstruction targetInstr =
471 (LabelInstruction)targets[i];
472
473 if (i == 0 && next == null) {
474 // Flow to the first target if instruction doesn't
475 // flow to its next instruction.
476 next = targetInstr;
477 continue;
478 }
479
480 variableResolve
481 (var, targetInstr, activeLocations,
482 possibleLocations, true);
483 }
484 }
485
486 instr = next;
487 }
488
489 return true;
490 }
491
492 private boolean isIntersecting(BitList a, BitList b) {
493 a = (BitList)a.clone();
494 a.and(b);
495 return !a.isAllClear();
496 }
497
498 private boolean isIntersecting(BitList bits, LocationRange range) {
499 // TODO: how efficient is this?
500 int start = range.getStartLocation().getLocation();
501 int end = range.getEndLocation().getLocation();
502
503 for (int i=start; i<end; i++) {
504 if (bits.get(i)) {
505 return true;
506 }
507 }
508
509 return false;
510 }
511 */
512
513 private int stackResolve(int stackDepth,
514 Instruction instr,
515 Map subAdjustMap) {
516 while (instr != null) {
517 // Set the stack depth, marking this instruction as being visited.
518 // If already visited, break out of this flow.
519 if (instr.mStackDepth < 0) {
520 instr.mStackDepth = stackDepth;
521 }
522 else {
523 if (instr.mStackDepth != stackDepth) {
524 throw new RuntimeException
525 ("Stack depth different at previously visited " +
526 "instruction: " + instr.mStackDepth +
527 " != " + stackDepth);
528 }
529
530 break;
531 }
532
533 // Determine the next instruction to flow down to.
534 Instruction next = null;
535
536 if (instr.isFlowThrough()) {
537 if ((next = instr.mNext) == null) {
538 throw new RuntimeException
539 ("Execution flows through end of method");
540 }
541 }
542
543 stackDepth += instr.getStackAdjustment();
544 if (stackDepth > mMaxStack) {
545 mMaxStack = stackDepth;
546 }
547 else if (stackDepth < 0) {
548 throw new RuntimeException("Stack depth is negative: " +
549 stackDepth);
550 }
551
552 Location[] targets = instr.getBranchTargets();
553 if (targets != null) {
554 for (int i=0; i<targets.length; i++) {
555 LabelInstruction targetInstr =
556 (LabelInstruction)targets[i];
557
558 if (i == 0 && next == null) {
559 // Flow to the first target if instruction doesn't
560 // flow to its next instruction.
561 next = targetInstr;
562 continue;
563 }
564
565 if (!instr.isSubroutineCall()) {
566 stackResolve
567 (stackDepth, targetInstr, subAdjustMap);
568 }
569 else {
570 Integer subAdjust =
571 (Integer)subAdjustMap.get(targetInstr);
572
573 if (subAdjust == null) {
574 int newDepth =
575 stackResolve(stackDepth, targetInstr,
576 subAdjustMap);
577 subAdjust = new Integer(newDepth - stackDepth);
578 subAdjustMap.put(targetInstr, subAdjust);
579 }
580
581 stackDepth += subAdjust.intValue();
582 }
583 }
584 }
585
586 instr = next;
587 }
588
589 return stackDepth;
590 }
591
592 private class LocalVariableImpl implements LocalVariable {
593 private String mName;
594 private TypeDescriptor mType;
595 private boolean mIsDoubleWord;
596
597 private int mNumber;
598 private boolean mFixed;
599
600 private List mStoreInstructions;
601 private SortedSet mLocationRangeSet;
602
603 public LocalVariableImpl(String name, TypeDescriptor type,
604 int number) {
605 mName = name;
606 mType = type;
607 Class clazz = type.getClassArg();
608 mIsDoubleWord = clazz == long.class || clazz == double.class;
609 mNumber = number;
610 if (number >= 0) {
611 mFixed = true;
612 }
613 mStoreInstructions = new ArrayList();
614 }
615
616 /**
617 * May return null if this LocalVariable is unnamed.
618 */
619 public String getName() {
620 return mName;
621 }
622
623 public void setName(String name) {
624 mName = name;
625 }
626
627 public TypeDescriptor getType() {
628 return mType;
629 }
630
631 public boolean isDoubleWord() {
632 return mIsDoubleWord;
633 }
634
635 public int getNumber() {
636 return mNumber;
637 }
638
639 public void setNumber(int number) {
640 mNumber = number;
641 }
642
643 public SortedSet getLocationRangeSet() {
644 return mLocationRangeSet;
645 }
646
647 public void setLocations(Set locations) {
648 /* TODO
649 List sortedLocations = new ArrayList(locations);
650 Collections.sort(sortedLocations);
651
652 mLocationRangeSet = new TreeSet();
653
654 Iterator it = sortedLocations.iterator();
655 Instruction first = null;
656 Instruction last = null;
657 while (it.hasNext()) {
658 Instruction instr = (Instruction)it.next();
659 if (first == null) {
660 first = last = instr;
661 }
662 else if (last.mNext == instr) {
663 last = instr;
664 }
665 else {
666 if (last.mNext != null) {
667 last = last.mNext;
668 }
669 mLocationRangeSet.add(new LocationRangeImpl(first, last));
670 first = last = instr;
671 }
672 }
673
674 if (first != null && last != null) {
675 if (last.mNext != null) {
676 last = last.mNext;
677 }
678 mLocationRangeSet.add(new LocationRangeImpl(first, last));
679 }
680
681 mLocationRangeSet =
682 Collections.unmodifiableSortedSet(mLocationRangeSet);
683 */
684 }
685
686 public boolean isFixedNumber() {
687 return mFixed;
688 }
689
690 public void addStoreInstruction(Instruction instr) {
691 mStoreInstructions.add(instr);
692 }
693
694 public Iterator iterateStoreInstructions() {
695 return mStoreInstructions.iterator();
696 }
697
698 public String toString() {
699 if (getName() != null) {
700 return String.valueOf(getType()) + ' ' + getName();
701 }
702 else {
703 return String.valueOf(getType());
704 }
705 }
706 }
707
708 /////////////////////////////////////////////////////////////////////////
709 //
710 // Begin inner class definitions for instructions of the InstructionList.
711 //
712 /////////////////////////////////////////////////////////////////////////
713
714 /**************************************************************************
715 * An Instruction is an element in an InstructionList, and represents a
716 * Java byte code instruction.
717 *
718 * @author Brian S O'Neill
719 * @version
720 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
721 */
722 public abstract class Instruction implements Location {
723 private int mStackAdjust;
724
725 Instruction mPrev;
726 Instruction mNext;
727
728 // Indicates what the stack depth is when this instruction is reached.
729 // Is -1 if not reached. Flow analysis sets this value.
730 int mStackDepth = -1;
731
732 // Indicates the address of this instruction is, or -1 if not known.
733 int mLocation = -1;
734
735 /**
736 * Newly created instructions are automatically added to the
737 * InstructionList.
738 */
739 public Instruction(int stackAdjust) {
740 mStackAdjust = stackAdjust;
741 add();
742 }
743
744 /**
745 * This constructor allows sub-classes to disable auto-adding to the
746 * InstructionList.
747 */
748 protected Instruction(int stackAdjust, boolean addInstruction) {
749 mStackAdjust = stackAdjust;
750
751 if (addInstruction) {
752 add();
753 }
754 }
755
756 /**
757 * Add this instruction to the end of the InstructionList. If the
758 * Instruction is already in the list, then it is moved to the end.
759 */
760 protected void add() {
761 InstructionList.this.mResolved = false;
762
763 if (mPrev != null) {
764 mPrev.mNext = mNext;
765 }
766
767 if (mNext != null) {
768 mNext.mPrev = mPrev;
769 }
770
771 mNext = null;
772
773 if (InstructionList.this.mFirst == null) {
774 mPrev = null;
775 InstructionList.this.mFirst = this;
776 }
777 else {
778 mPrev = InstructionList.this.mLast;
779 InstructionList.this.mLast.mNext = this;
780 }
781
782 InstructionList.this.mLast = this;
783 }
784
785 /**
786 * Insert an Instruction immediately following this one.
787 */
788 public void insert(Instruction instr) {
789 InstructionList.this.mResolved = false;
790
791 instr.mPrev = this;
792 instr.mNext = mNext;
793
794 mNext = instr;
795
796 if (this == InstructionList.this.mLast) {
797 InstructionList.this.mLast = instr;
798 }
799 }
800
801 /**
802 * Removes this Instruction from its parent InstructionList.
803 */
804 public void remove() {
805 InstructionList.this.mResolved = false;
806
807 if (mPrev != null) {
808 mPrev.mNext = mNext;
809 }
810
811 if (mNext != null) {
812 mNext.mPrev = mPrev;
813 }
814
815 if (this == InstructionList.this.mFirst) {
816 InstructionList.this.mFirst = mNext;
817 }
818
819 if (this == InstructionList.this.mLast) {
820 InstructionList.this.mLast = mPrev;
821 }
822
823 mPrev = null;
824 mNext = null;
825 }
826
827 /**
828 * Replace this Instruction with another one.
829 */
830 public void replace(Instruction replacement) {
831 if (replacement == null) {
832 remove();
833 return;
834 }
835
836 InstructionList.this.mResolved = false;
837
838 replacement.mPrev = mPrev;
839 replacement.mNext = mNext;
840
841 if (mPrev != null) {
842 mPrev.mNext = replacement;
843 }
844
845 if (mNext != null) {
846 mNext.mPrev = replacement;
847 }
848
849 if (this == InstructionList.this.mFirst) {
850 InstructionList.this.mFirst = replacement;
851 }
852
853 if (this == InstructionList.this.mLast) {
854 InstructionList.this.mLast = replacement;
855 }
856 }
857
858 /**
859 * Returns a positive, negative or zero value indicating what affect
860 * this generated instruction has on the runtime stack.
861 */
862 public int getStackAdjustment() {
863 return mStackAdjust;
864 }
865
866 /**
867 * Returns the stack depth for when this instruction is reached. If the
868 * value is negative, then this instruction is never reached.
869 */
870 public int getStackDepth() {
871 return mStackDepth;
872 }
873
874 /**
875 * Returns the address of this instruction or -1 if not known.
876 */
877 public int getLocation() {
878 return mLocation;
879 }
880
881 /**
882 * Returns all of the targets that this instruction may branch to. Not
883 * all instructions support branching, and null is returned by default.
884 */
885 public Location[] getBranchTargets() {
886 return null;
887 }
888
889 /**
890 * Returns true if execution flow may continue after this instruction.
891 * It may be a goto, a method return, an exception throw or a
892 * subroutine return. Default implementation returns true.
893 */
894 public boolean isFlowThrough() {
895 return true;
896 }
897
898 public boolean isSubroutineCall() {
899 return false;
900 }
901
902 /**
903 * Returns null if this is a pseudo instruction and no bytes are
904 * generated.
905 */
906 public abstract byte[] getBytes();
907
908 /**
909 * An instruction is resolved when it has all information needed to
910 * generate correct byte code.
911 */
912 public abstract boolean isResolved();
913
914 public int compareTo(Object obj) {
915 if (this == obj) {
916 return 0;
917 }
918 Location other = (Location)obj;
919
920 int loca = getLocation();
921 int locb = other.getLocation();
922
923 if (loca < locb) {
924 return -1;
925 }
926 else if (loca > locb) {
927 return 1;
928 }
929 else {
930 return 0;
931 }
932 }
933
934 /**
935 * Returns a string containing the type of this instruction, the stack
936 * adjustment and the list of byte codes. Unvisted instructions are
937 * marked with an asterisk.
938 */
939 public String toString() {
940 String name = getClass().getName();
941 int index = name.lastIndexOf('.');
942 if (index >= 0) {
943 name = name.substring(index + 1);
944 }
945 index = name.lastIndexOf('$');
946 if (index >= 0) {
947 name = name.substring(index + 1);
948 }
949
950 StringBuffer buf = new StringBuffer(name.length() + 20);
951
952 int adjust = getStackAdjustment();
953 int depth = getStackDepth();
954
955 if (depth >= 0) {
956 buf.append(' ');
957 }
958 else {
959 buf.append('*');
960 }
961
962 buf.append('[');
963 buf.append(mLocation);
964 buf.append("] ");
965
966 buf.append(name);
967 buf.append(" (");
968
969 if (depth >= 0) {
970 buf.append(depth);
971 buf.append(" + ");
972 buf.append(adjust);
973 buf.append(" = ");
974 buf.append(depth + adjust);
975 }
976 else {
977 buf.append(adjust);
978 }
979
980 buf.append(") ");
981
982 try {
983 byte[] bytes = getBytes();
984 boolean wide = false;
985 if (bytes != null) {
986 for (int i=0; i<bytes.length; i++) {
987 if (i > 0) {
988 buf.append(',');
989 }
990
991 byte code = bytes[i];
992
993 if (i == 0 || wide) {
994 buf.append(Opcode.getMnemonic(code));
995 wide = code == Opcode.WIDE;
996 }
997 else {
998 buf.append(code & 0xff);
999 }
1000 }
1001 }
1002 }
1003 catch (Exception e) {
1004 }
1005
1006 return buf.toString();
1007 }
1008 }
1009
1010 /**************************************************************************
1011 * Defines a psuedo instruction for a label. No byte code is ever generated
1012 * from a label. Labels are not automatically added to the list.
1013 *
1014 * @author Brian S O'Neill
1015 * @version
1016 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1017 */
1018 public class LabelInstruction extends Instruction implements Label {
1019 public LabelInstruction() {
1020 super(0, false);
1021 }
1022
1023 /**
1024 * Set this label's branch location to be the current address
1025 * in this label's parent CodeBuilder or InstructionList.
1026 *
1027 * @return This Label.
1028 */
1029 public Label setLocation() {
1030 add();
1031 return this;
1032 }
1033
1034 /**
1035 * @return -1 when not resolved yet
1036 */
1037 public int getLocation() throws IllegalStateException {
1038 int loc;
1039 if ((loc = mLocation) < 0) {
1040 if (mPrev == null && mNext == null) {
1041 throw new IllegalStateException
1042 ("Label location is not set");
1043 }
1044 }
1045
1046 return loc;
1047 }
1048
1049 /**
1050 * Always returns null.
1051 */
1052 public byte[] getBytes() {
1053 return null;
1054 }
1055
1056 public boolean isResolved() {
1057 return getLocation() >= 0;
1058 }
1059 }
1060
1061 /**************************************************************************
1062 * Defines a code instruction and has storage for byte codes.
1063 *
1064 * @author Brian S O'Neill
1065 * @version
1066 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1067 */
1068 public class CodeInstruction extends Instruction {
1069 protected byte[] mBytes;
1070
1071 public CodeInstruction(int stackAdjust) {
1072 super(stackAdjust);
1073 }
1074
1075 protected CodeInstruction(int stackAdjust, boolean addInstruction) {
1076 super(stackAdjust, addInstruction);
1077 }
1078
1079 public CodeInstruction(int stackAdjust, byte b) {
1080 super(stackAdjust);
1081 mBytes = new byte[] {b};
1082 }
1083
1084 public CodeInstruction(int stackAdjust, byte[] bytes) {
1085 super(stackAdjust);
1086 mBytes = bytes;
1087 }
1088
1089 public boolean isFlowThrough() {
1090 if (mBytes != null && mBytes.length > 0) {
1091 switch (mBytes[0]) {
1092 case Opcode.GOTO:
1093 case Opcode.GOTO_W:
1094 case Opcode.IRETURN:
1095 case Opcode.LRETURN:
1096 case Opcode.FRETURN:
1097 case Opcode.DRETURN:
1098 case Opcode.ARETURN:
1099 case Opcode.RETURN:
1100 case Opcode.ATHROW:
1101 return false;
1102 }
1103 }
1104
1105 return true;
1106 }
1107
1108 public byte[] getBytes() {
1109 return mBytes;
1110 }
1111
1112 public boolean isResolved() {
1113 return true;
1114 }
1115 }
1116
1117 /**************************************************************************
1118 * Defines a branch instruction, like a goto, jsr or any conditional
1119 * branch.
1120 *
1121 * @author Brian S O'Neill
1122 * @version
1123 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1124 */
1125 public class BranchInstruction extends CodeInstruction {
1126 private Location mTarget;
1127 private boolean mHasShortHop = false;
1128 private boolean mIsSub = false;
1129
1130 public BranchInstruction(int stackAdjust,
1131 byte opcode, Location target) {
1132 this(stackAdjust, true, opcode, target);
1133 }
1134
1135 private BranchInstruction(int stackAdjust, boolean addInstruction,
1136 byte opcode, Location target) {
1137 super(stackAdjust, addInstruction);
1138
1139 mTarget = target;
1140
1141 switch (opcode) {
1142 case Opcode.GOTO_W:
1143 case Opcode.JSR_W:
1144 mIsSub = true;
1145 mBytes = new byte[5];
1146 mBytes[0] = opcode;
1147 break;
1148 case Opcode.JSR:
1149 mIsSub = true;
1150 // Flow through to next case.
1151 case Opcode.GOTO:
1152 case Opcode.IF_ACMPEQ:
1153 case Opcode.IF_ACMPNE:
1154 case Opcode.IF_ICMPEQ:
1155 case Opcode.IF_ICMPNE:
1156 case Opcode.IF_ICMPLT:
1157 case Opcode.IF_ICMPGE:
1158 case Opcode.IF_ICMPGT:
1159 case Opcode.IF_ICMPLE:
1160 case Opcode.IFEQ:
1161 case Opcode.IFNE:
1162 case Opcode.IFLT:
1163 case Opcode.IFGE:
1164 case Opcode.IFGT:
1165 case Opcode.IFLE:
1166 case Opcode.IFNONNULL:
1167 case Opcode.IFNULL:
1168 mBytes = new byte[3];
1169 mBytes[0] = opcode;
1170 break;
1171 default:
1172 throw new IllegalArgumentException
1173 ("Opcode not a branch instruction: " +
1174 Opcode.getMnemonic(opcode));
1175 }
1176 }
1177
1178 public Location[] getBranchTargets() {
1179 return new Location[] {mTarget};
1180 }
1181
1182 public boolean isSubroutineCall() {
1183 return mIsSub;
1184 }
1185
1186 public byte[] getBytes() {
1187 if (!isResolved() || mHasShortHop) {
1188 return mBytes;
1189 }
1190
1191 int offset = mTarget.getLocation() - mLocation;
1192 byte opcode = mBytes[0];
1193
1194 if (opcode == Opcode.GOTO_W || opcode == Opcode.JSR_W) {
1195 mBytes[1] = (byte)(offset >> 24);
1196 mBytes[2] = (byte)(offset >> 16);
1197 mBytes[3] = (byte)(offset >> 8);
1198 mBytes[4] = (byte)(offset >> 0);
1199 }
1200 else if (-32768 <= offset && offset <= 32767) {
1201 mBytes[1] = (byte)(offset >> 8);
1202 mBytes[2] = (byte)(offset >> 0);
1203 }
1204 else if (opcode == Opcode.GOTO || opcode == Opcode.JSR) {
1205 mBytes = new byte[5];
1206 if (opcode == Opcode.GOTO) {
1207 mBytes[0] = Opcode.GOTO_W;
1208 }
1209 else {
1210 mBytes[0] = Opcode.JSR_W;
1211 }
1212 mBytes[1] = (byte)(offset >> 24);
1213 mBytes[2] = (byte)(offset >> 16);
1214 mBytes[3] = (byte)(offset >> 8);
1215 mBytes[4] = (byte)(offset >> 0);
1216 }
1217 else {
1218 // The if branch requires a 32 bit offset.
1219
1220 // Convert:
1221 //
1222 // if <cond> goto target
1223 // // reached if <cond> false
1224 // target: // reached if <cond> true
1225
1226 // to this:
1227 //
1228 // if not <cond> goto shortHop
1229 // goto_w target
1230 // shortHop: // reached if <cond> false
1231 // target: // reached if <cond> true
1232
1233 mHasShortHop = true;
1234
1235 opcode = Opcode.reverseIfOpcode(opcode);
1236
1237 mBytes[0] = opcode;
1238 mBytes[1] = (byte)0;
1239 mBytes[2] = (byte)8;
1240
1241 // insert goto_w instruction after this one.
1242 insert
1243 (new BranchInstruction(0, false, Opcode.GOTO_W, mTarget));
1244 }
1245
1246 return mBytes;
1247 }
1248
1249 public boolean isResolved() {
1250 return mTarget.getLocation() >= 0;
1251 }
1252 }
1253
1254 /**************************************************************************
1255 * Defines an instruction that has a single operand which references a
1256 * constant in the constant pool.
1257 *
1258 * @author Brian S O'Neill
1259 * @version
1260 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1261 */
1262 public class ConstantOperandInstruction extends CodeInstruction {
1263 private ConstantInfo mInfo;
1264
1265 public ConstantOperandInstruction(int stackAdjust,
1266 byte[] bytes,
1267 ConstantInfo info) {
1268 super(stackAdjust, bytes);
1269 mInfo = info;
1270 }
1271
1272 public byte[] getBytes() {
1273 int index = mInfo.getIndex();
1274
1275 if (index < 0) {
1276 throw new RuntimeException("Constant pool index not resolved");
1277 }
1278
1279 mBytes[1] = (byte)(index >> 8);
1280 mBytes[2] = (byte)index;
1281
1282 return mBytes;
1283 }
1284
1285 public boolean isResolved() {
1286 return mInfo.getIndex() >= 0;
1287 }
1288 }
1289
1290 /**************************************************************************
1291 * Defines an instruction that loads a constant onto the stack from the
1292 * constant pool.
1293 *
1294 * @author Brian S O'Neill
1295 * @version
1296 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1297 */
1298 public class LoadConstantInstruction extends CodeInstruction {
1299 private ConstantInfo mInfo;
1300 private boolean mWideOnly;
1301
1302 public LoadConstantInstruction(int stackAdjust,
1303 ConstantInfo info) {
1304 this(stackAdjust, info, false);
1305 }
1306
1307 public LoadConstantInstruction(int stackAdjust,
1308 ConstantInfo info,
1309 boolean wideOnly) {
1310 super(stackAdjust);
1311 mInfo = info;
1312 mWideOnly = wideOnly;
1313 }
1314
1315 public boolean isFlowThrough() {
1316 return true;
1317 }
1318
1319 public byte[] getBytes() {
1320 int index = mInfo.getIndex();
1321
1322 if (index < 0) {
1323 throw new RuntimeException("Constant pool index not resolved");
1324 }
1325
1326 if (mWideOnly) {
1327 byte[] bytes = new byte[3];
1328 bytes[0] = Opcode.LDC2_W;
1329 bytes[1] = (byte)(index >> 8);
1330 bytes[2] = (byte)index;
1331 return bytes;
1332 }
1333 else if (index <= 255) {
1334 byte[] bytes = new byte[2];
1335 bytes[0] = Opcode.LDC;
1336 bytes[1] = (byte)index;
1337 return bytes;
1338 }
1339 else {
1340 byte[] bytes = new byte[3];
1341 bytes[0] = Opcode.LDC_W;
1342 bytes[1] = (byte)(index >> 8);
1343 bytes[2] = (byte)index;
1344 return bytes;
1345 }
1346 }
1347
1348 public boolean isResolved() {
1349 return mInfo.getIndex() >= 0;
1350 }
1351 }
1352
1353 /**************************************************************************
1354 * Defines an instruction that contains an operand for referencing a
1355 * LocalVariable.
1356 *
1357 * @author Brian S O'Neill
1358 * @version
1359 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1360 */
1361 public class LocalOperandInstruction extends CodeInstruction {
1362 protected LocalVariable mLocal;
1363
1364 public LocalOperandInstruction(int stackAdjust,
1365 LocalVariable local) {
1366 super(stackAdjust);
1367 mLocal = local;
1368 }
1369
1370 public boolean isResolved() {
1371 return mLocal.getNumber() >= 0;
1372 }
1373
1374 public LocalVariable getLocalVariable() {
1375 return mLocal;
1376 }
1377
1378 public int getVariableNumber() {
1379 int varNum = mLocal.getNumber();
1380
1381 if (varNum < 0) {
1382 throw new RuntimeException
1383 ("Local variable number not resolved");
1384 }
1385
1386 return varNum;
1387 }
1388 }
1389
1390 /**************************************************************************
1391 * Defines an instruction that loads a local variable onto the stack.
1392 *
1393 * @author Brian S O'Neill
1394 * @version
1395 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1396 */
1397 public class LoadLocalInstruction extends LocalOperandInstruction {
1398 public LoadLocalInstruction(int stackAdjust,
1399 LocalVariable local) {
1400 super(stackAdjust, local);
1401 }
1402
1403 public boolean isFlowThrough() {
1404 return true;
1405 }
1406
1407 public byte[] getBytes() {
1408 int varNum = getVariableNumber();
1409 byte opcode;
1410 boolean writeIndex = false;
1411
1412 TypeDescriptor type = mLocal.getType();
1413 Class clazz;
1414 if (type.getDimensions() > 0) {
1415 clazz = null;
1416 }
1417 else {
1418 clazz = type.getClassArg();
1419 }
1420
1421 switch(varNum) {
1422 case 0:
1423 if (clazz == null || !clazz.isPrimitive()) {
1424 opcode = Opcode.ALOAD_0;
1425 }
1426 else if (clazz == long.class) {
1427 opcode = Opcode.LLOAD_0;
1428 }
1429 else if (clazz == float.class) {
1430 opcode = Opcode.FLOAD_0;
1431 }
1432 else if (clazz == double.class) {
1433 opcode = Opcode.DLOAD_0;
1434 }
1435 else {
1436 opcode = Opcode.ILOAD_0;
1437 }
1438 break;
1439 case 1:
1440 if (clazz == null || !clazz.isPrimitive()) {
1441 opcode = Opcode.ALOAD_1;
1442 }
1443 else if (clazz == long.class) {
1444 opcode = Opcode.LLOAD_1;
1445 }
1446 else if (clazz == float.class) {
1447 opcode = Opcode.FLOAD_1;
1448 }
1449 else if (clazz == double.class) {
1450 opcode = Opcode.DLOAD_1;
1451 }
1452 else {
1453 opcode = Opcode.ILOAD_1;
1454 }
1455 break;
1456 case 2:
1457 if (clazz == null || !clazz.isPrimitive()) {
1458 opcode = Opcode.ALOAD_2;
1459 }
1460 else if (clazz == long.class) {
1461 opcode = Opcode.LLOAD_2;
1462 }
1463 else if (clazz == float.class) {
1464 opcode = Opcode.FLOAD_2;
1465 }
1466 else if (clazz == double.class) {
1467 opcode = Opcode.DLOAD_2;
1468 }
1469 else {
1470 opcode = Opcode.ILOAD_2;
1471 }
1472 break;
1473 case 3:
1474 if (clazz == null || !clazz.isPrimitive()) {
1475 opcode = Opcode.ALOAD_3;
1476 }
1477 else if (clazz == long.class) {
1478 opcode = Opcode.LLOAD_3;
1479 }
1480 else if (clazz == float.class) {
1481 opcode = Opcode.FLOAD_3;
1482 }
1483 else if (clazz == double.class) {
1484 opcode = Opcode.DLOAD_3;
1485 }
1486 else {
1487 opcode = Opcode.ILOAD_3;
1488 }
1489 break;
1490 default:
1491 writeIndex = true;
1492
1493 if (clazz == null || !clazz.isPrimitive()) {
1494 opcode = Opcode.ALOAD;
1495 }
1496 else if (clazz == long.class) {
1497 opcode = Opcode.LLOAD;
1498 }
1499 else if (clazz == float.class) {
1500 opcode = Opcode.FLOAD;
1501 }
1502 else if (clazz == double.class) {
1503 opcode = Opcode.DLOAD;
1504 }
1505 else {
1506 opcode = Opcode.ILOAD;
1507 }
1508 break;
1509 }
1510
1511 if (!writeIndex) {
1512 mBytes = new byte[] { opcode };
1513 }
1514 else {
1515 if (varNum <= 255) {
1516 mBytes = new byte[] { opcode, (byte)varNum };
1517 }
1518 else {
1519 mBytes = new byte[]
1520 {
1521 Opcode.WIDE,
1522 opcode,
1523 (byte)(varNum >> 8),
1524 (byte)varNum
1525 };
1526 }
1527 }
1528
1529 return mBytes;
1530 }
1531 }
1532
1533 /**************************************************************************
1534 * Defines an instruction that stores a value from the stack into a local
1535 * variable.
1536 *
1537 * @author Brian S O'Neill
1538 * @version
1539 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1540 */
1541 public class StoreLocalInstruction extends LocalOperandInstruction {
1542 public StoreLocalInstruction(int stackAdjust,
1543 LocalVariable local) {
1544 super(stackAdjust, local);
1545 ((LocalVariableImpl)local).addStoreInstruction(this);
1546 }
1547
1548 public boolean isFlowThrough() {
1549 return true;
1550 }
1551
1552 public byte[] getBytes() {
1553 int varNum = getVariableNumber();
1554 byte opcode;
1555 boolean writeIndex = false;
1556
1557 TypeDescriptor type = mLocal.getType();
1558 Class clazz;
1559 if (type.getDimensions() > 0) {
1560 clazz = null;
1561 }
1562 else {
1563 clazz = type.getClassArg();
1564 }
1565
1566 switch(varNum) {
1567 case 0:
1568 if (clazz == null || !clazz.isPrimitive()) {
1569 opcode = Opcode.ASTORE_0;
1570 }
1571 else if (clazz == long.class) {
1572 opcode = Opcode.LSTORE_0;
1573 }
1574 else if (clazz == float.class) {
1575 opcode = Opcode.FSTORE_0;
1576 }
1577 else if (clazz == double.class) {
1578 opcode = Opcode.DSTORE_0;
1579 }
1580 else {
1581 opcode = Opcode.ISTORE_0;
1582 }
1583 break;
1584 case 1:
1585 if (clazz == null || !clazz.isPrimitive()) {
1586 opcode = Opcode.ASTORE_1;
1587 }
1588 else if (clazz == long.class) {
1589 opcode = Opcode.LSTORE_1;
1590 }
1591 else if (clazz == float.class) {
1592 opcode = Opcode.FSTORE_1;
1593 }
1594 else if (clazz == double.class) {
1595 opcode = Opcode.DSTORE_1;
1596 }
1597 else {
1598 opcode = Opcode.ISTORE_1;
1599 }
1600 break;
1601 case 2:
1602 if (clazz == null || !clazz.isPrimitive()) {
1603 opcode = Opcode.ASTORE_2;
1604 }
1605 else if (clazz == long.class) {
1606 opcode = Opcode.LSTORE_2;
1607 }
1608 else if (clazz == float.class) {
1609 opcode = Opcode.FSTORE_2;
1610 }
1611 else if (clazz == double.class) {
1612 opcode = Opcode.DSTORE_2;
1613 }
1614 else {
1615 opcode = Opcode.ISTORE_2;
1616 }
1617 break;
1618 case 3:
1619 if (clazz == null || !clazz.isPrimitive()) {
1620 opcode = Opcode.ASTORE_3;
1621 }
1622 else if (clazz == long.class) {
1623 opcode = Opcode.LSTORE_3;
1624 }
1625 else if (clazz == float.class) {
1626 opcode = Opcode.FSTORE_3;
1627 }
1628 else if (clazz == double.class) {
1629 opcode = Opcode.DSTORE_3;
1630 }
1631 else {
1632 opcode = Opcode.ISTORE_3;
1633 }
1634 break;
1635 default:
1636 writeIndex = true;
1637
1638 if (clazz == null || !clazz.isPrimitive()) {
1639 opcode = Opcode.ASTORE;
1640 }
1641 else if (clazz == long.class) {
1642 opcode = Opcode.LSTORE;
1643 }
1644 else if (clazz == float.class) {
1645 opcode = Opcode.FSTORE;
1646 }
1647 else if (clazz == double.class) {
1648 opcode = Opcode.DSTORE;
1649 }
1650 else {
1651 opcode = Opcode.ISTORE;
1652 }
1653 break;
1654 }
1655
1656 if (!writeIndex) {
1657 mBytes = new byte[] { opcode };
1658 }
1659 else {
1660 if (varNum <= 255) {
1661 mBytes = new byte[] { opcode, (byte)varNum };
1662 }
1663 else {
1664 mBytes = new byte[]
1665 {
1666 Opcode.WIDE,
1667 opcode,
1668 (byte)(varNum >> 8),
1669 (byte)varNum
1670 };
1671 }
1672 }
1673
1674 return mBytes;
1675 }
1676 }
1677
1678 /**************************************************************************
1679 * Defines a ret instruction for returning from a jsr call.
1680 *
1681 * @author Brian S O'Neill
1682 * @version
1683 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1684 */
1685 public class RetInstruction extends LocalOperandInstruction {
1686 public RetInstruction(LocalVariable local) {
1687 super(0, local);
1688 }
1689
1690 public boolean isFlowThrough() {
1691 return false;
1692 }
1693
1694 public byte[] getBytes() {
1695 int varNum = getVariableNumber();
1696
1697 if (varNum <= 255) {
1698 mBytes = new byte[] { Opcode.RET, (byte)varNum };
1699 }
1700 else {
1701 mBytes = new byte[]
1702 {
1703 Opcode.WIDE,
1704 Opcode.RET,
1705 (byte)(varNum >> 8),
1706 (byte)varNum
1707 };
1708 }
1709
1710 return mBytes;
1711 }
1712 }
1713
1714 /**************************************************************************
1715 * Defines a specialized instruction that increments a local variable by
1716 * a signed 16-bit amount.
1717 *
1718 * @author Brian S O'Neill
1719 * @version
1720 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1721 */
1722 public class ShortIncrementInstruction extends LocalOperandInstruction {
1723 private short mAmount;
1724
1725 public ShortIncrementInstruction(LocalVariable local, short amount) {
1726 super(0, local);
1727 mAmount = amount;
1728 }
1729
1730 public boolean isFlowThrough() {
1731 return true;
1732 }
1733
1734 public byte[] getBytes() {
1735 int varNum = getVariableNumber();
1736
1737 if ((-128 <= mAmount && mAmount <= 127) && varNum <= 255) {
1738 mBytes = new byte[]
1739 { Opcode.IINC,
1740 (byte)varNum,
1741 (byte)mAmount
1742 };
1743 }
1744 else {
1745 mBytes = new byte[]
1746 {
1747 Opcode.WIDE,
1748 Opcode.IINC,
1749 (byte)(varNum >> 8),
1750 (byte)varNum,
1751 (byte)(mAmount >> 8),
1752 (byte)mAmount
1753 };
1754 }
1755
1756 return mBytes;
1757 }
1758 }
1759
1760 /**************************************************************************
1761 * Defines a switch instruction. The choice of which actual switch
1762 * implementation to use (table or lookup switch) is determined
1763 * automatically based on which generates to the smallest amount of bytes.
1764 *
1765 * @author Brian S O'Neill
1766 * @version
1767 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
1768 */
1769 public class SwitchInstruction extends CodeInstruction {
1770 private int[] mCases;
1771 private Location[] mLocations;
1772 private Location mDefaultLocation;
1773
1774 private byte mOpcode;
1775
1776 private int mSmallest;
1777 private int mLargest;
1778
1779 public SwitchInstruction(int[] casesParam,
1780 Location[] locationsParam,
1781 Location defaultLocation) {
1782 // A SwitchInstruction always adjusts the stack by -1 because it
1783 // pops the switch key off the stack.
1784 super(-1);
1785
1786 if (casesParam.length != locationsParam.length) {
1787 throw new IllegalArgumentException
1788 ("Switch cases and locations sizes differ: " +
1789 casesParam.length + ", " + locationsParam.length);
1790 }
1791
1792 mCases = new int[casesParam.length];
1793 System.arraycopy(casesParam, 0, mCases, 0, casesParam.length);
1794
1795 mLocations = new Location[locationsParam.length];
1796 System.arraycopy(locationsParam, 0, mLocations,
1797 0, locationsParam.length);
1798
1799 mDefaultLocation = defaultLocation;
1800
1801 // First sort the cases and locations.
1802 sort(0, mCases.length - 1);
1803
1804 // Check for duplicate cases.
1805 int lastCase = 0;
1806 for (int i=0; i<mCases.length; i++) {
1807 if (i > 0 && mCases[i] == lastCase) {
1808 throw new RuntimeException("Duplicate switch cases: " +
1809 lastCase);
1810 }
1811 lastCase = mCases[i];
1812 }
1813
1814 // Now determine which kind of switch to use.
1815
1816 mSmallest = mCases[0];
1817 mLargest = mCases[mCases.length - 1];
1818 int tSize = 12 + 4 * (mLargest - mSmallest + 1);
1819
1820 int lSize = 8 + 8 * mCases.length;
1821
1822 if (tSize <= lSize) {
1823 mOpcode = Opcode.TABLESWITCH;
1824 }
1825 else {
1826 mOpcode = Opcode.LOOKUPSWITCH;
1827 }
1828 }
1829
1830 public Location[] getBranchTargets() {
1831 Location[] targets = new Location[mLocations.length + 1];
1832 System.arraycopy(mLocations, 0, targets, 0, mLocations.length);
1833 targets[targets.length - 1] = mDefaultLocation;
1834
1835 return targets;
1836 }
1837
1838 public boolean isFlowThrough() {
1839 return false;
1840 }
1841
1842 public byte[] getBytes() {
1843 int length = 1;
1844 int pad = 3 - (mLocation & 3);
1845 length += pad;
1846
1847 if (mOpcode == Opcode.TABLESWITCH) {
1848 length += 12 + 4 * (mLargest - mSmallest + 1);
1849 }
1850 else {
1851 length += 8 + 8 * mCases.length;
1852 }
1853
1854 mBytes = new byte[length];
1855
1856 if (!isResolved()) {
1857 return mBytes;
1858 }
1859
1860 mBytes[0] = mOpcode;
1861 int cursor = pad + 1;
1862
1863 int defaultOffset = mDefaultLocation.getLocation() - mLocation;
1864 mBytes[cursor++] = (byte)(defaultOffset >> 24);
1865 mBytes[cursor++] = (byte)(defaultOffset >> 16);
1866 mBytes[cursor++] = (byte)(defaultOffset >> 8);
1867 mBytes[cursor++] = (byte)(defaultOffset >> 0);
1868
1869 if (mOpcode == Opcode.TABLESWITCH) {
1870 mBytes[cursor++] = (byte)(mSmallest >> 24);
1871 mBytes[cursor++] = (byte)(mSmallest >> 16);
1872 mBytes[cursor++] = (byte)(mSmallest >> 8);
1873 mBytes[cursor++] = (byte)(mSmallest >> 0);
1874
1875 mBytes[cursor++] = (byte)(mLargest >> 24);
1876 mBytes[cursor++] = (byte)(mLargest >> 16);
1877 mBytes[cursor++] = (byte)(mLargest >> 8);
1878 mBytes[cursor++] = (byte)(mLargest >> 0);
1879
1880 int index = 0;
1881 for (int case_ = mSmallest; case_ <= mLargest; case_++) {
1882 if (case_ == mCases[index]) {
1883 int offset =
1884 mLocations[index].getLocation() - mLocation;
1885 mBytes[cursor++] = (byte)(offset >> 24);
1886 mBytes[cursor++] = (byte)(offset >> 16);
1887 mBytes[cursor++] = (byte)(offset >> 8);
1888 mBytes[cursor++] = (byte)(offset >> 0);
1889
1890 index++;
1891 }
1892 else {
1893 mBytes[cursor++] = (byte)(defaultOffset >> 24);
1894 mBytes[cursor++] = (byte)(defaultOffset >> 16);
1895 mBytes[cursor++] = (byte)(defaultOffset >> 8);
1896 mBytes[cursor++] = (byte)(defaultOffset >> 0);
1897 }
1898 }
1899 }
1900 else {
1901 mBytes[cursor++] = (byte)(mCases.length >> 24);
1902 mBytes[cursor++] = (byte)(mCases.length >> 16);
1903 mBytes[cursor++] = (byte)(mCases.length >> 8);
1904 mBytes[cursor++] = (byte)(mCases.length >> 0);
1905
1906 for (int index = 0; index < mCases.length; index++) {
1907 int case_ = mCases[index];
1908
1909 mBytes[cursor++] = (byte)(case_ >> 24);
1910 mBytes[cursor++] = (byte)(case_ >> 16);
1911 mBytes[cursor++] = (byte)(case_ >> 8);
1912 mBytes[cursor++] = (byte)(case_ >> 0);
1913
1914 int offset = mLocations[index].getLocation() - mLocation;
1915 mBytes[cursor++] = (byte)(offset >> 24);
1916 mBytes[cursor++] = (byte)(offset >> 16);
1917 mBytes[cursor++] = (byte)(offset >> 8);
1918 mBytes[cursor++] = (byte)(offset >> 0);
1919 }
1920 }
1921
1922 return mBytes;
1923 }
1924
1925 public boolean isResolved() {
1926 if (mDefaultLocation.getLocation() >= 0) {
1927 for (int i=0; i<mLocations.length; i++) {
1928 if (mLocations[i].getLocation() < 0) {
1929 break;
1930 }
1931 }
1932
1933 return true;
1934 }
1935
1936 return false;
1937 }
1938
1939 private void sort(int left, int right) {
1940 if (left >= right) {
1941 return;
1942 }
1943
1944 swap(left, (left + right) / 2); // move middle element to 0
1945
1946 int last = left;
1947
1948 for (int i = left + 1; i <= right; i++) {
1949 if (mCases[i] < mCases[left]) {
1950 swap(++last, i);
1951 }
1952 }
1953
1954 swap(left, last);
1955 sort(left, last-1);
1956 sort(last + 1, right);
1957 }
1958
1959 private void swap(int i, int j) {
1960 int tempInt = mCases[i];
1961 mCases[i] = mCases[j];
1962 mCases[j] = tempInt;
1963
1964 Location tempLocation = mLocations[i];
1965 mLocations[i] = mLocations[j];
1966 mLocations[j] = tempLocation;
1967 }
1968 }
1969 }