Java tutorial
/******************************************************************************* * Indus, a program analysis and transformation toolkit for Java. * Copyright (c) 2001, 2007 Venkatesh Prasad Ranganath * * All rights reserved. This program and the accompanying materials are made * available under the terms of the Eclipse Public License v1.0 which accompanies * the distribution containing this program, and is available at * http://www.opensource.org/licenses/eclipse-1.0.php. * * For questions about the license, copyright, and software, contact * Venkatesh Prasad Ranganath at venkateshprasad.ranganath@gmail.com * * This software was developed by Venkatesh Prasad Ranganath in SAnToS Laboratory * at Kansas State University. *******************************************************************************/ package edu.ksu.cis.indus.staticanalyses.concurrency.escape; import edu.ksu.cis.indus.common.collections.CollectionUtils; import edu.ksu.cis.indus.common.collections.ITransformer; import edu.ksu.cis.indus.common.collections.SetUtils; import edu.ksu.cis.indus.common.datastructures.FastUnionFindElement; import edu.ksu.cis.indus.common.datastructures.HistoryAwareFIFOWorkBag; import edu.ksu.cis.indus.common.datastructures.HistoryAwareLIFOWorkBag; import edu.ksu.cis.indus.common.datastructures.IWorkBag; import edu.ksu.cis.indus.common.datastructures.Pair; import edu.ksu.cis.indus.common.datastructures.Triple; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.lang.builder.ToStringBuilder; import soot.SootClass; import soot.SootMethod; import soot.Type; import soot.jimple.InvokeStmt; /** * This class represents an alias set as specified in the techreport <a * href="http://www.cis.ksu.edu/santos/papers/technicalReports/SAnToS-TR2003-6.pdf">Honing the Detection of Interference and * Ready Dependence for Slicing Concurrent Java Programs.</a> It represents an equivalence class in escape analysis defined * in the same document. * * @author <a href="http://www.cis.ksu.edu/~rvprasad">Venkatesh Prasad Ranganath</a> * @author $Author: rvprasad $ * @version $Revision: 1.66 $ */ final class AliasSet extends FastUnionFindElement<AliasSet> implements Cloneable { /** * This is used to generate unique lock entities. */ private static int lockEntityCount; /** * This is used to generate unique ready entities. */ private static long readyEntityCount; /** * This is used to generate unique reference entities. */ private static int referenceEntityCount; /** * This is used to retrieve the representative of the alias set. */ private static final ITransformer<AliasSet, AliasSet> REPRESENTATIVE_ALIAS_SET_RETRIEVER = new ITransformer<AliasSet, AliasSet>() { public AliasSet transform(final AliasSet input) { return input.find(); } }; /** * This is used to generate unique share entities. */ private static int shareEntityCount; /** * This indicates if the object associated with the alias set was accessed. This is related to read-write info and not to * escape or shared info. */ private boolean accessed; /** * This maps field signatures to their alias sets. */ private Map<String, AliasSet> fieldMap; /** * This field indicates if this alias set is associated with a static field or a field reachable from a static field. This * field is orthogonal to sharing information. It is used solely for the purpose of tracking globalness of the alias sets * and if methods read and write global data. */ private boolean global; /** * This represents the interprocedural reference entities associated with this alias set. */ private Collection<Object> intraThreadInterProcRefEntities; /** * This indicates the alias set participated in a locking operation. */ private boolean locked; /** * This represents the lock entities associated with this alias set. */ private Collection<Object> lockEntities; /** * This is used to indicate that the alias set represents an object that is accessible in multiple threads. This differs * from sharing as this captures the exposure of the object in multiple threads and not the access. */ private boolean multiThreadAccessibility; /** * This indicates if the variable associated with this alias set is the receiver of <code>notify()/notifyAll()</code> * call. */ private boolean notifies; /** * This is the signatures of the fields of the objects associated with this alias set that are read. */ private Collection<String> readFields; /** * The threads that read fields of the associated object. */ private Collection<Triple<InvokeStmt, SootMethod, SootClass>> readThreads; /** * This represents the read-write entities associated with this alias set. */ private Collection<Object> readwriteEntities; /** * This represents the ready Entities associated with this alias set. */ private Collection<Object> readyEntities; /** * This is the collection of field signatures of fields of this alias set's object that are shared across multiple threads * via read-write access. */ private Collection<String> sigsOfRWSharedFields; /** * This is the collection of field signatures of fields of this alias set's object that are shared across multiple threads * via write-write access. */ private Collection<String> sigsOfWWSharedFields; /** * This indicates that this object is being stringified. */ private boolean stringifying; /** * This indicates if the variable associated with this alias set is the receiver of <code>wait()</code> call. */ private boolean waits; /** * The threads that write fields of the associated object. */ private Collection<Triple<InvokeStmt, SootMethod, SootClass>> writeThreads; /** * This represents the write-write entities associated with this alias set. */ private Collection<Object> writewriteEntities; /** * This is the signatures of the fields of the objects associated with this alias set that are written. */ private Collection<String> writtenFields; /** * Creates a new instance of this class. */ private AliasSet() { fieldMap = new HashMap<String, AliasSet>(); accessed = false; readyEntities = null; readThreads = new HashSet<Triple<InvokeStmt, SootMethod, SootClass>>(); writeThreads = new HashSet<Triple<InvokeStmt, SootMethod, SootClass>>(); readwriteEntities = null; writewriteEntities = null; sigsOfRWSharedFields = null; sigsOfWWSharedFields = null; lockEntities = null; intraThreadInterProcRefEntities = null; multiThreadAccessibility = false; readFields = Collections.emptySet(); writtenFields = Collections.emptySet(); } /** * Creates a new alias set. * * @return a new alias set. * @post result != null */ static AliasSet createAliasSet() { return new AliasSet(); } /** * Creates an alias set suitable for the given type. * * @param type is the type from which Alias set is requested. * @return the alias set corresponding to the given type. * @post AliasSet.canHaveAliasSet(type) implies result != null * @post not AliasSet.canHaveAliasSet(type) implies result == null */ static AliasSet getASForType(final Type type) { AliasSet _result = null; if (EquivalenceClassBasedEscapeAnalysis.canHaveAliasSet(type)) { _result = new AliasSet(); } return _result; } /** * Unifies the given object with itself. This is required when the alias set is occurs in the context of a site which is * executed multiple times, in particular, reachable from a call-site which may be executed multiple times. * * @param as the alias set to be unified with itself. * @pre as != null */ static void selfUnify(final AliasSet as) { final Collection<AliasSet> _processed = new HashSet<AliasSet>(); final IWorkBag<AliasSet> _wb = new HistoryAwareLIFOWorkBag<AliasSet>(_processed); _wb.addWork(as.find()); while (_wb.hasWork()) { final AliasSet _repr = _wb.getWork(); _repr.unifyThreadEscapeInfo(_repr); _wb.addAllWorkNoDuplicates( CollectionUtils.collect(_repr.fieldMap.values(), REPRESENTATIVE_ALIAS_SET_RETRIEVER)); } } /** * Returns a new lock entity object. * * @return a new lock entity object. * @post result != null */ private static Object getNewLockEntity() { return "LockEntity:" + lockEntityCount++; } /** * Returns a new ready entity object. * * @return a new ready entity object. * @post result != null */ private static Object getNewReadyEntity() { return "ReadyEntity:" + readyEntityCount++; } /** * Returns a new lock entity object. * * @return a new lock entity object. * @post result != null */ private static Object getNewReferenceEntity() { return "ReferenceEntity:" + referenceEntityCount++; } /** * Returns a new share entity object. * * @return a new share entity object. * @post result != null */ private static Object getNewShareEntity() { return "ShareEntity:" + shareEntityCount++; } /** * Clones this alias set. * * @return the clone of this object. * @throws CloneNotSupportedException is thrown if it is thrown by <code>java.lang.Object.clone()</code>. * @post result != null and result.set != null and result.fieldMap != self.fieldMap */ @Override public AliasSet clone() throws CloneNotSupportedException { final AliasSet _result; if (find() != this) { // just work on the representative of the class _result = find().clone(); } else { final AliasSet _clone = (AliasSet) super.clone(); _clone.fieldMap = (Map) ((HashMap<String, AliasSet>) fieldMap).clone(); _clone.fieldMap.clear(); if (readyEntities != null) { _clone.readyEntities = (Collection) ((HashSet<Object>) readyEntities).clone(); } if (readwriteEntities != null) { _clone.readwriteEntities = (Collection) ((HashSet<Object>) readwriteEntities).clone(); } if (writewriteEntities != null) { _clone.writewriteEntities = (Collection) ((HashSet<Object>) writewriteEntities).clone(); } _clone.intraThreadInterProcRefEntities = null; final Collection<String> _emptySet = Collections.emptySet(); if (readFields != _emptySet) { _clone.readFields = (Collection) ((HashSet<String>) readFields).clone(); } if (writtenFields != _emptySet) { _clone.writtenFields = (Collection) ((HashSet<String>) writtenFields).clone(); } _clone.readThreads = new HashSet<Triple<InvokeStmt, SootMethod, SootClass>>(readThreads); _clone.writeThreads = new HashSet<Triple<InvokeStmt, SootMethod, SootClass>>(writeThreads); _clone.set = null; _result = _clone; } return _result; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { final boolean _result; if (obj instanceof AliasSet) { final AliasSet _a = (AliasSet) obj; if (_a.find() == find()) { _result = true; } else { _result = false; } } else { _result = super.equals(obj); } return _result; } /** * {@inheritDoc} */ @Override public int hashCode() { final int _result; if (find() != this) { _result = find().hashCode(); } else { _result = super.hashCode(); } return _result; } /** * {@inheritDoc} * * @see java.lang.Object#toString() */ @Override public String toString() { final String _result; if (find() != this) { _result = find().toString(); } else { if (stringifying) { _result = Integer.toHexString(hashCode()); } else { stringifying = true; _result = new ToStringBuilder(this).append("multiThreadAccess", this.multiThreadAccessibility) .append("accessed", this.accessed).append("notifies", this.notifies) .append("waits", this.waits).append("locked", this.locked) .append("writtenFields", this.writtenFields).append("readFields", this.readFields) .append("readyEntities", this.readyEntities).append("lockEntities", this.lockEntities) .append("rwEntities", this.readwriteEntities).append("wwEntities", this.writewriteEntities) .append("intraProcRefEntities", this.intraThreadInterProcRefEntities) .append("sigsOfSharedFields", sigsOfRWSharedFields) .append("sigsOfWriteWriteSharedFields", sigsOfWWSharedFields) .append("readThreads", readThreads).append("writeThreads", writeThreads) .append("fieldMap", this.fieldMap).toString(); stringifying = false; } } return _result; } /** * Adds the given field signature to the collection of read fields of this alias set's object. * * @param fieldSig is the field signature. */ void addReadField(final String fieldSig) { final AliasSet _l = find(); final Collection<String> _emptySet = Collections.emptySet(); if (_l.readFields == _emptySet) { _l.readFields = new HashSet<String>(); } _l.readFields.add(fieldSig); } /** * Adds the given threads as the threads that read a member of an object via the variable associated with this aliasset. * * @param abstractThreads collection of abstract thread objects in which the write occurs. */ void addReadThreads(final Collection<Triple<InvokeStmt, SootMethod, SootClass>> abstractThreads) { find().readThreads.addAll(abstractThreads); } /** * Adds the given threads as the threads that wrote a member of an object via the variable associated with this aliasset. * * @param abstractThreads collection of abstract thread objects in which the write occurs. */ void addWriteThreads(final Collection<Triple<InvokeStmt, SootMethod, SootClass>> abstractThreads) { find().writeThreads.addAll(abstractThreads); } /** * Adds the given field signature to the collection of written fields of this alias set's object. * * @param fieldSig is the field signature. */ void addWrittenField(final String fieldSig) { final AliasSet _l = find(); final Collection<String> _emptySet = Collections.emptySet(); if (_l.writtenFields == _emptySet) { _l.writtenFields = new HashSet<String>(); } _l.writtenFields.add(fieldSig); } /** * Erases intra thread and inter-procedural reference entities. */ void eraseIntraThreadInterProcRefEntities() { if (intraThreadInterProcRefEntities != null) { intraThreadInterProcRefEntities.clear(); } } /** * Checks if the object associated with this alias set is accessible in multiple threads. * * @return <code>true</code> if the object is shared; <code>false</code>, otherwise. */ boolean escapes() { return find().multiThreadAccessibility; } /** * Retrieves the end point of the specified access path. * * @param accesspath is a sequence of primaries in the access path. This should not include the primary for this alias * set. It should contain the stringized form of the field signature or <code>IEscapeInfo.ARRAY_FIELD</code>. * @return the alias set that corresponds to the end point of the access path. <code>null</code> is returned if no such * path exists. */ AliasSet getAccessPathEndPoint(final String[] accesspath) { AliasSet _result = this; for (final String _pathElement : accesspath) { final AliasSet _as = _result.getFieldMap().get(_pathElement); if (_as != null) { _result = _as.find(); } else { _result = null; break; } } return _result; } /** * Retrieves the alias set corresponding to the given field of the object represented by this alias set. * * @param field is the signature of the field. * @return the alias set associated with <code>field</code>. * @post result == self.find().fieldMap.get(field) */ AliasSet getASForField(final String field) { return find().fieldMap.get(field); } /** * Retrieves an unmodifiable copy of the field map of this alias set. * * @return the field map. */ Map<String, AliasSet> getFieldMap() { return Collections.unmodifiableMap(find().fieldMap); } /** * Retrieves the alias set that is reachable from <code>root</code> and that corresponds to <code>ref</code>, an * alias set reachable from this alias set. This method can be used to retrieve the alias set in the site-context * corresponding to an alias side in the callee method context. * * @param root the alias set to start point in the search context. * @param ref the alias set in end point in the reference (this) context. * @param processed a collection of alias set pairs that is used during the search. The contents of this alias set is not * relevant to the caller. * @return the alias set reachable from <code>root</code> and that corresponds to <code>ref</code>. This will be * <code>null</code> if there is no such alias set. * @pre root != null and ref != null and processed != null */ AliasSet getImageOfRefUnderRoot(final AliasSet root, final AliasSet ref, final Collection<Pair<AliasSet, AliasSet>> processed) { AliasSet _result = null; if (ref.find() == find()) { _result = root; } else { processed.add(new Pair<AliasSet, AliasSet>(find(), root.find())); final Set<String> _keySet = getFieldMap().keySet(); final Iterator<String> _i = _keySet.iterator(); final int _iEnd = _keySet.size(); for (int _iIndex = 0; _iIndex < _iEnd && _result == null; _iIndex++) { final String _key = _i.next(); final AliasSet _as1 = getASForField(_key); final AliasSet _as2 = root.getASForField(_key); if (_as1 != null && _as2 != null) { final Pair<AliasSet, AliasSet> _pair = new Pair<AliasSet, AliasSet>(_as1.find(), _as2.find()); if (!processed.contains(_pair)) { _result = _as1.getImageOfRefUnderRoot(_as2, ref, processed); } } } } return _result; } /** * Retrieves the reference entities of this object. * * @return a collection of objects. */ Collection<Object> getIntraProcRefEntities() { final Collection<Object> _collection = find().intraThreadInterProcRefEntities; final Collection<Object> _result; if (_collection != null) { _result = Collections.unmodifiableCollection(_collection); } else { _result = null; } return _result; } /** * Retrieves the lock entities of this object. * * @return a collection of objects. */ Collection<Object> getLockEntities() { final Collection<Object> _collection = find().lockEntities; final Collection<Object> _result; if (_collection != null) { _result = Collections.unmodifiableCollection(_collection); } else { _result = null; } return _result; } /** * Retrieves the threads that read fields of the associated object. * * @return the reading threads. */ Collection<Triple<InvokeStmt, SootMethod, SootClass>> getReadThreads() { final Collection<Triple<InvokeStmt, SootMethod, SootClass>> _collection = find().readThreads; final Collection<Triple<InvokeStmt, SootMethod, SootClass>> _result; if (_collection != null) { _result = Collections.unmodifiableCollection(_collection); } else { _result = Collections.emptySet(); } return _result; } /** * Retrieves the shared entities of this object. * * @return a collection of objects. */ Collection<Object> getReadWriteShareEntities() { final Collection<Object> _collection = find().readwriteEntities; final Collection<Object> _result; if (_collection != null) { _result = Collections.unmodifiableCollection(_collection); } else { _result = null; } return _result; } /** * Retrieves the ready entity object of this alias set. * * @return the associated readyentity object. * @post result == self.find().readyEntity */ Collection<Object> getReadyEntities() { final Collection<Object> _collection = find().readyEntities; final Collection<Object> _result; if (_collection != null) { _result = Collections.unmodifiableCollection(_collection); } else { _result = null; } return _result; } /** * Retrieves the threads that write fields of the associated object. * * @return the writing threads. */ Collection<Triple<InvokeStmt, SootMethod, SootClass>> getWriteThreads() { final Collection<Triple<InvokeStmt, SootMethod, SootClass>> _collection = find().writeThreads; final Collection<Triple<InvokeStmt, SootMethod, SootClass>> _result; if (_collection != null) { _result = Collections.unmodifiableCollection(_collection); } else { _result = Collections.emptySet(); } return _result; } /** * Retrieves the shared entities pertaining to write-write sharing of the associated object. * * @return a collection of objects. */ Collection<Object> getWriteWriteShareEntities() { final Collection<Object> _collection = find().writewriteEntities; final Collection<Object> _result; if (_collection != null) { _result = Collections.unmodifiableCollection(_collection); } else { _result = null; } return _result; } /** * Checks if the alias set was accessed. * * @return <code>true</code> if it was accessed; <code>false</code>, otherwise. */ boolean isAccessed() { return find().accessed; } /** * Checks if this alias set is associated with a global variable. * * @return <code>true</code> if this alias set is associated with a global variable; <code>false</code>, otherwise. */ boolean isGlobal() { return find().global; } /** * Checks if an alias set associated with a global variable is reachable from this alias set. * * @return <code>true</code> if an alias set associated with a global variable is reachable from this alias set.; * <code>false</code>, otherwise. */ boolean isGlobalDataReachable() { boolean _result = false; final IWorkBag<AliasSet> _wb = new HistoryAwareFIFOWorkBag<AliasSet>(new HashSet<AliasSet>()); _wb.addWork(find()); while (!_result && _wb.hasWork()) { final AliasSet _a = _wb.getWork(); _result |= _a.isGlobal(); for (final AliasSet _fs : _a.getFieldMap().values()) { _wb.addWork(_fs.find()); } } return _result; } /** * Checks if an alias set associated with an inter-thread shared variable is reachable from this alias set. * * @return <code>true</code> if an alias set associated with an inter-thread shared variable is reachable from this * alias set.; <code>false</code>, otherwise. */ boolean isSharedDataReachable() { boolean _result = false; final IWorkBag<AliasSet> _process = new HistoryAwareLIFOWorkBag<AliasSet>(new HashSet<AliasSet>()); _process.addWork(find()); while (_process.hasWork() && !_result) { final AliasSet _as = _process.getWork(); _result |= _as.multiThreadAccessibility; for (final AliasSet _fs : _as.getFieldMap().values()) { _process.addWork(_fs.find()); } } return _result; } /** * Checks if the object associated with this alias set is accessed by multiple threads for locks and unlocks. * * @return <code>true</code> if the object is lock-unlock shared; <code>false</code>, otherwise. */ boolean lockUnlockShared() { final AliasSet _rep = find(); return _rep.lockEntities != null && !_rep.lockEntities.isEmpty(); } /** * Marks all reachable alias sets as being crossing thread boundary, i.e, visible in multiple threads. */ void markAsCrossingThreadBoundary() { final IWorkBag<AliasSet> _wb = new HistoryAwareLIFOWorkBag<AliasSet>(new HashSet<AliasSet>()); _wb.addWork(find()); while (_wb.hasWork()) { final AliasSet _as = _wb.getWork(); _as.multiThreadAccessibility = true; for (final Iterator<AliasSet> _i = _as.fieldMap.values().iterator(); _i.hasNext();) { _wb.addWork(_i.next().find()); } } } /** * Checks if the object associated with this alias set is accessed by multiple methods. * * @return <code>true</code> if the object is method shared; <code>false</code>, otherwise. */ boolean methodShared() { final AliasSet _rep = find(); return _rep.intraThreadInterProcRefEntities != null && !_rep.intraThreadInterProcRefEntities.isEmpty(); } /** * Propogates the information from the this alias set to the destination alias set. * * @param to is the destination of the information transfer. * @post to.isShared() == (isShared() or to.isShared()) * @post to.getReadyEntities().containsAll(getReadyEntities()) * @post to.getShareEntities().containsAll(getShareEntities()) */ void propogateInfoFromTo(final AliasSet to) { final IWorkBag<Pair<AliasSet, AliasSet>> _wb = new HistoryAwareLIFOWorkBag<Pair<AliasSet, AliasSet>>( new HashSet<Pair<AliasSet, AliasSet>>()); _wb.addWork(new Pair<AliasSet, AliasSet>(this, to)); while (_wb.hasWork()) { final Pair<AliasSet, AliasSet> _pair = _wb.getWork(); final AliasSet _fromRep = _pair.getFirst().find(); final AliasSet _toRep = _pair.getSecond().find(); if (_fromRep != _toRep) { _toRep.multiThreadAccessibility |= _fromRep.multiThreadAccessibility; /* * This is tricky. A constructor can be called to construct 2 instances in which one is used in wait/notify * but not the other. This means on top-down propogation of alias set in ECBA, the 2 alias set of the primary * of the <init> method will be rep1 and one may provide a non-null ready entity to rep2 and the other may * come and erase it if the check is not made. */ if (_fromRep.readyEntities != null) { if (_toRep.readyEntities == null) { _toRep.readyEntities = new HashSet<Object>(); } _toRep.readyEntities.addAll(_fromRep.readyEntities); } if (_fromRep.readwriteEntities != null) { if (_toRep.readwriteEntities == null) { _toRep.readwriteEntities = new HashSet<Object>(); } _toRep.readwriteEntities.addAll(_fromRep.readwriteEntities); } if (_fromRep.writewriteEntities != null) { if (_toRep.writewriteEntities == null) { _toRep.writewriteEntities = new HashSet<Object>(); } _toRep.writewriteEntities.addAll(_fromRep.writewriteEntities); } if (_fromRep.lockEntities != null) { if (_toRep.lockEntities == null) { _toRep.lockEntities = new HashSet<Object>(); } _toRep.lockEntities.addAll(_fromRep.lockEntities); } if (_fromRep.intraThreadInterProcRefEntities != null) { if (_toRep.intraThreadInterProcRefEntities == null) { _toRep.intraThreadInterProcRefEntities = new HashSet<Object>(); } _toRep.intraThreadInterProcRefEntities.addAll(_fromRep.intraThreadInterProcRefEntities); } if (_fromRep.sigsOfRWSharedFields != null) { if (_toRep.sigsOfRWSharedFields == null) { _toRep.sigsOfRWSharedFields = new HashSet<String>(); } _toRep.sigsOfRWSharedFields.addAll(_fromRep.sigsOfRWSharedFields); } if (_fromRep.sigsOfWWSharedFields != null) { if (_toRep.sigsOfWWSharedFields == null) { _toRep.sigsOfWWSharedFields = new HashSet<String>(); } _toRep.sigsOfWWSharedFields.addAll(_fromRep.sigsOfWWSharedFields); } for (final Iterator<String> _i = _toRep.getFieldMap().keySet().iterator(); _i.hasNext();) { final String _field = _i.next(); final AliasSet _to = _toRep.getASForField(_field); final AliasSet _from = _fromRep.getASForField(_field); if ((_to != null) && (_from != null)) { _wb.addWork(new Pair<AliasSet, AliasSet>(_from, _to)); } } } } } /** * Records the given alias set represents the given field signature. * * @param field for which the alias set info needs to be recorded. * @param as is the alias set associated with <code>field</code> * @pre as != null */ void putASForField(final String field, final AliasSet as) { find().fieldMap.put(field, as); } /** * Checks if the object associated with this alias set is accessed by multiple threads for reads and writes. * * @return <code>true</code> if the object is read-write shared; <code>false</code>, otherwise. */ boolean readWriteShared() { final AliasSet _rep = find(); return _rep.readwriteEntities != null && !_rep.readwriteEntities.isEmpty(); } /** * Checks if the object associated with this alias set is accessed by multiple threads for read and writes of the * specified field. * * @param fieldSignature is the signature of the field. * @return <code>true</code> if the object is shared via read-write of the given field; <code>false</code>, * otherwise. */ boolean readWriteShared(final String fieldSignature) { final AliasSet _rep = find(); return readWriteShared() && _rep.sigsOfRWSharedFields.contains(fieldSignature); } /** * Marks the alias set was accessed. */ void setAccessed() { find().accessed = true; } /** * Marks the alias set as being associated with a global variable. */ void setGlobal() { final IWorkBag<AliasSet> _wb = new HistoryAwareFIFOWorkBag<AliasSet>(new HashSet<AliasSet>()); _wb.addWork(find()); while (_wb.hasWork()) { final AliasSet _a = _wb.getWork(); _a.global = true; for (final AliasSet _fs : _a.getFieldMap().values()) { _wb.addWork(_fs.find()); } } } /** * Marks the alias set as participating in a lock operation. */ void setLocked() { find().locked = true; } /** * Marks the object associated with this alias set as appearing in a <code>notify()/notifyAll()</code> call. * * @post find().notifies == true */ void setNotifies() { find().notifies = true; } /** * Marks the object associated with this alias set as appearing in a <code>wait()</code> call. * * @post find().waits == true */ void setWaits() { find().waits = true; } /** * Unifies the given alias set with this alias set. * * @param a is the alias set to be unified with this alias set. * @pre a != null */ void unifyAliasSet(final AliasSet a) { unifyAliasSetHelper(a, true); } /** * Unifies the given alias set with this alias set. * * @param as2 obviously. * @param unifyAll <code>true</code> indicates that unification should be multi-thread access sensitive; * <code>false</code>, otherwise. * @pre as2 != null */ void unifyAliasSetHelper(final AliasSet as2, final boolean unifyAll) { final AliasSet _m = find(); final AliasSet _n = as2.find(); if (_m != _n) { _m.union(_n); final AliasSet _representative = _m.find(); final AliasSet _represented; if (_representative == _m) { _represented = _n; } else { _represented = _m; } _representative.waits |= _represented.waits; _representative.notifies |= _represented.notifies; _representative.multiThreadAccessibility |= _represented.multiThreadAccessibility; _representative.locked |= _represented.locked; _representative.accessed |= _represented.accessed; _representative.readThreads.addAll(_represented.readThreads); _represented.readThreads = null; _representative.writeThreads.addAll(_represented.writeThreads); _represented.writeThreads = null; if (unifyAll && _representative.multiThreadAccessibility) { _representative.unifyThreadEscapeInfo(_represented); } // We need to unify the read/write fields after unification as they are used to calcuate shared entities in // unifyThreadEscapeInfo() _representative.handleInfoUnification(_represented); _representative.unifyFields(_represented, unifyAll); } else if (unifyAll && _m.multiThreadAccessibility) { _m.unifyThreadEscapeInfo(_n); } } /** * Checks if the object associated with this alias set is accessed by multiple threads for waits and notifys. * * @return <code>true</code> if the object is wait-notify shared; <code>false</code>, otherwise. */ boolean waitNotifyShared() { final AliasSet _rep = find(); return _rep.readyEntities != null && !_rep.readyEntities.isEmpty(); } /** * Checks if the a field of the object associated with alias set was read. * * @return <code>true</code> if it was read; <code>false</code>, otherwise. */ boolean wasAnyFieldRead() { return !find().readFields.isEmpty(); } /** * Checks if the a field of the object associated with alias set was written. * * @return <code>true</code> if it was written; <code>false</code>, otherwise. */ boolean wasAnyFieldWritten() { return !find().writtenFields.isEmpty(); } /** * Checks if the field of the provided signature was read via the object associated with this alias set. * * @param fieldSig is the field signature. * @return <code>true</code> if it was read; <code>false</code>, otherwise. */ boolean wasFieldRead(final String fieldSig) { return find().readFields.contains(fieldSig); } /** * Checks if the given field or an object reachable from it was read. * * @param fieldSig is the field signature. * @param recurse <code>true</code> indicates if alias sets reachable from this alias set should be considered; * <code>false</code>, otherwise. * @return <code>true</code> if this alias set was read or if the given alias set is <code>null</code>; * <code>false</code>, otherwise. */ boolean wasFieldRead(final String fieldSig, final boolean recurse) { boolean _result = wasFieldRead(fieldSig); if (!_result && recurse) { _result = recursiveBooleanPropertyDiscovery(fieldSig, new ITransformer<AliasSet, Boolean>() { public Boolean transform(final AliasSet input) { return Boolean.valueOf(input.wasAnyFieldRead()); } }); } return _result; } /** * Checks if the field of the provided signature was read via the object associated with this alias set. * * @param fieldSig is the field signature. * @return <code>true</code> if it was read; <code>false</code>, otherwise. */ boolean wasFieldWritten(final String fieldSig) { return find().writtenFields.contains(fieldSig); } /** * Checks if the given field or an object reachable from it was written. * * @param fieldSig is the field signature. * @param recurse <code>true</code> indicates if alias sets reachable from this alias set should be considered; * <code>false</code>, otherwise. * @return <code>true</code> if this alias set was written or if the given alias set is <code>null</code>; * <code>false</code>, otherwise. */ boolean wasFieldWritten(final String fieldSig, final boolean recurse) { boolean _result = wasFieldWritten(fieldSig); if (!_result && recurse) { _result = recursiveBooleanPropertyDiscovery(fieldSig, new ITransformer<AliasSet, Boolean>() { public Boolean transform(final AliasSet input) { return Boolean.valueOf(input.wasAnyFieldWritten()); } }); } return _result; } /** * Checks if the object associated with this alias set is accessed by multiple threads for writes. * * @return <code>true</code> if the object is write-write shared; <code>false</code>, otherwise. */ boolean writeWriteShared() { final AliasSet _rep = find(); return _rep.writewriteEntities != null && !_rep.writewriteEntities.isEmpty(); } /** * Checks if the object associated with this alias set is shared by multiple threads for writes of the specified field. * * @param fieldSignature is the signature of the field. * @return <code>true</code> if the object is shared via write-write of the given field; <code>false</code>, * otherwise. */ boolean writeWriteShared(final String fieldSignature) { final AliasSet _rep = find(); return writeWriteShared() && _rep.sigsOfWWSharedFields.contains(fieldSignature); } /** * Handles the unification of entity info. * * @param represented is the alias set being unified with this alias set such that this alias set is the representative * alias set. * @pre represented != null */ private void handleInfoUnification(final AliasSet represented) { if (lockEntities == null) { lockEntities = represented.lockEntities; } else if (represented.lockEntities != null) { lockEntities.addAll(represented.lockEntities); } represented.lockEntities = null; if (intraThreadInterProcRefEntities == null) { intraThreadInterProcRefEntities = represented.intraThreadInterProcRefEntities; if (intraThreadInterProcRefEntities == null) { intraThreadInterProcRefEntities = new HashSet<Object>(); intraThreadInterProcRefEntities.add(getNewReferenceEntity()); } } else if (represented.intraThreadInterProcRefEntities != null) { intraThreadInterProcRefEntities.addAll(represented.intraThreadInterProcRefEntities); } represented.intraThreadInterProcRefEntities = null; if (readyEntities == null) { readyEntities = represented.readyEntities; } else if (represented.readyEntities != null) { readyEntities.addAll(represented.readyEntities); } represented.readyEntities = null; if (readwriteEntities == null) { readwriteEntities = represented.readwriteEntities; } else if (represented.readwriteEntities != null) { readwriteEntities.addAll(represented.readwriteEntities); } represented.readwriteEntities = null; if (writewriteEntities == null) { writewriteEntities = represented.writewriteEntities; } else if (represented.writewriteEntities != null) { writewriteEntities.addAll(represented.writewriteEntities); } represented.writewriteEntities = null; if (sigsOfRWSharedFields == null) { sigsOfRWSharedFields = represented.sigsOfRWSharedFields; } else if (represented.sigsOfRWSharedFields != null) { sigsOfRWSharedFields.addAll(represented.sigsOfRWSharedFields); } represented.sigsOfRWSharedFields = null; if (sigsOfWWSharedFields == null) { sigsOfWWSharedFields = represented.sigsOfWWSharedFields; } else if (represented.sigsOfWWSharedFields != null) { sigsOfWWSharedFields.addAll(represented.sigsOfWWSharedFields); } represented.sigsOfWWSharedFields = null; final Collection<String> _emptySet = Collections.emptySet(); if (readFields == _emptySet) { readFields = represented.readFields; } else { readFields.addAll(represented.readFields); } represented.readFields = null; if (writtenFields == _emptySet) { writtenFields = represented.writtenFields; } else { writtenFields.addAll(represented.writtenFields); } represented.writtenFields = null; } /** * Discovers a boolean property recursively through the alias set tree. * * @param fieldSig is the signature of the field whose alias set serves as the anchor for recursion. * @param transformer is used to extract the property. * @return <code>true</code> if the property holds on an alias set reachable from this alias set; <code>false</code>, * otherwise. * @pre transformer != null and fieldSig != null */ private boolean recursiveBooleanPropertyDiscovery(final String fieldSig, final ITransformer<AliasSet, Boolean> transformer) { boolean _result = false; final Object _fieldAS = find().fieldMap.get(fieldSig); if (_fieldAS != null) { final IWorkBag<AliasSet> _wb = new HistoryAwareFIFOWorkBag<AliasSet>(new HashSet<AliasSet>()); _wb.addWork((AliasSet) _fieldAS); while (_wb.hasWork() && !_result) { final AliasSet _rep = _wb.getWork(); _result |= (transformer.transform(_rep)).booleanValue(); if (!_result) { final Collection<AliasSet> _values = _rep.getFieldMap().values(); _wb.addAllWorkNoDuplicates( CollectionUtils.collect(_values, REPRESENTATIVE_ALIAS_SET_RETRIEVER)); } } } return _result; } /** * Unify the fields of the given alias sets with that of this alias set. * * @param aliasSet is the other alias set involved in the unification. * @param unifyAll <code>true</code> indicates that unification should be multi-thread access sensitive; * <code>false</code>, otherwise. * @pre aliasSet != null */ private void unifyFields(final AliasSet aliasSet, final boolean unifyAll) { for (final Iterator<Map.Entry<String, AliasSet>> _i = aliasSet.fieldMap.entrySet().iterator(); _i .hasNext();) { final Map.Entry<String, AliasSet> _entry = _i.next(); final String _field = _entry.getKey(); final AliasSet _fieldAS = _entry.getValue(); final AliasSet _repAS = getASForField(_field); if (_repAS != null) { _repAS.unifyAliasSetHelper(_fieldAS, unifyAll); } else { putASForField(_field, _fieldAS); } } } /** * Unify thread escape and sharing information in the given alias set. * * @param represented is the other alias set involved in the unification. * @pre represented != null */ private void unifyThreadEscapeInfo(final AliasSet represented) { if ((waits && represented.notifies) || (notifies && represented.waits)) { if (readyEntities == null) { readyEntities = new HashSet<Object>(); } if (readyEntities.isEmpty()) { if (represented.readyEntities != null && !represented.readyEntities.isEmpty()) { readyEntities.addAll(represented.readyEntities); } else { readyEntities.add(getNewReadyEntity()); } } } if (locked && represented.locked) { if (lockEntities == null) { lockEntities = new HashSet<Object>(); } if (lockEntities.isEmpty()) { if (represented.lockEntities != null && !represented.lockEntities.isEmpty()) { lockEntities.addAll(represented.lockEntities); } else { lockEntities.add(getNewLockEntity()); } } } if (CollectionUtils.containsAny(readFields, represented.writtenFields) || CollectionUtils.containsAny(writtenFields, represented.readFields)) { if (readwriteEntities == null) { readwriteEntities = new HashSet<Object>(); sigsOfRWSharedFields = new HashSet<String>(); } if (readwriteEntities.isEmpty()) { if (represented.readwriteEntities != null && !represented.readwriteEntities.isEmpty()) { readwriteEntities.addAll(represented.readwriteEntities); } else { readwriteEntities.add(getNewShareEntity()); } } sigsOfRWSharedFields.addAll(SetUtils.intersection(readFields, represented.writtenFields)); sigsOfRWSharedFields.addAll(SetUtils.intersection(writtenFields, represented.readFields)); } if (CollectionUtils.containsAny(writtenFields, represented.writtenFields)) { if (writewriteEntities == null) { writewriteEntities = new HashSet<Object>(); sigsOfWWSharedFields = new HashSet<String>(); } if (writewriteEntities.isEmpty()) { if (represented.writewriteEntities != null && !represented.writewriteEntities.isEmpty()) { writewriteEntities.addAll(represented.writewriteEntities); } else { writewriteEntities.add(getNewShareEntity()); } } sigsOfWWSharedFields.addAll(SetUtils.intersection(writtenFields, represented.writtenFields)); } } } // End of File