/*
* 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.types.*;
import polyglot.util.*;
import polyglot.visit.*;
import x10.errors.Errors;
/**
* An immutable representation of a <code>try</code> block, one or more
* <code>catch</code> blocks, and an optional <code>finally</code> block.
*/
public class Try_c extends Stmt_c implements Try
{
protected Block tryBlock;
protected List<Catch> catchBlocks;
protected Block finallyBlock;
public Try_c(Position pos, Block tryBlock, List<Catch> catchBlocks, Block finallyBlock) {
super(pos);
assert(tryBlock != null && catchBlocks != null); // finallyBlock may be null, catchBlocks empty
assert(! catchBlocks.isEmpty() || finallyBlock != null); // must be either try-catch or try(-catch)-finally
this.tryBlock = tryBlock;
this.catchBlocks = TypedList.copyAndCheck(catchBlocks, Catch.class, true);
this.finallyBlock = finallyBlock;
}
/** Get the try block of the statement. */
public Block tryBlock() {
return this.tryBlock;
}
/** Set the try block of the statement. */
public Try tryBlock(Block tryBlock) {
Try_c n = (Try_c) copy();
n.tryBlock = tryBlock;
return n;
}
/** Get the catch blocks of the statement. */
public List<Catch> catchBlocks() {
return Collections.unmodifiableList(this.catchBlocks);
}
/** Set the catch blocks of the statement. */
public Try catchBlocks(List<Catch> catchBlocks) {
Try_c n = (Try_c) copy();
n.catchBlocks = TypedList.copyAndCheck(catchBlocks, Catch.class, true);
return n;
}
/** Get the finally block of the statement. */
public Block finallyBlock() {
return this.finallyBlock;
}
/** Set the finally block of the statement. */
public Try finallyBlock(Block finallyBlock) {
Try_c n = (Try_c) copy();
n.finallyBlock = finallyBlock;
return n;
}
/** Reconstruct the statement. */
protected Try_c reconstruct(Block tryBlock, List<Catch> catchBlocks, Block finallyBlock) {
if (tryBlock != this.tryBlock || ! CollectionUtil.allEqual(catchBlocks, this.catchBlocks) || finallyBlock != this.finallyBlock) {
Try_c n = (Try_c) copy();
n.tryBlock = tryBlock;
n.catchBlocks = TypedList.copyAndCheck(catchBlocks, Catch.class, true);
n.finallyBlock = finallyBlock;
return n;
}
return this;
}
/** Visit the children of the statement. */
public Node visitChildren(NodeVisitor v) {
Block tryBlock = (Block) visitChild(this.tryBlock, v);
List<Catch> catchBlocks = visitList(this.catchBlocks, v);
Block finallyBlock = (Block) visitChild(this.finallyBlock, v);
return reconstruct(tryBlock, catchBlocks, finallyBlock);
}
@Override
public Node typeCheck(ContextVisitor tc) {
TypeSystem ts = tc.typeSystem();
SubtypeSet caught = new SubtypeSet(ts.CheckedThrowable());
// Walk through our catch blocks, making sure that they each can
// catch something.
for (Catch cb : this.catchBlocks) {
Type catchType = cb.catchType();
// Check if the exception has already been caught.
if (caught.contains(catchType)) {
Errors.issue(tc.job(),
new SemanticException("The exception \"" +catchType + "\" has been caught by an earlier catch block.",cb.position()),
this);
} else {
caught.add(catchType);
}
}
return super.typeCheck(tc);
}
/**
* Bypass all children when peforming an exception check.
* exceptionCheck(), called from ExceptionChecker.leave(),
* will handle visiting children.
*/
public NodeVisitor exceptionCheckEnter(ExceptionChecker ec) {
//ec = (ExceptionChecker) super.exceptionCheckEnter(ec);
//System.out.println("pruning at "+this+" "+this.position());
return new PruningVisitor();
}
/**
* Performs exceptionChecking. This is a special method that is called
* via the exceptionChecker's override method (i.e, doesn't follow the
* standard model for visitation.
*
* @param ec The ExceptionChecker that was run against the
* child node. It contains the exceptions that can be thrown by the try
* block.
*/
public Node exceptionCheck(ExceptionChecker ec) {
TypeSystem ts = ec.typeSystem();
ExceptionChecker origEC = ec;
if (this.finallyBlock != null && !this.finallyBlock.reachable()) {
// the finally block cannot terminate normally.
// This implies that exceptions thrown in the try and catch
// blocks will not propogate upwards.
// Prevent exceptions from propagation upwards past the finally
// block. (The original exception checker will be used
// for checking the finally block).
ec = ec.pushCatchAllThrowable();
}
ExceptionChecker newec = ec.push();
for (ListIterator<Catch> i = this.catchBlocks.listIterator(this.catchBlocks.size()); i.hasPrevious(); ) {
Catch cb = i.previous();
Type catchType = cb.catchType();
newec = newec.push(catchType);
}
// Visit the try block.
Block tryBlock = (Block) this.visitChild(this.tryBlock, newec);
SubtypeSet caught = new SubtypeSet(ts.CheckedThrowable());
// Walk through our catch blocks, making sure that they each can
// catch something.
// Visit the catch blocks, using the original exception checker
List<Catch> catchBlocks = new ArrayList<Catch>(this.catchBlocks.size());
for (Catch cb : this.catchBlocks) {
Type catchType = cb.catchType();
ec = ec.push();
cb = (Catch) this.visitChild(cb, ec);
ec = ec.pop();
// Check if the exception has already been caught.
if (caught.contains(catchType)) {
Errors.issue(ec.job(),
new SemanticException("The exception \"" +catchType + "\" has been caught by an earlier catch block.",cb.position()),
this);
} else {
catchBlocks.add(cb);
caught.add(catchType);
}
}
Block finallyBlock = null;
if (this.finallyBlock != null) {
ec = origEC;
finallyBlock = (Block) this.visitChild(this.finallyBlock, ec);
if (!this.finallyBlock.reachable()) {
// warn the user
// ###Don't warn, some versions of javac don't.
// ec.errorQueue().enqueue(ErrorInfo.WARNING,
// "The finally block cannot complete normally",
// finallyBlock.position());
}
ec = ec.pop();
}
// now that all the exceptions have been added to the exception checker,
// call the super method, which should set the exceptions field of
// Term_c.
Try_c t = (Try_c)super.exceptionCheck(ec);
return t.reconstruct(tryBlock, catchBlocks, finallyBlock);
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("try ");
sb.append(tryBlock.toString());
int count = 0;
for (Iterator<Catch> it = catchBlocks.iterator(); it.hasNext(); ) {
Catch cb = it.next();
if (count++ > 2) {
sb.append("...");
break;
}
sb.append(" ");
sb.append(cb.toString());
}
if (finallyBlock != null) {
sb.append(" finally ");
sb.append(finallyBlock.toString());
}
return sb.toString();
}
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
w.write("try");
printSubStmt(tryBlock, w, tr);
for (Iterator<Catch> it = catchBlocks.iterator(); it.hasNext(); ) {
Catch cb = it.next();
w.newline(0);
printBlock(cb, w, tr);
}
if (finallyBlock != null) {
w.newline(0);
w.write ("finally");
printSubStmt(finallyBlock, w, tr);
}
}
public Term firstChild() {
return tryBlock;
}
public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) {
// Add edges from the try entry to any catch blocks for Error and
// RuntimeException.
TypeSystem ts = v.typeSystem();
CFGBuilder v1 = v.push(this, false);
CFGBuilder v2 = v.push(this, true);
for (Iterator<Type> i = ts.uncheckedExceptions().iterator(); i.hasNext(); ) {
Type type = i.next();
v1.visitThrow(tryBlock, ENTRY, type);
}
// Handle the normal return case. The throw case will be handled
// specially.
if (finallyBlock != null) {
v1.visitCFG(tryBlock, finallyBlock, ENTRY);
v.visitCFG(finallyBlock, this, EXIT);
} else {
v1.visitCFG(tryBlock, this, EXIT);
}
for (Iterator<Catch> it = catchBlocks.iterator(); it.hasNext(); ) {
Catch cb = it.next();
if (finallyBlock != null) {
v2.visitCFG(cb, finallyBlock, ENTRY);
} else {
v2.visitCFG(cb, this, EXIT);
}
}
return succs;
}
}