/* * This file is part of the OpenJML project. * Author: David R. Cok */ package org.jmlspecs.openjml.esc; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jmlspecs.annotation.Pure; import org.jmlspecs.openjml.JmlPretty; import org.jmlspecs.openjml.JmlTokenKind; import org.jmlspecs.openjml.JmlTree.JmlBBArrayAssignment; import org.jmlspecs.openjml.JmlTree.JmlBBFieldAssignment; import org.jmlspecs.openjml.JmlTree.JmlStatementExpr; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCArrayAccess; import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Context; /** * A BoogieProgram is an equivalent representation of a method as a Boogie 2 program: * <UL> * <LI>it expresses the program as a DAG of basic blocks * <LI>each block represents a non-branching set of code * <LI>FIXME - describe Boogie form * </UL> * * @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 BoogieProgram extends BasicProgramParent<BoogieProgram.BoogieBlock>{ /** Sets the style of handling heaps and types used by this program. * (FIXME - document these) */ protected int style = 0; protected final int HEAP_STYLE = 1; protected final int SEP_STYLE = 0; /** Constructor of an empty program */ public BoogieProgram(Context context) { super(context); } /** Factory method to create a new block. */ @Override protected BoogieBlock newBlock(JCIdent id) { return new BoogieBlock(id); } /** 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 definitions */ @Pure public List<JCExpression> background() { return background; } // FIXME - document public Map<String,JmlStatementExpr> assertMap = new HashMap<String,JmlStatementExpr>(); /** A pretty printer for types */ public String trType(Type t) { if (t == syms.booleanType) return "bool"; if (t == syms.intType) return "int"; // FIXME - needs more type conversions to Boogie types return "REF"; } /** Writes out the BasicProgram to the given Writer */ public void write(Writer w) { try { JmlPretty pw = new BoogiePrinter(w); pw.print("type REF; "); pw.println(); pw.print("type Arrays a = [REF][int] a; "); pw.println(); pw.print("var arrays_int: Arrays int ;"); pw.println(); pw.print("var arrays_REF: Arrays REF; "); pw.println(); pw.print("const unique null : REF ;"); pw.println(); pw.print("const unique this__0 : REF ;"); pw.println(); pw.print("procedure "); pw.print(methodDecl.name); pw.print("("); boolean first = true; for (JCVariableDecl p : methodDecl.params) { if (first) { first = false; } else pw.print(","); pw.print(p.name.toString() + "__" + p.pos); // FIXME - does this format have to match something somewhere pw.print(" : "); pw.print(trType(p.type)); } pw.print(") returns ()"); // FIXME - needs return type pw.println(); pw.print(" modifies arrays_int;"); pw.println(); // FIXME - needs the spec pw.print("{"); pw.println(); for (JCIdent e: declarations) { if (e.sym != null && e.sym.owner != null && !e.sym.isStatic() && e.sym.owner instanceof Symbol.ClassSymbol) { if (style == SEP_STYLE) { pw.print("var "); pw.print(e.name.toString()); pw.print(" : [REF]"); pw.print(trType(e.type)); } } else { pw.print("var "); pw.print(e.name.toString()); pw.print(" : "); pw.print(trType(e.type)); } pw.print(" ;"); pw.println(); w.flush(); } for (JCExpression e: background) { e.accept(pw); pw.println(); w.flush(); } for (BoogieProgram.BoogieBlock b: this.blocks) { b.write(w,pw); } pw.print("}"); pw.println(); } catch (java.io.IOException e) { // FIXME - make some sort of error instead of writing to System.out 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 BoogieBlock use JCTree nodes. * Note that a basic block becomes basic as a process of evolution. * @author David Cok * */ static public class BoogieBlock extends BasicProgramParent.BlockParent<BoogieBlock> { /** A constructor creating an empty block with a name and position * * @param id the name of the block */ BoogieBlock(/*@ 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, JmlPretty pw) { try { pw.print(id+":"+JmlPretty.lineSep); pw.flush(); pw.indentAndRealign(); for (JCTree t: statements) { t.accept(pw); pw.print("\n"); pw.flush(); } pw.undent(); if (followers.isEmpty()) { pw.print("return;"); } else { pw.print("goto"); boolean first = true; for (BoogieBlock ss: followers) { if (first) first = false; else w.write(","); 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, new JmlPretty(w,false)); } } // FIXME - document public class BoogiePrinter extends JmlPretty { public BoogiePrinter(Writer out) { super(out,false); } public void visitJmlStatementExpr(JmlStatementExpr that) { try { if (that.token == JmlTokenKind.COMMENT) { // SKIP //print("// "); //print(((JCLiteral)that.expression).value); // FIXME - can the comment span more than one line? } else { print(that.token.internedName()); print(" "); if (that.label != null) { print("{ :reason \""); print(that.label); print("\"} "); } if (that.id != null) { print("{ :id "); print(that.id); print("} "); } printExpr(that.expression); print(";"); assertMap.put(that.id, that); //println(); } } catch (IOException e) { perr(that,e); } } @Override public void visitExec(JCExpressionStatement tree) { try { printExpr(tree.expr); println(); } catch (IOException e) { throw new UncheckedIOException(e); } } @Override public void visitVarDef(JCVariableDecl tree) { // try { // print("var "); // print(tree.name.toString()); // print(" : "); // print(trType(tree.type)); // print(" ;"); // println(); // } catch (IOException e) { // throw new UncheckedIOException(e); // } } @Override public void visitAssign(JCAssign tree) { try { open(prec, TreeInfo.assignPrec); if (tree.lhs instanceof JCIdent) { printExpr(tree.lhs, TreeInfo.assignPrec + 1); print(" := "); printExpr(tree.rhs, TreeInfo.assignPrec); print(" ;"); } else if (tree.lhs instanceof JCFieldAccess) { JCFieldAccess fa = (JCFieldAccess)tree.lhs; if (style == SEP_STYLE) { // f := f[o := expr ] print(fa.name); print(" := "); print(fa.name); print("["); printExpr(fa.selected); print(" := "); printExpr(tree.rhs); print("];"); } else { throw new RuntimeException("Unimplemented in BoogiePrinter.visitAssign " + tree.getClass()); } } else if (tree.lhs instanceof JCArrayAccess) { JCArrayAccess fa = (JCArrayAccess)tree.lhs; if (style == SEP_STYLE) { // arrays_T := arrays_T[indexed := arrays_T[indexed][index := value] ] print("arrays_int"); print(" := "); print("arrays_int"); print("["); printExpr(fa.indexed); print(" := "); print("arrays_int"); print("["); printExpr(fa.indexed); print("]["); printExpr(fa.index); print(" := "); printExpr(tree.rhs); print("]];"); } else { throw new RuntimeException("Unimplemented in BoogiePrinter.visitAssign " + tree.getClass()); } } else { throw new RuntimeException("Unimplemented in BoogiePrinter.visitAssign " + tree.getClass()); } close(prec, TreeInfo.assignPrec); } catch (IOException e) { throw new UncheckedIOException(e); } } @Override public void visitSelect(JCFieldAccess tree) { try { if (style == SEP_STYLE) { print(tree.name); print("["); printExpr(tree.selected); print("]"); } else { throw new RuntimeException("Unimplemented in BoogiePrinter.visitSelect " + style); } } catch (IOException e) { throw new UncheckedIOException(e); } } @Override public void visitIndexed(JCArrayAccess tree) { try { if (style == SEP_STYLE) { print("arrays_int"); print("["); print(tree.indexed); print("]"); print("["); printExpr(tree.index); print("]"); } else { throw new RuntimeException("Unimplemented in BoogiePrinter.visitSelect " + style); } } catch (IOException e) { throw new UncheckedIOException(e); } } @Override public void visitApply(JCMethodInvocation tree) { try { JCExpression m = tree.meth; if (m instanceof JCIdent) { // if (((JCIdent)m).name.toString().equals(BasicBlocker2.STOREString)) { // result = F.fcn(F.symbol("store"), // convertExpr(tree.args.get(0)), // convertExpr(tree.args.get(1)), // convertExpr(tree.args.get(2)) // ); // return; // } } else if (m == null) { if (tree instanceof JmlBBFieldAssignment) { JCTree name = tree.args.get(1); JCTree obj = tree.args.get(2); JCTree value = tree.args.get(3); if (style == SEP_STYLE) { // f := f[o := expr ] print(name.toString()); print(" := "); print(name.toString()); print("["); printExpr(obj); print(" := "); printExpr(value); print("]"); return; } else { throw new RuntimeException("Unimplemented in BoogiePrinter.visitAssign " + tree.getClass()); } } else if (tree instanceof JmlBBArrayAssignment) { // [0] = store([1],[2], store(select([1],[2]),[3],[4])) // IExpr.IFcnExpr sel = F.fcn(F.symbol("select"), // convertExpr(tree.args.get(1)), // convertExpr(tree.args.get(2)) // ); // IExpr.IFcnExpr newarray = F.fcn(F.symbol("store"), // sel, // convertExpr(tree.args.get(3)), // convertExpr(tree.args.get(4)) // ); // // IExpr.IFcnExpr right = F.fcn(F.symbol("store"), // convertExpr(tree.args.get(1)), // convertExpr(tree.args.get(2)), // newarray // ); // result = F.fcn(F.symbol("="), convertExpr(tree.args.get(0)),right); // return; } } notImpl(tree); super.visitApply(tree); } catch (IOException e) { throw new UncheckedIOException(e); } } } }