/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10.types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.HashSet;
import polyglot.frontend.Job;
import polyglot.frontend.Source;
import polyglot.types.ClassDef;
import polyglot.types.ClassDef_c;
import polyglot.types.ClassType;
import polyglot.types.CodeInstance;
import polyglot.types.ConstructorDef;
import polyglot.types.ContainerType;
import polyglot.types.Context;
import polyglot.types.FieldDef;
import polyglot.types.Flags;
import polyglot.types.LazyRef;
import polyglot.types.LazyRef_c;
import polyglot.types.MethodDef;
import polyglot.types.Name;
import polyglot.types.Package;
import polyglot.types.QName;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.util.FilteringList;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import polyglot.util.Predicate;
import polyglot.util.Transformation;
import polyglot.util.TransformingList;
import polyglot.util.TypedList;
import x10.constraint.XFailure;
import x10.constraint.XTerm;
import x10.constraint.XVar;
import x10.types.constraints.CConstraint;
import x10.types.constraints.ConstraintManager;
import x10.types.constraints.TypeConstraint;
import x10.types.constraints.XConstrainedTerm;
public class X10ClassDef_c extends ClassDef_c implements X10ClassDef {
protected transient Source fromSource;
protected Ref<? extends Type> superType;
protected List<Ref<? extends Type>> interfaces;
protected List<MethodDef> methods;
protected List<FieldDef> fields;
protected List<ConstructorDef> constructors;
protected Ref<? extends Package> package_;
protected Flags flags;
protected Kind kind;
protected Name name;
protected Ref<X10ClassDef> outer;
protected List<Ref<? extends ClassType>> memberClasses;
protected transient ClassType asType;
private static final long serialVersionUID = -4644427081636650171L;
protected List<ParameterType.Variance> variances;
public X10ClassDef_c(TypeSystem ts, Source fromSource) {
super(ts, fromSource);
this.fromSource = fromSource;
this.interfaces = new ArrayList<Ref<? extends Type>>();
this.methods = new ArrayList<MethodDef>();
this.fields = new ArrayList<FieldDef>();
this.constructors = new ArrayList<ConstructorDef>();
this.memberClasses = new ArrayList<Ref<? extends ClassType>>();
sourceKind = fromSource != null ? SourceKind.SOURCE : SourceKind.UNKNOWN;
this.variances = new ArrayList<ParameterType.Variance>();
this.typeParameters = new ArrayList<ParameterType>();
this.typeMembers = new ArrayList<TypeDef>();
this.thisDef = null;
}
public XVar thisVar() {
if (this.thisDef != null)
return this.thisDef.thisVar();
return ConstraintManager.getConstraintSystem().makeThis(); // Why #this instead of this?
}
ThisDef thisDef;
public ThisDef thisDef() {
return this.thisDef;
}
public void setThisDef(ThisDef thisDef) {
this.thisDef = thisDef;
}
// BEGIN ANNOTATION MIXIN
List<Ref<? extends Type>> annotations;
public List<Ref<? extends Type>> defAnnotations() {
if (annotations == null)
return Collections.<Ref<? extends Type>>emptyList();
return Collections.unmodifiableList(annotations);
}
public void setDefAnnotations(List<Ref<? extends Type>> annotations) {
this.annotations = TypedList.<Ref<? extends Type>>copyAndCheck(annotations, Ref.class, true);
}
public List<Type> annotations() {
return X10TypeObjectMixin.annotations(this);
}
public List<Type> annotationsMatching(Type t) {
return X10TypeObjectMixin.annotationsMatching(this, t);
}
public List<Type> annotationsNamed(QName fullName) {
return X10TypeObjectMixin.annotationsNamed(this, fullName);
}
// END ANNOTATION MIXIN
Ref<TypeConstraint> typeBounds;
public Ref<TypeConstraint> typeBounds() {
return typeBounds;
}
public void setTypeBounds(Ref<TypeConstraint> c) {
this.typeBounds = c;
}
protected Ref<CConstraint> classInvariant;
public void setClassInvariant(Ref<CConstraint> c) {
this.classInvariant = c;
}
/**
* Return the explicitly declared class invariant. If you want to get
* all the statically known information about instances of this class, then
* use the getRealClause().
*/
public Ref<CConstraint> classInvariant() {
return classInvariant;
}
// Cached realClause of the root type.
protected SemanticException realClauseInvalid;
public void checkRealClause() throws SemanticException {
getRealClause(); // force the computation.
if (realClauseInvalid != null)
throw realClauseInvalid;
}
// Cached realClause of the root type.
Ref<CConstraint> realClause = setRealClause();
Ref<CConstraint> realClauseWithThis = setRealClauseWithThis();
private Ref<CConstraint> setRealClauseWithThis() {
final LazyRef<CConstraint> ref = new LazyRef_c<CConstraint>(ConstraintManager.getConstraintSystem().makeCConstraint());
final Runnable runnable = new Runnable() {
public void run() {
CConstraint c = X10ClassDef_c.this.realClause.get();
c = c.instantiateSelf(thisVar());
ref.update(c);
}
public String toString() {
return "realClauseWithThis for " + X10ClassDef_c.this;
}
};
ref.setResolver(runnable);
return ref;
}
/**
* Set the realClause for this type. The realClause is the conjunction of the
* depClause and the baseClause for the type -- it represents all the constraints
* that are satisfied by an instance of this type. The baseClause is the invariant for
* the base type. If the base type C has defined properties P1 p1, ..., Pk pk,
* and inherits from type B, then the baseClause for C is the baseClause for B
* conjoined with r1[self.p1/self, self/this] && ... && rk[self.pk/self, self/this]
* where ri is the realClause for Pi.
*
*/
private Ref<CConstraint> setRealClause() {
final LazyRef<CConstraint> ref = new LazyRef_c<CConstraint>(ConstraintManager.getConstraintSystem().makeCConstraint());
Runnable runnable = new Runnable() {
boolean computing = false;
public void run() {
if (computing) {
return;
}
computing = true;
CConstraint result = ConstraintManager.getConstraintSystem().makeCConstraint();
try {
List<X10FieldDef> properties = properties();
XVar oldThis = thisVar(); // xts.xtypeTranslator().translateThisWithoutTypeConstraint();
try {
// Add in constraints from the supertypes. This is
// no need to change self, and no occurrence of this is possible in
// a type's base constraint.
// vj: 08/12/09. Incorrect. this can occur in a type's base constraint.
{
Type type = Types.get(superType());
if (type != null) {
CConstraint rs = Types.realX(type);
if (rs != null && ! rs.valid()) {
if (rs.thisVar() != null)
rs = rs.substitute(oldThis, (XVar) rs.thisVar());
result.addIn(rs);
}
}
}
// Add in constraints from the interfaces.
for (Iterator<Ref<? extends Type>> i = interfaces().iterator(); i.hasNext(); ) {
Ref<? extends Type> it = (Ref<? extends Type>) i.next();
CConstraint rs = Types.realX(it.get());
// no need to change self, and no occurrence of this is possible in
// a type's base constraint.
if (rs != null && ! rs.valid()) {
// rs = rs.substitute(rs.self(), oldThis);
result.addIn(rs);
}
}
// add in the bindings from the property declarations.
for (X10FieldDef fi : properties) {
Type type = fi.asInstance().type(); // ### check for recursive call here
XVar fiThis = fi.thisVar();
CConstraint rs = Types.realX(type);
if (rs != null) {
// Given: f:C{c}
// Add in: c[self.f/self,self/this]
XTerm newSelf = ts.xtypeTranslator().translate(result.self(), fi.asInstance());
CConstraint rs1 = rs.instantiateSelf(newSelf);
CConstraint rs2;
if (fiThis != null)
rs2 = rs1.substitute(result.self(), fiThis);
else
rs2 = rs1;
result.addIn(rs2);
}
}
// Finally, add in the class invariant.
// It is important to do this last since we need avoid type-checking constraints
// until after the base type of the supertypes are resolved.
XVar thisVar = thisVar();
CConstraint ci = Types.get(classInvariant);
if (ci != null && ! ci.valid()) {
ci = ci.substitute(ci.self(), oldThis);
result.addIn(ci);
}
}
catch (XFailure f) {
result.setInconsistent();
X10ClassDef_c.this.realClauseInvalid =
new SemanticException("The real clause for "
+ this + " is inconsistent.", position());
}
// Now verify that the root clause entails the assertions of the properties.
// We need to set the root clause first, to avoid a spurious report of a cyclic dependency.
// This can happen when one of the properties is subtype of this type:
// class Ref(home: Place) { }
// class Place extends Ref { ... }
// Disable this for now since it can cause an infinite loop.
// TODO: vj 08/12/09 Revisit this.
/*if (false && result.consistent()) {
// Verify that the realclause, as it stands, entails the assertions of the
// property.
for (X10FieldDef fi : properties) {
Type ftype = fi.asInstance().type();
CConstraint c = X10TypeMixin.realX(ftype);
XTerm newSelf = xts.xtypeTranslator().trans(c, c.self(), fi.asInstance());
c = c.substitute(newSelf, c.self());
if (! result.entails(c, ((X10Context) ts.emptyContext()).constraintProjection(result, c))) {
this.rootClause = Types.ref(result);
this.rootClauseInvalid =
new SemanticException("The real clause, " + result + ", does not satisfy constraints from " + fi + ".", position());
}
}
}*/
}
/* catch (XFailure e) {
CConstraint result = ConstraintManager.getConstraintSystem().makeCConstraint();
result.setInconsistent();
this.rootClause = Types.ref(result);
this.rootClauseInvalid = new SemanticException(e.getMessage(), position());
}*/
finally {
ref.update(result);
this.computing = false;
}
}
public String toString() {
return "realClause for " + X10ClassDef_c.this;
}
};
ref.setResolver(runnable);
return ref;
}
public Ref<CConstraint> realClause() {
return realClause;
}
public Ref<CConstraint> realClauseWithThis() {
return realClauseWithThis;
}
public CConstraint getRealClause() {
return realClause.get();
}
public boolean isJavaType() {
return fromJavaClassFile();
}
public List<X10FieldDef> properties() {
List<X10FieldDef> x10fields = new TransformingList<FieldDef, X10FieldDef>(fields(),
new Transformation<FieldDef,X10FieldDef>() {
public X10FieldDef transform(FieldDef o) {
return (X10FieldDef) o;
}
});
return new FilteringList<X10FieldDef>(x10fields, new Predicate<X10FieldDef>() {
public boolean isTrue(X10FieldDef o) {
return o.isProperty();
}
});
}
List<ParameterType> typeParameters;
public List<ParameterType> typeParameters() {
return Collections.unmodifiableList(typeParameters);
}
public List<ParameterType.Variance> variances() {
return Collections.unmodifiableList(variances);
}
public ParameterType.Variance getVariance(ParameterType t) {
int index = typeParameters.indexOf(t);
assert index!=-1 : "Param "+t+" not found in "+typeParameters;
return variances.get(index);
}
public void addTypeParameter(ParameterType p, ParameterType.Variance v) {
typeParameters = new ArrayList<ParameterType>(typeParameters);
typeParameters.add(p);
variances = new ArrayList<ParameterType.Variance>(variances);
variances.add(v);
}
public void replaceTypeParameter(int i, ParameterType p, ParameterType.Variance v) {
assert (typeParameters.size() == variances.size());
typeParameters = new ArrayList<ParameterType>(typeParameters);
typeParameters.set(i, p);
variances = new ArrayList<ParameterType.Variance>(variances);
variances.set(i, v);
}
TypeParamSubst subst = null;
public TypeParamSubst subst() { return this.subst; }
public void setSubst(TypeParamSubst subst) { this.subst = subst; }
List<TypeDef> typeMembers;
// TODO:
// Add .class property
// Add class <: C to class invariant
// Add code to not complain that it's not initialized; ignore when disambiguating C[T]
public List<TypeDef> memberTypes() {
return Collections.unmodifiableList(typeMembers);
}
public void addMemberType(TypeDef t) {
typeMembers.add(t);
}
public Ref<TypeConstraint> typeGuard() {
return new LazyRef_c<TypeConstraint>(Types.parameterBounds(asType()));
}
public boolean isStruct() {
return flags().isStruct();
}
// This is overridden by the synthetic Fun_** classes created in X10TypeSystem_c.
public boolean isFunction() {
return false;
}
private String typeParameterString() {
return ""; //(typeParameters.isEmpty() ? "" : typeParameters.toString());
}
public String toString() {
Name name = name();
if (kind() == null) {
return "<unknown class " + name + typeParameterString() + ">";
}
if (kind() == ANONYMOUS) {
if (interfaces != null && ! interfaces.isEmpty()) {
return isFunction() ? "" + interfaces.get(0) : "<anonymous subtype of " + interfaces.get(0) + typeParameters() + ">";
}
if (superType != null) {
return "<anonymous subclass of " + superType + ">" + typeParameterString();
}
}
if (kind() == TOP_LEVEL) {
Package p = Types.get(package_());
return (p != null ? p.toString() + "." : "") + name + typeParameterString();
}
else if (kind() == MEMBER) {
ClassDef outer = Types.get(outer());
return (outer != null ? outer.toString() + "." : "") + name + typeParameterString();
}
else {
return name.toString() + typeParameterString();
}
}
/** Get the class's flags. */
/** Override the implementation for Java in ClassDef_c which ignores the flag if the class is anonymous.
* In the current X10 implementation closure types are final anonymous classes.
*/
@Override
public Flags flags() {
if (kind() == ANONYMOUS)
return Flags.FINAL;
return flags;
}
public X10ClassType asType() {
if (asType == null) {
asType = ts.createClassType(position(), errorPosition(), Types.ref(this));
}
return (X10ClassType) asType;
}
public boolean hasDeserializationConstructor(Context context) {
for (ConstructorDef cd: constructors()) {
if (cd.formalTypes().size() == 1) {
Type type = cd.formalTypes().get(0).get();
if (type.isSubtype(type.typeSystem().SerialData(), context)) {
return true;
}
}
}
return false;
}
/** Was the class declared in a static context? */
protected boolean inStaticContext = false;
/** Whether we need to serialize this class. */
protected boolean needSerialization = true;
public boolean isMember() {
return kind == MEMBER;
}
public boolean isTopLevel() {
return kind == TOP_LEVEL;
}
public boolean isLocal() {
return kind == LOCAL;
}
public boolean isAnonymous() {
return kind == ANONYMOUS;
}
public boolean isNested() {
return !isTopLevel();
}
public boolean isInnerClass() {
return !flags().isInterface() && isNested() && !flags().isStatic()
&& !inStaticContext();
}
public Source sourceFile() {
return fromSource;
}
protected enum SourceKind { SOURCE, JAVA, ENCODED, UNKNOWN };
protected SourceKind sourceKind;
public boolean fromSourceFile() {
return sourceKind == SourceKind.SOURCE;
}
public void setFromSourceFile() {
sourceKind = SourceKind.SOURCE;
}
public boolean fromEncodedClassFile() {
return sourceKind == SourceKind.ENCODED;
}
public void setFromEncodedClassFile() {
sourceKind = SourceKind.ENCODED;
}
public boolean fromJavaClassFile() {
return sourceKind == SourceKind.JAVA;
}
public void setFromJavaClassFile() {
sourceKind = SourceKind.JAVA;
}
Job job;
public Job job() {
return job;
}
public void setJob(Job job) {
this.job = job;
}
public Kind kind() {
return kind;
}
public void inStaticContext(boolean inStaticContext) {
this.inStaticContext = inStaticContext;
}
public boolean inStaticContext() {
return inStaticContext;
}
public Ref<? extends X10ClassDef> outer() {
if (kind() == TOP_LEVEL)
return null;
if (outer == null)
throw new InternalCompilerError("Nested class " + this + " must have outer classes.");
return outer;
}
public Name name() {
return name;
}
public void setContainer(Ref<? extends ContainerType> container) {
throw new InternalCompilerError("Call outer(container.def()) instead.");
}
/** Get the class's super type. */
public Ref<? extends Type> superType() {
return this.superType;
}
/** Get the class's package. */
public Ref<? extends Package> package_() {
return package_;
}
public void setFlags(Flags flags) {
this.flags = flags;
}
public void flags(Flags flags) {
this.flags = flags;
}
public void kind(Kind kind) {
this.kind = kind;
}
public void outer(Ref<X10ClassDef> outer) {
if (outer != null && kind() == TOP_LEVEL)
throw new InternalCompilerError("Top-level classes cannot have outer classes.");
this.outer = outer;
}
public Ref<? extends ContainerType> container() {
return Types.<ClassType>ref(ts.createClassType(position(), errorPosition(), this.outer));
}
public void name(Name name) {
if (kind() == ANONYMOUS)
throw new InternalCompilerError("Anonymous classes cannot have names.");
this.name = name;
}
public void position(Position pos) {
this.position = pos;
}
public void errorPosition(Position pos) {
this.errorPosition = pos;
}
public void setPackage(Ref<? extends Package> p) {
this.package_ = p;
}
public void superType(Ref<? extends Type> t) {
this.superType = t;
}
public void addInterface(Ref<? extends Type> t) {
interfaces.add(t);
}
public void addMethod(MethodDef mi) {
methods.add(mi);
}
public void addConstructor(ConstructorDef ci) {
constructors.add(ci);
}
public void addField(FieldDef fi) {
fields.add(fi);
}
public void addMemberClass(Ref<? extends ClassType> t) {
memberClasses.add(t);
}
public void setConstructors(List<? extends ConstructorDef> l) {
this.constructors = new ArrayList<ConstructorDef>(l);
}
public void setFields(List<? extends FieldDef> l) {
this.fields = new ArrayList<FieldDef>(l);
}
public void setInterfaces(List<Ref<? extends Type>> l) {
this.interfaces = new ArrayList<Ref<? extends Type>>(l);
}
public void setMemberClasses(List<Ref<? extends ClassType>> l) {
this.memberClasses = new ArrayList<Ref<? extends ClassType>>(l);
}
public void setMethods(List<? extends MethodDef> l) {
this.methods = new ArrayList<MethodDef>(l);
}
/** Return an immutable list of constructors */
public List<ConstructorDef> constructors() {
return Collections.unmodifiableList(constructors);
}
/** Return an immutable list of member classes */
public List<Ref<? extends ClassType>> memberClasses() {
return Collections.<Ref<? extends ClassType>>unmodifiableList(memberClasses);
}
/** Return an immutable list of methods. */
public List<MethodDef> methods() {
return Collections.unmodifiableList(methods);
}
/** Return an immutable list of fields */
public List<FieldDef> fields() {
return Collections.unmodifiableList(fields);
}
/** Return an immutable list of interfaces */
public List<Ref<? extends Type>> interfaces() {
return Collections.<Ref<? extends Type>>unmodifiableList(interfaces);
}
public void needSerialization(boolean b) {
needSerialization = b;
}
public boolean needSerialization() {
return needSerialization;
}
/** Get the full name of the class, if possible. */
public QName fullName() {
Name name = name();
if (kind() == TOP_LEVEL) {
Package p = Types.get(package_());
return QName.make(p != null ? p.fullName() : null, name);
}
else if (kind() == MEMBER) {
ClassDef outer = Types.get(outer());
return QName.make(outer != null ? outer.fullName() : null, name);
}
else if (kind() == LOCAL) {
return QName.make(null, name);
}
else if (kind() == ANONYMOUS) {
return QName.make(null, Name.make("<anonymous class>"));
}
else {
return QName.make(null, Name.make("<unknown class>"));
}
}
private static void addTypeToWorkList(ArrayList<ClassDef> worklist, Ref<? extends Type> ref) {
Type sup = Types.get(ref);
sup = Types.baseType(sup);
// can be a parameter type
if (sup instanceof ParameterType) return;
if (! (sup instanceof X10ParsedClassType))
assert sup instanceof X10ParsedClassType;
worklist.add( ((X10ParsedClassType)sup).def() );
}
private static void addToWorkList(ClassDef def, ArrayList<ClassDef> worklist) {
final Ref<? extends Type> ref = def.superType();
if (ref!=null) {
addTypeToWorkList(worklist, ref);
}
final List<X10FieldDef> props = def.properties();
for (X10FieldDef p : props) {
addTypeToWorkList(worklist, p.type());
}
}
public boolean hasCircularProperty() {
ArrayList<ClassDef> worklist = new ArrayList<ClassDef>();
HashSet<ClassDef> smaller = new HashSet<ClassDef>();
addToWorkList(this, worklist);
while (worklist.size()>0) {
ClassDef cur = worklist.remove(worklist.size()-1);
if (smaller.contains(cur)) continue;
smaller.add(cur);
addToWorkList(cur, worklist);
}
return smaller.contains(this);
}
private boolean wasInner = false;
public boolean wasInner() {
return wasInner;
}
public void setWasInner(boolean v) {
wasInner = v;
}
// [DC] the following 5 functions added to allow ClassDef to be a CodeDef
// which I wanted to do so that the code reference stored in a context can understand
// when an expression is within an inner class scope (e.g. invariant of a local class)
@Override
public CodeInstance<?> asInstance() {
return asType();
}
@Override
public void setTypeParameters(List<ParameterType> typeParameters) {
this.typeParameters = typeParameters;
}
@Override
public boolean staticContext() {
// [DC] A class guard is never in a static context
return false;
}
// [DC] assuming this is the correct thing to do, have to implement the following methods so do it as a setter/getter...
XConstrainedTerm placeTerm;
@Override
public XConstrainedTerm placeTerm() {
return placeTerm;
}
@Override
public void setPlaceTerm(XConstrainedTerm xt) {
placeTerm = xt;
}
}