/* * This file is part of the OpenJML project. * Author: David R. Cok */ package org.jmlspecs.openjml.esc; import java.io.Writer; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.jmlspecs.annotation.NonNull; import org.jmlspecs.annotation.Pure; import org.jmlspecs.openjml.JmlPretty; import org.jmlspecs.openjml.JmlTree; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.util.Context; // FIXME - needs review - do we need all the fields of BasicProgram /** * An instantiation of BasicProgramParent with a specific kind of BasicBlock. * * @author David Cok */ // Note: everything declared protected is intended for use just in this class // and any future derived classes - not in the containing package public class BasicProgram extends BasicProgramParent<BasicProgram.BasicBlock> { /** Constructor of an empty program */ public BasicProgram(Context context) { super(context); } /** Factory method to create a new block. */ @Override protected BasicBlock newBlock(JCIdent id) { return new BasicBlock(id); } /** The id of the starting block; the pos of the block indicates where * in the original program the block starts */ //@ non_null protected JCIdent startId; /** This class represents a definition * <UL> * <LI>id: the identifier being defined * <LI>value: the defined value for the identifier * <LI>expr: an expression that is (id == value) * <LI>pos: a character position for the definition * </UL> */ static public class Definition { /** The character position for the definition */ public int pos; /** The identifier being defined */ public JCIdent id; /** The defined value of the identifier */ public JCExpression value; /** An expression representing (id == value) */ private JCExpression expr; /** Constructor for a new definition */ public Definition(int pos, JCIdent id, JCExpression value) { this.pos = pos; this.id = id; this.value = value; this.expr = null; } /** Returns the lazily created equality for the definition */ public JCExpression expr(Context context) { if (expr != null) return expr; expr = JmlTree.Maker.instance(context).Binary(JCTree.Tag.EQ,id,value); // use treeutils? expr.pos = id.pos; // FIXME _ end position not set, do we need it? expr.type = Symtab.instance(context).booleanType; return expr; } } /** A list of logical assertions (e.g. equalities that are definitions) * used in the block equations but are not block equations themselves. */ protected List<Definition> definitions = new ArrayList<Definition>(); // /** Axioms - that is, assertions that do not rely on declarations within the basic block program */ // protected List<JCExpression> pdefinitions = new ArrayList<JCExpression>(); /** A map of expressions and ids that are the assumptions to be checked for vacuity. */ //@ non_null public List<Map.Entry<JCExpression,String>> assumptionsToCheck = new LinkedList<Map.Entry<JCExpression,String>>(); /** Returns the (mutable) list of definitions that are part of this program * @return the program's definitions */ @Pure public List<Definition> definitions() { return definitions; } /** A list of background assertions that are needed to support the functions * and constants used in the program. */ protected List<JCExpression> background = new ArrayList<JCExpression>(); /** Returns the (mutable) list of background assertions that are part of this program * @return the program's background assertions */ @Pure public List<JCExpression> background() { return background; } // FIXME -document public JCIdent assumeCheckVar; /** The identifier for the starting block - must match one of the blocks. */ @Pure @NonNull public JCIdent startId() { return startId; } /** The starting block */ @Pure @NonNull public BasicBlock startBlock() { // Almost always the first one, but just in case, we // start with the first but check them all for (BasicBlock b: blocks) { if (b.id == startId) return b; } throw new RuntimeException("INTERNAL ERROR - A BasicProgram does not contain its own start block"); // FIXME - what exception to use } /** Writes out the BasicProgram to the given Writer (e.g. log.noticeWriter) for diagnostics */ public void write(Writer w) { try { w.append("START = " + startId + "\n"); JmlPretty pw = new JmlPretty(w,true); pw.useJMLComments = false; for (JCExpression e: background) { e.accept(pw); pw.println(); w.flush(); } for (JCIdent e: declarations) { pw.print(e.name); pw.print(" : "); pw.print(e.type); pw.println(); w.flush(); } for (Definition e: definitions) { e.id.accept(pw); pw.print(" ::: "); if (e.value != null) e.value.accept(pw); pw.println(); w.flush(); } for (BasicProgram.BasicBlock b: this.blocks) { b.write(w,this); } } catch (java.io.IOException e) { // FIXME - create an error of some sort System.out.println("EXCEPTION: " + e); e.printStackTrace(System.out); } } /** This class holds a basic block (a sequence of non-branching * statements, expressions have no embedded calls or side-effects such * as assignments). * The expressions in a BasicBlock use JCTree nodes. * Note that a BasicBlock becomes basic as a process of evolution. * @author David Cok * */ static public class BasicBlock extends BasicProgramParent.BlockParent<BasicBlock> { /** A constructor creating an empty block with a name * * @param id the name of the block */ BasicBlock(/*@ non_null*/JCIdent id) { super(id); } /** Writes out the basic block to the given Writer * * @param w where to put a String representation of the block */ public void write(Writer w, BasicProgram program) { // The 'false' argument allows non-compilable output and avoids // putting JML comment symbols everywhere JmlPretty pw = new JmlPretty(w,false); try { pw.print(id+":"+JmlPretty.lineSep); pw.print(" follows"); for (BasicBlock ss: preceders()) { pw.print(" "); pw.print(ss.id.toString()); } pw.print(JmlPretty.lineSep); pw.flush(); //pw.indentAndPrint(); // FIXME - can't we use indenting? for (JCTree t: statements) { pw.print(" "); t.accept(pw); if (program != null && t instanceof JmlTree.JmlStatementExpr && ((JmlTree.JmlStatementExpr)t).expression instanceof JCIdent) { JCIdent i = (JCIdent)((JmlTree.JmlStatementExpr)t).expression; for (Definition def : program.definitions) { if (def.id.name.equals(i.name)) { JCExpression rhs = def.value; w.write(" [ "); rhs.accept(pw); w.write(" ]"); break; } } } pw.print(JmlPretty.lineSep); pw.flush(); } if (followers.isEmpty()) { pw.print(" return;"); } else { pw.print(" goto"); boolean first = true; for (BasicBlock ss: followers) { if (first) first = false; else pw.print(","); pw.print(" "); pw.print(ss.id.toString()); } pw.print(";"); } //pw.undent(); // FIXME - check that this undents in the right place pw.print(JmlPretty.lineSep); pw.flush(); } catch (java.io.IOException e) { try { pw.print("EXCEPTION while pretty printing: " + e); } catch (java.io.IOException ee) { // Give up } } } @Override public void write(Writer w) { write(w,null); } } }