/* * 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.main.Reporter; import polyglot.types.*; import polyglot.util.*; import polyglot.visit.*; import x10.errors.Errors; import x10.errors.Errors.InterfaceMethodsMustBePublic; import x10.types.MethodInstance; import x10.types.X10ClassDef; import x10.types.X10TypeEnv_c; import x10.visit.Desugarer; import x10.ast.X10ClassDecl_c; /** * A method declaration. */ public abstract class MethodDecl_c extends Term_c implements MethodDecl { protected FlagsNode flags; protected TypeNode returnType; protected Id name; protected List<Formal> formals; // protected List<TypeNode> throwTypes; protected Block body; protected MethodDef mi; public MethodDecl_c(Position pos, FlagsNode flags, TypeNode returnType, Id name, List<Formal> formals, Block body) { super(pos); assert(flags != null && returnType != null && name != null && formals != null); // body may be null this.flags = flags; this.returnType = returnType; this.name = name; this.formals = TypedList.copyAndCheck(formals, Formal.class, true); this.body = body; } public List<Def> defs() { return Collections.<Def>singletonList(mi); } public MemberDef memberDef() { return mi; } /** Get the flags of the method. */ public FlagsNode flags() { return this.flags; } /** Set the flags of the method. */ public MethodDecl flags(FlagsNode 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 Id name() { return this.name; } /** Set the name of the method. */ public MethodDecl name(Id name) { MethodDecl_c n = (MethodDecl_c) copy(); n.name = name; return n; } /** Get the formals of the method. */ public List<Formal> formals() { return Collections.<Formal>unmodifiableList(this.formals); } /** Set the formals of the method. */ public MethodDecl formals(List<Formal> formals) { MethodDecl_c n = (MethodDecl_c) copy(); n.formals = TypedList.copyAndCheck(formals, Formal.class, true); return n; } public Term codeBody() { return this.body; } /** Get the body of the method. */ public Block body() { return this.body; } /** Set the body of the method. */ public CodeBlock body(Block body) { MethodDecl_c n = (MethodDecl_c) copy(); n.body = body; return n; } /** Get the method instance of the method. */ public MethodDef methodDef() { return mi; } /** Set the method instance of the method. */ public MethodDecl methodDef(MethodDef mi) { if (mi == this.mi) return this; MethodDecl_c n = (MethodDecl_c) copy(); n.mi = mi; return n; } public CodeDef codeDef() { return procedureInstance(); } /** Get the procedure instance of the method. */ public ProcedureDef procedureInstance() { return mi; } /** Reconstruct the method. */ protected MethodDecl_c reconstruct(FlagsNode flags, TypeNode returnType, Id name, List<Formal> formals, Block body) { if (flags != this.flags || returnType != this.returnType || name != this.name || ! CollectionUtil.<Formal>allEqual(formals, this.formals) || body != this.body) { MethodDecl_c n = (MethodDecl_c) copy(); n.flags = flags; n.returnType = returnType; n.name = name; n.formals = TypedList.copyAndCheck(formals, Formal.class, true); n.body = body; return n; } return this; } /** Visit the children of the method. */ public Node visitChildren(NodeVisitor v) { MethodDecl_c n = (MethodDecl_c) visitSignature(v); Block body = (Block) n.visitChild(n.body, v); return body == n.body ? n : n.body(body); } public abstract Node buildTypesOverride(TypeBuilder tb); protected abstract MethodDef createMethodDef(TypeSystem ts, X10ClassDef ct, Flags flags); public Context enterScope(Context c) { Reporter reporter = c.typeSystem().extensionInfo().getOptions().reporter; if (reporter.should_report(TOPICS, 5)) reporter.report(5, "enter scope of method " + name); c = c.pushCode(mi); return c; } public abstract Node visitSignature(NodeVisitor v); /** Type check the declaration. */ public Node typeCheckBody(Node parent, TypeChecker tc, TypeChecker childtc) { MethodDecl_c n = this; Block body = (Block) n.visitChild(n.body, childtc); n = (MethodDecl_c) n.body(body); return n; } @Override public Node conformanceCheck(ContextVisitor tc) { // Get the mi flags, not the node flags since the mi flags // account for being nested within an interface. Flags flags = mi.flags(); checkFlags(tc, flags); overrideMethodCheck(tc); return this; } protected void checkFlags(ContextVisitor tc, Flags flags) { TypeSystem ts = tc.typeSystem(); if (tc.context().currentClass().flags().isInterface()) { if (flags.isProtected() || flags.isPrivate()) { Errors.issue(tc.job(), new Errors.InterfaceMethodsMustBePublic(name().position())); } if (flags.isStatic()) { Errors.issue(tc.job(), new Errors.InterfaceMethodsCannotBeStatic(name().position())); } } try { ts.checkMethodFlags(flags); } catch (SemanticException e) { Errors.issue(tc.job(), e, this); } Type container = Types.get(methodDef().container()); ClassType ct = container.toClass(); if (body == null && ! (flags.isAbstract() || flags.isNative())) { Errors.issue(tc.job(), new Errors.MissingMethodBody(name().position())); } if (body != null && ct.flags().isInterface()) { Errors.issue(tc.job(), new Errors.InterfaceMethodsCannotHaveBody(name().position())); } if (body != null && flags.isAbstract()) { Errors.issue(tc.job(), new Errors.AbstractMethodCannotHaveBody(name().position())); } if (body != null && flags.isNative()) { Errors.issue(tc.job(), new Errors.NativeMethodCannotHaveBody(name().position())); } // check that inner classes do not declare static methods if (ct != null && flags.isStatic() && (ct.isInnerClass() || ct.isLocal() || ct.isAnonymous())) { // it's a static method in an inner class. Errors.issue(tc.job(), new Errors.InnerClassesCannotDeclareStaticMethods(methodDef(), ct, name().position())); } } protected void overrideMethodCheck(ContextVisitor tc) { TypeSystem ts = tc.typeSystem(); MethodInstance mi = methodDef().asInstance(); for (MethodInstance mj : mi.implemented(tc.context()) ){ if (! ts.isAccessible(mj, tc.context())) { // [DC] presumably this will be raising an error somewhere else? continue; } mj = X10ClassDecl_c.expandMacros(tc, ts, mi, mj); try { ts.checkOverride(mi, mj, tc.context()); } catch (SemanticException e) { Errors.issue(tc.job(), e, this); } } } public NodeVisitor exceptionCheckEnter(ExceptionChecker ec) { return ec.push(new ExceptionChecker.CodeTypeReporter("Method " + mi.signature())) .push(methodDef().asInstance().throwTypes()); } public abstract String toString(); /** Write the method to an output file. */ public void prettyPrintHeader(CodeWriter w, PrettyPrinter tr) { w.begin(0); print(flags, w, tr); print(returnType, w, tr); w.allowBreak(2, 2, " ", 1); w.write(name + "("); w.allowBreak(2, 2, "", 0); w.begin(0); for (Iterator<Formal> i = formals.iterator(); i.hasNext(); ) { Formal f = 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<TypeNode> 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(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(); } public Term firstChild() { return listChild(formals(), returnType()); } public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) { v.visitCFGList(formals(), returnType(), ENTRY); if (body() == null) { v.visitCFG(returnType(), this, EXIT); } else { v.visitCFG(returnType(), body(), ENTRY); v.visitCFG(body(), this, EXIT); } return succs; } private static final Collection<String> TOPICS = CollectionUtil.list(Reporter.types, Reporter.context); }