/* * 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; import x10.util.CollectionFactory; /** * A <code>Switch</code> is an immutable representation of a Java * <code>switch</code> statement. Such a statement has an expression which * is evaluated to determine where to branch to, an a list of labels * and block statements which are conditionally evaluated. One of the * labels, rather than having a constant expression, may be lablled * default. */ public class Switch_c extends Stmt_c implements Switch { protected Expr expr; protected List<SwitchElement> elements; public Switch_c(Position pos, Expr expr, List<SwitchElement> elements) { super(pos); assert(expr != null && elements != null); this.expr = expr; this.elements = TypedList.copyAndCheck(elements, SwitchElement.class, true); } /** Get the expression to switch on. */ public Expr expr() { return this.expr; } /** Set the expression to switch on. */ public Switch expr(Expr expr) { Switch_c n = (Switch_c) copy(); n.expr = expr; return n; } /** Get the switch elements of the statement. */ public List<SwitchElement> elements() { return Collections.unmodifiableList(this.elements); } /** Set the switch elements of the statement. */ public Switch elements(List<SwitchElement> elements) { Switch_c n = (Switch_c) copy(); n.elements = TypedList.copyAndCheck(elements, SwitchElement.class, true); return n; } /** Reconstruct the statement. */ protected Switch_c reconstruct(Expr expr, List<SwitchElement> elements) { if (expr != this.expr || ! CollectionUtil.allEqual(elements, this.elements)) { Switch_c n = (Switch_c) copy(); n.expr = expr; n.elements = TypedList.copyAndCheck(elements, SwitchElement.class, true); return n; } return this; } public Context enterScope(Context c) { return c.pushBlock(); } /** Visit the children of the statement. */ public Node visitChildren(NodeVisitor v) { Expr expr = (Expr) visitChild(this.expr, v); List<SwitchElement> elements = visitList(this.elements, v); return reconstruct(expr, elements); } /** Type check the statement. */ public Node typeCheckOverride(Node parent, ContextVisitor tc) { TypeSystem ts = tc.typeSystem(); Context c = parent.enterChildScope(this, tc.context()); ContextVisitor childtc = c == tc.context() ? tc : tc.context(c); Expr expr = (Expr) visitChild(this.expr, childtc); Type t = expr.type(); if (!ts.isIntOrLess(t) && !ts.isUInt(t) && !ts.isChar(t)) { Errors.issue(tc.job(), new SemanticException("Switch index must be a char or a (signed or unsigned) byte, short, or int, not "+t+".", position())); } Context bodyc = c.pushSwitchType(Types.baseType(t)); List<SwitchElement> elements = visitList(this.elements, childtc.context(bodyc)); Switch_c n = reconstruct(expr, elements); n = (Switch_c) tc.leave(parent, this, n, childtc); return n; } public Node checkConstants(ContextVisitor tc) { Collection<Object> labels = CollectionFactory.newHashSet(); List<SwitchElement> newBody = new ArrayList<SwitchElement>(); boolean changed = false; // Check for duplicate labels. for (Iterator<SwitchElement> i = elements.iterator(); i.hasNext();) { SwitchElement s = i.next(); if (s instanceof Case) { Case c = (Case) s; Object key; String str; if (c.isDefault()) { key = "default"; str = "default"; } else if (c.expr().isConstant()) { key = Long.valueOf(c.value()); str = c.expr().toString() + " (" + c.value() + ")"; } else { newBody.add(s); continue; } if (labels.contains(key)) { Errors.issue(tc.job(), new SemanticException("Duplicate case label: " +str + ".", c.position()), this); changed = true; } else { newBody.add(s); labels.add(key); } } } Switch n = this; if (changed) { n = n.elements(newBody); } return n; } public String toString() { return "switch (" + expr + ") { ... }"; } /** Write the statement to an output file. */ public void prettyPrint(CodeWriter w, PrettyPrinter tr) { w.write("switch ("); printBlock(expr, w, tr); w.write(") {"); w.unifiedBreak(4); w.begin(0); boolean lastWasCase = false; boolean first = true; for (Iterator<SwitchElement> i = elements.iterator(); i.hasNext();) { SwitchElement s = i.next(); if (s instanceof Case) { if (lastWasCase) w.unifiedBreak(0); else if (! first) w.unifiedBreak(0); printBlock(s, w, tr); lastWasCase = true; } else { w.unifiedBreak(4); print(s, w, tr); lastWasCase = false; } first = false; } w.end(); w.unifiedBreak(0); w.write("}"); } public Term firstChild() { return expr; } public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) { List<Term> cases = new ArrayList<Term>(elements.size()+1); List<Integer> entry = new ArrayList<Integer>(elements.size()+1); boolean hasDefault = false; for (Iterator<SwitchElement> i = elements.iterator(); i.hasNext(); ) { SwitchElement s = i.next(); if (s instanceof Case) { cases.add(s); entry.add(Integer.valueOf(ENTRY)); if (((Case) s).expr() == null) { hasDefault = true; } } } // If there is no default case, add an edge to the end of the switch. if (! hasDefault) { cases.add(this); entry.add(new Integer(EXIT)); } v.visitCFG(expr, FlowGraph.EDGE_KEY_OTHER, cases, entry); v.push(this).visitCFGList(elements, this, EXIT); return succs; } }