package org.jmlspecs.openjml.esc;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.tools.JavaFileObject;
import org.jmlspecs.openjml.JmlPretty;
import org.jmlspecs.openjml.JmlSpecs;
import org.jmlspecs.openjml.Strings;
import org.jmlspecs.openjml.Utils;
import org.jmlspecs.openjml.JmlTree.JmlClassDecl;
import org.jmlspecs.openjml.JmlTree.JmlMethodDecl;
import org.jmlspecs.openjml.JmlTree.JmlMethodSpecs;
import org.jmlspecs.openjml.JmlTree.JmlStatementExpr;
import org.jmlspecs.openjml.proverinterface.IProverResult;
import org.jmlspecs.openjml.utils.ExternalProcess;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.comp.JmlEnter;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.util.Log.WriterKind;
import com.sun.tools.javac.util.Options;
import com.sun.tools.javac.util.Position;
public class MethodProverBoogie extends MethodProverSMT {
protected JmlSpecs specs;
public MethodProverBoogie(JmlEsc jmlesc) {
super(jmlesc);
this.specs = JmlSpecs.instance(context);
}
// FIXME: REVIEW THIS
/** Perform an ESC check of the method using boogie+Z3 */
public IProverResult prove(JmlMethodDecl decl) {
boolean print = true; //true;
PrintWriter noticeWriter = log.getWriter(WriterKind.NOTICE);
if (print && decl.name.toString().equals("<init>")) {
noticeWriter.println("SKIPPING PROOF OF " + decl.name);
return null;
}
utils.progress(1,2,"Starting proof of " + utils.qualifiedMethodSig(decl.sym)+ " with prover " + jmlesc.proverToUse);
if (print) {
noticeWriter.println(Strings.empty);
noticeWriter.println("--------------------------------------");
noticeWriter.println(Strings.empty);
noticeWriter.println("STARTING PROOF OF " + decl.sym.owner.getQualifiedName() + "." + decl.sym);
noticeWriter.println(JmlPretty.write(decl.body));
}
JmlMethodDecl tree = (JmlMethodDecl)decl;
JmlClassDecl currentClassDecl = (JmlClassDecl)JmlEnter.instance(context).getEnv((ClassSymbol)decl.sym.owner).tree;
// Get the denested specs for the method - FIXME - when might they be null?
if (tree.sym == null) {
log.error("jml.internal.notsobad", "Unexpected null symbol for " + decl.name);
}
JmlMethodSpecs denestedSpecs = tree.sym == null ? null : specs.getDenestedSpecs(tree.sym);
JmlAssertionAdder assertionAdder = new JmlAssertionAdder(context,true,false);
JCBlock newblock = assertionAdder.convertMethodBody(tree,currentClassDecl);
noticeWriter.println(JmlPretty.write(newblock));
BoogieProgram program = new Boogier(context).convertMethodBody(newblock, decl, denestedSpecs, currentClassDecl, assertionAdder);
String filename = "boogie_" + decl.getName() + ".bpl";
StringWriter sw = new StringWriter();
String programString;
try {
program.write(sw);
FileWriter fw = new FileWriter(filename);
programString = sw.toString();
fw.append(programString);
fw.close();
} catch (IOException e) {
noticeWriter.println("Could not write boogie output file"); // FIXME - error
return null;
}
noticeWriter.println(programString);
String boogie = Options.instance(context).get("openjml.prover.boogie");
ExternalProcess p = new ExternalProcess(context,null,
boogie,
"/nologo",
"/proverWarnings:1",
"/coalesceBlocks:0",
"/removeEmptyBlocks:0",
filename);
try {
p.start();
int exitVal = p.readToCompletion();
noticeWriter.println("Boogie exit val " + exitVal); // FIXME - guard or delete verbose output
String out = p.outputString.toString();
noticeWriter.println("OUTPUT: " + out);
noticeWriter.println("ERROR: " + p.errorString.toString());
if (out.contains("This assertion might not hold")) {
int k = out.indexOf('(');
int kk = out.indexOf(',');
int line = Integer.parseInt(out.substring(k+1,kk));
k = 0;
while (--line > 0) k = 1 + programString.indexOf('\n',k);
kk = 1 + programString.indexOf("\"",programString.indexOf(":reason",k));
int kkk = programString.indexOf('"',kk);
String reason = programString.substring(kk,kkk);
kk = 4 + programString.indexOf(":id",k);
kkk = programString.indexOf('}',kk);
String id = programString.substring(kk,kkk);
JmlStatementExpr assertStat = program.assertMap.get(id);
Label label = Label.find(reason);
// FIXME - defensive chjeck assertStat not null
kk = out.lastIndexOf(BasicBlockerParent.RETURN);
if (kk < 0) kk = out.lastIndexOf(BasicBlockerParent.THROW);
int terminationPos = 0;
if (kk >= 0) {
kkk = out.lastIndexOf(BasicBlockerParent.blockPrefix,kk) + BasicBlockerParent.blockPrefix.length();
try {
terminationPos = Integer.parseInt(out.substring(kkk,kk));
} catch (NumberFormatException e) {
noticeWriter.println("NO RETURN FOUND"); // FIXME
// continue
}
}
if (terminationPos == 0) terminationPos = decl.pos;
JavaFileObject prev = null;
int pos = assertStat.pos;
if (pos == Position.NOPOS || pos == decl.pos) pos = terminationPos;
if (assertStat.source != null) prev = log.useSource(assertStat.source);
String extra = Strings.empty;
JCExpression optional = assertStat.optionalExpression;
if (optional != null) {
if (optional instanceof JCTree.JCLiteral) extra = ": " + ((JCTree.JCLiteral)optional).getValue().toString();
}
log.warning(pos,"esc.assertion.invalid",label,decl.getName(),extra);
String loc = utils.locationString(pos);
// TODO - above we include the optionalExpression as part of the error message
// however, it is an expression, and not evaluated for ESC. Even if it is
// a literal string, it is printed with quotes around it.
if (prev != null) log.useSource(prev);
if (assertStat.associatedPos != Position.NOPOS) {
if (assertStat.associatedSource != null) prev = log.useSource(assertStat.associatedSource);
log.warning(assertStat.associatedPos,
Utils.testingMode ? "jml.associated.decl" :"jml.associated.decl.cf",loc);
if (assertStat.associatedSource != null) log.useSource(prev);
}
// if (label == Label.POSTCONDITION || label == Label.SIGNALS) {
// log.warning(terminationPos,"esc.assertion.invalid",label,decl.getName() + cf, Strings.empty);
// log.warning(assertStat.pos, "jml.associated.decl");
// } else if (label == Label.ASSIGNABLE) {
// log.warning(assertStat.pos,"esc.assertion.invalid",label,decl.getName() + cf, Strings.empty);
// log.warning(assertStat.associatedPos, "jml.associated.decl");
// } else if (label != Label.EXPLICIT_ASSERT && label != Label.EXPLICIT_ASSUME){
// log.warning(assertStat.pos,"esc.assertion.invalid",label,decl.getName() + cf, Strings.empty);
// if (assertStat.pos != assertStat.associatedPos && assertStat.associatedPos != Position.NOPOS){
// log.warning(assertStat.associatedPos, "jml.associated.decl");
// }
// } else {
// log.warning(assertStat.pos,"esc.assertion.invalid",label,decl.getName() + cf, Strings.empty);
// }
return null;
} else if (out.contains(" 0 errors")) {
return null;
} else {
log.error(0,"jml.internal","Unknown result returned from prover"); // FIXME - use a different message
}
} catch (Exception e) {
System.out.println("EXCEPTION: " + e); // FIXME
return null;
}
return null;
}
}