package com.sun.tools.javac.code;
import com.sun.tools.javac.code.RPLElement.ArrayIndexRPLElement;
import com.sun.tools.javac.code.RPLElement.RPLCaptureParameter;
import com.sun.tools.javac.code.RPLElement.RPLParameterElement;
import com.sun.tools.javac.code.RPLElement.UndetRPLParameterElement;
import com.sun.tools.javac.code.RPLElement.VarRPLElement;
import com.sun.tools.javac.code.Substitute.AsMemberOf;
import com.sun.tools.javac.code.Substitute.SubstIndex;
import com.sun.tools.javac.code.Substitute.SubstRPLForVar;
import com.sun.tools.javac.code.Substitute.SubstRPLs;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Type.ClassType;
import com.sun.tools.javac.code.Type.TypeVar;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.comp.Resolve;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
/** A class for representing DPJ region path lists (RPLs). An RPL is a list
* of RPL elements. Various operations on RPLs, pairs of RPLs, and lists
* of RPLs required by the DPJ type system are supported.
*/
public class RPL implements
SubstRPLs<RPL>,
AsMemberOf<RPL>,
SubstIndex<RPL>,
SubstRPLForVar<RPL>
{
///////////////////////////////////////////////////////////////////////////
// Fields
///////////////////////////////////////////////////////////////////////////
/** The elements comprising this RPL */
public List<RPLElement> elts;
///////////////////////////////////////////////////////////////////////////
// Constructors
///////////////////////////////////////////////////////////////////////////
public RPL() {
this.elts = List.nil();
}
public RPL(RPLElement singletonElement) {
this.elts = List.of(singletonElement);
}
public RPL(List<RPLElement> elts) {
this.elts = elts;
}
///////////////////////////////////////////////////////////////////////////
// RPL query methods
///////////////////////////////////////////////////////////////////////////
public int size() {
return elts.size();
}
public boolean isEmpty() {
return elts.isEmpty();
}
public boolean isAtomic() {
for (RPLElement elt : elts) {
if (elt.isAtomic()) return true;
}
return false;
}
/** RPL under relation
* See Section 1.2.2 of the DPJ Tech Report
*/
public boolean isNestedUnder(RPL that) {
// Deal with z regions and capture parameters by converting first element
// to its upper bound
RPL upperBound = this.upperBound();
if (upperBound != this && upperBound.isNestedUnder(that))
return true;
// UNDER-ROOT
if (that.isRoot()) return true;
// UNDER-NAME
if (!this.isEmpty()) {
if (this.withoutLastElement().isNestedUnder(that)) return true;
}
// UNDER-STAR
if (this.endsWithStar() && this.withoutLastElement().isNestedUnder(that)) return true;
// UNDER-INCLUDE
if (this.isIncludedIn(that)) return true;
return false;
}
/**
* Does this RPL start with a local region name, or is it under an RPL
* that starts with a local region name?
*/
public boolean isUnderLocal() {
RPL upperBound = this.upperBound();
if (upperBound != this && upperBound.isUnderLocal())
return true;
if (elts.isEmpty() || elts.head == null)
return false;
return elts.head.isLocalName();
}
/** RPL inclusion relation
* See Section 1.2.3 of the DPJ Tech Report
*/
public boolean isIncludedIn(RPL that) {
// Handle capture parameters
if (this.elts.head instanceof RPLCaptureParameter) {
return this.upperBound().isIncludedIn(that);
}
// Handle undetermined parameters
if (that.elts.head instanceof UndetRPLParameterElement) {
UndetRPLParameterElement element =
(UndetRPLParameterElement) that.elts.head;
if (element.includedIn == null) {
// Nothing to do
} else if (this.isIncludedIn(element.includedIn)) {
element.includedIn = this;
} else {
element.includedIn = null;
}
return true;
}
// Reflexivity
if (this.equals(that)) return true;
// INCLUDE-STAR
if (that.endsWithStar()) {
if (this.isNestedUnder(that.withoutLastElement())) return true;
}
// INCLUDE-NAME
if (!this.isEmpty() && !that.isEmpty()) {
if (this.elts.last().isIncludedIn(that.elts.last()) &&
this.withoutLastElement().isIncludedIn(that.withoutLastElement()))
return true;
}
return false;
}
private boolean endsWithStar() {
return size() > 1 && elts.last() == RPLElement.STAR;
}
private boolean isRoot() {
return size() == 1 && (elts.last() == RPLElement.ROOT_ELEMENT);
}
/**
* Is this RPL fully specified? An RPL is fully specified if it contains
* no * or [?].
*/
public boolean isFullySpecified() {
for (RPLElement e : elts) {
if (!e.isFullySpecified()) return false;
}
return true;
}
/**
* Compute an upper bound for this RPL
*/
public RPL upperBound() {
if (elts.isEmpty() ||
(!(elts.head instanceof VarRPLElement) &&
!(elts.head instanceof RPLCaptureParameter)))
return this;
RPL upperBound = elts.head.upperBound();
//if (elts.size() == 1) return upperBound;
return new RPL(upperBound.elts.appendList(elts.tail));
}
///////////////////////////////////////////////////////////////////////////
// RPL manipulation methods
///////////////////////////////////////////////////////////////////////////
private RPL withoutLastElement() {
ListBuffer<RPLElement> buf = new ListBuffer<RPLElement>();
List<RPLElement> elts = this.elts;
while (elts.tail != null) {
if (elts.tail.tail == null) break;
buf.append(elts.head);
elts = elts.tail;
}
return new RPL(buf.toList());
}
/** Replace 'from' RPL params with 'to' RPLs */
public RPL substRPLParams(Iterable<RPL> from, Iterable<RPL> to) {
return Substitute.iterable(RPLs.substRPLParams, this, from, to);
}
public RPL substRPLParam(RPL from, RPL to) {
if (this.elts.head.equals(from.elts.head))
return new RPL(to.elts.appendList(this.elts.tail));
return this;
}
/**
* Do TR substitutions implied by type bindings
*/
public RPL substTRParams(Iterable<Type> from, Iterable<Type> to) {
return Substitute.iterable(RPLs.substTRParams, this, from, to);
}
public RPL substTRParam(Type fromType, Type toType) {
RPL result = this;
if (fromType instanceof TypeVar) {
List<RPL> params = fromType.tsym.type.getRPLArguments();
List<RPL> args = toType.getRPLArguments();
result = this.substRPLParams(params, args);
}
return result;
}
public static RPL exprToRPL(JCExpression tree) {
Symbol sym = tree.getSymbol();
if (sym != null) return symToRPL(sym);
RPL owner = tree.type.getOwner();
RPL result = new RPL(owner.elts.append(RPLElement.STAR));
return result;
}
public static RPL symToRPL(Symbol sym) {
RPL result = null;
if ((sym instanceof VarSymbol) &&
(sym.owner.kind == Kinds.MTH || sym.name.toString().equals("this"))
&& (sym.flags() & Flags.FINAL) != 0) {
// If the variable is a final local variable, use it as the RPL
result = new RPL(List.<RPLElement>of(new RPLElement.VarRPLElement((VarSymbol) sym)));
} else {
// Otherwise, use the owner region
RPL owner = sym.type.getOwner();
result = new RPL(owner.elts.append(RPLElement.STAR));
}
return result;
}
public RPL substRPLForVar(VarSymbol from, RPL to) {
if (!(this.elts.head instanceof RPLElement.VarRPLElement)) return this;
RPLElement.VarRPLElement vrs = (RPLElement.VarRPLElement) this.elts.head;
if (vrs.vsym != from) return this;
return new RPL(to.elts.appendList(this.elts.tail));
}
public RPL substVar(VarSymbol from, VarSymbol to) {
RPL toRPL = symToRPL(to);
return (toRPL == null) ? this : substRPLForVar(from, toRPL);
}
public RPL substExpForVar(VarSymbol from, JCExpression to) {
RPL toRPL = exprToRPL(to);
return (toRPL == null) ? this : substRPLForVar(from, toRPL);
}
public RPL substExpsForVars(Iterable<VarSymbol> from, Iterable<JCExpression> to) {
return Substitute.iterable(RPLs.substExpsForVars, this, from, to);
}
public RPL substIndices(Iterable<VarSymbol> from, Iterable<JCExpression> to) {
return Substitute.iterable(RPLs.substIndices, this, from, to);
}
public RPL substIndex(VarSymbol from, JCExpression to) {
ListBuffer<RPLElement> buf = ListBuffer.<RPLElement>lb();
for (RPLElement e : elts) {
if (e instanceof ArrayIndexRPLElement) {
ArrayIndexRPLElement ai = (ArrayIndexRPLElement) e;
JCExpression subst = substIndexHelper(ai.indexExp, from, to);
if (subst != ai.indexExp)
e = new ArrayIndexRPLElement(subst);
}
buf.append(e);
}
return new RPL(buf.toList());
}
// where
private JCExpression substIndexHelper(JCExpression tree, VarSymbol from,
JCExpression to) {
return (new SubstIndexVisitor(from, to)).substIndex(tree);
}
/**
* A simple visitor for substituting expressions for index variables.
* Note the following:
*
* 1. We can't use TreeTranslator here, because we don't want to mess
* up the actual AST used by the program! So we have to replicate
* every node that we want to modify.
*
* 2. This simple visitor only handles singleton indices and (recursively)
* binary expressions containing singleton indices. However, that's
* enough for now: because we can only disambiguate constants, negated
* variables, and binary expressions in array typing, expanding this
* visitor to do more would be pointless. If and when we add more
* robust expression disambiguation, we can expand this visitor as
* necessary.
*/
private static class SubstIndexVisitor extends JCTree.Visitor {
private VarSymbol from = null;
private JCExpression to = null;
public JCExpression result = null;
public SubstIndexVisitor(VarSymbol from, JCExpression to) {
this.from = from;
this.to = to;
}
public JCExpression substIndex(JCExpression tree) {
result = tree;
if (tree != null)
tree.accept(this);
return result;
}
public void visitIdent(JCIdent tree) {
if (tree.sym == from) {
result = to;
}
}
public void visitBinary(JCBinary tree) {
JCExpression lhs = substIndex(tree.lhs);
JCExpression rhs = substIndex(tree.rhs);
if (lhs != tree.lhs || rhs != tree.rhs) {
result = new JCBinary(tree.getTag(), lhs, rhs, tree.getOperator());
result.pos = tree.pos;
}
}
@Override
public void visitTree(JCTree tree) {}
};
/** Compute the capture of an RPL:
* - If the RPL is fully specified, then the capture is the same as the input
* - If the RPL is partially specified, then the capture is a fresh RPL consisting
* of a fresh capture parameter under the input RPL
*/
public RPL capture() {
return this.isFullySpecified() ? this :
new RPL(new RPLElement.RPLCaptureParameter(this));
}
/**
* The RPL as a member of t
* @param t The type where we want this to be a member, after translation
*/
public RPL asMemberOf(Type t, Types types) {
RPLElement elt = this.elts.head;
if (elt instanceof RPLParameterElement) {
RPLParameterElement paramElt =
(RPLParameterElement) elt;
Symbol owner = paramElt.sym.enclClass();
return this.asMemberOf(types, t, owner);
}
return this;
}
//where
private RPL asMemberOf(Types types, Type t, Symbol owner) {
RPL result = this;
if (owner.type.hasRegionParams()) {
Type base = types.asOuterSuper(t, owner);
if (base != null) {
List<RPL> from = owner.type.allrgnparams();
List<RPL> to = base.allrgnparams();
if (from.nonEmpty()) {
result = result.substRPLParams(from, to);
}
result = result.substTRParams(owner.type.alltyparams(),
base.alltyparams());
}
}
return Substitute.allRPLParams(result, t);
}
/**
* Conform the RPL to an enclosing environment. An RPL may contain
* elements written in terms of local region names and/ or local variables
* that are no longer in scope. If so, we need to either (1) replace the RPL
* with a more general RPL whose elements are in scope; or (2) delete the
* RPL (i.e., return null), if all regions it represents are out of scope.
*/
public RPL inEnvironment(Resolve rs, Env<AttrContext> env, boolean pruneLocalEffects) {
// If the RPL is a capture parameter, compute its bound in the enclosing
// environment
if (elts.head instanceof RPLCaptureParameter) {
RPLCaptureParameter capture = (RPLCaptureParameter) elts.head;
RPL includedIn = capture.includedIn.inEnvironment(rs, env, pruneLocalEffects);
// If bound is out of scope, so is capture parameter
if (includedIn == null) return null;
// Otherwise return new parameter only if bound changed
return (includedIn == capture.includedIn) ? this :
new RPL(List.<RPLElement>of(new RPLCaptureParameter(includedIn)).
appendList(elts.tail));
}
// If the RPL starts with a variable v that is out of scope, then
// replace the whole thing with R : *, where R is the owner parameter
// of v's type, in this environment. Note that if R itself is out of
// scope, the whole RPL may be deleted.
if (elts.head instanceof RPLElement.VarRPLElement) {
RPLElement.VarRPLElement vrs = (RPLElement.VarRPLElement) elts.head;
if (!rs.isInScope(vrs.vsym, env)) {
if (vrs.vsym.type instanceof ClassType) {
ClassType ct = (ClassType) vrs.vsym.type;
List<RPL> actuals = ct.getRPLArguments();
RPL owner = actuals.isEmpty() ? RPLs.ROOT : actuals.head;
return new RPL(owner.elts.append(RPLElement.STAR).appendList(elts.tail)).inEnvironment(rs, env, pruneLocalEffects);
}
}
}
// Truncate an RPL containing a non-variable element E that is out of scope. If
// E occurs in the first position, then the whole RPL is out of scope; return null.
// Otherwise, replace E and all following elements with *.
for (RPLElement elt : elts) {
if (!rs.isInScope(elt, env))
return this.truncateTo(elt);
if (pruneLocalEffects && elt.isLocalName())
return this.truncateTo(elt);
}
// Otherwise, go through the elements and look for array index elements
// [e] where e is out of scope. Replace every such [e] with [?].
ListBuffer<RPLElement> buf = ListBuffer.lb();
boolean added = false;
for (RPLElement elt : elts) {
if (elt instanceof ArrayIndexRPLElement) {
ArrayIndexRPLElement ae = (ArrayIndexRPLElement) elt;
if (!rs.isInScope(ae.indexExp, env)) {
// Replace elt with [?]
elt = new ArrayIndexRPLElement(null);
added = true;
}
}
buf.append(elt);
}
return added ? new RPL(buf.toList()) : this;
}
RPL truncateTo(RPLElement elt) {
ListBuffer<RPLElement> buf = ListBuffer.lb();
List<RPLElement> list = elts;
while (list.nonEmpty() && list.head != elt) {
buf.append(list.head);
list = list.tail;
}
if (buf.isEmpty()) return null;
buf.append(RPLElement.STAR);
return new RPL(buf.toList());
}
///////////////////////////////////////////////////////////////////////////
@Override public boolean equals(Object other) {
if (this == other)
return true;
else if (other != null && other instanceof RPL)
return this.elts.equals(((RPL)other).elts);
else
return false;
}
@Override public int hashCode() {
return elts.hashCode();
}
///////////////////////////////////////////////////////////////////////////
@Override public String toString() {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (RPLElement e : elts) {
if (first) first = false; else sb.append(" : ");
sb.append(e);
}
return sb.toString();
}
/**
* The Java source which this RPL list represents. A List is
* represented as a comma-separated listing of the elements in
* that list.
*/
public static String toString(java.util.List<RPL> rpls) {
return rpls.toString();
}
public boolean containsArrayAccess() {
for (RPLElement e : elts)
if (e instanceof ArrayIndexRPLElement)
return true;
return false;
}
}