c34e1c104977ea82eb09c0f474bc0e0a60856992
[jpf-core.git] / src / main / gov / nasa / jpf / vm / ElementInfo.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.Config;
21 import gov.nasa.jpf.JPFException;
22 import gov.nasa.jpf.util.HashData;
23 import gov.nasa.jpf.util.ObjectList;
24 import gov.nasa.jpf.util.Processor;
25
26 import java.io.PrintWriter;
27
28 /**
29  * Describes an element of memory containing the field values of a class or an
30  * object. In the case of a class, contains the values of the static fields. For
31  * an object contains the values of the object fields.
32  *
33  * @see gov.nasa.jpf.vm.FieldInfo
34  */
35 public abstract class ElementInfo implements Cloneable {
36
37
38   //--- the lower 2 bytes of the attribute field are sticky (state stored/restored)
39
40   // object attribute flag values
41
42   // the first 8 bits constitute an unsigned pinDown count
43   public static final int   ATTR_PINDOWN_MASK = 0xff;
44
45   // this ElementInfo is not allowed to be modified anymore since it has been state stored
46   // ElementInfos are constructed in a non-frozen state, and will be frozen upon
47   // heap store (usuall in the heap or ElementInfo memento ctor)
48   // This is the basis for lifting the change management from the fields level (fields,monitor,attributes)
49   // to the ElementInfo object level
50   public static final int   ATTR_IS_FROZEN     = 0x100;
51
52   // object has an immutable type (no field value change)
53   public static final int   ATTR_IMMUTABLE     = 0x200;
54
55   // The constructor for the object has returned.  Final fields can no longer break POR
56   // This attribute is set in gov.nasa.jpf.vm.bytecode.RETURN.enter().
57   // If ThreadInfo.usePorSyncDetection() is false, then this attribute is never set.
58   public static final int   ATTR_CONSTRUCTED   = 0x400;
59
60   // object has been added to finalizer queue
61   public static final int ATTR_FINALIZED = 0x800;
62   
63   public static final int   ATTR_EXPOSED       = 0x1000;
64   
65   // object is shared between threads
66   public static final int   ATTR_SHARED        = 0x4000;
67   
68   // ATTR_SHARED is frozen (has to be changed explicitly, will not be updated by checkUpdatedSharedness)
69   public static final int   ATTR_FREEZE_SHARED = 0x8000; 
70   
71   
72   //--- the upper two bytes are for transient (heap internal) use only, and are not stored
73
74   // BEWARE if you add or change values, make sure these are not used in derived classes !
75   // <2do> this is efficient but fragile
76
77   public static final int   ATTR_TREF_CHANGED       = 0x10000; // referencingThreads have changed
78   public static final int   ATTR_FLI_CHANGED        = 0x20000; // fieldLockInfos changed
79   public static final int   ATTR_ATTRIBUTE_CHANGED  = 0x80000; // refers only to sticky bits
80
81   
82   //--- useful flag sets & masks
83
84   static final int   ATTR_STORE_MASK = 0x0000ffff;
85
86   static final int   ATTR_ANY_CHANGED = (ATTR_TREF_CHANGED | ATTR_FLI_CHANGED | ATTR_ATTRIBUTE_CHANGED);
87
88   // transient flag set if object is reachable from root object, i.e. can't be recycled
89   public static final int   ATTR_IS_MARKED   = 0x80000000;
90   
91   // this bit is set/unset by the heap in order to identify live objects that have
92   // already been unmarked. This is to avoid additional passes over the whole heap in
93   // order to clean up dangling references etc.
94   // NOTE - this bit should never be state stored - restored ElementInfo should never have it set
95   public static final int   ATTR_LIVE_BIT    = 0x40000000;
96   
97   public static final int   ATTR_MARKED_OR_LIVE_BIT = (ATTR_IS_MARKED | ATTR_LIVE_BIT);
98
99
100   //--- instance fields
101
102   protected ClassInfo       ci;
103   protected Fields          fields;
104   protected Monitor         monitor;
105   
106   // the set of threads using this object. Note this is not used for state matching
107   // so that order or thread id do not have a direct impact on heap symmetry
108   protected ThreadInfoSet referencingThreads;
109
110   // this is where we keep track of lock sets that potentially protect field access
111   // of shared objects. Since usually only a subset of objects are shared, we
112   // initialize this on demand
113   protected FieldLockInfo[] fLockInfo;
114
115   
116   // this is the reference value for the object represented by this ElementInfo
117   // (note this is a slight misnomer for StaticElementInfos, which don't really
118   // represent objects but collections of static fields belonging to the same class)
119   protected int objRef;
120
121   // these are our state-stored object attributes
122   // WATCH OUT! only include info that otherwise reflects a state change, so
123   // that we don't introduce new changes. Its value is used to hash the state!
124   // <2do> what a pity - 32 stored bits for (currently) only 2 bits of
125   // information,but we might use this as a hash for more complex reference
126   // info in the future.
127   // We distinguish between propagates and private object attributes, the first
128   // one stored in the lower 2 bytes
129   protected int attributes;
130
131   //--- the following fields are transient or search global, i.e. their values
132   // are not state-stored, but might be set during state restoration
133
134   // cache for unchanged ElementInfos, so that we don't have to re-create cachedMemento
135   // objects all the time
136   protected Memento<ElementInfo> cachedMemento;
137
138   // cache for a serialized representation of the object, which can be used
139   // by state-matching. Value interpretation depends on the configured Serializer
140   protected long sid;
141
142
143   // helpers for state storage/restore processing, to avoid explicit iterators on
144   // respective ElementInfo containers (heap,statics)
145   
146   static class Restorer implements Processor<ElementInfo> {
147     @Override
148     public void process (ElementInfo ei) {
149       ei.attributes &= ElementInfo.ATTR_STORE_MASK;
150       ei.sid = 0;
151       ei.updateLockingInfo();
152       ei.markUnchanged();
153     }        
154   }
155   static Restorer restorer = new Restorer();
156   
157   static class Storer implements Processor<ElementInfo> {
158     @Override
159     public void process (ElementInfo ei) {
160       ei.freeze();
161     }
162   }
163   static Storer storer = new Storer();
164   
165   
166   static boolean init (Config config) {
167     return true;
168   }
169
170   protected ElementInfo (int id, ClassInfo c, Fields f, Monitor m, ThreadInfo ti) {
171     objRef = id;
172     ci = c;
173     fields = f;
174     monitor = m;
175
176     assert ti != null; // we need that for our POR
177   }
178
179   public abstract ElementInfo getModifiableInstance();
180     
181   // not ideal, a sub-type checker.
182   public abstract boolean isObject();
183
184   public abstract boolean hasFinalizer();
185   
186   protected ElementInfo() {
187   }
188
189   public boolean hasChanged() {
190     return !isFrozen();
191     //return (attributes & ATTR_ANY_CHANGED) != 0;
192   }
193
194   @Override
195   public String toString() {
196     return ((ci != null ? ci.getName() : "ElementInfo") + '@' + Integer.toHexString(objRef));
197   }
198
199   public FieldLockInfo getFieldLockInfo (FieldInfo fi) {
200     if (fLockInfo != null){
201       return fLockInfo[fi.getFieldIndex()];
202     } else {
203       return null;
204     }
205   }
206
207   public void setFieldLockInfo (FieldInfo fi, FieldLockInfo flInfo) {
208     checkIsModifiable();
209
210     if (fLockInfo == null){
211       fLockInfo = new FieldLockInfo[getNumberOfFields()];
212     }
213     
214     fLockInfo[fi.getFieldIndex()] = flInfo;
215     attributes |= ATTR_FLI_CHANGED;
216   }
217   
218   public boolean isLockProtected (FieldInfo fi){
219     if (fLockInfo != null){
220       FieldLockInfo fli = fLockInfo[fi.getFieldIndex()];
221       if (fli != null){
222         return fli.isProtected();
223       }
224     }
225     
226     return false;
227   }
228
229   /**
230    * object is recycled (after potential finalization)
231    */
232   public void processReleaseActions(){
233     // type based release actions
234     ci.processReleaseActions(this);
235     
236     // instance based release actions
237     if (fields.hasObjectAttr()){
238       for (ReleaseAction action : fields.objectAttrIterator(ReleaseAction.class)){
239         action.release(this);
240       }
241     }
242   }
243
244   /**
245    * post transition live object cleanup
246    * update all non-fields references used by this object. This is only called
247    * at the end of the gc, and recycled objects should be either null or not marked
248    */
249   void cleanUp (Heap heap, boolean isThreadTermination, int tid) {
250     if (fLockInfo != null) {
251       for (int i=0; i<fLockInfo.length; i++) {
252         FieldLockInfo fli = fLockInfo[i];
253         if (fli != null) {
254           fLockInfo[i] = fli.cleanUp(heap);
255         }
256       }
257     }
258   }
259   
260   
261   //--- sids are only supposed to be used by the Serializer
262   public void setSid(long id){
263     sid = id;
264   }
265
266   public long getSid() {
267     return sid;
268   }
269
270   //--- cached mementos are only supposed to be used/set by the Restorer
271
272   public Memento<ElementInfo> getCachedMemento(){
273     return cachedMemento;
274   }
275
276   public void setCachedMemento (Memento<ElementInfo> memento){
277     cachedMemento = memento;
278   }
279
280   /**
281    * do we have a reference field with value objRef?
282    */
283   public boolean hasRefField (int objRef) {
284     return ci.hasRefField( objRef, fields);
285   }
286
287
288   public int numberOfUserThreads() {
289     return referencingThreads.size();
290   }
291
292
293   /**
294    * the recursive phase2 marker entry, which propagates the attributes set by a
295    * previous phase1. This one is called on all 'root'-marked objects after
296    * phase1 is completed. ElementInfo is not an ideal place for this method, as
297    * it has to access some innards of both ClassInfo (FieldInfo container) and
298    * Fields. But on the other hand, we want to keep the whole heap traversal
299    * business as much centralized in ElementInfo and Heap implementors
300    */
301   void markRecursive(Heap heap) {
302     int i, n;
303
304     if (isArray()) {
305       if (fields.isReferenceArray()) {
306         n = ((ArrayFields)fields).arrayLength();
307         for (i = 0; i < n; i++) {
308           int objref = fields.getReferenceValue(i);
309           if (objref != MJIEnv.NULL){
310             heap.queueMark( objref);
311           }
312         }
313       }
314
315     } else { // not an array
316       ClassInfo ci = getClassInfo();
317       boolean isWeakRef = ci.isWeakReference();
318
319       do {
320         n = ci.getNumberOfDeclaredInstanceFields();
321         boolean isRef = isWeakRef && ci.isReferenceClassInfo(); // is this the java.lang.ref.Reference part?
322
323         for (i = 0; i < n; i++) {
324           FieldInfo fi = ci.getDeclaredInstanceField(i);
325           if (fi.isReference()) {
326             if ((i == 0) && isRef) {
327               // we need to reset the ref field once the referenced object goes away
328               // NOTE: only the *first* WeakReference field is a weak ref
329               // (this is why we have our own implementation)
330               heap.registerWeakReference(this);
331             } else {
332               int objref = fields.getReferenceValue(fi.getStorageOffset());
333               if (objref != MJIEnv.NULL){
334                 heap.queueMark( objref);
335               }
336             }
337           }
338         }
339         ci = ci.getSuperClass();
340       } while (ci != null);
341     }
342   }
343
344
345   int getAttributes () {
346     return attributes;
347   }
348
349   int getStoredAttributes() {
350     return attributes & ATTR_STORE_MASK;
351   }
352
353   public boolean isImmutable() {
354     return ((attributes & ATTR_IMMUTABLE) != 0);
355   }
356
357   //--- freeze handling
358   
359   public void freeze() {
360     attributes |= ATTR_IS_FROZEN;
361   }
362
363   public void defreeze() {
364     attributes &= ~ATTR_IS_FROZEN;
365   }
366   
367   public boolean isFrozen() {
368     return ((attributes & ATTR_IS_FROZEN) != 0);    
369   }
370   
371   //--- shared handling
372
373   /**
374    * set the referencing threads. Unless you know this is from a non-shared
375    * context, make sure to update sharedness by calling setShared()
376    */
377   public void setReferencingThreads (ThreadInfoSet refThreads){
378     checkIsModifiable();    
379     referencingThreads = refThreads;
380   }
381   
382   public ThreadInfoSet getReferencingThreads (){
383     return referencingThreads;
384   }
385   
386   public void freezeSharedness (boolean freeze) {
387     if (freeze) {
388       if ((attributes & ATTR_FREEZE_SHARED) == 0) {
389         checkIsModifiable();
390         attributes |= (ATTR_FREEZE_SHARED | ATTR_ATTRIBUTE_CHANGED);
391       }
392     } else {
393       if ((attributes & ATTR_FREEZE_SHARED) != 0) {
394         checkIsModifiable();
395         attributes &= ~ATTR_FREEZE_SHARED;
396         attributes |= ATTR_ATTRIBUTE_CHANGED;
397       }
398     }
399   }
400   
401   public boolean isSharednessFrozen () {
402     return (attributes & ATTR_FREEZE_SHARED) != 0;
403   }
404   
405   public boolean isShared() {
406     //return usingTi.getNumberOfLiveThreads() > 1;
407     return ((attributes & ATTR_SHARED) != 0);
408   }
409   
410   public void setShared (ThreadInfo ti, boolean isShared) {
411     if (isShared) {
412       if ((attributes & ATTR_SHARED) == 0) {
413         checkIsModifiable();
414         attributes |= (ATTR_SHARED | ATTR_ATTRIBUTE_CHANGED);
415         
416         // note we don't report the thread here since this is explicitly set via Verify.setShared
417         VM.getVM().notifyObjectShared(ti, this);
418       }
419     } else {
420       if ((attributes & ATTR_SHARED) != 0) {
421         checkIsModifiable();
422         attributes &= ~ATTR_SHARED;
423         attributes |= ATTR_ATTRIBUTE_CHANGED;
424       }
425     }
426   }
427   
428   /**
429    * NOTE - this should only be called internally if we know the object is
430    * modifiable (e.g. from the ctor)
431    */
432   void setSharednessFromReferencingThreads () {
433     if (referencingThreads.isShared( null, this)) {
434       if ((attributes & ATTR_SHARED) == 0) {
435         checkIsModifiable();
436         attributes |= (ATTR_SHARED | ATTR_ATTRIBUTE_CHANGED);
437       }
438     }
439   }
440   
441   public boolean isReferencedBySameThreads (ElementInfo eiOther) {
442     return referencingThreads.equals(eiOther.referencingThreads);
443   }
444   
445   public boolean isReferencedByThread (ThreadInfo ti) {
446     return referencingThreads.contains(ti);
447   }
448   
449   public boolean isExposed(){
450     return (attributes & ATTR_EXPOSED) != 0;
451   }
452   
453   public boolean isExposedOrShared(){
454     return (attributes & (ATTR_SHARED | ATTR_EXPOSED)) != 0;
455   }
456   
457   public ElementInfo getExposedInstance (ThreadInfo ti, ElementInfo eiFieldOwner){
458     ElementInfo ei = getModifiableInstance();
459     ei.setExposed( ti, eiFieldOwner);
460     
461     // <2do> do we have to traverse every object reachable from here?
462     // (does every reference of an indirectly exposed object have to go through this one?)
463     
464     return ei;
465   }
466   
467   protected void setExposed (){
468     attributes |= (ATTR_EXPOSED | ATTR_ATTRIBUTE_CHANGED);
469   }
470   
471   public void setExposed (ThreadInfo ti, ElementInfo eiFieldOwner){
472     // we actually have to add this to the attributes to avoid endless loops by
473     // re-exposing the same object along a given path
474     attributes |= (ATTR_EXPOSED | ATTR_ATTRIBUTE_CHANGED);
475     
476     ti.getVM().notifyObjectExposed(ti, eiFieldOwner, this);
477   }
478   
479   /**
480    * this is called before the system attempts to reclaim the object. If
481    * we return 'false', the object will *not* be removed
482    */
483   protected boolean recycle () {  
484     // this is required to avoid loosing field lock assumptions
485     // when the system sequentialized threads with conflicting assumptions,
486     // but the offending object goes out of scope before the system backtracks
487     if (hasVolatileFieldLockInfos()) {
488       return false;
489     }
490
491     setObjectRef(MJIEnv.NULL);
492
493     return true;
494   }
495
496   boolean hasVolatileFieldLockInfos() {
497     if (fLockInfo != null) {
498       for (int i=0; i<fLockInfo.length; i++) {
499         FieldLockInfo fli = fLockInfo[i];
500         if (fli != null) {
501           if (fli.needsPindown(this)) {
502             return true;
503           }
504         }
505       }
506     }
507
508     return false;
509   }
510   
511   public void hash(HashData hd) {
512     hd.add(ci.getClassLoaderInfo().getId());
513     hd.add(ci.getId());
514     fields.hash(hd);
515     monitor.hash(hd);
516     hd.add(attributes & ATTR_STORE_MASK);
517   }
518
519   @Override
520   public int hashCode() {
521     HashData hd = new HashData();
522
523     hash(hd);
524
525     return hd.getValue();
526   }
527
528   @Override
529   public boolean equals(Object o) {
530     if (o != null && o instanceof ElementInfo) {
531       ElementInfo other = (ElementInfo) o;
532
533       if (ci != other.ci){
534         return false;
535       }
536
537       if ((attributes & ATTR_STORE_MASK) != (other.attributes & ATTR_STORE_MASK)){
538         return false;
539       }
540       if (!fields.equals(other.fields)) {
541         return false;
542       }
543       if (!monitor.equals(other.monitor)){
544         return false;
545       }
546       if (referencingThreads != other.referencingThreads){
547         return false;
548       }
549
550       return true;
551
552     } else {
553       return false;
554     }
555   }
556
557   public ClassInfo getClassInfo() {
558     return ci;
559   }
560
561   abstract protected FieldInfo getDeclaredFieldInfo(String clsBase, String fname);
562
563   abstract protected FieldInfo getFieldInfo(String fname);
564
565   protected abstract int getNumberOfFieldsOrElements();
566
567   
568   //--- Object attribute accessors
569
570   public boolean hasObjectAttr(){
571     return fields.hasObjectAttr();
572   }
573   
574   public boolean hasObjectAttr(Class<?> attrType) {
575     return fields.hasObjectAttr(attrType);
576   }
577
578   /**
579    * this returns all of them - use either if you know there will be only
580    * one attribute at a time, or check/process result with ObjectList
581    */
582   public Object getObjectAttr(){
583     return fields.getObjectAttr();
584   }
585   
586   /**
587    * this replaces all of them - use only if you know 
588    *  - there will be only one attribute at a time
589    *  - you obtained the value you set by a previous getXAttr()
590    *  - you constructed a multi value list with ObjectList.createList()
591    */
592   public void setObjectAttr (Object a){
593     checkIsModifiable();
594     fields.setObjectAttr(a);
595   }
596
597   /**
598    * this replaces all of them - use only if you know 
599    *  - there will be only one attribute at a time
600    *  - you obtained the value you set by a previous getXAttr()
601    *  - you constructed a multi value list with ObjectList.createList()
602    */
603   public void setObjectAttrNoClone (Object a){
604     fields.setObjectAttr(a);
605   }
606
607   
608   public void addObjectAttr(Object a){
609     checkIsModifiable();
610     fields.addObjectAttr(a);
611   }
612   public void removeObjectAttr(Object a){
613     checkIsModifiable();
614     fields.removeObjectAttr(a);
615   }
616   public void replaceObjectAttr(Object oldAttr, Object newAttr){
617     checkIsModifiable();
618     fields.replaceObjectAttr(oldAttr, newAttr);
619   }
620
621   
622   /**
623    * this only returns the first attr of this type, there can be more
624    * if you don't use client private types or the provided type is too general
625    */
626   public <T> T getObjectAttr (Class<T> attrType) {
627     return fields.getObjectAttr(attrType);
628   }
629   public <T> T getNextObjectAttr (Class<T> attrType, Object prev) {
630     return fields.getNextObjectAttr(attrType, prev);
631   }
632   public ObjectList.Iterator objectAttrIterator(){
633     return fields.objectAttrIterator();
634   }
635   public <T> ObjectList.TypedIterator<T> objectAttrIterator(Class<T> type){
636     return fields.objectAttrIterator(type);
637   }
638   
639   //--- field attribute accessors
640   
641   public boolean hasFieldAttr() {
642     return fields.hasFieldAttr();
643   }
644
645   public boolean hasFieldAttr (Class<?> attrType){
646     return fields.hasFieldAttr(attrType);
647   }
648
649   /**
650    * this returns all of them - use either if you know there will be only
651    * one attribute at a time, or check/process result with ObjectList
652    */
653   public Object getFieldAttr (FieldInfo fi){
654     return fields.getFieldAttr(fi.getFieldIndex());
655   }
656
657   /**
658    * this replaces all of them - use only if you know 
659    *  - there will be only one attribute at a time
660    *  - you obtained the value you set by a previous getXAttr()
661    *  - you constructed a multi value list with ObjectList.createList()
662    */
663   public void setFieldAttr (FieldInfo fi, Object attr){
664     checkIsModifiable();
665     
666     int nFields = getNumberOfFieldsOrElements();
667     fields.setFieldAttr( nFields, fi.getFieldIndex(), attr);    
668   }
669
670   
671   public void addFieldAttr (FieldInfo fi, Object a){
672     checkIsModifiable();
673     
674     int nFields = getNumberOfFieldsOrElements();    
675     fields.addFieldAttr( nFields, fi.getFieldIndex(), a);
676   }
677   public void removeFieldAttr (FieldInfo fi, Object a){
678     checkIsModifiable();
679     fields.removeFieldAttr(fi.getFieldIndex(), a);
680   }
681   public void replaceFieldAttr (FieldInfo fi, Object oldAttr, Object newAttr){
682     checkIsModifiable();    
683     fields.replaceFieldAttr(fi.getFieldIndex(), oldAttr, newAttr);
684   }
685   
686   /**
687    * this only returns the first attr of this type, there can be more
688    * if you don't use client private types or the provided type is too general
689    */
690   public <T> T getFieldAttr (FieldInfo fi, Class<T> attrType) {
691     return fields.getFieldAttr(fi.getFieldIndex(), attrType);
692   }
693   public <T> T getNextFieldAttr (FieldInfo fi, Class<T> attrType, Object prev) {
694     return fields.getNextFieldAttr(fi.getFieldIndex(), attrType, prev);
695   }
696   public ObjectList.Iterator fieldAttrIterator (FieldInfo fi){
697     return fields.fieldAttrIterator(fi.getFieldIndex());
698   }
699   public <T> ObjectList.TypedIterator<T> fieldAttrIterator (FieldInfo fi, Class<T> type){
700     return fields.fieldAttrIterator(fi.getFieldIndex(), type);
701   }
702   
703
704   
705   //--- element attribute accessors
706   
707   public boolean hasElementAttr() {
708     return fields.hasFieldAttr();
709   }
710
711   public boolean hasElementAttr (Class<?> attrType){
712     return fields.hasFieldAttr(attrType);
713   }
714
715   
716   /**
717    * this returns all of them - use either if you know there will be only
718    * one attribute at a time, or check/process result with ObjectList
719    */
720   public Object getElementAttr (int idx){
721     return fields.getFieldAttr(idx);
722   }
723
724   /**
725    * this replaces all of them - use only if you know 
726    *  - there will be only one attribute at a time
727    *  - you obtained the value you set by a previous getXAttr()
728    *  - you constructed a multi value list with ObjectList.createList()
729    */
730   public void setElementAttr (int idx, Object attr){
731     int nElements = getNumberOfFieldsOrElements();
732     checkIsModifiable();
733     fields.setFieldAttr( nElements, idx, attr);
734   }
735
736   /**
737    * this replaces all of them - use only if you know 
738    *  - there will be only one attribute at a time
739    *  - you obtained the value you set by a previous getXAttr()
740    *  - you constructed a multi value list with ObjectList.createList()
741    */
742   public void setElementAttrNoClone (int idx, Object attr){
743     int nElements = getNumberOfFieldsOrElements();
744     fields.setFieldAttr(nElements,idx, attr);
745   }
746
747   
748   public void addElementAttr (int idx, Object a){
749     checkIsModifiable();
750     
751     int nElements = getNumberOfFieldsOrElements();   
752     fields.addFieldAttr( nElements, idx, a);
753   }
754   public void removeElementAttr (int idx, Object a){
755     checkIsModifiable();
756     fields.removeFieldAttr(idx, a);
757   }
758   public void replaceElementAttr (int idx, Object oldAttr, Object newAttr){
759     checkIsModifiable();
760     fields.replaceFieldAttr(idx, oldAttr, newAttr);
761   }
762
763 /** <2do> those will be obsolete */
764   public void addElementAttrNoClone (int idx, Object a){
765     int nElements = getNumberOfFieldsOrElements();   
766     fields.addFieldAttr( nElements, idx, a);
767   }
768   public void removeElementAttrNoClone (int idx, Object a){
769     fields.removeFieldAttr(idx, a);
770   }
771   public void replaceElementAttrNoClone (int idx, Object oldAttr, Object newAttr){
772     fields.replaceFieldAttr(idx, oldAttr, newAttr);
773   }
774   
775   /**
776    * this only returns the first attr of this type, there can be more
777    * if you don't use client private types or the provided type is too general
778    */
779   public <T> T getElementAttr (int idx, Class<T> attrType) {
780     return fields.getFieldAttr(idx, attrType);
781   }
782   public <T> T getNextElementAttr (int idx, Class<T> attrType, Object prev) {
783     return fields.getNextFieldAttr(idx, attrType, prev);
784   }
785   public ObjectList.Iterator elementAttrIterator (int idx){
786     return fields.fieldAttrIterator(idx);
787   }
788   public <T> ObjectList.TypedIterator<T> elementAttrIterator (int idx, Class<T> type){
789     return fields.fieldAttrIterator(idx, type);
790   }
791
792   // -- end attributes --
793   
794   
795   public void setDeclaredIntField(String fname, String clsBase, int value) {
796     setIntField(getDeclaredFieldInfo(clsBase, fname), value);
797   }
798
799   public void setBooleanField (String fname, boolean value) {
800     setBooleanField( getFieldInfo(fname), value);
801   }
802   public void setByteField (String fname, byte value) {
803     setByteField( getFieldInfo(fname), value);
804   }
805   public void setCharField (String fname, char value) {
806     setCharField( getFieldInfo(fname), value);
807   }
808   public void setShortField (String fname, short value) {
809     setShortField( getFieldInfo(fname), value);
810   }
811   public void setIntField(String fname, int value) {
812     setIntField(getFieldInfo(fname), value);
813   }
814   public void setLongField (String fname, long value) {
815     setLongField( getFieldInfo(fname), value);
816   }
817   public void setFloatField (String fname, float value) {
818     setFloatField( getFieldInfo(fname), value);
819   }
820   public void setDoubleField (String fname, double value) {
821     setDoubleField( getFieldInfo(fname), value);
822   }
823   public void setReferenceField (String fname, int value) {
824     setReferenceField( getFieldInfo(fname), value);
825   }
826
827
828   // <2do> we need to tell 'null' values apart from 'no such field'
829   public Object getFieldValueObject (String fname) {
830     Object ret = null;
831     FieldInfo fi = getFieldInfo(fname);
832
833     if (fi != null){
834       ret = fi.getValueObject(fields);
835
836     } else {
837       // check if there is an enclosing class object
838       ElementInfo eiEnclosing = getEnclosingElementInfo();
839       if (eiEnclosing != null){
840         ret = eiEnclosing.getFieldValueObject(fname);
841
842       } else {
843         // we should check static fields in enclosing scopes, but there is no
844         // other way than to guess this from the name, and the outer
845         // classes might not even be initialized yet
846       }
847     }
848
849     return ret;
850   }
851
852   public ElementInfo getEnclosingElementInfo() {
853     return null; // only for DynamicElementInfos
854   }
855
856   public void setBooleanField(FieldInfo fi, boolean newValue) {
857     checkIsModifiable();
858     
859     if (fi.isBooleanField()) {
860       int offset = fi.getStorageOffset();
861       fields.setBooleanValue( offset, newValue);
862     } else {
863       throw new JPFException("not a boolean field: " + fi.getFullName());
864     }
865   }
866
867   public void setByteField(FieldInfo fi, byte newValue) {
868     checkIsModifiable();
869     
870     if (fi.isByteField()) {
871       int offset = fi.getStorageOffset();
872       fields.setByteValue( offset, newValue);
873     } else {
874       throw new JPFException("not a byte field: " + fi.getFullName());
875     }
876   }
877
878   public void setCharField(FieldInfo fi, char newValue) {
879     checkIsModifiable();
880     
881     if (fi.isCharField()) {
882       int offset = fi.getStorageOffset();
883       fields.setCharValue( offset, newValue);
884     } else {
885       throw new JPFException("not a char field: " + fi.getFullName());
886     }
887   }
888
889   public void setShortField(FieldInfo fi, short newValue) {
890     checkIsModifiable();
891
892     if (fi.isShortField()) {
893       int offset = fi.getStorageOffset();
894       fields.setShortValue( offset, newValue);
895     } else {
896       throw new JPFException("not a short field: " + fi.getFullName());
897     }
898   }
899
900   public void setIntField(FieldInfo fi, int newValue) {
901     checkIsModifiable();
902
903     if (fi.isIntField()) {
904       int offset = fi.getStorageOffset();
905       fields.setIntValue( offset, newValue);
906     } else {
907       throw new JPFException("not an int field: " + fi.getFullName());
908     }
909   }
910
911   public void setLongField(FieldInfo fi, long newValue) {
912     checkIsModifiable();
913
914     if (fi.isLongField()) {
915       int offset = fi.getStorageOffset();
916       fields.setLongValue( offset, newValue);
917     } else {
918       throw new JPFException("not a long field: " + fi.getFullName());
919     }
920   }
921
922   public void setFloatField(FieldInfo fi, float newValue) {
923     checkIsModifiable();
924
925     if (fi.isFloatField()) {
926       int offset = fi.getStorageOffset();
927       fields.setFloatValue( offset, newValue);
928     } else {
929       throw new JPFException("not a float field: " + fi.getFullName());
930     }
931   }
932
933   public void setDoubleField(FieldInfo fi, double newValue) {
934     checkIsModifiable();
935
936     if (fi.isDoubleField()) {
937       int offset = fi.getStorageOffset();
938       fields.setDoubleValue( offset, newValue);
939     } else {
940       throw new JPFException("not a double field: " + fi.getFullName());
941     }
942   }
943
944   public void setReferenceField(FieldInfo fi, int newValue) {
945     checkIsModifiable();
946
947     if (fi.isReference()) {
948       int offset = fi.getStorageOffset();
949       fields.setReferenceValue( offset, newValue);
950     } else {
951       throw new JPFException("not a reference field: " + fi.getFullName());
952     }
953   }
954
955   public void set1SlotField(FieldInfo fi, int newValue) {
956     checkIsModifiable();
957
958     if (fi.is1SlotField()) {
959       int offset = fi.getStorageOffset();
960       fields.setIntValue( offset, newValue);
961     } else {
962       throw new JPFException("not a 1 slot field: " + fi.getFullName());
963     }
964   }
965
966   public void set2SlotField(FieldInfo fi, long newValue) {
967     checkIsModifiable();
968
969     if (fi.is2SlotField()) {
970       int offset = fi.getStorageOffset();
971       fields.setLongValue( offset, newValue);
972     } else {
973       throw new JPFException("not a 2 slot field: " + fi.getFullName());
974     }
975   }
976
977
978   public void setDeclaredReferenceField(String fname, String clsBase, int value) {
979     setReferenceField(getDeclaredFieldInfo(clsBase, fname), value);
980   }
981
982   public int getDeclaredReferenceField(String fname, String clsBase) {
983     FieldInfo fi = getDeclaredFieldInfo(clsBase, fname);
984     return getReferenceField( fi);
985   }
986
987   public int getReferenceField(String fname) {
988     FieldInfo fi = getFieldInfo(fname);
989     return getReferenceField( fi);
990   }
991
992
993   public int getDeclaredIntField(String fname, String clsBase) {
994     // be aware of that static fields are not flattened (they are unique), i.e.
995     // the FieldInfo might actually refer to another ClassInfo/StaticElementInfo
996     FieldInfo fi = getDeclaredFieldInfo(clsBase, fname);
997     return getIntField( fi);
998   }
999
1000   public int getIntField(String fname) {
1001     // be aware of that static fields are not flattened (they are unique), i.e.
1002     // the FieldInfo might actually refer to another ClassInfo/StaticElementInfo
1003     FieldInfo fi = getFieldInfo(fname);
1004     return getIntField( fi);
1005   }
1006
1007   public void setDeclaredLongField(String fname, String clsBase, long value) {
1008     checkIsModifiable();
1009     
1010     FieldInfo fi = getDeclaredFieldInfo(clsBase, fname);
1011     fields.setLongValue( fi.getStorageOffset(), value);
1012   }
1013
1014   public long getDeclaredLongField(String fname, String clsBase) {
1015     FieldInfo fi = getDeclaredFieldInfo(clsBase, fname);
1016     return getLongField( fi);
1017   }
1018
1019   public long getLongField(String fname) {
1020     FieldInfo fi = getFieldInfo(fname);
1021     return getLongField( fi);
1022   }
1023
1024   public boolean getDeclaredBooleanField(String fname, String refType) {
1025     FieldInfo fi = getDeclaredFieldInfo(refType, fname);
1026     return getBooleanField( fi);
1027   }
1028
1029   public boolean getBooleanField(String fname) {
1030     FieldInfo fi = getFieldInfo(fname);
1031     return getBooleanField( fi);
1032   }
1033
1034   public byte getDeclaredByteField(String fname, String refType) {
1035     FieldInfo fi = getDeclaredFieldInfo(refType, fname);
1036     return getByteField( fi);
1037   }
1038
1039   public byte getByteField(String fname) {
1040     FieldInfo fi = getFieldInfo(fname);
1041     return getByteField( fi);
1042   }
1043
1044   public char getDeclaredCharField(String fname, String refType) {
1045     FieldInfo fi = getDeclaredFieldInfo(refType, fname);
1046     return getCharField( fi);
1047   }
1048
1049   public char getCharField(String fname) {
1050     FieldInfo fi = getFieldInfo(fname);
1051     return getCharField( fi);
1052   }
1053
1054   public double getDeclaredDoubleField(String fname, String refType) {
1055     FieldInfo fi = getDeclaredFieldInfo(refType, fname);
1056     return getDoubleField( fi);
1057   }
1058
1059   public double getDoubleField(String fname) {
1060     FieldInfo fi = getFieldInfo(fname);
1061     return getDoubleField( fi);
1062   }
1063
1064   public float getDeclaredFloatField(String fname, String refType) {
1065     FieldInfo fi = getDeclaredFieldInfo(refType, fname);
1066     return getFloatField( fi);
1067   }
1068
1069   public float getFloatField(String fname) {
1070     FieldInfo fi = getFieldInfo(fname);
1071     return getFloatField( fi);
1072   }
1073
1074   public short getDeclaredShortField(String fname, String refType) {
1075     FieldInfo fi = getDeclaredFieldInfo(refType, fname);
1076     return getShortField( fi);
1077   }
1078
1079   public short getShortField(String fname) {
1080     FieldInfo fi = getFieldInfo(fname);
1081     return getShortField( fi);
1082   }
1083
1084   /**
1085    * note this only holds for instance fields, and hence the method has to
1086    * be overridden in StaticElementInfo
1087    */
1088   private void checkFieldInfo(FieldInfo fi) {
1089     if (!getClassInfo().isInstanceOf(fi.getClassInfo())) {
1090       throw new JPFException("wrong FieldInfo : " + fi.getName()
1091           + " , no such field in " + getClassInfo().getName());
1092     }
1093   }
1094
1095   // those are the cached field value accessors. The caller is responsible
1096   // for assuring type compatibility
1097
1098   public boolean getBooleanField(FieldInfo fi) {
1099     if (fi.isBooleanField()){
1100       return fields.getBooleanValue(fi.getStorageOffset());
1101     } else {
1102       throw new JPFException("not a boolean field: " + fi.getName());
1103     }
1104   }
1105   public byte getByteField(FieldInfo fi) {
1106     if (fi.isByteField()){
1107       return fields.getByteValue(fi.getStorageOffset());
1108     } else {
1109       throw new JPFException("not a byte field: " + fi.getName());
1110     }
1111   }
1112   public char getCharField(FieldInfo fi) {
1113     if (fi.isCharField()){
1114       return fields.getCharValue(fi.getStorageOffset());
1115     } else {
1116       throw new JPFException("not a char field: " + fi.getName());
1117     }
1118   }
1119   public short getShortField(FieldInfo fi) {
1120     if (fi.isShortField()){
1121       return fields.getShortValue(fi.getStorageOffset());
1122     } else {
1123       throw new JPFException("not a short field: " + fi.getName());
1124     }
1125   }
1126   public int getIntField(FieldInfo fi) {
1127     if (fi.isIntField()){
1128       return fields.getIntValue(fi.getStorageOffset());
1129     } else {
1130       throw new JPFException("not a int field: " + fi.getName());
1131     }
1132   }
1133   public long getLongField(FieldInfo fi) {
1134     if (fi.isLongField()){
1135       return fields.getLongValue(fi.getStorageOffset());
1136     } else {
1137       throw new JPFException("not a long field: " + fi.getName());
1138     }
1139   }
1140   public float getFloatField (FieldInfo fi){
1141     if (fi.isFloatField()){
1142       return fields.getFloatValue(fi.getStorageOffset());
1143     } else {
1144       throw new JPFException("not a float field: " + fi.getName());
1145     }
1146   }
1147   public double getDoubleField (FieldInfo fi){
1148     if (fi.isDoubleField()){
1149       return fields.getDoubleValue(fi.getStorageOffset());
1150     } else {
1151       throw new JPFException("not a double field: " + fi.getName());
1152     }
1153   }
1154   public int getReferenceField (FieldInfo fi) {
1155     if (fi.isReference()){
1156       return fields.getReferenceValue(fi.getStorageOffset());
1157     } else {
1158       throw new JPFException("not a reference field: " + fi.getName());
1159     }
1160   }
1161
1162   public int get1SlotField(FieldInfo fi) {
1163     if (fi.is1SlotField()){
1164       return fields.getIntValue(fi.getStorageOffset());
1165     } else {
1166       throw new JPFException("not a 1 slot field: " + fi.getName());
1167     }
1168   }
1169   public long get2SlotField(FieldInfo fi) {
1170     if (fi.is2SlotField()){
1171       return fields.getLongValue(fi.getStorageOffset());
1172     } else {
1173       throw new JPFException("not a 2 slot field: " + fi.getName());
1174     }
1175   }
1176
1177   protected void checkArray(int index) {
1178     if (fields instanceof ArrayFields) { // <2do> should check for !long array
1179       if ((index < 0) || (index >= ((ArrayFields)fields).arrayLength())) {
1180         throw new JPFException("illegal array offset: " + index);
1181       }
1182     } else {
1183       throw new JPFException("cannot access non array objects by index");
1184     }
1185   }
1186
1187   public boolean isReferenceArray() {
1188     return getClassInfo().isReferenceArray();
1189   }
1190
1191   /**
1192    * this is the backend for System.arraycopy implementations, but since it only
1193    * throws general exceptions it can also be used in other contexts that require
1194    * type and objRef checking
1195    *
1196    * note that we have to do some additional type checking here because we store
1197    * reference arrays as int[], i.e. for reference arrays we can't rely on
1198    * System.arraycopy to do the element type checking for us
1199    *
1200    * @throws java.lang.ArrayIndexOutOfBoundsException
1201    * @throws java.lang.ArrayStoreException
1202    */
1203   public void copyElements( ThreadInfo ti, ElementInfo eiSrc, int srcIdx, int dstIdx, int length){
1204
1205     if (!isArray()){
1206       throw new ArrayStoreException("destination object not an array: " + ci.getName());
1207     }
1208     if (!eiSrc.isArray()){
1209       throw new ArrayStoreException("source object not an array: " + eiSrc.getClassInfo().getName());
1210     }
1211
1212     boolean isRefArray = isReferenceArray();
1213     if (eiSrc.isReferenceArray() != isRefArray){
1214       throw new ArrayStoreException("array types not compatible: " + eiSrc.getClassInfo().getName() + " and " + ci.getName());
1215     }
1216
1217     // since the caller has to handle normal ArrayStoreExceptions and
1218     // ArrayIndexOutOfBoundsExceptions, we don't have to explicitly check array length here
1219
1220     // if we copy reference arrays, we first have to check element type compatibility
1221     // (the underlying Fields type is always int[], hence we have to do this explicitly)
1222     if (isRefArray){
1223       ClassInfo dstElementCi = ci.getComponentClassInfo();
1224       int[] srcRefs = ((ArrayFields)eiSrc.fields).asReferenceArray();
1225       int max = srcIdx + length;
1226
1227       for (int i=srcIdx; i<max; i++){
1228         int eref = srcRefs[i];
1229         if (eref != MJIEnv.NULL){
1230           ClassInfo srcElementCi = ti.getClassInfo(eref);
1231           if (!srcElementCi.isInstanceOf(dstElementCi)) {
1232             throw new ArrayStoreException("incompatible reference array element type (required " + dstElementCi.getName() +
1233                     ", found " + srcElementCi.getName());
1234           }
1235         }
1236       }
1237     }
1238
1239     // NOTE - we have to clone the fields even in case System.arraycopy fails, since
1240     // the caller might handle ArrayStore/IndexOutOfBounds, and partial changes
1241     // have to be preserved
1242     // note also this preserves values in case of a self copy
1243     checkIsModifiable();
1244
1245     Object srcVals = ((ArrayFields)eiSrc.getFields()).getValues();
1246     Object dstVals = ((ArrayFields)fields).getValues();
1247
1248     // this might throw ArrayIndexOutOfBoundsExceptions and ArrayStoreExceptions
1249     System.arraycopy(srcVals, srcIdx, dstVals, dstIdx, length);
1250
1251     // now take care of the attributes
1252     // <2do> what in case arraycopy did throw - we should only copy the changed element attrs
1253     if (eiSrc.hasFieldAttr()){
1254       if (eiSrc == this && srcIdx < dstIdx) { // self copy
1255         for (int i = length - 1; i >= 0; i--) {
1256           Object a = eiSrc.getElementAttr( srcIdx+i);
1257           setElementAttr( dstIdx+i, a);
1258         }
1259       } else {
1260         for (int i = 0; i < length; i++) {
1261           Object a = eiSrc.getElementAttr(srcIdx+i);
1262           setElementAttr( dstIdx+i, a);
1263         }
1264       }
1265     }
1266   }
1267
1268   public void setBooleanElement(int idx, boolean value){
1269     checkArray(idx);
1270     checkIsModifiable();
1271     fields.setBooleanValue(idx, value);
1272   }
1273   public void setByteElement(int idx, byte value){
1274     checkArray(idx);
1275     checkIsModifiable();
1276     fields.setByteValue(idx, value);
1277   }
1278   public void setCharElement(int idx, char value){
1279     checkArray(idx);
1280     checkIsModifiable();
1281     fields.setCharValue(idx, value);
1282   }
1283   public void setShortElement(int idx, short value){
1284     checkArray(idx);
1285     checkIsModifiable();
1286     fields.setShortValue(idx, value);
1287   }
1288   public void setIntElement(int idx, int value){
1289     checkArray(idx);
1290     checkIsModifiable();
1291     fields.setIntValue(idx, value);
1292   }
1293   public void setLongElement(int idx, long value) {
1294     checkArray(idx);
1295     checkIsModifiable();
1296     fields.setLongValue(idx, value);
1297   }
1298   public void setFloatElement(int idx, float value){
1299     checkArray(idx);
1300     checkIsModifiable();
1301     fields.setFloatValue(idx, value);
1302   }
1303   public void setDoubleElement(int idx, double value){
1304     checkArray(idx);
1305     checkIsModifiable();
1306     fields.setDoubleValue(idx, value);
1307   }
1308   public void setReferenceElement(int idx, int value){
1309     checkArray(idx);
1310     checkIsModifiable();
1311     fields.setReferenceValue(idx, value);
1312   }
1313
1314   /**
1315    * NOTE - this doesn't support element type checks or overlapping in-array copy 
1316    */
1317   public void arrayCopy (ElementInfo src, int srcPos, int dstPos, int len){
1318     checkArray(dstPos+len-1);
1319     src.checkArray(srcPos+len-1);
1320     checkIsModifiable();
1321     
1322     ArrayFields da = (ArrayFields)fields;
1323     ArrayFields sa = (ArrayFields)src.fields;
1324     
1325     da.copyElements(sa, srcPos, dstPos, len);
1326   }
1327
1328   public boolean getBooleanElement(int idx) {
1329     checkArray(idx);
1330     return fields.getBooleanValue(idx);
1331   }
1332   public byte getByteElement(int idx) {
1333     checkArray(idx);
1334     return fields.getByteValue(idx);
1335   }
1336   public char getCharElement(int idx) {
1337     checkArray(idx);
1338     return fields.getCharValue(idx);
1339   }
1340   public short getShortElement(int idx) {
1341     checkArray(idx);
1342     return fields.getShortValue(idx);
1343   }
1344   public int getIntElement(int idx) {
1345     checkArray(idx);
1346     return fields.getIntValue(idx);
1347   }
1348   public long getLongElement(int idx) {
1349     checkArray(idx);
1350     return fields.getLongValue(idx);
1351   }
1352   public float getFloatElement(int idx) {
1353     checkArray(idx);
1354     return fields.getFloatValue(idx);
1355   }
1356   public double getDoubleElement(int idx) {
1357     checkArray(idx);
1358     return fields.getDoubleValue(idx);
1359   }
1360   public int getReferenceElement(int idx) {
1361     checkArray(idx);
1362     return fields.getReferenceValue(idx);
1363   }
1364
1365   public void setObjectRef(int newObjRef) {
1366     objRef = newObjRef;
1367   }
1368
1369   public int getObjectRef() {
1370     return objRef;
1371   }
1372
1373   /** use getObjectRef() - this is not an index */
1374   @Deprecated
1375   public int getIndex(){
1376     return objRef;
1377   }
1378
1379   public int getLockCount() {
1380     return monitor.getLockCount();
1381   }
1382
1383   public ThreadInfo getLockingThread() {
1384     return monitor.getLockingThread();
1385   }
1386
1387   public boolean isLocked() {
1388     return (monitor.getLockCount() > 0);
1389   }
1390
1391   public boolean isArray() {
1392     return ci.isArray();
1393   }
1394
1395   public boolean isCharArray(){
1396     return (fields instanceof CharArrayFields);
1397   }
1398   
1399   public boolean isFloatArray(){
1400     return (fields instanceof FloatArrayFields);
1401   }
1402
1403   public boolean isDoubleArray(){
1404     return (fields instanceof DoubleArrayFields);
1405   }
1406
1407   
1408   public String getArrayType() {
1409     if (!ci.isArray()) {
1410       throw new JPFException("object is not an array");
1411     }
1412
1413     return Types.getArrayElementType(ci.getType());
1414   }
1415
1416   public Object getBacktrackData() {
1417     return null;
1418   }
1419
1420
1421   // <2do> these will check for corresponding ArrayFields types
1422   public boolean[] asBooleanArray() {
1423     if (fields instanceof ArrayFields){
1424       return ((ArrayFields)fields).asBooleanArray();
1425     } else {
1426       throw new JPFException("not an array: " + ci.getName());
1427     }
1428   }
1429
1430   public byte[] asByteArray() {
1431     if (fields instanceof ArrayFields){
1432       return ((ArrayFields)fields).asByteArray();
1433     } else {
1434       throw new JPFException("not an array: " + ci.getName());
1435     }
1436   }
1437
1438   public short[] asShortArray() {
1439     if (fields instanceof ArrayFields){
1440       return ((ArrayFields)fields).asShortArray();
1441     } else {
1442       throw new JPFException("not an array: " + ci.getName());
1443     }
1444   }
1445
1446   public char[] asCharArray() {
1447     if (fields instanceof ArrayFields){
1448       return ((ArrayFields)fields).asCharArray();
1449     } else {
1450       throw new JPFException("not an array: " + ci.getName());
1451     }
1452   }
1453
1454   public int[] asIntArray() {
1455     if (fields instanceof ArrayFields){
1456       return ((ArrayFields)fields).asIntArray();
1457     } else {
1458       throw new JPFException("not an array: " + ci.getName());
1459     }
1460   }
1461
1462   public long[] asLongArray() {
1463     if (fields instanceof ArrayFields){
1464       return ((ArrayFields)fields).asLongArray();
1465     } else {
1466       throw new JPFException("not an array: " + ci.getName());
1467     }
1468   }
1469
1470   public float[] asFloatArray() {
1471     if (fields instanceof ArrayFields){
1472       return ((ArrayFields)fields).asFloatArray();
1473     } else {
1474       throw new JPFException("not an array: " + ci.getName());
1475     }
1476   }
1477
1478   public double[] asDoubleArray() {
1479     if (fields instanceof ArrayFields){
1480       return ((ArrayFields)fields).asDoubleArray();
1481     } else {
1482       throw new JPFException("not an array: " + ci.getName());
1483     }
1484   }
1485
1486   public int[] asReferenceArray() {
1487     if (fields instanceof ArrayFields){
1488       return ((ArrayFields)fields).asReferenceArray();
1489     } else {
1490       throw new JPFException("not an array: " + ci.getName());
1491     }
1492   }
1493     
1494   public boolean isNull() {
1495     return (objRef == MJIEnv.NULL);
1496   }
1497
1498   public ElementInfo getDeclaredObjectField(String fname, String referenceType) {
1499     return VM.getVM().getHeap().get(getDeclaredReferenceField(fname, referenceType));
1500   }
1501
1502   public ElementInfo getObjectField(String fname) {
1503     return VM.getVM().getHeap().get(getReferenceField(fname));
1504   }
1505
1506
1507   /**
1508    * answer an estimate of the heap size in bytes (this is of course VM
1509    * dependent, but we can give an upper bound for the fields/elements, and that
1510    * should be good in terms of application specific properties)
1511    */
1512   public int getHeapSize() {
1513     return fields.getHeapSize();
1514   }
1515
1516   public String getStringField(String fname) {
1517     int ref = getReferenceField(fname);
1518
1519     if (ref != MJIEnv.NULL) {
1520       ElementInfo ei = VM.getVM().getHeap().get(ref);
1521       return ei.asString();
1522     } else {
1523       return "null";
1524     }
1525   }
1526
1527   public String getType() {
1528     return ci.getType();
1529   }
1530
1531   /**
1532    * return all threads that are trying to acquire this lock
1533    * (blocked, waiting, interrupted)
1534    * NOTE - this is not a copy, don't modify the array
1535    */
1536   public ThreadInfo[] getLockedThreads() {
1537     return monitor.getLockedThreads();
1538   }
1539   
1540   /**
1541    * get a cloned list of the waiters for this object
1542    */
1543   public ThreadInfo[] getWaitingThreads() {
1544     return monitor.getWaitingThreads();
1545   }
1546
1547   public boolean hasWaitingThreads() {
1548     return monitor.hasWaitingThreads();
1549   }
1550
1551   public ThreadInfo[] getBlockedThreads() {
1552     return monitor.getBlockedThreads();
1553   }
1554
1555   public ThreadInfo[] getBlockedOrWaitingThreads() {
1556     return monitor.getBlockedOrWaitingThreads();
1557   }
1558     
1559   public int arrayLength() {
1560     if (fields instanceof ArrayFields){
1561       return ((ArrayFields)fields).arrayLength();
1562     } else {
1563       throw new JPFException("not an array: " + ci.getName());
1564     }
1565   }
1566
1567   public boolean isStringObject() {
1568     return ClassInfo.isStringClassInfo(ci);
1569   }
1570
1571   public String asString() {
1572     throw new JPFException("not a String object: " + this);
1573   }
1574
1575   public char[] getStringChars(){
1576     throw new JPFException("not a String object: " + this);    
1577   }
1578   
1579   /**
1580    * just a helper to avoid creating objects just for the sake of comparing
1581    */
1582   public boolean equalsString (String s) {
1583     throw new JPFException("not a String object: " + this);
1584   }
1585
1586   /**
1587    * is this a Number, a Boolean or a Character object
1588    * Note these classes are all final, so we don't have to check for subtypes
1589    * 
1590    * <2do> we should probably use a regular expression here
1591    */
1592   public boolean isBoxObject(){
1593     return false;
1594   }
1595   
1596   public Object asBoxObject(){
1597     throw new JPFException("not a box object: " + this);    
1598   }
1599   
1600   void updateLockingInfo() {
1601     int i;
1602
1603     ThreadInfo ti = monitor.getLockingThread();
1604     if (ti != null) {
1605       // here we can update ThreadInfo lock object info (so that we don't
1606       // have to store it separately)
1607
1608       // NOTE - the threads need to be restored *before* the ElementInfo containers,
1609       // or this is going to choke
1610
1611       // note that we add only once, i.e. rely on the monitor lockCount to
1612       // determine when to remove an object from our lock set
1613       //assert area.ks.tl.get(ti.objRef) == ti;  // covered by verifyLockInfo
1614       ti.updateLockedObject(this);
1615     }
1616
1617     if (monitor.hasLockedThreads()) {
1618       ThreadInfo[] lockedThreads = monitor.getLockedThreads();
1619       for (i=0; i<lockedThreads.length; i++) {
1620         ti = lockedThreads[i];
1621         //assert area.ks.tl.get(ti.objRef) == ti;  // covered by verifyLockInfo
1622         
1623         // note that the thread might still be runnable if we have several threads
1624         // competing for the same lock
1625         if (!ti.isRunnable()){
1626           ti.setLockRef(objRef);
1627         }
1628       }
1629     }
1630   }
1631
1632   public boolean canLock(ThreadInfo th) {
1633     return monitor.canLock(th);
1634   }
1635
1636   public void checkArrayBounds(int index) throws ArrayIndexOutOfBoundsExecutiveException {
1637     if (fields instanceof ArrayFields) {
1638       if (index < 0 || index >= ((ArrayFields)fields).arrayLength()){
1639         throw new ArrayIndexOutOfBoundsExecutiveException(
1640               ThreadInfo.getCurrentThread().createAndThrowException(
1641               "java.lang.ArrayIndexOutOfBoundsException", Integer.toString(index)));
1642       }
1643     } else {
1644       throw new JPFException("object is not an array: " + this);
1645     }
1646   }
1647
1648   @Override
1649   public ElementInfo clone() {
1650     try {
1651       ElementInfo ei = (ElementInfo) super.clone();
1652       ei.fields = fields.clone();
1653       ei.monitor = monitor.clone();
1654
1655       return ei;
1656       
1657     } catch (CloneNotSupportedException e) {
1658       throw new InternalError("should not happen");
1659     }
1660   }
1661
1662   // this is the one that should be used by heap
1663   public ElementInfo deepClone() {
1664     try {
1665       ElementInfo ei = (ElementInfo) super.clone();
1666       ei.fields = fields.clone();
1667       ei.monitor = monitor.clone();
1668       
1669       // referencingThreads is at least subtree global, hence doesn't need to be cloned
1670       
1671       ei.cachedMemento = null;
1672       ei.defreeze();
1673       
1674       return ei;
1675       
1676     } catch (CloneNotSupportedException e) {
1677       throw new InternalError("should not happen");
1678     }
1679     
1680   }
1681
1682   public boolean instanceOf(String type) {
1683     return Types.instanceOf(ci.getType(), type);
1684   }
1685
1686   abstract public int getNumberOfFields();
1687
1688   abstract public FieldInfo getFieldInfo(int fieldIndex);
1689
1690   /**
1691    * threads that will grab our lock on their next execution have to be
1692    * registered, so that they can be blocked in case somebody else gets
1693    * scheduled
1694    */
1695   public void registerLockContender (ThreadInfo ti) {
1696
1697     assert ti.lockRef == MJIEnv.NULL || ti.lockRef == objRef :
1698       "thread " + ti + " trying to register for : " + this +
1699       " ,but already blocked on: " + ti.getElementInfo(ti.lockRef);
1700
1701     // note that using the lockedThreads list is a bit counter-intuitive
1702     // since the thread is still in RUNNING or UNBLOCKED state, but it will
1703     // remove itself from there once it resumes: lock() calls setMonitorWithoutLocked(ti)
1704     setMonitorWithLocked(ti);
1705
1706     // added to satisfy invariant implied by updateLockingInfo() -peterd
1707     //ti.setLockRef(objRef);
1708   }
1709
1710   public boolean isRegisteredLockContender (ThreadInfo ti){
1711     return monitor.isLocking(ti);
1712   }
1713   
1714   /**
1715    * somebody made up his mind and decided not to enter a synchronized section
1716    * of code it had registered before (e.g. INVOKECLINIT)
1717    */
1718   public void unregisterLockContender (ThreadInfo ti) {
1719     setMonitorWithoutLocked(ti);
1720
1721     // moved from INVOKECLINIT -peterd
1722     //ti.resetLockRef();
1723   }
1724
1725   void blockLockContenders () {
1726     // check if there are any other threads that have to change status because they
1727     // require to lock us in their next exec
1728     ThreadInfo[] lockedThreads = monitor.getLockedThreads();
1729     for (int i=0; i<lockedThreads.length; i++) {
1730       ThreadInfo ti = lockedThreads[i];
1731       if (ti.isRunnable()) {
1732         ti.setBlockedState(objRef);
1733       }
1734     }
1735   }
1736
1737   /**
1738    * from a MONITOR_ENTER or sync INVOKExx if we cannot acquire the lock
1739    * note: this is not called from a NOTIFIED_UNBLOCKED state, so we don't have to restore NOTIFIED
1740    */
1741   public void block (ThreadInfo ti) {
1742     assert (monitor.getLockingThread() != null) && (monitor.getLockingThread() != ti) :
1743           "attempt to block " + ti.getName() + " on unlocked or own locked object: " + this;
1744
1745     setMonitorWithLocked(ti);
1746     
1747     ti.setBlockedState(objRef);    
1748   }
1749
1750   /**
1751    * from a MONITOR_ENTER or sync INVOKExx if we can acquire the lock
1752    */
1753   public void lock (ThreadInfo ti) {
1754     // if we do unlock consistency checks with JPFExceptions, we should do the same here
1755     if ((monitor.getLockingThread() != null) &&  (monitor.getLockingThread() != ti)){
1756       throw new JPFException("thread " + ti.getName() + " tries to lock object: "
1757               + this + " which is locked by: " + monitor.getLockingThread().getName());
1758     }
1759     
1760     // the thread might be still in the lockedThreads list if this is the
1761     // first step of a transition
1762     setMonitorWithoutLocked(ti);
1763     monitor.setLockingThread(ti);
1764     monitor.incLockCount();
1765
1766     // before we enter anything else, mark this thread as not being blocked anymore
1767     ti.resetLockRef();
1768
1769     ThreadInfo.State state = ti.getState();
1770     if (state == ThreadInfo.State.UNBLOCKED) {
1771       ti.setState(ThreadInfo.State.RUNNING);
1772     }
1773
1774     // don't re-add if we are recursive - the lock count is avaliable in the monitor
1775     if (monitor.getLockCount() == 1) {
1776       ti.addLockedObject(this);
1777     }
1778
1779     // this might set other threads blocked - make sure we lock first or the sequence
1780     // of notifications is a bit screwed (i.e. the lock would appear *after* the block)
1781     blockLockContenders();
1782   }
1783
1784   /**
1785    * from a MONITOR_EXIT or sync method RETURN
1786    * release a possibly recursive lock if lockCount goes to zero
1787    * 
1788    * return true if this unblocked any waiters
1789    */
1790   public boolean unlock (ThreadInfo ti) {
1791     boolean didUnblock = false;
1792
1793     checkIsModifiable();
1794     
1795     /* If there is a compiler bug, we need to flag it.  Most compilers should 
1796      * generate balanced monitorenter and monitorexit instructions for all code 
1797      * paths.  The VM is being used for more non-Java languages.  Some of these 
1798      * compilers might be experimental and might generate unbalanced 
1799      * instructions.  In a more likely case, dynamically generated bytecode is
1800      * more likely to make a mistake and miss a code path.
1801      */
1802     if ((monitor.getLockCount() <= 0) || (monitor.getLockingThread() != ti)){
1803       throw new JPFException("thread " + ti.getName() + " tries to release non-owned lock for object: " + this);
1804     }
1805
1806     if (monitor.getLockCount() == 1) {
1807       ti.removeLockedObject(this);
1808
1809       ThreadInfo[] lockedThreads = monitor.getLockedThreads();
1810       for (int i = 0; i < lockedThreads.length; i++) {
1811         ThreadInfo lti = lockedThreads[i];
1812         switch (lti.getState()) {
1813
1814         case BLOCKED:
1815         case NOTIFIED:
1816         case TIMEDOUT:
1817         case INTERRUPTED:
1818           // Ok, this thread becomes runnable again
1819           lti.resetLockRef();
1820           lti.setState(ThreadInfo.State.UNBLOCKED);
1821           didUnblock = true;
1822           break;
1823
1824         case WAITING:
1825         case TIMEOUT_WAITING:
1826           // nothing to do yet, thread has to timeout, get notified, or interrupted
1827           break;
1828
1829         default:
1830           assert false : "Monitor.lockedThreads<->ThreadData.status inconsistency! " + lockedThreads[i].getStateName();
1831           // why is it in the list - when someone unlocks, all others should have been blocked
1832         }
1833       }
1834
1835       // leave the contenders - we need to know whom to block on subsequent lock
1836
1837       monitor.decLockCount();
1838       monitor.setLockingThread(null);
1839
1840     } else { // recursive unlock
1841       monitor.decLockCount();
1842     }
1843     
1844     return didUnblock;
1845   }
1846
1847   /**
1848    * notifies one of the waiters. Note this is a potentially non-deterministic action
1849    * if we have several waiters, since we have to try all possible choices.
1850    * Note that even if we notify a thread here, it still remains in the lockedThreads
1851    * list until the lock is released (notified threads cannot run right away)
1852    */
1853   public boolean notifies(SystemState ss, ThreadInfo ti){
1854     return notifies(ss, ti, true);
1855   }
1856   
1857   
1858   private void notifies0 (ThreadInfo tiWaiter){
1859     if (tiWaiter.isWaitingOrTimedOut()){
1860       if (tiWaiter.getLockCount() > 0) {
1861         // waiter did hold the lock, but gave it up in the wait,  so it can't run yet
1862         tiWaiter.setNotifiedState();
1863
1864       } else {
1865         // waiter didn't hold the lock, set it running
1866         setMonitorWithoutLocked(tiWaiter);
1867         tiWaiter.resetLockRef();
1868         tiWaiter.setRunning();
1869       }
1870     }
1871   }
1872
1873   
1874   /** return true if this did notify any waiters */
1875   public boolean notifies (SystemState ss, ThreadInfo ti, boolean hasToHoldLock){
1876     if (hasToHoldLock){
1877       assert monitor.getLockingThread() != null : "notify on unlocked object: " + this;
1878     }
1879
1880     ThreadInfo[] locked = monitor.getLockedThreads();
1881     int i, nWaiters=0, iWaiter=0;
1882
1883     for (i=0; i<locked.length; i++) {
1884       if (locked[i].isWaitingOrTimedOut() ) {
1885         nWaiters++;
1886         iWaiter = i;
1887       }
1888     }
1889
1890     if (nWaiters == 0) {
1891       // no waiters, nothing to do
1892     } else if (nWaiters == 1) {
1893       // only one waiter, no choice point
1894       notifies0(locked[iWaiter]);
1895
1896     } else {
1897       // Ok, this is the non-deterministic case
1898       ThreadChoiceGenerator cg = ss.getCurrentChoiceGeneratorOfType(ThreadChoiceGenerator.class);
1899
1900       assert (cg != null) : "no ThreadChoiceGenerator in notify";
1901
1902       notifies0(cg.getNextChoice());
1903     }
1904
1905     ti.getVM().notifyObjectNotifies(ti, this);
1906     return (nWaiters > 0);
1907   }
1908
1909   /**
1910    * notify all waiters. This is a deterministic action
1911    * all waiters remain in the locked list, since they still have to be unblocked,
1912    * which happens in the unlock (monitor_exit or sync return) following the notifyAll()
1913    */
1914   public boolean notifiesAll() {
1915     assert monitor.getLockingThread() != null : "notifyAll on unlocked object: " + this;
1916
1917     ThreadInfo[] locked = monitor.getLockedThreads();
1918     for (int i=0; i<locked.length; i++) {
1919       // !!!! if there is more than one BLOCKED thread (sync call or monitor enter), only one can be
1920       // unblocked
1921       notifies0(locked[i]);
1922     }
1923
1924     VM.getVM().notifyObjectNotifiesAll(ThreadInfo.currentThread, this);
1925     return (locked.length > 0);
1926   }
1927
1928
1929   /**
1930    * wait to be notified. thread has to hold the lock, but gives it up in the wait.
1931    * Make sure lockCount can be restored properly upon notification
1932    */
1933   public void wait(ThreadInfo ti, long timeout){
1934     wait(ti,timeout,true);
1935   }
1936
1937   // this is used from a context where we don't require a lock, e.g. Unsafe.park()/unpark()
1938   public void wait (ThreadInfo ti, long timeout, boolean hasToHoldLock){
1939     checkIsModifiable();
1940     
1941     boolean holdsLock = monitor.getLockingThread() == ti;
1942
1943     if (hasToHoldLock){
1944       assert holdsLock : "wait on unlocked object: " + this;
1945     }
1946
1947     setMonitorWithLocked(ti);
1948     ti.setLockRef(objRef);
1949     
1950     if (timeout == 0) {
1951       ti.setState(ThreadInfo.State.WAITING);
1952     } else {
1953       ti.setState(ThreadInfo.State.TIMEOUT_WAITING);
1954     }
1955
1956     if (holdsLock) {
1957       ti.setLockCount(monitor.getLockCount());
1958
1959       monitor.setLockingThread(null);
1960       monitor.setLockCount(0);
1961
1962       ti.removeLockedObject(this);
1963
1964       // unblock all runnable threads that are blocked on this lock
1965       ThreadInfo[] lockedThreads = monitor.getLockedThreads();
1966       for (int i = 0; i < lockedThreads.length; i++) {
1967         ThreadInfo lti = lockedThreads[i];
1968         switch (lti.getState()) {
1969           case NOTIFIED:
1970           case BLOCKED:
1971           case INTERRUPTED:
1972             lti.resetLockRef();
1973             lti.setState(ThreadInfo.State.UNBLOCKED);
1974             break;
1975         }
1976       }
1977     }
1978
1979     // <2do> not sure if this is right if we don't hold the lock
1980     ti.getVM().notifyObjectWait(ti, this);
1981   }
1982
1983
1984   /**
1985    * re-acquire lock after being notified. This is the notified thread, i.e. the one
1986    * that will come out of a wait()
1987    */
1988   public void lockNotified (ThreadInfo ti) {
1989     assert ti.isUnblocked() : "resume waiting thread " + ti.getName() + " which is not unblocked";
1990
1991     setMonitorWithoutLocked(ti);
1992     monitor.setLockingThread( ti);
1993     monitor.setLockCount( ti.getLockCount());
1994
1995     ti.setLockCount(0);
1996     ti.resetLockRef();
1997     ti.setState( ThreadInfo.State.RUNNING);
1998
1999     blockLockContenders();
2000
2001     // this is important, if we later-on backtrack (reset the
2002     // ThreadInfo.lockedObjects set, and then restore from the saved heap), the
2003     // lock set would not include the lock when we continue to enter this thread
2004     ti.addLockedObject(this); //wv: add locked object back here
2005   }
2006
2007   /**
2008    * this is for waiters that did not own the lock
2009    */
2010   public void resumeNonlockedWaiter (ThreadInfo ti){
2011     setMonitorWithoutLocked(ti);
2012
2013     ti.setLockCount(0);
2014     ti.resetLockRef();
2015     ti.setRunning();
2016   }
2017
2018
2019   void dumpMonitor () {
2020     PrintWriter pw = new PrintWriter(System.out, true);
2021     pw.print( "monitor ");
2022     //pw.print( mIndex);
2023     monitor.printFields(pw);
2024     pw.flush();
2025   }
2026
2027   /**
2028    * updates a pinDown counter. If it is > 0 the object is kept alive regardless
2029    * if it is reachable from live objects or not.
2030    * @return true if the new counter is 1, i.e. the object just became pinned down
2031    *
2032    * NOTE - this is *not* a public method and you probably want to use
2033    * Heap.register/unregisterPinDown(). Pinning down an object is now
2034    * done through the Heap API, which updates the counter here, but might also
2035    * have to update internal caches
2036    */
2037   boolean incPinDown() {
2038     int pdCount = (attributes & ATTR_PINDOWN_MASK);
2039
2040     pdCount++;
2041     if ((pdCount & ~ATTR_PINDOWN_MASK) != 0){
2042       throw new JPFException("pinDown limit exceeded: " + this);
2043     } else {
2044       int a = (attributes & ~ATTR_PINDOWN_MASK);
2045       a |= pdCount;
2046       a |= ATTR_ATTRIBUTE_CHANGED;
2047       attributes = a;
2048       
2049       return (pdCount == 1);
2050     }
2051   }
2052
2053   /**
2054    * see incPinDown
2055    *
2056    * @return true if the counter becomes 0, i.e. the object just ceased to be
2057    * pinned down
2058    */
2059   boolean decPinDown() {
2060     int pdCount = (attributes & ATTR_PINDOWN_MASK);
2061
2062     if (pdCount > 0){
2063       pdCount--;
2064       int a = (attributes & ~ATTR_PINDOWN_MASK);
2065       a |= pdCount;
2066       a |= ATTR_ATTRIBUTE_CHANGED;
2067       attributes = a;
2068
2069       return (pdCount == 0);
2070       
2071     } else {
2072       return false;
2073     }
2074   }
2075
2076   public int getPinDownCount() {
2077     return (attributes & ATTR_PINDOWN_MASK);
2078   }
2079
2080   public boolean isPinnedDown() {
2081     return (attributes & ATTR_PINDOWN_MASK) != 0;
2082   }
2083
2084
2085   public boolean isConstructed() {
2086     return (attributes & ATTR_CONSTRUCTED) != 0;
2087   }
2088
2089   public void setConstructed() {
2090     attributes |= (ATTR_CONSTRUCTED | ATTR_ATTRIBUTE_CHANGED);
2091   }
2092
2093   public void restoreFields(Fields f) {
2094     fields = f;
2095   }
2096
2097   /**
2098    * BEWARE - never change the returned object without knowing about the
2099    * ElementInfo change status, this field is state managed!
2100    */
2101   public Fields getFields() {
2102     return fields;
2103   }
2104
2105   public ArrayFields getArrayFields(){
2106     if (fields instanceof ArrayFields){
2107       return (ArrayFields)fields;
2108     } else {
2109       throw new JPFException("not an array: " + ci.getName());
2110     }
2111   }
2112   
2113   public void restore(int index, int attributes, Fields fields, Monitor monitor){
2114     markUnchanged();
2115     
2116     this.objRef = index;
2117     this.attributes = attributes;
2118     this.fields = fields;
2119     this.monitor = monitor;
2120   }
2121
2122   public void restoreMonitor(Monitor m) {
2123     monitor = m;
2124   }
2125
2126   /**
2127    * BEWARE - never change the returned object without knowing about the
2128    * ElementInfo change status, this field is state managed!
2129    */
2130   public Monitor getMonitor() {
2131     return monitor;
2132   }
2133
2134   public void restoreAttributes(int a) {
2135     attributes = a;
2136   }
2137
2138   public boolean isAlive(boolean liveBitValue) {
2139     return ((attributes & ATTR_LIVE_BIT) == 0) ^ liveBitValue;
2140   }
2141
2142   public void setAlive(boolean liveBitValue){
2143     if (liveBitValue){
2144       attributes |= ATTR_LIVE_BIT;
2145     } else {
2146       attributes &= ~ATTR_LIVE_BIT;
2147     }
2148   }
2149
2150   public boolean isMarked() {
2151     return (attributes & ATTR_IS_MARKED) != 0;
2152   }
2153
2154   public boolean isFinalized() {
2155     return (attributes & ATTR_FINALIZED) != 0;
2156   }
2157   
2158   public void setFinalized() {
2159     attributes |= ATTR_FINALIZED;
2160   }
2161   
2162   public void setMarked() {
2163     attributes |= ATTR_IS_MARKED;
2164   }
2165
2166   public boolean isMarkedOrAlive (boolean liveBitValue){
2167     return ((attributes & ATTR_IS_MARKED) != 0) | (((attributes & ATTR_LIVE_BIT) == 0) ^ liveBitValue);
2168   }
2169
2170   public void markUnchanged() {
2171     attributes &= ~ATTR_ANY_CHANGED;
2172   }
2173
2174   public void setUnmarked() {
2175     attributes &= ~ATTR_IS_MARKED;
2176   }
2177
2178
2179   protected void checkIsModifiable() {
2180     if ((attributes & ATTR_IS_FROZEN) != 0) {
2181       throw new JPFException("attempt to modify frozen object: " + this);
2182     }
2183   }
2184
2185   void setMonitorWithLocked( ThreadInfo ti) {
2186     checkIsModifiable();
2187     monitor.addLocked(ti);
2188   }
2189
2190   void setMonitorWithoutLocked (ThreadInfo ti) {
2191     checkIsModifiable();    
2192     monitor.removeLocked(ti);
2193   }
2194
2195   public boolean isLockedBy(ThreadInfo ti) {
2196     return ((monitor != null) && (monitor.getLockingThread() == ti));
2197   }
2198
2199   public boolean isLocking(ThreadInfo ti){
2200     return (monitor != null) && monitor.isLocking(ti);
2201   }
2202   
2203   void _printAttributes(String cls, String msg, int oldAttrs) {
2204     if (getClassInfo().getName().equals(cls)) {
2205       System.out.println(msg + " " + this + " attributes: "
2206           + Integer.toHexString(attributes) + " was: "
2207           + Integer.toHexString(oldAttrs));
2208     }
2209   }
2210
2211     
2212   public void checkConsistency() {
2213 /**
2214     ThreadInfo ti = monitor.getLockingThread();
2215     if (ti != null) {
2216       // object has to be in the lockedObjects list of this thread
2217       checkAssertion( ti.getLockedObjects().contains(this), "locked object not in thread: " + ti);
2218     }
2219
2220     if (monitor.hasLockedThreads()) {
2221       checkAssertion( refTid.cardinality() > 1, "locked threads without multiple referencing threads");
2222
2223       for (ThreadInfo lti : monitor.getBlockedOrWaitingThreads()){
2224         checkAssertion( lti.lockRef == objRef, "blocked or waiting thread has invalid lockRef: " + lti);
2225       }
2226
2227       // we can't check for having lock contenders without being shared, since this can happen
2228       // in case an object is behind a FieldInfo shared-ness firewall (e.g. ThreadGroup.threads), or
2229       // is kept/used in native code (listener, peer)
2230     }
2231 **/
2232   }
2233   
2234   protected void checkAssertion(boolean cond, String failMsg){
2235     if (!cond){
2236       System.out.println("!!!!!! failed ElementInfo consistency: "  + this + ": " + failMsg);
2237
2238       System.out.println("object: " + this);
2239       System.out.println("usingTi: " + referencingThreads);
2240       
2241       ThreadInfo tiLock = getLockingThread();
2242       if (tiLock != null) System.out.println("locked by: " + tiLock);
2243       
2244       if (monitor.hasLockedThreads()){
2245         System.out.println("lock contenders:");
2246         for (ThreadInfo ti : monitor.getLockedThreads()){
2247           System.out.println("  " + ti + " = " + ti.getState());
2248         }
2249       }
2250       
2251       VM.getVM().dumpThreadStates();
2252       assert false;
2253     }
2254   }
2255
2256 }
2257