/* * This file is part of the OpenJML project. * Author: David R. Cok */ package org.jmlspecs.openjml.esc; import static com.sun.tools.javac.code.TypeTag.CLASS; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.tools.JavaFileObject; import org.jmlspecs.annotation.NonNull; import org.jmlspecs.annotation.Nullable; import org.jmlspecs.openjml.JmlInternalError; import org.jmlspecs.openjml.JmlPretty; import org.jmlspecs.openjml.JmlSpecs; import org.jmlspecs.openjml.JmlSpecs.TypeSpecs; import org.jmlspecs.openjml.JmlTokenKind; import org.jmlspecs.openjml.JmlTree; import org.jmlspecs.openjml.JmlTree.*; import org.jmlspecs.openjml.JmlTreeScanner; import org.jmlspecs.openjml.JmlTreeUtils; import org.jmlspecs.openjml.Nowarns; import org.jmlspecs.openjml.Utils; import org.jmlspecs.openjml.esc.BasicBlocker2.VarMap; import org.jmlspecs.openjml.esc.BasicProgramParent.BlockParent; import org.jmlspecs.openjml.esc.BoogieProgram; import org.jmlspecs.openjml.esc.BoogieProgram.BoogieBlock; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Scope; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Symbol.TypeVariableSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.ArrayType; import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.code.TypeTag; import com.sun.tools.javac.code.TypeTag; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.comp.Attr; import com.sun.tools.javac.comp.JmlAttr; import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Log.WriterKind; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Position; /** This class converts a Java AST into a Boogie2 program. It leaves to whatever * tool processes the Boogie program the tasks of DSA and passification. * All Java (and JML) statements are rewritten into assignment, assume and * assert statements, with basic blocks being created to represent the control * flow. In addition, note the following: * <UL> * <LI> No assertions to represent Java or JML semantics are added, except for those * needed to convert control flow into basic blocks * </UL> * <P> * The input tree must consist of * <UL> * <LI> A valid Java program (with any Java constructs) * <LI> JML assume and assert statements, with JML expressions * <LI> The JML expressions contain only * <UL> * <LI> Java operators * <LI> quantified expressions * <LI> set comprehension expressions * <LI> \\old and \\pre expressions * <LI> [ FIXME ??? JML type literals, subtype operations, method calls in specs?] * </UL * </UL> * * <P> * Basic block output form contains only this subset of AST nodes: * <UL> * <LI> JCLiteral - numeric (all of them? FIXME), null, boolean, class (String?, character?) * <LI> JCIdent * <LI> JCParens * <LI> JCUnary * <LI> JCBinary * <LI> JCConditional * <LI> JmlBBFieldAccess * <LI> JmlBBArrayAccess * <LI> JmlBBFieldAssign * <LI> JmlBBArrayAssign * <LI> JCMethodInvocation - only pure methods within specifications * <LI> JmlMethodInvocation - old, typeof * <LI> JmlQuantifiedExpr - only forall and exists * <LI> JCTypeCast - but the clazz element now has a JCLiteral (which is a type literal) * <LI> [JCInstanceOf - not present - use a typeof and a subtype operation] * </UL> * * @author David Cok */ public class Boogier extends BasicBlockerParent<BoogieProgram.BoogieBlock,BoogieProgram> { /////// To have a unique BoogieBlocker2 instance for each method translated // In the initialization of tools, call BoogieBlocker2.Factory.preRegister(context); // Obtain a new BoogieBlocker2 when desired with context.get(BoogieBlocker2.class); // /** Register a BoogieBlocker Factory, if nothing is registered yet */ // public static void preRegister(final Context context) { // //if (context.get(key) != null) return; // context.put(key, new Context.Factory<BoogieBlocker2>() { // @Override // public BoogieBlocker2 make(Context context) { // return new BoogieBlocker2(context); // } // }); // } // // final public static Context.Key<BoogieBlocker2> key = // new Context.Key<BoogieBlocker2>(); /////// To have one BoogieBlocker2 instance per context use this method without the pre-registration // Don't need pre-registration since we are not replacing any tool and not using a M // To obtain a reference to the instance of BoogieBlocker2 for the current context // BoogieBlocker2.instance(context); // /** Get the instance for this context. // * // * @param context the compilation context // * @return a (unique for the context) BoogieBlocker instance // */ // public static BoogieBlocker2 instance(@NonNull Context context) { // BoogieBlocker2 instance = context.get(key); // // This is lazily initialized so that a derived class can preRegister to // // replace this BoogieBlocker // if (instance == null) { // instance = new BoogieBlocker2(context); // } // return instance; // } // Options // This implements checking of assumption feasibility. After an // assumption that is to be checked, we add the assertion // assert assumeCheck$<uniqueint>$<label> // and the definition // assume assumeCheck$<uniqueint>$<label> == <assumecheckvar> != <uniqueint> // where <uniqueint> is a positive integer not used elsewhere for // this purpose. Here we use the source code location so that it // can be used as well to generate error messages. // Then we also add to the VC the assumption // assume <assumecheckvar> == 0 // That way all the inserted assertions above are true. However, we // can change any one of them to false by replacing the assumption // above with // assume <assumecheckvar> == <uniqueid> // using the specific <uniqueint> of the assumption we want to test // FIXME - review and document static public boolean insertAssumptionChecks = true; // FIXME - review and document static boolean useCountedAssumeCheck = true; // FIXME - review and document static JCExpression booleanAssumeCheck; // FIXME - review and document JCExpression assumeCheck = null; /** This static field controls whether (user) assume statements are turned into assumptions tracked * with the assume count variable; if so, then there is an easy mechanism to test whether * the assumptions are feasible. */ public static boolean useAssumeDefinitions = false; // THE FOLLOWING ARE ALL FIXED STRINGS //----------------------------------------------------------------- // Names for a bunch of synthetic variables /** Standard name for the variable that represents the heap (which excludes local variables) */ public static final @NonNull String HEAP_VAR = "_heap__"; /** Standard name for the variable that tracks allocations */ public static final @NonNull String ALLOC_VAR = "_alloc__"; /** Prefix for assumptions defined in the basic block */ public static final String ASSUMPTION_PREFIX = "assumption"; /** Name of the encoded this variable */ public static final String THIS = "THIS_"; /** The prefix of the variables used in checking assumptions */ public static final String ASSUME_CHECK_PREFIX = "ASSUMECHECK_"; /** A variable name used in checking assumptions */ public static final String ASSUME_CHECK_COUNT = "__assumeCheckCount"; /** Name of length field */ public static final String LENGTH = "length"; // THE FOLLOWING FIELDS ARE EXPECTED TO BE CONSTANT FOR THE LIFE OF THE OBJECT // They are either initialized in the constructor or initialized on first use /** Identifier of a synthesized object field holding the length of an array object, initialized in the constructor */ @NonNull protected JCIdent lengthIdent; /** Symbol of a synthesized object field holding the length of an array object, initialized in the constructor */ @NonNull protected VarSymbol lengthSym; /** A fixed id for 'this' of the method being translated (see currentThisId * for the 'this' of called methods). */ @NonNull protected JCIdent thisId; // FIXME - document the following; check when initialized // FIXME - exceptionVar and terminationVar are no longer needed I think protected JCIdent exceptionVar = null; protected JCIdent heapVar; protected JCIdent terminationVar; // 0=no termination requested; 1=return executed; 2 = exception happening // THE FOLLOWING FIELDS ARE USED IN THE COURSE OF DOING THE WORK OF CONVERTING // TO BASIC BLOCKS. They are fields of the class because they need to be // shared across the visitor methods. /** Place to put new background assertions, such as class predicates */ protected List<JCExpression> background; /** The variable name that is currently the 'this' variable */ protected JCIdent currentThisId; /** The jfoMap and jfoArray keep track of a mapping between JavaFileObjects and * unique Integers. When position information in an encoded identifier refers to * a file that is not the file containing the implementation of the method being * translated and verified, then we have to indicate which file contains the source * for the position reference. This indication is an @ followed by an integer included in the identifier, * where the integer is a unique positive integer associated with the file. Since * these mappings are static, the associations remain constant across different methods * and different compilation contexts. * <BR> * jfoMap is a mapping from JavaFileObject to Integer */ // FIXME - should reconsider whether these mappings should be static static Map<JavaFileObject,Integer> jfoMap = new HashMap<JavaFileObject,Integer>(); /** Maps integers to JavaFileObject, the reverse of the mapping in jfoMap */ static ArrayList<JavaFileObject> jfoArray = new ArrayList<JavaFileObject>(); static { jfoArray.add(0,null); } /** Returns the int associated with a file, creating it if necessary */ // FIXME - check what equals and hashmap are being used. public static int getIntForFile(JavaFileObject jfo) { Integer i = jfoMap.get(jfo); int k; if (i == null) { k = jfoArray.size(); jfoArray.add(k,jfo); jfoMap.put(jfo,k); } else { k = i; } return k; } /** Returns the file associated with an int */ public static JavaFileObject getFileForInt(int i) { return jfoArray.get(i); } /** The constructor, but use the instance() method to get a new instance, * in order to support extension. This constructor should only be * invoked by a derived class constructor. * @param context the compilation context */ protected Boogier(@NonNull Context context) { super(context); // This is the symbol to access the length of an array lengthSym = syms.lengthVar; lengthIdent = treeutils.makeIdent(0,lengthSym); } /** Creates an empty new BoogieProgram */ @Override public BoogieProgram newProgram(Context context) { return new BoogieProgram(context); } /** Creates an empty new BoogieBlock */ @Override public BoogieBlock newBlock(JCIdent id){ return new BoogieProgram.BoogieBlock(id); } // METHODS // @Override // public void scan(com.sun.tools.javac.util.List<? extends JCTree> trees) { // if (trees != null) // for (com.sun.tools.javac.util.List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) { // scan(l.head); // } // } public void scanList(com.sun.tools.javac.util.List<JCExpression> trees) { if (trees != null) for (com.sun.tools.javac.util.List<JCExpression> l = trees; l.nonEmpty(); l = l.tail) { scan(l.head); l.head = result; } } // FIXME - use treeutils? /** Creates a translated expression whose value is the given type; * the result is a JML type, e.g. a representation of an instantiated generic.*/ protected JCExpression makeTypeLiteral(Type type, int pos) { return treeutils.trType(pos,type); } Set<Symbol> isDefined = new HashSet<Symbol>(); // FIXME - review /** Creates name for a symbol unique to its declaration * <symbol name>__<declaration position> * If the symbol has a negative declaration position, that value is not included in the string * @param sym the symbol being given a logical name * @param incarnationPosition the incarnation position for which to give a new name * @return the new name */ protected Name encodedName(VarSymbol sym) { // if (isDefined.add(n)) { // //System.out.println("AddedC " + sym + " " + n); // JCIdent id = treeutils.makeIdent(0, sym); // id.name = n; // program.declarations.add(id); // } return names.fromString(sym.getQualifiedName() + (sym.pos < 0 ? "" : ("__" + sym.pos))); } // FIXME - Review protected Name encodedArrayName(VarSymbol sym, int incarnationPosition) { Name n; if (incarnationPosition == 0) { n = sym.getQualifiedName(); } else { n = names.fromString(sym.getQualifiedName() + (sym.pos < 0 ? "" : ("_" + sym.pos)) ); } // if (isDefined.add(n)) { // //System.out.println("AddedC " + sym + " " + n); // JCIdent id = treeutils.makeIdent(0, sym); // id.name = n; // program.declarations.add(id); // } return n; //return names.fromString(sym.getQualifiedName() + (sym.pos < 0 ? "_" : ("_" + sym.pos + "_")) + incarnationPosition); } // FIXME - review and document protected Name encodedName(TypeSymbol tp, int incarnationPosition) { return names.fromString(tp.name + "_" + incarnationPosition); } // FIXME - review and document protected Name encodedNameNoUnique(VarSymbol sym, int incarnationPosition) { return names.fromString(sym.getQualifiedName() + (sym.pos < 0 ? "_" : ("_" + sym.pos + "_")) + incarnationPosition); } // FIXME - review /** Creates an encoded name for a Type variable. There is no incarnation position * because type variables are not assigned after initialization. * @param sym * @param declarationPosition * @return the new name */ protected Name encodedTypeName(TypeSymbol sym, int declarationPosition) { return names.fromString(sym.flatName() + "_" + declarationPosition); } // FIXME - review /** Creates an encoded name from a symbol and an incarnation position of the form * <symbol name>$<declaration position>$<use position> * Does not include a unique id * If the symbol has a negative declaration position, that value is not included in the string * @param sym the symbol being given a logical name * @param incarnationPosition the incarnation position for which to give a new name * @return the new name */ protected Name encodedName(MethodSymbol sym, int declpos, int incarnationPosition) { return names.fromString(sym.getQualifiedName() + (declpos < 0 ? "_" : ("_" + declpos + "_")) + incarnationPosition); } // FIXME - review /** Creates a new Ident node, but in this case we are not using the name from * the current incarnation map - so we supply the name. This is just used for * DSA assignments. */ protected JCIdent newIdentUse(VarSymbol sym, Name name) { // Boogie does not care about AST position JCIdent n = M.Ident(name); n.sym = sym; n.type = sym.type; return n; } /** Returns the name to use for a symbol under the current Map. */ protected Name getCurrentName(VarSymbol sym) { return encodedName(sym); } /** Converts the top-level block of a method into the elements of a BasicProgram * * @param methodDecl the method to convert to to a BasicProgram * @param denestedSpecs the specs of the method * @param classDecl the declaration of the containing class * @return the completed BasicProgram */ protected @NonNull BoogieProgram convertMethodBody(JCBlock block, @NonNull JCMethodDecl methodDecl, JmlMethodSpecs denestedSpecs, @NonNull JCClassDecl classDecl, @NonNull JmlAssertionAdder assertionAdder) { initialize(methodDecl,classDecl,assertionAdder); // JmlClassInfo classInfo = getClassInfo(classDecl.sym); // if (classInfo == null) { // log.error("jml.internal","There is no class information for " + classDecl.sym); // return null; // } background = new LinkedList<JCExpression>(); terminationVar = treeutils.makeIdent(methodDecl.pos,terminationSym); exceptionVar = treeutils.makeIdent(methodDecl.pos,assertionAdder.exceptionSymbols.get(methodDecl)); // newAuxIdent(EXCEPTION,syms.exceptionType,0,true); heapVar = treeutils.makeIdent(methodDecl.pos,HEAP_VAR,syms.intType); // FIXME - would this be better as its own uninterpreted type? // assumeCheckCountVar = treeutils.makeIdent(0,ASSUME_CHECK_COUNT,syms.intType); // assumeCheckCount = 0; // Define the start block int pos = methodDecl.pos; BoogieBlock startBlock = newBlock(START_BLOCK_NAME,pos); // Define the body block // Put all the program statements in the Body Block BoogieBlock bodyBlock = newBlock(BODY_BLOCK_NAME,methodDecl.body.pos); // Then the program bodyBlock.statements.addAll(block.getStatements()); follows(startBlock,bodyBlock); // Handle the start block a little specially // It does not have any statements in it startBlock(startBlock); // Start it so the currentMap, currentBlock, remainingStatements are defined // Define the thisId if (this.methodDecl._this != null) { thisId = treeutils.makeIdent(pos,this.methodDecl._this.name.toString(),methodDecl.sym.owner.type); currentThisId = thisId; } // FIXME - have to do static vars of super types also // FIXME - and all the model fields // FIXME - and all the fields of referenced classes // We have to create and store incarnations of class fields so that there is a record of // them in the oldMap. Otherwise, if the variables are used within \old later on, a new // identifier will be created, with a new unique number. // for (JCTree tree: classInfo.decl.defs ) { // if (tree instanceof JCVariableDecl) { // JCVariableDecl d = (JCVariableDecl)tree; // newIdentIncarnation(d.sym,0); // } // } completeBlock(currentBlock); processBlock(bodyBlock); // Finished processing all the blocks // Make the BasicProgram // program.startId = startBlock.id; //program.blocks.addAll(blocksCompleted); if (assumeCheck != null) booleanAssumeCheck = assumeCheck; program.background = background; // program.assumeCheckVar = assumeCheckCountVar; // program.toLogicalForm = toLogicalForm; return program; } int assertCount = 0; // FIXME - REVIEW and document protected void addAssert(Label label, JCExpression that, int declpos, List<JCStatement> statements, int usepos, JavaFileObject source, JCTree statement) { if (Nowarns.instance(context).suppress(source,usepos,label.toString())) return; // if (useAssertDefinitions && label != Label.ASSUME_CHECK) { // //if (extraEnv) { usepos++; declpos++; } // String n; // if (source == log.currentSourceFile()) { // n = "assert_" + usepos + "_" + declpos + "_" + label + "_" + (unique++); // } else { // Integer i = getIntForFile(source); // n = "assert_" + usepos + "_" + declpos + "__" + i + "_" + label + "_" + (unique++); // } // // JCIdent id = newAuxIdent(n,syms.booleanType,that.getStartPosition(),false); // copyEndPosition(id,that); // FIXME - merge into the call above // // //JCExpression expr = treeutils.makeBinary(that.pos,JCTree.EQ,id,that); // // FIXME - start and end? // BasicProgram.Definition stat = new BasicProgram.Definition(statement.pos,id,that); // FIXME - if we keep this, should use a factory // newdefs.add(stat); // that = id; // } JmlTree.JmlStatementExpr st = M.at(statement.pos).JmlExpressionStatement(JmlTokenKind.ASSERT,label,that); st.optionalExpression = null; st.source = source; st.associatedPos = declpos; st.id = "ASSERT__" + (++assertCount); st.type = null; // no type for a statement copyEndPosition(st,statement); statements.add(st); } // FIXME - REVIEW and document public void copyEndPosition(JCTree newnode, JCTree srcnode) { } // FIXME - REVIEW and document /** Adds an assertion with an untranslated expression to the given statement list; * it is presumed the statement will be translated later */ protected void addUntranslatedAssert(Label label, JCExpression that, int declpos, List<JCStatement> statements, int usepos, /*@Nullable*/JavaFileObject source) { JmlStatementExpr st; st = M.at(usepos).JmlExpressionStatement(JmlTokenKind.ASSERT,label,that); st.optionalExpression = null; st.source = source; st.associatedPos = declpos; st.id = "ASSERT__" + (++assertCount); st.type = null; // no type for a statement statements.add(st); } // FIXME - REVIEW and document /** Adds an assertion to the given statement list; the expression is presumed translated */ protected void addAssertNoTrack(Label label, JCExpression that, List<JCStatement> statements, int usepos, /*@Nullable*/JavaFileObject source) { JmlStatementExpr st; st = M.at(usepos).JmlExpressionStatement(JmlTokenKind.ASSERT,label,that); st.optionalExpression = null; st.type = null; // no type for a statement st.source = source; st.id = "ASSERT__" + (++assertCount); st.associatedPos = usepos;// FIXME - what should this be set to? statements.add(st); } // FIXME - REVIEW and document /** Adds a new assume statement to the end of the currentBlock; the assume statement is * given the declaration pos and label from the arguments; it is presumed the input expression is * translated, as is the produced assume statement. * @param pos the declaration position of the assumption * @param label the kind of assumption * @param that the (translated) expression being assumed */ protected void addAssume(int pos, Label label, JCExpression that) { addAssume(pos,label,that,currentBlock.statements); } // FIXME - REVIEW and document /** Adds a new assume statement to the end of the given statements list; the assume statement is * given the declaration pos and label from the arguments; it is presumed the input expression is * translated, as is the produced assume statement. * @param pos the declaration position of the assumption * @param label the kind of assumption * @param that the (translated) expression being assumed * @param statements the list to add the new assume statement to */ protected JmlStatementExpr addAssume(int pos, Label label, JCExpression that, List<JCStatement> statements) { M.at(pos); JmlStatementExpr st; // if (useAssumeDefinitions) { // JCIdent id = M.Ident(names.fromString(ASSUMPTION_PREFIX+(unique++))); // id.type = syms.booleanType; // newdefs.add(new BasicProgram.Definition(that.pos,id,that)); // FIXME- end position? // st = M.JmlExpressionStatement(JmlToken.ASSUME,label,id); // } else { st = M.JmlExpressionStatement(JmlTokenKind.ASSUME,label,that); // } // copyEndPosition(st,that); st.type = null; // statements do not have a type st.id = "ASSUME__" + (++assertCount); statements.add(st); return st; } // FIXME - REVIEW and document protected JmlStatementExpr addAssume(int startpos, JCTree endpos, Label label, JCExpression that, List<JCStatement> statements) { if (startpos < 0) startpos = that.pos; // FIXME - temp M.at(startpos); JmlStatementExpr st; // if (useAssumeDefinitions) { // JCIdent id = M.Ident(names.fromString(ASSUMPTION_PREFIX+(unique++))); // id.type = syms.booleanType; // newdefs.add(new BasicProgram.Definition(that.pos,id,that)); // FIXME- start, end position? // st = M.JmlExpressionStatement(JmlToken.ASSUME,label,id); // } else { st = M.JmlExpressionStatement(JmlTokenKind.ASSUME,label,that); // } // copyEndPosition(st,endpos); st.type = null; // statements do not have a type st.id = "ASSUME__" + (++assertCount); statements.add(st); return st; } // // FIXME - REVIEW and document // protected JmlStatementExpr addAssumeNoDef(int startpos, JCTree endpos, Label label, JCExpression that, List<JCStatement> statements) { // if (startpos < 0) startpos = that.pos; // FIXME - temp // M.at(startpos); // JmlStatementExpr st; // st = M.JmlExpressionStatement(JmlToken.ASSUME,label,that); //// copyEndPosition(st,endpos); // st.type = null; // statements do not have a type // statements.add(st); // return st; // } // // FIXME - REVIEW and document // /** Adds a new UNTRANSLATED assume statement to the end of the given statements list; the statements list // * should be a list of statements that will be processed (and translated) at some later time; // * the assume statement is // * given the declaration pos and label from the arguments; it is presumed the input expression is // * untranslated, as is the produced assume statement. // * @param pos the declaration position of the assumption // * @param label the kind of assumption // * @param that the (untranslated) expression being assumed // * @param statements the list to add the new assume statement to // */ // protected JmlStatementExpr addUntranslatedAssume(int pos, Label label, JCExpression that, List<JCStatement> statements) { // JmlStatementExpr st = M.at(pos).JmlExpressionStatement(JmlToken.ASSUME,label,that); // st.type = null; // statements do not have a type //// copyEndPosition(st,that); // statements.add(st); // return st; // } // // FIXME - REVIEW and document // protected JmlStatementExpr addUntranslatedAssume(int pos, JCTree posend, Label label, JCExpression that, List<JCStatement> statements) { // JmlStatementExpr st = M.at(pos).JmlExpressionStatement(JmlToken.ASSUME,label,that); // st.type = null; // statements do not have a type //// copyEndPosition(st,posend); // statements.add(st); // return st; // } // FIXME - REVIEW and document static public String encodeType(Type t) { // FIXME String? char? void? unsigned? TypeTag tag = t.getTag(); if (t instanceof ArrayType) { return "refA$" + encodeType(((ArrayType)t).getComponentType()); } else if (!t.isPrimitive()) { return "REF"; } else if (tag == TypeTag.INT || tag == TypeTag.SHORT || tag == TypeTag.LONG || tag == TypeTag.BYTE) { return "int"; } else if (tag == TypeTag.BOOLEAN) { return "bool"; } else if (tag == TypeTag.FLOAT || tag == TypeTag.DOUBLE) { return "real"; } else if (tag == TypeTag.CHAR) { return "int"; } else { return "unknown"; } } // FIXME - review and document private Map<String,JCIdent> arrayIdMap = new HashMap<String,JCIdent>(); // FIXME - review and document protected JCIdent getArrayIdent(Type componentType) { String s = "arrays_" + encodeType(componentType); JCIdent id = arrayIdMap.get(s); if (id == null) { id = M.Ident(names.fromString(s)); id.pos = 0; id.type = new ArrayType(componentType,syms.arrayClass); VarSymbol sym = new VarSymbol(0,id.name,id.type,null); sym.pos = 0; id.sym = sym; arrayIdMap.put(s,id); } id = treeutils.makeIdent(0,id.sym); return id; } /** This generates a comment statement (not added to any statement list) whose content is the * given String. */ public JmlStatementExpr comment(int pos, String s) { return M.at(pos).JmlExpressionStatement(JmlTokenKind.COMMENT,null,M.Literal(s)); } /** This generates a comment statement (not in any statement list) whose content is the * given JCTree, pretty-printed. */ public JmlStatementExpr comment(JCTree t) { return comment(t.pos,JmlPretty.write(t,false)); } // FIXME - do we need this - here? /** Makes a JML \typeof expression, with the given expression as the argument */ protected JCExpression makeTypeof(JCExpression e) { JCExpression typeof = M.at(e.pos).JmlMethodInvocation(JmlTokenKind.BSTYPEOF,e); typeof.type = syms.classType; return typeof; } // FIXME - review and document /** Makes a Java this parse tree node (attributed) */ protected JCIdent makeThis(int pos) { return treeutils.makeIdent(pos,methodDecl._this); } // FIXME - review and document /** Makes the equivalent of an instanceof operation: \typeof(e) <: \type(type) */ protected JCExpression makeNNInstanceof(JCExpression e, int epos, Type type, int typepos) { JCExpression e1 = makeTypeof(e); JCExpression e2 = makeTypeLiteral(type,typepos); //if (inSpecExpression) e2 = trSpecExpr(e2,null); JCExpression ee = treeutils.makeJmlBinary(epos,JmlTokenKind.SUBTYPE_OF,e1,e2); return ee; } // FIXME - review and document /** Makes the equivalent of an instanceof operation: e !=null && \typeof(e) <: \type(type) */ protected JCExpression makeInstanceof(JCExpression e, int epos, Type type, int typepos) { JCExpression e1 = treeutils.makeNeqObject(epos,e,treeutils.nullLit); JCExpression e2 = treeutils.makeJmlBinary(epos,JmlTokenKind.SUBTYPE_OF,makeTypeof(e),makeTypeLiteral(type,typepos)); //if (inSpecExpression) e2 = trSpecExpr(e2,null); JCExpression ee = treeutils.makeBinary(epos,JCTree.Tag.AND,e1,e2); return ee; } // FIXME - review and document protected MethodSymbol makeFunction(Name name, Type resultType, Type... argTypes) { ListBuffer<Type> args = new ListBuffer<Type>().appendArray(argTypes); MethodType methodType = new MethodType(args.toList(),resultType,com.sun.tools.javac.util.List.<Type>nil(),syms.methodClass); MethodSymbol meth = new MethodSymbol(Flags.STATIC,name,methodType,null); // no owner return meth; } // FIXME - review and document protected JCExpression makeFunctionApply(int pos, MethodSymbol meth, JCExpression... args) { JCIdent methid = M.at(pos).Ident(meth); JCExpression e = M.at(pos).Apply(null,methid,new ListBuffer<JCExpression>().appendArray(args).toList()); e.type = meth.getReturnType(); return e; } // FIXME - review and document protected JCExpression makeSignalsOnly(JmlMethodClauseSignalsOnly clause) { JCExpression e = treeutils.makeBooleanLiteral(clause.pos,false); JCExpression id = M.at(0).JmlSingleton(JmlTokenKind.BSEXCEPTION); for (JCExpression typetree: clause.list) { int pos = typetree.getStartPosition(); e = treeutils.makeBinary(pos, JCTree.Tag.OR, makeNNInstanceof(id, pos, typetree.type, pos), e); } return e; } // FIXME - review and document protected int endPos(JCTree t) { if (t instanceof JCBlock) { return ((JCBlock)t).endpos; } else if (t instanceof JCMethodDecl) { return endPos(((JCMethodDecl)t).body); } else { // FIXME - fix this sometime - we don't know the end position of // statements that are not blocks if (JmlEsc.escdebug) log.getWriter(WriterKind.NOTICE).println("UNKNOWN END POS"); return t.pos; } } // STATEMENT NODES // FIXME - REVIEW public void visitExec(JCExpressionStatement that) { // This includes assignments and stand-alone method invocations scan(that.expr); } // FIXME - needs review - al;ready converted to a BoogieBlock assert? public void visitAssert(JCAssert that) { // This is a Java assert statement currentBlock.statements.add(comment(that)); scan(that.cond); scan(that.detail); JCExpression cond = (that.cond); JCExpression detail = (that.detail); // FIXME - what to do with detail // FIXME - for now turn cond into a JML assertion // FIXME - need a label for the assert statement // FIXME - set line and source addAssert(Label.EXPLICIT_ASSERT, cond, that.cond.getStartPosition(), currentBlock.statements, that.cond.getStartPosition(),log.currentSourceFile(),that); } // FIXME - needs review public void visitApply(JCMethodInvocation that) { // This is an expression so we just use trExpr JCExpression now; JCExpression obj; MethodSymbol msym; Type.ForAll tfa = null; if (that.meth instanceof JCIdent) { now = (that.meth); if ( ((JCIdent)now).sym instanceof MethodSymbol) { msym = (MethodSymbol)((JCIdent)now).sym; if (msym.isStatic()) obj = null; else obj = currentThisId; } else { msym=null; obj = null; } // FIXME - this shouldn't really happen - there is a mis translation in creating makeTYPE expressions } else if (that.meth instanceof JCFieldAccess) { JCFieldAccess fa = (JCFieldAccess)that.meth; msym = (MethodSymbol)(fa.sym); if (msym == null || msym.isStatic()) obj = null; // msym is null for injected methods such as box and unbox else { obj = ( fa.selected ); // FIXME - should do better than converting to String //if (!fa.selected.type.toString().endsWith("JMLTYPE")) checkForNull(obj,fa.pos,trueLiteral,null); } } else { // FIXME - not implemented log.warning("esc.not.implemented","BoogieBlocker.visitApply for " + that.meth.getClass()); msym = null; obj = null; result = treeutils.trueLit; return; } if (msym != null && msym.type instanceof Type.ForAll) tfa = (Type.ForAll)msym.type; // FIXME - what does this translation mean? // ListBuffer<JCExpression> newtypeargs = new ListBuffer<JCExpression>(); // for (JCExpression arg: that.typeargs) { // JCExpression n = trExpr(arg); // newtypeargs.append(n); // } ListBuffer<JCExpression> newargs = new ListBuffer<JCExpression>(); for (JCExpression arg: that.args) { JCExpression n = (arg); newargs.append(n); } pushTypeArgs(); if (tfa != null) { // tfa is the declaration of a parameterized method // that is the actual call, which may not have explicit parameters Iterator<Type> tv = tfa.tvars.iterator(); Iterator<JCExpression> e = that.typeargs.iterator(); if (e.hasNext()) { while (tv.hasNext()) { typeargs.put(tv.next().tsym,e.next().type); } } else { log.getWriter(WriterKind.NOTICE).println("NOT IMPLEMENTED - parameterized method call with implicit type parameters"); } } // FIXME - concerned that the position here is not after the // positions of all of the arguments // if (inSpecExpression) { // result = insertSpecMethodCall(that.pos,msym,obj,that.typeargs,newargs.toList()); // } else { // result = insertMethodCall(that,msym,obj,that.getTypeArguments(),newargs.toList()); // typeargs ? FIXME // } popTypeArgs(); toLogicalForm.put(that,result); return; } // FIXME - review this //boolean extraEnv = false; public void visitJmlMethodInvocation(JmlMethodInvocation that) { if (that.token == JmlTokenKind.BSOLD || that.token == JmlTokenKind.BSPRE || that.token == JmlTokenKind.BSPAST) { if (that.args.size() == 1) { that.args.get(0).accept(this); } else { JCIdent label = (JCIdent)that.args.get(1); that.args.get(0).accept(this); that.args = com.sun.tools.javac.util.List.<JCExpression>of(that.args.get(0)); } that.token = JmlTokenKind.BSSAME; // A no-op // TODO - Review this } else if (that.token == null) { super.visitApply(that); // See testBox - this comes from the implicitConversion - should it be a JCMethodInvocation instead? scan(that.typeargs); scan(that.meth); that.meth = result; scanList(that.args); result = that; } else { log.error(that.pos, "esc.internal.error", "Did not expect this kind of Jml node in BoogieBlocker: " + that.token.internedName()); // for (JCExpression e: that.args) { // e.accept(this); // } } } // FIXME - REVIEW and document protected List<Type> allTypeArgs(Type type) { ListBuffer<Type> list = new ListBuffer<Type>(); allTypeArgs(list,type); return list.toList(); } // FIXME - REVIEW and document protected void allTypeArgs(ListBuffer<Type> list, Type type) { if (type == Type.noType) return; allTypeArgs(list,type.getEnclosingType()); list.appendList(type.getTypeArguments()); } // // FIXME - REVIEW and document // // Generate a (translated) allocation predicate // FIXME - check this out and use it // protected void declareAllocated(VarSymbol vsym, int pos) { // JCIdent var = treeutils.makeIdent(pos,vsym); // declareAllocated(var,pos); // } // // // Generate a (translated) allocation predicate // FIXME - check this out and use it // protected void declareAllocated(JCExpression e, int pos) { // currentBlock.statements.add(comment(pos, e + " != null || " + e + " .alloc < " + allocSym)); // JCExpression eee = new JmlBBFieldAccess(allocIdent,e); // eee.pos = pos; // eee.type = syms.intType; // eee = treeutils.makeBinary(pos,JCTree.LE,eee,treeutils.makeIdent(pos,allocSym)); // eee = treeutils.makeBinary(pos,JCTree.OR,treeutils.makeEqObject(pos,e,nullLiteral),eee); // addAssume(pos,Label.SYN,eee,currentBlock.statements); // } // // // Generate a (translated) alloc comparison // FIXME - check this out and use it and docuiment // protected JCExpression allocCompare(int pos, JCExpression e) { // JCExpression eee = new JmlBBFieldAccess(allocIdent,e); // eee.pos = pos; // eee.type = syms.intType; // eee = treeutils.makeBinary(pos,JCTree.LE,eee,treeutils.makeIdent(pos,allocSym)); // return eee; // } // // Generate a (translated) alloc selection // FIXME - check this out and use it and document // protected JCExpression allocSelect(int pos, JCExpression e) { // JCExpression eee = new JmlBBFieldAccess(allocIdent,e); // eee.pos = pos; // eee.type = syms.intType; // return eee; // } // FIXME - review and document protected void havocAssignables(int pos, JmlMethodInfo mi) { //// * a store-ref //// * is a JCIdent, a JCSelect (potentially with a null field), or a JmlStoreRefArrayRange; //// * there may be more than one use of a JmlStoreRefArrayRange, e.g. a[2..3][4..5] or //// * a.f[4..5].g[6..7] // for (JmlMethodInfo.Entry entry: mi.assignables) { // JCExpression preCondition = trSpecExpr(entry.pre,log.currentSourceFile()); // FIXME - fix this // for (JCTree sr: entry.storerefs) { // if (sr == null) { // log.error(pos,"jml.internal","Unexpected null store-ref in BoogieBlocker.havocAssignables"); // continue; // } // int npos = pos*100000 + sr.pos; // JCExpression prevCondition = condition; // if (sr instanceof JCIdent) { // JCIdent id = (JCIdent)sr; // if (utils.isJMLStatic(id.sym)) { // JCExpression oldid = trSpecExpr(id,log.currentSourceFile()); // FIXME // JCIdent newid = newIdentIncarnation(id,npos); // new incarnation // // newid == precondition ? newid : oldid // JCExpression e = M.at(pos).Conditional(preCondition,newid,oldid); // e.type = newid.type; // e = treeutils.makeBinary(pos,JCTree.EQ,newid,e); // addAssume(pos,Label.HAVOC,e,currentBlock.statements); // } else { // // Same as for JCFieldAccess except that fa.selected is always 'this' (currentThisId) // Type type = id.type; // checkForNull(currentThisId,id.pos,preCondition,null); // // JCIdent oldid = newIdentUse((VarSymbol)id.sym,id.pos); // JCFieldAccess oldaccess = new JmlBBFieldAccess(oldid,currentThisId); // oldaccess.pos = id.pos; // oldaccess.type = type; // // JCIdent newid = newIdentIncarnation(oldid,npos); // JCFieldAccess newaccess = new JmlBBFieldAccess(newid,currentThisId); // newaccess.pos = id.pos; // newaccess.type = type; // // JCExpression right = M.at(id.pos).Conditional(preCondition,newaccess,oldaccess); // right.type = type; // // JCExpression expr = new JmlBBFieldAssignment(newid,oldid,currentThisId,right); // expr.pos = pos; // expr.type = type; // // addAssume(pos,Label.HAVOC,expr,currentBlock.statements); // } // } else if (sr instanceof JCFieldAccess) { // // FIXME - this duplicates logic in visitSelect and doAssignment // // s.f' = precondition ? s.f' : s.f // JCFieldAccess fa = (JCFieldAccess)sr; // JCExpression selected = fa.selected; // boolean isType = true; // if ((selected instanceof JCIdent) && ((JCIdent)selected).sym instanceof ClassSymbol) { // // do nothing // } else if ((selected instanceof JCFieldAccess) && ((JCFieldAccess)selected).sym instanceof ClassSymbol) { // // do nothing // } else { // //selected = trSpecExpr(fa.selected,log.currentSourceFile()); // FIXME // selected = (fa.selected); // FIXME // isType = false; // } // // try { // //if (!isType) checkForNull(selected,sr.pos,preCondition,null); // // if (fa.sym == null) { // Symbol ownerSym = fa.selected.type.tsym; // if (ownerSym instanceof ClassSymbol) { // ClassSymbol csym = (ClassSymbol)ownerSym; // Scope.Entry symentry = csym.members().elems; // while (symentry != null) { // Symbol sym = symentry.sym; // symentry = symentry.sibling; // if (sym instanceof VarSymbol) { // if (utils.isJMLStatic(sym)) { // JCIdent newid = newIdentIncarnation((VarSymbol)sym,npos); // JCExpression e = treeutils.makeEquality(npos,newid,newid); // addAssume(sr.pos,Label.HAVOC,e,currentBlock.statements); // // } else if (!isType) { // havocField((VarSymbol)sym,selected,fa.pos,npos,sym.type,preCondition); // } // } // } // } else { // log.noticeWriter.println("FOUND " + ownerSym.getClass()); // } // // } else { // VarSymbol vsym = (VarSymbol)fa.sym; // havocField(vsym,selected,fa.pos,npos,fa.type,preCondition); // } // } finally { // condition = prevCondition; // } // // } else if (sr instanceof JmlStoreRefArrayRange) { // JmlStoreRefArrayRange ar = (JmlStoreRefArrayRange)sr; // // ListBuffer<Name> ns = new ListBuffer<Name>(); // JCExpression array = extractQuantifiers(ar.expression,ns); // // condition = treeutils.makeBinary(sr.pos,JCTree.AND,condition,preCondition); // try { // if (ar.hi != ar.lo || ar.lo == null) { // // wildcard at the top level // if (ns.size() > 0) { // // and wildcards within // } else { // // no wildcards within // // JCIdent arrayId = getArrayIdent(sr.type); // // array = trSpecExpr(array,log.currentSourceFile()); // FIXME // checkForNull(array,sr.pos,trueLiteral,null); // // JCExpression indexlo = trSpecExpr(ar.lo,log.currentSourceFile()); // FIXME // if (indexlo != null) checkArrayAccess(array,indexlo,sr.pos); // else indexlo = zeroLiteral; // // JCExpression indexhi = trSpecExpr(ar.hi,log.currentSourceFile()); // FIXME // boolean above = false; // if (indexhi != null) checkArrayAccess(array,indexhi,sr.pos); // else { // //indexhi = M.at(sr.pos).Select(array,lengthSym); // indexhi = new JmlBBFieldAccess(lengthIdent,array); // indexhi.pos = sr.pos; // indexhi.type = syms.intType; // above = true; // } // // // JCIdent nid = newArrayIncarnation(sr.type,pos); // JCExpression e = new JmlBBArrayHavoc(nid,arrayId,array,indexlo,indexhi,preCondition,above); // // addAssume(pos,Label.HAVOC,e,currentBlock.statements); // // } // } else { // // single element at the top level // // if (ns.size() > 0) { // // FIXME - this is all wrong // // But wild-cards within the ar.expression // //// JCIdent label = newAuxIdent("havoclabel$"+npos,syms.intType,npos,false); //// labelmaps.put(label.name,currentMap.copy()); //// JCExpression oldaccess = M.at(npos).JmlMethodInvocation(JmlToken.BSOLD,access,label); //// //// JCArrayAccess newaccess = M.at(access.pos).Indexed(access.indexed,access.index); //// newaccess.type = access.type; //// //// // JCIdent meth = newAuxIdent("arbitrary$",syms.intType,npos); //// // ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); //// // for (Name n: ns) { //// // JCIdent id = M.at(npos).Ident(n); //// // id.type = syms.intType; //// // args.append(id); //// // } //// // JCMethodInvocation app = M.at(npos).Apply(null,meth,args.toList()); //// // app.type = ar.type; //// //// JCConditional cond = M.at(sr.pos).Conditional( //// treeutils.makeBinary(JCTree.AND,entry.pre,accumRange,npos),newaccess,oldaccess); //// cond.type = access.type; //// //// JCExpression assign = treeutils.makeBinary(JCTree.EQ,newaccess,cond,npos); //// //// JmlQuantifiedExpr quant = M.at(sr.pos).JmlQuantifiedExpr(JmlToken.BSFORALL,null,M.Type(syms.intType),ns,fullRange,assign); //// //// JCIdent nid = newArrayIncarnation(sr.type,npos); //// JmlQuantifiedExpr trQuant = (JmlQuantifiedExpr)trSpecExpr(quant,log.currentSourceFile()); // FIXME //// // Now we fix up the expression //// JCExpression predicate = trQuant.predicate; //// JCBinary bin = (JCBinary)predicate; //// cond = (JCConditional)bin.rhs; //// JmlBBArrayAccess newaa = (JmlBBArrayAccess)cond.truepart; //// JmlBBArrayAccess oldaa = (JmlBBArrayAccess)cond.falsepart; //// //// JCExpression expr = new JmlBBArrayAssignment(nid,oldaa.arraysId,oldaa.indexed,oldaa.index,cond); //// expr.pos = sr.pos; //// expr.type = cond.type; //// //// trQuant.predicate = expr; //// //// addAssume(pos,Label.HAVOC,trQuant,currentBlock.statements,false); // // } else { // // single element // // a'[i] = preCondition ? a'[i] : a[i]; // // array = trSpecExpr(array,log.currentSourceFile()); // FIXME // checkForNull(array,sr.pos,trueLiteral,null); // // JCExpression index = trSpecExpr(ar.lo,log.currentSourceFile()); // FIXME // checkArrayAccess(array,index,sr.pos); // // JCIdent arrayID = getArrayIdent(sr.type); // JCExpression oldvalue = new JmlBBArrayAccess(arrayID,array,index,sr.pos,sr.type); // // JCIdent nid = newArrayIncarnation(sr.type,pos); // JCExpression newvalue = new JmlBBArrayAccess(nid,array,index,sr.pos,sr.type); // // JCExpression condValue = M.at(sr.pos).Conditional(preCondition,newvalue,oldvalue); // condValue.type = oldvalue.type; // // JCExpression expr = new JmlBBArrayAssignment(nid,arrayID,array,index,condValue); // expr.pos = sr.pos; // expr.type = oldvalue.type; // addAssume(pos,Label.HAVOC,expr,currentBlock.statements); // } // } // } finally { // condition = prevCondition; // } // // } else if (sr instanceof JmlStoreRefKeyword) { // if (((JmlStoreRefKeyword)sr).token == JmlToken.BSNOTHING) { // // OK // } else { // havocEverything(preCondition,sr.pos); // } // } else if (sr instanceof JmlSingleton) { // FIXME - why do we get JmlSingleton as a store-ref? // if (((JmlSingleton)sr).token == JmlToken.BSNOTHING) { // // OK // } else { // havocEverything(preCondition,sr.pos); // } // } else { // log.error(sr.pos,"jml.internal","Unexpected kind of store-ref in BoogieBlocker.havocAssignables: " + sr.getClass()); // } // } // } } // FIXME - review and document private JCExpression fullRange; private JCExpression accumRange; protected JCExpression extractQuantifiers(JCExpression expr, ListBuffer<Name> ns) { if (expr instanceof JCIdent) { accumRange = treeutils.trueLit; fullRange = treeutils.trueLit; return expr; } else if (expr instanceof JmlStoreRefArrayRange) { JmlStoreRefArrayRange a = (JmlStoreRefArrayRange)expr; JCExpression e = extractQuantifiers(a.expression,ns); JCExpression id; if (a.lo == a.hi && a.lo != null) { id = a.lo; } else { Name n = names.fromString("i"+(ns.size()+1)); id = M.at(expr.pos).Ident(n); // No symbol - FIXME ??? id.type = syms.intType; ns.append(n); fullRange = treeutils.makeBinary(a.pos,JCTree.Tag.AND,fullRange,treeutils.makeBinary(a.pos,JCTree.Tag.LE,treeutils.makeZeroEquivalentLit(a.pos,syms.intType),id)); //JCExpression len = M.at(a.pos).Select(a.expression,lengthSym); JCExpression len = new JmlBBFieldAccess(lengthIdent,a.expression); len.pos = a.pos; len.type = syms.intType; fullRange = treeutils.makeBinary(a.pos,JCTree.Tag.AND,fullRange,treeutils.makeBinary(a.pos,JCTree.Tag.LT,id,len)); if (a.lo != null) accumRange = treeutils.makeBinary(a.lo.pos,JCTree.Tag.AND,accumRange,treeutils.makeBinary(a.lo.pos,JCTree.Tag.LE,a.lo,id)); if (a.hi != null) accumRange = treeutils.makeBinary(a.hi.pos,JCTree.Tag.AND,accumRange,treeutils.makeBinary(a.hi.pos,JCTree.Tag.LE,id,a.hi)); } e = M.at(expr.pos).Indexed(e,id); e.type = expr.type; return e; } else if (expr instanceof JCFieldAccess) { JCFieldAccess a = (JCFieldAccess)expr; JCExpression e = extractQuantifiers(a.selected,ns); if (e == a.selected) return e; e = M.at(expr.pos).Select(e,a.sym); e.type = a.type; return e; } else { return expr; } } // FIXME - review and document protected void havocField(VarSymbol vsym, JCExpression selected, int pos, int npos, Type type, JCExpression preCondition) { // JCIdent oldid = treeutils.makeIdent(pos,vsym); // JCFieldAccess oldaccess = new JmlBBFieldAccess(oldid,selected); // oldaccess.pos = pos; // oldaccess.type = type; // // JCIdent newid = newIdentIncarnation(oldid,npos); // JCFieldAccess newaccess = new JmlBBFieldAccess(newid,selected); // newaccess.pos = pos; // newaccess.type = type; // // JCExpression right = M.at(pos).Conditional(preCondition,newaccess,oldaccess); // right.type = type; // // JCExpression expr = new JmlBBFieldAssignment(newid,oldid,selected,right); // expr.pos = pos; // expr.type = type; // // addAssume(pos,Label.HAVOC,expr,currentBlock.statements); } // FIXME - review and document protected void havoc(JCExpression storeref) { // if (storeref instanceof JCIdent) { // JCIdent id = newIdentIncarnation((JCIdent)storeref,storeref.pos); // program.declarations.add(id); // //} else if (e instanceof JCFieldAccess) { // //} else if (e instanceof JCArrayAccess) { // // } else { // // FIXME - havoc in loops // log.noticeWriter.println("UNIMPLEMENTED HAVOC " + storeref.getClass()); // } } // FIXME - review and document protected void havocEverything(JCExpression preCondition, int newpos) { // FIXME - if the precondition is true, then we do not need to add the // assumptions - we just need to call newIdentIncarnation to make a new // value in the map. This would shorten the VC. How often is this // really the case? Actually the preCondition does not need to be true, // it just needs to encompass all allowed cases. // FIXME - check on special variables - should they/are they havoced? // this // terminationVar // exceptionVar // resultVar // exception // others? // Change everything in the current map // for (VarSymbol vsym : currentMap.keySet()) { // if (vsym.owner == null || vsym.owner.type.tag != TypeTag.CLASS) { // continue; // } // JCIdent oldid = newIdentUse(vsym,newpos); // JCIdent newid = newIdentIncarnation(vsym,newpos); // JCExpression e = M.at(newpos).Conditional(preCondition,newid,oldid); // e.type = vsym.type; // e = treeutils.makeEquality(newpos,newid,e); // addAssume(newpos,Label.HAVOC,e,currentBlock.statements); // } // currentMap.everythingIncarnation = newpos; // FIXME - this now applies to every not-yet-referenced variable, independent of the preCondition } /** This method is not called for top-level classes, since the BoogieBlocker is invoked * directly for each method. */ // FIXME - what about for anonymous classes or local classes or nested classes @Override public void visitClassDef(JCClassDecl that) { // Nested classes are found in JmlEsc. We get to this point if there is a local // class declaration within method body. JmlEsc.instance(context).visitClassDef(that); } // FIXME - review this, and compare to the above @Override public void visitJmlClassDecl(JmlClassDecl that) { // Nested classes are found in JmlEsc. We get to this point if there is a local // class declaration within method body. JmlEsc.instance(context).visitClassDef(that); } // OK @Override public void visitJmlStatementExpr(JmlStatementExpr that) { if (that.token == JmlTokenKind.COMMENT) { currentBlock.statements.add(that); } else if (that.token == JmlTokenKind.ASSUME || that.token == JmlTokenKind.ASSERT) { scan(that.expression); currentBlock.statements.add(that); } else { log.error(that.pos,"esc.internal.error","Unknown token in BoogieBlocker2.visitJmlStatementExpr: " + that.token.internedName()); } } // OK @Override public void visitJmlStatementHavoc(JmlStatementHavoc that) { // TODO - seems a shame to recopy the whole list on the chance that there is a \nothing to remove Iterator<JCExpression> iter = that.storerefs.iterator(); ListBuffer<JCExpression> newlist = new ListBuffer<JCExpression>(); while (iter.hasNext()) { JCExpression x = iter.next(); if (x instanceof JmlStoreRefKeyword && ((JmlStoreRefKeyword)x).token == JmlTokenKind.BSNOTHING) {} else newlist.add(x); } that.storerefs = newlist.toList(); if (!that.storerefs.isEmpty()) currentBlock.statements.add(that); // FIXME - are the targets all OK for Boogie? } // We introduce the name 'assumeCheck$<int>$<label>' in order to make // it easy to identify the places where assumptions are being checked. /** Adds (translated) assertions/assumptions that do assumption feasibility checking * for an assumption that is just added to the currentBlock * @param pos a positive integer different than that used for any other checkAssumption call; * it should also be the textual location of the assumption being tested * @param label a Label giving the kind of assumption being tested (in order to * better interpret the implications of the assumptino not being feasible) */ // FIXME - REVIEW and document protected void checkAssumption(int pos, /*@ non_null*/ Label label, List<JCStatement> statements) { // if (!insertAssumptionChecks) return; // JCExpression e; // JCIdent id; // String n = ASSUME_CHECK_PREFIX + pos + "" + label.toString(); // if (useCountedAssumeCheck) { // JCExpression count = treeutils.makeIntLiteral(pos,pos); // e = treeutils.makeBinary(pos,JCTree.NE,assumeCheckCountVar,count); // id = treeutils.makeIdent(pos,n,syms.booleanType); // //e = treeutils.makeBinary(pos,JCTree.EQ,id,e); // // assume assumeCheck$<int>$<label> == <assumeCheckCountVar> != <int> // // To do the coreId method, we need to put this in the definitions list // // instead. And it does not hurt anyway. // //addAssume(pos,Label.ASSUME_CHECK,e); // adds to the currentBlock // } else { // id = treeutils.makeIdent(pos,n,syms.booleanType); // e = id; // if (assumeCheck == null) assumeCheck = e; // else assumeCheck = treeutils.makeBinary(pos,JCTree.AND,e,assumeCheck); // } // // an assert without tracking // // assert assumeCheck$<int>$<label> // addAssertNoTrack(Label.ASSUME_CHECK,id,statements,pos,null); // FIXME - need the position of the assume, I think } // FIXME - REVIEW and document protected void checkAssumption(int pos, /*@ non_null*/ Label label) { checkAssumption(pos,label,currentBlock.statements); } //OK public void visitJmlStatementDecls(JmlStatementDecls that) { shouldNotBeCalled(that); } // Visit methods for Expressions for the most part just use the super class's // visit methods. These just call visitors on each subexpression. // Everything is transformed in place. // There are a few nodes that get special treatment: // JCIdent - the name is overwritten with the single-assignment name (note that // the name will be out of synch with the symbol) // \old and \pre expressions - these need to find the correct scope and translate // JCIdent nodes within their scopes using the correct single-assignment names public Map<JCTree,JCTree> toLogicalForm = new HashMap<JCTree,JCTree>(); public Map<JCTree,String> toValue = new HashMap<JCTree,String>(); // OK @Override public void visitIdent(JCIdent that) { if (that.sym instanceof Symbol.VarSymbol){ Symbol.VarSymbol vsym = (Symbol.VarSymbol)that.sym; that.name = getCurrentName(vsym); // if (isDefined.add(that.name)) { // System.out.println("Added " + that.sym + " " + that.name); // program.declarations.add(that); // } } else if (that.sym == null) { // Temporary variables that are introduced by decomposing expressions do not have associated symbols // They are also only initialized once and only used locally, so we do not track them for DSA purposes // Just skip } else if (that.sym instanceof Symbol.ClassSymbol) { // Just skip } else if (that.sym instanceof Symbol.PackageSymbol) { // Just skip } else { log.error(that.pos,"jml.internal","THIS KIND OF IDENT IS NOT HANDLED: " + that + " " + that.sym.getClass()); } result = that; } public void visitLiteral(JCLiteral tree) { result = tree; } @Override public void visitSelect(JCFieldAccess that) { if (!(that.sym instanceof Symbol.VarSymbol)) return; // This is a qualified type name if (that.sym.isStatic()) { that.name = getCurrentName((Symbol.VarSymbol)that.sym); JCIdent id = treeutils.makeIdent(that.pos,that.sym); id.name = that.name; if (isDefined.add(that.sym)) { // System.out.println("AddedF " + that.sym + " " + that.name); program.declarations.add(id); } result = id; } else { that.name = getCurrentName((Symbol.VarSymbol)that.sym); scan(that.selected); if (isDefined.add(that.sym)) { // System.out.println("AddedF " + that.sym + " " + that.name); JCIdent id = treeutils.makeIdent(that.pos,that.sym); id.name = that.name; program.declarations.add(id); } result = that; } } public void visitIndexed(JCArrayAccess that) { scan(that.indexed); scan(that.index); result = that; // JCIdent arr = getArrayIdent(that.type); // // if (that instanceof JmlBBArrayAccess) { // that.indexed = indexed; // that.index = index; // ((JmlBBArrayAccess)that).arraysId = arr; // result = that; // } else { // log.warning(that,"jml.internal","Did not expect a JCArrayAccess node in BoogieBlocker2.visitIndexed"); // result = new JmlBBArrayAccess(arr,indexed,index); // FIXME -= M // } } // FIXME - review public void visitAssign(JCAssign that) { scan(that.lhs); JCExpression left = result; scan(that.rhs); JCExpression right = result; result = doAssignment(that.type,left,right,that.pos,that); } // // FIXME - embedded assignments to array elements are not implemented; no warning either // FIXME - is all implicit casting handled // Note that the left and right expressions are translated. protected JCExpression doAssignment(Type restype, JCExpression left, JCExpression right, int pos, JCExpression assignExpr) { // scan(left); // scan(right); currentBlock.statements.add(treeutils.makeAssignStat(pos,left,right)); return assignExpr; // if (left instanceof JCIdent) { //// JCIdent id = (JCIdent)left; //// JCIdent newid = id; //// currentBlock.statements.add(treeutils.makeVarDef(newid.type, newid.name, id.sym.owner, pos)); //// JCBinary expr = treeutils.makeEquality(pos,newid,right); //// copyEndPosition(expr,right); //// addAssume(TreeInfo.getStartPos(statement),Label.ASSIGNMENT,expr); // currentBlock.statements.add(treeutils.makeAssignStat(pos,left,right)); // return assignExpr; // } else if (left instanceof JCArrayAccess) { //// JCIdent arr = getArrayIdent(right.type); //// JCExpression ex = ((JCArrayAccess)left).indexed; //// JCExpression index = ((JCArrayAccess)left).index; //// JCIdent nid = newArrayIncarnation(right.type,left.pos); //// //// //JCExpression rhs = makeStore(ex,index,right); //// JCExpression expr = new JmlBBArrayAssignment(nid,arr,ex,((JCArrayAccess)left).index,right); //// expr.pos = pos; //// expr.type = restype; //// //// // FIXME - set line and source ////// JCBinary bin = treeutils.makeEquality(Position.NOPOS,nid,expr); ////// copyEndPosition(bin,expr); //// addAssume(TreeInfo.getStartPos(left),Label.ASSIGNMENT,expr,currentBlock.statements); //// //newIdentIncarnation(heapVar,pos); // return right; // } else if (left instanceof JCFieldAccess) { // VarSymbol sym = (VarSymbol)selectorSym(left); // if (sym.isStatic()) { // JCIdent id = treeutils.makeIdent(left.pos,sym); // JCIdent newid = id; // currentBlock.statements.add(treeutils.makeVarDef(newid.type, newid.name, id.sym.owner, pos)); // JCBinary expr = treeutils.makeEquality(pos,newid,right); // copyEndPosition(expr,right); // addAssume(TreeInfo.getStartPos(assignExpr),Label.ASSIGNMENT,expr); // return newid; // } else { // JCFieldAccess fa = (JCFieldAccess)left; // JCIdent oldfield = treeutils.makeIdent(pos,(VarSymbol)fa.sym); // if (isDefined.add(fa.sym)) { //// System.out.println("AddedFF " + oldfield.sym + " " + oldfield.name); // program.declarations.add(oldfield); // } // JCIdent newfield = oldfield; //// if (isDefined.add(newfield.name)) { //// System.out.println("AddedFF " + newfield.sym + " " + newfield.name); //// program.declarations.add(newfield); //// } // JCExpression expr = new JmlBBFieldAssignment(newfield,oldfield,fa.selected,right); // expr.pos = pos; // expr.type = restype; // // // FIXME - set line and source //// addAssume(TreeInfo.getStartPos(left),Label.ASSIGNMENT,expr,currentBlock.statements); //// newIdentIncarnation(heapVar,pos); // } // return left; // } else { // log.error("jml.internal","Unexpected case in BoogieBlocker.doAssignment: " + left.getClass() + " " + left); // return null; // } } protected Symbol selectorSym(JCTree tree) { if (tree instanceof JCIdent) return ((JCIdent)tree).sym; if (tree instanceof JCFieldAccess) return ((JCFieldAccess)tree).sym; log.error("jml.internal","Unexpected case in selectorSym: " + tree.getClass() + " " + tree); return null; } // FIXME - this is all wrong -= except FIXME - review newIdentIncarnation public void visitVarDef(JCVariableDecl that) { currentBlock.statements.add(comment(that)); JCIdent lhs = treeutils.makeIdent(that.getPreferredPosition(),that.sym); program.declarations.add(lhs); if (that.init != null) { // Create and store the new lhs incarnation before translating the // initializer because the initializer is in the scope of the newly // declared variable. Actually if there is such a situation, it // will likely generate an error about use of an uninitialized variable. scan(that.init); JCBinary expr = treeutils.makeBinary(that.pos,JCBinary.Tag.EQ,lhs,that.init); // FIXME - INITIALIZATION instead of ASSIGNMENT? addAssume(that.getStartPosition(),Label.ASSIGNMENT,expr,currentBlock.statements); } } // OK public void visitSynchronized(JCSynchronized that) { super.visitSynchronized(that); } // FIXME - review and document List<Map<Symbol,Type>> typeargsStack = new LinkedList<Map<Symbol,Type>>(); // FIXME - review and document Map<Symbol,Type> typeargs = new HashMap<Symbol,Type>(); // FIXME - review and document public void pushTypeArgs() { typeargsStack.add(0,typeargs); typeargs = new HashMap<Symbol,Type>(); } // FIXME - review and document public void popTypeArgs() { typeargs = typeargsStack.remove(0); } // FIXME - review and document public void pushTypeArgs(Type type) { if (type.getTypeArguments() != null && type.getTypeArguments().size() != 0) { pushTypeArgs(); Iterator<Type> args = type.getTypeArguments().iterator(); Iterator<TypeVariableSymbol> params = type.tsym.getTypeParameters().iterator(); while (params.hasNext()) { typeargs.put(params.next(),args.next()); } } } // FIXME - review and document public void popTypeArgs(Type type) { if (type.getTypeArguments() != null && type.getTypeArguments().size() != 0) { popTypeArgs(); } } // // FIXME - review and document // public Type trType(Type type) { // if (type instanceof Type.TypeVar) { // Type t = typeargs.get(type.tsym); // type = t != null ? t : ((Type.TypeVar)type).getUpperBound(); // FIXME - what about the other bounds? // } // return type; // } // public void visitTypeIdent(JCPrimitiveTypeTree that) { notImpl(that); } public void visitTypeArray(JCArrayTypeTree that) { notImpl(that); } public void visitTypeApply(JCTypeApply that) { notImpl(that); } public void visitTypeParameter(JCTypeParameter that) { notImpl(that); } public void visitWildcard(JCWildcard that) { notImpl(that); } public void visitTypeBoundKind(TypeBoundKind that) { notImpl(that); } public void visitAnnotation(JCAnnotation that) { notImpl(that); } public void visitModifiers(JCModifiers that) { notImpl(that); } public void visitErroneous(JCErroneous that) { notImpl(that); } public void visitLetExpr(LetExpr that) { notImpl(that); } // Adds specs to a Java Variable Declaration // FIXME - delegate to visitVarDef? // FIXME - use a constructed name? public void visitJmlVariableDecl(JmlVariableDecl that) { if (that.sym == null || that.sym.owner == null) { if (that.init != null) { scan(that.init); that.init = result; } if (that.init instanceof JCMethodInvocation) { that.init = null; currentBlock.statements.add(that); } else { JCIdent lhs = treeutils.makeIdent(that.getPreferredPosition(),that.sym); program.declarations.add(lhs); if (that.init != null) { JCStatement stat = treeutils.makeAssignStat(that.pos, lhs, that.init); currentBlock.statements.add(stat); } } } else if (that.init == null) { JCIdent lhs = treeutils.makeIdent(that.getPreferredPosition(),that.sym); scan(lhs); program.declarations.add(lhs); } else { JCIdent lhs = treeutils.makeIdent(that.getPreferredPosition(),that.sym); scan(lhs); program.declarations.add(lhs); scan(that.init); that.init = result; JCStatement stat = treeutils.makeAssignStat(that.pos, lhs, that.init); currentBlock.statements.add(stat); } // if (that.init != null) scan(that.init); // if (that.sym != null) currentMap.put(that.sym, currentMap.everythingIncarnation, that.name); // currentBlock.statements.add(that); // currentBlock.statements.add(comment(that)); // // FIXME - need to add various field specs tests // JCIdent vd = newIdentIncarnation(that,that.pos); // toLogicalForm.put(that,vd); // if (that.init != null) { // int p = that.init.pos; // boolean prevInSpecExpression = inSpecExpression; // try { // if (Utils.instance(context).isJML(that.mods)) inSpecExpression = true; // JCExpression ninit = (that.init); // addAssume(TreeInfo.getStartPos(that),Label.ASSIGNMENT,treeutils.makeEquality(p,vd,ninit)); // } finally { // inSpecExpression = prevInSpecExpression; // } // } } // FIXME - how are these checked for definedness? @Override public void visitJmlQuantifiedExpr(JmlQuantifiedExpr that) { notImpl(that); // JmlToken op = that.op; // if (op == JmlToken.BSFORALL || op == JmlToken.BSEXISTS) { // JCExpression prevCondition = condition; // try { // ListBuffer<JCVariableDecl> decls = ListBuffer.<JCVariableDecl>lb(); // for (JCVariableDecl d: that.decls) { // JCIdent id = newIdentUse(d.sym,0); // JCVariableDecl dd = M.at(d.pos).VarDef(d.mods,id.name,d.vartype,null); // dd.type = d.type; // decls.append(dd); // } // JCExpression range = trExpr(that.range); // condition = range == null ? condition : treeutils.makeBinary(condition.pos,JCTree.AND,condition,range); // JCExpression predicate = trExpr(that.value); // JmlQuantifiedExpr now = M.at(that.pos).JmlQuantifiedExpr(op,decls,range,predicate); // now.type = that.type; // result = now; // } finally { // condition = prevCondition; // } // } else { // result = trueLiteral; // notImpl(that); // } // toLogicalForm.put(that,result); } // FIXME _ implement @Override public void visitJmlSetComprehension(JmlSetComprehension that) { notImpl(that); } @Override public void visitJmlStoreRefListExpression(JmlStoreRefListExpression that) { // switch (that.token) { // case BSNOTMODIFIED: // // Allows multiple arguments; they may be store-refs with wildcards (FIXME) // JCExpression combined = null; // for (JCExpression a : that.list){ // // FIXME - there is an issue with condition - how do we evaluate if old(e) is well-defined? // // defined as arg == \old(arg) // int pos = that.pos; // JCExpression e = trExpr(a); // VarMap prevMap = currentMap; // currentMap = oldMap; // try { // // FIXME - what happens if not_modifieds are nested, or within an old // //extraEnv = true; // JCExpression ee = trExpr(a); // ee = treeutils.makeEquality(pos,e,ee); // if (combined == null) combined = ee; // else combined = treeutils.makeBinary(pos,JCTree.AND,combined,ee); // } finally { // currentMap = prevMap; // //extraEnv = false; // } // } // result = combined; // break; // // default: notImpl(that); // } } @Override public void visitJmlChoose(JmlChoose that) { notImpl(that); } @Override public void visitJmlMethodSig(JmlMethodSig that) { notImpl(that); } @Override public void visitJmlMethodClauseCallable(JmlMethodClauseCallable that) { notImpl(that); } @Override public void visitJmlModelProgramStatement(JmlModelProgramStatement that) { notImpl(that); } @Override public void visitJmlGroupName(JmlGroupName that) { notImpl(that); } @Override public void visitJmlTypeClauseIn(JmlTypeClauseIn that) { notImpl(that); } @Override public void visitJmlTypeClauseMaps(JmlTypeClauseMaps that) { notImpl(that); } @Override public void visitJmlTypeClauseExpr(JmlTypeClauseExpr that) { notImpl(that); } @Override public void visitJmlTypeClauseDecl(JmlTypeClauseDecl that) { notImpl(that); } @Override public void visitJmlTypeClauseInitializer(JmlTypeClauseInitializer that) { notImpl(that); } @Override public void visitJmlTypeClauseConstraint(JmlTypeClauseConstraint that) { notImpl(that); } @Override public void visitJmlTypeClauseRepresents(JmlTypeClauseRepresents that) { notImpl(that); } @Override public void visitJmlTypeClauseConditional(JmlTypeClauseConditional that) { notImpl(that); } @Override public void visitJmlTypeClauseMonitorsFor(JmlTypeClauseMonitorsFor that) { notImpl(that); } @Override public void visitJmlMethodClauseGroup(JmlMethodClauseGroup that) { notImpl(that); } @Override public void visitJmlMethodClauseDecl(JmlMethodClauseDecl that) { notImpl(that); } @Override public void visitJmlMethodClauseExpr(JmlMethodClauseExpr that) { notImpl(that); } @Override public void visitJmlMethodClauseConditional(JmlMethodClauseConditional that) { notImpl(that); } @Override public void visitJmlMethodClauseSignals(JmlMethodClauseSignals that) { notImpl(that); } @Override public void visitJmlMethodClauseSigOnly(JmlMethodClauseSignalsOnly that) { notImpl(that); } @Override public void visitJmlMethodClauseStoreRef(JmlMethodClauseStoreRef that) { notImpl(that); } @Override public void visitJmlSpecificationCase(JmlSpecificationCase that){ notImpl(that); } @Override public void visitJmlMethodSpecs(JmlMethodSpecs that) { notImpl(that); } @Override public void visitJmlPrimitiveTypeTree(JmlPrimitiveTypeTree that){ notImpl(that); } @Override public void visitJmlStoreRefKeyword(JmlStoreRefKeyword that) { notImpl(that); } @Override public void visitJmlStoreRefArrayRange(JmlStoreRefArrayRange that){ notImpl(that); } // These should all be translated away prior to calling the basic blocker @Override public void visitJmlBinary(JmlBinary that) { shouldNotBeCalled(that); } @Override public void visitJmlLblExpression(JmlLblExpression that) { shouldNotBeCalled(that); } @Override public void visitJmlStatement(JmlStatement that) { shouldNotBeCalled(that); } // These do not need to be implemented @Override public void visitTopLevel(JCCompilationUnit that) { shouldNotBeCalled(that); } @Override public void visitImport(JCImport that) { shouldNotBeCalled(that); } @Override public void visitJmlCompilationUnit(JmlCompilationUnit that) { shouldNotBeCalled(that); } @Override public void visitJmlImport(JmlImport that) { shouldNotBeCalled(that); } @Override public void visitMethodDef(JCMethodDecl that) { shouldNotBeCalled(that); } @Override public void visitJmlMethodDecl(JmlMethodDecl that) { shouldNotBeCalled(that); } @Override public void visitJmlStatementSpec(JmlStatementSpec that) { shouldNotBeCalled(that); } // Expressions just need to set the result field @Override public void visitBinary(JCBinary that) { scan(that.lhs); that.lhs = result; scan(that.rhs); that.rhs = result; result = that; } @Override public void visitUnary(JCUnary that) { scan(that.arg); that.arg = result; result = that; } @Override public void visitParens(JCParens that) { scan(that.expr); that.expr = result; result = that; } @Override public void visitConditional(JCConditional that) { scan(that.cond); that.cond = result; scan(that.truepart); that.truepart = result; scan(that.falsepart); that.falsepart = result; result = that; } @Override public void visitTypeTest(JCInstanceOf that) { result = treeutils.trueLit; // FIXME - fix } // Do not need to override these methods // @Override public void visitSkip(JCSkip that) { super.visitSkip(that); } public void visitJmlStatementLoop(JmlStatementLoop that) { shouldNotBeCalled(that); // These are the specs for loops - they are handled in the loop visitors } // FIXME - what about Indexed, TypeCast, TypeTest, AssignOp, NewClass, NewArray /** This class is a tree walker that finds any references to classes in the * tree being walked: types of anything, explicit type literals * * @author David Cok * */ public static class ClassFinder extends JmlTreeScanner { private Set<Type> types; public ClassFinder() {} public static Set<Type> findS(List<? extends JCTree> that, Set<Type> v) { ClassFinder vf = new ClassFinder(); return vf.find(that,v); } public static Set<Type> findS(JCTree that, Set<Type> v) { ClassFinder vf = new ClassFinder(); return vf.find(that,v); } public Set<Type> find(List<? extends JCTree> that, Set<Type> v) { if (v == null) types = new HashSet<Type>(); else types = v; for (JCTree t: that) t.accept(this); return types; } public Set<Type> find(JCTree that, Set<Type> v) { if (v == null) types = new HashSet<Type>(); else types = v; that.accept(this); return types; } // visitAnnotation - FIXME // visitApply - expression: just scan the component expressions // visitAssert - statement: just scan the component expressions // visitAssign - no new types - just scan the component expressions // visitAssignOp - no new types - just scan the component expressions // visitBinary - only primitive types // visitBlock - statement: just scan the component expressions // visitBreak - statement: just scan the component expressions // visitCase - statement: just scan the component expressions // visitCatch - statement: just scan the component expressions - FIXME - make sure to get the declaration // visitClassDef - FIXME ??? // visitConditional - no new types - just scan the component expressions // visitContinue - statement: just scan the component expressions // visitDoLoop - statement: just scan the component expressions // visitErroneous - statement: just scan the component expressions // visitExec - statement: just scan the component expressions // visitForEachLoop - statement: just scan the component expressions - FIXME - implied iterator? // visitForLoop - statement: just scan the component expressions public void visitIdent(JCIdent that) { if (!that.type.isPrimitive()) types.add(that.type); super.visitIdent(that); } // visitIf - statement: just scan the component expressions // visitImport - statement: just scan the component expressions // visitIndexed - FIXME // visitLabelled - statement: just scan the component expressions // visitLetExpr - FIXME // visitLiteral - FIXME // visitMethodDef - FIXME // visitModifiers - no new types // visitNewArray - FIXME public void visitNewClass(JCNewClass that) { types.add(that.type); } // visitParens - no new types - just scan the component expressions // visitReturn - statement: just scan the component expressions // visitSelect - FIXME // visitSkip - statement: just scan the component expressions // visitSwitch - statement: just scan the component expressions (FIXME _ might be an Enum) // visitSynchronized - statement: just scan the component expressions // visitThrow - statement: just scan the component expressions // visitTopLevel - statement: just scan the component expressions // visitTree // visitTry - statement: just scan the component expressions // visitTypeApply - FIXME ?? // visitTypeArray - FIXME ?? // visitTypeBoundKind - FIXME ?? // visitTypeCast - FIXME ?? public void visitTypeIdent(JCPrimitiveTypeTree that) { types.add(that.type); super.visitTypeIdent(that); } // visitTypeParameter - FIXME ?? // visitTypeTest (instanceof) - scans the expression and the type // visitUnary - only primitive types // visitVarDef - FIXME ?? // visitWhileLoop - statement: just scan the component expressions // visitWildcard - FIXME ?? // visitJmlBBArrayAccess - FIXME ? // visitJmlBBArrayAssignment - FIXME ? // visitJmlBBFieldAccess - FIXME ? // visitJmlBBFieldAssignment - FIXME ? // visitJmlBinary - no new types - FIXME - subtype? // visitJmlClassDecl - FIXME - do specs also // visitJmlCompilationUnit - just scan internal components // visitJmlConstraintMethodSig - FIXME ? // visitJmlDoWhileLoop - FIXME - scan specs // visitJmlEnhancedForLoop - FIXME - scan specs // visitJmlForLoop - FIXME - scan specs // visitJmlGroupName - FIXME?? // visitJmlImport - no types // visitLblExpression - no new types - scan component expressions // visitJmlMethodClause... - scan all component expressions - FIXME : watch Decls, Signals, SigOnly // visitJmlMethodDecl - FIXME?? - do specs also // visitJmlMethodSpecs - FIXME?? // visitJmlPrimitiveTypeTree - FIXME?? // visitJmlQuantifiedExpr - FIXME?? // visitJmlRefines - FIXME?? // visitJmlSetComprehension - FIXME?? // visitJmlSingleton - FIXME?? // visitJmlSpecificationCase - FIXME?? - FIXME?? // visitJmlStatement - FIXME?? // visitJmlStatementDecls - FIXME?? // visitJmlStatementExpr - FIXME?? // visitJmlStatementLoop - FIXME?? // visitJmlStatementSpec - FIXME?? // visitJmlStoreRefArrayRange - FIXME?? // visitJmlStoreRefKeyword - FIXME?? // visitJmlStoreRefListExpression - FIXME?? // visitJmlTypeClause... - scan all components - FIXME - is there more to do? // visitJmlVariableDecl - FIXME?? // visitJmlWhileLoop - FIXME - be sure to scan specs // FIXME - some things that can probably always be counted on: Object, String, Class // FIXME - closure of super types and super interfaces } /** This class is a tree walker that finds everything that is the target of * a modification in the tree being walked: assignments, assignment-ops, * increment and decrement operators, fields specified as modified by a * method call. */ // FIXME - is the tree already in reduced BoogieBlock form? public static class TargetFinder extends JmlTreeScanner { private ListBuffer<JCExpression> vars; public TargetFinder() {} /** Finds variables in the given JCTree, adding them to the list that is the * second argument; the second argument is returned. */ public static /*@Nullable*/ListBuffer<JCExpression> findVars(JCTree that, /*@Nullable*/ListBuffer<JCExpression> v) { if (that == null) return v; TargetFinder vf = new TargetFinder(); return vf.find(that,v); } /** Finds variables in the given JCTrees, adding them to the list that is the * second argument; the second argument is returned. */ public static ListBuffer<JCExpression> findVars(Iterable<? extends JCTree> list, /*@Nullable*/ListBuffer<JCExpression> v) { TargetFinder vf = new TargetFinder(); return vf.find(list,v); } /** Finds variables in the given JCTrees, adding them to the list that is the * second argument; the second argument is returned. */ public ListBuffer<JCExpression> find(Iterable<? extends JCTree> list, /*@Nullable*/ListBuffer<JCExpression> v) { if (v == null) vars = new ListBuffer<JCExpression>(); else vars = v; for (JCTree t: list) t.accept(this); return vars; } /** Finds variables in the given JCTrees, adding them to the list that is the * second argument; the second argument is returned. */ public ListBuffer<JCExpression> find(JCTree that, ListBuffer<JCExpression> v) { if (that == null) return v; if (v == null) vars = new ListBuffer<JCExpression>(); else vars = v; that.accept(this); return vars; } public void visitAssign(JCAssign that) { vars.add(that.lhs); } public void visitAssignOp(JCAssign that) { vars.add(that.lhs); } public void visitUnary(JCUnary that) { JCTree.Tag op = that.getTag(); if (op == JCTree.Tag.POSTDEC || op == JCTree.Tag.POSTINC || op == JCTree.Tag.PREINC || op == JCTree.Tag.PREDEC) vars.add(that.getExpression()); } // FIXME - also need targets of method calls, update statements of loops, // initialization statements of loops, specs of method calls } /** A Map that caches class info for a given class symbol */ @NonNull protected Map<Symbol,JmlClassInfo> classInfoMap = new HashMap<Symbol,JmlClassInfo>(); /** Returns the jmlClassInfo structure for a class, computing and caching * it if necessary. * @param cls the declaration whose info is desired * @return the corresponding JmlClassInfo structure; may be null if the * argument has no associated symbol */ //@ modifies (* contents of the classInfoMap *); //@ ensures cls.sym != null <==> \result != null; @Nullable JmlClassInfo getClassInfo(@NonNull JCClassDecl cls) { JmlClassInfo mi = classInfoMap.get(cls.sym); if (mi == null) { mi = computeClassInfo(cls.sym); classInfoMap.put(cls.sym,mi); } return mi; } /** Returns the JmlClassInfo structure for the given Class Symbol, * computing and caching it if necessary * @param sym the Symbol for the class whose JmlClassInfo is wanted * @return the corresponding JmlClassInfo structure, null if sym is null */ @Nullable JmlClassInfo getClassInfo(@NonNull Symbol sym) { if (sym == null) return null; ClassSymbol csym = (ClassSymbol)sym; JmlClassInfo mi = classInfoMap.get(sym); if (mi == null) { mi = computeClassInfo(csym); classInfoMap.put(sym,mi); } return mi; } // FIXME - what about nested classes ($-separated ?) /** Returns the JmlClassInfo structure for the given dot-separated, * fully-qualified class name, * computing and caching it if necessary * @param qualifiedName the fully-qualified name of the class * @return the corresponding JmlClassInfo structure, null if sym is null */ @Nullable JmlClassInfo getClassInfo(@NonNull String qualifiedName) { Name n = Names.instance(context).fromString(qualifiedName); Symbol sym = Symtab.instance(context).classes.get(n); return getClassInfo(sym); } // TODO - review this /** Computes the class information for a given class declaration. * @param csym the ClassSymbol for which to get JmlClassInfo * @return the corresponding JmlClassInfo */ protected @Nullable JmlClassInfo computeClassInfo(@NonNull ClassSymbol csym) { TypeSpecs typeSpecs = specs.get(csym); if (typeSpecs == null) { if (csym == syms.arrayClass) { // This one is special JmlClassInfo classInfo = new JmlClassInfo(null); classInfo.typeSpecs = new TypeSpecs(csym); // FIXME - save this inJmlSpecs? classInfo.csym = csym; Type type = syms.objectType; classInfo.superclassInfo = getClassInfo(type.tsym); return classInfo; } // This should not happen - every class referenced is loaded, // even binary files. If there is no source and no specs, then // the typespecs will have essentially null // innards, but the object should be there. // If this point is reached, some class somehow escaped being loaded. log.error("jml.internal","No typespecs for class " + csym); return null; } JCClassDecl tree = typeSpecs.decl; // 'tree' may be null if there is a binary class with no specs. // So we have to be sure there are default specs, which for // a class is essentially empty. JmlClassInfo classInfo = new JmlClassInfo(tree); classInfo.typeSpecs = typeSpecs; classInfo.csym = csym; Type type = csym.getSuperclass(); classInfo.superclassInfo = (csym == syms.objectType.tsym || csym.isInterface() ) ? null : getClassInfo(type.tsym); // Divide up the various type specification clauses into the various types ListBuffer<JmlTypeClauseRepresents> represents = new ListBuffer<JmlTypeClauseRepresents>(); ListBuffer<JCVariableDecl> modelFields = new ListBuffer<JCVariableDecl>(); for (JmlTypeClause c: typeSpecs.clauses) { // FIXME - decls? boolean isStatic = c.modifiers != null && (c.modifiers.flags & Flags.STATIC) != 0; if (c instanceof JmlTypeClauseDecl) { JCTree tt = ((JmlTypeClauseDecl)c).decl; if (tt instanceof JCVariableDecl && ((JmlAttr)Attr.instance(context)).isModel(((JCVariableDecl)tt).mods)) { // model field - find represents or make into abstract method modelFields.append((JCVariableDecl)tt); } else { // ghost fields, model methods, model classes are used as is //newlist.append(tt); } } else { JmlTokenKind token = c.token; if (token == JmlTokenKind.INVARIANT) { JmlTypeClauseExpr copy = (JmlTypeClauseExpr)c.clone(); //copy.expression = treetrans.translate(copy.expression); if (isStatic) classInfo.staticinvariants.add(copy); else classInfo.invariants.add(copy); } else if (token == JmlTokenKind.REPRESENTS) { JmlTypeClauseRepresents r = (JmlTypeClauseRepresents)c; represents.append(r); } else if (token == JmlTokenKind.CONSTRAINT) { if (isStatic) classInfo.staticconstraints.add((JmlTypeClauseConstraint)c); else classInfo.constraints.add((JmlTypeClauseConstraint)c); } else if (token == JmlTokenKind.INITIALLY) { classInfo.initiallys.add((JmlTypeClauseExpr)c); } else if (token == JmlTokenKind.AXIOM) { JmlTypeClauseExpr copy = (JmlTypeClauseExpr)c.clone(); //copy.expression = treetrans.translate(copy.expression); classInfo.axioms.add(copy); } else { log.warning("esc.not.implemented","JmlEsc does not yet implement (and ignores) " + token.internedName()); // FIXME - readable if, writable if, monitors for, in, maps, initializer, static_initializer, (model/ghost declaration?) } } } return classInfo; } // OK @Override public void visitLabelled(JCLabeledStatement that) { super.visitLabelled(that); } // // FIXME - Review // /** This class converts a counterexample into more readable information */ // public static class Tracer extends JmlTreeScanner { // // /** The compilation context */ // @NonNull Context context; // // /** The counterexample information */ // @NonNull ICounterexample ce; // // /** The log for output */ // @NonNull Log log; // // @NonNull Writer w; // // /** A runtime exception used to jump up to a finally block in the visitor calling stack */ // private static class ReturnException extends RuntimeException { // private static final long serialVersionUID = -3475328526478936978L;} // // /** A runtime exception used to jump up to a finally block in the visitor calling stack */ // private static class ExException extends RuntimeException { // private static final long serialVersionUID = -5610207201211221750L;} // // /** Outputs the counterexample information in more readable form // * @param context the compilation context // * @param decl the method declaration // * @param s the counterexample information to translate // */ // public String trace(@NonNull Context context, @NonNull JCMethodDecl decl, @NonNull ICounterexample s) { // Tracer t = new Tracer(context,s); // try { // try { // decl.accept(t); // } catch (ReturnException e) { // // ignore // } catch (ExException e) { // // ignore // } catch (RuntimeException e) { // t.w.append("FAILED : " + e + "\n"); // } // t.w.append("END\n"); // return t.w.toString(); // } catch (IOException e) { // log.noticeWriter.println("IOException"); // return ""; // } // } // // /** Translates the given position information into source, line and column information // * @param pos the position information to translate // * @return A String containing human-readable source location information // */ // public String getPosition(int pos) { // TODO - check about the second argument of getColumnNumber // return log.currentSourceFile().getName() + ":" + log.currentSource().getLineNumber(pos) + " (col " + log.currentSource().getColumnNumber(pos,false) + "): "; // } // // /** The constructor for this class // * @param context the compilation context // * @param s the counterexample information // */ // protected Tracer(@NonNull Context context, @NonNull ICounterexample s) { // this.context = context; // ce = s; // log = Log.instance(context); // w = new StringWriter(); // } // // // CAUTION: The Strings in use in these visit methods correspond to the // // encoding used in the BoogieBlocker methods. The BoogieBlocker encodes // // variables using combinations of variable name, declaration position, // // and incarnation position. These are reflected in the counterexample // // information and we need to match that as we interpret the counterexample // // information in these methods. // // // FIXME - this implementation needs fleshing out // // public void visitMethodDef(JCMethodDecl that) { // try { // w.append("START METHOD " + that.sym + "\n"); // for (JCVariableDecl param: that.params) { // String s = param.name + "$" + param.pos + "$0"; // String value = ce.get(s); // w.append("Parameter value: " + param.name + " = " + (value == null ? "<unused>" : value) + "\n"); // } // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // super.visitMethodDef(that); // } // // public void visitIf(JCIf that) { // String s = "branchCondition_" + that.pos + "_" + 0; // String value = ce.get(s); // try { // if (value == null) w.append(getPosition(that.pos) + "!!! Could not find value for branch ("+s+")\n"); // else { // w.append(getPosition(that.pos) + "Branch condition value: " + value + "\n"); // if (value.equals("true")) { // if (that.thenpart != null) that.thenpart.accept(this); // } else if (value.equals("false")) { // if (that.elsepart != null) that.elsepart.accept(this); // } else { // w.append("!!! Unknown value: " + value + "\n"); // } // } // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // } // // public void visitAssign(JCAssign that) { // try { // if (that.lhs instanceof JCIdent) { // JCIdent id = (JCIdent)that.lhs; // String s = id.name + "$" + ((VarSymbol)id.sym).pos + "$" + that.pos; // String value = ce.get(s); // if (value == null) w.append(getPosition(that.pos) + "!!! Could not find value for variable ("+s+")\n"); // else w.append(getPosition(that.pos) + "Assignment: " + id.name + " = " + value + "\n"); // } // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // } // // public void visitTry(JCTry that) { // try { // try { // that.body.accept(this); // } catch (ReturnException e) { // // do the finally block // if (that.finalizer != null) { // w.append(getPosition(that.finalizer.pos) + "Executing finally block\n"); // that.finalizer.accept(this); // } // throw e; // } catch (ExException e) { // // FIXME // } // } catch (IOException e) { // // FIXME // } // } // // public void visitReturn(JCReturn that) { // String s = "RESULT_"; // FIXME - should replace with defined constnat, but this is missing the final $ // String value = ce.get(s); // try { // if (that.expr != null) { // if (value == null) w.append(getPosition(that.pos) + "!!! Could not find return value ("+s+")\n"); // else w.append(getPosition(that.pos) + "Executed return: value = " + value + "\n"); // } else { // w.append(getPosition(that.pos) + "Executed return\n"); // } // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // throw new ReturnException(); // } // } // /** A runtime exception used to jump up to a finally block in the visitor calling stack */ // private static class ReturnException extends RuntimeException { // private static final long serialVersionUID = -3475328526478936978L;} // // /** A runtime exception used to jump up to a finally block in the visitor calling stack */ // private static class ExException extends RuntimeException { // private static final long serialVersionUID = -5610207201211221750L;} /** Outputs the counterexample information in more readable form * @param context the compilation context * @param program the program whose counterexample information is to be printed * @param ce the counterexample information to translate * @param prover the prover from which the counterexample information came */ // public static String trace(@NonNull Context context, @NonNull BasicProgram program, @NonNull ICounterexample ce, IProver prover) { // String s = null; // try { // s = (new TracerBB(context)).trace(program,ce,prover); // } catch (ReturnException e) { // // ignore // } catch (ExException e) { // // ignore // } catch (IOException e) { // log.noticeWriter.println("ABORTED"); // } catch (RuntimeException e) { // log.noticeWriter.println("ABORTED"); // throw e; // } // return s; // } // // // FIXME - Review // /** This class converts a counterexample into more readable information; // * it uses the basic program form rather than using the Java AST. */ // public static class TracerBB extends JmlTreeScanner { // // /** The counterexample information */ // ICounterexample ce; // // boolean showSubexpressions; // // /** The log for output */ // @NonNull Log log; // // /** The program being traced */ // BasicProgram program; // // /** The compilation context */ // @NonNull Context context; // // Map<String,String> values; // // Writer w; // // /** The prover that was used to create the counterexample */ // IProver prover; // // Symtab syms; // // List<IProverResult.Span> path = null; // // /** Translates the given position information into source, line and column information // * @param pos the position information to translate // * @return A String containing human-readable source location information // */ // public String getPosition(int pos) { // TODO - check about the second argument of getColumnNumber // return log.currentSourceFile().getName() + ":" + log.currentSource().getLineNumber(pos) + " (col " + log.currentSource().getColumnNumber(pos,false) + "): \t"; // } // // /** The constructor for this class // * @param context the compilation context // */ // public TracerBB(@NonNull Context context) { // this.context = context; // log = Log.instance(context); // syms = Symtab.instance(context); // w = new StringWriter(); // showSubexpressions = JmlOption.isOption(context,JmlOption.SUBEXPRESSIONS) || true; // } // // // FIXME - DOCUMENT // public static String trace(@NonNull Context context, @NonNull BasicProgram program, ICounterexample ce, IProver prover) { // try { // return new TracerBB(context).trace(program,ce,prover); // } catch (IOException e) { // return "<IOException: " + e + ">"; // } // } // // //@ ensures this.program != null && this.ce != null; // //@ ensures this.program != program && this.ce != ce; // public String trace(@NonNull BasicProgram program, ICounterexample ce, IProver prover) throws IOException { // this.ce = ce; // this.program = program; // this.prover = prover; // w = new StringWriter(); // w.append("COUNTEREXAMPLE TRACE \n\n"); // values = ce.getMap(); // this.subexp = new Subexpressor(context,prover,values,w); // this.path = new LinkedList<IProverResult.Span>(); // // for (JCVariableDecl vd: program.methodDecl.params) { // String n = vd.name + "$" + vd.pos + "$0"; // String value = ce.get(n); // w.append(getPosition(vd.pos) + "Parameter \t" + n + " \t= " + (value==null?"?":value) + "\n"); // } // // if (showSubexpressions) prover.reassertCounterexample(ce); //// for (Map.Entry<JCTree,String> entry: ((Counterexample)ce).mapv.entrySet()) { // FIXME - hacked to get at map - fix this //// values.put(entry.getKey(),entry.getValue()); //// } // BoogieBlock block = program.startBlock(); // outer: while (traceBlockStatements(block,ce)) { // for (BoogieBlock next: block.succeeding()) { // String s = next.id().toString(); // String value = ce.get(s); // if (value.equals("false")) { // block = next; // continue outer; // } // } // break; // } // w.append("END\n"); // ce.putMap(values); // ce.putPath(path); // return w.toString(); // } // // // // // CAUTION: The Strings in use in these visit methods correspond to the // // encoding used in the BoogieBlocker methods. The BoogieBlocker encodes // // variables using combinations of variable name, declaration position, // // and incarnation position. These are reflected in the counterexample // // information and we need to match that as we interpret the counterexample // // information in these methods. // // // FIXME - Review // protected boolean traceBlockStatements(BoogieBlock b, ICounterexample ce) throws IOException { // w.append(" [ block " + b.id() + " ]\n"); // boolean sawFalseAssert = false; // String pos=null, lastpos; // for (JCStatement statement: b.statements) { // if (!(statement instanceof JmlStatementExpr)) { // log.error(statement.pos,"esc.internal.error","Incorrect statement type in traceBlockStatements: " + statement.getClass()); // continue; // } // JmlStatementExpr s = (JmlStatementExpr)statement; // JCExpression expr = s.expression; // if (expr instanceof JCIdent) { // Name nm = ((JCIdent)expr).name; // if (nm.toString().startsWith(BoogieBlocker.ASSUMPTION_PREFIX)) { // for (BasicProgram.Definition def : program.definitions) { // if (def.id.name.equals(nm)) { // expr = def.value; // break; // } // } //// for (JCExpression e : program.pdefinitions) { //// if (e instanceof JCBinary && ((JCBinary)e).lhs instanceof JCIdent && ((JCIdent)((JCBinary)e).lhs).name.equals(nm)) { //// expr = ((JCBinary)e).rhs; //// } //// } // } // } // lastpos = pos; // pos = getPosition(s.pos); // Label label = s.label; // if (label == Label.ASSUME_CHECK) { // // skip // } else if (s.token == JmlToken.ASSUME) { // if (label == Label.ASSIGNMENT) { // // FIXME - array, field assignments // if (expr instanceof JCBinary) { // JCBinary bin = (JCBinary)expr; // if (!(bin.lhs instanceof JCIdent)) { failure(label,expr); continue; } // Name n = ((JCIdent)bin.lhs).name; // String v = value((JCIdent)bin.lhs); // w.append(pos + "Assignment " + n + " = " + v // + " [" + bin.rhs + "]\n"); // record(bin.lhs,v); // showSubexpressions(bin.rhs); // // } else if (expr instanceof JmlBBArrayAssignment){ // JmlBBArrayAssignment asg = (JmlBBArrayAssignment)expr; // JCExpression array = asg.args.get(2); // JCExpression index = asg.args.get(3); // JCExpression value = asg.args.get(4); // // List<String> results = subexp.getValues(array,index,value); // if (results != null) { // w.append(pos + "ArrayAssignment " // + results.get(0) + "[" + results.get(1) + "] = " + results.get(2) // + " [ (" + array + ")[" + index + "] = " + value + " ]\n"); // } // showSubexpressions(array); // showSubexpressions(index); // showSubexpressions(value); // } else if (expr instanceof JmlBBFieldAssignment){ // JmlBBFieldAssignment asg = (JmlBBFieldAssignment)expr; // JCExpression obj = asg.args.get(2); // JCIdent field = (JCIdent)asg.args.get(0); // JCExpression value = asg.args.get(3); // // List<String> results = subexp.getValues(obj,value); // w.append(pos + "FieldAssignment " // + results.get(0) + "." + field + " = " + results.get(1) // + " [ (" + obj + ")." + field + " = " + value + " ]\n"); // showSubexpressions(obj); // showSubexpressions(value); // // } else { // failure(label,expr); // } // } else if (label == Label.ARGUMENT) { // // Called methods and new object (called constructor) calls // if (!(expr instanceof JCBinary)) { failure(label,expr); continue; } // JCBinary bin = (JCBinary)expr; // if (!(bin.lhs instanceof JCIdent)) { failure(label,expr); continue; } // Name n = ((JCIdent)bin.lhs).name; // String v = value((JCIdent)bin.lhs); // w.append(pos + "ArgumentEvaluation " + n + " = " + v // + " [" + bin.rhs + "]\n"); // record(bin.lhs,v); // showSubexpressions(bin.rhs); // // } else if (label == Label.RECEIVER) { // // Called methods and new object (called constructor) calls // if (!(expr instanceof JCBinary)) { failure(label,expr); continue; } // JCBinary bin = (JCBinary)expr; // if (!(bin.lhs instanceof JCIdent)) { failure(label,expr); continue; } // Name n = ((JCIdent)bin.lhs).name; // String v = value((JCIdent)bin.lhs); // w.append(pos + "ReceiverEvaluation " + n + " = " + v // + " [" + bin.rhs + "]\n"); // record(bin.lhs,v); // showSubexpressions(bin.rhs); // // } else if (label == Label.BRANCHC) { // if (!(expr instanceof JCBinary)) { failure(label,expr); continue; } // JCBinary bin = (JCBinary)expr; // if (!(bin.lhs instanceof JCIdent)) { failure(label,expr); continue; } // String v = value((JCIdent)bin.lhs); // w.append(pos + label + " = " + v // + " [" + bin.rhs + "]\n"); // record(bin.lhs,v); // showSubexpressions(bin.rhs); // // } else if (label == Label.LBL) { // if (!(expr instanceof JCBinary)) { failure(label,expr); continue; } // JCBinary bin = (JCBinary)expr; // if (!(bin.lhs instanceof JCIdent)) { failure(label,expr); continue; } // JCIdent id = (JCIdent)bin.lhs; // String lbl = id.toString(); // int k = lbl.lastIndexOf('$'); // lbl = lbl.substring(k+1); // String v = value(id); // w.append(pos + label + ": " + lbl + " = " + v // + " [" + bin.rhs + "]\n"); // record(id,v); // showSubexpressions(bin.rhs); // // } else if (label == Label.SWITCH_VALUE) { // if (!(expr instanceof JCBinary)) { failure(label,expr); continue; } // JCBinary bin = (JCBinary)expr; // if (!(bin.lhs instanceof JCIdent)) { failure(label,expr); continue; } // String v = value((JCIdent)bin.lhs); // w.append(pos + "switch value = " + v // + " [" + bin.rhs + "]\n"); // record(bin.lhs,v); // showSubexpressions(bin.rhs); // // } else if (label == Label.SYN) { // FIXME - rename the SYN types that are wanted // if (expr instanceof JCBinary) { // JCExpression lhs = ((JCBinary)expr).lhs; // if (lhs instanceof JCIdent) { // String value = ce.get(((JCIdent)lhs).name.toString()); // w.append(pos + "Syn " + lhs + " = " + value + "\n"); // } else { // w.append(pos + "Syn " + expr + "\n"); // } // } else { // w.append(pos + "Syn " + expr + "\n"); // } // } else if (label == Label.EXPLICIT_ASSUME) { // if (expr instanceof JCIdent) { // // This will happen for tracked assumptions // Name n = ((JCIdent)expr).name; // String value = ce.get(n.toString()); // w.append(pos + label + " " + n + " = " + value + "\n"); // JCExpression e = findDefinition(n); // record(expr,value); // if (e != null) showSubexpressions(e); // } else { // w.append(pos + label + " " + expr + "\n"); // showSubexpressions(expr); // } // } else if (label == Label.DSA) { // if (!(expr instanceof JCBinary)) { failure(label,expr); continue; } // JCBinary bin = (JCBinary)expr; // if (!(bin.lhs instanceof JCIdent)) { failure(label,expr); continue; } // if (!(bin.rhs instanceof JCIdent)) { failure(label,expr); continue; } // w.append(lastpos + label + " = " + value((JCIdent)bin.lhs) // + " [" + bin.rhs + "]\n"); // // no subexpressions // } else if (label == Label.RETURN) { // w.append(pos + "Executing return statement\n"); // } else if (label == Label.TERMINATION) { // if (!(expr instanceof JCBinary)) { failure(label,expr); continue; } // JCBinary bin = (JCBinary)expr; // if (!(bin.lhs instanceof JCBinary)) { failure(label,expr); continue; } // bin = (JCBinary)bin.lhs; // if (!(bin.lhs instanceof JCIdent)) { failure(label,expr); continue; } // String v = value((JCIdent)bin.lhs); // if (v.equals("0")) { // String rv = bin.lhs.toString().replace("terminationVar","result"); // String vv = valueNull(rv); // w.append(pos + "Called method returned normally [" + bin.lhs + "=" + v +"]"+ (vv==null?"":", return value = " + vv + " ["+rv+"]\n")); // } else { // String rv = bin.lhs.toString().replace("terminationVar","exception"); // String vv = subexp.getType(rv); // w.append(pos + "Called method exited with an exception [" + bin.lhs + "=" + v +"]" // + (vv==null?"":", exception type = "+vv) + "\n"); // } // } else if (label == Label.METHODAXIOM) { // // Just print the axiom - don't try to evaluate it // w.append(pos + label + " " + expr + "\n"); // } else if (label == Label.ARRAY_INIT) { // // Just print the expression - don't try to evaluate it // w.append(pos + label + " " + expr + "\n"); // } else if (label == Label.BRANCHT || label == Label.HAVOC) { // // skip // } else if (label == Label.BRANCHE) { // if (expr instanceof JCUnary) { // JCExpression e = ((JCUnary)expr).getExpression(); // if (e instanceof JCIdent) { // String value = value((JCIdent)e); // record(expr,value); // } // } // } else { // w.append(pos + label + " " + expr + "\n"); // showSubexpressions(expr); // } // } else if (s.token == JmlToken.ASSERT) { // String value = null; // String name = null; // if (expr instanceof JCIdent) { // name = ((JCIdent)expr).toString(); // value = ce.get(name); // JCExpression e = findDefinition(((JCIdent)expr).name); // if (e != null) expr = e; // } // w.append(pos + "Assert [" + label + "] " // + (value == null? "" : value) // + " [" + expr + "]\n"); // showSubexpressions(expr); // if ("false".equals(value)) { // sawFalseAssert = true; // } // } else if (s.token == JmlToken.COMMENT) { // w.append(pos); // w.append("Comment: // "); // w.append(((JCLiteral)s.expression).value.toString()); // w.append("\n"); // } else { // log.error(pos,"esc.internal.error","Incorrect token type in traceBlockStatements: " + s.token.internedName()); // } // if (label == Label.PRECONDITION) { //// int sp = TreeInfo.getStartPos(expr); //// int ep = TreeInfo.getEndPos(expr,log.currentSource().getEndPosTable()); //// int type = IProverResult.Span.NORMAL; //// String result = values.get(expr.toString()); //// type = result == null ? IProverResult.Span.NORMAL : result.equals("true") ? IProverResult.Span.TRUE : result.equals("false") ? IProverResult.Span.FALSE : IProverResult.Span.NORMAL; //// if (sp >= 0 && ep >= sp) path.add(new IProverResult.Span(sp,ep,type)); // FIXME - don't think the end position is right for statements // doLogicalSubExprs(expr); // } else if (label == Label.ASSIGNMENT || label == Label.EXPLICIT_ASSERT || label == Label.EXPLICIT_ASSUME // || label == Label.BRANCHT || label == Label.BRANCHE // || label == Label.SWITCH_VALUE) { // int sp = TreeInfo.getStartPos(s); // int ep = TreeInfo.getEndPos(s,log.currentSource().getEndPosTable()); // int type = IProverResult.Span.NORMAL; // if (label != Label.ASSIGNMENT) { // String result = values.get(expr.toString()); // type = result == null ? IProverResult.Span.NORMAL : result.equals("true") ? IProverResult.Span.TRUE : result.equals("false") ? IProverResult.Span.FALSE : IProverResult.Span.NORMAL; // } // if (sp >= 0 && ep >= sp) path.add(new IProverResult.Span(sp,ep,type)); // FIXME - don't think the end position is right for statements // } else if (label == Label.CATCH_CONDITION) { // int sp = TreeInfo.getStartPos(s); // int ep = TreeInfo.getEndPos(s,log.currentSource().getEndPosTable()); // int type = IProverResult.Span.NORMAL; // type = IProverResult.Span.NORMAL; // if (sp >= 0 && ep >= sp) path.add(new IProverResult.Span(sp,ep,type)); // FIXME - don't think the end position is right for statements // } else if (label == Label.POSTCONDITION || label == Label.SIGNALS) { // JCExpression texpr = expr; // if (label == Label.SIGNALS) {// FIXME - a NPE thrown from here does not produce any visible error // texpr = (texpr instanceof JmlBinary && ((JmlBinary)texpr).op == JmlToken.IMPLIES) ? ((JmlBinary)texpr).rhs : null; // texpr = (texpr instanceof JCBinary && ((JCBinary)texpr).getTag() == JCTree.AND) ? ((JCBinary)texpr).rhs : null; // if (texpr instanceof JmlBinary && ((JmlBinary)texpr).op == JmlToken.IMPLIES) { // JCExpression tt = ((JmlBinary)texpr).rhs; // if (tt instanceof JmlBinary && ((JmlBinary)tt).op == JmlToken.IMPLIES) { // texpr = (JmlBinary)tt; // } // } else { // texpr = null; // } // } else { // texpr = (texpr instanceof JmlBinary && ((JmlBinary)texpr).op == JmlToken.IMPLIES) ? ((JmlBinary)texpr).rhs : null; // texpr = (texpr instanceof JmlBinary && ((JmlBinary)texpr).op == JmlToken.IMPLIES) ? texpr : null; // } // if (texpr instanceof JmlBinary) { // JmlBinary rexpr = (JmlBinary)texpr; // JCExpression lhs = rexpr.lhs; // JCExpression rhs = rexpr.rhs; // int sp = TreeInfo.getStartPos(lhs); // int ep = TreeInfo.getEndPos(lhs,log.currentSource().getEndPosTable()); // int type = IProverResult.Span.NORMAL; // String result = values.get(lhs.toString()); // type = result == null ? IProverResult.Span.NORMAL : result.equals("true") ? IProverResult.Span.TRUE : result.equals("false") ? IProverResult.Span.FALSE : IProverResult.Span.NORMAL; // if (sp >= 0 && ep >= sp) path.add(new IProverResult.Span(sp,ep,type)); // FIXME - don't think the end position is right for statements // if (type != IProverResult.Span.FALSE) { // sp = TreeInfo.getStartPos(rhs); // ep = TreeInfo.getEndPos(rhs,log.currentSource().getEndPosTable()); // type = IProverResult.Span.NORMAL; // result = values.get(rhs.toString()); // type = result == null ? IProverResult.Span.NORMAL : result.equals("true") ? IProverResult.Span.TRUE : result.equals("false") ? IProverResult.Span.FALSE : IProverResult.Span.NORMAL; // if (sp >= 0 && ep >= sp) path.add(new IProverResult.Span(sp,ep,type)); // FIXME - don't think the end position is right for statements // } // } else { // int sp = TreeInfo.getStartPos(s); // int ep = TreeInfo.getEndPos(s,log.currentSource().getEndPosTable()); // int type = IProverResult.Span.NORMAL; // String result = values.get(expr.toString()); // type = result == null ? IProverResult.Span.NORMAL : result.equals("true") ? IProverResult.Span.TRUE : result.equals("false") ? IProverResult.Span.FALSE : IProverResult.Span.NORMAL; // if (sp >= 0 && ep >= sp) path.add(new IProverResult.Span(sp,ep,type)); // FIXME - don't think the end position is right for statements // } // } else if (label == Label.TERMINATION) { // int sp = TreeInfo.getStartPos(s); // int ep = TreeInfo.getEndPos(s,log.currentSource().getEndPosTable()); // int type = IProverResult.Span.NORMAL; // { // JCExpression e = ((JCBinary)((JCBinary)expr).lhs).lhs; // String result = values.get(e.toString()); // int k = result == null ? 0 : Integer.valueOf(result); // type = k < 0 ? IProverResult.Span.EXCEPTION : IProverResult.Span.NORMAL; // } // if (sp >= 0 && ep >= sp) path.add(new IProverResult.Span(sp,ep,type)); // FIXME - don't think the end position is right for statements // } // if (sawFalseAssert) break;// Stop reporting the trace if we encounter a false assertion // } // return !sawFalseAssert; // } // // public int doExpr(JCExpression expr, boolean show) { // int sp = TreeInfo.getStartPos(expr); // int ep = TreeInfo.getEndPos(expr,log.currentSource().getEndPosTable()); // int type = IProverResult.Span.NORMAL; // String result = values.get(expr.toString()); // type = result == null ? IProverResult.Span.NORMAL : result.equals("true") ? IProverResult.Span.TRUE : result.equals("false") ? IProverResult.Span.FALSE : IProverResult.Span.NORMAL; // if (show && sp >= 0 && ep >= sp) path.add(new IProverResult.Span(sp,ep,type)); // FIXME - don't think the end position is right for statements // return type; // } // // public int doLogicalSubExprs(JCExpression expr) { // int r = -10; // if (expr instanceof JCBinary) { // int op = expr.getTag(); // JCBinary bin = (JCBinary)expr; // if (op == JCTree.OR) { // r = doLogicalSubExprs(bin.lhs); // if (r != IProverResult.Span.TRUE) { // r = doLogicalSubExprs(bin.rhs); // } // } else if (op == JCTree.AND) { // r = doLogicalSubExprs(bin.lhs); // if (r != IProverResult.Span.FALSE) { // r = doLogicalSubExprs(bin.rhs); // } // } else if (op == JCTree.BITOR) { // r = doLogicalSubExprs(bin.lhs); // int rr = doLogicalSubExprs(bin.rhs); // r = (rr==IProverResult.Span.TRUE) ? rr : r; // } else { // r = doExpr(expr,true); // } // } else if (expr instanceof JmlBinary) { // JmlBinary bin = (JmlBinary)expr; // JmlToken op = bin.op; // if (op == JmlToken.IMPLIES) { // r = doLogicalSubExprs(bin.lhs); // if (r != IProverResult.Span.FALSE) { // r = doLogicalSubExprs(bin.rhs); // } // } else if (op == JmlToken.REVERSE_IMPLIES) { // r = doLogicalSubExprs(bin.lhs); // if (r != IProverResult.Span.TRUE) { // r = doLogicalSubExprs(bin.rhs); // } // } else { // r = doLogicalSubExprs(bin.rhs); // r = doExpr(expr,false); // } // } else { // r = doExpr(expr,true); // } // // // FIXME - also do NOT, conditional expression, boolean ==, EQUIVALENCE, INEQUIVALENCE // return r; // } // // public JCExpression findDefinition(Name name) { // for (BasicProgram.Definition def: program.definitions()) { // JCIdent id = def.id; // if (id.name != name) continue; // return def.value; // } //// for (JCExpression e: program.pdefinitions) { //// if (!(e instanceof JCBinary)) continue; //// JCBinary bin = (JCBinary)e; //// if (!(bin.lhs instanceof JCIdent)) continue; //// JCIdent id = (JCIdent)bin.lhs; //// if (id.name != name) continue; //// return bin.rhs; //// } // return null; // } // // public String value(JCIdent id) { // String v = ce.get(id.name.toString()); // if (v == null) v = "?"; // return v; // } // // public String valueNull(JCIdent id) { // return ce.get(id.name.toString()); // } // // public String valueNull(String id) { // return ce.get(id); // } // // public void failure(Label label, JCExpression expr) { // log.warning("jml.internal.notsobad","Unable to interpret counterexample trace. A " + label + " statement has unexpected structure: " + expr); // } // // Subexpressor subexp; // // public String showSubexpressions(JCExpression expr) { // if (showSubexpressions) try { // subexp.walk(expr); // return w.toString(); // } catch (IOException e) { // return "<IOException>"; // } // return ""; // } // // public void record(JCExpression expr, String value) { // subexp.values.put(expr.toString(),value); // } // } // // static int count = 1000000; // // /** This class requests values of subexpressions from the prover */ // public static class Subexpressor extends JmlTreeScanner { // // Context context; // IProver prover; // JmlTree.Maker M; // Names names; // Symtab syms; // Writer w; // final String prefix = "X$$$"; // List<JCBinary> exprs = new LinkedList<JCBinary>(); // Map<String,JCExpression> requests = new HashMap<String,JCExpression>(); // Map<String,String> values = null; // // /** Top-level call for the class - puts requests to the prover for each // * subexpression of the argument, returning the results in 'requests'. // * This method can be reused, but is not thread-safe. // */ // public void walk(JCExpression expr) throws IOException { // exprs.clear(); // requests.clear(); // scan(expr); // IProverResult res = null; // try { // for (JCExpression e: exprs) { // prover.assume(e); // } // res = prover.check(true); // } catch (ProverException ex) { // w.append(ex.toString()); // FIXME - clean up the error reporting here and in the RUntimeExceptions just below. // w.append("\n"); // return; // } // if (res == null) { // throw new RuntimeException("ERROR: no additional information available"); // } else if (!res.isSat()) { // throw new RuntimeException("ERROR: no longer satisfiable"); // } else { // ICounterexample nce = res.counterexample(); // for (JCBinary bin: exprs) { // JCIdent id = (JCIdent)bin.lhs; // String value = nce.get(id.toString()); // if (value != null && id.type.tag == TypeTag.CHAR) { // value = ((Character)(char)Integer.parseInt(value)).toString() + " (" + value + ")"; // } // if (value == null) value = "?"; // w.append(" " + value + "\t = " + bin.rhs + "\n"); // values.put(bin.rhs.toString(), value); // } // } // } // // /** Top-level call that returns a list of values (as Strings) corresponding to the list of // * expressions in the argument */ // public List<String> getValues(JCExpression... exprlist) throws IOException { // IProverResult res = null; // List<JCIdent> ids = new LinkedList<JCIdent>(); // try { // for (JCExpression e: exprlist) { // JCIdent id = newIdent(e); // JCExpression ex = M.at(Position.NOPOS).Binary(JCTree.EQ,id,e); // ex.type = syms.booleanType; // ids.add(id); // prover.assume(ex); // } // res = prover.check(true); // } catch (ProverException ex) { // w.append(ex.toString()); w.append("\n"); // FIXME - better error response here and below // return null; // } // if (res == null) { // w.append("ERROR: no additional information available\n"); // } else if (!res.isSat()) { // w.append("ERROR: no longer satisfiable\n"); // } else { // ICounterexample nce = res.counterexample(); // List<String> out = new LinkedList<String>(); // int k = 0; // for (JCIdent id: ids) { // String value = nce.get(id.name.toString()); // if (value == null) value = "?"; // out.add(value); // if (values != null) { // JCExpression ee = exprlist[k]; // String e = ee.toString(); // if (ee.type.tag == TypeTag.CHAR) { // e = ((Character)(char)Integer.parseInt(e)).toString(); // } // values.put(e,value); // //System.out.println("MAPPING: " + e + " " + value); // k++; // FIXME - increment inside or outside the if statement // } // } // prover.reassertCounterexample(nce); // return out; // } // return null; // } // // /** Returns the dynamic type of the variable given in the argument */ // public String getType(String eid) { // try { // M.at(Position.NOPOS); // JCIdent expr = M.Ident(Names.instance(context).fromString(eid)); // expr.type = syms.objectType; // JCExpression e = M.JmlMethodInvocation(JmlToken.BSTYPEOF,expr); // e.type = syms.classType; // JCIdent id = newIdent(e); // JCExpression ex = M.at(Position.NOPOS).Binary(JCTree.EQ,id,e); // ex.type = syms.booleanType; // prover.assume(ex); // IProverResult res = prover.check(true); // if (res == null) { // w.append("ERROR: no additional information available\n"); // } else if (!res.isSat()) { // w.append("ERROR: no longer satisfiable\n"); // } else { // ICounterexample nce = res.counterexample(); // String value = nce.get(id.name.toString()); // return value; // } // } catch (IOException e) { // Log.instance(context).noticeWriter.println(e.toString()); // // } catch (ProverException e) { // Log.instance(context).noticeWriter.println(e.toString()); // } // return null; // } // // public Subexpressor(Context context, IProver prover, Map<String,String> values, Writer w) { // this.context = context; // this.prover = prover; // this.M = JmlTree.Maker.instance(context); // this.names = Names.instance(context); // this.syms = Symtab.instance(context); // this.w = w; // this.values = values; // } // // public void request(JCExpression expr) { // JCIdent id = newIdent(expr); // requests.put(id.name.toString(),expr); // JCBinary bin = M.Binary(JCTree.EQ,id,expr); // bin.type = syms.booleanType; // bin.pos = Position.NOPOS; // exprs.add(bin); // } // // /** Creates a unique identifier with the type of the given expression */ // public JCIdent newIdent(JCExpression expr) { // Type t = expr.type; // Name n = names.fromString(prefix + (++count)); // JCIdent id = M.Ident(n); // id.type = t; // return id; // } // // /** Scan the given JCTree, issuing a request() call for each subexpression encountered */ // public void scan(JCTree that) { // super.scan(that); // if (that instanceof JCExpression && // !(that instanceof JCParens) && // !(that instanceof JCLiteral)) request((JCExpression)that); // } // // /** Scan the given JCTree, issuing a request() call for each subexpression encountered, // * but not for the argument itself */ // public void scanNoRequest(JCTree that) { // super.scan(that); // } // // public void visitLiteral(JCLiteral tree) { // String r = tree.toString(); // values.put(r,r); // } // // /** Overridden so that we request the arguments but not the method call itself.*/ // public void visitApply(JCMethodInvocation tree) { // scanNoRequest(tree.meth); // scan(tree.args); // } // // /** Don't request values for quantified expressions */ // public void visitJmlQuantifiedExpr(JmlQuantifiedExpr tree) { // // do not scan the subexpressions of a quantified expression // } // // public void visitCatch(JCCatch tree) { // super.visitCatch(tree); // } // } // // /** This class computes metrics over a BoogieBlock */ // public static class Counter extends JmlTreeScanner { // // int nodes = 0; // nodes // int assumes = 0; // int asserts = 0; // int blocks = 0; // int statements = 0; // int paths = 0; // int maxBlockNodes = 0; // // public void count(BoogieBlock b) { // for (JCTree t: b.statements()) t.accept(this); // nodes += b.statements().size(); // } // // public static int counts(BoogieBlock b) { // return counts(b.statements()); // } // // public static int counts(List<JCStatement> sts) { // Counter c = new Counter(); // for (JCTree t: sts) t.accept(c); // return c.nodes + sts.size(); // } // // static public Counter count(BasicProgram b) { // Counter c = new Counter(); // int max = 0; // for (BoogieBlock bb: b.blocks()) { // int c1 = c.nodes; // c.count(bb); // if (c.nodes - c1 > max) max = c.nodes - c1; // } // c.maxBlockNodes = max; // for (BasicProgram.Definition t: b.definitions()) { t.id.accept(c); t.value.accept(c); } // for (JCTree t: b.pdefinitions) t.accept(c); // for (JCTree t: b.background()) t.accept(c); // c.blocks = b.blocks().size(); // return c; // } // // static public int countx(BasicProgram b) { // Counter c = new Counter(); // for (BasicProgram.Definition t: b.definitions()) { t.id.accept(c); t.value.accept(c); } // for (JCTree t: b.pdefinitions) t.accept(c); // for (JCTree t: b.background()) t.accept(c); // return c.nodes; // } // // static public int countAST(JCTree node) { // Counter c = new Counter(); // node.accept(c); // if (node instanceof JCBlock) c.nodes++; // return c.nodes; // } // // static public int countASTStatements(JCTree node) { // Counter c = new Counter(); // node.accept(c); // if (node instanceof JCBlock) c.statements++; // return c.statements; // } // // public Counter() { // } // // public void add(Counter c) { // nodes += c.nodes; // assumes += c.assumes; // asserts += c.asserts; // blocks += c.blocks; // statements += c.statements; // maxBlockNodes = maxBlockNodes < c.maxBlockNodes ? c.maxBlockNodes : maxBlockNodes; // } // // public void scan(JCTree that) { // nodes++; // if (that instanceof JCStatement) statements++; // super.scan(that); // } // // public void visitJmlStatementExpr(JmlStatementExpr that) { // if (that.token == JmlToken.ASSUME) assumes++; // if (that.token == JmlToken.ASSERT) asserts++; // super.visitJmlStatementExpr(that); // } // // public String toString() { // return " " + blocks + " blocks; " + nodes + " nodes; " + maxBlockNodes + " max; " + assumes + " assumes; " + asserts + " asserts; " ; // } // } }