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