Initial import
[jpf-core.git] / src / main / gov / nasa / jpf / vm / StackFrame.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18 package gov.nasa.jpf.vm;
19
20 import gov.nasa.jpf.JPFException;
21 import gov.nasa.jpf.util.BitSetN;
22 import gov.nasa.jpf.util.BitSet1024;
23 import gov.nasa.jpf.util.BitSet256;
24 import gov.nasa.jpf.util.BitSet64;
25 import gov.nasa.jpf.util.FixedBitSet;
26 import gov.nasa.jpf.util.HashData;
27 import gov.nasa.jpf.util.Misc;
28 import gov.nasa.jpf.util.OATHash;
29 import gov.nasa.jpf.util.ObjectList;
30 import gov.nasa.jpf.util.PrintUtils;
31 import gov.nasa.jpf.vm.bytecode.InvokeInstruction;
32
33 import java.io.PrintStream;
34 import java.io.PrintWriter;
35 import java.io.StringWriter;
36 import java.util.Iterator;
37
38
39 /**
40  * Describes callerSlots stack frame.
41  *
42  * Java methods always have bounded local and operand stack sizes, computed
43  * at compile time, stored in the classfile, and checked at runtime by the
44  * bytecode verifier. Consequently, we combine locals and operands in one
45  * data structure with the following layout
46  *
47  *   slot[0]                : 'this'
48  *   ..                          .. local vars
49  *   slot[stackBase-1]      : last local var
50  *   slot[stackBase]        : first operand slot
51  *   ..    ^
52  *   ..    | operand stack range
53  *   ..    v
54  *   slot[top]              : highest used operand slot
55  *
56  */
57 public abstract class StackFrame implements Cloneable {
58   
59   /**
60    * this StackFrame is not allowed to be modified anymore because it has been state stored.
61    * Set during state storage and checked upon each modification, causing exceptions on attempts
62    * to modify callerSlots frozen instance. The flag is reset in clones
63    */
64   public static final int   ATTR_IS_FROZEN  = 0x100;  
65   static final int ATTR_IS_REFLECTION = 0x1000;  
66    /**
67     * the previous StackFrame (usually the caller, null if first). To be set when
68     * the frame is pushed on the ThreadInfo callstack
69     */
70   protected StackFrame prev;
71
72   /**
73    * state management related attributes similar to ElementInfo. The lower 16 bits
74    * are stored/restored, the upper 16 bits are for transient use
75    */
76   protected int attributes;
77
78   
79   protected int top;                // top index of the operand stack (NOT size)
80                                     // this points to the last pushed value
81
82   protected int thisRef = MJIEnv.NULL;       // slots[0] can change, but we have to keep 'this'
83   protected int stackBase;          // index where the operand stack begins
84
85   protected int[] slots;            // the combined local and operand slots
86   protected FixedBitSet isRef;      // which slots contain references
87
88   protected Object frameAttr;       // optional user attrs for the whole frame
89   
90   /*
91    * This array can be used to store attributes (e.g. variable names) for
92    * operands. We don't do anything with this except of preserving it (across
93    * dups etc.), so it's pretty much up to the VM listeners/peers what's stored
94    *
95    * NOTE: attribute values are not restored upon backtracking per default, but
96    * attribute references are. If you need restoration of values, use copy-on-write
97    * in your clients
98    *
99    * these are set on demand
100    */
101   protected Object[] attrs = null;  // the combined user-defined callerSlots (set on demand)
102
103   protected Instruction pc;         // the next insn to execute (program counter)
104   protected MethodInfo mi;          // which method is executed in this frame
105
106   static final int[] EMPTY_ARRAY = new int[0];
107   static final FixedBitSet EMPTY_BITSET = new BitSet64();
108
109   protected StackFrame (MethodInfo callee, int nLocals, int nOperands){
110     mi = callee;
111     pc = mi.getInstruction(0);
112
113     stackBase = nLocals;
114     top = nLocals-1;
115
116     int nSlots = nLocals + nOperands;
117     if (nSlots > 0){
118       slots = new int[nLocals + nOperands];
119       isRef = createReferenceMap(slots.length);
120     } else {
121       // NativeStackFrames don't use locals or operands, but we
122       // don't want to add tests to all our methods
123       slots = EMPTY_ARRAY;
124       isRef = EMPTY_BITSET;
125     }
126   }
127   
128   public StackFrame (MethodInfo callee){
129     this( callee, callee.getMaxLocals(), callee.getMaxStack());
130   }
131
132
133
134   /**
135    * Creates an empty stack frame. Used by clone.
136    */
137   protected StackFrame () {
138   }
139
140   /**
141    * creates callerSlots dummy Stackframe for testing of operand/local operations
142    * NOTE - TESTING ONLY! this does not have a MethodInfo
143    */
144   protected StackFrame (int nLocals, int nOperands){
145     stackBase = nLocals;
146     slots = new int[nLocals + nOperands];
147     isRef = createReferenceMap(slots.length);
148     top = nLocals-1;  // index, not size!
149   }
150   
151   /**
152    * re-execute method from the beginning - use with care
153    */
154   public void reset() {
155     pc = mi.getInstruction(0);
156   }  
157   
158
159
160   protected FixedBitSet createReferenceMap (int nSlots){
161     if (nSlots <= 64){
162       return new BitSet64();
163     } else if (nSlots <= 256){
164       return new BitSet256();  
165     } else if (nSlots <= 1024) {
166       return new BitSet1024();
167     } else {
168       return new BitSetN(nSlots);
169     }
170   }
171
172   public boolean isNative() {
173     return false;
174   }
175   
176   public StackFrame getCallerFrame (){
177     MethodInfo callee = mi;
178     for (StackFrame frame = getPrevious(); frame != null; frame = frame.getPrevious()){
179       Instruction insn = frame.getPC();
180       if (insn instanceof InvokeInstruction){
181         InvokeInstruction call = (InvokeInstruction)insn;
182         if (call.getInvokedMethod() == callee){
183           return frame;
184         }
185       }
186     }
187     
188     return null;
189   }
190   
191   /**
192    * return the object reference for an instance method to be called (we are still in the
193    * caller's frame). This only makes sense after all params have been pushed, before the
194    * INVOKEx insn is executed
195    */
196   public int getCalleeThis (MethodInfo mi) {
197     return getCalleeThis(mi.getArgumentsSize());
198   }
199
200   /**
201    * return reference of called object in the context of the caller
202    * (i.e. we are in the caller frame)
203    */
204   public int getCalleeThis (int size) {
205     // top is the topmost index
206     int i = size-1;
207     if (top < i) {
208       return MJIEnv.NULL;
209     }
210
211     return slots[top-i];
212   }
213
214   public StackFrame getPrevious() {
215     return prev;
216   }
217   
218   /**
219    * to be set (by ThreadInfo) when the frame is pushed. Can also be used
220    * for non-local gotos, but be warned - that's tricky
221    */
222   public void setPrevious (StackFrame frame){
223     prev = frame;
224   }
225
226   public Object getLocalOrFieldValue (String id) {
227     // try locals first
228     LocalVarInfo lv = mi.getLocalVar(id, pc.getPosition());
229     if (lv != null){
230       return getLocalValueObject(lv);
231     }
232
233     // then fields
234     return getFieldValue(id);
235   }
236
237   public Object getLocalValueObject (LocalVarInfo lv) {
238     if (lv != null) { // might not have been compiled with debug info
239       String sig = lv.getSignature();
240       int slotIdx = lv.getSlotIndex();
241       int v = slots[slotIdx];
242
243       switch (sig.charAt(0)) {
244         case 'Z':
245           return Boolean.valueOf(v != 0);
246         case 'B':
247           return new Byte((byte) v);
248         case 'C':
249           return new Character((char) v);
250         case 'S':
251           return new Short((short) v);
252         case 'I':
253           return new Integer(v);
254         case 'J':
255           return new Long(Types.intsToLong(slots[slotIdx + 1], v)); // Java is big endian, Types expects low,high
256         case 'F':
257           return new Float(Float.intBitsToFloat(v));
258         case 'D':
259           return new Double(Double.longBitsToDouble(Types.intsToLong(slots[slotIdx + 1], v)));
260         default:  // reference
261           if (v >= 0) {
262             return VM.getVM().getHeap().get(v);
263           }
264       }
265     }
266
267     return null;
268   }
269
270   public Object getFieldValue (String id) {
271     // try instance fields first
272     if (thisRef != MJIEnv.NULL) {  // it's an instance method
273       ElementInfo ei = VM.getVM().getHeap().get(thisRef);
274       Object v = ei.getFieldValueObject(id);
275       if (v != null) {
276         return v;
277       }
278     }
279
280     // check static fields (in method class and its superclasses)
281     return mi.getClassInfo().getStaticFieldValueObject(id);
282   }
283
284   public ClassInfo getClassInfo () {
285     return mi.getClassInfo();
286   }
287
288   public String getClassName () {
289     return mi.getClassInfo().getName();
290   }
291
292   public String getSourceFile () {
293     return mi.getClassInfo().getSourceFileName();
294   }
295
296   /**
297    * does any of the 'nTopSlots' hold callerSlots reference value of 'objRef'
298    * 'nTopSlots' is usually obtained from MethodInfo.getNumberOfCallerStackSlots()
299    */
300   public boolean includesReferenceOperand (int nTopSlots, int objRef){
301
302     for (int i=0, j=top-nTopSlots+1; i<nTopSlots && j>=0; i++, j++) {
303       if (isRef.get(j) && (slots[j] == objRef)){
304         return true;
305       }
306     }
307
308     return false;
309   }
310
311   /**
312    * does any of the operand slots hold callerSlots reference value of 'objRef'
313    */
314   public boolean includesReferenceOperand (int objRef){
315
316     for (int i=stackBase; i<=top; i++) {
317       if (isRef.get(i) && (slots[i] == objRef)){
318         return true;
319       }
320     }
321
322     return false;
323   }
324
325   /**
326    * is this StackFrame modifying the KernelState
327    * this is true unless this is callerSlots NativeStackFrame
328    */
329   public boolean modifiesState() {
330     return true;
331   }
332
333   public boolean isDirectCallFrame () {
334     return false;
335   }
336
337   public boolean isSynthetic() {
338     return false;
339   }
340
341   // gets and sets some derived information
342   public int getLine () {
343     return mi.getLineNumber(pc);
344   }
345
346
347   /**
348    * generic visitor for reference arguments
349    */
350   public void processRefArguments (MethodInfo miCallee, ReferenceProcessor visitor){
351     int nArgSlots = miCallee.getArgumentsSize();
352
353     for (int i=top-1; i>=top-nArgSlots; i--){
354       if (isRef.get(i)){
355         visitor.processReference(slots[i]);
356       }
357     }
358   }
359
360   public int getSlot(int idx){
361     return slots[idx];
362   }
363   public boolean isReferenceSlot(int idx){
364     return isRef.get(idx);
365   }
366
367
368   public void setOperand (int offset, int v, boolean isRefValue){
369     int i = top-offset;
370     slots[i] = v;
371     isRef.set(i, isRefValue);
372   }
373
374   
375   //----------------------------- various attribute accessors
376
377   public boolean hasAttrs () {
378     return attrs != null;
379   }
380
381   public boolean hasFrameAttr(){
382     return frameAttr != null;
383   }
384   
385   public boolean hasFrameAttr (Class<?> attrType){
386     return ObjectList.containsType(frameAttr, attrType);
387   }
388   
389   public boolean hasFrameAttrValue (Object a){
390     return ObjectList.contains(frameAttr, a);
391   }
392   
393   //--- the frame attr accessors 
394   
395  /**
396    * this returns all of them - use either if you know there will be only
397    * one attribute at callerSlots time, or check/process result with ObjectList
398    */
399   public Object getFrameAttr(){
400     return frameAttr;
401   }
402
403   /**
404    * this replaces all of them - use only if you know there are no 
405    * SystemAttributes in the list (which would cause an exception)
406    */
407   public void setFrameAttr (Object attr){
408     frameAttr = ObjectList.set(frameAttr, attr);    
409   }
410
411   public void addFrameAttr (Object attr){
412     frameAttr = ObjectList.add(frameAttr, attr);
413   }
414
415   public void removeFrameAttr (Object attr){
416     frameAttr = ObjectList.remove(frameAttr, attr);
417   }
418
419   public void replaceFrameAttr (Object oldAttr, Object newAttr){
420     frameAttr = ObjectList.replace(frameAttr, oldAttr, newAttr);
421   }
422
423   /**
424    * this only returns the first attr of this type, there can be more
425    * if you don't use client private types or the provided type is too general
426    */
427   public <T> T getFrameAttr (Class<T> attrType) {
428     return ObjectList.getFirst(frameAttr, attrType);
429   }
430   
431   public <T> T getAndResetFrameAttr (Class<T> attrType) {
432     T attr = ObjectList.getFirst(frameAttr, attrType);
433     if (attr != null){
434       frameAttr = ObjectList.remove(frameAttr, attr);
435     }
436     return attr;
437   }
438   
439
440   public <T> T getNextFrameAttr (Class<T> attrType, Object prev) {
441     return ObjectList.getNext(frameAttr, attrType, prev);
442   }
443
444   public ObjectList.Iterator frameAttrIterator(){
445     return ObjectList.iterator(frameAttr);
446   }
447   
448   public <T> ObjectList.TypedIterator<T> frameAttrIterator(Class<T> attrType){
449     return ObjectList.typedIterator(frameAttr, attrType);
450   }
451   
452   //--- the top single-slot operand attrs
453
454   public boolean hasOperandAttr(){
455     if ((top >= stackBase) && (attrs != null)){
456       return (attrs[top] != null);
457     }
458     return false;
459   }
460   public boolean hasOperandAttr(Class<?> type){
461     if ((top >= stackBase) && (attrs != null)){
462       return ObjectList.containsType(attrs[top], type);
463     }
464     return false;
465   }
466   
467   /**
468    * this returns all of them - use either if you know there will be only
469    * one attribute at callerSlots time, or check/process result with ObjectList
470    */
471   public Object getOperandAttr () {
472     if ((top >= stackBase) && (attrs != null)){
473       return attrs[top];
474     }
475     return null;
476   }
477
478   /**
479    * this replaces all of them - use only if you know 
480    *  - there will be only one attribute at callerSlots time
481    *  - you obtained the value you set by callerSlots previous getXAttr()
482    *  - you constructed callerSlots multi value list with ObjectList.createList()
483    */
484   public void setOperandAttr (Object a){
485     assert (top >= stackBase);
486     if (attrs == null) {
487       if (a == null) return;
488       attrs = new Object[slots.length];
489     }
490     attrs[top] = a;
491   }
492
493   
494   /**
495    * this only returns the first attr of this type, there can be more
496    * if you don't use client private types or the provided type is too general
497    */
498   public <T> T getOperandAttr (Class<T> attrType){
499     assert (top >= stackBase);
500     
501     if ((attrs != null)){
502       return ObjectList.getFirst(attrs[top], attrType);
503     }
504     return null;
505   }
506   public <T> T getNextOperandAttr (Class<T> attrType, Object prev){
507     assert (top >= stackBase);
508     if (attrs != null){
509       return ObjectList.getNext( attrs[top], attrType, prev);
510     }
511     return null;
512   }
513   public Iterator operandAttrIterator(){
514     assert (top >= stackBase);
515     Object a = (attrs != null) ? attrs[top] : null;
516     return ObjectList.iterator(a);
517   }
518   public <T> Iterator<T> operandAttrIterator(Class<T> attrType){
519     assert (top >= stackBase);
520     Object a = (attrs != null) ? attrs[top] : null;
521     return ObjectList.typedIterator(a, attrType);
522   }
523   
524
525   public void addOperandAttr (Object a){
526     assert (top >= stackBase);
527     if (a != null){
528       if (attrs == null) {
529         attrs = new Object[slots.length];
530       }
531
532       attrs[top] = ObjectList.add(attrs[top], a);
533     }        
534   }
535   
536   public void removeOperandAttr (Object a){
537     assert (top >= stackBase) && (a != null);
538     if (attrs != null){
539       attrs[top] = ObjectList.remove(attrs[top], a);
540     }        
541   }
542   
543   public void replaceOperandAttr (Object oldAttr, Object newAttr){
544     assert (top >= stackBase) && (oldAttr != null) && (newAttr != null);
545     if (attrs != null){
546       attrs[top] = ObjectList.replace(attrs[top], oldAttr, newAttr);
547     }        
548   }
549   
550   
551   //--- offset operand attrs
552
553   public boolean hasOperandAttr(int offset){
554     int i = top-offset;
555     assert (i >= stackBase);
556     if (attrs != null){
557       return (attrs[i] != null);
558     }
559     return false;
560   }
561   public boolean hasOperandAttr(int offset, Class<?> type){
562     int i = top-offset;
563     assert (i >= stackBase);
564     if (attrs != null){
565       return ObjectList.containsType(attrs[i], type);
566     }
567     return false;
568   }
569   
570   /**
571    * this returns all of them - use either if you know there will be only
572    * one attribute at callerSlots time, or check/process result with ObjectList
573    */
574   public Object getOperandAttr (int offset) {
575     int i = top-offset;
576     assert (i >= stackBase);
577     
578     if (attrs != null) {
579       return attrs[i];
580     }
581     return null;
582   }
583
584   /**
585    * this replaces all of them - use only if you know 
586    *  - there will be only one attribute at callerSlots time
587    *  - you obtained the value you set by callerSlots previous getXAttr()
588    *  - you constructed callerSlots multi value list with ObjectList.createList()
589    */  
590   public void setOperandAttr (int offset, Object a){
591     int i = top-offset;
592     assert (i >= stackBase);
593
594     if (attrs == null) {
595       if (a == null) return;
596       attrs = new Object[slots.length];
597     }
598     attrs[i] = a;
599   }
600
601   /**
602    * this only returns the first attr of this type, there can be more
603    * if you don't use client private types or the provided type is too general
604    */
605   public <T> T getOperandAttr (int offset, Class<T> attrType){
606     int i = top-offset;
607     assert (i >= stackBase) : this;
608     if (attrs != null){
609       return ObjectList.getFirst(attrs[i], attrType);
610     }
611     return null;
612   }
613   public <T> T getNextOperandAttr (int offset, Class<T> attrType, Object prev){
614     int i = top-offset;
615     assert (i >= stackBase);
616     if (attrs != null){
617       return ObjectList.getNext( attrs[i], attrType, prev);
618     }
619     return null;
620   }
621   public ObjectList.Iterator operandAttrIterator(int offset){
622     int i = top-offset;
623     assert (i >= stackBase);
624     Object a = (attrs != null) ? attrs[i] : null;
625     return ObjectList.iterator(a);
626   }
627   public <T> ObjectList.TypedIterator<T> operandAttrIterator(int offset, Class<T> attrType){
628     int i = top-offset;
629     assert (i >= stackBase);
630     Object a = (attrs != null) ? attrs[i] : null;
631     return ObjectList.typedIterator(a, attrType);
632   }
633
634
635   public void addOperandAttr (int offset, Object a){
636     int i = top-offset;
637     assert (i >= stackBase);
638
639     if (a != null){
640       if (attrs == null) {
641         attrs = new Object[slots.length];
642       }
643       attrs[i] = ObjectList.add(attrs[i],a);
644     }    
645   }
646
647   public void removeOperandAttr (int offset, Object a){
648     int i = top-offset;
649     assert (i >= stackBase) && (a != null);
650     if (attrs != null){
651       attrs[i] = ObjectList.remove(attrs[i], a);
652     }        
653   }
654   
655   public void replaceOperandAttr (int offset, Object oldAttr, Object newAttr){
656     int i = top-offset;
657     assert (i >= stackBase) && (oldAttr != null) && (newAttr != null);
658     if (attrs != null){
659       attrs[i] = ObjectList.replace(attrs[i], oldAttr, newAttr);
660     }        
661   }
662   
663   
664   //--- top double-slot operand attrs
665   // we store attributes for double slot values at the local var index,
666   // which is the lower one. The ..LongOperand.. APIs are handling this offset
667  
668   public boolean hasLongOperandAttr(){
669     return hasOperandAttr(1);
670   }
671   public boolean hasLongOperandAttr(Class<?> type){
672     return hasOperandAttr(1, type);
673   }
674   
675   /**
676    * this returns all of them - use either if you know there will be only
677    * one attribute at callerSlots time, or check/process result with ObjectList
678    */
679   public Object getLongOperandAttr () {
680     return getOperandAttr(1);
681   }
682
683   /**
684    * this replaces all of them - use only if you know 
685    *  - there will be only one attribute at callerSlots time
686    *  - you obtained the value you set by callerSlots previous getXAttr()
687    *  - you constructed callerSlots multi value list with ObjectList.createList()
688    */  
689   public void setLongOperandAttr (Object a){
690     setOperandAttr(1, a);
691   }
692   
693   /**
694    * this only returns the first attr of this type, there can be more
695    * if you don't use client private types or the provided type is too general
696    */
697   public <T> T getLongOperandAttr (Class<T> attrType) {
698     return getOperandAttr(1, attrType);
699   }
700   public <T> T getNextLongOperandAttr (Class<T> attrType, Object prev) {
701     return getNextOperandAttr(1, attrType, prev);
702   }
703   public ObjectList.Iterator longOperandAttrIterator(){
704     return operandAttrIterator(1);
705   }
706   public <T> ObjectList.TypedIterator<T> longOperandAttrIterator(Class<T> attrType){
707     return operandAttrIterator(1, attrType);
708   }
709     
710   public void addLongOperandAttr (Object a){
711     addOperandAttr(1, a);
712   }
713
714   public void removeLongOperandAttr (Object a){
715     removeOperandAttr(1, a);
716   }
717
718   public void replaceLongOperandAttr (Object oldAttr, Object newAttr){
719     replaceOperandAttr(1, oldAttr, newAttr);
720   }
721
722
723   //--- local attrs
724   // single- or double-slot - you have to provide the var index anyways)
725   
726   public boolean hasLocalAttr(int index){
727     assert index < stackBase;
728     if (attrs != null){
729       return (attrs[index] != null);
730     }
731     return false;
732   }
733   public boolean hasLocalAttr(int index, Class<?> type){
734     assert index < stackBase;
735     if (attrs != null){
736       return ObjectList.containsType(attrs[index], type);
737     }
738     return false;
739   }
740
741   /**
742    * this returns all of them - use either if you know there will be only
743    * one attribute at callerSlots time, or check/process result with ObjectList
744    */
745   public Object getLocalAttr (int index){
746     assert index < stackBase;
747     if (attrs != null){
748       return attrs[index];
749     }
750     return null;
751   }
752    
753   public Object getLongLocalAttr (int index){
754     return getLocalAttr( index);
755   }
756   
757   /**
758    * this replaces all of them - use only if you know 
759    *  - there will be only one attribute at callerSlots time
760    *  - you obtained the value you set by callerSlots previous getXAttr()
761    *  - you constructed callerSlots multi value list with ObjectList.createList()
762    */  
763   public void setLocalAttr (int index, Object a) {
764     assert index < stackBase;
765     if (attrs == null){
766       if (a == null) return;
767       attrs = new Object[slots.length];
768     }
769     attrs[index] = a;
770   }
771
772   public void setLongLocalAttr (int index, Object a){
773     setLocalAttr( index, a);
774   }
775   
776   public void addLongLocalAttr (int index, Object a){
777     addLocalAttr( index, a);
778   }
779   
780   /**
781    * this only returns the first attr of this type, there can be more
782    * if you don't use client private types or the provided type is too general
783    */
784   public <T> T getLocalAttr (int index, Class<T> attrType){
785     assert index < stackBase;
786     if (attrs != null){
787       return ObjectList.getFirst( attrs[index], attrType);
788     }
789     return null;
790   }
791   public <T> T getNextLocalAttr (int index, Class<T> attrType, Object prev){
792     assert index < stackBase;
793     if (attrs != null){
794       return ObjectList.getNext( attrs[index], attrType, prev);
795     }
796     return null;
797   }
798   public ObjectList.Iterator localAttrIterator(int index){
799     assert index < stackBase;
800     Object a = (attrs != null) ? attrs[index] : null;
801     return ObjectList.iterator(a);
802   }
803   public <T> ObjectList.TypedIterator<T> localAttrIterator(int index, Class<T> attrType){
804     assert index < stackBase;
805     Object a = (attrs != null) ? attrs[index] : null;
806     return ObjectList.typedIterator(a, attrType);
807   }
808   
809
810   public void addLocalAttr (int index, Object attr){
811     assert index < stackBase;
812     if (attrs == null){
813       if (attr == null) return;
814       attrs = new Object[slots.length];
815     }
816     attrs[index] = ObjectList.add(attrs[index], attr);
817   }
818   
819   public void removeLocalAttr (int index, Object attr){
820     assert index < stackBase && attr != null;
821     if (attr != null){
822       attrs[index] = ObjectList.remove(attrs[index], attr);    
823     }
824   }
825
826   public void replaceLocalAttr (int index, Object oldAttr, Object newAttr){
827     assert index < stackBase && oldAttr != null && newAttr != null;
828     if (attrs == null){
829       attrs[index] = ObjectList.replace(attrs[index], oldAttr, newAttr);    
830     }
831   }
832   
833   //--- various special attr accessors
834
835   /**
836    * helper to quickly find out if any of the locals slots holds
837    * an attribute of the provided type
838    * 
839    * @param attrType type of attribute to look for
840    * @param startIdx local index to start from
841    * @return index of local slot with attribute, -1 if none found
842    */
843   public int getLocalAttrIndex (Class<?> attrType, int startIdx){
844     if (attrs != null){
845       for (int i=startIdx; i<stackBase; i++){
846         Object a = attrs[i];
847         if (ObjectList.containsType(a, attrType)){
848           return i;
849         }
850       }
851     }
852
853     return -1;
854   }
855   
856   // <2do> this is machine dependent since it uses the operand stack. Only here because there
857   // is no suitable place to factor this out between xStackFrame, xNativeStackFrame and xDirectCallStackFrame
858   // (another example of missing multiple inheritance)
859   // Needs to be overridden for Dalvik
860   
861   /**
862    * this retrieves the argument values from the caller, i.e. the previous stackframe 
863    * 
864    * references are returned as ElementInfos or null
865    * primitive values are returned as box objects (e.g. int -> Integer)
866    */
867   public Object[] getArgumentValues (ThreadInfo ti){
868     StackFrame callerFrame = getCallerFrame();
869     if (callerFrame != null){
870       return callerFrame.getCallArguments(ti);
871     } else {
872       // <2do> what about main(String[] args) ?
873     }
874     
875     return null;
876   }
877   
878   /**
879    * get the arguments of the executed call
880    * Note - this throws an exception if the StackFrame pc is not an InvokeInstruction
881    */
882   public Object[] getCallArguments (ThreadInfo ti){
883     if (pc == null || !(pc instanceof InvokeInstruction)){
884       throw new JPFException("stackframe not executing invoke: " + pc);
885     }
886     
887     InvokeInstruction call = (InvokeInstruction) pc;    
888     MethodInfo callee = call.getInvokedMethod();
889
890     byte[] argTypes = callee.getArgumentTypes();
891
892     return getArgumentsValues(ti, argTypes);
893   }
894
895   public Object[] getArgumentsValues (ThreadInfo ti, byte[] argTypes){
896     int n = argTypes.length;
897     Object[] args = new Object[n];
898
899     for (int i=n-1, off=0; i>=0; i--) {
900       switch (argTypes[i]) {
901       case Types.T_ARRAY:
902       //case Types.T_OBJECT:
903       case Types.T_REFERENCE:
904         int ref = peek(off);
905         if (ref != MJIEnv.NULL) {
906           args[i] = ti.getElementInfo(ref);
907         } else {
908           args[i] = null;
909         }
910         off++;
911         break;
912
913       case Types.T_LONG:
914         args[i] = new Long(peekLong(off));
915         off+=2;
916         break;
917       case Types.T_DOUBLE:
918         args[i] = new Double(Types.longToDouble(peekLong(off)));
919         off+=2;
920         break;
921
922       case Types.T_BOOLEAN:
923         args[i] = new Boolean(peek(off) != 0);
924         off++;
925         break;
926       case Types.T_BYTE:
927         args[i] = new Byte((byte)peek(off));
928         off++;
929         break;
930       case Types.T_CHAR:
931         args[i] = new Character((char)peek(off));
932         off++;
933         break;
934       case Types.T_SHORT:
935         args[i] = new Short((short)peek(off));
936         off++;
937         break;
938       case Types.T_INT:
939         args[i] = new Integer(peek(off));
940         off++;
941         break;
942       case Types.T_FLOAT:
943         args[i] = new Float(Types.intToFloat(peek(off)));
944         off++;
945         break;
946       default:
947         // error, unknown argument type
948       }
949     }
950     return args;
951   }
952   
953   /**
954    * return an array of all argument attrs, which in turn can be lists. If
955    * you have to retrieve values, use the ObjectList APIs
956    * 
957    * this is here (and not in ThreadInfo) because we might call it
958    * on callerSlots cached/cloned StackFrame (caller stack might be already
959    * modified, e.g. for callerSlots native method).
960    * to be used from listeners.
961    */
962   public Object[] getArgumentAttrs (MethodInfo miCallee) {
963     if (attrs != null) {
964       int nArgs = miCallee.getNumberOfArguments();
965       byte[] at = miCallee.getArgumentTypes();
966       Object[] a;
967
968       if (!miCallee.isStatic()) {
969         a = new Object[nArgs+1];
970         a[0] = getOperandAttr(miCallee.getArgumentsSize()-1);
971       } else {
972         a = new Object[nArgs];
973       }
974
975       for (int i=nArgs-1, off=0, j=a.length-1; i>=0; i--, j--) {
976         byte argType = at[i];
977         if (argType == Types.T_LONG || argType == Types.T_DOUBLE) {
978           a[j] = getOperandAttr(off+1);
979           off +=2;
980         } else {
981           a[j] = getOperandAttr(off);
982           off++;
983         }
984       }
985
986       return a;
987
988     } else {
989       return null;
990     }
991   }
992
993   /**
994    * check if there is any argument attr of the provided type on the operand stack
995    * this is far more efficient than retrieving attribute values (we don't
996    * care for argument types)
997    */
998   public boolean hasArgumentAttr (MethodInfo miCallee, Class<?> attrType){
999     if (attrs != null) {
1000       int nArgSlots = miCallee.getArgumentsSize();
1001
1002       for (int i=0; i<nArgSlots; i++){
1003         Object a = getOperandAttr(i);
1004         if (ObjectList.containsType(a, attrType)){
1005           return true;
1006         }
1007       }
1008     }
1009
1010     return false;
1011   }
1012
1013   public boolean hasArgumentObjectAttr (ThreadInfo ti, MethodInfo miCallee, Class<?> type){
1014     int nArgSlots = miCallee.getArgumentsSize();
1015     for (int i=0; i<nArgSlots; i++){
1016       if (isOperandRef(i)){
1017         int objRef = peek(i);
1018         if (objRef != MJIEnv.NULL){
1019           ElementInfo ei = ti.getElementInfo(objRef);
1020           if (ei.getObjectAttr(type) != null) {
1021             return true;
1022           }
1023         }
1024       }
1025     }
1026
1027     return false;
1028   }
1029   
1030   
1031   // -- end attrs --
1032   
1033   public void setLocalReferenceVariable (int index, int ref){
1034     if (slots[index] != MJIEnv.NULL){
1035       VM.getVM().getSystemState().activateGC();
1036     }
1037     
1038     slots[index] = ref;
1039     isRef.set(index);
1040   }
1041
1042   public void setLocalVariable (int index, int v){
1043     // Hmm, should we treat this an error?
1044     if (isRef.get(index) && slots[index] != MJIEnv.NULL){
1045       VM.getVM().getSystemState().activateGC();      
1046     }
1047     
1048     slots[index] = v;
1049     isRef.clear(index);
1050   }
1051   
1052   public void setFloatLocalVariable (int index, float f){
1053     setLocalVariable( index, Float.floatToIntBits(f));
1054   }
1055
1056   public void setDoubleLocalVariable (int index, double f){
1057     setLongLocalVariable( index, Double.doubleToLongBits(f));
1058   }
1059
1060   
1061   // <2do> replace with non-ref version
1062   public void setLocalVariable (int index, int v, boolean ref) {
1063     // <2do> activateGc should be replaced by local refChanged
1064     boolean activateGc = ref || (isRef.get(index) && (slots[index] != MJIEnv.NULL));
1065
1066     slots[index] = v;
1067     isRef.set(index,ref);
1068
1069     if (activateGc) {
1070         VM.getVM().getSystemState().activateGC();
1071     }
1072   }
1073
1074   public int getLocalVariable (int i) {
1075     return slots[i];
1076   }
1077
1078   public int getLocalVariable (String name) {
1079     int idx = getLocalVariableSlotIndex(name);
1080     if (idx >= 0) {
1081       return getLocalVariable(idx);
1082     } else {
1083       throw new JPFException("local variable not found: " + name);
1084     }
1085   }
1086
1087   public int getLocalVariableCount() {
1088     return stackBase;
1089   }
1090
1091   /**
1092    * <2do> - this should return only LocalVarInfo for the current pc
1093    */
1094   public LocalVarInfo[] getLocalVars () {
1095     return mi.getLocalVars();
1096   }
1097
1098
1099   public boolean isLocalVariableRef (int idx) {
1100     return isRef.get(idx);
1101   }
1102
1103   public String getLocalVariableType (String name) {
1104     LocalVarInfo lv = mi.getLocalVar(name, pc.getPosition()+pc.getLength());
1105     if (lv != null){
1106       return lv.getType();
1107     }
1108
1109     return null;
1110   }
1111
1112   public String getLocalVariableType (int idx){
1113     LocalVarInfo lv = mi.getLocalVar(idx, pc.getPosition()+pc.getLength());
1114     if (lv != null){
1115       return lv.getType();
1116     }
1117
1118     return null;
1119   }
1120
1121   public LocalVarInfo getLocalVarInfo (String name){
1122     return mi.getLocalVar(name, pc.getPosition()+pc.getLength());
1123   }
1124
1125   public LocalVarInfo getLocalVarInfo (int idx){
1126     return mi.getLocalVar(idx, pc.getPosition()+pc.getLength());
1127   }
1128
1129   public void setThis (int objRef){
1130     thisRef = objRef;
1131   }
1132   
1133   public FixedBitSet getReferenceMap(){
1134     return isRef;
1135   }
1136
1137   //--- direct slot access - provided for machine-independent clients
1138   
1139   public int[] getSlots () {
1140     return slots; // we should probably clone
1141   }
1142   public Object[] getSlotAttrs(){
1143     return attrs;
1144   }
1145   public Object getSlotAttr (int i){
1146     if (attrs != null){
1147       return attrs[i];
1148     }
1149     return null;
1150   }
1151   public <T> T getSlotAttr (int i, Class<T> attrType){
1152     if (attrs != null){
1153       return ObjectList.getFirst( attrs[i], attrType);
1154     }
1155     return null;
1156   }  
1157   public void setSlotAttr (int i, Object a){
1158     if (attrs == null){
1159       attrs = new Object[slots.length];
1160     }
1161     attrs[i] = a;
1162   }
1163   public void addSlotAttr (int i, Object a){
1164     if (a != null){
1165       if (attrs == null) {
1166         attrs = new Object[slots.length];
1167       }
1168
1169       attrs[i] = ObjectList.add(attrs[i], a);
1170     }        
1171   }  
1172   public void replaceSlotAttr (int i, Object oldAttr, Object newAttr){
1173     if (attrs != null){
1174       attrs[i] = ObjectList.replace(attrs[i], oldAttr, newAttr);
1175     }        
1176   }
1177   
1178   
1179   
1180   public void visitReferenceSlots (ReferenceProcessor visitor){
1181     for (int i=isRef.nextSetBit(0); i>=0 && i<=top; i=isRef.nextSetBit(i+1)){
1182       visitor.processReference(slots[i]);
1183     }
1184   }
1185
1186   public void setLongLocalVariable (int index, long v) {
1187     // WATCH OUT: apparently, slots can change type, so we have to
1188     // reset the reference flag (happened in JavaSeq)
1189
1190     slots[index] = Types.hiLong(v);
1191     isRef.clear(index);
1192
1193     index++;
1194     slots[index] = Types.loLong(v);
1195     isRef.clear(index);
1196   }
1197
1198   public long getLongLocalVariable (int idx) {
1199     return Types.intsToLong(slots[idx + 1], slots[idx]);
1200   }
1201   
1202   public double getDoubleLocalVariable (int idx) {
1203     return Types.intsToDouble(slots[idx + 1], slots[idx]);
1204   }
1205
1206   public float getFloatLocalVariable (int idx) {
1207     int bits = slots[idx];
1208     return Float.intBitsToFloat(bits);
1209   }
1210
1211   public double getDoubleLocalVariable (String name) {
1212     int idx = getLocalVariableSlotIndex(name);
1213     if (idx >= 0) {
1214       return getDoubleLocalVariable(idx);
1215     } else {
1216       throw new JPFException("long local variable not found: " + name);
1217     }    
1218   }
1219   
1220   public long getLongLocalVariable (String name) {
1221     int idx = getLocalVariableSlotIndex(name);
1222
1223     if (idx >= 0) {
1224       return getLongLocalVariable(idx);
1225     } else {
1226       throw new JPFException("long local variable not found: " + name);
1227     }
1228   }
1229
1230   public MethodInfo getMethodInfo () {
1231     return mi;
1232   }
1233
1234   public String getMethodName () {
1235     return mi.getName();
1236   }
1237
1238   public boolean isOperandRef (int offset) {
1239     return isRef.get(top-offset);
1240   }
1241
1242   public boolean isOperandRef () {
1243     return isRef.get(top);
1244   }
1245
1246   //--- direct pc modification
1247   // NOTE: this is dangerous, caller has to guarantee stack consistency
1248   public void setPC (Instruction newpc) {
1249     pc = newpc;
1250   }
1251
1252   public Instruction getPC () {
1253     return pc;
1254   }
1255
1256   public void advancePC() {
1257     int i = pc.getInstructionIndex() + 1;
1258     if (i < mi.getNumberOfInstructions()) {
1259       pc = mi.getInstruction(i);
1260     } else {
1261       pc = null;
1262     }
1263   }
1264
1265   public int getTopPos() {
1266     return top;
1267   }
1268
1269   ExceptionHandler getHandlerFor (ClassInfo ciException){
1270     return mi.getHandlerFor (ciException, pc);
1271   }
1272   
1273   public boolean isFirewall (){
1274     return mi.isFirewall();
1275   }
1276   
1277   public String getStackTraceInfo () {
1278     StringBuilder sb = new StringBuilder(128);
1279
1280     if(!mi.isJPFInternal()) {
1281         sb.append(mi.getStackTraceName());
1282         
1283         if(pc != null) {
1284                 sb.append('(');
1285             sb.append( pc.getFilePos());
1286             sb.append(')');
1287         }
1288     } else {
1289         sb.append(mi.getName());
1290         
1291         if(mi.isMJI()) {
1292                 sb.append("(Native)");
1293         } else {
1294                 sb.append("(Synthetic)");
1295         }
1296     }
1297
1298     return sb.toString();
1299   }
1300
1301   /**
1302    * if this is an instance method, return the reference of the corresponding object
1303    * (note this only has to be in slot 0 upon entry)
1304    */
1305   public int getThis () {
1306     return thisRef;
1307   }
1308
1309   // stack operations
1310   public void clearOperandStack () {
1311     if (attrs != null){
1312       for (int i=stackBase; i<= top; i++){
1313         attrs[i] = null;
1314       }
1315     }
1316     
1317     top = stackBase-1;
1318   }
1319   
1320   // this is callerSlots deep copy
1321   @Override
1322   public StackFrame clone () {
1323     try {
1324       StackFrame sf = (StackFrame) super.clone();
1325
1326       sf.defreeze();
1327       
1328       sf.slots = slots.clone();
1329       sf.isRef = isRef.clone();
1330
1331       if (attrs != null){
1332         sf.attrs = attrs.clone();
1333       }
1334
1335       // frameAttr is not cloned to allow search global use 
1336
1337       return sf;
1338     } catch (CloneNotSupportedException cnsx) {
1339       throw new JPFException(cnsx);
1340     }
1341   }
1342   
1343   //--- change management
1344   
1345   protected void checkIsModifiable() {
1346     if ((attributes & ATTR_IS_FROZEN) != 0) {
1347       throw new JPFException("attempt to modify frozen stackframe: " + this);
1348     }
1349   }
1350   
1351   public void freeze() {
1352     attributes |= ATTR_IS_FROZEN;
1353   }
1354
1355   public void defreeze() {
1356     attributes &= ~ATTR_IS_FROZEN;
1357   }
1358   
1359   public boolean isFrozen() {
1360     return ((attributes & ATTR_IS_FROZEN) != 0);    
1361   }
1362   
1363   
1364   public void setReflection(){
1365     attributes |= ATTR_IS_REFLECTION;
1366   }
1367   
1368   public boolean isReflection(){
1369     return ((attributes & ATTR_IS_REFLECTION) != 0);
1370   }
1371
1372   // all the dupses don't have any GC side effect (everything is already
1373   // on the stack), so skip the GC requests associated with push()/pop()
1374
1375   public void dup () {
1376     // .. A     =>
1377     // .. A A
1378     //    ^
1379
1380     int t= top;
1381
1382     int td=t+1;
1383     slots[td] = slots[t];
1384     isRef.set(td, isRef.get(t));
1385
1386     if (attrs != null){
1387       attrs[td] = attrs[t];
1388     }
1389
1390     top = td;
1391   }
1392
1393   public void dup2 () {
1394     // .. A B        =>
1395     // .. A B A B
1396     //      ^
1397
1398     int ts, td;
1399     int t=top;
1400
1401     // duplicate A
1402     td = t+1; ts = t-1;
1403     slots[td] = slots[ts];
1404     isRef.set(td, isRef.get(ts));
1405     if (attrs != null){
1406       attrs[td] = attrs[ts];
1407     }
1408
1409     // duplicate B
1410     td++; ts=t;
1411     slots[td] = slots[ts];
1412     isRef.set(td, isRef.get(ts));
1413     if (attrs != null){
1414       attrs[td] = attrs[ts];
1415     }
1416
1417     top = td;
1418   }
1419
1420   public void dup2_x1 () {
1421     // .. A B C       =>
1422     // .. B C A B C
1423     //        ^
1424
1425     int b, c;
1426     boolean bRef, cRef;
1427     Object bAnn = null, cAnn = null;
1428     int ts, td;
1429     int t = top;
1430
1431     // duplicate C
1432     ts=t; td = t+2;                              // ts=top, td=top+2
1433     slots[td] = c = slots[ts];
1434     cRef = isRef.get(ts);
1435     isRef.set(td,cRef);
1436     if (attrs != null){
1437       attrs[td] = cAnn = attrs[ts];
1438     }
1439
1440     // duplicate B
1441     ts--; td--;                                  // ts=top-1, td=top+1
1442     slots[td] = b = slots[ts];
1443     bRef = isRef.get(ts);
1444     isRef.set(td, bRef);
1445     if (attrs != null){
1446       attrs[td] = bAnn = attrs[ts];
1447     }
1448
1449     // shuffle A
1450     ts=t-2; td=t;                                // ts=top-2, td=top
1451     slots[td] = slots[ts];
1452     isRef.set(td, isRef.get(ts));
1453     if (attrs != null){
1454       attrs[td] = attrs[ts];
1455     }
1456
1457     // shuffle B
1458     td = ts;                                     // td=top-2
1459     slots[td] = b;
1460     isRef.set(td, bRef);
1461     if (attrs != null){
1462       attrs[td] = bAnn;
1463     }
1464
1465     // shuffle C
1466     td++;                                        // td=top-1
1467     slots[td] = c;
1468     isRef.set(td, cRef);
1469     if (attrs != null){
1470       attrs[td] = cAnn;
1471     }
1472
1473     top += 2;
1474   }
1475
1476   public void dup2_x2 () {
1477     // .. A B C D       =>
1478     // .. C D A B C D
1479     //          ^
1480
1481     int c, d;
1482     boolean cRef, dRef;
1483     Object cAnn = null, dAnn = null;
1484     int ts, td;
1485     int t = top;
1486
1487     // duplicate C
1488     ts = t-1; td = t+1;                          // ts=top-1, td=top+1
1489     slots[td] = c = slots[ts];
1490     cRef = isRef.get(ts);
1491     isRef.set(td, cRef);
1492     if (attrs != null){
1493       attrs[td] = cAnn = attrs[ts];
1494     }
1495
1496     // duplicate D
1497     ts=t; td++;                                  // ts=top, td=top+2
1498     slots[td] = d = slots[ts];
1499     dRef = isRef.get(ts);
1500     isRef.set(td, dRef);
1501     if (attrs != null){
1502       attrs[td] = dAnn = attrs[ts];
1503     }
1504
1505     // shuffle A
1506     ts = t-3; td = t-1;                          // ts=top-3, td=top-1
1507     slots[td] = slots[ts];
1508     isRef.set( td, isRef.get(ts));
1509     if (attrs != null){
1510       attrs[td] = attrs[ts];
1511     }
1512
1513     // shuffle B
1514     ts++; td = t;                                // ts = top-2
1515     slots[td] = slots[ts];
1516     isRef.set( td, isRef.get(ts));
1517     if (attrs != null){
1518       attrs[td] = attrs[ts];
1519     }
1520
1521     // shuffle D
1522     td = ts;                                     // td = top-2
1523     slots[td] = d;
1524     isRef.set( td, dRef);
1525     if (attrs != null){
1526       attrs[td] = dAnn;
1527     }
1528
1529     // shuffle C
1530     td--;                                        // td = top-3
1531     slots[td] = c;
1532     isRef.set(td, cRef);
1533     if (attrs != null){
1534       attrs[td] = cAnn;
1535     }
1536
1537     top += 2;
1538   }
1539
1540   public void dup_x1 () {
1541     // .. A B     =>
1542     // .. B A B
1543     //      ^
1544
1545     int b;
1546     boolean bRef;
1547     Object bAnn = null;
1548     int ts, td;
1549     int t = top;
1550
1551     // duplicate B
1552     ts = t; td = t+1;
1553     slots[td] = b = slots[ts];
1554     bRef = isRef.get(ts);
1555     isRef.set(td, bRef);
1556     if (attrs != null){
1557       attrs[td] = bAnn = attrs[ts];
1558     }
1559
1560     // shuffle A
1561     ts--; td = t;       // ts=top-1, td = top
1562     slots[td] = slots[ts];
1563     isRef.set( td, isRef.get(ts));
1564     if (attrs != null){
1565       attrs[td] = attrs[ts];
1566     }
1567
1568     // shuffle B
1569     td = ts;            // td=top-1
1570     slots[td] = b;
1571     isRef.set( td, bRef);
1572     if (attrs != null){
1573       attrs[td] = bAnn;
1574     }
1575
1576     top++;
1577   }
1578
1579   public void dup_x2 () {
1580     // .. A B C     =>
1581     // .. C A B C
1582     //        ^
1583
1584     int c;
1585     boolean cRef;
1586     Object cAnn = null;
1587     int ts, td;
1588     int t = top;
1589
1590     // duplicate C
1591     ts = t; td = t+1;
1592     slots[td] = c = slots[ts];
1593     cRef = isRef.get(ts);
1594     isRef.set( td, cRef);
1595     if (attrs != null){
1596       attrs[td] = cAnn = attrs[ts];
1597     }
1598
1599     // shuffle B
1600     td = ts; ts--;               // td=top, ts=top-1
1601     slots[td] = slots[ts];
1602     isRef.set( td, isRef.get(ts));
1603     if (attrs != null){
1604       attrs[td] = attrs[ts];
1605     }
1606
1607     // shuffle A
1608     td=ts; ts--;                 // td=top-1, ts=top-2
1609     slots[td] = slots[ts];
1610     isRef.set( td, isRef.get(ts));
1611     if (attrs != null){
1612       attrs[td] = attrs[ts];
1613     }
1614
1615     // shuffle C
1616     td = ts;                     // td = top-2
1617     slots[td] = c;
1618     isRef.set(td, cRef);
1619     if (attrs != null){
1620       attrs[td] = cAnn;
1621     }
1622
1623     top++;
1624   }
1625
1626   /**
1627    * to be used to check if a StackFrame got cloned due to its execution
1628    * changing attributes and/or slots, but otherwise represents the same
1629    * execution
1630    */
1631   public boolean originatesFrom (StackFrame other){
1632     if (other == this){
1633       return true;
1634     } else {
1635       return ((mi == other.mi) &&
1636               (prev == other.prev) &&
1637               (top == other.top) &&
1638               (getClass() == other.getClass()));
1639     }
1640   }
1641   
1642   
1643   // <2do> pcm - I assume this compares snapshots, not types. Otherwise it
1644   // would be pointless to equals stack/local values
1645   @Override
1646   public boolean equals (Object o) {
1647     if (o instanceof StackFrame){
1648       StackFrame other = (StackFrame)o;
1649
1650       if (prev != other.prev) {
1651         return false;
1652       }
1653       if (pc != other.pc) {
1654         return false;
1655       }
1656       if (mi != other.mi) {
1657         return false;
1658       }
1659       if (top != other.top){
1660         return false;
1661       }
1662
1663       int[] otherSlots = other.slots;
1664       FixedBitSet otherIsRef = other.isRef;
1665       for (int i=0; i<=top; i++){
1666         if ( slots[i] != otherSlots[i]){
1667           return false;
1668         }
1669         if ( isRef.get(i) != otherIsRef.get(i)){
1670           return false;
1671         }
1672       }
1673
1674       if (!Misc.compare(top,attrs,other.attrs)){
1675         return false;
1676       }
1677       
1678       if (!ObjectList.equals(frameAttr, other.frameAttr)){
1679         return false;
1680       }
1681
1682       return true;
1683     }
1684
1685     return false;
1686   }
1687   
1688   public boolean hasAnyRef () {
1689     return isRef.cardinality() > 0;
1690   }
1691   
1692   public int mixinExecutionStateHash (int h) {
1693     h = OATHash.hashMixin( h, mi.getGlobalId());
1694     
1695     if (pc != null){
1696       h = OATHash.hashMixin(h, pc.getInstructionIndex());
1697       // we don't need the bytecode since there can only be one insn with this index in this method
1698     }
1699     
1700     for (int i=0; i<top; i++) {
1701       h = OATHash.hashMixin(h, slots[i]);
1702     }
1703    
1704     return h;
1705   }
1706
1707   protected void hash (HashData hd) {
1708     if (prev != null){
1709       hd.add(prev.objectHashCode());
1710     }
1711     hd.add(mi.getGlobalId());
1712
1713     if (pc != null){
1714       hd.add(pc.getInstructionIndex());
1715     }
1716
1717     for (int i=0; i<=top; i++){
1718       hd.add(slots[i]);
1719     }
1720
1721     isRef.hash(hd);
1722
1723     // it's debatable if we add the attributes to the state, but whatever it
1724     // is, it should be kept consistent with the Fields.hash()
1725     if (attrs != null){
1726       for (int i=0; i<=top; i++){
1727         ObjectList.hash( attrs[i], hd);
1728       }
1729     }
1730     
1731     if (frameAttr != null){
1732       ObjectList.hash(frameAttr, hd);
1733     }
1734   }
1735
1736   // computes an hash code for the hash table
1737   // the default hash code is different for each object
1738   // we need to redifine it to make the hash table work
1739   @Override
1740   public int hashCode () {
1741     HashData hd = new HashData();
1742     hash(hd);
1743     return hd.getValue();
1744   }
1745
1746   /**
1747    * mark all objects reachable from local or operand stack positions containing
1748    * references. Done during phase1 marking of threads (the stack is one of the
1749    * Thread gc roots)
1750    */
1751   public void markThreadRoots (Heap heap, int tid) {
1752
1753     /**
1754     for (int i = isRef.nextSetBit(0); i>=0 && i<=top; i = isRef.nextSetBit(i + 1)) {
1755       int objref = slots[i];
1756       if (objref != MJIEnv.NULL) {
1757         heap.markThreadRoot(objref, tid);
1758       }
1759     }
1760     **/
1761     for (int i = 0; i <= top; i++) {
1762       if (isRef.get(i)) {
1763         int objref = slots[i];
1764         if (objref != MJIEnv.NULL) {
1765           heap.markThreadRoot(objref, tid);
1766         }
1767       }
1768     }
1769   }
1770
1771   //--- debugging methods
1772
1773   public void printOperands (PrintStream pw){
1774     pw.print("operands = [");
1775     for (int i=stackBase; i<=top; i++){
1776       if (i>0){
1777         pw.print(',');
1778       }
1779       if (isOperandRef(i)){
1780         pw.print('^');
1781       }
1782       pw.print(slots[i]);
1783       Object a = getOperandAttr(top-i);
1784       if (a != null){
1785         pw.print(" {");
1786         pw.print(a);
1787         pw.print('}');
1788       }
1789     }
1790     pw.println(']');
1791   }
1792
1793   /**
1794    * this includes locals and pc
1795    */
1796   public void printStackContent () {
1797     PrintStream pw = System.out;
1798
1799     pw.print( "\tat ");
1800     pw.print( mi.getFullName());
1801
1802     if (pc != null) {
1803       pw.println( ":" + pc.getPosition());
1804     } else {
1805       pw.println();
1806     }
1807
1808     pw.print("\t slots: ");
1809     for (int i=0; i<=top; i++){
1810       if (i == stackBase){
1811         pw.println("\t      ----------- operand stack");
1812       }
1813
1814       pw.print( "\t    [");
1815       pw.print(i);
1816       pw.print("] ");
1817       if (isRef.get(i)) {
1818         pw.print( "@");
1819       }
1820       pw.print( slots[i]);
1821
1822       if (attrs != null){
1823         pw.print("  attr=");
1824         pw.print(attrs[i]);
1825       }
1826
1827       pw.println();
1828     }
1829   }
1830
1831   public void printStackTrace () {
1832     System.out.println( getStackTraceInfo());
1833   }
1834
1835   public void swap () {
1836     int t = top-1;
1837
1838     int v = slots[top];
1839     boolean isTopRef = isRef.get(top);
1840
1841     slots[top] = slots[t];
1842     isRef.set( top, isRef.get(t));
1843
1844     slots[t] = v;
1845     isRef.set( t, isTopRef);
1846
1847     if (attrs != null){
1848       Object a = attrs[top];
1849       attrs[top] = attrs[t];
1850       attrs[t] = a;
1851     }
1852   }
1853
1854   protected void printContentsOn(PrintWriter pw){
1855     pw.print("isFrozen=");
1856     pw.print(isFrozen());
1857     pw.print(",mi=");
1858     pw.print( mi != null ? mi.getUniqueName() : "null");
1859     pw.print(",top="); pw.print(top);
1860     pw.print(",slots=[");
1861
1862     for (int i = 0; i <= top; i++) {
1863       if (i == stackBase){
1864         pw.print("||");
1865       } else {
1866         if (i != 0) {
1867           pw.print(',');
1868         }
1869       }
1870
1871       if (isRef.get(i)){
1872         pw.print('@');
1873       }
1874       pw.print(slots[i]);
1875
1876       if (attrs != null && attrs[i] != null) {
1877         pw.print('(');
1878         pw.print(attrs[i]);
1879         pw.print(')');
1880       }
1881     }
1882
1883     pw.print("],pc=");
1884     pw.print(pc != null ? pc.getPosition() : "null");
1885
1886     pw.print(']');
1887
1888   }
1889   
1890   // <2do> there are way too many different print/debug methods here
1891   public void printSlots (PrintStream ps){
1892     for (int i = 0; i <= top; i++) {
1893       if (i == stackBase){
1894         ps.print("||");
1895       } else {
1896         if (i != 0) {
1897           ps.print(',');
1898         }
1899       }
1900
1901       if (isRef.get(i)){
1902         PrintUtils.printReference(ps, slots[i]);
1903       } else {
1904         ps.print(slots[i]);
1905       }
1906     }    
1907   }
1908
1909   public int getDepth(){
1910     int depth = 0;
1911     
1912     for (StackFrame frame = prev; frame != null; frame = frame.prev){
1913       depth++;
1914     }
1915     
1916     return depth;
1917   }
1918   
1919   protected int objectHashCode() {
1920     return super.hashCode();
1921   }
1922
1923   @Override
1924   public String toString () {
1925     StringWriter sw = new StringWriter(128);
1926     PrintWriter pw = new PrintWriter(sw);
1927
1928     pw.print(getClass().getSimpleName() + '{');
1929     //pw.print(Integer.toHexString(objectHashCode()));
1930     printContentsOn(pw);
1931     pw.print('}');
1932
1933     return sw.toString();
1934   }
1935
1936   public float peekFloat() {
1937     return Float.intBitsToFloat(slots[top]);
1938   }
1939
1940   public float peekFloat (int offset){
1941     return Float.intBitsToFloat(slots[top-offset]);    
1942   }
1943   
1944   public double peekDouble() {
1945     int i = top;
1946     return Types.intsToDouble( slots[i], slots[i-1]);
1947   }
1948   
1949   public double peekDouble (int offset){
1950     int i = top-offset;
1951     return Types.intsToDouble( slots[i], slots[i-1]);
1952   }
1953   
1954   public long peekLong () {
1955     int i = top;
1956     return Types.intsToLong( slots[i], slots[i-1]);
1957   }
1958
1959   public long peekLong (int offset) {
1960     int i = top - offset;
1961     return Types.intsToLong( slots[i], slots[i-1]);
1962   }
1963
1964   public void pushLong (long v) {
1965     push( (int) (v>>32));
1966     push( (int) v);
1967   }
1968
1969   public void pushDouble (double v) {
1970     long l = Double.doubleToLongBits(v);
1971     push( (int) (l>>32));
1972     push( (int) l);
1973   }
1974
1975   public void pushFloat (float v) {
1976     push( Float.floatToIntBits(v));
1977   }
1978   
1979   public double popDouble () {
1980     int i = top;
1981
1982     int lo = slots[i--];
1983     int hi = slots[i--];
1984
1985     if (attrs != null){
1986       i = top;
1987       attrs[i--] = null; // not really required
1988       attrs[i--] = null; // that's where the attribute should be
1989     }
1990
1991     top = i;
1992     return Types.intsToDouble(lo, hi);
1993   }
1994
1995   public long popLong () {
1996     int i = top;
1997
1998     int lo = slots[i--];
1999     int hi = slots[i--];
2000
2001     if (attrs != null){
2002       i = top;
2003       attrs[i--] = null; // not really required
2004       attrs[i--] = null; // that's where the attribute should be
2005     }
2006
2007     top = i;
2008     return Types.intsToLong(lo, hi);
2009   }
2010
2011   public int peek () {
2012     return slots[top];
2013   }
2014
2015   public int peek (int offset) {
2016     return slots[top-offset];
2017   }
2018
2019   public void removeArguments (MethodInfo mi) {
2020     int i = mi.getArgumentsSize();
2021
2022     if (i != 0) {
2023       pop(i);
2024     }
2025   }
2026   
2027   public void pop (int n) {
2028     //assert (top >= stackBase) : "stack empty";
2029
2030     int t = top - n;
2031
2032     // <2do> get rid of this !
2033     for (int i=top; i>t; i--) {
2034       if (isRef.get(i) && (slots[i] != MJIEnv.NULL)) {
2035         VM.getVM().getSystemState().activateGC();
2036         break;
2037       }
2038     }
2039
2040     if (attrs != null){  // just to avoid memory leaks
2041       for (int i=top; i>t; i--){
2042         attrs[i] = null;
2043       }
2044     }
2045
2046     top = t;
2047   }
2048
2049   public float popFloat() {    
2050     int v = slots[top];
2051
2052     if (attrs != null){ // just to avoid memory leaks
2053       attrs[top] = null;
2054     }
2055
2056     top--;
2057
2058     return Float.intBitsToFloat(v);
2059   }
2060   
2061   public int pop () {
2062     //assert (top >= stackBase) : "stack empty";
2063     
2064     int v = slots[top];
2065
2066     // <2do> get rid of this
2067     if (isRef.get(top)) {
2068       if (v != MJIEnv.NULL) {
2069         VM.getVM().getSystemState().activateGC();
2070       }
2071     }
2072
2073     if (attrs != null){ // just to avoid memory leaks
2074       attrs[top] = null;
2075     }
2076
2077     top--;
2078
2079     // note that we don't reset the operands or oRefs values, so that
2080     // we can still access them after the insn doing the pop got executed
2081     // (e.g. useful for listeners)
2082
2083     return v;
2084   }
2085   
2086   public void pushLocal (int index) {
2087     top++;
2088     slots[top] = slots[index];
2089     isRef.set(top, isRef.get(index));
2090
2091     if (attrs != null){
2092       attrs[top] = attrs[index];
2093     }
2094   }
2095
2096   public void pushLongLocal (int index){
2097     int t = top;
2098
2099     slots[++t] = slots[index];
2100     isRef.clear(t);
2101     slots[++t] = slots[index+1];
2102     isRef.clear(t);
2103
2104     if (attrs != null){
2105       attrs[t-1] = attrs[index];
2106       attrs[t] = null;
2107     }
2108
2109     top = t;
2110   }
2111
2112   public void storeOperand (int index){
2113     slots[index] = slots[top];
2114     isRef.set( index, isRef.get(top));
2115
2116     if (attrs != null){
2117       attrs[index] = attrs[top];
2118       attrs[top] = null;
2119     }
2120
2121     top--;
2122   }
2123
2124   public void storeLongOperand (int index){
2125     int t = top-1;
2126     int i = index;
2127
2128     slots[i] = slots[t];
2129     isRef.clear(i);
2130
2131     slots[++i] = slots[t+1];
2132     isRef.clear(i);
2133
2134     if (attrs != null){
2135       attrs[index] = attrs[t]; // its in the lower word
2136       attrs[i] = null;
2137
2138       attrs[t] = null;
2139       attrs[t+1] = null;
2140     }
2141
2142     top -=2;
2143   }
2144
2145   public void push (int v){
2146     top++;
2147     slots[top] = v;
2148     isRef.clear(top);
2149
2150     //if (attrs != null){ // done on pop
2151     //  attrs[top] = null;
2152     //}
2153   }
2154
2155   public void pushRef (int ref){
2156     top++;
2157     slots[top] = ref;
2158     isRef.set(top);
2159
2160     //if (attrs != null){ // done on pop
2161     //  attrs[top] = null;
2162     //}
2163
2164     if (ref != MJIEnv.NULL) {
2165       VM.getVM().getSystemState().activateGC();
2166     }
2167   }
2168
2169   public void push (int v, boolean ref) {
2170     top++;
2171     slots[top] = v;
2172     isRef.set(top, ref);
2173
2174     //if (attrs != null){ // done on pop
2175     //  attrs[top] = null;
2176     //}
2177
2178     if (ref && (v != MJIEnv.NULL)) {
2179       VM.getVM().getSystemState().activateGC();
2180     }
2181   }
2182
2183   // return the value of callerSlots variable given the name
2184   public int getLocalVariableSlotIndex (String name) {
2185     LocalVarInfo lv = mi.getLocalVar(name, pc.getPosition());
2186
2187     if (lv != null){
2188       return lv.getSlotIndex();
2189     }
2190
2191     return -1;
2192   }
2193
2194   //--- abstract argument & return passing that can have VM dependend implementation
2195   
2196   public void setReferenceResult (int ref, Object attr){
2197     pushRef(ref);
2198     if (attr != null){
2199       setOperandAttr(attr);
2200     }
2201   }
2202   
2203   public void setResult (int r, Object attr){
2204     push(r);
2205     if (attr != null){
2206       setOperandAttr(attr);
2207     }    
2208   }
2209   
2210   public void setResult (long r, Object attr){
2211     pushLong(r);
2212     if (attr != null){
2213       setLongOperandAttr(attr);
2214     }    
2215   }
2216   
2217   public int getResult(){
2218     return pop();
2219   }
2220   
2221   public long getLongResult(){
2222     return popLong();
2223   }
2224
2225   public int getReferenceResult () {
2226     return pop();
2227   }
2228   
2229   public Object getResultAttr () {
2230     return getOperandAttr();
2231   }
2232
2233   public Object getLongResultAttr () {
2234     return getLongOperandAttr();
2235   }
2236   
2237   public float getFloatResult(){
2238     return Float.intBitsToFloat(getResult());    
2239   }
2240   public double getDoubleResult(){
2241     return Double.longBitsToDouble(getLongResult());
2242   }
2243   public Object getFloatResultAttr(){
2244     return getResultAttr();
2245   }
2246   public Object getDoubleResultAttr(){
2247     return getLongResultAttr();
2248   }
2249
2250   
2251   //--- VM independent exception handler setup
2252   
2253   public void setExceptionReference (int exRef){
2254     pushRef(exRef);
2255   }
2256   
2257   public int getExceptionReference (){
2258     return pop();
2259   }
2260   
2261   public void setExceptionReferenceAttribute (Object attr){
2262     setOperandAttr(attr);
2263   }
2264   
2265   public Object getExceptionReferenceAttribute (){
2266     return getOperandAttr();
2267   }
2268   
2269   
2270   // those set the local vars that are normally initialized from call arguments
2271   public abstract void setArgumentLocal (int idx, int value, Object attr);
2272   public abstract void setLongArgumentLocal (int idx, long value, Object attr);
2273   public abstract void setReferenceArgumentLocal (int idx, int ref, Object attr);
2274
2275   public void setFloatArgumentLocal (int idx, float value, Object attr){
2276     setArgumentLocal( idx, Float.floatToIntBits(value), attr);
2277   }
2278   public void setDoubleArgumentLocal (int idx, double value, Object attr){
2279     setLongArgumentLocal( idx, Double.doubleToLongBits(value), attr);
2280   }
2281 }