package polyglot.ext.jl.ast;
import java.util.*;
import polyglot.ast.*;
import polyglot.main.Report;
import polyglot.types.*;
import polyglot.util.CodeWriter;
import polyglot.util.CollectionUtil;
import polyglot.util.Position;
import polyglot.util.SubtypeSet;
import polyglot.util.TypedList;
import polyglot.visit.*;
import polyglot.main.Options;
/**
* A method declaration.
*/
public class MethodDecl_c extends Term_c implements MethodDecl
{
protected Flags flags;
protected TypeNode returnType;
protected String name;
protected List formals;
protected List throwTypes;
protected Block body;
protected MethodInstance mi;
public MethodDecl_c(Position pos, Flags flags, TypeNode returnType, String name, List formals, List throwTypes, Block body) {
super(pos);
this.flags = flags;
this.returnType = returnType;
this.name = name;
this.formals = TypedList.copyAndCheck(formals, Formal.class, true);
this.throwTypes = TypedList.copyAndCheck(throwTypes, TypeNode.class, true);
this.body = body;
}
/** Get the flags of the method. */
public Flags flags() {
return this.flags;
}
/** Set the flags of the method. */
public MethodDecl flags(Flags flags) {
MethodDecl_c n = (MethodDecl_c) copy();
n.flags = flags;
return n;
}
/** Get the return type of the method. */
public TypeNode returnType() {
return this.returnType;
}
/** Set the return type of the method. */
public MethodDecl returnType(TypeNode returnType) {
MethodDecl_c n = (MethodDecl_c) copy();
n.returnType = returnType;
return n;
}
/** Get the name of the method. */
public String name() {
return this.name;
}
/** Set the name of the method. */
public MethodDecl name(String name) {
MethodDecl_c n = (MethodDecl_c) copy();
n.name = name;
return n;
}
/** Get the formals of the method. */
public List formals() {
return Collections.unmodifiableList(this.formals);
}
/** Set the formals of the method. */
public MethodDecl formals(List formals) {
MethodDecl_c n = (MethodDecl_c) copy();
n.formals = TypedList.copyAndCheck(formals, Formal.class, true);
return n;
}
/** Get the exception types of the method. */
public List throwTypes() {
return Collections.unmodifiableList(this.throwTypes);
}
/** Set the exception types of the method. */
public MethodDecl throwTypes(List throwTypes) {
MethodDecl_c n = (MethodDecl_c) copy();
n.throwTypes = TypedList.copyAndCheck(throwTypes, TypeNode.class, true);
return n;
}
/** Get the body of the method. */
public Block body() {
return this.body;
}
/** Set the body of the method. */
public CodeDecl body(Block body) {
MethodDecl_c n = (MethodDecl_c) copy();
n.body = body;
return n;
}
/** Get the method instance of the method. */
public MethodInstance methodInstance() {
return mi;
}
/** Set the method instance of the method. */
public MethodDecl methodInstance(MethodInstance mi) {
MethodDecl_c n = (MethodDecl_c) copy();
n.mi = mi;
return n;
}
public CodeInstance codeInstance() {
return procedureInstance();
}
/** Get the procedure instance of the method. */
public ProcedureInstance procedureInstance() {
return mi;
}
/** Reconstruct the method. */
protected MethodDecl_c reconstruct(TypeNode returnType, List formals, List throwTypes, Block body) {
if (returnType != this.returnType || ! CollectionUtil.equals(formals, this.formals) || ! CollectionUtil.equals(throwTypes, this.throwTypes) || body != this.body) {
MethodDecl_c n = (MethodDecl_c) copy();
n.returnType = returnType;
n.formals = TypedList.copyAndCheck(formals, Formal.class, true);
n.throwTypes = TypedList.copyAndCheck(throwTypes, TypeNode.class, true);
n.body = body;
return n;
}
return this;
}
/** Visit the children of the method. */
public Node visitChildren(NodeVisitor v) {
List formals = visitList(this.formals, v);
TypeNode returnType = (TypeNode) visitChild(this.returnType, v);
List throwTypes = visitList(this.throwTypes, v);
Block body = (Block) visitChild(this.body, v);
return reconstruct(returnType, formals, throwTypes, body);
}
public NodeVisitor buildTypesEnter(TypeBuilder tb) throws SemanticException {
return tb.pushCode();
}
public Node buildTypes(TypeBuilder tb) throws SemanticException {
TypeSystem ts = tb.typeSystem();
List l = new ArrayList(formals.size());
for (int i = 0; i < formals.size(); i++) {
l.add(ts.unknownType(position()));
}
List m = new ArrayList(throwTypes().size());
for (int i = 0; i < throwTypes().size(); i++) {
m.add(ts.unknownType(position()));
}
MethodInstance mi = ts.methodInstance(position(), ts.Object(),
Flags.NONE,
ts.unknownType(position()),
name, l, m);
return methodInstance(mi);
}
/** Build type objects for the method. */
public NodeVisitor disambiguateEnter(AmbiguityRemover ar) throws SemanticException {
if (ar.kind() == AmbiguityRemover.SUPER) {
return ar.bypassChildren(this);
}
else if (ar.kind() == AmbiguityRemover.SIGNATURES) {
if (body != null) {
return ar.bypass(body);
}
}
return ar;
}
public Node disambiguate(AmbiguityRemover ar) throws SemanticException {
if (ar.kind() == AmbiguityRemover.SIGNATURES) {
Context c = ar.context();
TypeSystem ts = ar.typeSystem();
ParsedClassType ct = c.currentClassScope();
MethodInstance mi = makeMethodInstance(ct, ts);
return flags(mi.flags()).methodInstance(mi);
}
return this;
}
public NodeVisitor addMembersEnter(AddMemberVisitor am) {
ParsedClassType ct = am.context().currentClassScope();
ct.addMethod(mi);
return am.bypassChildren(this);
}
public Context enterScope(Context c) {
if (Report.should_report(TOPICS, 5))
Report.report(5, "enter scope of method " + name);
c = c.pushCode(mi);
return c;
}
/** Type check the method. */
public Node typeCheck(TypeChecker tc) throws SemanticException {
TypeSystem ts = tc.typeSystem();
if (tc.context().currentClass().flags().isInterface()) {
if (flags().isProtected() || flags().isPrivate()) {
throw new SemanticException("Interface methods must be public.",
position());
}
}
try {
ts.checkMethodFlags(flags());
}
catch (SemanticException e) {
throw new SemanticException(e.getMessage(), position());
}
if (body == null && ! (flags().isAbstract() || flags().isNative())) {
throw new SemanticException("Missing method body.", position());
}
if (body != null && flags().isAbstract()) {
throw new SemanticException(
"An abstract method cannot have a body.", position());
}
if (body != null && flags().isNative()) {
throw new SemanticException(
"A native method cannot have a body.", position());
}
for (Iterator i = throwTypes().iterator(); i.hasNext(); ) {
TypeNode tn = (TypeNode) i.next();
Type t = tn.type();
if (! t.isThrowable()) {
throw new SemanticException("Type \"" + t +
"\" is not a subclass of \"" + ts.Throwable() + "\".",
tn.position());
}
}
// check that inner classes do not declare static methods
if (flags().isStatic() &&
methodInstance().container().toClass().isInnerClass()) {
// it's a static method in an inner class.
throw new SemanticException("Inner classes cannot declare " +
"static methods.", this.position());
}
overrideMethodCheck(tc);
return this;
}
protected void overrideMethodCheck(TypeChecker tc) throws SemanticException {
TypeSystem ts = tc.typeSystem();
for (Iterator j = mi.implemented().iterator(); j.hasNext(); ) {
MethodInstance mj = (MethodInstance) j.next();
if (! ts.isAccessible(mj, tc.context())) {
continue;
}
ts.checkOverride(mi, mj);
}
}
/** Check exceptions thrown by the method. */
public Node exceptionCheck(ExceptionChecker ec) throws SemanticException {
TypeSystem ts = ec.typeSystem();
SubtypeSet s = (SubtypeSet) ec.throwsSet();
for (Iterator i = s.iterator(); i.hasNext(); ) {
Type t = (Type) i.next();
boolean throwDeclared = false;
if (! t.isUncheckedException()) {
for (Iterator j = throwTypes().iterator(); j.hasNext(); ) {
TypeNode tn = (TypeNode) j.next();
Type tj = tn.type();
if (ts.isSubtype(t, tj)) {
throwDeclared = true;
break;
}
}
if (! throwDeclared) {
ec.throwsSet().clear();
Position pos = ec.exceptionPosition(t);
throw new SemanticException("The exception \"" + t +
"\" must either be caught or declared to be thrown.",
pos==null?position():pos);
}
}
}
ec.throwsSet().clear();
return super.exceptionCheck(ec);
}
public String toString() {
return flags.translate() + returnType + " " + name + "(...)";
}
/** Write the method to an output file. */
public void prettyPrintHeader(Flags flags, CodeWriter w, PrettyPrinter tr) {
w.begin(0);
w.write(flags.translate());
print(returnType, w, tr);
w.write(" " + name + "(");
w.begin(0);
for (Iterator i = formals.iterator(); i.hasNext(); ) {
Formal f = (Formal) i.next();
print(f, w, tr);
if (i.hasNext()) {
w.write(",");
w.allowBreak(0, " ");
}
}
w.end();
w.write(")");
if (! throwTypes().isEmpty()) {
w.allowBreak(6);
w.write("throws ");
for (Iterator i = throwTypes().iterator(); i.hasNext(); ) {
TypeNode tn = (TypeNode) i.next();
print(tn, w, tr);
if (i.hasNext()) {
w.write(",");
w.allowBreak(4, " ");
}
}
}
w.end();
}
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
prettyPrintHeader(flags(), w, tr);
if (body != null) {
printSubStmt(body, w, tr);
}
else {
w.write(";");
}
}
public void translate(CodeWriter w, Translator tr) {
Flags flags = flags();
prettyPrintHeader(flags(), w, tr);
if (body != null) {
printSubStmt(body, w, tr);
}
else {
w.write(";");
}
}
public void dump(CodeWriter w) {
super.dump(w);
if (mi != null) {
w.allowBreak(4, " ");
w.begin(0);
w.write("(instance " + mi + ")");
w.end();
}
w.allowBreak(4, " ");
w.begin(0);
w.write("(name " + name + ")");
w.end();
}
protected MethodInstance makeMethodInstance(ClassType ct, TypeSystem ts)
throws SemanticException {
List argTypes = new LinkedList();
List excTypes = new LinkedList();
for (Iterator i = formals.iterator(); i.hasNext(); ) {
Formal f = (Formal) i.next();
argTypes.add(f.declType());
}
for (Iterator i = throwTypes().iterator(); i.hasNext(); ) {
TypeNode tn = (TypeNode) i.next();
excTypes.add(tn.type());
}
Flags flags = this.flags;
if (ct.flags().isInterface()) {
flags = flags.Public().Abstract();
}
return ts.methodInstance(position(),
ct, flags, returnType.type(), name,
argTypes, excTypes);
}
/**
* Return the first (sub)term performed when evaluating this
* term.
*/
public Term entry() {
return listEntry(formals(), (body()==null? this : body().entry()));
}
/**
* Visit this term in evaluation order.
*/
public List acceptCFG(CFGBuilder v, List succs) {
if (body() == null) {
v.visitCFGList(formals(), this);
}
else {
v.visitCFGList(formals(), body().entry());
v.visitCFG(body(), this);
}
return succs;
}
protected static final Collection TOPICS =
CollectionUtil.list(Report.types, Report.context);
}