/*
* 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
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.ast;
import java.util.*;
import polyglot.frontend.Globals;
import polyglot.frontend.Goal;
import polyglot.types.*;
import polyglot.util.*;
import polyglot.visit.*;
import x10.errors.Errors;
import x10.ast.X10New;
import x10.types.X10ClassDef;
import x10.types.X10ConstructorInstance;
/**
* A <code>New</code> is an immutable representation of the use of the
* <code>new</code> operator to create a new instance of a class. In
* addition to the type of the class being created, a <code>New</code> has a
* list of arguments to be passed to the constructor of the object and an
* optional <code>ClassBody</code> used to support anonymous classes.
*/
public abstract class New_c extends Expr_c implements X10New
{
protected Expr qualifier;
protected TypeNode tn;
protected List<Expr> arguments;
protected ClassBody body;
protected ConstructorInstance ci;
protected X10ClassDef anonType;
public New_c(Position pos, Expr qualifier, TypeNode tn, List<Expr> arguments, ClassBody body) {
super(pos);
assert(tn != null && arguments != null); // qualifier and body may be null
this.qualifier = qualifier;
this.tn = tn;
this.arguments = TypedList.copyAndCheck(arguments, Expr.class, true);
this.body = body;
}
public List<Def> defs() {
if (body != null) {
return Collections.<Def>singletonList(anonType);
}
return Collections.<Def>emptyList();
}
/** Get the qualifier expression of the allocation. */
public Expr qualifier() {
return this.qualifier;
}
/** Set the qualifier expression of the allocation. */
public X10New qualifier(Expr qualifier) {
New_c n = (New_c) copy();
n.qualifier = qualifier;
return n;
}
/** Get the type we are instantiating. */
public TypeNode objectType() {
return this.tn;
}
/** Set the type we are instantiating. */
public X10New objectType(TypeNode tn) {
New_c n = (New_c) copy();
n.tn = tn;
return n;
}
public X10ClassDef anonType() {
return (X10ClassDef) this.anonType;
}
public X10New anonType(X10ClassDef anonType) {
if (anonType == this.anonType) return this;
New_c n = (New_c) copy();
n.anonType = anonType;
return n;
}
public ConstructorInstance procedureInstance() {
return constructorInstance();
}
public X10ConstructorInstance constructorInstance() {
return (X10ConstructorInstance)this.ci;
}
public New procedureInstance(ProcedureInstance<? extends ProcedureDef> pi) {
return constructorInstance((ConstructorInstance) pi);
}
public X10New constructorInstance(ConstructorInstance ci) {
if (ci == this.ci) return this;
New_c n = (New_c) copy();
n.ci = ci;
return n;
}
public List<Expr> arguments() {
return this.arguments;
}
public ProcedureCall arguments(List<Expr> arguments) {
New_c n = (New_c) copy();
n.arguments = TypedList.copyAndCheck(arguments, Expr.class, true);
return n;
}
public ClassBody body() {
return this.body;
}
public X10New body(ClassBody body) {
New_c n = (New_c) copy();
n.body = body;
return n;
}
/** Reconstruct the expression. */
protected New_c reconstruct(Expr qualifier, TypeNode tn, List<Expr> arguments, ClassBody body) {
if (qualifier != this.qualifier || tn != this.tn || ! CollectionUtil.allEqual(arguments, this.arguments) || body != this.body) {
New_c n = (New_c) copy();
n.tn = tn;
n.qualifier = qualifier;
n.arguments = TypedList.copyAndCheck(arguments, Expr.class, true);
n.body = body;
return n;
}
return this;
}
/** Visit the children of the expression. */
public abstract Node visitChildren(NodeVisitor v);
public Context enterChildScope(Node child, Context c) {
if (child == body && anonType != null && body != null) {
c = c.pushClass(anonType, anonType.asType());
}
return super.enterChildScope(child, c);
}
Goal enable = null;
public Node buildTypesOverride(TypeBuilder tb) {
TypeSystem ts = tb.typeSystem();
New_c n = this;
ConstructorInstance ci = ts.createConstructorInstance(position(), position(), new ErrorRef_c<ConstructorDef>(ts, position(), "Cannot get ConstructorDef before type-checking new expression."));
n = (New_c) n.constructorInstance(ci);
Expr qual = (Expr) n.visitChild(n.qualifier(), tb);
TypeNode objectType = (TypeNode) n.visitChild(n.objectType(), tb);
List<Expr> arguments = n.visitList(n.arguments(), tb);
ClassBody body = null;
if (n.body() != null) {
TypeBuilder tb2 = tb.pushAnonClass(position());
X10ClassDef type = tb2.currentClass();
type.superType(objectType.typeRef());
n = (New_c) n.anonType(type);
body = (ClassBody) n.visitChild(n.body(), tb2);
}
n = n.reconstruct(qual, objectType, arguments, body);
return n.type(ts.unknownType(position()));
}
public abstract New_c typeCheckObjectType(TypeChecker childtc);
@Override
public Node typeCheckOverride(Node parent, ContextVisitor tc) {
TypeChecker childtc;
NodeVisitor childv = tc.enter(parent, this);
if (childv instanceof TypeChecker) {
childtc = (TypeChecker) childv;
}
else {
return this;
}
New_c n = typeCheckHeader(childtc);
ClassBody body = (ClassBody) n.visitChild(n.body, childtc);
n = (New_c) n.body(body);
return tc.leave(parent, this, n, childtc);
}
protected New_c typeCheckHeader(TypeChecker childtc) {
TypeSystem ts = childtc.typeSystem();
New_c n = this;
n = n.typeCheckObjectType(childtc);
Expr qualifier = n.qualifier;
TypeNode tn = n.tn;
List<Expr> arguments = n.arguments;
ClassBody body = n.body;
if (body != null) {
Ref<? extends Type> ct = tn.typeRef();
ClassDef anonType = n.anonType();
assert anonType != null;
if (! ct.get().toClass().flags().isInterface()) {
anonType.superType(ct);
}
else {
// [DC] assume that not setting this is OK
// we don't have a root class anymore (just a root interface: Any)
//anonType.superType(Types.<Type>ref(ts.Object()));
assert anonType.interfaces().isEmpty() || anonType.interfaces().get(0) == ct;
if (anonType.interfaces().isEmpty())
anonType.addInterface(ct);
}
}
arguments = visitList(arguments, childtc);
n = n.reconstruct(qualifier, tn, arguments, body);
return n;
}
/**
* @param ar
* @param ct
* @throws SemanticException
*/
protected abstract New findQualifier(TypeChecker ar, ClassType ct);
public abstract Node typeCheck(ContextVisitor tc);
protected void typeCheckNested(ContextVisitor tc) throws SemanticException {
if (qualifier != null) {
// We have not disambiguated the type node yet.
// Get the qualifier type first.
Type qt = qualifier.type();
if (! qt.isClass()) {
throw new SemanticException("Cannot instantiate member class of a non-class type.", qualifier.position());
}
// Disambiguate the type node as a member of the qualifier type.
ClassType ct = tn.type().toClass();
// According to JLS2 15.9.1, the class type being
// instantiated must be inner.
if (! ct.isInnerClass()) {
if (!(qualifier instanceof Special)) // Yoav added "this" qualifier for non-static anonymous classes
throw new SemanticException("Cannot provide a containing instance for non-inner class " + ct.fullName() + ".", qualifier.position());
}
}
else {
ClassType ct = tn.type().toClass();
if (ct.isMember()) {
for (ClassType t = ct; t.isMember(); t = t.outer()) {
if (! t.flags().isStatic()) {
throw new SemanticException("Cannot allocate non-static member class \"" +t + "\".", position());
}
}
}
}
}
protected void typeCheckFlags(ContextVisitor tc) throws SemanticException {
ClassType ct = tn.type().toClass();
if (this.body == null) {
if (ct.flags().isInterface()) {
throw new SemanticException("Cannot instantiate an interface.", position());
}
if (ct.flags().isAbstract()) {
throw new SemanticException("Cannot instantiate an abstract class.", position());
}
}
else {
if (ct.flags().isFinal()) {
throw new SemanticException("Cannot create an anonymous subclass of a final class.", position());
}
if (ct.flags().isInterface() && ! arguments.isEmpty()) {
throw new SemanticException("Cannot pass arguments to an anonymous class that implements an interface.",arguments.get(0).position());
}
}
}
@Override
public Node conformanceCheck(ContextVisitor tc) {
if (body == null) {
return this;
}
// Check that the anonymous class implements all abstract methods that it needs to.
try {
TypeSystem ts = tc.typeSystem();
ts.checkClassConformance(anonType.asType(), enterChildScope(body, tc.context()));
} catch (SemanticException e) {
Errors.issue(tc.job(), e, this);
}
return this;
}
/** Get the precedence of the expression. */
public Precedence precedence() {
return Precedence.LITERAL;
}
public String toString() {
return (qualifier != null ? (qualifier.toString() + ".") : "") +
"new " + tn + "(...)" + (body != null ? " " + body : "");
}
protected void printQualifier(CodeWriter w, PrettyPrinter tr) {
if (qualifier != null) {
print(qualifier, w, tr);
w.write(".");
}
}
protected void printArgs(CodeWriter w, PrettyPrinter tr) {
w.write("(");
w.allowBreak(2, 2, "", 0);
w.begin(0);
for (Iterator<Expr> i = arguments.iterator(); i.hasNext();) {
Expr e = i.next();
print(e, w, tr);
if (i.hasNext()) {
w.write(",");
w.allowBreak(0);
}
}
w.end();
w.write(")");
}
protected void printBody(CodeWriter w, PrettyPrinter tr) {
if (body != null) {
w.write(" {");
print(body, w, tr);
w.write("}");
}
}
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
printQualifier(w, tr);
w.write("new ");
// We need to be careful when pretty printing "new" expressions for
// member classes. For the expression "e.new C()" where "e" has
// static type "T", the TypeNode for "C" is actually the type "T.C".
// But, if we print "T.C", the post compiler will try to lookup "T"
// in "T". Instead, we print just "C".
if (qualifier != null) {
w.write(tn.nameString());
}
else {
print(tn, w, tr);
}
printArgs(w, tr);
printBody(w, tr);
}
public Term firstChild() {
return qualifier != null ? (Term) qualifier : tn;
}
public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) {
if (qualifier != null) {
v.visitCFG(qualifier, tn, ENTRY);
}
if (body() != null) {
v.visitCFG(tn, listChild(arguments, body()), ENTRY);
v.visitCFGList(arguments, body(), ENTRY);
v.visitCFG(body(), this, EXIT);
} else {
if (!arguments.isEmpty()) {
v.visitCFG(tn, listChild(arguments, null), ENTRY);
v.visitCFGList(arguments, this, EXIT);
} else {
v.visitCFG(tn, this, EXIT);
}
}
return succs;
}
public List<Type> throwTypes(TypeSystem ts) {
List<Type> l = new ArrayList<Type>();
assert ci != null : "null ci for " + this;
l.addAll(ci.throwTypes());
l.addAll(ts.uncheckedExceptions());
return l;
}
}