package polyglot.ext.jl.ast;
import java.util.*;
import polyglot.ast.*;
import polyglot.types.*;
import polyglot.util.*;
import polyglot.visit.*;
import polyglot.main.Options;
/**
* A <code>ConstructorCall_c</code> represents a direct call to a constructor.
* For instance, <code>super(...)</code> or <code>this(...)</code>.
*/
public class ConstructorCall_c extends Stmt_c implements ConstructorCall
{
protected Kind kind;
protected Expr qualifier;
protected List arguments;
protected ConstructorInstance ci;
public ConstructorCall_c(Position pos, Kind kind, Expr qualifier, List arguments) {
super(pos);
this.kind = kind;
this.qualifier = qualifier;
this.arguments = TypedList.copyAndCheck(arguments, Expr.class, true);
}
/** Get the qualifier of the constructor call. */
public Expr qualifier() {
return this.qualifier;
}
/** Set the qualifier of the constructor call. */
public ConstructorCall qualifier(Expr qualifier) {
ConstructorCall_c n = (ConstructorCall_c) copy();
n.qualifier = qualifier;
return n;
}
/** Get the kind of the constructor call. */
public Kind kind() {
return this.kind;
}
/** Set the kind of the constructor call. */
public ConstructorCall kind(Kind kind) {
ConstructorCall_c n = (ConstructorCall_c) copy();
n.kind = kind;
return n;
}
/** Get the actual arguments of the constructor call. */
public List arguments() {
return Collections.unmodifiableList(this.arguments);
}
/** Set the actual arguments of the constructor call. */
public ProcedureCall arguments(List arguments) {
ConstructorCall_c n = (ConstructorCall_c) copy();
n.arguments = TypedList.copyAndCheck(arguments, Expr.class, true);
return n;
}
public ProcedureInstance procedureInstance() {
return constructorInstance();
}
/** Get the constructor we are calling. */
public ConstructorInstance constructorInstance() {
return ci;
}
/** Set the constructor we are calling. */
public ConstructorCall constructorInstance(ConstructorInstance ci) {
ConstructorCall_c n = (ConstructorCall_c) copy();
n.ci = ci;
return n;
}
/**
* An explicit constructor call is a static context. We need to record
* this.
*/
public Context enterScope(Context c) {
return c.pushStatic();
}
/** Reconstruct the constructor call. */
protected ConstructorCall_c reconstruct(Expr qualifier, List arguments) {
if (qualifier != this.qualifier || ! CollectionUtil.equals(arguments, this.arguments)) {
ConstructorCall_c n = (ConstructorCall_c) copy();
n.qualifier = qualifier;
n.arguments = TypedList.copyAndCheck(arguments, Expr.class, true);
return n;
}
return this;
}
/** Visit the children of the call. */
public Node visitChildren(NodeVisitor v) {
Expr qualifier = (Expr) visitChild(this.qualifier, v);
List arguments = visitList(this.arguments, v);
return reconstruct(qualifier, arguments);
}
public Node buildTypes(TypeBuilder tb) throws SemanticException {
TypeSystem ts = tb.typeSystem();
// Remove super() calls for java.lang.Object.
if (kind == SUPER && tb.currentClass() == ts.Object()) {
return tb.nodeFactory().Empty(position());
}
ConstructorCall_c n = (ConstructorCall_c) super.buildTypes(tb);
List l = new ArrayList(arguments.size());
for (int i = 0; i < arguments.size(); i++) {
l.add(ts.unknownType(position()));
}
ConstructorInstance ci = ts.constructorInstance(position(), ts.Object(),
Flags.NONE, l,
Collections.EMPTY_LIST);
return n.constructorInstance(ci);
}
/** Type check the call. */
public Node typeCheck(TypeChecker tc) throws SemanticException {
TypeSystem ts = tc.typeSystem();
Context c = tc.context();
ClassType ct = c.currentClass();
Type superType = ct.superType();
// The qualifier specifies the enclosing instance of this inner class.
// The type of the qualifier must be the outer class of this
// inner class or one of its super types.
//
// Example:
//
// class Outer {
// class Inner { }
// }
//
// class ChildOfInner extends Outer.Inner {
// ChildOfInner() { (new Outer()).super(); }
// }
if (qualifier != null) {
if (kind != SUPER) {
throw new SemanticException("Can only qualify a \"super\"" +
"constructor invocation.",
position());
}
if (!superType.isClass() || !superType.toClass().isInnerClass() ||
superType.toClass().inStaticContext()) {
throw new SemanticException("The class \"" + superType + "\"" +
" is not an inner class, or was declared in a static " +
"context; a qualified constructor invocation cannot " +
"be used.", position());
}
Type qt = qualifier.type();
if (! qt.isClass() || !qt.isSubtype(superType.toClass().outer())) {
throw new SemanticException("The type of the qualifier " +
"\"" + qt + "\" does not match the immediately enclosing " +
"class of the super class \"" +
superType.toClass().outer() + "\".", qualifier.position());
}
}
if (kind == SUPER) {
if (! superType.isClass()) {
throw new SemanticException("Super type of " + ct +
" is not a class.", position());
}
// If the super class is an inner class (i.e., has an enclosing
// instance of its container class), then either a qualifier
// must be provided, or ct must have an enclosing instance of the
// super class's container class, or a subclass thereof.
if (qualifier == null && superType.isClass() && superType.toClass().isInnerClass()) {
ClassType superContainer = superType.toClass().outer();
// ct needs an enclosing instance of superContainer,
// or a subclass of superContainer.
ClassType e = ct;
while (e != null) {
if (e.isSubtype(superContainer) && ct.hasEnclosingInstance(e)) {
break;
}
e = e.outer();
}
if (e == null) {
throw new SemanticException(ct + " must have an enclosing instance" +
" that is a subtype of " + superContainer, position());
}
if (e == ct) {
throw new SemanticException(ct + " is a subtype of " + superContainer +
"; an enclosing instance that is a subtype of " + superContainer +
" must be specified in the super constructor call.", position());
}
}
ct = ct.superType().toClass();
}
List argTypes = new LinkedList();
for (Iterator iter = this.arguments.iterator(); iter.hasNext();) {
Expr e = (Expr) iter.next();
argTypes.add(e.type());
}
ConstructorInstance ci = ts.findConstructor(ct, argTypes, c.currentClass());
return constructorInstance(ci);
}
public Type childExpectedType(Expr child, AscriptionVisitor av) {
TypeSystem ts = av.typeSystem();
if (child == qualifier) {
// FIXME: Can be more specific
return ts.Object();
}
Iterator i = this.arguments.iterator();
Iterator j = ci.formalTypes().iterator();
while (i.hasNext() && j.hasNext()) {
Expr e = (Expr) i.next();
Type t = (Type) j.next();
if (e == child) {
return t;
}
}
return child.type();
}
public String toString() {
return (qualifier != null ? qualifier + "." : "") + kind + "(...)";
}
/** Write the call to an output file. */
public void prettyPrint(CodeWriter w, PrettyPrinter tr)
{
if (qualifier != null) {
print(qualifier, w, tr);
w.write(".");
}
w.write(kind + "(");
w.begin(0);
for (Iterator i = arguments.iterator(); i.hasNext(); ) {
Expr e = (Expr) i.next();
print(e, w, tr);
if (i.hasNext()) {
w.write(",");
w.allowBreak(0);
}
}
w.end();
w.write(");");
}
public Term entry() {
if (qualifier != null) {
return qualifier.entry();
}
return listEntry(arguments, this);
}
public List acceptCFG(CFGBuilder v, List succs) {
if (qualifier != null) {
v.visitCFG(qualifier, listEntry(arguments, this));
}
v.visitCFGList(arguments, this);
return succs;
}
public List throwTypes(TypeSystem ts) {
List l = new LinkedList();
l.addAll(ci.throwTypes());
l.addAll(ts.uncheckedExceptions());
return l;
}
}