// // Copyright (C) 2006 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.vm; import java.io.PrintWriter; import java.util.List; import cmu.conditional.Conditional; import java.util.function.Function; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import de.fosd.typechef.featureexpr.FeatureExprFactory; import gov.nasa.jpf.Config; import gov.nasa.jpf.JPFException; import gov.nasa.jpf.util.HashData; import gov.nasa.jpf.util.ObjectList; import gov.nasa.jpf.util.Processor; /** * Describes an element of memory containing the field values of a class or an * object. In the case of a class, contains the values of the static fields. For * an object contains the values of the object fields. * * @see gov.nasa.jpf.vm.FieldInfo */ public abstract class ElementInfo implements Cloneable { //--- the lower 2 bytes of the attribute field are sticky (state stored/restored) // object attribute flag values // the first 8 bits constitute an unsigned pinDown count public static final int ATTR_PINDOWN_MASK = 0xff; // this ElementInfo is not allowed to be modified anymore since it has been state stored // ElementInfos are constructed in a non-frozen state, and will be frozen upon // heap store (usuall in the heap or ElementInfo memento ctor) // This is the basis for lifting the change management from the fields level (fields,monitor,attributes) // to the ElementInfo object level public static final int ATTR_IS_FROZEN = 0x100; // object has an immutable type (no field value change) public static final int ATTR_IMMUTABLE = 0x200; // The constructor for the object has returned. Final fields can no longer break POR // This attribute is set in gov.nasa.jpf.vm.bytecode.RETURN.enter(). // If ThreadInfo.usePorSyncDetection() is false, then this attribute is never set. public static final int ATTR_CONSTRUCTED = 0x400; public static final int ATTR_EXPOSED = 0x1000; // object is shared between threads public static final int ATTR_SHARED = 0x4000; // ATTR_SHARED is frozen (has to be changed explicitly, will not be updated by checkUpdatedSharedness) public static final int ATTR_FREEZE_SHARED = 0x8000; //--- the upper two bytes are for transient (heap internal) use only, and are not stored // BEWARE if you add or change values, make sure these are not used in derived classes ! // <2do> this is efficient but fragile public static final int ATTR_TREF_CHANGED = 0x40000; // referencingThreads have changed public static final int ATTR_ATTRIBUTE_CHANGED = 0x80000; // refers only to sticky bits //--- useful flag sets & masks static final int ATTR_STORE_MASK = 0x0000ffff; static final int ATTR_ANY_CHANGED = (ATTR_TREF_CHANGED | ATTR_ATTRIBUTE_CHANGED); // transient flag set if object is reachable from root object, i.e. can't be recycled public static final int ATTR_IS_MARKED = 0x80000000; // this bit is set/unset by the heap in order to identify live objects that have // already been unmarked. This is to avoid additional passes over the whole heap in // order to clean up dangling references etc. // NOTE - this bit should never be state stored - restored ElementInfo should never have it set public static final int ATTR_LIVE_BIT = 0x40000000; public static final int ATTR_MARKED_OR_LIVE_BIT = (ATTR_IS_MARKED | ATTR_LIVE_BIT); public static final int ATTR_FINALIZED = 0x800; //--- instance fields protected ClassInfo ci; protected Fields fields; protected Monitor monitor; // the set of threads using this object. Note this is not used for state matching // so that order or thread id do not have a direct impact on heap symmetry protected ThreadInfoSet referencingThreads; // this is the reference value for the object represented by this ElementInfo // (note this is a slight misnomer for StaticElementInfos, which don't really // represent objects but collections of static fields belonging to the same class) protected int objRef; // these are our state-stored object attributes // WATCH OUT! only include info that otherwise reflects a state change, so // that we don't introduce new changes. Its value is used to hash the state! // <2do> what a pity - 32 stored bits for (currently) only 2 bits of // information,but we might use this as a hash for more complex reference // info in the future. // We distinguish between propagates and private object attributes, the first // one stored in the lower 2 bytes protected int attributes; //--- the following fields are transient or search global, i.e. their values // are not state-stored, but might be set during state restoration // FieldLockInfos are never state-stored/backtracked! They are set in the order of // field access during the search, so that we can detect potential // inconsistencies and re-run accordingly protected FieldLockInfo[] fLockInfo; // cache for unchanged ElementInfos, so that we don't have to re-create cachedMemento // objects all the time protected Memento<ElementInfo> cachedMemento; // cache for a serialized representation of the object, which can be used // by state-matching. Value interpretation depends on the configured Serializer protected int sid; // helpers for state storage/restore processing, to avoid explicit iterators on // respective ElementInfo containers (heap,statics) static class Restorer implements Processor<ElementInfo> { @Override public void process (ElementInfo ei) { ei.attributes &= ElementInfo.ATTR_STORE_MASK; ei.sid = 0; ei.updateLockingInfo(); ei.markUnchanged(); } } static Restorer restorer = new Restorer(); static class Storer implements Processor<ElementInfo> { @Override public void process (ElementInfo ei) { ei.freeze(); } } static Storer storer = new Storer(); static boolean init (Config config) { return true; } protected ElementInfo (int id, ClassInfo c, Fields f, Monitor m, ThreadInfo ti) { objRef = id; ci = c; fields = f; monitor = m; assert ti != null; // we need that for our POR } public abstract ElementInfo getModifiableInstance(); // we need to delegate this in case it is global protected abstract ThreadInfoSet createThreadInfoSet(ThreadInfo ti); // not ideal, a sub-type checker. public abstract boolean isObject(); public abstract boolean hasFinalizer(); protected ElementInfo() { } public boolean hasChanged() { return !isFrozen(); //return (attributes & ATTR_ANY_CHANGED) != 0; } public String toString() { return ((ci != null ? ci.getName() : "ElementInfo") + '@' + Integer.toHexString(objRef)); } public FieldLockInfo getFieldLockInfo (FieldInfo fi) { if (fLockInfo == null) { fLockInfo = new FieldLockInfo[getNumberOfFields()]; } return fLockInfo[fi.getFieldIndex()]; } public void setFieldLockInfo (FieldInfo fi, FieldLockInfo flInfo) { fLockInfo[fi.getFieldIndex()] = flInfo; } /** * object is recycled (after potential finalization) */ public void processReleaseActions(){ // type based release actions ci.processReleaseActions(this); // instance based release actions if (fields.hasObjectAttr()){ for (ReleaseAction action : fields.objectAttrIterator(ReleaseAction.class)){ action.release(this); } } } /** * post transition live object cleanup * update all non-fields references used by this object. This is only called * at the end of the gc, and recycled objects should be either null or not marked * * if the current thread terminated, update the refTids accordingly */ void cleanUp (Heap heap, boolean isThreadTermination, int tid) { if (fLockInfo != null) { for (int i=0; i<fLockInfo.length; i++) { FieldLockInfo fli = fLockInfo[i]; if (fli != null) { fLockInfo[i] = fli.cleanUp(heap); } } } } //--- sids are only supposed to be used by the Serializer public void setSid(int id){ sid = id; } public int getSid() { return sid; } //--- cached mementos are only supposed to be used/set by the Restorer public Memento<ElementInfo> getCachedMemento(){ return cachedMemento; } public void setCachedMemento (Memento<ElementInfo> memento){ cachedMemento = memento; } /** * do we have a reference field with value objRef? */ public boolean hasRefField (int objRef) { return ci.hasRefField( objRef, fields); } public int numberOfUserThreads() { return referencingThreads.size(); } /** * the recursive phase2 marker entry, which propagates the attributes set by a * previous phase1. This one is called on all 'root'-marked objects after * phase1 is completed. ElementInfo is not an ideal place for this method, as * it has to access some innards of both ClassInfo (FieldInfo container) and * Fields. But on the other hand, we want to keep the whole heap traversal * business as much centralized in ElementInfo and Heap implementors */ void markRecursive(Heap heap) { int i, n; if (isArray()) { if (fields.isReferenceArray()) { n = ((ArrayFields)fields).arrayLength().getValue(); for (i = 0; i < n; i++) { Conditional<Integer> objref = fields.getReferenceValue(i); for (Integer ref : objref.toList()) { if (ref != MJIEnv.NULL){ heap.queueMark(ref); } } } } } else { // not an array ClassInfo ci = getClassInfo(); boolean isWeakRef = ci.isWeakReference(); do { n = ci.getNumberOfDeclaredInstanceFields(); boolean isRef = isWeakRef && ci.isReferenceClassInfo(); // is this the java.lang.ref.Reference part? for (i = 0; i < n; i++) { FieldInfo fi = ci.getDeclaredInstanceField(i); if (fi.isReference()) { if ((i == 0) && isRef) { // we need to reset the ref field once the referenced object goes away // NOTE: only the *first* WeakReference field is a weak ref // (this is why we have our own implementation) heap.registerWeakReference(this); } else { Conditional<Integer> objref = fields.getReferenceValue(fi.getStorageOffset()); for (Integer ref : objref.toList()) { if (ref != MJIEnv.NULL){ heap.queueMark(ref); } } } } } ci = ci.getSuperClass(); } while (ci != null); } } int getAttributes () { return attributes; } int getStoredAttributes() { return attributes & ATTR_STORE_MASK; } public boolean isImmutable() { return ((attributes & ATTR_IMMUTABLE) != 0); } //--- freeze handling public void freeze() { attributes |= ATTR_IS_FROZEN; } public void defreeze() { attributes &= ~ATTR_IS_FROZEN; } public boolean isFrozen() { return ((attributes & ATTR_IS_FROZEN) != 0); } //--- shared handling public void freezeSharedness (boolean freeze) { if (freeze) { if ((attributes & ATTR_FREEZE_SHARED) == 0) { checkIsModifiable(); attributes |= (ATTR_FREEZE_SHARED | ATTR_ATTRIBUTE_CHANGED); } } else { if ((attributes & ATTR_FREEZE_SHARED) != 0) { checkIsModifiable(); attributes &= ~ATTR_FREEZE_SHARED; attributes |= ATTR_ATTRIBUTE_CHANGED; } } } public boolean isSharednessFrozen () { return (attributes & ATTR_FREEZE_SHARED) != 0; } public boolean isShared() { //return usingTi.getNumberOfLiveThreads() > 1; return ((attributes & ATTR_SHARED) != 0); } public void setShared (boolean isShared) { if (isShared) { if ((attributes & ATTR_SHARED) == 0) { checkIsModifiable(); attributes |= (ATTR_SHARED | ATTR_ATTRIBUTE_CHANGED); // note we don't report the thread here since this is explicitly set via Verify.setShared VM.getVM().notifyObjectShared(null, this); } } else { if ((attributes & ATTR_SHARED) != 0) { checkIsModifiable(); attributes &= ~ATTR_SHARED; attributes |= ATTR_ATTRIBUTE_CHANGED; } } } /** * NOTE - this should only be called internally if we know the object is * modifiable (e.g. from the ctor) */ void setSharednessFromReferencingThreads () { if (referencingThreads.isShared( null, this)) { if ((attributes & ATTR_SHARED) == 0) { checkIsModifiable(); attributes |= (ATTR_SHARED | ATTR_ATTRIBUTE_CHANGED); } } } public boolean isReferencedBySameThreads (ElementInfo eiOther) { return referencingThreads.equals(eiOther.referencingThreads); } public boolean isReferencedByThread (ThreadInfo ti) { return referencingThreads.contains(ti); } public boolean isExposed(){ return (attributes & ATTR_EXPOSED) != 0; } /** * update referencingThreads and set shared flag accordingly (if not frozen) * * NOTE - this might return a new (cloned) ElementInfo in case the state stored/restored * flag has been changed and/or the SharedObjectPolicy in effect uses persistent * ThreadInfoSet objects (i.e. replaces them upon add). * Use only from system code that is aware of the potential ElementInfo * identity change (i.e. does not keep a reference to the old one) */ public ElementInfo getInstanceWithUpdatedSharedness (ThreadInfo ti) { ElementInfo updatedEi = this; // we update the referencingThreads no matter what // NOTE - it is up to the policy to decide if this creates a new set or destructively updates // the existing one (which translates into carry-over between paths, i.e. search global state) ThreadInfoSet newReferencingThreads = referencingThreads.add(ti); if (newReferencingThreads != referencingThreads){ // policy uses persistent (invariant) ThreadInfoSets updatedEi = getModifiableInstance(); updatedEi.referencingThreads = newReferencingThreads; } // the thread might already have been in referencingThreads, but we might get here after // backtracking. In this case the ATTR_SHARED attribute might have been reset and we have // to check if it needs updating (in case sharedness is not frozen) if ((updatedEi.attributes & ATTR_FREEZE_SHARED) == 0) { // note that we can only go from non-shared to shared, but not vice versa // (this is called in response to a reference from a live thread) if (newReferencingThreads.isShared( ti, updatedEi)) { if ((updatedEi.attributes & ATTR_SHARED) == 0) { // make sure we clone if this is the first modification updatedEi = updatedEi.getModifiableInstance(); updatedEi.attributes |= (ATTR_SHARED | ATTR_ATTRIBUTE_CHANGED); ti.getVM().notifyObjectShared(ti, updatedEi); } } } return updatedEi; } public void setExposed (ThreadInfo ti, ElementInfo eiFieldOwner){ // we actually have to add this to the attributes to avoid endless loops by // re-exposing the same object along a given path attributes |= ATTR_EXPOSED; ti.getVM().notifyObjectExposed(ti, eiFieldOwner, this); } /** * this is called before the system attempts to reclaim the object. If * we return 'false', the object will *not* be removed */ protected boolean recycle () { // this is required to avoid loosing field lock assumptions // when the system sequentialized threads with conflicting assumptions, // but the offending object goes out of scope before the system backtracks if (hasVolatileFieldLockInfos()) { return false; } setObjectRef(MJIEnv.NULL); return true; } boolean hasVolatileFieldLockInfos() { if (fLockInfo != null) { for (int i=0; i<fLockInfo.length; i++) { FieldLockInfo fli = fLockInfo[i]; if (fli != null) { if (fli.needsPindown(this)) { return true; } } } } return false; } public void hash(HashData hd) { hd.add(ci.getClassLoaderInfo().getId()); hd.add(ci.getId()); fields.hash(hd); monitor.hash(hd); hd.add(attributes & ATTR_STORE_MASK); } @Override public int hashCode() { HashData hd = new HashData(); hash(hd); return hd.getValue(); } @Override public boolean equals(Object o) { if (o instanceof ElementInfo) { ElementInfo other = (ElementInfo) o; if (ci != other.ci){ return false; } if ((attributes & ATTR_STORE_MASK) != (other.attributes & ATTR_STORE_MASK)){ return false; } if (!fields.equals(other.fields)) { return false; } if (!monitor.equals(other.monitor)){ return false; } if (referencingThreads != other.referencingThreads){ return false; } return true; } else { return false; } } public ClassInfo getClassInfo() { return ci; } abstract protected FieldInfo getDeclaredFieldInfo(String clsBase, String fname); abstract protected FieldInfo getFieldInfo(String fname); protected abstract int getNumberOfFieldsOrElements(); //--- Object attribute accessors public boolean hasObjectAttr(){ return fields.hasObjectAttr(); } public boolean hasObjectAttr(Class<?> attrType) { return fields.hasObjectAttr(attrType); } /** * this returns all of them - use either if you know there will be only * one attribute at a time, or check/process result with ObjectList */ public Object getObjectAttr(){ return fields.getObjectAttr(); } /** * this replaces all of them - use only if you know * - there will be only one attribute at a time * - you obtained the value you set by a previous getXAttr() * - you constructed a multi value list with ObjectList.createList() */ public void setObjectAttr (Object a){ checkIsModifiable(); fields.setObjectAttr(a); } /** * this replaces all of them - use only if you know * - there will be only one attribute at a time * - you obtained the value you set by a previous getXAttr() * - you constructed a multi value list with ObjectList.createList() */ public void setObjectAttrNoClone (Object a){ fields.setObjectAttr(a); } public void addObjectAttr(Object a){ checkIsModifiable(); fields.addObjectAttr(a); } public void removeObjectAttr(Object a){ checkIsModifiable(); fields.removeObjectAttr(a); } public void replaceObjectAttr(Object oldAttr, Object newAttr){ checkIsModifiable(); fields.replaceObjectAttr(oldAttr, newAttr); } /** * this only returns the first attr of this type, there can be more * if you don't use client private types or the provided type is too general */ public <T> T getObjectAttr (Class<T> attrType) { return fields.getObjectAttr(attrType); } public <T> T getNextObjectAttr (Class<T> attrType, Object prev) { return fields.getNextObjectAttr(attrType, prev); } public ObjectList.Iterator objectAttrIterator(){ return fields.objectAttrIterator(); } public <T> ObjectList.TypedIterator<T> objectAttrIterator(Class<T> type){ return fields.objectAttrIterator(type); } //--- field attribute accessors public boolean hasFieldAttr() { return fields.hasFieldAttr(); } public boolean hasFieldAttr (Class<?> attrType){ return fields.hasFieldAttr(attrType); } /** * this returns all of them - use either if you know there will be only * one attribute at a time, or check/process result with ObjectList */ public Object getFieldAttr (FieldInfo fi){ return fields.getFieldAttr(fi.getFieldIndex()); } /** * this replaces all of them - use only if you know * - there will be only one attribute at a time * - you obtained the value you set by a previous getXAttr() * - you constructed a multi value list with ObjectList.createList() */ public void setFieldAttr (FieldInfo fi, Object attr){ checkIsModifiable(); int nFields = getNumberOfFieldsOrElements(); fields.setFieldAttr( nFields, fi.getFieldIndex(), attr); } public void addFieldAttr (FieldInfo fi, Object a){ checkIsModifiable(); int nFields = getNumberOfFieldsOrElements(); fields.addFieldAttr( nFields, fi.getFieldIndex(), a); } public void removeFieldAttr (FieldInfo fi, Object a){ checkIsModifiable(); fields.removeFieldAttr(fi.getFieldIndex(), a); } public void replaceFieldAttr (FieldInfo fi, Object oldAttr, Object newAttr){ checkIsModifiable(); fields.replaceFieldAttr(fi.getFieldIndex(), oldAttr, newAttr); } /** * this only returns the first attr of this type, there can be more * if you don't use client private types or the provided type is too general */ public <T> T getFieldAttr (FieldInfo fi, Class<T> attrType) { return fields.getFieldAttr(fi.getFieldIndex(), attrType); } public <T> T getNextFieldAttr (FieldInfo fi, Class<T> attrType, Object prev) { return fields.getNextFieldAttr(fi.getFieldIndex(), attrType, prev); } public ObjectList.Iterator fieldAttrIterator (FieldInfo fi){ return fields.fieldAttrIterator(fi.getFieldIndex()); } public <T> ObjectList.TypedIterator<T> fieldAttrIterator (FieldInfo fi, Class<T> type){ return fields.fieldAttrIterator(fi.getFieldIndex(), type); } //--- element attribute accessors public boolean hasElementAttr() { return fields.hasFieldAttr(); } public boolean hasElementAttr (Class<?> attrType){ return fields.hasFieldAttr(attrType); } /** * this returns all of them - use either if you know there will be only * one attribute at a time, or check/process result with ObjectList */ public Object getElementAttr (int idx){ return fields.getFieldAttr(idx); } /** * this replaces all of them - use only if you know * - there will be only one attribute at a time * - you obtained the value you set by a previous getXAttr() * - you constructed a multi value list with ObjectList.createList() */ public void setElementAttr (int idx, Object attr){ int nElements = getNumberOfFieldsOrElements(); checkIsModifiable(); fields.setFieldAttr( nElements, idx, attr); } /** * this replaces all of them - use only if you know * - there will be only one attribute at a time * - you obtained the value you set by a previous getXAttr() * - you constructed a multi value list with ObjectList.createList() */ public void setElementAttrNoClone (int idx, Object attr){ int nElements = getNumberOfFieldsOrElements(); fields.setFieldAttr(nElements,idx, attr); } public void addElementAttr (int idx, Object a){ checkIsModifiable(); int nElements = getNumberOfFieldsOrElements(); fields.addFieldAttr( nElements, idx, a); } public void removeElementAttr (int idx, Object a){ checkIsModifiable(); fields.removeFieldAttr(idx, a); } public void replaceElementAttr (int idx, Object oldAttr, Object newAttr){ checkIsModifiable(); fields.replaceFieldAttr(idx, oldAttr, newAttr); } /** <2do> those will be obsolete */ public void addElementAttrNoClone (int idx, Object a){ int nElements = getNumberOfFieldsOrElements(); fields.addFieldAttr( nElements, idx, a); } public void removeElementAttrNoClone (int idx, Object a){ fields.removeFieldAttr(idx, a); } public void replaceElementAttrNoClone (int idx, Object oldAttr, Object newAttr){ fields.replaceFieldAttr(idx, oldAttr, newAttr); } /** * this only returns the first attr of this type, there can be more * if you don't use client private types or the provided type is too general */ public <T> T getElementAttr (int idx, Class<T> attrType) { return fields.getFieldAttr(idx, attrType); } public <T> T getNextElementAttr (int idx, Class<T> attrType, Object prev) { return fields.getNextFieldAttr(idx, attrType, prev); } public ObjectList.Iterator elementAttrIterator (int idx){ return fields.fieldAttrIterator(idx); } public <T> ObjectList.TypedIterator<T> elementAttrIterator (int idx, Class<T> type){ return fields.fieldAttrIterator(idx, type); } // -- end attributes -- public void setDeclaredIntField(FeatureExpr ctx, String fname, String clsBase, Conditional<Integer> value) { setIntField(ctx, getDeclaredFieldInfo(clsBase, fname), value); } public void setBooleanField (FeatureExpr ctx, String fname, Conditional<Boolean> value) { setBooleanField( ctx, getFieldInfo(fname), value); } public void setByteField (FeatureExpr ctx, String fname, Conditional<Byte> value) { setByteField( ctx, getFieldInfo(fname), value); } public void setCharField (FeatureExpr ctx, String fname, Conditional<Character> value) { setCharField( ctx, getFieldInfo(fname), value); } public void setShortField (FeatureExpr ctx, String fname, Conditional<Short> value) { setShortField( ctx, getFieldInfo(fname), value); } public void setIntField(FeatureExpr ctx, String fname, Conditional<Integer> value) { setIntField(ctx, getFieldInfo(fname), value); } public void setLongField (FeatureExpr ctx, String fname, Conditional<Long> value) { setLongField( ctx, getFieldInfo(fname), value); } public void setFloatField (FeatureExpr ctx, String fname, Conditional<Float> value) { setFloatField( ctx, getFieldInfo(fname), value); } public void setDoubleField (FeatureExpr ctx, String fname, Conditional<Double> value) { setDoubleField( ctx, getFieldInfo(fname), value); } public void setReferenceField(FeatureExpr ctx, String fname, Conditional<Integer> value) { setReferenceField( ctx, getFieldInfo(fname), value); } // <2do> we need to tell 'null' values apart from 'no such field' public Object getFieldValueObject (String fname) { Object ret = null; FieldInfo fi = getFieldInfo(fname); if (fi != null){ ret = fi.getValueObject(fields); } else { // check if there is an enclosing class object ElementInfo eiEnclosing = getEnclosingElementInfo(); if (eiEnclosing != null){ ret = eiEnclosing.getFieldValueObject(fname); } else { // we should check static fields in enclosing scopes, but there is no // other way than to guess this from the name, and the outer // classes might not even be initialized yet } } return ret; } public ElementInfo getEnclosingElementInfo() { return null; // only for DynamicElementInfos } public void setBooleanField(FeatureExpr ctx, FieldInfo fi, Conditional<Boolean> newValue) { checkIsModifiable(); if (fi.isBooleanField()) { int offset = fi.getStorageOffset(); fields.setBooleanValue( ctx, offset, newValue); } else { throw new JPFException("not a boolean field: " + fi.getName()); } } public void setByteField(FeatureExpr ctx, FieldInfo fi, Conditional<Byte> newValue) { checkIsModifiable(); if (fi.isByteField()) { set(ctx, fi, newValue); } else { throw new JPFException("not a byte field: " + fi.getName()); } } public void setCharField(FeatureExpr ctx, FieldInfo fi, Conditional<Character> newValue) { checkIsModifiable(); if (fi.isCharField()) { int offset = fi.getStorageOffset(); fields.setCharValue(ctx, offset, newValue); } else { throw new JPFException("not a char field: " + fi.getName()); } } public void setShortField(FeatureExpr ctx, FieldInfo fi, Conditional<Short> newValue) { checkIsModifiable(); if (fi.isShortField()) { set(ctx, fi, newValue); } else { throw new JPFException("not a short field: " + fi.getName()); } } private <T extends Number> void set(FeatureExpr ctx, FieldInfo fi, Conditional<T> newValue) { int offset = fi.getStorageOffset(); if (fi instanceof FloatFieldInfo) { fields.setFloatValue(ctx, offset, newValue.map(new Function<T, Float>() { public Float apply(T x) { return x.floatValue(); } })); } else if (fi instanceof DoubleFieldInfo) { fields.setDoubleValue(ctx, offset, newValue.map(new Function<T, Double>() { public Double apply(T x) { return x.doubleValue(); } })); } else if (fi instanceof IntegerFieldInfo) { fields.setIntValue(ctx, offset, newValue.map(new Function<T, Integer>() { public Integer apply(T x) { return x.intValue(); } })); } else if (fi instanceof LongFieldInfo) { fields.setLongValue(ctx, offset, newValue.map(new Function<T, Long>() { public Long apply(T x) { return x.longValue(); } })); } else if (fi instanceof ByteFieldInfo) { fields.setByteValue(ctx, offset, newValue.map(new Function<T, Byte>() { public Byte apply(T x) { return x.byteValue(); } })); } else if (fi instanceof ShortFieldInfo) { fields.setShortValue(ctx, offset, newValue.map(new Function<T, Short>() { public Short apply(T x) { return x.shortValue(); } })); } else { throw new RuntimeException(fi.getType()); } } public void setIntField(FeatureExpr ctx, FieldInfo fi, Conditional<Integer> newValue) { checkIsModifiable(); if (fi.isIntField()) { set(ctx, fi, newValue); } else { throw new JPFException("not an int field: " + fi.getName()); } } public void setLongField(FeatureExpr ctx, FieldInfo fi, Conditional<Long> newValue) { checkIsModifiable(); if (fi.isLongField()) { set(ctx, fi, newValue); } else { throw new JPFException("not a long field: " + fi.getName()); } } public void setFloatField(FeatureExpr ctx, FieldInfo fi, Conditional<Float> newValue) { checkIsModifiable(); if (fi.isFloatField()) { set(ctx, fi, newValue); } else { throw new JPFException("not a float field: " + fi.getName()); } } public void setDoubleField(FeatureExpr ctx, FieldInfo fi, Conditional<Double> newValue) { checkIsModifiable(); if (fi.isDoubleField()) { int offset = fi.getStorageOffset(); fields.setDoubleValue( ctx, offset, newValue); } else { throw new JPFException("not a double field: " + fi.getName()); } } public void setReferenceField(FeatureExpr ctx, FieldInfo fi, Conditional<Integer> newValue) { checkIsModifiable(); if (fi.isReference()) { int offset = fi.getStorageOffset(); fields.setReferenceValue( ctx, offset, newValue); } else { throw new JPFException("not a reference field: " + fi.getName()); } } public void set1SlotField(FeatureExpr ctx, FieldInfo fi, Conditional<Integer> val) { checkIsModifiable(); if (fi.is1SlotField()) { int offset = fi.getStorageOffset(); fields.setIntValue( ctx, offset, val); } else { throw new JPFException("not a 1 slot field: " + fi.getName()); } } public void set2SlotField(FeatureExpr ctx, FieldInfo fi, Conditional<Long> newValue) { checkIsModifiable(); if (fi.is2SlotField()) { int offset = fi.getStorageOffset(); fields.setLongValue( ctx, offset, newValue); } else { throw new JPFException("not a 2 slot field: " + fi.getName()); } } public void setDeclaredReferenceField(String fname, String clsBase, int value) { setReferenceField(FeatureExprFactory.True(), getDeclaredFieldInfo(clsBase, fname), new One<>(value)); } public Conditional<Integer> getDeclaredReferenceField(String fname, String clsBase) { FieldInfo fi = getDeclaredFieldInfo(clsBase, fname); return getReferenceField( fi); } public Conditional<Integer> getReferenceField(String fname) { FieldInfo fi = getFieldInfo(fname); return getReferenceField( fi); } public int getDeclaredIntField(String fname, String clsBase) { // be aware of that static fields are not flattened (they are unique), i.e. // the FieldInfo might actually refer to another ClassInfo/StaticElementInfo FieldInfo fi = getDeclaredFieldInfo(clsBase, fname); return getIntField( fi).getValue();// TODO jens } public Conditional<Integer> getIntField(String fname) { // be aware of that static fields are not flattened (they are unique), i.e. // the FieldInfo might actually refer to another ClassInfo/StaticElementInfo FieldInfo fi = getFieldInfo(fname); return getIntField( fi); } public void setDeclaredLongField(String fname, String clsBase, long value) { checkIsModifiable(); FieldInfo fi = getDeclaredFieldInfo(clsBase, fname); fields.setLongValue( null, fi.getStorageOffset(), new One<>(value)); } public long getDeclaredLongField(String fname, String clsBase) { FieldInfo fi = getDeclaredFieldInfo(clsBase, fname); return getLongField( fi).getValue(); } public Conditional<Long> getLongField(String fname) { FieldInfo fi = getFieldInfo(fname); return getLongField( fi); } public Conditional<Boolean> getDeclaredBooleanField(String fname, String refType) { FieldInfo fi = getDeclaredFieldInfo(refType, fname); return getBooleanField( fi); } public Conditional<Boolean> getBooleanField(String fname) { FieldInfo fi = getFieldInfo(fname); return getBooleanField( fi); } public Conditional<Byte> getDeclaredByteField(String fname, String refType) { FieldInfo fi = getDeclaredFieldInfo(refType, fname); return getByteField( fi); } public Conditional<Byte> getByteField(String fname) { FieldInfo fi = getFieldInfo(fname); return getByteField( fi); } public Conditional<Character> getDeclaredCharField(String fname, String refType) { FieldInfo fi = getDeclaredFieldInfo(refType, fname); return getCharField( fi); } public Conditional<Character> getCharField(String fname) { FieldInfo fi = getFieldInfo(fname); return getCharField( fi); } public Conditional<Double> getDeclaredDoubleField(String fname, String refType) { FieldInfo fi = getDeclaredFieldInfo(refType, fname); return getDoubleField( fi); } public Conditional<Double> getDoubleField(String fname) { FieldInfo fi = getFieldInfo(fname); return getDoubleField( fi); } public Conditional<Float> getDeclaredFloatField(String fname, String refType) { FieldInfo fi = getDeclaredFieldInfo(refType, fname); return getFloatField( fi); } public Conditional<Float> getFloatField(String fname) { FieldInfo fi = getFieldInfo(fname); return getFloatField( fi); } public Conditional<Short> getDeclaredShortField(String fname, String refType) { FieldInfo fi = getDeclaredFieldInfo(refType, fname); return getShortField( fi); } public Conditional<Short> getShortField(String fname) { FieldInfo fi = getFieldInfo(fname); return getShortField( fi); } /** * note this only holds for instance fields, and hence the method has to * be overridden in StaticElementInfo */ @SuppressWarnings("unused") private void checkFieldInfo(FieldInfo fi) { if (!getClassInfo().isInstanceOf(fi.getClassInfo())) { throw new JPFException("wrong FieldInfo : " + fi.getName() + " , no such field in " + getClassInfo().getName()); } } // those are the cached field value accessors. The caller is responsible // for assuring type compatibility public Conditional<Boolean> getBooleanField(FieldInfo fi) { if (fi.isBooleanField()){ return fields.getBooleanValue(fi.getStorageOffset()); } else { throw new JPFException("not a boolean field: " + fi.getName()); } } public Conditional<Byte> getByteField(FieldInfo fi) { if (fi.isByteField()){ return fields.getByteValue(fi.getStorageOffset()); } else { throw new JPFException("not a byte field: " + fi.getName()); } } public Conditional<Character> getCharField(FieldInfo fi) { if (fi.isCharField()){ return fields.getCharValue(fi.getStorageOffset()); } else { throw new JPFException("not a char field: " + fi.getName()); } } public Conditional<Short> getShortField(FieldInfo fi) { if (fi.isShortField()){ return fields.getShortValue(fi.getStorageOffset()); } else { throw new JPFException("not a short field: " + fi.getName()); } } public Conditional<Integer> getIntField(FieldInfo fi) { if (fi.isIntField()){ return fields.getIntValue(fi.getStorageOffset()); } else { throw new JPFException("not a int field: " + fi.getName()); } } public Conditional<Long> getLongField(FieldInfo fi) { if (fi.isLongField()){ return fields.getLongValue(fi.getStorageOffset()); } else { throw new JPFException("not a long field: " + fi.getName()); } } public Conditional<Float> getFloatField (FieldInfo fi){ if (fi.isFloatField()){ return fields.getFloatValue(fi.getStorageOffset()); } else { throw new JPFException("not a float field: " + fi.getName()); } } public Conditional<Double> getDoubleField (FieldInfo fi){ if (fi.isDoubleField()){ return fields.getDoubleValue(fi.getStorageOffset()); } else { throw new JPFException("not a double field: " + fi.getName()); } } public Conditional<Integer> getReferenceField (FieldInfo fi) { if (fi.isReference()){ return fields.getReferenceValue(fi.getStorageOffset()); } else { throw new JPFException(fields.getClass().getSimpleName() + " not a reference field: " + fi.getName()); } } public Conditional<Integer> get1SlotField(FieldInfo fi) { if (fi.is1SlotField()){ return fields.getIntValue(fi.getStorageOffset()); } else { throw new JPFException("not a 1 slot field: " + fi.getName()); } } public Conditional<Long> get2SlotField(FieldInfo fi) { if (fi.is2SlotField()){ return fields.getLongValue(fi.getStorageOffset()); } else { throw new JPFException("not a 2 slot field: " + fi.getName()); } } protected void checkArray(int index) { if (fields instanceof ArrayFields) { // <2do> should check for !long array if ((index < 0) || (index >= ((ArrayFields)fields).arrayLength().getValue())) { throw new JPFException("illegal array offset: " + index); } } else { throw new JPFException("cannot access non array objects by index"); } } public boolean isReferenceArray() { return getClassInfo().isReferenceArray(); } /** * this is the backend for System.arraycopy implementations, but since it only * throws general exceptions it can also be used in other contexts that require * type and objRef checking * * note that we have to do some additional type checking here because we store * reference arrays as int[], i.e. for reference arrays we can't rely on * System.arraycopy to do the element type checking for us * * @throws java.lang.ArrayIndexOutOfBoundsException * @throws java.lang.ArrayStoreException */ public void copyElements( FeatureExpr ctx, ThreadInfo ti, ElementInfo eiSrc, int srcIdx, int dstIdx, int length){ if (ctx.isContradiction()) { return; } if (!isArray()){ throw new ArrayStoreException("destination object not an array: " + ci.getName()); } if (!eiSrc.isArray()){ throw new ArrayStoreException("source object not an array: " + eiSrc.getClassInfo().getName()); } boolean isRefArray = isReferenceArray(); if (eiSrc.isReferenceArray() != isRefArray){ throw new ArrayStoreException("array types not compatible: " + eiSrc.getClassInfo().getName() + " and " + ci.getName()); } // since the caller has to handle normal ArrayStoreExceptions and // ArrayIndexOutOfBoundsExceptions, we don't have to explicitly check array length here // if we copy reference arrays, we first have to check element type compatibility // (the underlying Fields type is always int[], hence we have to do this explicitly) if (isRefArray) { ClassInfo dstElementCi = ci.getComponentClassInfo(); Conditional<Integer>[] srcRefs = ((ArrayFields) eiSrc.fields).asReferenceArray(); int max = srcIdx + length; for (int i = srcIdx; i < max; i++) { List<Integer> erefs = srcRefs[i].simplify(ctx).toList(); for (int eref : erefs) { if (eref != MJIEnv.NULL) { ClassInfo srcElementCi = ti.getClassInfo(eref); if (!srcElementCi.isInstanceOf(dstElementCi)) { throw new ArrayStoreException("incompatible reference array element type (required " + dstElementCi.getName() + ", found " + srcElementCi.getName()); } } } } } // NOTE - we have to clone the fields even in case System.arraycopy fails, since // the caller might handle ArrayStore/IndexOutOfBounds, and partial changes // have to be preserved // note also this preserves values in case of a self copy checkIsModifiable(); Conditional<?> srcVals = ((ArrayFields)eiSrc.getFields()).getValues(); Conditional<?> dstVals = ((ArrayFields)fields).getValues(); // this might throw ArrayIndexOutOfBoundsExceptions and ArrayStoreExceptions if (srcVals instanceof One && dstVals instanceof One) {// TODO jens revise array copy if (srcVals.getValue() instanceof Conditional[]) { try { Fields src = eiSrc.getFields(); if (src == fields) { src = src.clone(); } if (src instanceof DoubleArrayFields) { for (int i = 0; i < length; i++) { fields.setDoubleValue(ctx, dstIdx + i, src.getDoubleValue(i + srcIdx)); } } else if (eiSrc.getFields() instanceof LongArrayFields) { for (int i = 0; i < length; i++) { fields.setLongValue(ctx, dstIdx + i, src.getLongValue(i + srcIdx)); } } else if (eiSrc.getFields() instanceof IntArrayFields) { for (int i = 0; i < length; i++) { fields.setIntValue(ctx, dstIdx + i, src.getIntValue(i + srcIdx)); } } else if (eiSrc.getFields() instanceof ByteArrayFields) { for (int i = 0; i < length; i++) { fields.setByteValue(ctx, dstIdx + i, src.getByteValue(i + srcIdx)); } } else if (eiSrc.getFields() instanceof ReferenceArrayFields) { for (int i = 0; i < length; i++) { fields.setReferenceValue(ctx, dstIdx + i, src.getReferenceValue(i + srcIdx)); } } else if (eiSrc.getFields() instanceof BooleanArrayFields) { for (int i = 0; i < length; i++) { fields.setBooleanValue(ctx, dstIdx + i, src.getBooleanValue(i + srcIdx)); } } else if (eiSrc.getFields() instanceof ShortArrayFields) { for (int i = 0; i < length; i++) { fields.setShortValue(ctx, dstIdx + i, src.getShortValue(i + srcIdx)); } } else if (eiSrc.getFields() instanceof FloatArrayFields) { for (int i = 0; i < length; i++) { fields.setFloatValue(ctx, dstIdx + i, src.getFloatValue(i + srcIdx)); } } else { throw new RuntimeException("TODO implement array copy for " + src.getClass()); } } catch (JPFException e) { throw new ArrayStoreException(e.getMessage()); } } else { System.arraycopy(srcVals.getValue(), srcIdx, dstVals.getValue(), dstIdx, length); } } else { if (fields instanceof CharArrayFields) { for (int i = 0; i < length; i++) { fields.setCharValue(ctx, dstIdx + i, eiSrc.getFields().getCharValue(i + srcIdx)); } } else { throw new RuntimeException("TODO implement"); } } // now take care of the attributes // <2do> what in case arraycopy did throw - we should only copy the changed element attrs if (eiSrc.hasFieldAttr()){ if (eiSrc == this && srcIdx < dstIdx) { // self copy for (int i = length - 1; i >= 0; i--) { Object a = eiSrc.getElementAttr( srcIdx+i); setElementAttr( dstIdx+i, a); } } else { for (int i = 0; i < length; i++) { Object a = eiSrc.getElementAttr(srcIdx+i); setElementAttr( dstIdx+i, a); } } } } public void setBooleanElement(FeatureExpr ctx, int idx, Conditional<Boolean> value){ checkArray(idx); checkIsModifiable(); fields.setBooleanValue(ctx, idx, value); } public void setByteElement(FeatureExpr ctx, int idx, Conditional<Byte> value){ checkArray(idx); checkIsModifiable(); fields.setByteValue(ctx, idx, value); } public void setCharElement(FeatureExpr ctx, int idx, Conditional<Character> value){ checkArray(idx); checkIsModifiable(); fields.setCharValue(ctx, idx, value); } public void setShortElement(FeatureExpr ctx, int idx, Conditional<Short> value){ checkArray(idx); checkIsModifiable(); fields.setShortValue(ctx, idx, value); } public void setIntElement(FeatureExpr ctx, int idx, Conditional<Integer> value){ checkArray(idx); checkIsModifiable(); fields.setIntValue(ctx, idx, value); } public void setLongElement(FeatureExpr ctx, int idx, Conditional<Long> value) { checkArray(idx); checkIsModifiable(); fields.setLongValue(ctx, idx, value); } public void setFloatElement(FeatureExpr ctx, int idx, Conditional<Float> value){ checkArray(idx); checkIsModifiable(); fields.setFloatValue(ctx, idx, value); } public void setDoubleElement(FeatureExpr ctx, int idx, Conditional<Double> value){ checkArray(idx); checkIsModifiable(); fields.setDoubleValue(ctx, idx, value); } public void setReferenceElement(FeatureExpr ctx, int idx, Conditional<Integer> value){ checkArray(idx); checkIsModifiable(); fields.setReferenceValue(ctx, idx, value); } public Conditional<Boolean> getBooleanElement(int idx) { checkArray(idx); return fields.getBooleanValue(idx); } public Conditional<Byte> getByteElement(int idx) { checkArray(idx); return fields.getByteValue(idx); } public Conditional<Character> getCharElement(int idx) { checkArray(idx); return fields.getCharValue(idx); } public Conditional<Short> getShortElement(int idx) { checkArray(idx); return fields.getShortValue(idx); } public Conditional<Integer> getIntElement(int idx) { checkArray(idx); return fields.getIntValue(idx); // Conditional<Integer> res = // System.out.println(res); // // return res == null ? 0 : res.getValue(); } public Conditional<Long> getLongElement(int idx) { checkArray(idx); return fields.getLongValue(idx); } public Conditional<Float> getFloatElement(int idx) { checkArray(idx); return fields.getFloatValue(idx); } public Conditional<Double> getDoubleElement(int idx) { checkArray(idx); return fields.getDoubleValue(idx); } public Conditional<Integer> getReferenceElement(int idx) { checkArray(idx); return fields.getReferenceValue(idx); } public void setObjectRef(int newObjRef) { objRef = newObjRef; } public int getObjectRef() { return objRef; } /** use getObjectRef() - this is not an index */ @Deprecated public int getIndex(){ return objRef; } public int getLockCount() { return monitor.getLockCount(); } public ThreadInfo getLockingThread() { return monitor.getLockingThread(); } public boolean isLocked() { return (monitor.getLockCount() > 0); } public boolean isArray() { return ci.isArray(); } public boolean isCharArray(){ return (fields instanceof CharArrayFields); } public boolean isFloatArray(){ return (fields instanceof FloatArrayFields); } public boolean isDoubleArray(){ return (fields instanceof DoubleArrayFields); } public String getArrayType() { if (!ci.isArray()) { throw new JPFException("object is not an array"); } return Types.getArrayElementType(ci.getType()); } public Object getBacktrackData() { return null; } // <2do> these will check for corresponding ArrayFields types public Conditional<Boolean>[] asBooleanArray() { if (fields instanceof ArrayFields){ return ((ArrayFields)fields).asBooleanArray(); } else { throw new JPFException("not an array: " + ci.getName()); } } public Conditional<Byte>[] asByteArray() { if (fields instanceof ArrayFields){ return ((ArrayFields)fields).asByteArray(); } else { throw new JPFException("not an array: " + ci.getName()); } } public Conditional<Short>[] asShortArray() { if (fields instanceof ArrayFields){ return ((ArrayFields)fields).asShortArray(); } else { throw new JPFException("not an array: " + ci.getName()); } } public Conditional<char[]> asCharArray() { if (fields instanceof ArrayFields){ return ((ArrayFields)fields).asCharArray(); } else { throw new JPFException("not an array: " + ci.getName()); } } public Conditional<Integer>[] asIntArray() { if (fields instanceof ArrayFields){ return ((ArrayFields)fields).asIntArray(); } else { throw new JPFException("not an array: " + ci.getName()); } } public Conditional<Long>[] asLongArray() { if (fields instanceof ArrayFields){ return ((ArrayFields)fields).asLongArray(); } else { throw new JPFException("not an array: " + ci.getName()); } } public Conditional<Float>[] asFloatArray() { if (fields instanceof ArrayFields){ return ((ArrayFields)fields).asFloatArray(); } else { throw new JPFException("not an array: " + ci.getName()); } } public Conditional<Double>[] asDoubleArray() { if (fields instanceof ArrayFields){ return ((ArrayFields)fields).asDoubleArray(); } else { throw new JPFException("not an array: " + ci.getName()); } } public Conditional<Integer>[] asReferenceArray() { if (fields instanceof ArrayFields){ return ((ArrayFields)fields).asReferenceArray(); } else { throw new JPFException("not an array: " + ci.getName()); } } public boolean isNull() { return (objRef == MJIEnv.NULL); } public ElementInfo getDeclaredObjectField(String fname, String referenceType) { return VM.getVM().getHeap().get(getDeclaredReferenceField(fname, referenceType).getValue()); } public ElementInfo getObjectField(String fname) { return VM.getVM().getHeap().get(getReferenceField(fname).getValue()); } /** * answer an estimate of the heap size in bytes (this is of course VM * dependent, but we can give an upper bound for the fields/elements, and that * should be good in terms of application specific properties) */ public int getHeapSize() { return fields.getHeapSize(); } public Conditional<String> getStringField(FeatureExpr ctx, String fname) { int ref = getReferenceField(fname).simplify(ctx).getValue(); if (ref != MJIEnv.NULL) { ElementInfo ei = VM.getVM().getHeap().get(ref); return ei.asString(); } else { return new One<>("null"); } } public String getType() { return ci.getType(); } /** * get a cloned list of the waiters for this object */ public ThreadInfo[] getWaitingThreads() { return monitor.getWaitingThreads(); } public boolean hasWaitingThreads() { return monitor.hasWaitingThreads(); } public ThreadInfo[] getBlockedThreads() { return monitor.getBlockedThreads(); } public ThreadInfo[] getBlockedOrWaitingThreads() { return monitor.getBlockedOrWaitingThreads(); } public Conditional<Integer> arrayLength() { if (fields instanceof ArrayFields){ return ((ArrayFields)fields).arrayLength(); } else { throw new JPFException("not an array: " + ci.getName()); } } public boolean isStringObject() { return ClassInfo.isStringClassInfo(ci); } public Conditional<String> asString() { throw new JPFException("not a String object: " + this); } public Conditional<char[]> getStringChars(){ throw new JPFException("not a String object: " + this); } /** * just a helper to avoid creating objects just for the sake of comparing */ public boolean equalsString (String s) { throw new JPFException("not a String object: " + this); } /** * is this a Number, a Boolean or a Character object * Note these classes are all final, so we don't have to check for subtypes * * <2do> we should probably use a regular expression here */ public boolean isBoxObject(){ return false; } public Object asBoxObject(){ throw new JPFException("not a box object: " + this); } void updateLockingInfo() { int i; ThreadInfo ti = monitor.getLockingThread(); if (ti != null) { // here we can update ThreadInfo lock object info (so that we don't // have to store it separately) // NOTE - the threads need to be restored *before* the ElementInfo containers, // or this is going to choke // note that we add only once, i.e. rely on the monitor lockCount to // determine when to remove an object from our lock set //assert area.ks.tl.get(ti.objRef) == ti; // covered by verifyLockInfo ti.updateLockedObject(this); } if (monitor.hasLockedThreads()) { ThreadInfo[] lockedThreads = monitor.getLockedThreads(); for (i=0; i<lockedThreads.length; i++) { ti = lockedThreads[i]; //assert area.ks.tl.get(ti.objRef) == ti; // covered by verifyLockInfo // note that the thread might still be runnable if we have several threads // competing for the same lock if (!ti.isRunnable()){ ti.setLockRef(objRef); } } } } public boolean canLock(ThreadInfo th) { return monitor.canLock(th); } public void checkArrayBounds(FeatureExpr ctx, int index) throws ArrayIndexOutOfBoundsExecutiveException { if (fields instanceof ArrayFields) { if (index < 0 || index >= ((ArrayFields)fields).arrayLength().getValue()){ throw new ArrayIndexOutOfBoundsException(index); } } else { throw new JPFException("object is not an array: " + this); } } public ElementInfo clone() { try { ElementInfo ei = (ElementInfo) super.clone(); ei.fields = fields.clone(); ei.monitor = monitor.clone(); return ei; } catch (CloneNotSupportedException e) { throw new InternalError("should not happen"); } } // this is the one that should be used by heap public ElementInfo deepClone() { try { ElementInfo ei = (ElementInfo) super.clone(); ei.fields = fields.clone(); ei.monitor = monitor.clone(); // referencingThreads is at least subtree global, hence doesn't need to be cloned ei.cachedMemento = null; ei.defreeze(); return ei; } catch (CloneNotSupportedException e) { throw new InternalError("should not happen"); } } public boolean instanceOf(String type) { return Types.instanceOf(ci.getType(), type); } abstract public int getNumberOfFields(); abstract public FieldInfo getFieldInfo(int fieldIndex); /** * threads that will grab our lock on their next execution have to be * registered, so that they can be blocked in case somebody else gets * scheduled */ public void registerLockContender (ThreadInfo ti) { assert ti.lockRef == MJIEnv.NULL || ti.lockRef == objRef : "thread " + ti + " trying to register for : " + this + " ,but already blocked on: " + ti.getElementInfo(ti.lockRef); // note that using the lockedThreads list is a bit counter-intuitive // since the thread is still in RUNNING or UNBLOCKED state, but it will // remove itself from there once it resumes: lock() calls setMonitorWithoutLocked(ti) setMonitorWithLocked(ti); // added to satisfy invariant implied by updateLockingInfo() -peterd //ti.setLockRef(objRef); } /** * somebody made up his mind and decided not to enter a synchronized section * of code it had registered before (e.g. INVOKECLINIT) */ public void unregisterLockContender (ThreadInfo ti) { setMonitorWithoutLocked(ti); // moved from INVOKECLINIT -peterd //ti.resetLockRef(); } void blockLockContenders () { // check if there are any other threads that have to change status because they // require to lock us in their next exec ThreadInfo[] lockedThreads = monitor.getLockedThreads(); for (int i=0; i<lockedThreads.length; i++) { ThreadInfo ti = lockedThreads[i]; if (ti.isRunnable()) { ti.setBlockedState(objRef); } } } /** * from a MONITOR_ENTER or sync INVOKExx if we cannot acquire the lock * note: this is not called from a NOTIFIED_UNBLOCKED state, so we don't have to restore NOTIFIED */ public void block (ThreadInfo ti) { assert (monitor.getLockingThread() != null) && (monitor.getLockingThread() != ti) : "attempt to block " + ti.getName() + " on unlocked or own locked object: " + this; setMonitorWithLocked(ti); ti.setBlockedState(objRef); } /** * from a MONITOR_ENTER or sync INVOKExx if we can acquire the lock */ public void lock (ThreadInfo ti) { // if we do unlock consistency checks with JPFExceptions, we should do the same here if ((monitor.getLockingThread() != null) && (monitor.getLockingThread() != ti)){ throw new JPFException("thread " + ti.getName() + " tries to lock object: " + this + " which is locked by: " + monitor.getLockingThread().getName()); } // the thread might be still in the lockedThreads list if this is the // first step of a transition setMonitorWithoutLocked(ti); monitor.setLockingThread(ti); monitor.incLockCount(); // before we enter anything else, mark this thread as not being blocked anymore ti.resetLockRef(); ThreadInfo.State state = ti.getState(); if (state == ThreadInfo.State.UNBLOCKED) { ti.setState(ThreadInfo.State.RUNNING); } // don't re-add if we are recursive - the lock count is avaliable in the monitor if (monitor.getLockCount() == 1) { ti.addLockedObject(this); } // this might set other threads blocked - make sure we lock first or the sequence // of notifications is a bit screwed (i.e. the lock would appear *after* the block) blockLockContenders(); } /** * from a MONITOR_EXIT or sync method RETURN * release a possibly recursive lock if lockCount goes to zero */ public void unlock (ThreadInfo ti) { checkIsModifiable(); /* If there is a compiler bug, we need to flag it. Most compilers should * generate balanced monitorenter and monitorexit instructions for all code * paths. The VM is being used for more non-Java languages. Some of these * compilers might be experimental and might generate unbalanced * instructions. In a more likely case, dynamically generated bytecode is * more likely to make a mistake and miss a code path. */ if (monitor.getLockCount() <= 0) {// TODO JENS remove System.err.println("monitor already unlocked"); return; } if ((monitor.getLockCount() <= 0) || (monitor.getLockingThread() != ti)){ throw new JPFException("thread " + ti.getName() + " tries to release non-owned lock for object: " + this); } if (monitor.getLockCount() == 1) { ti.removeLockedObject(this); ThreadInfo[] lockedThreads = monitor.getLockedThreads(); for (int i = 0; i < lockedThreads.length; i++) { ThreadInfo lti = lockedThreads[i]; switch (lti.getState()) { case BLOCKED: case NOTIFIED: case TIMEDOUT: case INTERRUPTED: // Ok, this thread becomes runnable again lti.resetLockRef(); lti.setState(ThreadInfo.State.UNBLOCKED); break; case WAITING: case TIMEOUT_WAITING: // nothing to do yet, thread has to timeout, get notified, or interrupted break; default: assert false : "Monitor.lockedThreads<->ThreadData.status inconsistency! " + lockedThreads[i].getStateName(); // why is it in the list - when someone unlocks, all others should have been blocked } } // leave the contenders - we need to know whom to block on subsequent lock monitor.decLockCount(); monitor.setLockingThread(null); } else { // recursive unlock monitor.decLockCount(); } } /** * notifies one of the waiters. Note this is a potentially non-deterministic action * if we have several waiters, since we have to try all possible choices. * Note that even if we notify a thread here, it still remains in the lockedThreads * list until the lock is released (notified threads cannot run right away) */ public void notifies(SystemState ss, ThreadInfo ti){ notifies(ss, ti, true); } private void notifies0 (ThreadInfo tiWaiter){ if (tiWaiter.isWaiting()){ if (tiWaiter.getLockCount() > 0) { // waiter did hold the lock, but gave it up in the wait, so it can't run yet tiWaiter.setNotifiedState(); } else { // waiter didn't hold the lock, set it running setMonitorWithoutLocked(tiWaiter); tiWaiter.resetLockRef(); tiWaiter.setRunning(); } } } public void notifies (SystemState ss, ThreadInfo ti, boolean hasToHoldLock){ if (hasToHoldLock){ assert monitor.getLockingThread() != null : "notify on unlocked object: " + this; } ThreadInfo[] locked = monitor.getLockedThreads(); int i, nWaiters=0, iWaiter=0; for (i=0; i<locked.length; i++) { if (locked[i].isWaiting()) { nWaiters++; iWaiter = i; } } if (nWaiters == 0) { // no waiters, nothing to do } else if (nWaiters == 1) { // only one waiter, no choice point notifies0(locked[iWaiter]); } else { // Ok, this is the non-deterministic case ThreadChoiceGenerator cg = ss.getCurrentChoiceGeneratorOfType(ThreadChoiceGenerator.class); assert (cg != null) : "no ThreadChoiceGenerator in notify"; notifies0(cg.getNextChoice()); } ti.getVM().notifyObjectNotifies(ti, this); } /** * notify all waiters. This is a deterministic action * all waiters remain in the locked list, since they still have to be unblocked, * which happens in the unlock (monitor_exit or sync return) following the notifyAll() */ public void notifiesAll() { assert monitor.getLockingThread() != null : "notifyAll on unlocked object: " + this; ThreadInfo[] locked = monitor.getLockedThreads(); for (int i=0; i<locked.length; i++) { // !!!! if there is more than one BLOCKED thread (sync call or monitor enter), only one can be // unblocked notifies0(locked[i]); } VM.getVM().notifyObjectNotifiesAll(ThreadInfo.currentThread, this); } /** * wait to be notified. thread has to hold the lock, but gives it up in the wait. * Make sure lockCount can be restored properly upon notification */ public void wait(ThreadInfo ti, long timeout){ wait(ti,timeout,true); } // this is used from a context where we don't require a lock, e.g. Unsafe.park()/unpark() @SuppressWarnings("incomplete-switch") public void wait (ThreadInfo ti, long timeout, boolean hasToHoldLock){ checkIsModifiable(); boolean holdsLock = monitor.getLockingThread() == ti; if (hasToHoldLock){ assert holdsLock : "wait on unlocked object: " + this; } setMonitorWithLocked(ti); ti.setLockRef(objRef); if (timeout == 0) { ti.setState(ThreadInfo.State.WAITING); } else { ti.setState(ThreadInfo.State.TIMEOUT_WAITING); } if (holdsLock) { ti.setLockCount(monitor.getLockCount()); monitor.setLockingThread(null); monitor.setLockCount(0); ti.removeLockedObject(this); // unblock all runnable threads that are blocked on this lock ThreadInfo[] lockedThreads = monitor.getLockedThreads(); for (int i = 0; i < lockedThreads.length; i++) { ThreadInfo lti = lockedThreads[i]; switch (lti.getState()) { case NOTIFIED: case BLOCKED: case INTERRUPTED: lti.resetLockRef(); lti.setState(ThreadInfo.State.UNBLOCKED); break; } } } // <2do> not sure if this is right if we don't hold the lock ti.getVM().notifyObjectWait(ti, this); } /** * re-acquire lock after being notified. This is the notified thread, i.e. the one * that will come out of a wait() */ public void lockNotified (ThreadInfo ti) { assert ti.isUnblocked() : "resume waiting thread " + ti.getName() + " which is not unblocked"; setMonitorWithoutLocked(ti); monitor.setLockingThread( ti); monitor.setLockCount( ti.getLockCount()); ti.setLockCount(0); ti.resetLockRef(); ti.setState( ThreadInfo.State.RUNNING); blockLockContenders(); // this is important, if we later-on backtrack (reset the // ThreadInfo.lockedObjects set, and then restore from the saved heap), the // lock set would not include the lock when we continue to enter this thread ti.addLockedObject(this); //wv: add locked object back here } /** * this is for waiters that did not own the lock */ public void resumeNonlockedWaiter (ThreadInfo ti){ setMonitorWithoutLocked(ti); ti.setLockCount(0); ti.resetLockRef(); ti.setRunning(); } void dumpMonitor () { PrintWriter pw = new PrintWriter(System.out, true); pw.print( "monitor "); //pw.print( mIndex); monitor.printFields(pw); pw.flush(); } /** * updates a pinDown counter. If it is > 0 the object is kept alive regardless * if it is reachable from live objects or not. * @return true if the new counter is 1, i.e. the object just became pinned down * * NOTE - this is not a public method, pinning down an object is now * done through the Heap API, which updates the counter here, but might also * have to update internal caches */ boolean incPinDown() { int pdCount = (attributes & ATTR_PINDOWN_MASK); pdCount++; if ((pdCount & ~ATTR_PINDOWN_MASK) != 0){ throw new JPFException("pinDown limit exceeded: " + this); } else { int a = (attributes & ~ATTR_PINDOWN_MASK); a |= pdCount; a |= ATTR_ATTRIBUTE_CHANGED; attributes = a; return (pdCount == 1); } } /** * see incPinDown * * @return true if the counter becomes 0, i.e. the object just ceased to be * pinned down */ boolean decPinDown() { int pdCount = (attributes & ATTR_PINDOWN_MASK); if (pdCount > 0){ pdCount--; int a = (attributes & ~ATTR_PINDOWN_MASK); a |= pdCount; a |= ATTR_ATTRIBUTE_CHANGED; attributes = a; return (pdCount == 0); } else { return false; } } public int getPinDownCount() { return (attributes & ATTR_PINDOWN_MASK); } public boolean isPinnedDown() { return (attributes & ATTR_PINDOWN_MASK) != 0; } public boolean isConstructed() { return (attributes & ATTR_CONSTRUCTED) != 0; } public void setConstructed() { attributes |= (ATTR_CONSTRUCTED | ATTR_ATTRIBUTE_CHANGED); } public void restoreFields(Fields f) { fields = f; } /** * BEWARE - never change the returned object without knowing about the * ElementInfo change status, this field is state managed! */ public Fields getFields() { return fields; } public ArrayFields getArrayFields(){ if (fields instanceof ArrayFields){ return (ArrayFields)fields; } else { throw new JPFException("not an array: " + ci.getName()); } } public void restore(int index, int attributes, Fields fields, Monitor monitor){ markUnchanged(); this.objRef = index; this.attributes = attributes; this.fields = fields; this.monitor = monitor; } public void restoreMonitor(Monitor m) { monitor = m; } /** * BEWARE - never change the returned object without knowing about the * ElementInfo change status, this field is state managed! */ public Monitor getMonitor() { return monitor; } public void restoreAttributes(int a) { attributes = a; } public boolean isAlive(boolean liveBitValue) { return ((attributes & ATTR_LIVE_BIT) == 0) ^ liveBitValue; } public void setAlive(boolean liveBitValue){ if (liveBitValue){ attributes |= ATTR_LIVE_BIT; } else { attributes &= ~ATTR_LIVE_BIT; } } public boolean isMarked() { return (attributes & ATTR_IS_MARKED) != 0; } public boolean isFinalized() { return (attributes & ATTR_FINALIZED) != 0; } public void setFinalized() { attributes |= ATTR_FINALIZED; } public void setMarked() { attributes |= ATTR_IS_MARKED; } public boolean isMarkedOrAlive (boolean liveBitValue){ return ((attributes & ATTR_IS_MARKED) != 0) | (((attributes & ATTR_LIVE_BIT) == 0) ^ liveBitValue); } public void markUnchanged() { attributes &= ~ATTR_ANY_CHANGED; } public void setUnmarked() { attributes &= ~ATTR_IS_MARKED; } protected void checkIsModifiable() { if ((attributes & ATTR_IS_FROZEN) != 0) { throw new JPFException("attempt to modify frozen object: " + this); } } void setMonitorWithLocked( ThreadInfo ti) { checkIsModifiable(); monitor.addLocked(ti); } void setMonitorWithoutLocked (ThreadInfo ti) { checkIsModifiable(); monitor.removeLocked(ti); } public boolean isLockedBy(ThreadInfo ti) { return ((monitor != null) && (monitor.getLockingThread() == ti)); } public boolean isLocking(ThreadInfo ti){ return (monitor != null) && monitor.isLocking(ti); } void _printAttributes(String cls, String msg, int oldAttrs) { if (getClassInfo().getName().equals(cls)) { System.out.println(msg + " " + this + " attributes: " + Integer.toHexString(attributes) + " was: " + Integer.toHexString(oldAttrs)); } } public void checkConsistency() { /** ThreadInfo ti = monitor.getLockingThread(); if (ti != null) { // object has to be in the lockedObjects list of this thread checkAssertion( ti.getLockedObjects().contains(this), "locked object not in thread: " + ti); } if (monitor.hasLockedThreads()) { checkAssertion( refTid.cardinality() > 1, "locked threads without multiple referencing threads"); for (ThreadInfo lti : monitor.getBlockedOrWaitingThreads()){ checkAssertion( lti.lockRef == objRef, "blocked or waiting thread has invalid lockRef: " + lti); } // we can't check for having lock contenders without being shared, since this can happen // in case an object is behind a FieldInfo shared-ness firewall (e.g. ThreadGroup.threads), or // is kept/used in native code (listener, peer) } **/ } protected void checkAssertion(boolean cond, String failMsg){ if (!cond){ System.out.println("!!!!!! failed ElementInfo consistency: " + this + ": " + failMsg); System.out.println("object: " + this); System.out.println("usingTi: " + referencingThreads); ThreadInfo tiLock = getLockingThread(); if (tiLock != null) System.out.println("locked by: " + tiLock); if (monitor.hasLockedThreads()){ System.out.println("lock contenders:"); for (ThreadInfo ti : monitor.getLockedThreads()){ System.out.println(" " + ti + " = " + ti.getState()); } } VM.getVM().dumpThreadStates(); assert false; } } }