package polyglot.ext.jl.ast;
import java.util.*;
import polyglot.main.Options;
import polyglot.ast.*;
import polyglot.frontend.Pass;
import polyglot.frontend.Job;
import polyglot.types.*;
import polyglot.util.*;
import polyglot.visit.*;
/**
* 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 class New_c extends Expr_c implements New
{
protected Expr qualifier;
protected TypeNode tn;
protected List arguments;
protected ClassBody body;
protected ConstructorInstance ci;
protected ParsedClassType anonType;
public New_c(Position pos, Expr qualifier, TypeNode tn, List arguments, ClassBody body) {
super(pos);
this.qualifier = qualifier;
this.tn = tn;
this.arguments = TypedList.copyAndCheck(arguments, Expr.class, true);
this.body = body;
}
/** Get the qualifier expression of the allocation. */
public Expr qualifier() {
return this.qualifier;
}
/** Set the qualifier expression of the allocation. */
public New 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 New objectType(TypeNode tn) {
New_c n = (New_c) copy();
n.tn = tn;
return n;
}
public ParsedClassType anonType() {
return this.anonType;
}
public New anonType(ParsedClassType anonType) {
New_c n = (New_c) copy();
n.anonType = anonType;
return n;
}
public ProcedureInstance procedureInstance() {
return constructorInstance();
}
public ConstructorInstance constructorInstance() {
return this.ci;
}
public New constructorInstance(ConstructorInstance ci) {
New_c n = (New_c) copy();
n.ci = ci;
return n;
}
public List arguments() {
return this.arguments;
}
public ProcedureCall arguments(List arguments) {
New_c n = (New_c) copy();
n.arguments = TypedList.copyAndCheck(arguments, Expr.class, true);
return n;
}
public ClassBody body() {
return this.body;
}
public New 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 arguments, ClassBody body) {
if (qualifier != this.qualifier || tn != this.tn || ! CollectionUtil.equals(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 Node visitChildren(NodeVisitor v) {
Expr qualifier = (Expr) visitChild(this.qualifier, v);
TypeNode tn = (TypeNode) visitChild(this.tn, v);
List arguments = visitList(this.arguments, v);
ClassBody body = (ClassBody) visitChild(this.body, v);
return reconstruct(qualifier, tn, arguments, body);
}
public Context enterScope(Node child, Context c) {
if (child == body && anonType != null && body != null) {
c = c.pushClass(anonType, anonType);
}
return super.enterScope(child, c);
}
public NodeVisitor buildTypesEnter(TypeBuilder tb) throws SemanticException {
if (body != null) {
// bybass the visiting of the body of the anonymous class. We'll
// get around to visiting it in the buildTypes method.
// We do this because we need to visit the body of the anonymous
// class after we've pushed an anon class onto the type builder,
// but we need to check the arguments, and qualifier, etc. outside
// of the scope of the anon class.
return tb.bypass(body);
}
return tb;
}
public Node buildTypes(TypeBuilder tb) throws SemanticException {
New_c n = this;
if (n.body() != null) {
// let's get a type builder that is prepared to visit the
// body; tb wants to bypass it, due to the builtTypesEnter method.
TypeBuilder bodyTB = (TypeBuilder)tb.visitChildren();
// push an anonymous class on the stack.
bodyTB = bodyTB.pushAnonClass(position());
n = (New_c) n.body((ClassBody)n.body().visit(bodyTB));
ParsedClassType type = (ParsedClassType) bodyTB.currentClass();
n = (New_c) n.anonType(type);
}
TypeSystem ts = tb.typeSystem();
List l = new ArrayList(n.arguments.size());
for (int i = 0; i < n.arguments.size(); i++) {
l.add(ts.unknownType(position()));
}
ConstructorInstance ci = ts.constructorInstance(position(), ts.Object(),
Flags.NONE, l,
Collections.EMPTY_LIST);
n = (New_c) n.constructorInstance(ci);
return n.type(ts.unknownType(position()));
}
public NodeVisitor disambiguateEnter(AmbiguityRemover ar)
throws SemanticException
{
// We can't disambiguate the type node if we have a qualifier. The
// type node represents an inner class of the qualifier, and we don't
// know which outer class to look in until the qualifier is type
// checked.
if (qualifier != null) {
ar = (AmbiguityRemover) ar.bypass(tn);
}
if (body != null) {
ar = (AmbiguityRemover) ar.bypass(body);
}
return ar;
}
public Node disambiguate(AmbiguityRemover ar) throws SemanticException {
if (ar.kind() != AmbiguityRemover.ALL) {
return this;
}
if (qualifier == null) {
ClassType ct = tn.type().toClass();
if (! ct.isMember() || ct.flags().isStatic()) {
return this;
}
// If we're instantiating a non-static member class, add a "this"
// qualifier.
NodeFactory nf = ar.nodeFactory();
TypeSystem ts = ar.typeSystem();
Context c = ar.context();
// Search for the outer class of the member. The outer class is
// not just ct.outer(); it may be a subclass of ct.outer().
Type outer = null;
String name = ct.name();
ClassType t = c.currentClass();
// We're in one scope too many.
if (t == anonType) {
t = t.outer();
}
while (t != null) {
try {
// HACK: PolyJ outer() doesn't work
t = ts.staticTarget(t).toClass();
ClassType mt = ts.findMemberClass(t, name, c.currentClass());
if (ts.equals(mt, ct)) {
outer = t;
break;
}
}
catch (SemanticException e) {
}
t = t.outer();
}
if (outer == null) {
throw new SemanticException("Could not find non-static member class \"" +
name + "\".", position());
}
// Create the qualifier.
Expr q;
if (outer.equals(c.currentClass())) {
q = nf.This(position());
}
else {
q = nf.This(position(),
nf.CanonicalTypeNode(position(),
outer));
}
return qualifier(q);
}
return this;
}
public NodeVisitor typeCheckEnter(TypeChecker tc) throws SemanticException {
if (qualifier != null) {
tc = (TypeChecker) tc.bypass(tn);
}
if (body != null) {
tc = (TypeChecker) tc.bypass(body);
}
return tc;
}
public Node typeCheck(TypeChecker tc) throws SemanticException {
New_c n = this;
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.
TypeNode tn = disambiguateTypeNode(tc, qt.toClass());
ClassType ct = tn.type().toClass();
/*
FIXME: check super types as well.
if (! ct.isMember() || ! ts.isEnclosed(ct, qt.toClass())) {
throw new SemanticException("Class \"" + qt +
"\" does not enclose \"" + ct + "\".",
qualifier.position());
}
*/
// According to JLS2 15.9.1, the class type being
// instantiated must be inner.
if (! ct.isInnerClass()) {
throw new SemanticException(
"Cannot provide a containing instance for non-inner class " +
ct.fullName() + ".", qualifier.position());
}
n = (New_c) n.objectType(tn);
}
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());
}
}
}
}
return n.typeCheckEpilogue(tc);
}
protected Node typeCheckEpilogue(TypeChecker tc) throws SemanticException {
TypeSystem ts = tc.typeSystem();
List argTypes = new ArrayList(arguments.size());
for (Iterator i = this.arguments.iterator(); i.hasNext(); ) {
Expr e = (Expr) i.next();
argTypes.add(e.type());
}
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.",
((Expr) arguments.get(0)).position());
}
}
if (! ct.flags().isInterface()) {
Context c = tc.context();
if (body != null) {
// Enter the body of this class so we can access protected
// super-constructors.
// temporarily set the super type; we'll set it correctly below
anonType.superType(ct);
c = c.pushClass(anonType, anonType);
}
ci = ts.findConstructor(ct, argTypes, c.currentClass());
}
else {
ci = ts.defaultConstructor(position(), ct);
}
New_c n = (New_c) this.constructorInstance(ci).type(ct);
if (n.body == null) {
return n;
}
// Now, need to read symbols, clean, disambiguate, and type check
// the body.
if (! ct.flags().isInterface()) {
anonType.superType(ct);
}
else {
anonType.superType(ts.Object());
anonType.addInterface(ct);
}
anonType.inStaticContext(tc.context().inStaticContext());
// The type of the new expression is actually the anon type.
n = (New_c)n.type(anonType);
// Now, run the four passes on the body.
ClassBody body = n.typeCheckBody(tc, ct);
return n.body(body);
}
protected TypeNode partialDisambTypeNode(TypeNode tn, TypeChecker tc, ClassType outer) throws SemanticException
{
// We have to disambiguate the type node as if it were a member of the
// outer class. For Java this is simple: outer is just a name and we
// use that name to lookup a member of the outer class. For some
// extensions (e.g., PolyJ), the type node may be more complex than
// just a name. We'll just punt here and let the extensions handle
// this complexity.
if (tn instanceof CanonicalTypeNode) {
return tn;
}
String name = null;
if (tn instanceof AmbTypeNode && ((AmbTypeNode) tn).qual() == null) {
name = ((AmbTypeNode) tn).name();
}
else {
throw new SemanticException(
"Cannot instantiate an member class.",
tn.position());
}
TypeSystem ts = tc.typeSystem();
NodeFactory nf = tc.nodeFactory();
Context c = tc.context();
ClassType ct = ts.findMemberClass(outer, name, c.currentClass());
return nf.CanonicalTypeNode(tn.position(), ct);
}
protected TypeNode disambiguateTypeNode(TypeChecker tc, ClassType ct)
throws SemanticException
{
TypeNode tn = this.partialDisambTypeNode(this.tn, tc, ct);
if (tn instanceof CanonicalTypeNode) {
return tn;
}
// Run the disambiguation passes on the node.
Job sj = tc.job().spawn(tc.context(), tn,
Pass.CLEAN_SUPER, Pass.DISAM_ALL);
if (! sj.status()) {
if (! sj.reportedErrors()) {
throw new SemanticException("Could not disambiguate type.",
this.tn.position());
}
throw new SemanticException();
}
tn = (TypeNode) sj.ast();
// Now, type-check the type node.
return (TypeNode) visitChild(tn, tc);
}
protected ClassBody typeCheckBody(TypeChecker tc, ClassType superType)
throws SemanticException
{
Context bodyCtxt = tc.context().pushClass(anonType, anonType);
Job sj = tc.job().spawn(bodyCtxt, body,
Pass.CLEAN_SUPER, Pass.DISAM_ALL);
if (! sj.status()) {
if (! sj.reportedErrors()) {
throw new SemanticException("Could not disambiguate body of " +
"anonymous " +
(superType.flags().isInterface() ?
"implementor" : "subclass") +
" of \"" + superType + "\".");
}
throw new SemanticException();
}
ClassBody b = (ClassBody) sj.ast();
// Now, type-check the body.
TypeChecker bodyTC = (TypeChecker)tc.context(bodyCtxt);
b = (ClassBody) visitChild(b, bodyTC.visitChildren());
// check the class implements all abstract methods that it needs to.
bodyTC.typeSystem().checkClassConformance(anonType());
return b;
}
public Type childExpectedType(Expr child, AscriptionVisitor av) {
if (child == qualifier) {
ReferenceType t = ci.container();
if (t.isClass() && t.toClass().isMember()) {
t = t.toClass().container();
return t;
}
return child.type();
}
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 Node exceptionCheck(ExceptionChecker ec) throws SemanticException {
// something didn't work in the type check phase, so just ignore it.
if (ci == null) {
throw new InternalCompilerError(position(),
"Null constructor instance after type check.");
}
for (Iterator i = ci.throwTypes().iterator(); i.hasNext(); ) {
Type t = (Type) i.next();
ec.throwsException(t, position());
}
return super.exceptionCheck(ec);
}
/** 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.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(")");
}
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.name());
}
else {
print(tn, w, tr);
}
printArgs(w, tr);
printBody(w, tr);
}
public Term entry() {
if (qualifier != null) return qualifier.entry();
Term afterArgs = this;
if (body() != null) {
afterArgs = body();
}
return listEntry(arguments, afterArgs);
}
public List acceptCFG(CFGBuilder v, List succs) {
Term afterArgs = this;
if (body() != null) {
afterArgs = body();
}
if (qualifier != null) {
v.visitCFG(qualifier, listEntry(arguments, afterArgs));
}
v.visitCFGList(arguments, afterArgs);
if (body() != null) {
v.visitCFG(body(), this);
}
return succs;
}
public List throwTypes(TypeSystem ts) {
List l = new LinkedList();
l.addAll(ci.throwTypes());
l.addAll(ts.uncheckedExceptions());
return l;
}
}