DynamicScope.java

package edu.udel.cis.vsl.civl.state;

import java.io.PrintStream;
import java.util.Arrays;
import java.util.BitSet;

import edu.udel.cis.vsl.civl.model.IF.Scope;
import edu.udel.cis.vsl.civl.model.IF.variable.Variable;
import edu.udel.cis.vsl.sarl.IF.expr.SymbolicExpression;

/**
 * Represents state of a dynamic scope.
 * 
 * Participates in Flyweight Pattern.
 * 
 * TODO: Note that two scopes are different if they have different parentIDs.
 * Think about this. Would it be better to have a separate component of the
 * state which specifies the parent structure, say an array parents of length
 * numScopes: DynamicScope[] parents?
 * 
 * Other components have references to the scope id: StackEntry.
 * 
 * TODO: could use BitSet to record the set of processes that can reach this
 * dynamic scope. Would need to re-do bit set when processes change. Is a sparse
 * representation better?
 * 
 * operations: add a process, remove a process, renumber procs by shifting
 * 
 * @author Timothy K. Zirkel (zirkel)
 * @author Timothy J. McClory (tmcclory)
 * 
 */
public class DynamicScope {

	private static boolean debug = false;

	private boolean hashed = false;

	private int hashCode = -1;

	boolean canonic = false;

	/**
	 * Non-null static scope to which this dynamic scope is associated.
	 */
	private Scope lexicalScope;

	/**
	 * TODO: CONSIDER this: move this field out of DynamicScope and into a
	 * separate state component recording parent structure to facilitate re-use.
	 */
	private int parent;

	/**
	 * Non-null array of variable values. The symbolic expression in position i
	 * is the value of the variable of index i. May contain null values.
	 */
	private SymbolicExpression[] variableValues;

	/**
	 * Sets of PIDs of processes that can reach this dynamic scope.
	 */
	private BitSet reachers;

	/**
	 * A dynamic scope in which all variable values are null.
	 * 
	 * @param lexicalScope
	 *            The lexical scope corresponding to this dynamic scope.
	 * @param parent
	 *            The parent of this dynamic scope. -1 only for the topmost
	 *            dynamic scope.
	 */
	DynamicScope(Scope lexicalScope, int parent, BitSet reachers) {
		assert lexicalScope != null;
		this.lexicalScope = lexicalScope;
		this.parent = parent;
		this.reachers = reachers;
		variableValues = new SymbolicExpression[lexicalScope.numVariables()]; // FIX
	}

	DynamicScope(Scope lexicalScope, int parent,
			SymbolicExpression[] variableValues, BitSet reachers) {
		assert variableValues != null
				&& variableValues.length == lexicalScope.numVariables();
		this.lexicalScope = lexicalScope;
		this.parent = parent;
		this.variableValues = variableValues;
		this.reachers = reachers;
	}

	DynamicScope changeParent(int newParent) {
		return new DynamicScope(lexicalScope, newParent, variableValues,
				reachers);
	}

	DynamicScope changeReachers(BitSet newBitSet) {
		return new DynamicScope(lexicalScope, parent, variableValues, newBitSet);
	}

	DynamicScope changeVariableValues(SymbolicExpression[] newVariableValues) {
		return new DynamicScope(lexicalScope, parent, newVariableValues,
				reachers);
	}

	public SymbolicExpression getValue(int vid) {
		return variableValues[vid];
	}

	public int numberOfVariables() {
		return variableValues.length;
	}

	/**
	 * How many processes can reach this dynamic scope? A process p can reach a
	 * dynamic scope d iff there is a path starting from a dynamic scope which
	 * is referenced in a frame on p's call stack to d, following the "parent"
	 * edges in the scope tree.
	 * 
	 * @return the number of processes which can reach this dynamic scope
	 */
	public int numberOfReachers() {
		return reachers.cardinality();
	}

	/**
	 * Is this dynamic scope reachable by the process with the given PID?
	 * 
	 * @param pid
	 * @return true iff this dynamic scope is reachable from the process with
	 *         pid PID
	 */
	public boolean reachableByProcess(int pid) {
		return reachers.get(pid);
	}

	/**
	 * @return The lexical scope corresponding to this dynamic scope.
	 */
	public Scope lexicalScope() {
		return lexicalScope;
	}

	// TODO: move this to factory....
	/**
	 * @return The parent of this dynamic scope. Null only for the topmost
	 *         dynamic scope.
	 */
	int parent() {
		return parent;
	}

	BitSet reachers() {
		return reachers;
	}

	/**
	 * @return Copy the set of values in this scopes.
	 */
	SymbolicExpression[] copyValues() {
		SymbolicExpression[] newValues = new SymbolicExpression[variableValues.length];

		System.arraycopy(variableValues, 0, newValues, 0, variableValues.length);
		return newValues;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		if (!hashed) {
			final int prime = 31;

			hashCode = 1;
			hashCode = prime * hashCode + lexicalScope.hashCode();
			hashCode = prime * hashCode + parent;
			hashCode = prime * hashCode + Arrays.hashCode(variableValues);
			hashCode = prime * hashCode + reachers.hashCode();
			hashed = true;
		}
		return hashCode;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj instanceof DynamicScope) {
			DynamicScope that = (DynamicScope) obj;

			if (canonic && that.canonic)
				return false;
			if (hashed && that.hashed && hashCode != that.hashCode)
				return false;
			if (!lexicalScope.equals(that.lexicalScope))
				return false;
			if (parent != that.parent)
				return false;
			if (!Arrays.equals(variableValues, that.variableValues))
				return false;
			if (!reachers.equals(that.reachers))
				return false;
			return true;
		}
		return false;
	}

	public void print(PrintStream out, int id, String prefix) {
		int numVars = lexicalScope.numVariables();
		int bitSetLength = reachers.length();
		boolean first = true;

		out.println(prefix + "scope " + id + " (parent=" + parent + ", static="
				+ lexicalScope.id() + ")");
		out.print(prefix + "| reachers: ");
		for (int j = 0; j < bitSetLength; j++) {
			if (reachers.get(j)) {
				if (first)
					first = false;
				else
					out.print(",");
				out.print(j);
			}
		}
		out.println();
		for (int i = 0; i < numVars; i++) {
			Variable variable = lexicalScope.getVariable(i);
			SymbolicExpression value = variableValues[i];

			out.print(prefix + "| " + variable.name() + " = ");
			if (debug)
				out.println(value.toStringBufferLong());
			else
				out.println(value + " : " + value.type());
		}
		out.flush();
	}

	@Override
	public String toString() {
		return "DynamicScope[static=" + lexicalScope.id() + ", parent="
				+ parent + "]";
	}
}