package polyglot.visit; import java.util.HashMap; import java.util.Map; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.frontend.Job; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.util.ErrorQueue; import polyglot.util.InternalCompilerError; import polyglot.util.Position; import polyglot.util.SubtypeSet; /** Visitor which checks if exceptions are caught or declared properly. */ public class ExceptionChecker extends ErrorHandlingVisitor { protected ExceptionChecker outer; /** * Lazily instantiate the SubtypeSet in the getter method */ private SubtypeSet scope = null; protected Map exceptionPositions; public ExceptionChecker(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf); this.outer = null; this.exceptionPositions = new HashMap(); } public ExceptionChecker pushNew() { ExceptionChecker ec = (ExceptionChecker) this.visitChildren(); ec.scope = null; ec.outer = this; ec.exceptionPositions = new HashMap(); return ec; } public ExceptionChecker push() { ExceptionChecker ec = (ExceptionChecker) this.visitChildren(); ec.outer = this; ec.exceptionPositions = new HashMap(); return ec; } public ExceptionChecker pop() { return outer; } /** * This method is called when we are to perform a "normal" traversal of * a subtree rooted at <code>n</code>. At every node, we will push a * stack frame. Each child node will add the exceptions that it throws * to this stack frame. For most nodes ( excdeption for the try / catch) * will just aggregate the stack frames. * * @param n The root of the subtree to be traversed. * @return The <code>NodeVisitor</code> which should be used to visit the * children of <code>n</code>. * */ protected NodeVisitor enterCall(Node n) throws SemanticException { return n.exceptionCheckEnter(this); } protected NodeVisitor enterError(Node n) { return push(); } /** * Here, we pop the stack frame that we pushed in enter and agregate the * exceptions. * * @param old The original state of root of the current subtree. * @param n The current state of the root of the current subtree. * @param v The <code>NodeVisitor</code> object used to visit the children. * @return The final result of the traversal of the tree rooted at * <code>n</code>. */ protected Node leaveCall(Node old, Node n, NodeVisitor v) throws SemanticException { ExceptionChecker inner = (ExceptionChecker) v; if (inner.outer != this) throw new InternalCompilerError("oops!"); // gather exceptions from this node. n = n.del().exceptionCheck(inner); // Merge results from the children and free the checker used for the // children. SubtypeSet t = inner.throwsSet(); throwsSet().addAll(t); exceptionPositions.putAll(inner.exceptionPositions); return n; } /** * The ast nodes will use this callback to notify us that they throw an * exception of type t. This should only be called by MethodExpr node, * and throw node, since they are the only node which can generate * exceptions. * * @param t The type of exception that the node throws. */ public void throwsException(Type t, Position pos) { throwsSet().add(t) ; exceptionPositions.put(t, pos); } /** * Method to allow the throws clause and method body to inspect and * modify the throwsSet. */ public SubtypeSet throwsSet() { if (scope == null) { this.scope = new SubtypeSet(ts.Throwable()); } return scope; } /** * Method to determine the position at which a particular exception is * thrown */ public Position exceptionPosition(Type t) { return (Position)exceptionPositions.get(t); } }